diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | bcb704366cb5e333a626c18c308c7e0448a8e69f (patch) | |
tree | f0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kopete/protocols/oscar | |
download | tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.tar.gz tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kopete/protocols/oscar')
274 files changed, 40488 insertions, 0 deletions
diff --git a/kopete/protocols/oscar/Makefile.am b/kopete/protocols/oscar/Makefile.am new file mode 100644 index 00000000..c782e8c1 --- /dev/null +++ b/kopete/protocols/oscar/Makefile.am @@ -0,0 +1,15 @@ +SUBDIRS = liboscar . aim icq icons +METASOURCES = AUTO +AM_CPPFLAGS = -I./ui -I$(srcdir)/ui \ + -I./liboscar -I$(srcdir)/liboscar \ + $(KOPETE_INCLUDES) $(all_includes) + +lib_LTLIBRARIES = libkopete_oscar.la + +libkopete_oscar_la_SOURCES = oscaraccount.cpp oscarcontact.cpp oscarmyselfcontact.cpp \ + oscarencodingselectionbase.ui oscarencodingselectiondialog.cpp \ + oscarlistcontactsbase.ui oscarlistnonservercontacts.cpp \ + oscarvisibilitybase.ui oscarvisibilitydialog.cpp \ + oscarversionupdater.cpp +libkopete_oscar_la_LDFLAGS = -no-undefined -version-info 2:0:0 $(all_libraries) +libkopete_oscar_la_LIBADD = $(LIB_KIO) $(top_builddir)/kopete/libkopete/libkopete.la ./liboscar/liboscar.la diff --git a/kopete/protocols/oscar/TODO b/kopete/protocols/oscar/TODO new file mode 100644 index 00000000..25c82ee8 --- /dev/null +++ b/kopete/protocols/oscar/TODO @@ -0,0 +1,53 @@ +This is the TODO file for the OSCAR plugin. + +====== Possible refactorings ===== + +- Unify status handling for ICQ and AIM? I like the ICQ::Presence thing, that's cool +- Do delayed contact creation like on MSN so that when we actually get a good status + code back from the SSI manipulation, we create the contact then rather than hoping + it all works out. +- serialize all the ssi information, either via properties for via the + Contact::serialize() method. We need to load it back to support proper auth handling + + +====== Catching up to OscarSocket ===== + +- Fill in all the ICQ user info +- Add preferences for "Requires Auth", "Web Aware", etc. + + +====== Adding new features not in oscarsocket ====== + +Support direct connections +Support file transfers +A bunch of other stuff i'm probably forgetting. + +Add support for the many privacy options OSCAR has + + +====== Left Over from the previous TODO ====== +There is some overlap here, and this is some of the stuff +that was done in oscarsocket, that will need redoing in liboscar + +- general support for SNAC (0x15, *) +- fix adding contacts for both addcontactwizard and serverside list +- support encoding-settings for RTF-messages +- use RTF in outgoing messages +- keepalive for connection to server (icq has ping packets) +- Keep users from adding their own UIN to their userlist +- honor encodings for both sides (I need more knowledge about this!) +- Option: Allow access from contacts on my contact list only +- group handling in general +- error handling on channel 0x04 messages. properly disconnect and emit a + signal in oscarsocket. +- save groupID in KopeteGroups +- somehow sync server and local list, this is not as trivial as everybody + always thinks it is because you cannot sure if local changes or + serverside-changes caused the difference (think about two clients being used + for the same account, one at home and one at work). +- make renaming serverside contacts possible (function is there but fails due + to massive contactlist bugs caused by above mentioned classes) +- support logging in with something different than "online" status for AIM +- finish icq userinfo dialog and sending your own icq userinfo to the server, + it's easy to do but because of the mass of items takes lots of time + and is extremely boring. (requires snac 0x15, * parsing) diff --git a/kopete/protocols/oscar/aim/Makefile.am b/kopete/protocols/oscar/aim/Makefile.am new file mode 100644 index 00000000..91d12552 --- /dev/null +++ b/kopete/protocols/oscar/aim/Makefile.am @@ -0,0 +1,18 @@ +SUBDIRS = ui +METASOURCES = AUTO +AM_CPPFLAGS = -I$(srcdir)/../ \ + -I$(srcdir)/ui/ \ + -I$(top_builddir)/kopete/protocols/oscar/aim/ui \ + -I$(srcdir)/../liboscar \ + $(KOPETE_INCLUDES) $(all_includes) + +kde_module_LTLIBRARIES = kopete_aim.la + +kopete_aim_la_SOURCES = aimprotocol.cpp aimaccount.cpp aimcontact.cpp aimuserinfo.cpp aimjoinchat.cpp aimchatsession.cpp + +kopete_aim_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries) +kopete_aim_la_LIBADD = ../libkopete_oscar.la ui/libkopeteaimui.la \ + $(top_builddir)/kopete/libkopete/libkopete.la + +service_DATA = kopete_aim.desktop aim.protocol +servicedir = $(kde_servicesdir) diff --git a/kopete/protocols/oscar/aim/aim.protocol b/kopete/protocols/oscar/aim/aim.protocol new file mode 100644 index 00000000..ae9f6c69 --- /dev/null +++ b/kopete/protocols/oscar/aim/aim.protocol @@ -0,0 +1,13 @@ +[Protocol] +exec=kopete "%u" +protocol=aim +input=none +output=none +helper=true +listing=false +reading=false +writing=false +makedir=false +deleting=false +URIMode=rawuri +Icon=aim_icon diff --git a/kopete/protocols/oscar/aim/aimaccount.cpp b/kopete/protocols/oscar/aim/aimaccount.cpp new file mode 100644 index 00000000..c6228040 --- /dev/null +++ b/kopete/protocols/oscar/aim/aimaccount.cpp @@ -0,0 +1,924 @@ +/* + aimaccount.cpp - Oscar Protocol Plugin, AIM part + + Kopete (c) 2002-2003 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 <qdom.h> +#include <qfile.h> + +#include <kdebug.h> +#include <kconfig.h> +#include <kdialogbase.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kmessagebox.h> +#include <kmdcodec.h> + +#include "kopeteawayaction.h" +#include "kopetepassword.h" +#include "kopetestdaction.h" +#include "kopeteuiglobal.h" +#include "kopetecontactlist.h" +#include "kopetemetacontact.h" +#include "kopeteprotocol.h" +#include "kopetechatsessionmanager.h" +#include "kopeteview.h" +#include <kopeteuiglobal.h> + +#include "aimprotocol.h" +#include "aimaccount.h" +#include "aimchatsession.h" +#include "aimcontact.h" +#include "aimuserinfo.h" +#include "aimjoinchat.h" +#include "oscarmyselfcontact.h" +#include "oscarvisibilitydialog.h" + +#include "oscarutils.h" +#include "client.h" +#include "ssimanager.h" + + +const DWORD AIM_ONLINE = 0x0; +const DWORD AIM_AWAY = 0x1; + +namespace Kopete { class MetaContact; } + +AIMMyselfContact::AIMMyselfContact( AIMAccount *acct ) +: OscarMyselfContact( acct ) +{ + m_acct = acct; +} + +void AIMMyselfContact::userInfoUpdated() +{ + if ( ( details().userClass() & 32 ) == 0 ) + setOnlineStatus( static_cast<AIMProtocol*>( protocol() )->statusOnline ); //we're online + else + setOnlineStatus( static_cast<AIMProtocol*>( protocol() )->statusAway ); //we're away +} + +void AIMMyselfContact::setOwnProfile( const QString& newProfile ) +{ + m_profileString = newProfile; + if ( m_acct->isConnected() ) + m_acct->engine()->updateProfile( newProfile ); +} + +QString AIMMyselfContact::userProfile() +{ + return m_profileString; +} + +Kopete::ChatSession* AIMMyselfContact::manager( Kopete::Contact::CanCreateFlags canCreate, + Oscar::WORD exchange, const QString& room ) +{ + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << endl; + Kopete::ContactPtrList chatMembers; + chatMembers.append( this ); + Kopete::ChatSession* genericManager = 0L; + genericManager = Kopete::ChatSessionManager::self()->findChatSession( account()->myself(), chatMembers, protocol() ); + AIMChatSession* session = dynamic_cast<AIMChatSession*>( genericManager ); + + if ( !session && canCreate == Contact::CanCreate ) + { + session = new AIMChatSession( this, chatMembers, account()->protocol(), exchange, room ); + session->setEngine( m_acct->engine() ); + + connect( session, SIGNAL( messageSent( Kopete::Message&, Kopete::ChatSession* ) ), + this, SLOT( sendMessage( Kopete::Message&, Kopete::ChatSession* ) ) ); + m_chatRoomSessions.append( session ); + } + return session; +} + +void AIMMyselfContact::chatSessionDestroyed( Kopete::ChatSession* session ) +{ + m_chatRoomSessions.remove( session ); +} + +void AIMMyselfContact::sendMessage( Kopete::Message& message, Kopete::ChatSession* session ) +{ + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "sending a message" << endl; + //TODO: remove duplication - factor into a message utils class or something + Oscar::Message msg; + QString s; + + if (message.plainBody().isEmpty()) // no text, do nothing + return; + //okay, now we need to change the message.escapedBody from real HTML to aimhtml. + //looking right now for docs on that "format". + //looks like everything except for alignment codes comes in the format of spans + + //font-style:italic -> <i> + //font-weight:600 -> <b> (anything > 400 should be <b>, 400 is not bold) + //text-decoration:underline -> <u> + //font-family: -> <font face=""> + //font-size:xxpt -> <font ptsize=xx> + + s=message.escapedBody(); + s.replace ( QRegExp( QString::fromLatin1("<span style=\"([^\"]*)\">([^<]*)</span>")), + QString::fromLatin1("<style>\\1;\"\\2</style>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-style:italic;([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("<i><style>\\1\\2\"\\3</style></i>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-weight:600;([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("<b><style>\\1\\2\"\\3</style></b>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)text-decoration:underline;([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("<u><style>\\1\\2\"\\3</style></u>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-family:([^;]*);([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("<font face=\"\\2\"><style>\\1\\3\"\\4</style></font>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-size:([^p]*)pt;([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("<font ptsize=\"\\2\"><style>\\1\\3\"\\4</style></font>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)color:([^;]*);([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("<font color=\"\\2\"><style>\\1\\3\"\\4</style></font>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("\\2")); + + //okay now change the <font ptsize="xx"> to <font size="xx"> + + //0-9 are size 1 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"\\d\">")), + QString::fromLatin1("<font size=\"1\">")); + //10-11 are size 2 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[01]\">")), + QString::fromLatin1("<font size=\"2\">")); + //12-13 are size 3 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[23]\">")), + QString::fromLatin1("<font size=\"3\">")); + //14-16 are size 4 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[456]\">")), + QString::fromLatin1("<font size=\"4\">")); + //17-22 are size 5 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"(?:1[789]|2[012])\">")), + QString::fromLatin1("<font size=\"5\">")); + //23-29 are size 6 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"2[3456789]\">")),QString::fromLatin1("<font size=\"6\">")); + //30- (and any I missed) are size 7 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"[^\"]*\">")),QString::fromLatin1("<font size=\"7\">")); + + s.replace ( QRegExp ( QString::fromLatin1("<br[ /]*>")), QString::fromLatin1("<br>") ); + + kdDebug(14190) << k_funcinfo << "sending " + << s << endl; + + msg.setSender( contactId() ); + msg.setText( Oscar::Message::UserDefined, s, m_acct->defaultCodec() ); + msg.setTimestamp(message.timestamp()); + msg.setType(0x03); + msg.addProperty( Oscar::Message::ChatRoom ); + + AIMChatSession* aimSession = dynamic_cast<AIMChatSession*>( session ); + if ( !aimSession ) + { + kdWarning(OSCAR_AIM_DEBUG) << "couldn't convert to AIM chat room session!" << endl; + session->messageSucceeded(); + return; + } + msg.setExchange( aimSession->exchange() ); + msg.setChatRoom( aimSession->roomName() ); + + m_acct->engine()->sendMessage( msg ); + //session->appendMessage( message ); + session->messageSucceeded(); +} + + +AIMAccount::AIMAccount(Kopete::Protocol *parent, QString accountID, const char *name) + : OscarAccount(parent, accountID, name, false) +{ + kdDebug(14152) << k_funcinfo << accountID << ": Called."<< endl; + AIMMyselfContact* mc = new AIMMyselfContact( this ); + setMyself( mc ); + myself()->setOnlineStatus( static_cast<AIMProtocol*>( parent )->statusOffline ); + QString profile = configGroup()->readEntry( "Profile", + i18n( "Visit the Kopete website at <a href=\"http://kopete.kde.org\">http://kopete.kde.org</a>") ); + mc->setOwnProfile( profile ); + + m_joinChatDialog = 0; + m_visibilityDialog = 0; + QObject::connect( Kopete::ContactList::self(), + SIGNAL( globalIdentityChanged( const QString&, const QVariant& ) ), + this, + SLOT( slotGlobalIdentityChanged( const QString&, const QVariant& ) ) ); + + QObject::connect( engine(), SIGNAL( chatRoomConnected( WORD, const QString& ) ), + this, SLOT( connectedToChatRoom( WORD, const QString& ) ) ); + + QObject::connect( engine(), SIGNAL( userJoinedChat( Oscar::WORD, const QString&, const QString& ) ), + this, SLOT( userJoinedChat( Oscar::WORD, const QString&, const QString& ) ) ); + + QObject::connect( engine(), SIGNAL( userLeftChat( Oscar::WORD, const QString&, const QString& ) ), + this, SLOT( userLeftChat( Oscar::WORD, const QString&, const QString& ) ) ); + + QObject::connect( this, SIGNAL( buddyIconChanged() ), this, SLOT( slotBuddyIconChanged() ) ); + +} + +AIMAccount::~AIMAccount() +{ +} + +OscarContact *AIMAccount::createNewContact( const QString &contactId, Kopete::MetaContact *parentContact, const SSI& ssiItem ) +{ + AIMContact* contact = new AIMContact( this, contactId, parentContact, QString::null, ssiItem ); + if ( !ssiItem.alias().isEmpty() ) + contact->setProperty( Kopete::Global::Properties::self()->nickName(), ssiItem.alias() ); + + return contact; +} + +QString AIMAccount::sanitizedMessage( const QString& message ) +{ + QDomDocument doc; + QString domError; + int errLine = 0, errCol = 0; + doc.setContent( message, false, &domError, &errLine, &errCol ); + if ( !domError.isEmpty() ) //error parsing, do nothing + { + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "error from dom document conversion: " + << domError << endl; + return message; + } + else + { + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "conversion to dom document successful." + << "looking for font tags" << endl; + QDomNodeList fontTagList = doc.elementsByTagName( "font" ); + if ( fontTagList.count() == 0 ) + { + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "No font tags found. Returning normal message" << endl; + return message; + } + else + { + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Found font tags. Attempting replacement" << endl; + uint numFontTags = fontTagList.count(); + for ( uint i = 0; i < numFontTags; i++ ) + { + QDomNode fontNode = fontTagList.item(i); + QDomElement fontEl; + if ( !fontNode.isNull() && fontNode.isElement() ) + fontEl = fontTagList.item(i).toElement(); + else + continue; + if ( fontEl.hasAttribute( "back" ) ) + { + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Found attribute to replace. Doing replacement" << endl; + QString backgroundColor = fontEl.attribute( "back" ); + backgroundColor.insert( 0, "background-color: " ); + backgroundColor.append( ';' ); + fontEl.setAttribute( "style", backgroundColor ); + fontEl.removeAttribute( "back" ); + } + } + } + } + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "sanitized message is " << doc.toString(); + return doc.toString(); +} + +KActionMenu* AIMAccount::actionMenu() +{ +// kdDebug(14152) << k_funcinfo << accountId() << ": Called." << endl; + // mActionMenu is managed by Kopete::Account. It is deleted when + // it is no longer shown, so we can (safely) just make a new one here. + KActionMenu *mActionMenu = new KActionMenu(accountId(), + myself()->onlineStatus().iconFor( this ), this, "AIMAccount::mActionMenu"); + + AIMProtocol *p = AIMProtocol::protocol(); + + QString accountNick = myself()->property( Kopete::Global::Properties::self()->nickName() ).value().toString(); + mActionMenu->popupMenu()->insertTitle( myself()->onlineStatus().iconFor( myself() ), + i18n( "%2 <%1>" ).arg( accountId(), accountNick )); + + mActionMenu->insert( new KAction( i18n("Online"), p->statusOnline.iconFor( this ), 0, this, + SLOT( slotGoOnline() ), mActionMenu, "AIMAccount::mActionOnline") ); + + KAction* mActionAway = new Kopete::AwayAction(i18n("Away"), p->statusAway.iconFor( this ), 0, this, + SLOT(slotGoAway( const QString & )), this, "AIMAccount::mActionNA" ); + mActionAway->setEnabled( isConnected() ); + mActionMenu->insert( mActionAway ); + + KAction* mActionOffline = new KAction( i18n("Offline"), p->statusOffline.iconFor(this), 0, this, + SLOT( slotGoOffline() ), mActionMenu, "AIMAccount::mActionOffline"); + + mActionMenu->insert( mActionOffline ); + mActionMenu->popupMenu()->insertSeparator(); + + KAction* m_joinChatAction = new KAction( i18n( "Join Chat..." ), QString::null, 0, this, + SLOT( slotJoinChat() ), mActionMenu, "join_a_chat" ); + + mActionMenu->insert( new KToggleAction( i18n( "Set Visibility..." ), 0, 0, + this, SLOT( slotSetVisiblility() ), this, + "AIMAccount::mActionSetVisibility") ); + + mActionMenu->insert( m_joinChatAction ); + + KAction* m_editInfoAction = new KAction( i18n( "Edit User Info..." ), "identity", 0, + this, SLOT( slotEditInfo() ), mActionMenu, "actionEditInfo"); + + mActionMenu->insert( m_editInfoAction ); + + return mActionMenu; +} + +void AIMAccount::setAway(bool away, const QString &awayReason) +{ +// kdDebug(14152) << k_funcinfo << accountId() << "reason is " << awayReason << endl; + if ( away ) + { + engine()->setStatus( Client::Away, awayReason ); + AIMMyselfContact* me = static_cast<AIMMyselfContact *> ( myself() ); + me->setLastAwayMessage(awayReason); + me->setProperty( Kopete::Global::Properties::self()->awayMessage(), awayReason ); + } + else + { + engine()->setStatus( Client::Online ); + AIMMyselfContact* me = static_cast<AIMMyselfContact *> ( myself() ); + me->setLastAwayMessage(QString::null); + me->removeProperty( Kopete::Global::Properties::self()->awayMessage() ); + } +} + +void AIMAccount::setOnlineStatus( const Kopete::OnlineStatus& status, const QString& reason ) +{ + kdDebug(14152) << k_funcinfo << "called with reason = " << reason <<" status = "<< status.status() << endl;; + if ( status.status() == Kopete::OnlineStatus::Online ) + setAway( false ); + if ( status.status() == Kopete::OnlineStatus::Away ) + setAway( true, reason ); +} + + +void AIMAccount::setUserProfile(const QString &profile) +{ + kdDebug(14152) << k_funcinfo << "called." << endl; + AIMMyselfContact* aimmc = dynamic_cast<AIMMyselfContact*>( myself() ); + if ( aimmc ) + aimmc->setOwnProfile( profile ); + configGroup()->writeEntry( QString::fromLatin1( "Profile" ), profile ); +} + +void AIMAccount::slotEditInfo() +{ + if ( !isConnected() ) + { + KMessageBox::sorry( Kopete::UI::Global::mainWidget(), + i18n( "Editing your user info is not possible because " + "you are not connected." ), + i18n( "Unable to edit user info" ) ); + return; + } + AIMUserInfoDialog *myInfo = new AIMUserInfoDialog(static_cast<AIMContact *>( myself() ), this, true, 0L, "myInfo"); + myInfo->exec(); // This is a modal dialog +} + +void AIMAccount::slotGlobalIdentityChanged( const QString& key, const QVariant& value ) +{ + //do something with the photo + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Global identity changed" << endl; + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "key: " << key << endl; + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "value: " << value << endl; + + if( !configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) ) + { + if ( key == Kopete::Global::Properties::self()->nickName().key() ) + { + //edit ssi item to change alias (if possible) + } + + if ( key == Kopete::Global::Properties::self()->photo().key() ) + { + setBuddyIcon( value.toString() ); + } + } +} + +void AIMAccount::slotBuddyIconChanged() +{ + // need to disconnect because we could end up with many connections + QObject::disconnect( engine(), SIGNAL( iconServerConnected() ), this, SLOT( slotBuddyIconChanged() ) ); + if ( !engine()->isActive() ) + { + QObject::connect( engine(), SIGNAL( iconServerConnected() ), this, SLOT( slotBuddyIconChanged() ) ); + return; + } + + QString photoPath = myself()->property( Kopete::Global::Properties::self()->photo() ).value().toString(); + + SSIManager* ssi = engine()->ssiManager(); + Oscar::SSI item = ssi->findItemForIconByRef( 1 ); + + if ( photoPath.isEmpty() ) + { + if ( item ) + { + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Removing icon hash item from ssi" << endl; + Oscar::SSI s(item); + + //remove hash and alias + QValueList<TLV> tList( item.tlvList() ); + TLV t = Oscar::findTLV( tList, 0x00D5 ); + if ( t ) + tList.remove( t ); + + item.setTLVList( tList ); + //s is old, item is new. modification will occur + engine()->modifySSIItem( s, item ); + } + } + else + { + QFile iconFile( photoPath ); + iconFile.open( IO_ReadOnly ); + + KMD5 iconHash; + iconHash.update( iconFile ); + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "hash is :" << iconHash.hexDigest() << endl; + + //find old item, create updated item + if ( !item ) + { + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "no existing icon hash item in ssi. creating new" << endl; + + TLV t; + t.type = 0x00D5; + t.data.resize( 18 ); + t.data[0] = 0x00; + t.data[1] = 0x10; + memcpy(t.data.data() + 2, iconHash.rawDigest(), 16); + t.length = t.data.size(); + + QValueList<Oscar::TLV> list; + list.append( t ); + + Oscar::SSI s( "1", 0, ssi->nextContactId(), ROSTER_BUDDYICONS, list ); + + //item is a non-valid ssi item, so the function will add an item + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "setting new icon item" << endl; + engine()->modifySSIItem( item, s ); + } + else + { //found an item + Oscar::SSI s(item); + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "modifying old item in ssi." << endl; + QValueList<TLV> tList( item.tlvList() ); + + TLV t = Oscar::findTLV( tList, 0x00D5 ); + if ( t ) + tList.remove( t ); + else + t.type = 0x00D5; + + t.data.resize( 18 ); + t.data[0] = 0x00; + t.data[1] = 0x10; + memcpy(t.data.data() + 2, iconHash.rawDigest(), 16); + t.length = t.data.size(); + tList.append( t ); + + item.setTLVList( tList ); + //s is old, item is new. modification will occur + engine()->modifySSIItem( s, item ); + } + iconFile.close(); + } +} + +void AIMAccount::slotJoinChat() +{ + if ( !isConnected() ) + { + KMessageBox::sorry( Kopete::UI::Global::mainWidget(), + i18n( "Joining an AIM chat room is not possible because " + "you are not connected." ), + i18n( "Unable to Join AIM Chat Room" ) ); + return; + } + + //get the exchange info + //create the dialog + //join the chat room + if ( !m_joinChatDialog ) + { + m_joinChatDialog = new AIMJoinChatUI( this, false, Kopete::UI::Global::mainWidget() ); + QObject::connect( m_joinChatDialog, SIGNAL( closing( int ) ), + this, SLOT( joinChatDialogClosed( int ) ) ); + QValueList<int> list = engine()->chatExchangeList(); + m_joinChatDialog->setExchangeList( list ); + m_joinChatDialog->show(); + } + else + m_joinChatDialog->raise(); +} + +void AIMAccount::slotGoOnline() +{ + if ( myself()->onlineStatus().status() == Kopete::OnlineStatus::Away ) + { + kdDebug(14152) << k_funcinfo << accountId() << " was away. welcome back." << endl; + engine()->setStatus( Client::Online ); + myself()->removeProperty( Kopete::Global::Properties::self()->awayMessage() ); + } + else if ( myself()->onlineStatus().status() == Kopete::OnlineStatus::Offline ) + { + kdDebug(14152) << k_funcinfo << accountId() << " was offline. time to connect" << endl; + OscarAccount::connect(); + } + else + { + kdDebug(14152) << k_funcinfo << accountId() << " is already online, doing nothing" << endl; + } +} + +void AIMAccount::slotGoAway(const QString &message) +{ + kdDebug(14152) << k_funcinfo << message << endl; + setAway(true, message); +} + +void AIMAccount::joinChatDialogClosed( int code ) +{ + if ( code == QDialog::Accepted ) + { + //join the chat + kdDebug(14152) << k_funcinfo << "chat accepted." << endl; + engine()->joinChatRoom( m_joinChatDialog->roomName(), + m_joinChatDialog->exchange().toInt() ); + } + + m_joinChatDialog->delayedDestruct(); + m_joinChatDialog = 0L; +} + +void AIMAccount::loginActions() +{ + OscarAccount::loginActions(); + + using namespace AIM::PrivacySettings; + int privacySetting = this->configGroup()->readNumEntry( "PrivacySetting", AllowAll ); + this->setPrivacySettings( privacySetting ); +} + +void AIMAccount::disconnected( DisconnectReason reason ) +{ + kdDebug( OSCAR_AIM_DEBUG ) << k_funcinfo << "Attempting to set status offline" << endl; + myself()->setOnlineStatus( static_cast<AIMProtocol*>( protocol() )->statusOffline ); + + QDictIterator<Kopete::Contact> it( contacts() ); + for( ; it.current(); ++it ) + { + OscarContact* oc = dynamic_cast<OscarContact*>( it.current() ); + if ( oc ) + oc->setOnlineStatus( static_cast<AIMProtocol*>( protocol() )->statusOffline ); + } + + OscarAccount::disconnected( reason ); +} + +void AIMAccount::messageReceived( const Oscar::Message& message ) +{ + kdDebug(14152) << k_funcinfo << " Got a message, calling OscarAccount::messageReceived" << endl; + // Want to call the parent to do everything else + if ( message.type() != 0x0003 ) + { + OscarAccount::messageReceived(message); + + // Check to see if our status is away, and send an away message + // Might be duplicate code from the parent class to get some needed information + // Perhaps a refactoring is needed. + kdDebug(14152) << k_funcinfo << "Checking to see if I'm online.." << endl; + if( myself()->onlineStatus().status() == Kopete::OnlineStatus::Away ) + { + QString sender = Oscar::normalize( message.sender() ); + AIMContact* aimSender = static_cast<AIMContact *> ( contacts()[sender] ); //should exist now + if ( !aimSender ) + { + kdWarning(OSCAR_RAW_DEBUG) << "For some reason, could not get the contact " + << "That this message is from: " << message.sender() << ", Discarding message" << endl; + return; + } + // Create, or get, a chat session with the contact + Kopete::ChatSession* chatSession = aimSender->manager( Kopete::Contact::CanCreate ); + + // get the away message we have set + AIMMyselfContact* myContact = static_cast<AIMMyselfContact *> ( myself() ); + QString msg = myContact->lastAwayMessage(); + kdDebug(14152) << k_funcinfo << "Got away message: " << msg << endl; + // Create the message + Kopete::Message chatMessage( myself(), aimSender, msg, Kopete::Message::Outbound, + Kopete::Message::RichText ); + kdDebug(14152) << k_funcinfo << "Sending autoresponse" << endl; + // Send the message + aimSender->sendAutoResponse( chatMessage ); + } + } + + if ( message.type() == 0x0003 ) + { + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "have chat message" << endl; + //handle chat room messages seperately + QValueList<Kopete::ChatSession*> chats = Kopete::ChatSessionManager::self()->sessions(); + QValueList<Kopete::ChatSession*>::iterator it, itEnd = chats.end(); + for ( it = chats.begin(); it != itEnd; ++it ) + { + Kopete::ChatSession* kcs = ( *it ); + AIMChatSession* session = dynamic_cast<AIMChatSession*>( kcs ); + if ( !session ) + continue; + + if ( session->exchange() == message.exchange() && + Oscar::normalize( session->roomName() ) == + Oscar::normalize( message.chatRoom() ) ) + { + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "found chat session for chat room" << endl; + Kopete::Contact* ocSender = contacts()[Oscar::normalize( message.sender() )]; + //sanitize; + QString sanitizedMsg = sanitizedMessage( message.text( defaultCodec() ) ); + + Kopete::ContactPtrList me; + me.append( myself() ); + Kopete::Message chatMessage( message.timestamp(), ocSender, me, sanitizedMsg, + Kopete::Message::Inbound, Kopete::Message::RichText ); + + session->appendMessage( chatMessage ); + } + } + } +} + +void AIMAccount::connectedToChatRoom( WORD exchange, const QString& room ) +{ + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Creating chat room session" << endl; + Kopete::ContactPtrList emptyList; + AIMMyselfContact* me = static_cast<AIMMyselfContact*>( myself() ); + AIMChatSession* session = dynamic_cast<AIMChatSession*>( me->manager( Kopete::Contact::CanCreate, + exchange, room ) ); + session->setDisplayName( room ); + if ( session->view( true ) ) + session->raiseView(); +} + +void AIMAccount::userJoinedChat( WORD exchange, const QString& room, const QString& contact ) +{ + if ( Oscar::normalize( contact ) == Oscar::normalize( myself()->contactId() ) ) + return; + + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "user " << contact << " has joined the chat" << endl; + QValueList<Kopete::ChatSession*> chats = Kopete::ChatSessionManager::self()->sessions(); + QValueList<Kopete::ChatSession*>::iterator it, itEnd = chats.end(); + for ( it = chats.begin(); it != itEnd; ++it ) + { + Kopete::ChatSession* kcs = ( *it ); + AIMChatSession* session = dynamic_cast<AIMChatSession*>( kcs ); + if ( !session ) + continue; + + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << session->exchange() << " " << exchange << endl; + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << session->roomName() << " " << room << endl; + if ( session->exchange() == exchange && session->roomName() == room ) + { + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "found correct chat session" << endl; + Kopete::Contact* c; + if ( contacts()[Oscar::normalize( contact )] ) + c = contacts()[Oscar::normalize( contact )]; + else + { + Kopete::MetaContact* mc = addContact( Oscar::normalize( contact ), + contact, 0, Kopete::Account::Temporary ); + if ( !mc ) + kdWarning(OSCAR_AIM_DEBUG) << "Unable to add contact for chat room" << endl; + + c = mc->contacts().first(); + c->setNickName( contact ); + } + + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "adding contact" << endl; + session->addContact( c, static_cast<AIMProtocol*>( protocol() )->statusOnline, true /* suppress */ ); + } + } +} + +void AIMAccount::userLeftChat( WORD exchange, const QString& room, const QString& contact ) +{ + if ( Oscar::normalize( contact ) == Oscar::normalize( myself()->contactId() ) ) + return; + + QValueList<Kopete::ChatSession*> chats = Kopete::ChatSessionManager::self()->sessions(); + QValueList<Kopete::ChatSession*>::iterator it, itEnd = chats.end(); + for ( it = chats.begin(); it != itEnd; ++it ) + { + Kopete::ChatSession* kcs = ( *it ); + AIMChatSession* session = dynamic_cast<AIMChatSession*>( kcs ); + if ( !session ) + continue; + + if ( session->exchange() == exchange && session->roomName() == room ) + { + //delete temp contact + Kopete::Contact* c = contacts()[Oscar::normalize( contact )]; + if ( !c ) + { + kdWarning(OSCAR_AIM_DEBUG) << k_funcinfo << "couldn't find the contact that's left the chat!" << endl; + continue; + } + session->removeContact( c ); + Kopete::MetaContact* mc = c->metaContact(); + if ( mc->isTemporary() ) + { + mc->removeContact( c ); + delete c; + delete mc; + } + } + } +} + + +void AIMAccount::connectWithPassword( const QString & ) +{ + kdDebug(14152) << k_funcinfo << "accountId='" << accountId() << "'" << endl; + + // Get the screen name for this account + QString screenName = accountId(); + QString server = configGroup()->readEntry( "Server", QString::fromLatin1( "login.oscar.aol.com" ) ); + uint port = configGroup()->readNumEntry( "Port", 5190 ); + + Connection* c = setupConnection( server, port ); + + QString _password = password().cachedValue(); + if ( _password.isEmpty() ) + { + kdDebug(14150) << "Kopete is unable to attempt to sign-on to the " + << "AIM network because no password was specified in the " + << "preferences." << endl; + } + else if ( myself()->onlineStatus() == static_cast<AIMProtocol*>( protocol() )->statusOffline ) + { + kdDebug(14152) << k_funcinfo << "Logging in as " << accountId() << endl ; + updateVersionUpdaterStamp(); + engine()->start( server, port, accountId(), _password ); + engine()->connectToServer( c, server, true /* doAuth */ ); + myself()->setOnlineStatus( static_cast<AIMProtocol*>( protocol() )->statusConnecting ); + } +} + +void AIMAccount::slotSetVisiblility() +{ + if( !isConnected() ) + { + KMessageBox::sorry( Kopete::UI::Global::mainWidget(), + i18n("You must be online to set users visibility."), + i18n("ICQ Plugin") ); + return; + } + + if ( !m_visibilityDialog ) + { + m_visibilityDialog = new OscarVisibilityDialog( engine(), Kopete::UI::Global::mainWidget() ); + QObject::connect( m_visibilityDialog, SIGNAL( closing() ), + this, SLOT( slotVisibilityDialogClosed() ) ); + + //add all contacts; + OscarVisibilityDialog::ContactMap contactMap; + QMap<QString, QString> revContactMap; + + QValueList<Oscar::SSI> contactList = engine()->ssiManager()->contactList(); + QValueList<Oscar::SSI>::const_iterator it, cEnd = contactList.constEnd(); + + for ( it = contactList.constBegin(); it != cEnd; ++it ) + { + QString contactId = ( *it ).name(); + + OscarContact* oc = dynamic_cast<OscarContact*>( contacts()[( *it ).name()] ); + if ( oc ) + { + contactMap.insert( oc->nickName(), contactId ); + revContactMap.insert( contactId, oc->nickName() ); + } + else + { + contactMap.insert( contactId, contactId ); + revContactMap.insert( contactId, contactId ); + } + } + m_visibilityDialog->addContacts( contactMap ); + + //add contacts from visible list + QStringList tmpList; + + contactList = engine()->ssiManager()->visibleList(); + cEnd = contactList.constEnd(); + + for ( it = contactList.constBegin(); it != cEnd; ++it ) + tmpList.append( revContactMap[( *it ).name()] ); + + m_visibilityDialog->addVisibleContacts( tmpList ); + + //add contacts from invisible list + tmpList.clear(); + + contactList = engine()->ssiManager()->invisibleList(); + cEnd = contactList.constEnd(); + + for ( it = contactList.constBegin(); it != cEnd; ++it ) + tmpList.append( revContactMap[( *it ).name()] ); + + m_visibilityDialog->addInvisibleContacts( tmpList ); + + m_visibilityDialog->resize( 550, 350 ); + m_visibilityDialog->show(); + } + else + { + m_visibilityDialog->raise(); + } +} + +void AIMAccount::slotVisibilityDialogClosed() +{ + m_visibilityDialog->delayedDestruct(); + m_visibilityDialog = 0L; +} + +void AIMAccount::setPrivacySettings( int privacy ) +{ + using namespace AIM::PrivacySettings; + + BYTE privacyByte = 0x01; + DWORD userClasses = 0xFFFFFFFF; + + switch ( privacy ) + { + case AllowAll: + privacyByte = 0x01; + break; + case BlockAll: + privacyByte = 0x02; + break; + case AllowPremitList: + privacyByte = 0x03; + break; + case BlockDenyList: + privacyByte = 0x04; + break; + case AllowMyContacts: + privacyByte = 0x05; + break; + case BlockAIM: + privacyByte = 0x01; + userClasses = 0x00000004; + break; + } + + this->setPrivacyTLVs( privacyByte, userClasses ); +} + +void AIMAccount::setPrivacyTLVs( BYTE privacy, DWORD userClasses ) +{ + SSIManager* ssi = engine()->ssiManager(); + Oscar::SSI item = ssi->findItem( QString::null, ROSTER_VISIBILITY ); + + QValueList<Oscar::TLV> tList; + + tList.append( TLV( 0x00CA, 1, (char *)&privacy ) ); + tList.append( TLV( 0x00CB, sizeof(userClasses), (char *)&userClasses ) ); + + if ( !item ) + { + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Adding new privacy TLV item" << endl; + Oscar::SSI s( QString::null, 0, ssi->nextContactId(), ROSTER_VISIBILITY, tList ); + engine()->modifySSIItem( item, s ); + } + else + { //found an item + Oscar::SSI s(item); + + if ( Oscar::uptateTLVs( s, tList ) == true ) + { + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Updating privacy TLV item" << endl; + engine()->modifySSIItem( item, s ); + } + } +} + +#include "aimaccount.moc" +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/aim/aimaccount.h b/kopete/protocols/oscar/aim/aimaccount.h new file mode 100644 index 00000000..034b9836 --- /dev/null +++ b/kopete/protocols/oscar/aim/aimaccount.h @@ -0,0 +1,146 @@ +/* + AIMAccount - Oscar Protocol Account + + Copyright (c) 2002 by Chris TenHarmsel <tenharmsel@staticmethod.net> + + Kopete (c) 2002 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. * + * * + ************************************************************************* + +*/ + +#ifndef AIMACCOUNT_H +#define AIMACCOUNT_H + +#include <qdict.h> +#include <qstring.h> +#include <qwidget.h> +#include "oscartypeclasses.h" + +#include "oscaraccount.h" +#include "oscarmyselfcontact.h" + +namespace AIM +{ + namespace PrivacySettings + { + enum { AllowAll = 0, AllowMyContacts, AllowPremitList, BlockAll, BlockAIM, BlockDenyList }; + } +} + +namespace Kopete +{ +class Contact; +class Group; +class ChatSession; +} + +class KAction; +class OscarContact; +class AIMContact; +class AIMAccount; +class AIMJoinChatUI; +class AIMChatSession; +class OscarVisibilityDialog; + +class AIMMyselfContact : public OscarMyselfContact +{ +Q_OBJECT +public: + AIMMyselfContact( AIMAccount *acct ); + void userInfoUpdated(); + void setOwnProfile( const QString& newProfile ); + QString userProfile(); + void setLastAwayMessage( const QString& msg) {m_lastAwayMessage = msg;} + QString lastAwayMessage() { return m_lastAwayMessage; }; + + virtual Kopete::ChatSession* manager( Kopete::Contact::CanCreateFlags = Kopete::Contact::CannotCreate, + WORD exchange = 0, const QString& room = QString::null); + +public slots: + void sendMessage( Kopete::Message&, Kopete::ChatSession* session ); + void chatSessionDestroyed( Kopete::ChatSession* ); + +private: + QString m_profileString; + AIMAccount* m_acct; + /** + * There has GOT to be a better way to get this away message + */ + QString m_lastAwayMessage; + QValueList<Kopete::ChatSession*> m_chatRoomSessions; + + +}; + +class AIMAccount : public OscarAccount +{ +Q_OBJECT + +public: + AIMAccount(Kopete::Protocol *parent, QString accountID, const char *name=0L); + virtual ~AIMAccount(); + + // Accessor method for the action menu + virtual KActionMenu* actionMenu(); + + void setAway(bool away, const QString &awayReason = QString::null ); + + virtual void connectWithPassword( const QString &password ); + + void setUserProfile(const QString &profile); + + void setPrivacySettings( int privacy ); + +public slots: + /** Reimplementation from Kopete::Account */ + void setOnlineStatus( const Kopete::OnlineStatus& status, const QString& reason = QString::null ); + void slotEditInfo(); + void slotGoOnline(); + + void slotGlobalIdentityChanged( const QString&, const QVariant& ); + void slotBuddyIconChanged(); + + void slotJoinChat(); + +protected slots: + void slotGoAway(const QString&); + void joinChatDialogClosed( int ); + + virtual void loginActions(); + virtual void disconnected( Kopete::Account::DisconnectReason reason ); + virtual void messageReceived( const Oscar::Message& message ); + + void connectedToChatRoom( WORD exchange, const QString& roomName ); + void userJoinedChat( Oscar::WORD exchange, const QString& room, const QString& contact ); + void userLeftChat( Oscar::WORD exchange, const QString& room, const QString& contact ); + + void slotSetVisiblility(); + void slotVisibilityDialogClosed(); + +protected: + + /** + * Implement virtual method from OscarAccount + * This allows OscarAccount to take care of adding new contacts + */ + OscarContact *createNewContact( const QString &contactId, Kopete::MetaContact *parentContact, const SSI& ssiItem ); + + QString sanitizedMessage( const QString& message ); + +private: + // Set privacy tlv item + void setPrivacyTLVs( BYTE privacy, DWORD userClasses ); + + AIMJoinChatUI* m_joinChatDialog; + OscarVisibilityDialog* m_visibilityDialog; +}; +#endif +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/aim/aimchatsession.cpp b/kopete/protocols/oscar/aim/aimchatsession.cpp new file mode 100644 index 00000000..fa0616a6 --- /dev/null +++ b/kopete/protocols/oscar/aim/aimchatsession.cpp @@ -0,0 +1,73 @@ +// aimchatsession.cpp + +// Copyright (C) 2005 Matt Rogers <mattr@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. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA +// 02110-1301, USA. + + +#include "aimchatsession.h" +#include "kopetecontact.h" +#include "kopetechatsessionmanager.h" +#include "kopeteprotocol.h" +#include "client.h" + +AIMChatSession::AIMChatSession( const Kopete::Contact* user, Kopete::ContactPtrList others, + Kopete::Protocol* protocol, Oscar::WORD exchange, + const QString& room ) + + : Kopete::ChatSession( user, others, protocol, "AIMChatSession" ) +{ + Kopete::ChatSessionManager::self()->registerChatSession( this ); + setInstance( protocol->instance() ); + setMayInvite( false ); + m_exchange = exchange; + m_roomName = room; + m_engine = 0; +} + +AIMChatSession::~AIMChatSession() +{ + m_engine->disconnectChatRoom( m_exchange, m_roomName ); +} + +void AIMChatSession::setEngine( Client* engine ) +{ + m_engine = engine; +} + +QString AIMChatSession::roomName() const +{ + + return m_roomName; +} + +void AIMChatSession::setRoomName( const QString& room ) +{ + m_roomName = room; +} + +Oscar::WORD AIMChatSession::exchange() const +{ + return m_exchange; +} + +void AIMChatSession::setExchange( Oscar::WORD exchange ) +{ + m_exchange = exchange; +} + + +#include "aimchatsession.moc" diff --git a/kopete/protocols/oscar/aim/aimchatsession.h b/kopete/protocols/oscar/aim/aimchatsession.h new file mode 100644 index 00000000..79c0685e --- /dev/null +++ b/kopete/protocols/oscar/aim/aimchatsession.h @@ -0,0 +1,77 @@ +// aimchatsession.h +// Copyright (C) 2005 Matt Rogers <mattr@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. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef AIMCHATSESSION_H +#define AIMCHATSESSION_H + +#include "kopetechatsession.h" +#include "oscartypes.h" + +class Client; + +class AIMChatSession : public Kopete::ChatSession +{ +Q_OBJECT +public: + AIMChatSession( const Kopete::Contact* contact, Kopete::ContactPtrList others, + Kopete::Protocol* protocol, Oscar::WORD exchange = 0, + const QString& room = QString::null ); + virtual ~AIMChatSession(); + + /** + * Set the engine to use so that we can disconnect from the chat service + * properly + */ + void setEngine( Client* engine ); + + /** + * Get the name of the AIM chat room represented by + * this ChatSession object + * @return the name of the chat room + */ + QString roomName() const; + + /** + * Set the name of the AIM chat room represented by + * this ChatSession object + * @param room the name of the AIM chat room + */ + void setRoomName( const QString& room ); + + /** + * Get the exchange of the AIM chat room represented by + * this ChatSession object + * @return the exchange of the chat room + */ + Oscar::WORD exchange() const; + + /** + * Set the exchange of the AIM chat room represented by + * this ChatSession object + * @param exchange the exchange of the AIM chat room + */ + void setExchange( Oscar::WORD exchange ); + +private: + QString m_roomName; + Oscar::WORD m_exchange; + Client* m_engine; +}; + + +#endif diff --git a/kopete/protocols/oscar/aim/aimcontact.cpp b/kopete/protocols/oscar/aim/aimcontact.cpp new file mode 100644 index 00000000..7e46c585 --- /dev/null +++ b/kopete/protocols/oscar/aim/aimcontact.cpp @@ -0,0 +1,517 @@ +/* + aimcontact.cpp - Oscar Protocol Plugin + + Copyright (c) 2003 by Will Stephenson + 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 <time.h> + +#include <qimage.h> +#include <qregexp.h> +#include <qtimer.h> +#include <qtextcodec.h> + +#include <kapplication.h> +#include <kactionclasses.h> +#include <klocale.h> +#include <kdebug.h> +#include <kmessagebox.h> + +#include "kopeteaway.h" +#include "kopetechatsession.h" +#include "kopeteuiglobal.h" +#include "kopetemetacontact.h" + +//liboscar +#include "client.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "ssimanager.h" + +#include "aimprotocol.h" +#include "aimuserinfo.h" +#include "aimcontact.h" +#include "aimaccount.h" + +AIMContact::AIMContact( Kopete::Account* account, const QString& name, Kopete::MetaContact* parent, + const QString& icon, const Oscar::SSI& ssiItem ) +: OscarContact(account, name, parent, icon, ssiItem ) +{ + mProtocol=static_cast<AIMProtocol *>(protocol()); + setOnlineStatus( mProtocol->statusOffline ); + + m_infoDialog = 0L; + m_warnUserAction = 0L; + mUserProfile=""; + m_haveAwayMessage = false; + m_mobile = false; + // Set the last autoresponse time to the current time yesterday + m_lastAutoresponseTime = QDateTime::currentDateTime().addDays(-1); + + QObject::connect( mAccount->engine(), SIGNAL( receivedUserInfo( const QString&, const UserDetails& ) ), + this, SLOT( userInfoUpdated( const QString&, const UserDetails& ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( userIsOffline( const QString& ) ), + this, SLOT( userOffline( const QString& ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( receivedAwayMessage( const QString&, const QString& ) ), + this, SLOT( updateAwayMessage( const QString&, const QString& ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( receivedProfile( const QString&, const QString& ) ), + this, SLOT( updateProfile( const QString&, const QString& ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( userWarned( const QString&, Q_UINT16, Q_UINT16 ) ), + this, SLOT( gotWarning( const QString&, Q_UINT16, Q_UINT16 ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( haveIconForContact( const QString&, QByteArray ) ), + this, SLOT( haveIcon( const QString&, QByteArray ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( iconServerConnected() ), + this, SLOT( requestBuddyIcon() ) ); + QObject::connect( this, SIGNAL( featuresUpdated() ), this, SLOT( updateFeatures() ) ); +} + +AIMContact::~AIMContact() +{ +} + +bool AIMContact::isReachable() +{ + return true; +} + +QPtrList<KAction> *AIMContact::customContextMenuActions() +{ + + QPtrList<KAction> *actionCollection = new QPtrList<KAction>(); + if ( !m_warnUserAction ) + { + m_warnUserAction = new KAction( i18n( "&Warn User" ), 0, this, SLOT( warnUser() ), this, "warnAction" ); + } + m_actionVisibleTo = new KToggleAction(i18n("Always &Visible To"), "", 0, + this, SLOT(slotVisibleTo()), this, "actionVisibleTo"); + m_actionInvisibleTo = new KToggleAction(i18n("Always &Invisible To"), "", 0, + this, SLOT(slotInvisibleTo()), this, "actionInvisibleTo"); + + bool on = account()->isConnected(); + + m_warnUserAction->setEnabled( on ); + + m_actionVisibleTo->setEnabled(on); + m_actionInvisibleTo->setEnabled(on); + + SSIManager* ssi = account()->engine()->ssiManager(); + m_actionVisibleTo->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_VISIBLE )); + m_actionInvisibleTo->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_INVISIBLE )); + + actionCollection->append( m_warnUserAction ); + + actionCollection->append(m_actionVisibleTo); + actionCollection->append(m_actionInvisibleTo); + + + return actionCollection; +} + +const QString AIMContact::awayMessage() +{ + return property(mProtocol->awayMessage).value().toString(); +} + +void AIMContact::setAwayMessage(const QString &message) +{ + kdDebug(14152) << k_funcinfo << + "Called for '" << contactId() << "', away msg='" << message << "'" << endl; + QString filteredMessage = message; + filteredMessage.replace( + QRegExp(QString::fromLatin1("<[hH][tT][mM][lL].*>(.*)</[hH][tT][mM][lL]>")), + QString::fromLatin1("\\1")); + filteredMessage.replace( + QRegExp(QString::fromLatin1("<[bB][oO][dD][yY].*>(.*)</[bB][oO][dD][yY]>")), + QString::fromLatin1("\\1") ); + QRegExp fontRemover( QString::fromLatin1("<[fF][oO][nN][tT].*>(.*)</[fF][oO][nN][tT]>") ); + fontRemover.setMinimal(true); + while ( filteredMessage.find( fontRemover ) != -1 ) + filteredMessage.replace( fontRemover, QString::fromLatin1("\\1") ); + setProperty(mProtocol->awayMessage, filteredMessage); +} + +int AIMContact::warningLevel() const +{ + return m_warningLevel; +} + +void AIMContact::updateSSIItem() +{ + if ( m_ssiItem.type() != 0xFFFF && m_ssiItem.waitingAuth() == false && + onlineStatus() == Kopete::OnlineStatus::Unknown ) + { + //make sure they're offline + setOnlineStatus( static_cast<AIMProtocol*>( protocol() )->statusOffline ); + } +} + +void AIMContact::slotUserInfo() +{ + if ( !m_infoDialog) + { + m_infoDialog = new AIMUserInfoDialog( this, static_cast<AIMAccount*>( account() ), false, Kopete::UI::Global::mainWidget(), 0 ); + if( !m_infoDialog ) + return; + connect( m_infoDialog, SIGNAL( finished() ), this, SLOT( closeUserInfoDialog() ) ); + m_infoDialog->show(); + if ( mAccount->isConnected() ) + { + mAccount->engine()->requestAIMProfile( contactId() ); + mAccount->engine()->requestAIMAwayMessage( contactId() ); + } + } + else + m_infoDialog->raise(); +} + +void AIMContact::userInfoUpdated( const QString& contact, const UserDetails& details ) +{ + if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) ) + return; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << endl; + + //if they don't have an SSI alias, make sure we use the capitalization from the + //server so their contact id looks all pretty. + QString nickname = property( Kopete::Global::Properties::self()->nickName() ).value().toString(); + if ( nickname.isEmpty() || Oscar::normalize( nickname ) == Oscar::normalize( contact ) ) + setNickName( contact ); + + ( details.userClass() & CLASS_WIRELESS ) ? m_mobile = true : m_mobile = false; + + if ( ( details.userClass() & CLASS_AWAY ) == STATUS_ONLINE ) + { + if ( m_mobile ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact: " << contact << " is mobile-online." << endl; + setOnlineStatus( mProtocol->statusWirelessOnline ); + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact: " << contact << " is online." << endl; + setOnlineStatus( mProtocol->statusOnline ); //we're online + } + removeProperty( mProtocol->awayMessage ); + m_haveAwayMessage = false; + } + else if ( ( details.userClass() & CLASS_AWAY ) ) // STATUS_AWAY + { + if ( m_mobile ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact: " << contact << " is mobile-away." << endl; + setOnlineStatus( mProtocol->statusWirelessOnline ); + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact: " << contact << " is away." << endl; + setOnlineStatus( mProtocol->statusAway ); //we're away + } + if ( !m_haveAwayMessage ) //prevent cyclic away message requests + { + mAccount->engine()->requestAIMAwayMessage( contactId() ); + m_haveAwayMessage = true; + } + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact: " << contact << " class " << details.userClass() << " is unhandled... defaulting to away." << endl; + setOnlineStatus( mProtocol->statusAway ); //we're away + if ( !m_haveAwayMessage ) //prevent cyclic away message requests + { + mAccount->engine()->requestAIMAwayMessage( contactId() ); + m_haveAwayMessage = true; + } + } + + if ( details.buddyIconHash().size() > 0 && details.buddyIconHash() != m_details.buddyIconHash() ) + { + if ( !mAccount->engine()->hasIconConnection() ) + mAccount->engine()->requestServerRedirect( 0x0010 ); + + int time = ( KApplication::random() % 10 ) * 1000; + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "updating buddy icon in " << time/1000 << " seconds" << endl; + QTimer::singleShot( time, this, SLOT( requestBuddyIcon() ) ); + } + + OscarContact::userInfoUpdated( contact, details ); +} + +void AIMContact::userOnline( const QString& userId ) +{ + if ( Oscar::normalize( userId ) == Oscar::normalize( contactId() ) ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Getting more contact info" << endl; + setOnlineStatus( mProtocol->statusOnline ); + } +} + +void AIMContact::userOffline( const QString& userId ) +{ + if ( Oscar::normalize( userId ) == Oscar::normalize( contactId() ) ) + { + setOnlineStatus( mProtocol->statusOffline ); + removeProperty( mProtocol->awayMessage ); + } +} + +void AIMContact::updateAwayMessage( const QString& contact, const QString& message ) +{ + if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) ) + return; + else + { + if ( message.isEmpty() ) + { + removeProperty( mProtocol->awayMessage ); + if ( !m_mobile ) + setOnlineStatus( mProtocol->statusOnline ); + else + setOnlineStatus( mProtocol->statusWirelessOnline ); + m_haveAwayMessage = false; + } + else + { + m_haveAwayMessage = true; + setAwayMessage( message ); + if ( !m_mobile ) + setOnlineStatus( mProtocol->statusAway ); + else + setOnlineStatus( mProtocol->statusWirelessAway ); + } + } + + emit updatedProfile(); +} + +void AIMContact::updateProfile( const QString& contact, const QString& profile ) +{ + if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) ) + return; + + setProperty( mProtocol->clientProfile, profile ); + emit updatedProfile(); +} + +void AIMContact::gotWarning( const QString& contact, Q_UINT16 increase, Q_UINT16 newLevel ) +{ + //somebody just got bitchslapped! :O + Q_UNUSED( increase ); + if ( Oscar::normalize( contact ) == Oscar::normalize( contactId() ) ) + m_warningLevel = newLevel; + + //TODO add a KNotify event after merge to HEAD +} + +void AIMContact::requestBuddyIcon() +{ + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Updating buddy icon for " << contactId() << endl; + if ( m_details.buddyIconHash().size() > 0 ) + { + account()->engine()->requestBuddyIcon( contactId(), m_details.buddyIconHash(), + m_details.iconCheckSumType() ); + } +} + +void AIMContact::haveIcon( const QString& user, QByteArray icon ) +{ + if ( Oscar::normalize( user ) != Oscar::normalize( contactId() ) ) + return; + + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Updating icon for " << contactId() << endl; + QImage buddyIcon( icon ); + if ( buddyIcon.isNull() ) + { + kdWarning(OSCAR_AIM_DEBUG) << k_funcinfo << "Failed to convert buddy icon to QImage" << endl; + return; + } + + setProperty( Kopete::Global::Properties::self()->photo(), buddyIcon ); +} + +void AIMContact::closeUserInfoDialog() +{ + m_infoDialog->delayedDestruct(); + m_infoDialog = 0L; +} + +void AIMContact::warnUser() +{ + QString nick = property( Kopete::Global::Properties::self()->nickName() ).value().toString(); + QString message = i18n( "<qt>Would you like to warn %1 anonymously or with your name?<br>" \ + "(Warning a user on AIM will result in a \"Warning Level\"" \ + " increasing for the user you warn. Once this level has reached a" \ + " certain point, they will not be able to sign on. Please do not abuse" \ + " this function, it is meant for legitimate practices.)</qt>" ).arg( nick ); + + + int result = KMessageBox::questionYesNoCancel( Kopete::UI::Global::mainWidget(), message, + i18n( "Warn User %1?" ).arg( nick ), + i18n( "Warn Anonymously" ), i18n( "Warn" ) ); + + if ( result == KMessageBox::Yes ) + mAccount->engine()->sendWarning( contactId(), true); + else if ( result == KMessageBox::No ) + mAccount->engine()->sendWarning( contactId(), false); +} + +void AIMContact::slotVisibleTo() +{ + account()->engine()->setVisibleTo( contactId(), m_actionVisibleTo->isChecked() ); +} + +void AIMContact::slotInvisibleTo() +{ + account()->engine()->setInvisibleTo( contactId(), m_actionInvisibleTo->isChecked() ); +} + +void AIMContact::slotSendMsg(Kopete::Message& message, Kopete::ChatSession *) +{ + Oscar::Message msg; + QString s; + + if (message.plainBody().isEmpty()) // no text, do nothing + return; + //okay, now we need to change the message.escapedBody from real HTML to aimhtml. + //looking right now for docs on that "format". + //looks like everything except for alignment codes comes in the format of spans + + //font-style:italic -> <i> + //font-weight:600 -> <b> (anything > 400 should be <b>, 400 is not bold) + //text-decoration:underline -> <u> + //font-family: -> <font face=""> + //font-size:xxpt -> <font ptsize=xx> + + s=message.escapedBody(); + s.replace ( QRegExp( QString::fromLatin1("<span style=\"([^\"]*)\">([^<]*)</span>")), + QString::fromLatin1("<style>\\1;\"\\2</style>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-style:italic;([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("<i><style>\\1\\2\"\\3</style></i>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-weight:600;([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("<b><style>\\1\\2\"\\3</style></b>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)text-decoration:underline;([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("<u><style>\\1\\2\"\\3</style></u>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-family:([^;]*);([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("<font face=\"\\2\"><style>\\1\\3\"\\4</style></font>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-size:([^p]*)pt;([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("<font ptsize=\"\\2\"><style>\\1\\3\"\\4</style></font>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)color:([^;]*);([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("<font color=\"\\2\"><style>\\1\\3\"\\4</style></font>")); + + s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)\"([^<]*)</style>")), + QString::fromLatin1("\\2")); + + //okay now change the <font ptsize="xx"> to <font size="xx"> + + //0-9 are size 1 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"\\d\">")), + QString::fromLatin1("<font size=\"1\">")); + //10-11 are size 2 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[01]\">")), + QString::fromLatin1("<font size=\"2\">")); + //12-13 are size 3 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[23]\">")), + QString::fromLatin1("<font size=\"3\">")); + //14-16 are size 4 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[456]\">")), + QString::fromLatin1("<font size=\"4\">")); + //17-22 are size 5 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"(?:1[789]|2[012])\">")), + QString::fromLatin1("<font size=\"5\">")); + //23-29 are size 6 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"2[3456789]\">")),QString::fromLatin1("<font size=\"6\">")); + //30- (and any I missed) are size 7 + s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"[^\"]*\">")),QString::fromLatin1("<font size=\"7\">")); + + s.replace ( QRegExp ( QString::fromLatin1("<br[ /]*>")), QString::fromLatin1("<br>") ); + + // strip left over line break + s.remove( QRegExp( QString::fromLatin1( "<br>$" ) ) ); + + kdDebug(14190) << k_funcinfo << "sending " + << s << endl; + + // XXX Need to check for message size? + + if ( m_details.hasCap( CAP_UTF8 ) ) + msg.setText( Oscar::Message::UCS2, s ); + else + msg.setText( Oscar::Message::UserDefined, s, contactCodec() ); + + msg.setReceiver(mName); + msg.setTimestamp(message.timestamp()); + msg.setType(0x01); + + mAccount->engine()->sendMessage(msg); + + // Show the message we just sent in the chat window + manager(Kopete::Contact::CanCreate)->appendMessage(message); + manager(Kopete::Contact::CanCreate)->messageSucceeded(); +} + +void AIMContact::updateFeatures() +{ + setProperty( static_cast<AIMProtocol*>(protocol())->clientFeatures, m_clientFeatures ); +} + +void AIMContact::sendAutoResponse(Kopete::Message& msg) +{ + // The target time is 2 minutes later than the last message + int delta = m_lastAutoresponseTime.secsTo( QDateTime::currentDateTime() ); + kdDebug(14152) << k_funcinfo << "Last autoresponse time: " << m_lastAutoresponseTime << endl; + kdDebug(14152) << k_funcinfo << "Current time: " << QDateTime::currentDateTime() << endl; + kdDebug(14152) << k_funcinfo << "Difference: " << delta << endl; + // Check to see if we're past that time + if(delta > 120) + { + kdDebug(14152) << k_funcinfo << "Sending auto response" << endl; + + // This code was yoinked straight from OscarContact::slotSendMsg() + // If only that slot wasn't private, but I'm not gonna change it right now. + Oscar::Message message; + + if ( m_details.hasCap( CAP_UTF8 ) ) + { + message.setText( Oscar::Message::UCS2, msg.plainBody() ); + } + else + { + QTextCodec* codec = contactCodec(); + message.setText( Oscar::Message::UserDefined, msg.plainBody(), codec ); + } + + message.setTimestamp( msg.timestamp() ); + message.setSender( mAccount->accountId() ); + message.setReceiver( mName ); + message.setType( 0x01 ); + + // isAuto defaults to false + mAccount->engine()->sendMessage( message, true); + kdDebug(14152) << k_funcinfo << "Sent auto response" << endl; + manager(Kopete::Contact::CanCreate)->appendMessage(msg); + manager(Kopete::Contact::CanCreate)->messageSucceeded(); + // Update the last autoresponse time + m_lastAutoresponseTime = QDateTime::currentDateTime(); + } + else + { + kdDebug(14152) << k_funcinfo << "Not enough time since last autoresponse, NOT sending" << endl; + } +} +#include "aimcontact.moc" +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/aim/aimcontact.h b/kopete/protocols/oscar/aim/aimcontact.h new file mode 100644 index 00000000..458db2f5 --- /dev/null +++ b/kopete/protocols/oscar/aim/aimcontact.h @@ -0,0 +1,102 @@ +/* + aimcontact.h - Oscar Protocol Plugin + + Copyright (c) 2003 by Will Stephenson + 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. * + * * + ************************************************************************* +*/ + +#ifndef AIMCONTACT_H +#define AIMCONTACT_H + +#include <qdatetime.h> + +#include "oscarcontact.h" + + +namespace Kopete +{ +class ChatSession; +} + +class AIMAccount; +class AIMProtocol; +class AIMUserInfoDialog; + +class AIMContact : public OscarContact +{ +Q_OBJECT + +public: + AIMContact( Kopete::Account*, const QString&, Kopete::MetaContact*, + const QString& icon = QString::null, const Oscar::SSI& ssiItem = Oscar::SSI() ); + virtual ~AIMContact(); + + bool isReachable(); + QPtrList<KAction> *customContextMenuActions(); + + const QString &userProfile() { return mUserProfile; } + + virtual const QString awayMessage(); + virtual void setAwayMessage( const QString &message ); + + int warningLevel() const; + + /** + * Gets the last time an autoresponse was sent to this contact + * @returns QDateTime Object that represents the date/time + */ + QDateTime lastAutoResponseTime() {return m_lastAutoresponseTime;} + + /** Sends an auto response to this contact */ + virtual void sendAutoResponse(Kopete::Message& msg); + +public slots: + void updateSSIItem(); + void slotUserInfo(); + void userInfoUpdated( const QString& contact, const UserDetails& details ); + void userOnline( const QString& userId ); + void userOffline( const QString& userId ); + void updateAwayMessage( const QString& userId, const QString& message ); + void updateProfile( const QString& contact, const QString& profile ); + void gotWarning( const QString& contact, Q_UINT16, Q_UINT16 ); + +signals: + void updatedProfile(); + +protected slots: + virtual void slotSendMsg(Kopete::Message& message, Kopete::ChatSession *); + virtual void updateFeatures(); + +private slots: + void requestBuddyIcon(); + void haveIcon( const QString&, QByteArray ); + void closeUserInfoDialog(); + void warnUser(); + + void slotVisibleTo(); + void slotInvisibleTo(); + +private: + AIMProtocol* mProtocol; + AIMUserInfoDialog* m_infoDialog; + QString mUserProfile; + bool m_haveAwayMessage; + bool m_mobile; // Is this user mobile (i.e. do they have message forwarding on, or mobile AIM) + QDateTime m_lastAutoresponseTime; + + KAction* m_warnUserAction; + KToggleAction *m_actionVisibleTo; + KToggleAction *m_actionInvisibleTo; +}; +#endif +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/aim/aimjoinchat.cpp b/kopete/protocols/oscar/aim/aimjoinchat.cpp new file mode 100644 index 00000000..8b8c78a9 --- /dev/null +++ b/kopete/protocols/oscar/aim/aimjoinchat.cpp @@ -0,0 +1,94 @@ +// aimjoinchat.cpp + +// 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 "aimjoinchat.h" + +#include <qlineedit.h> +#include <qcombobox.h> +#include <klocale.h> + +#include "aimjoinchatbase.h" +#include "aimaccount.h" + +AIMJoinChatUI::AIMJoinChatUI( AIMAccount* account, bool modal, + QWidget* parent, const char* name ) + : KDialogBase( parent, name, modal, i18n( "Join AIM Chat Room" ), + Cancel | User1, User1, true, i18n( "Join" ) ) +{ + + kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Account " << account->accountId() + << " joining a chat room" << endl; + + m_account = account; + + m_joinUI = new AIMJoinChatBase( this, "aimjoinchatbase" ); + + setMainWidget( m_joinUI ); + + QObject::connect( this, SIGNAL( user1Clicked() ), this, SLOT( joinChat() ) ); + QObject::connect( this, SIGNAL( cancelClicked() ), this, SLOT( closeClicked() ) ); +} + +AIMJoinChatUI::~AIMJoinChatUI() +{ + m_exchanges.clear(); +} + +void AIMJoinChatUI::setExchangeList( const QValueList<int>& list ) +{ + m_exchanges = list; + QStringList exchangeList; + QValueList<int>::const_iterator it = list.begin(); + while ( it != list.end() ) + { + exchangeList.append( QString::number( ( *it ) ) ); + ++it; + } + + + m_joinUI->exchange->insertStringList( exchangeList ); +} + +void AIMJoinChatUI::joinChat() +{ + m_roomName = m_joinUI->roomName->text(); + int item = m_joinUI->exchange->currentItem(); + m_exchange = m_joinUI->exchange->text( item ); + + emit closing( QDialog::Accepted ); +} + +void AIMJoinChatUI::closeClicked() +{ + //hmm, do nothing? + emit closing( QDialog::Rejected ); +} + +QString AIMJoinChatUI::roomName() const +{ + return m_roomName; +} + +QString AIMJoinChatUI::exchange() const +{ + return m_exchange; +} + +#include "aimjoinchat.moc" +//kate: space-indent on; indent-width 4; diff --git a/kopete/protocols/oscar/aim/aimjoinchat.h b/kopete/protocols/oscar/aim/aimjoinchat.h new file mode 100644 index 00000000..dc74a8a9 --- /dev/null +++ b/kopete/protocols/oscar/aim/aimjoinchat.h @@ -0,0 +1,62 @@ +// aimjoinchat.h + +// 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 + +#ifndef AIMJOINCHAT_H +#define AIMJOINCHAT_H + +#include <kdialogbase.h> + +#include "oscartypes.h" + +class AIMAccount; +class AIMJoinChatBase; + +class AIMJoinChatUI : public KDialogBase +{ +Q_OBJECT +public: + AIMJoinChatUI( AIMAccount*, bool modal, QWidget* parent = 0, + const char* name = 0 ); + ~AIMJoinChatUI(); + + void setExchangeList( const QValueList<int>& ); + QValueList<int> exchangeList() const; + + QString roomName() const; + QString exchange() const; + + +protected slots: + void joinChat(); + void closeClicked(); + +signals: + void closing( int ); + +private: + AIMJoinChatBase* m_joinUI; + AIMAccount* m_account; + QValueList<int> m_exchanges; + QString m_roomName; + QString m_exchange; + +}; + +#endif +//kate: space-indent on; indent-width 4; diff --git a/kopete/protocols/oscar/aim/aimprotocol.cpp b/kopete/protocols/oscar/aim/aimprotocol.cpp new file mode 100644 index 00000000..9f64fe28 --- /dev/null +++ b/kopete/protocols/oscar/aim/aimprotocol.cpp @@ -0,0 +1,320 @@ +/* + oscarprotocol.cpp - Oscar Protocol Plugin + + Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu> + + Kopete (c) 2002-2003 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 <qstringlist.h> +#include <kgenericfactory.h> +#include <kmessagebox.h> +#include <kdebug.h> + +#include "aimprotocol.h" +#include "aimaccount.h" +#include "aimcontact.h" +#include "aimaddcontactpage.h" +#include "aimeditaccountwidget.h" + +#include "accountselector.h" +#include "kopeteaccountmanager.h" +#include "kopeteonlinestatusmanager.h" +#include "kopeteglobal.h" +#include "kopeteuiglobal.h" +#include "kopetemetacontact.h" + +#include <kdialogbase.h> +#include <kmessagebox.h> +#include <kimageio.h> + +typedef KGenericFactory<AIMProtocol> AIMProtocolFactory; + +K_EXPORT_COMPONENT_FACTORY( kopete_aim, AIMProtocolFactory( "kopete_aim" ) ) + +AIMProtocol* AIMProtocol::protocolStatic_ = 0L; + + +AIMProtocolHandler::AIMProtocolHandler() : Kopete::MimeTypeHandler(false) +{ + registerAsProtocolHandler(QString::fromLatin1("aim")); +} + +void AIMProtocolHandler::handleURL(const KURL &url) const +{ +/** + * Send a Message ================================================= + * aim:goim + * aim:goim?screenname=screen+name + * aim:goim?screenname=screen+name&message=message + * Add Buddy ====================================================== + * aim:addbuddy + * aim:addbuddy?screenname=screen+name + * Buddy Icon ===================================================== + * aim:buddyicon + * aim:buddyicon?src=image_source + * aim:buddyicon?screename=screen+name + * aim:buddyicon?src=image_source&screename=screen+name + * Get and Send Files ============================================= + * aim:getfile?screename=(sn) + * aim:sendfile?screenname=(sn) + * Register User ================================================== + * aim:RegisterUser?ScreenName=sn&Password=pw&SignonNow=False + * Away Message =================================================== + * aim:goaway?message=brb+or+something + * Chat Rooms ===================================================== + * aim:GoChat?RoomName=room+name&Exchange=number + **/ + + AIMProtocol *proto = AIMProtocol::protocol(); + kdDebug(14152) << k_funcinfo << "URL url : '" << url.url() << "'" << endl; + kdDebug(14152) << k_funcinfo << "URL path : '" << url.path() << "'" << endl; + QString command = url.path(); + QString realCommand, firstParam, secondParam; + bool needContactAddition = false; + if ( command.find( "goim", 0, false ) != -1 ) + { + realCommand = "goim"; + kdDebug(14152) << k_funcinfo << "Handling send IM request" << endl; + command.remove(0,4); + if ( command.find( "?screenname=", 0, false ) == -1 ) + { + kdWarning(14152) << k_funcinfo << "Unhandled AIM URI:" << url.url() << endl; + return; + } + command.remove( 0, 12 ); + int andSign = command.find( "&" ); + if ( andSign > 0 ) + command = command.left( andSign ); + + firstParam = command; + firstParam.replace( "+", " " ); + needContactAddition = true; + } + else + if ( command.find( "addbuddy", 0, false ) != -1 ) + { + realCommand = "addbuddy"; + kdDebug(14152) << k_funcinfo << "Handling AIM add buddy request" << endl; + command.remove( 0, 8 ); + if ( command.find( "?screenname=", 0, false ) == -1 ) + { + kdWarning(14152) << k_funcinfo << "Unhandled AIM URI:" << url.url() << endl; + return; + } + + command.remove(0, 12); + int andSign = command.find("&"); + if ( andSign > 0 ) + command = command.left(andSign); + command.replace("+", " "); + + firstParam = command; + needContactAddition = true; + } + else + if ( command.find( "gochat", 0, false ) != -1 ) + { + realCommand = "gochat"; + kdDebug(14152) << k_funcinfo << "Handling AIM chat room request" << endl; + command.remove( 0, 6 ); + + if ( command.find( "?RoomName=", 0, false ) == -1 ) + { + kdWarning(14152) << "Unhandled AIM URI: " << url.url() << endl; + return; + } + + command.remove( 0, 10 ); + + int andSign = command.find("&"); + if (andSign > 0) // strip off anything else for now + { + firstParam = command.left(andSign); + } + command.remove( 0, andSign ); + kdDebug(14152) << "command is now: " << command << endl; + command.remove( 0, 10 ); //remove "&Exchange=" + secondParam = command; + kdDebug(14152) << k_funcinfo << firstParam << " " << secondParam << endl; + firstParam.replace("+", " "); + } + + Kopete::Account *account = 0; + QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts(proto); + + if (accounts.count() == 1) + { + QDictIterator<Kopete::Account> it(accounts); + account = it.current(); + + } + else + { + KDialogBase *chooser = new KDialogBase(0, "chooser", true, + i18n("Choose Account"), KDialogBase::Ok|KDialogBase::Cancel, + KDialogBase::Ok, false); + AccountSelector *accSelector = new AccountSelector(proto, chooser, "accSelector"); + chooser->setMainWidget(accSelector); + + int ret = chooser->exec(); + account = accSelector->selectedItem(); + + delete chooser; + if (ret == QDialog::Rejected || account == 0) + { + kdDebug(14152) << k_funcinfo << "Cancelled" << endl; + return; + } + } + + Kopete::MetaContact* mc = 0; + if ( needContactAddition || realCommand == "addbuddy" ) + { + if ( !account->isConnected() ) + { + kdDebug(14152) << k_funcinfo << "Can't add contact, we are offline!" << endl; + KMessageBox::sorry( Kopete::UI::Global::mainWidget(), i18n("You need to be connected to be able to add contacts."), + i18n("AIM") ); + return; + } + + if (KMessageBox::questionYesNo(Kopete::UI::Global::mainWidget(), + i18n("Do you want to add '%1' to your contact list?").arg(command), + QString::null, i18n("Add"), i18n("Do Not Add")) + != KMessageBox::Yes) + { + kdDebug(14152) << k_funcinfo << "Cancelled" << endl; + return; + } + + kdDebug(14152) << k_funcinfo << + "Adding Contact; screenname = " << firstParam << endl; + mc = account->addContact(firstParam, command, 0L, Kopete::Account::Temporary); + } + + if ( realCommand == "gochat" ) + { + AIMAccount* aimAccount = dynamic_cast<AIMAccount*>( account ); + if ( aimAccount && aimAccount->isConnected() ) + { + aimAccount->engine()->joinChatRoom( firstParam, secondParam.toInt() ); + } + else + KMessageBox::sorry( Kopete::UI::Global::mainWidget(), + i18n( "Unable to connect to the chat room %1 because the account" + " for %2 is not connected." ).arg( firstParam ).arg( aimAccount->accountId() ), + QString::null ); + + } + + if ( mc && realCommand == "goim" ) + { + mc->execute(); + } + +} + + + + +AIMProtocol::AIMProtocol(QObject *parent, const char *name, const QStringList &) + : Kopete::Protocol( AIMProtocolFactory::instance(), parent, name ), + statusOnline( Kopete::OnlineStatus::Online, 2, this, 0, QString::null, i18n("Online"), i18n("Online"), Kopete::OnlineStatusManager::Online ), + statusOffline( Kopete::OnlineStatus::Offline, 2, this, 10, QString::null, i18n("Offline"), i18n("Offline"), Kopete::OnlineStatusManager::Offline ), + statusAway( Kopete::OnlineStatus::Away, 2, this, 20, "contact_away_overlay", i18n("Away"), i18n("Away"), Kopete::OnlineStatusManager::Away, + Kopete::OnlineStatusManager::HasAwayMessage ), + statusWirelessOnline( Kopete::OnlineStatus::Online, 1, this, 30, "contact_phone_overlay", i18n("Mobile"), i18n("Mobile"), + Kopete::OnlineStatusManager::Online, Kopete::OnlineStatusManager::HideFromMenu ), + statusWirelessAway( Kopete::OnlineStatus::Away, 1, this, 31, QStringList::split( " ", "contact_phone_overlay contact_away_overlay"), + i18n("Mobile Away"), i18n("Mobile Away"), Kopete::OnlineStatusManager::Away, Kopete::OnlineStatusManager::HideFromMenu ), + statusConnecting(Kopete::OnlineStatus::Connecting, 99, this, 99, "aim_connecting", i18n("Connecting...")), + awayMessage(Kopete::Global::Properties::self()->awayMessage()), + clientFeatures("clientFeatures", i18n("Client Features"), 0, false), + clientProfile( "clientProfile", i18n( "User Profile"), 0, false, true), + iconHash("iconHash", i18n("Buddy Icon MD5 Hash"), QString::null, true, false, true) +{ + if (protocolStatic_) + kdDebug(14152) << k_funcinfo << "AIM plugin already initialized" << endl; + else + protocolStatic_ = this; + + setCapabilities(0x1FFF); // setting capabilities - FIXME to use proper enum + kdDebug(14152) << k_funcinfo << "capabilities set to 0x1FFF" << endl; + addAddressBookField("messaging/aim", Kopete::Plugin::MakeIndexField); + KImageIO::registerFormats(); +} + +AIMProtocol::~AIMProtocol() +{ + protocolStatic_ =0L; +} + +AIMProtocol *AIMProtocol::protocol(void) +{ + return protocolStatic_; +} + +Kopete::Contact *AIMProtocol::deserializeContact(Kopete::MetaContact *metaContact, + const QMap<QString, QString> &serializedData, + const QMap<QString, QString> &/*addressBookData*/) +{ + + QString contactId = serializedData["contactId"]; + QString accountId = serializedData["accountId"]; + QString displayName = serializedData["displayName"]; + + // Get the account it belongs to + QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( this ); + Kopete::Account *account = accounts[accountId]; + + if ( !account ) //no account + return 0; + + uint ssiGid = 0, ssiBid = 0, ssiType = 0xFFFF; + QString ssiName; + bool ssiWaitingAuth = false; + if ( serializedData.contains( "ssi_type" ) ) + { + ssiName = serializedData["ssi_name"]; + QString authStatus = serializedData["ssi_waitingAuth"]; + if ( authStatus == "true" ) + ssiWaitingAuth = true; + ssiGid = serializedData["ssi_gid"].toUInt(); + ssiBid = serializedData["ssi_bid"].toUInt(); + ssiType = serializedData["ssi_type"].toUInt(); + } + + Oscar::SSI item( ssiName, ssiGid, ssiBid, ssiType, QValueList<TLV>(), 0 ); + item.setWaitingAuth( ssiWaitingAuth ); + + AIMContact *c = new AIMContact( account, contactId, metaContact, QString::null, item ); + return c; +} + +AddContactPage *AIMProtocol::createAddContactWidget(QWidget *parent, Kopete::Account *account) +{ + return ( new AIMAddContactPage( account->isConnected(), parent ) ); +} + +KopeteEditAccountWidget *AIMProtocol::createEditAccountWidget(Kopete::Account *account, QWidget *parent) +{ + return ( new AIMEditAccountWidget( this, account, parent ) ); +} + +Kopete::Account *AIMProtocol::createNewAccount(const QString &accountId) +{ + return ( new AIMAccount( this, accountId ) ); +} + +#include "aimprotocol.moc" +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/protocols/oscar/aim/aimprotocol.h b/kopete/protocols/oscar/aim/aimprotocol.h new file mode 100644 index 00000000..e6c578e6 --- /dev/null +++ b/kopete/protocols/oscar/aim/aimprotocol.h @@ -0,0 +1,85 @@ +/* + oscarprotocol.h - Oscar Protocol Plugin + + Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu> + Copyright (c) 2005 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002 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. * + * * + ************************************************************************* + */ + +#ifndef AIMPROTOCOL_H +#define AIMPROTOCOL_H + +#include "kopeteprotocol.h" +#include "kopetecontactproperty.h" +#include "kopetemimetypehandler.h" +#include "kopeteonlinestatus.h" + +#include <qmap.h> + +namespace Kopete +{ +class OnlineStatus; +} + +class AIMProtocolHandler : public Kopete::MimeTypeHandler +{ +public: + AIMProtocolHandler(); + void handleURL( const KURL & url ) const; +}; + +class AIMProtocol : public Kopete::Protocol +{ + Q_OBJECT + +public: + AIMProtocol( QObject *parent, const char *name, const QStringList &args ); + virtual ~AIMProtocol(); + /** + * Return the active instance of the protocol + * because it's a singleton, can only be used inside AIM classes, not in oscar lib + */ + static AIMProtocol *protocol(); + + bool canSendOffline() const { return false; } + + virtual Kopete::Contact *deserializeContact( Kopete::MetaContact *metaContact, + const QMap<QString, QString> &serializedData, + const QMap<QString, QString> &addressBookData ); + + AddContactPage*createAddContactWidget( QWidget *parent, Kopete::Account *account ); + KopeteEditAccountWidget* createEditAccountWidget( Kopete::Account *account, QWidget *parent ); + Kopete::Account* createNewAccount( const QString &accountId ); + + /** + * The set of online statuses that AIM contacts can have + */ + const Kopete::OnlineStatus statusOnline; + const Kopete::OnlineStatus statusOffline; + const Kopete::OnlineStatus statusAway; + const Kopete::OnlineStatus statusWirelessOnline; + const Kopete::OnlineStatus statusWirelessAway; + const Kopete::OnlineStatus statusConnecting; + + const Kopete::ContactPropertyTmpl awayMessage; + const Kopete::ContactPropertyTmpl clientFeatures; + const Kopete::ContactPropertyTmpl clientProfile; + const Kopete::ContactPropertyTmpl iconHash; + +private: + /** The active instance of oscarprotocol */ + static AIMProtocol *protocolStatic_; + AIMProtocolHandler protohandler; +}; + +#endif +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/aim/aimuserinfo.cpp b/kopete/protocols/oscar/aim/aimuserinfo.cpp new file mode 100644 index 00000000..81bdd9c7 --- /dev/null +++ b/kopete/protocols/oscar/aim/aimuserinfo.cpp @@ -0,0 +1,224 @@ +/* + oscaruserinfo.cpp - Oscar Protocol Plugin + + Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu> + + Kopete (c) 2002 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 "aimuserinfo.h" + +#include "aimaccount.h" +#include "aimcontact.h" +#include "aimprotocol.h" + +#include <qlineedit.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qtimer.h> + +#include <klocale.h> +#include <kstandarddirs.h> +#include <ktextbrowser.h> +#include <kdebug.h> +#include <kapplication.h> + +#include <ktextedit.h> +#include <krun.h> + +AIMUserInfoDialog::AIMUserInfoDialog( Kopete::Contact *c, AIMAccount *acc, bool modal, + QWidget *parent, const char* name ) + : KDialogBase( parent, name, modal, i18n( "User Information on %1" ) + .arg( c->property( Kopete::Global::Properties::self()->nickName() ).value().toString() ), + Cancel | Ok , Ok, true ) +{ + kdDebug(14200) << k_funcinfo << "for contact '" << c->contactId() << "'" << endl; + + m_contact = c; + mAccount = acc; + + mMainWidget = new AIMUserInfoWidget(this, "aimuserinfowidget"); + setMainWidget(mMainWidget); + + QObject::connect(this, SIGNAL(okClicked()), this, SLOT(slotSaveClicked())); + QObject::connect(this, SIGNAL(user1Clicked()), this, SLOT(slotUpdateClicked())); + QObject::connect(this, SIGNAL(cancelClicked()), this, SLOT(slotCloseClicked())); + QObject::connect(c, SIGNAL(updatedProfile()), this, SLOT(slotUpdateProfile())); + + mMainWidget->txtScreenName->setText( c->contactId() ); + + QString nickName = c->property( Kopete::Global::Properties::self()->nickName() ).value().toString(); + if( nickName.isEmpty() ) + mMainWidget->txtNickName->setText( c->contactId() ); + else + mMainWidget->txtNickName->setText( nickName ); + + if(m_contact == mAccount->myself()) // edit own account profile + { + mMainWidget->lblWarnLevel->hide(); + mMainWidget->txtWarnLevel->hide(); + mMainWidget->lblIdleTime->hide(); + mMainWidget->txtIdleTime->hide(); + mMainWidget->lblOnlineSince->hide(); + mMainWidget->txtOnlineSince->hide(); + mMainWidget->txtAwayMessage->hide(); + mMainWidget->lblAwayMessage->hide(); + + userInfoView=0L; + mMainWidget->userInfoFrame->setFrameStyle(QFrame::NoFrame | QFrame::Plain); + QVBoxLayout *l = new QVBoxLayout(mMainWidget->userInfoFrame); + userInfoEdit = new KTextEdit(QString::null, QString::null, + mMainWidget->userInfoFrame, "userInfoEdit"); + userInfoEdit->setTextFormat(PlainText); + + AIMMyselfContact* aimmc = dynamic_cast<AIMMyselfContact*>( c ); + if ( aimmc ) + userInfoEdit->setText( aimmc->userProfile() ); + else + userInfoEdit->setText( QString::null ); + + setButtonText(Ok, i18n("&Save Profile")); + showButton(User1, false); + l->addWidget(userInfoEdit); + } + else + { + userInfoEdit=0L; + mMainWidget->userInfoFrame->setFrameStyle(QFrame::NoFrame | QFrame::Plain); + QVBoxLayout *l = new QVBoxLayout(mMainWidget->userInfoFrame); + userInfoView = new KTextBrowser(mMainWidget->userInfoFrame, "userInfoView"); + userInfoView->setTextFormat(AutoText); + userInfoView->setNotifyClick(true); + QObject::connect( + userInfoView, SIGNAL(urlClick(const QString&)), + this, SLOT(slotUrlClicked(const QString&))); + QObject::connect( + userInfoView, SIGNAL(mailClick(const QString&, const QString&)), + this, SLOT(slotMailClicked(const QString&, const QString&))); + showButton(Cancel, false); + setButtonText(Ok, i18n("&Close")); + setEscapeButton(Ok); + l->addWidget(userInfoView); + + if(m_contact->isOnline()) + { + // Update the user view to indicate that we're requesting the user's profile + userInfoView->setText(i18n("Requesting User Profile, please wait...")); + } + QTimer::singleShot(0, this, SLOT(slotUpdateProfile())); + } +} + +AIMUserInfoDialog::~AIMUserInfoDialog() +{ + kdDebug(14200) << k_funcinfo << "Called." << endl; +} + +void AIMUserInfoDialog::slotUpdateClicked() +{ + kdDebug(14200) << k_funcinfo << "Called." << endl; + QString newNick = mMainWidget->txtNickName->text(); + QString currentNick = m_contact->property( Kopete::Global::Properties::self()->nickName() ).value().toString(); + if ( !newNick.isEmpty() && ( newNick != currentNick ) ) + { + //m_contact->rename(newNick); + //emit updateNickname(newNick); + setCaption(i18n("User Information on %1").arg(newNick)); + } + +} + +void AIMUserInfoDialog::slotSaveClicked() +{ + kdDebug(14200) << k_funcinfo << "Called." << endl; + + if (userInfoEdit) + { // editable mode, set profile + QString newNick = mMainWidget->txtNickName->text(); + QString currentNick = m_contact->property( Kopete::Global::Properties::self()->nickName() ).value().toString(); + if(!newNick.isEmpty() && ( newNick != currentNick ) ) + { + //m_contact->rename(newNick); + //emit updateNickname(newNick); + setCaption(i18n("User Information on %1").arg(newNick)); + } + + mAccount->setUserProfile(userInfoEdit->text()); + } + + emit closing(); +} + +void AIMUserInfoDialog::slotCloseClicked() +{ + kdDebug(14200) << k_funcinfo << "Called." << endl; + emit closing(); +} + +void AIMUserInfoDialog::slotUpdateProfile() +{ + kdDebug(14152) << k_funcinfo << "Got User Profile." << endl; + AIMProtocol* p = static_cast<AIMProtocol*>( mAccount->protocol() ); + QString awayMessage = m_contact->property( p->awayMessage ).value().toString(); + mMainWidget->txtAwayMessage->setText( awayMessage ); + + if ( awayMessage.isNull() ) + { + mMainWidget->txtAwayMessage->hide(); + mMainWidget->lblAwayMessage->hide(); + } + else + { + mMainWidget->txtAwayMessage->show(); + mMainWidget->lblAwayMessage->show(); + } + + QString onlineSince = m_contact->property("onlineSince").value().toString(); + //QString onlineSince = m_details.onlineSinceTime().toString(); + mMainWidget->txtOnlineSince->setText( onlineSince ); + + AIMContact* c = static_cast<AIMContact*>( m_contact ); + mMainWidget->txtIdleTime->setText(c->formattedIdleTime()); + mMainWidget->txtWarnLevel->setText(QString::number(c->warningLevel())); + + QString contactProfile = m_contact->property( p->clientProfile ).value().toString(); + if ( contactProfile.isNull() ) + { + contactProfile = + i18n("<html><body><I>No user information provided</I></body></html>"); + } + + if(userInfoEdit) + { + userInfoEdit->setText(contactProfile); + } + else if(userInfoView) + { + userInfoView->setText(contactProfile); + } + +} + +void AIMUserInfoDialog::slotUrlClicked(const QString &url) +{ + new KRun(KURL(url)); +} + +void AIMUserInfoDialog::slotMailClicked(const QString&, const QString &address) +{ + new KRun(KURL(address)); +} + +#include "aimuserinfo.moc" + +//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/aim/aimuserinfo.h b/kopete/protocols/oscar/aim/aimuserinfo.h new file mode 100644 index 00000000..f128610f --- /dev/null +++ b/kopete/protocols/oscar/aim/aimuserinfo.h @@ -0,0 +1,59 @@ +/* + oscaruserinfo.h - Oscar Protocol Plugin + + Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu> + + Kopete (c) 2002 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. * + * * + ************************************************************************* +*/ + +#ifndef AIMUSERINFO_H +#define AIMUSERINFO_H + +#include <kdialogbase.h> +#include "aiminfobase.h" + +namespace Kopete { class Contact; } +class KTextEdit; +class OscarAccount; +class AIMMyselfContact; +class AIMAccount; + +class AIMUserInfoDialog : public KDialogBase +{ + Q_OBJECT + public: + AIMUserInfoDialog(Kopete::Contact *c, AIMAccount *acc, bool modal, + QWidget *parent, const char* name); + ~AIMUserInfoDialog(); + + private: + AIMAccount *mAccount; + Kopete::Contact* m_contact; + AIMUserInfoWidget *mMainWidget; + KTextBrowser *userInfoView; + KTextEdit *userInfoEdit; + + private slots: + void slotSaveClicked(); + void slotCloseClicked(); + void slotUpdateClicked(); + void slotUpdateProfile(); + void slotUrlClicked(const QString&); + void slotMailClicked(const QString&, const QString&); + + signals: +// void updateNickname(const QString &); + void closing(); +}; + +#endif + diff --git a/kopete/protocols/oscar/aim/kopete_aim.desktop b/kopete/protocols/oscar/aim/kopete_aim.desktop new file mode 100644 index 00000000..0ab4fe69 --- /dev/null +++ b/kopete/protocols/oscar/aim/kopete_aim.desktop @@ -0,0 +1,77 @@ +[Desktop Entry] +Type=Service +X-Kopete-Version=1000900 +Icon=aim_protocol +ServiceTypes=Kopete/Protocol +X-KDE-Library=kopete_aim +X-Kopete-Messaging-Protocol=messaging/aim +X-KDE-PluginInfo-Author=Kopete Developers +X-KDE-PluginInfo-Email=kopete-devel@kde.org +X-KDE-PluginInfo-Name=kopete_aim +X-KDE-PluginInfo-Version=0.10.0 +X-KDE-PluginInfo-Website=http://kopete.kde.org +X-KDE-PluginInfo-Category=Protocols +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false +Name=AIM +Name[bn]=এ-আই-এম +Name[hi]=एआरईएम +Name[ne]=एआईएम +Comment=Protocol to connect to AIM +Comment[ar]=البرتوكول سيتصل بـ AIM +Comment[be]=Пратакол AIM +Comment[bg]=Протокол за връзка с AIM +Comment[bn]=এ-আই-এমতে সংযোগ করতে প্রোটোকল +Comment[br]=Komenad kevreañ ouzh AIM +Comment[bs]=AIM protokol +Comment[ca]=Protocol per a connectar-se a AIM +Comment[cs]=Protokol k připojení k AIM +Comment[cy]=Protocol i gysylltu ag AIM +Comment[da]=Protokol til at forbinde til AIM +Comment[de]=Protokoll zur Verbindung mit dem AIM +Comment[el]=Πρωτόκολλο για σύνδεση στο AIM +Comment[es]=Protocolo para conectar a AIM +Comment[et]=Protokoll ühendumiseks AIM-iga +Comment[eu]=AIM-era konektatzeko protokoloa +Comment[fa]=قرارداد برای اتصال به AIM +Comment[fi]=Yhteyskäytäntö AIM-verkkoon kytkeytymiseen +Comment[fr]=Protocole pour se connecter sur AIM +Comment[ga]=Prótacal chun ceangal le AIM +Comment[gl]=Protocolo para se conectar ó AIM +Comment[he]=פרוטוקול התחברות ל- AIM +Comment[hi]=से जुड़ने का प्रोटोकॉल +Comment[hr]=Protokol za povezivanje na AIM +Comment[hu]=Protokoll az AIM használatához +Comment[is]=Samskiptamáti til að tengjast AIM +Comment[it]=Protocollo per connessione a AIM +Comment[ja]=AIM に接続するプロトコル +Comment[ka]=AIM დაკავშირების ოქმი +Comment[kk]=AIM-ге қосылу протоколы +Comment[km]=ពិធីការភ្ជាប់ទៅ AIM +Comment[lt]=Protokolas prisijungimui prie AIM +Comment[mk]=Протокол за поврзување на AIM +Comment[nb]=Protokoll for å koble til AIM +Comment[nds]=Protokoll för't Tokoppeln na AIM +Comment[ne]=एआईएम मा जडान गर्नुपर्ने प्रोटोकल +Comment[nl]=Protocol voor AOL Instant Messenger +Comment[nn]=Protokoll for å kopla til AIM +Comment[pl]=Protokół połączenia z serwerem AIM +Comment[pt]=Um protocolo para se ligar ao AIM +Comment[pt_BR]=Protocolo de conexão ao AIM +Comment[ro]=Protocol de conectare la AIM +Comment[ru]=Протокол для подключения к AIM +Comment[sk]=Protokol pre pripojenie k AIM +Comment[sl]=Protokol za povezavo na AIM +Comment[sr]=Протокол за повезивање на AIM +Comment[sr@Latn]=Protokol za povezivanje na AIM +Comment[sv]=Protokoll för att ansluta till AIM +Comment[ta]=IRC உடன் இணைக்க விதிமுறை +Comment[tg]=Қарордоди пайвастшавӣ ба AIM +Comment[tr]=AIM'e bağlantı iletişim kuralı +Comment[uk]=Протокол для з'єднання з AIM +Comment[uz]=AIM bilan aloqa oʻrnatish uchun protokol +Comment[uz@cyrillic]=AIM билан алоқа ўрнатиш учун протокол +Comment[zh_CN]=连接到 AIM 协议 +Comment[zh_HK]=用來連接至 AIM 的通訊協定 +Comment[zh_TW]=連線到 AIM 的協定 diff --git a/kopete/protocols/oscar/aim/ui/Makefile.am b/kopete/protocols/oscar/aim/ui/Makefile.am new file mode 100644 index 00000000..aa690449 --- /dev/null +++ b/kopete/protocols/oscar/aim/ui/Makefile.am @@ -0,0 +1,15 @@ +METASOURCES = AUTO +AM_CPPFLAGS = $(KOPETE_INCLUDES) \ + -I$(srcdir)/.. \ + -I$(srcdir)/../.. \ + -I$(srcdir)/../../liboscar \ + $(all_includes) + +noinst_LTLIBRARIES = libkopeteaimui.la + +libkopeteaimui_la_SOURCES = aimaddcontactui.ui aimeditaccountui.ui \ + aiminfobase.ui aimjoinchatbase.ui aimaddcontactpage.cpp aimeditaccountwidget.cpp + +libkopeteaimui_la_LIBADD = $(top_builddir)/kopete/libkopete/libkopete.la + + diff --git a/kopete/protocols/oscar/aim/ui/aimaddcontactpage.cpp b/kopete/protocols/oscar/aim/ui/aimaddcontactpage.cpp new file mode 100644 index 00000000..cf5fbae5 --- /dev/null +++ b/kopete/protocols/oscar/aim/ui/aimaddcontactpage.cpp @@ -0,0 +1,83 @@ +/*************************************************************************** + description + ------------------- + begin : + copyright : (C) 2002 by nbetcher + email : nbetcher@usinternet.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 "aimaddcontactui.h" +#include "aimaddcontactpage.h" + +#include "kopeteaccount.h" + +#include <qlayout.h> +#include <qlineedit.h> +#include <klocale.h> +#include <kmessagebox.h> + +AIMAddContactPage::AIMAddContactPage(bool connected, QWidget *parent, + const char *name ) + : AddContactPage(parent,name) +{ + m_gui = 0; + (new QVBoxLayout(this))->setAutoAdd(true); + + if(connected) + { + m_gui = new aimAddContactUI(this); + canadd = true; + } + else + { + noaddMsg1 = new QLabel(i18n("You need to be connected to be able to add contacts."), this); + noaddMsg2 = new QLabel(i18n("Connect to the AIM network and try again."), this); + canadd = false; + } +} + + +AIMAddContactPage::~AIMAddContactPage() +{ +} + +bool AIMAddContactPage::validateData() +{ + if ( !canadd ) + return false; + + if ( !m_gui ) + return false; + + QString sn = m_gui->addSN->text(); + if ( sn.isEmpty() ) + { + KMessageBox::sorry ( this, + i18n("<qt>You must enter a valid screen name.</qt>"), + i18n("No Screen Name") ); + return false; + } + return true; +} + +bool AIMAddContactPage::apply(Kopete::Account *account, + Kopete::MetaContact *metaContact) +{ + if(validateData()) + { // If everything is ok + return account->addContact( m_gui->addSN->text(), metaContact, Kopete::Account::ChangeKABC ); + } + return false; +} +//kate: tab-width 4; indent-mode csands; + +#include "aimaddcontactpage.moc" diff --git a/kopete/protocols/oscar/aim/ui/aimaddcontactpage.h b/kopete/protocols/oscar/aim/ui/aimaddcontactpage.h new file mode 100644 index 00000000..979d0472 --- /dev/null +++ b/kopete/protocols/oscar/aim/ui/aimaddcontactpage.h @@ -0,0 +1,41 @@ + +#ifndef AIMADDCONTACTPAGE_H +#define AIMADDCONTACTPAGE_H + +#include <qwidget.h> +#include <qlabel.h> +#include "addcontactpage.h" + +class aimAddContactUI; +class AIMAccount; +namespace Kopete +{ +class Account; +class MetaContact; +} + +class AIMAddContactPage : public AddContactPage +{ +Q_OBJECT + +public: + AIMAddContactPage(bool connected, QWidget *parent=0, + const char *name=0); + ~AIMAddContactPage(); + + /** Validates the data entered */ + virtual bool validateData(); + /** Applies the addition to the account */ + virtual bool apply( Kopete::Account *account, Kopete::MetaContact *); + +protected: + /** The actual GUI */ + aimAddContactUI *m_gui; + QLabel *noaddMsg1; + QLabel *noaddMsg2; + bool canadd; +}; +#endif + +//kate: tab-width 4; indent-mode csands; + diff --git a/kopete/protocols/oscar/aim/ui/aimaddcontactui.ui b/kopete/protocols/oscar/aim/ui/aimaddcontactui.ui new file mode 100644 index 00000000..b3267eb0 --- /dev/null +++ b/kopete/protocols/oscar/aim/ui/aimaddcontactui.ui @@ -0,0 +1,64 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>aimAddContactUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>aimAddContactUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>455</width> + <height>131</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>GroupBox1</cstring> + </property> + <property name="title"> + <string>Contact Information</string> + </property> + <property name="layoutMargin" stdset="0"> + </property> + <property name="layoutSpacing" stdset="0"> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>addSN</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel1</cstring> + </property> + <property name="text"> + <string>AIM screen name:</string> + </property> + </widget> + </grid> + </widget> + </vbox> +</widget> +<tabstops> + <tabstop>addSN</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/oscar/aim/ui/aimeditaccountui.ui b/kopete/protocols/oscar/aim/ui/aimeditaccountui.ui new file mode 100644 index 00000000..d8a7b9f3 --- /dev/null +++ b/kopete/protocols/oscar/aim/ui/aimeditaccountui.ui @@ -0,0 +1,540 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>aimEditAccountUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>aimEditAccountUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>560</width> + <height>583</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="caption"> + <string>Account Preferences - AIM</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>labelStatusMessage</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QTabWidget" row="0" column="0"> + <property name="name"> + <cstring>tabWidget6</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>&Basic Setup</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox72</cstring> + </property> + <property name="title"> + <string>Account Information</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>lblAccountId</cstring> + </property> + <property name="text"> + <string>AIM &screen name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>edtAccountId</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The screen name of your AIM account.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The screen name of your AIM account. This should be in the form of an alphanumeric string (spaces allowed, not case sensitive).</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>edtAccountId</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The screen name of your AIM account.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The screen name of your AIM account. This should be in the form of an alphanumeric string (spaces allowed, not case sensitive).</string> + </property> + </widget> + </hbox> + </widget> + <widget class="Kopete::UI::PasswordWidget" row="1" column="0"> + <property name="name"> + <cstring>mPasswordWidget</cstring> + </property> + </widget> + <widget class="QCheckBox" row="3" column="0"> + <property name="name"> + <cstring>mGlobalIdentity</cstring> + </property> + <property name="text"> + <string>Exclu&de from Global Identity</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>mAutoLogon</cstring> + </property> + <property name="text"> + <string>E&xclude from connect all</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If you check that case, the account will not be connected when you press the "Connect All" button, or at startup even if you selected to automatically connect at startup</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox5</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Registration</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>To connect to the AOL Instant Messaging network, you will need to use a screen name from AIM, AOL, or .Mac.<br><br>If you do not currently have an AIM screen name, please click the button to create one.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonRegister</cstring> + </property> + <property name="text"> + <string>Re&gister New Account</string> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>90</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Accou&nt Preferences</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>groupBox73</cstring> + </property> + <property name="title"> + <string>Connection Preferences</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>optionOverrideServer</cstring> + </property> + <property name="text"> + <string>&Override default server information</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout58</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>lblServer</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Ser&ver:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>edtServerAddress</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The IP address or hostmask of the AIM server you wish to connect to.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The IP address or hostmask of the AIM server you wish to connect to. Normally you will want the default (login.oscar.aol.com).</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>edtServerAddress</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>login.oscar.aol.com</string> + </property> + <property name="toolTip" stdset="0"> + <string>The IP address or hostmask of the AIM server you wish to connect to.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The IP address or hostmask of the AIM server you wish to connect to. Normally you will want the default (login.oscar.aol.com).</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>lblPort</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Po&rt:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>sbxServerPort</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The port on the AIM server that you would like to connect to.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The port on the AIM server that you would like to connect to. Normally this is 5190.</string> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>sbxServerPort</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maxValue"> + <number>65534</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="value"> + <number>5190</number> + </property> + <property name="toolTip" stdset="0"> + <string>The port on the AIM server that you would like to connect to.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The port on the AIM server that you would like to connect to. Normally this is 5190.</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <spacer row="2" column="0"> + <property name="name"> + <cstring>spacer21</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>200</height> + </size> + </property> + </spacer> + <widget class="QComboBox" row="1" column="1"> + <property name="name"> + <cstring>encodingCombo</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Default to the following &encoding for messages:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>encodingCombo</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Pri&vacy</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup" row="0" column="0"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="title"> + <string>Visibility settings</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="2" column="0"> + <property name="name"> + <cstring>rbAllowPerimtList</cstring> + </property> + <property name="text"> + <string>Allow only from visible list</string> + </property> + </widget> + <widget class="QRadioButton" row="0" column="1"> + <property name="name"> + <cstring>rbBlockAll</cstring> + </property> + <property name="text"> + <string>Block all users</string> + </property> + </widget> + <widget class="QRadioButton" row="1" column="1"> + <property name="name"> + <cstring>rbBlockAIM</cstring> + </property> + <property name="text"> + <string>Block AIM users</string> + </property> + </widget> + <widget class="QRadioButton" row="2" column="1"> + <property name="name"> + <cstring>rbBlockDenyList</cstring> + </property> + <property name="text"> + <string>Block only from invisible list</string> + </property> + </widget> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>rbAllowAll</cstring> + </property> + <property name="text"> + <string>Allow all users</string> + </property> + </widget> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>rbAllowMyContacts</cstring> + </property> + <property name="text"> + <string>Allow only contact list's users</string> + </property> + </widget> + </grid> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>225</height> + </size> + </property> + </spacer> + </grid> + </widget> + </widget> + </grid> +</widget> +<customwidgets> + <customwidget> + <class>Kopete::UI::PasswordWidget</class> + <header location="local">kopetepasswordwidget.h</header> + <sizehint> + <width>50</width> + <height>50</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>1</hordata> + <verdata>0</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + <signal>changed()</signal> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154388dad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a190172af6a9690000000049454e44ae426082</data> + </image> +</images> +<connections> + <connection> + <sender>optionOverrideServer</sender> + <signal>toggled(bool)</signal> + <receiver>lblServer</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideServer</sender> + <signal>toggled(bool)</signal> + <receiver>lblPort</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideServer</sender> + <signal>toggled(bool)</signal> + <receiver>edtServerAddress</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideServer</sender> + <signal>toggled(bool)</signal> + <receiver>sbxServerPort</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>tabWidget6</tabstop> + <tabstop>edtAccountId</tabstop> + <tabstop>mAutoLogon</tabstop> + <tabstop>buttonRegister</tabstop> + <tabstop>optionOverrideServer</tabstop> + <tabstop>edtServerAddress</tabstop> + <tabstop>sbxServerPort</tabstop> + <tabstop>encodingCombo</tabstop> + <tabstop>rbAllowAll</tabstop> + <tabstop>rbAllowMyContacts</tabstop> + <tabstop>rbAllowPerimtList</tabstop> + <tabstop>rbBlockAll</tabstop> + <tabstop>rbBlockAIM</tabstop> + <tabstop>rbBlockDenyList</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kopetepasswordwidget.h</includehint> +</includehints> +</UI> diff --git a/kopete/protocols/oscar/aim/ui/aimeditaccountwidget.cpp b/kopete/protocols/oscar/aim/ui/aimeditaccountwidget.cpp new file mode 100644 index 00000000..de720e17 --- /dev/null +++ b/kopete/protocols/oscar/aim/ui/aimeditaccountwidget.cpp @@ -0,0 +1,172 @@ +#include "aimeditaccountwidget.h" +#include "aimeditaccountui.h" + +#include <qlayout.h> +#include <qcheckbox.h> +#include <qpushbutton.h> +#include <qradiobutton.h> +#include <qlineedit.h> +#include <qspinbox.h> + +#include <kdebug.h> +#include <krun.h> +#include <kpassdlg.h> +#include <kconfig.h> + +#include "kopetepassword.h" +#include "kopetepasswordwidget.h" + +#include "aimprotocol.h" +#include "aimaccount.h" + +AIMEditAccountWidget::AIMEditAccountWidget( AIMProtocol *protocol, + Kopete::Account *account, QWidget *parent, const char *name ) + : QWidget( parent, name ), KopeteEditAccountWidget( account ) +{ + //kdDebug(14152) << k_funcinfo << "Called." << endl; + + mAccount = dynamic_cast<AIMAccount*>( account ); + mProtocol = protocol; + + // create the gui (generated from a .ui file) + ( new QVBoxLayout( this ) )->setAutoAdd( true ); + mGui = new aimEditAccountUI( this, "AIMEditAccountWidget::mGui" ); + + // Read in the settings from the account if it exists + if ( mAccount ) + { + mGui->mPasswordWidget->load( &mAccount->password() ); + mGui->edtAccountId->setText( account->accountId() ); + //Remove me after we can change Account IDs (Matt) + mGui->edtAccountId->setDisabled( true ); + mGui->mAutoLogon->setChecked( account->excludeConnect() ); + QString serverEntry = account->configGroup()->readEntry( "Server", "login.oscar.aol.com" ); + int portEntry = account->configGroup()->readNumEntry( "Port", 5190 ); + if ( serverEntry != "login.oscar.aol.com" || portEntry != 5190 ) + mGui->optionOverrideServer->setChecked( true ); + else + mGui->optionOverrideServer->setChecked( false ); + + mGui->edtServerAddress->setText( serverEntry ); + mGui->sbxServerPort->setValue( portEntry ); + + using namespace AIM::PrivacySettings; + + int privacySetting = mAccount->configGroup()->readNumEntry( "PrivacySetting", AllowAll ); + switch( privacySetting ) + { + case AllowAll: + mGui->rbAllowAll->setChecked( true ); + break; + case AllowMyContacts: + mGui->rbAllowMyContacts->setChecked( true ); + break; + case AllowPremitList: + mGui->rbAllowPerimtList->setChecked( true ); + break; + case BlockAll: + mGui->rbBlockAll->setChecked( true ); + break; + case BlockAIM: + mGui->rbBlockAIM->setChecked( true ); + break; + case BlockDenyList: + mGui->rbBlockDenyList->setChecked( true ); + break; + default: + mGui->rbAllowAll->setChecked( true ); + } + + // Global Identity + mGui->mGlobalIdentity->setChecked( account->configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) ); + } + QObject::connect( mGui->buttonRegister, SIGNAL( clicked() ), this, SLOT( slotOpenRegister() ) ); + + /* Set tab order to password custom widget correctly */ + QWidget::setTabOrder( mGui->edtAccountId, mGui->mPasswordWidget->mRemembered ); + QWidget::setTabOrder( mGui->mPasswordWidget->mRemembered, mGui->mPasswordWidget->mPassword ); + QWidget::setTabOrder( mGui->mPasswordWidget->mPassword, mGui->mAutoLogon ); +} + +AIMEditAccountWidget::~AIMEditAccountWidget() +{} + +Kopete::Account *AIMEditAccountWidget::apply() +{ + kdDebug( 14152 ) << k_funcinfo << "Called." << endl; + + // If this is a new account, create it + if ( !mAccount ) + { + kdDebug( 14152 ) << k_funcinfo << "creating a new account" << endl; + QString newId = mGui->edtAccountId->text(); + mAccount = new AIMAccount( mProtocol, newId ); + } + + mGui->mPasswordWidget->save( &mAccount->password() ); + + mAccount->setExcludeConnect( mGui->mAutoLogon->isChecked() ); // save the autologon choice + if ( mGui->optionOverrideServer->isChecked() ) + { + static_cast<OscarAccount *>( mAccount )->setServerAddress( mGui->edtServerAddress->text() ); + static_cast<OscarAccount *>( mAccount )->setServerPort( mGui->sbxServerPort->value() ); + } + else + { + static_cast<OscarAccount *>( mAccount )->setServerAddress( "login.oscar.aol.com" ); + static_cast<OscarAccount *>( mAccount )->setServerPort( 5190 ); + } + + using namespace AIM::PrivacySettings; + int privacySetting = AllowAll; + + if ( mGui->rbAllowAll->isChecked() ) + privacySetting = AllowAll; + else if ( mGui->rbAllowMyContacts->isChecked() ) + privacySetting = AllowMyContacts; + else if ( mGui->rbAllowPerimtList->isChecked() ) + privacySetting = AllowPremitList; + else if ( mGui->rbBlockAll->isChecked() ) + privacySetting = BlockAll; + else if ( mGui->rbBlockAIM->isChecked() ) + privacySetting = BlockAIM; + else if ( mGui->rbBlockDenyList->isChecked() ) + privacySetting = BlockDenyList; + + mAccount->configGroup()->writeEntry( "PrivacySetting", privacySetting ); + mAccount->setPrivacySettings( privacySetting ); + + // Global Identity + mAccount->configGroup()->writeEntry( "ExcludeGlobalIdentity", mGui->mGlobalIdentity->isChecked() ); + return mAccount; +} + +bool AIMEditAccountWidget::validateData() +{ + //kdDebug(14152) << k_funcinfo << "Called." << endl; + + QString userName = mGui->edtAccountId->text(); + QString server = mGui->edtServerAddress->text(); + int port = mGui->sbxServerPort->value(); + + if ( userName.length() < 1 ) + return false; + + if ( port < 1 ) + return false; + + if ( server.length() < 1 ) + return false; + + // Seems good to me + //kdDebug(14152) << k_funcinfo << "Account data validated successfully." << endl; + return true; +} + +void AIMEditAccountWidget::slotOpenRegister() +{ + KRun::runURL( "http://my.screenname.aol.com/_cqr/login/login.psp?siteId=snshomepage&mcState=initialized&createSn=1", "text/html" ); +} + +#include "aimeditaccountwidget.moc" +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/protocols/oscar/aim/ui/aimeditaccountwidget.h b/kopete/protocols/oscar/aim/ui/aimeditaccountwidget.h new file mode 100644 index 00000000..ccb2b451 --- /dev/null +++ b/kopete/protocols/oscar/aim/ui/aimeditaccountwidget.h @@ -0,0 +1,58 @@ +/* + AIMeditaccountwidget.h - AIM Account Widget + + Copyright (c) 2003 by Chris TenHarmsel <tenharmsel@staticmethod.net> + + Kopete (c) 2003 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. * + * * + ************************************************************************* +*/ + + +#ifndef AIMEDITACCOUNTWIDGET_H +#define AIMEDITACCOUNTWIDGET_H + +#include <qwidget.h> +#include "editaccountwidget.h" +/** + * @author Chris TenHarmsel <tenharmsel@staticmethod.net> + */ + +namespace Kopete +{ +class Account; +} + +class AIMAccount; +class AIMProtocol; +class aimEditAccountUI; + +class AIMEditAccountWidget : public QWidget, public KopeteEditAccountWidget +{ +Q_OBJECT + +public: + AIMEditAccountWidget(AIMProtocol *protocol, Kopete::Account *account, + QWidget *parent=0, const char *name=0); + virtual ~AIMEditAccountWidget(); + + virtual bool validateData(); + virtual Kopete::Account *apply(); + +private slots: + void slotOpenRegister(); + +protected: + AIMAccount *mAccount; + AIMProtocol *mProtocol; + aimEditAccountUI *mGui; +}; +#endif +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/aim/ui/aiminfobase.ui b/kopete/protocols/oscar/aim/ui/aiminfobase.ui new file mode 100644 index 00000000..db22a574 --- /dev/null +++ b/kopete/protocols/oscar/aim/ui/aiminfobase.ui @@ -0,0 +1,246 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>AIMUserInfoWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>AIMUserInfoWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>360</width> + <height>408</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>360</width> + <height>400</height> + </size> + </property> + <property name="layoutMargin" stdset="0"> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>lblNickName</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Nickname:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>txtNickName</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>lblScreenName</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Screen name:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>txtScreenName</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>lblWarnLevel</cstring> + </property> + <property name="text"> + <string>Warning level:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>txtWarnLevel</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>lblIdleTime</cstring> + </property> + <property name="text"> + <string>Idle minutes:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>txtIdleTime</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>lblOnlineSince</cstring> + </property> + <property name="text"> + <string>Online since:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>txtOnlineSince</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>lblAwayMessage</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Away message:</string> + </property> + <property name="alignment"> + <set>AlignTop</set> + </property> + </widget> + <widget class="KTextBrowser"> + <property name="name"> + <cstring>txtAwayMessage</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="textFormat"> + <enum>AutoText</enum> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Profile:</string> + </property> + </widget> + <widget class="QFrame"> + <property name="name"> + <cstring>userInfoFrame</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>64</width> + <height>16</height> + </size> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + </widget> + </vbox> +</widget> +<tabstops> + <tabstop>txtNickName</tabstop> + <tabstop>txtScreenName</tabstop> + <tabstop>txtWarnLevel</tabstop> + <tabstop>txtIdleTime</tabstop> + <tabstop>txtOnlineSince</tabstop> + <tabstop>txtAwayMessage</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>ktextbrowser.h</includehint> +</includehints> +</UI> diff --git a/kopete/protocols/oscar/aim/ui/aimjoinchatbase.ui b/kopete/protocols/oscar/aim/ui/aimjoinchatbase.ui new file mode 100644 index 00000000..d1d93edf --- /dev/null +++ b/kopete/protocols/oscar/aim/ui/aimjoinchatbase.ui @@ -0,0 +1,124 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>AIMJoinChatBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>AIMJoinChatBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>343</width> + <height>99</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Please enter the name of the chat room you wish to join.</string> + </property> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <spacer row="2" column="0"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Maximum</enum> + </property> + <property name="sizeHint"> + <size> + <width>60</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="2" column="1"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Room &name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>roomName</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="1"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>E&xchange:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>exchange</cstring> + </property> + </widget> + <widget class="QLineEdit" row="2" column="2"> + <property name="name"> + <cstring>roomName</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QComboBox" row="3" column="2"> + <property name="name"> + <cstring>exchange</cstring> + </property> + </widget> + <spacer row="4" column="2"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/oscar/icons/Makefile.am b/kopete/protocols/oscar/icons/Makefile.am new file mode 100644 index 00000000..9143c6b4 --- /dev/null +++ b/kopete/protocols/oscar/icons/Makefile.am @@ -0,0 +1,2 @@ +kopeteicondir = $(kde_datadir)/kopete/icons +kopeteicon_ICON = AUTO diff --git a/kopete/protocols/oscar/icons/cr128-app-aim_protocol.png b/kopete/protocols/oscar/icons/cr128-app-aim_protocol.png Binary files differnew file mode 100644 index 00000000..05a91d59 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr128-app-aim_protocol.png diff --git a/kopete/protocols/oscar/icons/cr128-app-icq_protocol.png b/kopete/protocols/oscar/icons/cr128-app-icq_protocol.png Binary files differnew file mode 100644 index 00000000..4c976880 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr128-app-icq_protocol.png diff --git a/kopete/protocols/oscar/icons/cr16-action-aim_away.png b/kopete/protocols/oscar/icons/cr16-action-aim_away.png Binary files differnew file mode 100644 index 00000000..36fa2beb --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-aim_away.png diff --git a/kopete/protocols/oscar/icons/cr16-action-aim_connecting.mng b/kopete/protocols/oscar/icons/cr16-action-aim_connecting.mng Binary files differnew file mode 100644 index 00000000..90417a14 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-aim_connecting.mng diff --git a/kopete/protocols/oscar/icons/cr16-action-aim_offline.png b/kopete/protocols/oscar/icons/cr16-action-aim_offline.png Binary files differnew file mode 100644 index 00000000..c070d66b --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-aim_offline.png diff --git a/kopete/protocols/oscar/icons/cr16-action-aim_online.png b/kopete/protocols/oscar/icons/cr16-action-aim_online.png Binary files differnew file mode 100644 index 00000000..ff1087cb --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-aim_online.png diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_away.png b/kopete/protocols/oscar/icons/cr16-action-icq_away.png Binary files differnew file mode 100644 index 00000000..81dfb456 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-icq_away.png diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_connecting.mng b/kopete/protocols/oscar/icons/cr16-action-icq_connecting.mng Binary files differnew file mode 100644 index 00000000..1a7aa5a5 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-icq_connecting.mng diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_dnd.png b/kopete/protocols/oscar/icons/cr16-action-icq_dnd.png Binary files differnew file mode 100644 index 00000000..cf94ee0f --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-icq_dnd.png diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_ffc.png b/kopete/protocols/oscar/icons/cr16-action-icq_ffc.png Binary files differnew file mode 100644 index 00000000..51f51623 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-icq_ffc.png diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_invisible.png b/kopete/protocols/oscar/icons/cr16-action-icq_invisible.png Binary files differnew file mode 100644 index 00000000..c7e37ce9 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-icq_invisible.png diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_na.png b/kopete/protocols/oscar/icons/cr16-action-icq_na.png Binary files differnew file mode 100644 index 00000000..b1aa91af --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-icq_na.png diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_occupied.png b/kopete/protocols/oscar/icons/cr16-action-icq_occupied.png Binary files differnew file mode 100644 index 00000000..d4689965 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-icq_occupied.png diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_offline.png b/kopete/protocols/oscar/icons/cr16-action-icq_offline.png Binary files differnew file mode 100644 index 00000000..a9d11031 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-icq_offline.png diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_online.png b/kopete/protocols/oscar/icons/cr16-action-icq_online.png Binary files differnew file mode 100644 index 00000000..8a9dac28 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-action-icq_online.png diff --git a/kopete/protocols/oscar/icons/cr16-app-aim_protocol.png b/kopete/protocols/oscar/icons/cr16-app-aim_protocol.png Binary files differnew file mode 100644 index 00000000..1e89df85 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-app-aim_protocol.png diff --git a/kopete/protocols/oscar/icons/cr16-app-icq_protocol.png b/kopete/protocols/oscar/icons/cr16-app-icq_protocol.png Binary files differnew file mode 100644 index 00000000..eda0f8c4 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr16-app-icq_protocol.png diff --git a/kopete/protocols/oscar/icons/cr32-app-aim_protocol.png b/kopete/protocols/oscar/icons/cr32-app-aim_protocol.png Binary files differnew file mode 100644 index 00000000..590aee96 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr32-app-aim_protocol.png diff --git a/kopete/protocols/oscar/icons/cr32-app-icq_protocol.png b/kopete/protocols/oscar/icons/cr32-app-icq_protocol.png Binary files differnew file mode 100644 index 00000000..0f153eac --- /dev/null +++ b/kopete/protocols/oscar/icons/cr32-app-icq_protocol.png diff --git a/kopete/protocols/oscar/icons/cr48-app-aim_protocol.png b/kopete/protocols/oscar/icons/cr48-app-aim_protocol.png Binary files differnew file mode 100644 index 00000000..8aeb38e1 --- /dev/null +++ b/kopete/protocols/oscar/icons/cr48-app-aim_protocol.png diff --git a/kopete/protocols/oscar/icons/cr48-app-icq_protocol.png b/kopete/protocols/oscar/icons/cr48-app-icq_protocol.png Binary files differnew file mode 100644 index 00000000..ee04d73b --- /dev/null +++ b/kopete/protocols/oscar/icons/cr48-app-icq_protocol.png diff --git a/kopete/protocols/oscar/icons/cr64-app-aim_protocol.png b/kopete/protocols/oscar/icons/cr64-app-aim_protocol.png Binary files differnew file mode 100644 index 00000000..49e8c91a --- /dev/null +++ b/kopete/protocols/oscar/icons/cr64-app-aim_protocol.png diff --git a/kopete/protocols/oscar/icons/cr64-app-icq_protocol.png b/kopete/protocols/oscar/icons/cr64-app-icq_protocol.png Binary files differnew file mode 100644 index 00000000..7b9d8cad --- /dev/null +++ b/kopete/protocols/oscar/icons/cr64-app-icq_protocol.png diff --git a/kopete/protocols/oscar/icons/hi16-action-aim_away.png b/kopete/protocols/oscar/icons/hi16-action-aim_away.png Binary files differnew file mode 100644 index 00000000..3924d7c6 --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-aim_away.png diff --git a/kopete/protocols/oscar/icons/hi16-action-aim_connecting.mng b/kopete/protocols/oscar/icons/hi16-action-aim_connecting.mng Binary files differnew file mode 100644 index 00000000..19767318 --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-aim_connecting.mng diff --git a/kopete/protocols/oscar/icons/hi16-action-aim_offline.png b/kopete/protocols/oscar/icons/hi16-action-aim_offline.png Binary files differnew file mode 100644 index 00000000..49a4ef5f --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-aim_offline.png diff --git a/kopete/protocols/oscar/icons/hi16-action-aim_online.png b/kopete/protocols/oscar/icons/hi16-action-aim_online.png Binary files differnew file mode 100644 index 00000000..e6e53d90 --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-aim_online.png diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_away.png b/kopete/protocols/oscar/icons/hi16-action-icq_away.png Binary files differnew file mode 100644 index 00000000..ccee571d --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-icq_away.png diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_connecting.mng b/kopete/protocols/oscar/icons/hi16-action-icq_connecting.mng Binary files differnew file mode 100644 index 00000000..a44714b7 --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-icq_connecting.mng diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_dnd.png b/kopete/protocols/oscar/icons/hi16-action-icq_dnd.png Binary files differnew file mode 100644 index 00000000..600e2438 --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-icq_dnd.png diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_ffc.png b/kopete/protocols/oscar/icons/hi16-action-icq_ffc.png Binary files differnew file mode 100644 index 00000000..d31ee480 --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-icq_ffc.png diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_invisible.png b/kopete/protocols/oscar/icons/hi16-action-icq_invisible.png Binary files differnew file mode 100644 index 00000000..c5ed807d --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-icq_invisible.png diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_na.png b/kopete/protocols/oscar/icons/hi16-action-icq_na.png Binary files differnew file mode 100644 index 00000000..7df16924 --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-icq_na.png diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_occupied.png b/kopete/protocols/oscar/icons/hi16-action-icq_occupied.png Binary files differnew file mode 100644 index 00000000..154bc21d --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-icq_occupied.png diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_offline.png b/kopete/protocols/oscar/icons/hi16-action-icq_offline.png Binary files differnew file mode 100644 index 00000000..42189ff5 --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-icq_offline.png diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_online.png b/kopete/protocols/oscar/icons/hi16-action-icq_online.png Binary files differnew file mode 100644 index 00000000..52e431a2 --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-action-icq_online.png diff --git a/kopete/protocols/oscar/icons/hi16-app-aim_protocol.png b/kopete/protocols/oscar/icons/hi16-app-aim_protocol.png Binary files differnew file mode 100644 index 00000000..e6e53d90 --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-app-aim_protocol.png diff --git a/kopete/protocols/oscar/icons/hi16-app-icq_protocol.png b/kopete/protocols/oscar/icons/hi16-app-icq_protocol.png Binary files differnew file mode 100644 index 00000000..45b2a9cd --- /dev/null +++ b/kopete/protocols/oscar/icons/hi16-app-icq_protocol.png diff --git a/kopete/protocols/oscar/icons/hi32-app-aim_protocol.png b/kopete/protocols/oscar/icons/hi32-app-aim_protocol.png Binary files differnew file mode 100644 index 00000000..f1fe71d2 --- /dev/null +++ b/kopete/protocols/oscar/icons/hi32-app-aim_protocol.png diff --git a/kopete/protocols/oscar/icons/hi32-app-icq_protocol.png b/kopete/protocols/oscar/icons/hi32-app-icq_protocol.png Binary files differnew file mode 100644 index 00000000..e8a3f030 --- /dev/null +++ b/kopete/protocols/oscar/icons/hi32-app-icq_protocol.png diff --git a/kopete/protocols/oscar/icq/Makefile.am b/kopete/protocols/oscar/icq/Makefile.am new file mode 100644 index 00000000..defadf37 --- /dev/null +++ b/kopete/protocols/oscar/icq/Makefile.am @@ -0,0 +1,22 @@ +SUBDIRS = ui . +METASOURCES = AUTO +AM_CPPFLAGS = -I$(srcdir)/../ \ + -I$(srcdir)/ui/ \ + -I$(top_builddir)/kopete/protocols/oscar/icq/ui \ + -I$(srcdir)/../liboscar \ + $(KOPETE_INCLUDES) $(all_includes) + +kde_module_LTLIBRARIES = kopete_icq.la + +kopete_icq_la_SOURCES = icqpresence.cpp icqaccount.cpp icqcontact.cpp icqprotocol.cpp +# icquserinfo.cpp icqreadaway.cpp icqsendsmsdialog.cpp + +kopete_icq_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries) +kopete_icq_la_LIBADD = ../libkopete_oscar.la \ + $(top_builddir)/kopete/libkopete/libkopete.la ui/libkopeteicqui.la + +service_DATA = kopete_icq.desktop +servicedir = $(kde_servicesdir) + +mime_DATA = x-icq.desktop +mimedir = $(kde_mimedir)/application diff --git a/kopete/protocols/oscar/icq/icqaccount.cpp b/kopete/protocols/oscar/icq/icqaccount.cpp new file mode 100644 index 00000000..9a071442 --- /dev/null +++ b/kopete/protocols/oscar/icq/icqaccount.cpp @@ -0,0 +1,529 @@ +/* + icqaccount.cpp - ICQ Account Class + + Copyright (c) 2002 by Chris TenHarmsel <tenharmsel@staticmethod.net> + Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk> + 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 <qfile.h> + +#include <kconfig.h> +#include <kdebug.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kmdcodec.h> +#include <kmessagebox.h> + +#include "kopeteawayaction.h" +#include "kopetemessage.h" +#include "kopetecontactlist.h" +#include "kopeteuiglobal.h" + +#include "client.h" +#include "icquserinfo.h" +#include "oscarsettings.h" +#include "oscarutils.h" +#include "ssimanager.h" + +#include "icqcontact.h" +#include "icqprotocol.h" +#include "icqaccount.h" + +#include "oscarvisibilitydialog.h" + +ICQMyselfContact::ICQMyselfContact( ICQAccount *acct ) : OscarMyselfContact( acct ) +{ + QObject::connect( acct->engine(), SIGNAL( loggedIn() ), this, SLOT( fetchShortInfo() ) ); + QObject::connect( acct->engine(), SIGNAL( receivedIcqShortInfo( const QString& ) ), + this, SLOT( receivedShortInfo( const QString& ) ) ); +} + +void ICQMyselfContact::userInfoUpdated() +{ + DWORD extendedStatus = details().extendedStatus(); + kdDebug( OSCAR_ICQ_DEBUG ) << k_funcinfo << "extendedStatus is " << QString::number( extendedStatus, 16 ) << endl; + ICQ::Presence presence = ICQ::Presence::fromOscarStatus( extendedStatus & 0xffff ); + setOnlineStatus( presence.toOnlineStatus() ); + setProperty( Kopete::Global::Properties::self()->awayMessage(), static_cast<ICQAccount*>( account() )->engine()->statusMessage() ); +} + +void ICQMyselfContact::receivedShortInfo( const QString& contact ) +{ + if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) ) + return; + + ICQShortInfo shortInfo = static_cast<ICQAccount*>( account() )->engine()->getShortInfo( contact ); + if ( !shortInfo.nickname.isEmpty() ) + { + setProperty( Kopete::Global::Properties::self()->nickName(), static_cast<ICQAccount*>( account() )->defaultCodec()->toUnicode( shortInfo.nickname ) ); + } +} + +void ICQMyselfContact::fetchShortInfo() +{ + static_cast<ICQAccount*>( account() )->engine()->requestShortInfo( contactId() ); +} + +ICQAccount::ICQAccount(Kopete::Protocol *parent, QString accountID, const char *name) + : OscarAccount(parent, accountID, name, true) +{ + kdDebug(14152) << k_funcinfo << accountID << ": Called."<< endl; + setMyself( new ICQMyselfContact( this ) ); + myself()->setOnlineStatus( ICQ::Presence( ICQ::Presence::Offline, ICQ::Presence::Visible ).toOnlineStatus() ); + + m_visibilityDialog = 0; + + QString nickName = configGroup()->readEntry("NickName", QString::null); + mWebAware = configGroup()->readBoolEntry( "WebAware", false ); + mHideIP = configGroup()->readBoolEntry( "HideIP", true ); + mInitialStatusMessage = QString::null; + + QObject::connect( Kopete::ContactList::self(), SIGNAL( globalIdentityChanged( const QString&, const QVariant& ) ), + this, SLOT( slotGlobalIdentityChanged( const QString&, const QVariant& ) ) ); + + QObject::connect( this, SIGNAL( buddyIconChanged() ), this, SLOT( slotBuddyIconChanged() ) ); + + //setIgnoreUnknownContacts(pluginData(protocol(), "IgnoreUnknownContacts").toUInt() == 1); + + /* FIXME: need to do this when web aware or hide ip change + if(isConnected() && (oldhideip != mHideIP || oldwebaware != mWebAware)) + { + kdDebug(14153) << k_funcinfo << + "sending status to reflect HideIP and WebAware settings" << endl; + //setStatus(mStatus, QString::null); + }*/ +} + +ICQAccount::~ICQAccount() +{ +} + +ICQProtocol* ICQAccount::protocol() +{ + return static_cast<ICQProtocol*>(OscarAccount::protocol()); +} + + +ICQ::Presence ICQAccount::presence() +{ + return ICQ::Presence::fromOnlineStatus( myself()->onlineStatus() ); +} + + +KActionMenu* ICQAccount::actionMenu() +{ + KActionMenu* actionMenu = Kopete::Account::actionMenu(); + + actionMenu->popupMenu()->insertSeparator(); + + KToggleAction* actionInvisible = + new KToggleAction( i18n( "In&visible" ), + ICQ::Presence( presence().type(), ICQ::Presence::Invisible ).toOnlineStatus().iconFor( this ), + 0, this, SLOT( slotToggleInvisible() ), this ); + actionInvisible->setChecked( presence().visibility() == ICQ::Presence::Invisible ); + actionMenu->insert( actionInvisible ); + + actionMenu->popupMenu()->insertSeparator(); + actionMenu->insert( new KToggleAction( i18n( "Set Visibility..." ), 0, 0, + this, SLOT( slotSetVisiblility() ), this, + "ICQAccount::mActionSetVisibility") ); + //actionMenu->insert( new KToggleAction( i18n( "Send &SMS..." ), 0, 0, this, SLOT( slotSendSMS() ), this, "ICQAccount::mActionSendSMS") ); + + return actionMenu; +} + + +void ICQAccount::connectWithPassword( const QString &password ) +{ + if ( password.isNull() ) + return; + + kdDebug(14153) << k_funcinfo << "accountId='" << accountId() << "'" << endl; + + Kopete::OnlineStatus status = initialStatus(); + if ( status == Kopete::OnlineStatus() && + status.status() == Kopete::OnlineStatus::Unknown ) + //use default online in case of invalid online status for connecting + status = Kopete::OnlineStatus( Kopete::OnlineStatus::Online ); + ICQ::Presence pres = ICQ::Presence::fromOnlineStatus( status ); + bool accountIsOffline = ( presence().type() == ICQ::Presence::Offline || + myself()->onlineStatus() == protocol()->statusManager()->connectingStatus() ); + + if ( accountIsOffline ) + { + myself()->setOnlineStatus( protocol()->statusManager()->connectingStatus() ); + QString icqNumber = accountId(); + kdDebug(14153) << k_funcinfo << "Logging in as " << icqNumber << endl ; + QString server = configGroup()->readEntry( "Server", QString::fromLatin1( "login.oscar.aol.com" ) ); + uint port = configGroup()->readNumEntry( "Port", 5190 ); + Connection* c = setupConnection( server, port ); + + //set up the settings for the account + Oscar::Settings* oscarSettings = engine()->clientSettings(); + oscarSettings->setWebAware( configGroup()->readBoolEntry( "WebAware", false ) ); + oscarSettings->setHideIP( configGroup()->readBoolEntry( "HideIP", true ) ); + //FIXME: also needed for the other call to setStatus (in setPresenceTarget) + DWORD status = pres.toOscarStatus(); + + if ( !mHideIP ) + status |= ICQ::StatusCode::SHOWIP; + if ( mWebAware ) + status |= ICQ::StatusCode::WEBAWARE; + + engine()->setStatus( status, mInitialStatusMessage ); + updateVersionUpdaterStamp(); + engine()->start( server, port, accountId(), password ); + engine()->connectToServer( c, server, true /* doAuth */ ); + + mInitialStatusMessage = QString::null; + } +} + +void ICQAccount::disconnected( DisconnectReason reason ) +{ + kdDebug(14153) << k_funcinfo << "Attempting to set status offline" << endl; + ICQ::Presence presOffline = ICQ::Presence( ICQ::Presence::Offline, presence().visibility() ); + myself()->setOnlineStatus( presOffline.toOnlineStatus() ); + + QDictIterator<Kopete::Contact> it( contacts() ); + for( ; it.current(); ++it ) + { + OscarContact* oc = dynamic_cast<OscarContact*>( it.current() ); + if ( oc ) + { + if ( oc->ssiItem().waitingAuth() ) + oc->setOnlineStatus( protocol()->statusManager()->waitingForAuth() ); + else + oc->setOnlineStatus( ICQ::Presence( ICQ::Presence::Offline, ICQ::Presence::Visible ).toOnlineStatus() ); + } + } + + OscarAccount::disconnected( reason ); +} + + +void ICQAccount::slotToggleInvisible() +{ + using namespace ICQ; + setInvisible( (presence().visibility() == Presence::Visible) ? Presence::Invisible : Presence::Visible ); +} + +void ICQAccount::slotSetVisiblility() +{ + if( !isConnected() ) + { + KMessageBox::sorry( Kopete::UI::Global::mainWidget(), + i18n("You must be online to set users visibility."), + i18n("ICQ Plugin") ); + return; + } + + if ( !m_visibilityDialog ) + { + m_visibilityDialog = new OscarVisibilityDialog( engine(), Kopete::UI::Global::mainWidget() ); + QObject::connect( m_visibilityDialog, SIGNAL( closing() ), + this, SLOT( slotVisibilityDialogClosed() ) ); + + //add all contacts; + OscarVisibilityDialog::ContactMap contactMap; + //temporary map for faster lookup of contactId + QMap<QString, QString> revContactMap; + + QValueList<Oscar::SSI> contactList = engine()->ssiManager()->contactList(); + QValueList<Oscar::SSI>::const_iterator it, cEnd = contactList.constEnd(); + + for ( it = contactList.constBegin(); it != cEnd; ++it ) + { + QString contactId = ( *it ).name(); + + OscarContact* oc = dynamic_cast<OscarContact*>( contacts()[( *it ).name()] ); + if ( oc ) + { //for better orientation in lists use nickName and icq number + QString screenName( "%1 (%2)" ); + screenName = screenName.arg( oc->nickName(), contactId); + contactMap.insert( screenName, contactId ); + revContactMap.insert( contactId, screenName ); + } + else + { + contactMap.insert( contactId, contactId ); + revContactMap.insert( contactId, contactId ); + } + } + m_visibilityDialog->addContacts( contactMap ); + + //add contacts from visible list + QStringList tmpList; + + contactList = engine()->ssiManager()->visibleList(); + cEnd = contactList.constEnd(); + + for ( it = contactList.constBegin(); it != cEnd; ++it ) + tmpList.append( revContactMap[( *it ).name()] ); + + m_visibilityDialog->addVisibleContacts( tmpList ); + + //add contacts from invisible list + tmpList.clear(); + + contactList = engine()->ssiManager()->invisibleList(); + cEnd = contactList.constEnd(); + + for ( it = contactList.constBegin(); it != cEnd; ++it ) + tmpList.append( revContactMap[( *it ).name()] ); + + m_visibilityDialog->addInvisibleContacts( tmpList ); + + m_visibilityDialog->resize( 550, 350 ); + m_visibilityDialog->show(); + } + else + { + m_visibilityDialog->raise(); + } +} + +void ICQAccount::slotVisibilityDialogClosed() +{ + m_visibilityDialog->delayedDestruct(); + m_visibilityDialog = 0L; +} + +void ICQAccount::setAway( bool away, const QString &awayReason ) +{ + kdDebug(14153) << k_funcinfo << "account='" << accountId() << "'" << endl; + if ( away ) + setPresenceType( ICQ::Presence::Away, awayReason ); + else + setPresenceType( ICQ::Presence::Online ); +} + + +void ICQAccount::setInvisible( ICQ::Presence::Visibility vis ) +{ + ICQ::Presence pres = presence(); + if ( vis == pres.visibility() ) + return; + + kdDebug(14153) << k_funcinfo << "changing invisible setting to " << (int)vis << endl; + setPresenceTarget( ICQ::Presence( pres.type(), vis ) ); +} + +void ICQAccount::setPresenceType( ICQ::Presence::Type type, const QString &message ) +{ + ICQ::Presence pres = presence(); + kdDebug(14153) << k_funcinfo << "new type=" << (int)type << ", old type=" << (int)pres.type() << ", new message=" << message << endl; + //setAwayMessage(awayMessage); + setPresenceTarget( ICQ::Presence( type, pres.visibility() ), message ); +} + +void ICQAccount::setPresenceTarget( const ICQ::Presence &newPres, const QString &message ) +{ + bool targetIsOffline = (newPres.type() == ICQ::Presence::Offline); + bool accountIsOffline = ( presence().type() == ICQ::Presence::Offline || + myself()->onlineStatus() == protocol()->statusManager()->connectingStatus() ); + + if ( targetIsOffline ) + { + OscarAccount::disconnect(); + // allow toggling invisibility when offline + myself()->setOnlineStatus( newPres.toOnlineStatus() ); + } + else if ( accountIsOffline ) + { + mInitialStatusMessage = message; + OscarAccount::connect( newPres.toOnlineStatus() ); + } + else + { + engine()->setStatus( newPres.toOscarStatus(), message ); + } +} + + +void ICQAccount::setOnlineStatus( const Kopete::OnlineStatus& status, const QString& reason ) +{ + if ( status.status() == Kopete::OnlineStatus::Invisible ) + { + // called from outside, i.e. not by our custom action menu entry... + + if ( presence().type() == ICQ::Presence::Offline ) + { + // ...when we are offline go online invisible. + setPresenceTarget( ICQ::Presence( ICQ::Presence::Online, ICQ::Presence::Invisible ) ); + } + else + { + // ...when we are not offline set invisible. + setInvisible( ICQ::Presence::Invisible ); + } + } + else + { + setPresenceType( ICQ::Presence::fromOnlineStatus( status ).type(), reason ); + } +} + + +OscarContact *ICQAccount::createNewContact( const QString &contactId, Kopete::MetaContact *parentContact, const SSI& ssiItem ) +{ + ICQContact* contact = new ICQContact( this, contactId, parentContact, QString::null, ssiItem ); + if ( !ssiItem.alias().isEmpty() ) + contact->setProperty( Kopete::Global::Properties::self()->nickName(), ssiItem.alias() ); + + if ( isConnected() ) + contact->loggedIn(); + + return contact; +} + +QString ICQAccount::sanitizedMessage( const QString& message ) +{ + return Kopete::Message::escape( message ); +} + + +void ICQAccount::slotGlobalIdentityChanged( const QString& key, const QVariant& value ) +{ + //do something with the photo + kdDebug(14153) << k_funcinfo << "Global identity changed" << endl; + kdDebug(14153) << k_funcinfo << "key: " << key << endl; + kdDebug(14153) << k_funcinfo << "value: " << value << endl; + + if( !configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) ) + { + if ( key == Kopete::Global::Properties::self()->nickName().key() ) + { + //edit ssi item to change alias (if possible) + } + + if ( key == Kopete::Global::Properties::self()->photo().key() ) + { + setBuddyIcon( value.toString() ); + } + } +} + +void ICQAccount::slotBuddyIconChanged() +{ + // need to disconnect because we could end up with many connections + QObject::disconnect( engine(), SIGNAL( iconServerConnected() ), this, SLOT( slotBuddyIconChanged() ) ); + if ( !engine()->isActive() ) + { + QObject::connect( engine(), SIGNAL( iconServerConnected() ), this, SLOT( slotBuddyIconChanged() ) ); + return; + } + + QString photoPath = myself()->property( Kopete::Global::Properties::self()->photo() ).value().toString(); + + SSIManager* ssi = engine()->ssiManager(); + Oscar::SSI item = ssi->findItemForIconByRef( 1 ); + + if ( photoPath.isEmpty() ) + { + if ( item ) + { + kdDebug(14153) << k_funcinfo << "Removing icon hash item from ssi" << endl; + Oscar::SSI s(item); + + //remove hash and alias + QValueList<TLV> tList( item.tlvList() ); + TLV t = Oscar::findTLV( tList, 0x00D5 ); + if ( t ) + tList.remove( t ); + + t = Oscar::findTLV( tList, 0x0131 ); + if ( t ) + tList.remove( t ); + + item.setTLVList( tList ); + //s is old, item is new. modification will occur + engine()->modifySSIItem( s, item ); + } + } + else + { + QFile iconFile( photoPath ); + iconFile.open( IO_ReadOnly ); + + KMD5 iconHash; + iconHash.update( iconFile ); + kdDebug(14153) << k_funcinfo << "hash is :" << iconHash.hexDigest() << endl; + + //find old item, create updated item + if ( !item ) + { + kdDebug(14153) << k_funcinfo << "no existing icon hash item in ssi. creating new" << endl; + + TLV t; + t.type = 0x00D5; + t.data.resize( 18 ); + t.data[0] = 0x01; + t.data[1] = 0x10; + memcpy(t.data.data() + 2, iconHash.rawDigest(), 16); + t.length = t.data.size(); + + //alias, it's always empty + TLV t2; + t2.type = 0x0131; + t2.length = 0; + + QValueList<Oscar::TLV> list; + list.append( t ); + list.append( t2 ); + + Oscar::SSI s( "1", 0, ssi->nextContactId(), ROSTER_BUDDYICONS, list ); + + //item is a non-valid ssi item, so the function will add an item + kdDebug(14153) << k_funcinfo << "setting new icon item" << endl; + engine()->modifySSIItem( item, s ); + } + else + { //found an item + Oscar::SSI s(item); + kdDebug(14153) << k_funcinfo << "modifying old item in ssi." << endl; + QValueList<TLV> tList( item.tlvList() ); + + TLV t = Oscar::findTLV( tList, 0x00D5 ); + if ( t ) + tList.remove( t ); + else + t.type = 0x00D5; + + t.data.resize( 18 ); + t.data[0] = 0x01; + t.data[1] = 0x10; + memcpy(t.data.data() + 2, iconHash.rawDigest(), 16); + t.length = t.data.size(); + tList.append( t ); + + //add empty alias + t = Oscar::findTLV( tList, 0x0131 ); + if ( !t ) + { + t.type = 0x0131; + t.length = 0; + tList.append( t ); + } + + item.setTLVList( tList ); + //s is old, item is new. modification will occur + engine()->modifySSIItem( s, item ); + } + iconFile.close(); + } +} + +#include "icqaccount.moc" + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/icq/icqaccount.h b/kopete/protocols/oscar/icq/icqaccount.h new file mode 100644 index 00000000..f6231ec9 --- /dev/null +++ b/kopete/protocols/oscar/icq/icqaccount.h @@ -0,0 +1,105 @@ +/* + icqaccount.h - ICQ Account Class Header + + Copyright (c) 2002 by Chris TenHarmsel <tenharmsel@staticmethod.net> + Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk> + 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. * + * * + ************************************************************************* + +*/ + +#ifndef ICQACCOUNT_H +#define ICQACCOUNT_H + +#include "oscaraccount.h" +#include "oscarmyselfcontact.h" + +#include "icqpresence.h" +#include "oscartypeclasses.h" + +class KAction; +namespace Kopete { class AwayAction; } +class ICQProtocol; +class ICQAccount; +class OscarVisibilityDialog; + +class ICQMyselfContact : public OscarMyselfContact +{ +Q_OBJECT +public: + ICQMyselfContact( ICQAccount *acct ); + void userInfoUpdated(); + +public slots: + void receivedShortInfo( const QString& ); + void fetchShortInfo(); +}; + + +class ICQAccount : public OscarAccount +{ +Q_OBJECT + +public: + ICQAccount( Kopete::Protocol *parent, QString accountID, const char *name = 0L ); + virtual ~ICQAccount(); + + ICQProtocol *protocol(); + + // Accessor method for the action menu + virtual KActionMenu* actionMenu(); + + /** Reimplementation from Kopete::Account */ + void setOnlineStatus( const Kopete::OnlineStatus&, const QString& ); + + virtual void setAway( bool away, const QString &awayReason ); + + void connectWithPassword( const QString &password ); + + void setUserProfile( const QString &profile ); + +protected: + virtual OscarContact *createNewContact( const QString &contactId, Kopete::MetaContact *parentContact, const SSI& ssiItem ); + + virtual QString sanitizedMessage( const QString& message ); + +protected slots: + virtual void disconnected( DisconnectReason reason ); + + +private: + ICQ::Presence presence(); + + void setInvisible( ICQ::Presence::Visibility ); + void setPresenceType( ICQ::Presence::Type, const QString &awayMessage = QString::null ); + void setPresenceTarget( const ICQ::Presence &presence, const QString &message = QString::null ); + + //const unsigned long fullStatus( const unsigned long plainStatus ); + +private slots: + void slotToggleInvisible(); + + void slotSetVisiblility(); + void slotVisibilityDialogClosed(); + + void slotGlobalIdentityChanged( const QString& key, const QVariant& value ); + + void slotBuddyIconChanged(); + +private: + bool mWebAware; + bool mHideIP; + QString mInitialStatusMessage; + OscarVisibilityDialog* m_visibilityDialog; +}; + +#endif +//kate: indent-mode csands; diff --git a/kopete/protocols/oscar/icq/icqcontact.cpp b/kopete/protocols/oscar/icq/icqcontact.cpp new file mode 100644 index 00000000..8ba8d195 --- /dev/null +++ b/kopete/protocols/oscar/icq/icqcontact.cpp @@ -0,0 +1,939 @@ +/* + icqontact.cpp - Oscar Protocol Plugin + + Copyright (c) 2003 by Stefan Gehn <metz AT gehn.net> + Copyright (c) 2003 by Olivier Goffart + Kopete (c) 2003-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 "icqcontact.h" + +#include <qtimer.h> +#include <qimage.h> +#include <qfile.h> + +#include <kaction.h> +#include <kactionclasses.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <knotifyclient.h> +#include <kpassivepopup.h> +#include <kinputdialog.h> +#include <kmdcodec.h> +#include <kstandarddirs.h> + +#include "kopetechatsessionmanager.h" +#include "kopeteuiglobal.h" +#include "kopetemetacontact.h" + +#include "icquserinfo.h" +#include "icqreadaway.h" +#include "icqprotocol.h" +#include "icqaccount.h" +#include "icqpresence.h" +#include "icquserinfowidget.h" +#include "icqauthreplydialog.h" + +#include "client.h" +#include "oscarutils.h" +#include "oscarencodingselectiondialog.h" +#include "ssimanager.h" + +ICQContact::ICQContact( ICQAccount *account, const QString &name, Kopete::MetaContact *parent, + const QString& icon, const Oscar::SSI& ssiItem ) +: OscarContact( account, name, parent, icon, ssiItem ) +{ + mProtocol = static_cast<ICQProtocol *>(protocol()); + m_infoWidget = 0L; + m_requestingNickname = false; + m_oesd = 0; + m_buddyIconDirty = false; + + if ( ssiItem.waitingAuth() ) + setOnlineStatus( mProtocol->statusManager()->waitingForAuth() ); + else + setOnlineStatus( ICQ::Presence( ICQ::Presence::Offline, ICQ::Presence::Visible ).toOnlineStatus() ); + + QObject::connect( mAccount->engine(), SIGNAL( loggedIn() ), this, SLOT( loggedIn() ) ); + //QObject::connect( mAccount->engine(), SIGNAL( userIsOnline( const QString& ) ), this, SLOT( userOnline( const QString&, UserDetails ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( userIsOffline( const QString& ) ), this, SLOT( userOffline( const QString& ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( authRequestReceived( const QString&, const QString& ) ), + this, SLOT( slotGotAuthRequest( const QString&, const QString& ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( authReplyReceived( const QString&, const QString&, bool ) ), + this, SLOT( slotGotAuthReply(const QString&, const QString&, bool ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( receivedIcqShortInfo( const QString& ) ), + this, SLOT( receivedShortInfo( const QString& ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( receivedIcqLongInfo( const QString& ) ), + this, SLOT( receivedLongInfo( const QString& ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( receivedUserInfo( const QString&, const UserDetails& ) ), + this, SLOT( userInfoUpdated( const QString&, const UserDetails& ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( receivedAwayMessage( const QString&, const QString& ) ), + this, SLOT( receivedStatusMessage( const QString&, const QString& ) ) ); + QObject::connect( mAccount->engine(), SIGNAL( receivedAwayMessage( const Oscar::Message& ) ), + this, SLOT( receivedStatusMessage( const Oscar::Message& ) ) ); + QObject::connect( this, SIGNAL( featuresUpdated() ), this, SLOT( updateFeatures() ) ); + QObject::connect( mAccount->engine(), SIGNAL( iconServerConnected() ), + this, SLOT( requestBuddyIcon() ) ); + QObject::connect( mAccount->engine(), SIGNAL( haveIconForContact( const QString&, QByteArray ) ), + this, SLOT( haveIcon( const QString&, QByteArray ) ) ); + +} + +ICQContact::~ICQContact() +{ + delete m_infoWidget; +} + +void ICQContact::updateSSIItem() +{ + //kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << endl; + if ( m_ssiItem.waitingAuth() ) + setOnlineStatus( mProtocol->statusManager()->waitingForAuth() ); + + if ( m_ssiItem.type() != 0xFFFF && m_ssiItem.waitingAuth() == false && + onlineStatus() == Kopete::OnlineStatus::Unknown ) + { + //make sure they're offline + setOnlineStatus( ICQ::Presence( ICQ::Presence::Offline, ICQ::Presence::Visible ).toOnlineStatus() ); + } +} + + +void ICQContact::userInfoUpdated( const QString& contact, const UserDetails& details ) +{ + //kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << contact << contactId() << endl; + if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) ) + return; + + // invalidate old away message if user was offline + if ( !isOnline() ) + removeProperty( mProtocol->awayMessage ); + + kdDebug( OSCAR_ICQ_DEBUG ) << k_funcinfo << "extendedStatus is " << details.extendedStatus() << endl; + ICQ::Presence presence = ICQ::Presence::fromOscarStatus( details.extendedStatus() & 0xffff ); + setOnlineStatus( presence.toOnlineStatus() ); + + // ICQ does not support status messages for state Online + if ( presence.type() == ICQ::Presence::Online ) + { + mAccount->engine()->removeICQAwayMessageRequest( contactId() ); + removeProperty( mProtocol->awayMessage ); + } + else + { + if ( ICQ::Presence::fromOnlineStatus( account()->myself()->onlineStatus() ).visibility() == ICQ::Presence::Visible ) + { + switch ( presence.type() ) + { + case ICQ::Presence::Away: + mAccount->engine()->addICQAwayMessageRequest( contactId(), Client::ICQAway ); + break; + case ICQ::Presence::NotAvailable: + mAccount->engine()->addICQAwayMessageRequest( contactId(), Client::ICQNotAvailable ); + break; + case ICQ::Presence::Occupied: + mAccount->engine()->addICQAwayMessageRequest( contactId(), Client::ICQOccupied ); + break; + case ICQ::Presence::DoNotDisturb: + mAccount->engine()->addICQAwayMessageRequest( contactId(), Client::ICQDoNotDisturb ); + break; + case ICQ::Presence::FreeForChat: + mAccount->engine()->addICQAwayMessageRequest( contactId(), Client::ICQFreeForChat ); + break; + default: + break; + } + } + else + { + mAccount->engine()->removeICQAwayMessageRequest( contactId() ); + } + } + + + if ( details.dcOutsideSpecified() ) + { + if ( details.dcExternalIp().isUnspecified() ) + removeProperty( mProtocol->ipAddress ); + else + setProperty( mProtocol->ipAddress, details.dcExternalIp().toString() ); + } + + if ( details.capabilitiesSpecified() ) + { + if ( details.clientName().isEmpty() ) + removeProperty( mProtocol->clientFeatures ); + else + setProperty( mProtocol->clientFeatures, details.clientName() ); + } + + if ( details.buddyIconHash().size() > 0 && details.buddyIconHash() != m_details.buddyIconHash() ) + { + m_buddyIconDirty = true; + if ( cachedBuddyIcon( details.buddyIconHash() ) == false ) + { + if ( !mAccount->engine()->hasIconConnection() ) + { + mAccount->engine()->connectToIconServer(); + } + else + { + int time = ( KApplication::random() % 10 ) * 1000; + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "updating buddy icon in " + << time/1000 << " seconds" << endl; + QTimer::singleShot( time, this, SLOT( requestBuddyIcon() ) ); + } + } + } + + OscarContact::userInfoUpdated( contact, details ); +} + +void ICQContact::userOnline( const QString& userId ) +{ + if ( Oscar::normalize( userId ) != Oscar::normalize( contactId() ) ) + return; + + kdDebug(OSCAR_ICQ_DEBUG) << "Setting " << userId << " online" << endl; + ICQ::Presence online = mProtocol->statusManager()->presenceOf( ICQ::Presence::Online ); + //mAccount->engine()->requestStatusInfo( contactId() ); +} + +void ICQContact::userOffline( const QString& userId ) +{ + if ( Oscar::normalize( userId ) != Oscar::normalize( contactId() ) ) + return; + + kdDebug(OSCAR_ICQ_DEBUG) << "Setting " << userId << " offline" << endl; + ICQ::Presence offline = mProtocol->statusManager()->presenceOf( ICQ::Presence::Offline ); + setOnlineStatus( mProtocol->statusManager()->onlineStatusOf( offline ) ); +} + +void ICQContact::loggedIn() +{ + if ( metaContact()->isTemporary() ) + return; + + if ( m_ssiItem.waitingAuth() ) + setOnlineStatus( mProtocol->statusManager()->waitingForAuth() ); + + if ( ( ( hasProperty( Kopete::Global::Properties::self()->nickName().key() ) + && nickName() == contactId() ) + || !hasProperty( Kopete::Global::Properties::self()->nickName().key() ) ) && + !m_requestingNickname && m_ssiItem.alias().isEmpty() ) + { + m_requestingNickname = true; + int time = ( KApplication::random() % 20 ) * 1000; + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "updating nickname in " << time/1000 << " seconds" << endl; + QTimer::singleShot( time, this, SLOT( requestShortInfo() ) ); + } + +} + +void ICQContact::requestShortInfo() +{ + if ( mAccount->isConnected() ) + mAccount->engine()->requestShortInfo( contactId() ); +} + +void ICQContact::slotRequestAuth() +{ + QString reason = KInputDialog::getText( i18n("Request Authorization"), + i18n("Reason for requesting authorization:") ); + if ( !reason.isNull() ) + mAccount->engine()->requestAuth( contactId(), reason ); +} + +void ICQContact::slotSendAuth() +{ + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "Sending auth reply" << endl; + ICQAuthReplyDialog replyDialog( 0, "replyDialog", false ); + + replyDialog.setUser( property( Kopete::Global::Properties::self()->nickName() ).value().toString() ); + if ( replyDialog.exec() ) + mAccount->engine()->sendAuth( contactId(), replyDialog.reason(), replyDialog.grantAuth() ); +} + +void ICQContact::slotGotAuthReply( const QString& contact, const QString& reason, bool granted ) +{ + if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) ) + return; + + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << endl; + QString message; + if( granted ) + { + message = i18n( "User %1 has granted your authorization request.\nReason: %2" ) + .arg( property( Kopete::Global::Properties::self()->nickName() ).value().toString() ) + .arg( reason ); + + // remove the unknown status + setOnlineStatus( ICQ::Presence( ICQ::Presence::Offline, ICQ::Presence::Visible ).toOnlineStatus() ); + } + else + { + message = i18n( "User %1 has rejected the authorization request.\nReason: %2" ) + .arg( property( Kopete::Global::Properties::self()->nickName() ).value().toString() ) + .arg( reason ); + } + KNotifyClient::event( Kopete::UI::Global::sysTrayWId(), "icq_authorization", message ); +} + +void ICQContact::slotGotAuthRequest( const QString& contact, const QString& reason ) +{ + if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) ) + return; + + ICQAuthReplyDialog *replyDialog = new ICQAuthReplyDialog(); + + connect( replyDialog, SIGNAL( okClicked() ), this, SLOT( slotAuthReplyDialogOkClicked() ) ); + replyDialog->setUser( property( Kopete::Global::Properties::self()->nickName() ).value().toString() ); + replyDialog->setRequestReason( reason ); + replyDialog->setModal( TRUE ); + replyDialog->show(); +} + +void ICQContact::slotAuthReplyDialogOkClicked() +{ + // Do not need to delete will delete itself automatically + ICQAuthReplyDialog *replyDialog = (ICQAuthReplyDialog*)sender(); + + if (replyDialog) + mAccount->engine()->sendAuth( contactId(), replyDialog->reason(), replyDialog->grantAuth() ); +} + +void ICQContact::receivedLongInfo( const QString& contact ) +{ + if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) ) + { + if ( m_infoWidget ) + m_infoWidget->delayedDestruct(); + return; + } + + QTextCodec* codec = contactCodec(); + + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "received long info from engine" << endl; + + ICQGeneralUserInfo genInfo = mAccount->engine()->getGeneralInfo( contact ); + if ( m_ssiItem.alias().isEmpty() && !genInfo.nickname.isEmpty() ) + setNickName( codec->toUnicode( genInfo.nickname ) ); + emit haveBasicInfo( genInfo ); + + ICQWorkUserInfo workInfo = mAccount->engine()->getWorkInfo( contact ); + emit haveWorkInfo( workInfo ); + + ICQMoreUserInfo moreInfo = mAccount->engine()->getMoreInfo( contact ); + emit haveMoreInfo( moreInfo ); + + ICQInterestInfo interestInfo = mAccount->engine()->getInterestInfo( contact ); + emit haveInterestInfo( interestInfo ); + +} + +void ICQContact::receivedShortInfo( const QString& contact ) +{ + if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) ) + return; + + QTextCodec* codec = contactCodec(); + + m_requestingNickname = false; //done requesting nickname + ICQShortInfo shortInfo = mAccount->engine()->getShortInfo( contact ); + /* + if(!shortInfo.firstName.isEmpty()) + setProperty( mProtocol->firstName, codec->toUnicode( shortInfo.firstName ) ); + else + removeProperty(mProtocol->firstName); + + if(!shortInfo.lastName.isEmpty()) + setProperty( mProtocol->lastName, codec->toUnicode( shortInfo.lastName ) ); + else + removeProperty(mProtocol->lastName); + */ + if ( m_ssiItem.alias().isEmpty() && !shortInfo.nickname.isEmpty() ) + { + kdDebug(14153) << k_funcinfo << + "setting new displayname for former UIN-only Contact" << endl; + setProperty( Kopete::Global::Properties::self()->nickName(), codec->toUnicode( shortInfo.nickname ) ); + } + +} + +void ICQContact::receivedStatusMessage( const QString &contact, const QString &message ) +{ + if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) ) + return; + + if ( ! message.isEmpty() ) + setProperty( mProtocol->awayMessage, message ); + else + removeProperty( mProtocol->awayMessage ); +} + +void ICQContact::receivedStatusMessage( const Oscar::Message &message ) +{ + if ( Oscar::normalize( message.sender() ) != Oscar::normalize( contactId() ) ) + return; + + //decode message + QTextCodec* codec = contactCodec(); + + QString realText = message.text(codec); + + if ( !realText.isEmpty() ) + setProperty( mProtocol->awayMessage, realText ); + else + removeProperty( mProtocol->awayMessage ); +} + +void ICQContact::slotSendMsg( Kopete::Message& msg, Kopete::ChatSession* session ) +{ + //Why is this unused? + Q_UNUSED( session ); + + QTextCodec* codec = contactCodec(); + + int messageChannel = 0x01; + Oscar::Message::Encoding messageEncoding; + + if ( isOnline() && m_details.hasCap( CAP_UTF8 ) ) + messageEncoding = Oscar::Message::UCS2; + else + messageEncoding = Oscar::Message::UserDefined; + + QString msgText( msg.plainBody() ); + // TODO: More intelligent handling of message length. + uint chunk_length = !isOnline() ? 450 : 4096; + uint msgPosition = 0; + + do + { + QString msgChunk( msgText.mid( msgPosition, chunk_length ) ); + // Try to split on space if needed + if ( msgChunk.length() == chunk_length ) + { + for ( int i = 0; i < 100; i++ ) + { + if ( msgChunk[chunk_length - i].isSpace() ) + { + msgChunk = msgChunk.left( chunk_length - i ); + msgPosition++; + } + } + } + msgPosition += msgChunk.length(); + + Oscar::Message message( messageEncoding, msgChunk, messageChannel, 0, msg.timestamp(), codec ); + message.setSender( mAccount->accountId() ); + message.setReceiver( mName ); + mAccount->engine()->sendMessage( message ); + } while ( msgPosition < msgText.length() ); + + manager(Kopete::Contact::CanCreate)->appendMessage(msg); + manager(Kopete::Contact::CanCreate)->messageSucceeded(); +} + +void ICQContact::updateFeatures() +{ + setProperty( static_cast<ICQProtocol*>(protocol())->clientFeatures, m_clientFeatures ); +} + +void ICQContact::requestBuddyIcon() +{ + if ( m_buddyIconDirty && m_details.buddyIconHash().size() > 0 ) + { + account()->engine()->requestBuddyIcon( contactId(), m_details.buddyIconHash(), + m_details.iconCheckSumType() ); + } +} + +void ICQContact::haveIcon( const QString& user, QByteArray icon ) +{ + if ( Oscar::normalize( user ) != Oscar::normalize( contactId() ) ) + return; + + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "Updating icon for " << contactId() << endl; + + KMD5 buddyIconHash( icon ); + if ( memcmp( buddyIconHash.rawDigest(), m_details.buddyIconHash().data(), 16 ) == 0 ) + { + QString iconLocation( locateLocal( "appdata", "oscarpictures/"+ contactId() ) ); + + QFile iconFile( iconLocation ); + if ( !iconFile.open( IO_WriteOnly ) ) + { + kdDebug(14153) << k_funcinfo << "Cannot open file" + << iconLocation << " for writing!" << endl; + return; + } + + iconFile.writeBlock( icon ); + iconFile.close(); + + setProperty( Kopete::Global::Properties::self()->photo(), QString::null ); + setProperty( Kopete::Global::Properties::self()->photo(), iconLocation ); + m_buddyIconDirty = false; + } + else + { + kdDebug(14153) << k_funcinfo << "Buddy icon hash does not match!" << endl; + removeProperty( Kopete::Global::Properties::self()->photo() ); + } +} + +bool ICQContact::cachedBuddyIcon( QByteArray hash ) +{ + QString iconLocation( locateLocal( "appdata", "oscarpictures/"+ contactId() ) ); + + QFile iconFile( iconLocation ); + if ( !iconFile.open( IO_ReadOnly ) ) + return false; + + KMD5 buddyIconHash; + buddyIconHash.update( iconFile ); + iconFile.close(); + + if ( memcmp( buddyIconHash.rawDigest(), hash.data(), 16 ) == 0 ) + { + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "Updating icon for " + << contactId() << " from local cache" << endl; + setProperty( Kopete::Global::Properties::self()->photo(), QString::null ); + setProperty( Kopete::Global::Properties::self()->photo(), iconLocation ); + m_buddyIconDirty = false; + return true; + } + else + { + return false; + } +} + +#if 0 +void ICQContact::slotContactChanged(const UserInfo &u) +{ + if (u.sn != contactName()) + return; + + // update mInfo and general stuff from OscarContact + slotParseUserInfo(u); + + /*kdDebug(14190) << k_funcinfo << "Called for '" + << displayName() << "', contactName()=" << contactName() << endl;*/ + QStringList capList; + // Append client name and version in case we found one + if (!mInfo.clientName.isEmpty()) + { + if (!mInfo.clientVersion.isEmpty()) + { + capList << i18n("Translators: client-name client-version", + "%1 %2").arg(mInfo.clientName, mInfo.clientVersion); + } + else + { + capList << mInfo.clientName; + } + } + // and now for some general informative capabilities + if (hasCap(CAP_UTF8)) + capList << i18n("UTF-8"); + if (hasCap(CAP_RTFMSGS)) + capList << i18n("RTF-Messages"); + if (hasCap(CAP_IMIMAGE)) + capList << i18n("DirectIM/IMImage"); + if (hasCap(CAP_CHAT)) + capList << i18n("Groupchat"); + + if (capList.count() > 0) + setProperty(mProtocol->clientFeatures, capList.join(", ")); + else + removeProperty(mProtocol->clientFeatures); + + unsigned int newStatus = 0; + mInvisible = (mInfo.icqextstatus & ICQ_STATUS_IS_INVIS); + + if (mInfo.icqextstatus & ICQ_STATUS_IS_FFC) + newStatus = OSCAR_FFC; + else if (mInfo.icqextstatus & ICQ_STATUS_IS_DND) + newStatus = OSCAR_DND; + else if (mInfo.icqextstatus & ICQ_STATUS_IS_OCC) + newStatus = OSCAR_OCC; + else if (mInfo.icqextstatus & ICQ_STATUS_IS_NA) + newStatus = OSCAR_NA; + else if (mInfo.icqextstatus & ICQ_STATUS_IS_AWAY) + newStatus = OSCAR_AWAY; + else + newStatus = OSCAR_ONLINE; + + if (this != account()->myself()) + { + if(newStatus != onlineStatus().internalStatus()) + { + if(newStatus != OSCAR_ONLINE) // if user changed to some state other than online + { + mAccount->engine()->requestAwayMessage(this); + } + else // user changed to "Online" status and has no away message anymore + { + removeProperty(mProtocol->awayMessage); + } + } + } + + setStatus(newStatus); +} + +void ICQContact::slotOffgoingBuddy(QString sender) +{ + if(sender != contactName()) + return; + + removeProperty(mProtocol->clientFeatures); + removeProperty(mProtocol->awayMessage); + setOnlineStatus(mProtocol->statusOffline); +} + +void ICQContact::gotIM(OscarSocket::OscarMessageType /*type*/, const QString &message) +{ + // Build a Kopete::Message and set the body as Rich Text + Kopete::ContactPtrList tmpList; + tmpList.append(account()->myself()); + Kopete::Message msg(this, tmpList, message, Kopete::Message::Inbound, + Kopete::Message::RichText); + manager(true)->appendMessage(msg); +} + + +void ICQContact::slotSendMsg(Kopete::Message& message, Kopete::ChatSession *) +{ + if (message.plainBody().isEmpty()) // no text, do nothing + return; + + // Check to see if we're even online + if(!account()->isConnected()) + { + KMessageBox::sorry(Kopete::UI::Global::mainWidget(), + i18n("<qt>You must be logged on to ICQ before you can " + "send a message to a user.</qt>"), + i18n("Not Signed On")); + return; + } + + // FIXME: We don't do HTML in ICQ + // we might be able to do that in AIM and we might also convert + // HTML to RTF for ICQ type-2 messages [mETz] + static_cast<OscarAccount*>(account())->engine()->sendIM( + message.plainBody(), this, false); + + // Show the message we just sent in the chat window + manager(Kopete::Contact::CanCreate)->appendMessage(message); + manager(Kopete::Contact::CanCreate)->messageSucceeded(); +} + +#endif + +bool ICQContact::isReachable() +{ + return account()->isConnected(); +} + +QPtrList<KAction> *ICQContact::customContextMenuActions() +{ + QPtrList<KAction> *actionCollection = new QPtrList<KAction>(); +/* + QString awTxt; + QString awIcn; + unsigned int status = onlineStatus().internalStatus(); + if (status >= 15) + status -= 15; // get rid of invis addon + switch(status) + { + case OSCAR_FFC: + awTxt = i18n("Read 'Free For Chat' &Message"); + awIcn = "icq_ffc"; + break; + case OSCAR_DND: + awTxt = i18n("Read 'Do Not Disturb' &Message"); + awIcn = "icq_dnd"; + break; + case OSCAR_NA: + awTxt = i18n("Read 'Not Available' &Message"); + awIcn = "icq_na"; + break; + case OSCAR_OCC: + awTxt = i18n("Read 'Occupied' &Message"); + awIcn = "icq_occ"; + break; + default: + awTxt = i18n("Read 'Away' &Message"); + awIcn = "icq_away"; + break; + } + + if(actionReadAwayMessage==0) + { + actionReadAwayMessage = new KAction(awTxt, awIcn, 0, + this, SLOT(slotReadAwayMessage()), this, "actionReadAwayMessage"); + */ + actionRequestAuth = new KAction(i18n("&Request Authorization"), "mail_reply", 0, + this, SLOT(slotRequestAuth()), this, "actionRequestAuth"); + actionSendAuth = new KAction(i18n("&Grant Authorization"), "mail_forward", 0, + this, SLOT(slotSendAuth()), this, "actionSendAuth"); + /* + } + else + { + actionReadAwayMessage->setText(awTxt); + actionReadAwayMessage->setIconSet(SmallIconSet(awIcn)); + } + +*/ + m_actionIgnore = new KToggleAction(i18n("&Ignore"), "", 0, + this, SLOT(slotIgnore()), this, "actionIgnore"); + m_actionVisibleTo = new KToggleAction(i18n("Always &Visible To"), "", 0, + this, SLOT(slotVisibleTo()), this, "actionVisibleTo"); + m_actionInvisibleTo = new KToggleAction(i18n("Always &Invisible To"), "", 0, + this, SLOT(slotInvisibleTo()), this, "actionInvisibleTo"); + + bool on = account()->isConnected(); + if ( m_ssiItem.waitingAuth() ) + actionRequestAuth->setEnabled(on); + else + actionRequestAuth->setEnabled(false); + + actionSendAuth->setEnabled(on); + + + m_selectEncoding = new KAction( i18n( "Select Encoding..." ), "charset", 0, + this, SLOT( changeContactEncoding() ), this, "changeEncoding" ); + +/* + actionReadAwayMessage->setEnabled(status != OSCAR_OFFLINE && status != OSCAR_ONLINE); +*/ + m_actionIgnore->setEnabled(on); + m_actionVisibleTo->setEnabled(on); + m_actionInvisibleTo->setEnabled(on); + + SSIManager* ssi = account()->engine()->ssiManager(); + m_actionIgnore->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_IGNORE )); + m_actionVisibleTo->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_VISIBLE )); + m_actionInvisibleTo->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_INVISIBLE )); + + actionCollection->append(actionRequestAuth); + actionCollection->append(actionSendAuth); + actionCollection->append( m_selectEncoding ); + + actionCollection->append(m_actionIgnore); + actionCollection->append(m_actionVisibleTo); + actionCollection->append(m_actionInvisibleTo); + +// actionCollection->append(actionReadAwayMessage); + + return actionCollection; +} + + +void ICQContact::slotUserInfo() +{ + m_infoWidget = new ICQUserInfoWidget( Kopete::UI::Global::mainWidget(), "icq info" ); + QObject::connect( m_infoWidget, SIGNAL( finished() ), this, SLOT( closeUserInfoDialog() ) ); + m_infoWidget->setContact( this ); + m_infoWidget->show(); + if ( account()->isConnected() ) + mAccount->engine()->requestFullInfo( contactId() ); +} + +void ICQContact::closeUserInfoDialog() +{ + QObject::disconnect( this, 0, m_infoWidget, 0 ); + m_infoWidget->delayedDestruct(); + m_infoWidget = 0L; +} + +void ICQContact::changeContactEncoding() +{ + if ( m_oesd ) + return; + + m_oesd = new OscarEncodingSelectionDialog( Kopete::UI::Global::mainWidget(), property(mProtocol->contactEncoding).value().toInt() ); + connect( m_oesd, SIGNAL( closing( int ) ), + this, SLOT( changeEncodingDialogClosed( int ) ) ); + m_oesd->show(); +} + +void ICQContact::changeEncodingDialogClosed( int result ) +{ + if ( result == QDialog::Accepted ) + { + int mib = m_oesd->selectedEncoding(); + if ( mib != 0 ) + { + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "setting encoding mib to " + << m_oesd->selectedEncoding() << endl; + setProperty( mProtocol->contactEncoding, m_oesd->selectedEncoding() ); + } + else + { + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo + << "setting encoding to default" << endl; + removeProperty( mProtocol->contactEncoding ); + } + } + + if ( m_oesd ) + { + m_oesd->delayedDestruct(); + m_oesd = 0L; + } +} + + +void ICQContact::slotIgnore() +{ + account()->engine()->setIgnore( contactId(), m_actionIgnore->isChecked() ); +} + +void ICQContact::slotVisibleTo() +{ + account()->engine()->setVisibleTo( contactId(), m_actionVisibleTo->isChecked() ); +} + +void ICQContact::slotInvisibleTo() +{ + account()->engine()->setInvisibleTo( contactId(), m_actionInvisibleTo->isChecked() ); +} + +#if 0 + +void ICQContact::slotReadAwayMessage() +{ + kdDebug(14153) << k_funcinfo << "account='" << account()->accountId() << + "', contact='" << displayName() << "'" << endl; + + if (!awayMessageDialog) + { + awayMessageDialog = new ICQReadAway(this, 0L, "awayMessageDialog"); + if(!awayMessageDialog) + return; + QObject::connect(awayMessageDialog, SIGNAL(closing()), this, SLOT(slotCloseAwayMessageDialog())); + awayMessageDialog->show(); + } + else + { + awayMessageDialog->raise(); + } +} + + +void ICQContact::slotCloseAwayMessageDialog() +{ + awayMessageDialog->delayedDestruct(); + awayMessageDialog = 0L; +} + + +const QString ICQContact::awayMessage() +{ + kdDebug(14150) << k_funcinfo << property(mProtocol->awayMessage).value().toString() << endl; + return property(mProtocol->awayMessage).value().toString(); +} + + +void ICQContact::setAwayMessage(const QString &message) +{ + /*kdDebug(14150) << k_funcinfo << + "Called for '" << displayName() << "', away msg='" << message << "'" << endl;*/ + setProperty(mProtocol->awayMessage, message); + emit awayMessageChanged(); +} + + +void ICQContact::slotUpdGeneralInfo(const int seq, const ICQGeneralUserInfo &inf) +{ + // compare reply's sequence with the one we sent with our last request + if(seq != userinfoRequestSequence) + return; + generalInfo = inf; + + if(!generalInfo.firstName.isEmpty()) + setProperty(mProtocol->firstName, generalInfo.firstName); + else + removeProperty(mProtocol->firstName); + + if(!generalInfo.lastName.isEmpty()) + setProperty(mProtocol->lastName, generalInfo.lastName); + else + removeProperty(mProtocol->lastName); + + if(!generalInfo.eMail.isEmpty()) + setProperty(mProtocol->emailAddress, generalInfo.eMail); + else + removeProperty(mProtocol->emailAddress); + /* + if(!generalInfo.phoneNumber.isEmpty()) + setProperty("privPhoneNum", generalInfo.phoneNumber); + else + removeProperty("privPhoneNum"); + + if(!generalInfo.faxNumber.isEmpty()) + setProperty("privFaxNum", generalInfo.faxNumber); + else + removeProperty("privFaxNum"); + + if(!generalInfo.cellularNumber.isEmpty()) + setProperty("privMobileNum", generalInfo.cellularNumber); + else + removeProperty("privMobileNum"); + */ + + if(contactName() == displayName() && !generalInfo.nickName.isEmpty()) + { + kdDebug(14153) << k_funcinfo << "setting new displayname for former UIN-only Contact" << endl; + setDisplayName(generalInfo.nickName); + } + + incUserInfoCounter(); +} + + +void ICQContact::slotSnacFailed(WORD snacID) +{ + if (userinfoRequestSequence != 0) + kdDebug(14153) << k_funcinfo << "snacID = " << snacID << " seq = " << userinfoRequestSequence << endl; + + //TODO: ugly interaction between snacID and request sequence, see OscarSocket::sendCLI_TOICQSRV + if (snacID == (0x0000 << 16) | userinfoRequestSequence) + { + userinfoRequestSequence = 0; + emit userInfoRequestFailed(); + } +} + +void ICQContact::slotIgnore() +{ + kdDebug(14150) << k_funcinfo << + "Called; ignore = " << actionIgnore->isChecked() << endl; + setIgnore(actionIgnore->isChecked(), true); +} + +void ICQContact::slotVisibleTo() +{ + kdDebug(14150) << k_funcinfo << + "Called; visible = " << actionVisibleTo->isChecked() << endl; + setVisibleTo(actionVisibleTo->isChecked(), true); +} +#endif +#include "icqcontact.moc" +//kate: indent-mode csands; tab-width 4; replace-tabs off; space-indent off; diff --git a/kopete/protocols/oscar/icq/icqcontact.h b/kopete/protocols/oscar/icq/icqcontact.h new file mode 100644 index 00000000..41084e63 --- /dev/null +++ b/kopete/protocols/oscar/icq/icqcontact.h @@ -0,0 +1,155 @@ +/* + icqcontact.h - ICQ Contact + + Copyright (c) 2003 by Stefan Gehn <metz AT gehn.net> + Copyright (c) 2003 by Olivier Goffart + Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk> + 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. * + * * + ************************************************************************* + */ + +#ifndef ICQCONTACT_H +#define ICQCONTACT_H + +#include "oscarcontact.h" +#include "userdetails.h" + +class OscarEncodingSelectionDialog; +class KAction; +class KToggleAction; +namespace Kopete { class ChatSession; } +namespace Kopete { class OnlineStatus; } +class ICQProtocol; +class ICQAccount; +class OscarAccount; +class ICQUserInfo; // user info dialog +class ICQReadAway; + +class ICQGeneralUserInfo; +class ICQWorkUserInfo; +class ICQUserInfoWidget; +class ICQInterestInfoWidget; + +/** + * Contact for ICQ over Oscar protocol + * @author Stefan Gehn + * @author Richard Smith + * @author Matt Rogers + */ +class ICQContact : public OscarContact +{ +Q_OBJECT + +public: + + /** Normal ICQ constructor */ + ICQContact( ICQAccount *account, const QString &name, Kopete::MetaContact *parent, + const QString& icon = QString::null, const Oscar::SSI& ssiItem = Oscar::SSI() ); + virtual ~ICQContact(); + + /** + * Returns a set of custom menu items for + * the context menu + */ + virtual QPtrList<KAction> *customContextMenuActions(); + + /** Return whether or not this contact is reachable. */ + virtual bool isReachable(); + + + //virtual const QString awayMessage(); + //virtual void setAwayMessage(const QString &message); + +public slots: + virtual void slotUserInfo(); + virtual void updateSSIItem(); + void userInfoUpdated( const QString& contact, const UserDetails& details ); + + void userOnline( const QString& userId ); + void userOffline( const QString& userID ); + void loggedIn(); + + void requestShortInfo(); + +signals: + void haveBasicInfo( const ICQGeneralUserInfo& ); + void haveWorkInfo( const ICQWorkUserInfo& ); + void haveEmailInfo( const ICQEmailInfo& ); + void haveMoreInfo( const ICQMoreUserInfo& ); + void haveInterestInfo( const ICQInterestInfo& ); + +private: + bool cachedBuddyIcon( QByteArray hash ); + bool m_buddyIconDirty; + + bool m_requestingNickname; + ICQProtocol *mProtocol; + ICQUserInfoWidget* m_infoWidget; + /* + ICQReadAway *awayMessageDialog; + KAction *actionReadAwayMessage; + */ + KAction *actionRequestAuth; + KAction *actionSendAuth; + KAction *m_selectEncoding; + + KToggleAction *m_actionIgnore; + KToggleAction *m_actionVisibleTo; + KToggleAction *m_actionInvisibleTo; + + /* + bool mInvisible; + */ + + OscarEncodingSelectionDialog* m_oesd; + +protected slots: + virtual void slotSendMsg(Kopete::Message& message, Kopete::ChatSession *); + virtual void updateFeatures(); + +private slots: + /** Request authorization from this contact */ + void slotRequestAuth(); + + /** Authorize this contact */ + void slotSendAuth(); + + void slotAuthReplyDialogOkClicked(); + + /** We have received an auth request */ + void slotGotAuthRequest( const QString& contact, const QString& reason ); + + /** We have received an auth reply */ + void slotGotAuthReply( const QString& contact, const QString& reason, bool granted ); + + void closeUserInfoDialog(); + + void receivedLongInfo( const QString& contact ); + void receivedShortInfo( const QString& contact ); + + void changeContactEncoding(); + void changeEncodingDialogClosed( int ); + + void requestBuddyIcon(); + void haveIcon( const QString&, QByteArray ); + void receivedStatusMessage( const QString &contact, const QString &message ); + void receivedStatusMessage( const Oscar::Message &message ); + +//void slotCloseAwayMessageDialog(); + //void slotReadAwayMessage(); + + void slotIgnore(); + void slotVisibleTo(); + void slotInvisibleTo(); +}; + +#endif +//kate: tab-width 4; indent-mode csands; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/icq/icqpresence.cpp b/kopete/protocols/oscar/icq/icqpresence.cpp new file mode 100644 index 00000000..ab6bb670 --- /dev/null +++ b/kopete/protocols/oscar/icq/icqpresence.cpp @@ -0,0 +1,294 @@ +/* + icqpresence.cpp - ICQ online status and presence management + + Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk> + 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 <utility> +#include <vector> + +#include <kdebug.h> +#include <klocale.h> +#include <kstaticdeleter.h> + +#include <kopeteonlinestatus.h> +#include <kopeteonlinestatusmanager.h> + +#include "icqprotocol.h" + +#include "icqpresence.h" + +namespace ICQ +{ + +//BEGIN struct PresenceTypeData + +struct PresenceTypeData +{ + Presence::Type type; + Kopete::OnlineStatus::StatusType onlineStatusType; + unsigned long setFlag; + unsigned long getFlag; + QString caption; + QString visibleName; + QString invisibleName; + const char *visibleIcon; + const char *invisibleIcon; + unsigned int categories; + unsigned int options; + + static const PresenceTypeData *all(); + static const PresenceTypeData &forType( Presence::Type type ); + static const PresenceTypeData &forStatus( unsigned long status ); + static const PresenceTypeData &forOnlineStatusType( const Kopete::OnlineStatus::StatusType statusType ); +}; + +const PresenceTypeData *PresenceTypeData::all() +{ + using namespace Kopete; + using namespace ICQ::StatusCode; + /** + * The order here is important - this is the order the IS_XXX flags will be checked for in. + * That, in particular, means that NA, Occupied and DND must appear before Away, and that + * DND must appear before Occupied. Offline (testing all bits) must go first, and Online + * (testing no bits - will always match a status) must go last. + * + * Free For Chat is currently listed after Away, since if someone is Away + Free For Chat we + * want to show them as Away more than we want to show them FFC. + */ + static const PresenceTypeData data[] = + { + { Presence::Offline, OnlineStatus::Offline, OFFLINE, OFFLINE, i18n( "O&ffline" ), i18n("Offline"), i18n("Offline"), 0, "contact_invisible_overlay", Kopete::OnlineStatusManager::Offline, 0 }, + { Presence::DoNotDisturb, OnlineStatus::Away, SET_DND, IS_DND, i18n( "&Do Not Disturb" ), i18n("Do Not Disturb"), i18n("Do Not Disturb (Invisible)"), "contact_busy_overlay", "contact_invisible_overlay", Kopete::OnlineStatusManager::Busy, Kopete::OnlineStatusManager::HasAwayMessage }, + { Presence::Occupied, OnlineStatus::Away, SET_OCC, IS_OCC, i18n( "O&ccupied" ), i18n("Occupied"), i18n("Occupied (Invisible)"), "contact_busy_overlay", "contact_invisible_overlay", 0, Kopete::OnlineStatusManager::HasAwayMessage }, + { Presence::NotAvailable, OnlineStatus::Away, SET_NA, IS_NA, i18n( "Not A&vailable" ), i18n("Not Available"), i18n("Not Available (Invisible)"), "contact_xa_overlay", "contact_invisible_overlay", Kopete::OnlineStatusManager::ExtendedAway, Kopete::OnlineStatusManager::HasAwayMessage }, + { Presence::Away, OnlineStatus::Away, SET_AWAY, IS_AWAY, i18n( "&Away" ), i18n("Away"), i18n("Away (Invisible)"), "contact_away_overlay", "contact_invisible_overlay", Kopete::OnlineStatusManager::Away, Kopete::OnlineStatusManager::HasAwayMessage }, + { Presence::FreeForChat, OnlineStatus::Online, SET_FFC, IS_FFC, i18n( "&Free for Chat" ), i18n("Free For Chat"), i18n("Free For Chat (Invisible)"), "icq_ffc", "contact_invisible_overlay", Kopete::OnlineStatusManager::FreeForChat, 0 }, + { Presence::Online, OnlineStatus::Online, ONLINE, ONLINE, i18n( "O&nline" ), i18n("Online"), i18n("Online (Invisible)"), 0, "contact_invisible_overlay", Kopete::OnlineStatusManager::Online, 0 } + }; + return data; +} + +const PresenceTypeData &PresenceTypeData::forType( Presence::Type type ) +{ + const PresenceTypeData *array = all(); + for ( uint n = 0; n < Presence::TypeCount; ++n ) + if ( array[n].type == type ) + return array[n]; + kdWarning(14153) << k_funcinfo << "type " << (int)type << " not found! Returning Offline" << endl; + return array[0]; +} + +const PresenceTypeData &PresenceTypeData::forStatus( unsigned long status ) +{ + const PresenceTypeData *array = all(); + for ( uint n = 0; n < Presence::TypeCount; ++n ) + { + //kdDebug(14153) << k_funcinfo << "array[n].getFlag is " << array[n].getFlag << ", status is " << status << ", & is " << (array[n].getFlag & status) << endl; + if ( (array[n].getFlag & status) == array[n].getFlag ) + return array[n]; + } + kdWarning(14153) << k_funcinfo << "status " << (int)status << " not found! Returning Offline. This should not happen." << endl; + return array[0]; +} + +const PresenceTypeData &PresenceTypeData::forOnlineStatusType( const Kopete::OnlineStatus::StatusType statusType ) +{ + const PresenceTypeData *array = all(); + for ( int n = Presence::TypeCount - 1; n >= 0; --n ) + { + if ( array[n].onlineStatusType == statusType ) + return array[n]; + } + kdWarning(14153) << k_funcinfo << "online status " << (int)statusType << " not found! Returning Offline. This should not happen." << endl; + return array[0]; +} + +//END struct PresenceTypeData + +//BEGIN class OnlineStatusManager + +class OnlineStatusManager::Private +{ +public: + typedef std::vector<Kopete::OnlineStatus> StatusList; + + // connecting and unknown should have the same internal status as offline, so converting to a Presence gives an Offline one + Private() + : connecting( Kopete::OnlineStatus::Connecting, 99, ICQProtocol::protocol(), + 99, "icq_connecting", i18n("Connecting...") ) + , unknown( Kopete::OnlineStatus::Unknown, 0, ICQProtocol::protocol(), + Presence::Offline, "status_unknown", i18n("Unknown") ) + , waitingForAuth( Kopete::OnlineStatus::Unknown, 1, ICQProtocol::protocol(), + Presence::Offline, "button_cancel", i18n("Waiting for Authorization") ) + , invisible( Kopete::OnlineStatus::Invisible, 2, ICQProtocol::protocol(), + Presence::Offline, QString::null, QString::null, + QString::null, Kopete::OnlineStatusManager::Invisible, + Kopete::OnlineStatusManager::HideFromMenu ) + + { + createStatusList( false, 0, visibleStatusList ); + createStatusList( true, Presence::TypeCount, invisibleStatusList ); + } + void createStatusList( bool invisible, const uint invisibleOffset, StatusList &statusList ) + { + //weight 0, 1 and 2 are used by KOS unknown, waitingForAuth and invisible + const uint firstUsableWeight = 3; + statusList.reserve( Presence::TypeCount ); + for ( uint n = 0; n < Presence::TypeCount; ++n ) + { + const PresenceTypeData &data = PresenceTypeData::forType( static_cast<Presence::Type>(n) ); + const uint weight = n + firstUsableWeight; + const uint internalStatus = n + invisibleOffset; + QStringList overlayIcons( data.visibleIcon ); + QString description( data.visibleName ); + Kopete::OnlineStatus status; + if ( invisible ) + { + overlayIcons << data.invisibleIcon; + description = data.invisibleName; + //don't add invisible KOS to account's context menu + status = Kopete::OnlineStatus( data.onlineStatusType, weight, + ICQProtocol::protocol(), internalStatus, + overlayIcons, description ); + } + else + { + //add visible KOS + status = Kopete::OnlineStatus( data.onlineStatusType, weight, + ICQProtocol::protocol(), internalStatus, + overlayIcons, description, + data.caption, data.categories, data.options ); + } + statusList.push_back( status ); + } + } + + StatusList visibleStatusList, invisibleStatusList; + Kopete::OnlineStatus connecting; + Kopete::OnlineStatus unknown; + Kopete::OnlineStatus waitingForAuth; + Kopete::OnlineStatus invisible; +}; + +OnlineStatusManager::OnlineStatusManager() + : d( new Private ) +{ +} + +OnlineStatusManager::~OnlineStatusManager() +{ + delete d; +} + +Presence OnlineStatusManager::presenceOf( uint internalStatus ) +{ + if ( internalStatus < Presence::TypeCount ) + { + return Presence( static_cast<Presence::Type>( internalStatus ), Presence::Visible ); + } + else if ( internalStatus < 2 * Presence::TypeCount ) + { + return Presence( static_cast<Presence::Type>( internalStatus - Presence::TypeCount ), Presence::Invisible ); + } + else + { + kdWarning(14153) << k_funcinfo << "No presence exists for internal status " << internalStatus << "! Returning Offline" << endl; + return Presence( Presence::Offline, Presence::Visible ); + } +} + +Kopete::OnlineStatus OnlineStatusManager::onlineStatusOf( const Presence &presence ) +{ + if ( presence.visibility() == Presence::Visible ) + return d->visibleStatusList[ presence.type() ]; + else + return d->invisibleStatusList[ presence.type() ]; +} + +Kopete::OnlineStatus OnlineStatusManager::connectingStatus() +{ + return d->connecting; +} + +Kopete::OnlineStatus OnlineStatusManager::unknownStatus() +{ + return d->unknown; +} + +Kopete::OnlineStatus OnlineStatusManager::waitingForAuth() +{ + return d->waitingForAuth; +} + +//END class OnlineStatusManager + +//BEGIN class Presence + +Presence Presence::fromOnlineStatus( const Kopete::OnlineStatus &status ) +{ + if ( status.protocol() == ICQProtocol::protocol() ) + { + OnlineStatusManager *store = ICQProtocol::protocol()->statusManager(); + return store->presenceOf( status.internalStatus() ); + } + else + { + //status is a libkopete builtin status object + //don't even think about converting it to ICQ::Presence using presenceOf! + return Presence( PresenceTypeData::forOnlineStatusType( status.status() ).type, + Presence::Visible ); + } +} + +Kopete::OnlineStatus Presence::toOnlineStatus() const +{ + OnlineStatusManager *store = ICQProtocol::protocol()->statusManager(); + return store->onlineStatusOf( *this ); +} + + +unsigned long Presence::toOscarStatus() const +{ + unsigned long basicStatus = basicOscarStatus(); + if ( _visibility == Invisible ) + basicStatus |= StatusCode::INVISIBLE; + return basicStatus; +} + +Presence Presence::fromOscarStatus( unsigned long code ) +{ + Type type = typeFromOscarStatus( code & ~StatusCode::INVISIBLE ); + bool invisible = (code & StatusCode::INVISIBLE) == StatusCode::INVISIBLE; + return Presence( type, invisible ? Invisible : Visible ); +} + + +unsigned long Presence::basicOscarStatus() const +{ + const PresenceTypeData &data = PresenceTypeData::forType( _type ); + return data.setFlag; +} + +Presence::Type Presence::typeFromOscarStatus( unsigned long status ) +{ + const PresenceTypeData &data = PresenceTypeData::forStatus( status ); + return data.type; +} + +//END class Presence + +} // end namespace ICQ + +// vim: set noet ts=4 sts=4 sw=4: +// kate: indent-mode: csands; space-indent off; tab-width 4; diff --git a/kopete/protocols/oscar/icq/icqpresence.h b/kopete/protocols/oscar/icq/icqpresence.h new file mode 100644 index 00000000..d7ef9ed2 --- /dev/null +++ b/kopete/protocols/oscar/icq/icqpresence.h @@ -0,0 +1,177 @@ +/* + icqpresence.h - ICQ online status and presence management + + Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk> + 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. * + * * + ************************************************************************* +*/ + + +#ifndef ICQCOMMON_H +#define ICQCOMMON_H + +#include <kdebug.h> + +namespace Kopete { class OnlineStatus; } + +namespace ICQ +{ + +class Presence; + +/** + * @brief This namespace contains status flags used in OSCAR's on-the-wire format. + * + * The IS_XXX values are bits representing actual status flags. However, the flags + * are just that -- flags. ICQ statuses are represented by a combination of these + * flags rather than just one value. This seems to be for backwards compatibility + * reasons -- this way you can add a new status and existing clients should still + * work correctly. + * + * So, when changing status you need to specify not only what status it is, but + * also all other status flags that are appropriate. The SET_XXX flags do just that; + * SET_DND for instance sets the DND, Occupied and Away bits. + */ +namespace StatusCode +{ + enum + { + OFFLINE = 0xFFFFFFFF, + ONLINE = 0x00000000, + INVISIBLE = 0x00000100, + + IS_DND = 0x00000002, ///< Do Not Disturb + IS_OCC = 0x00000010, ///< Occupied + IS_NA = 0x00000004, ///< Not Available + IS_AWAY = 0x00000001, ///< Away + IS_FFC = 0x00000020, ///< Free For Chat + + SET_DND = 0x00000013, //== DND + Occupied + Away + SET_OCC = 0x00000011, //== Occupied + Away + SET_NA = 0x00000005, //== NA + Away + SET_AWAY = 0x00000001, + SET_FFC = 0x00000020, + + WEBAWARE = 0x00010000, + SHOWIP = 0x00020000 + }; +} // end namespace StatusCode + +/** + * @brief A manager for ICQ's online statuses + * + * Looks after ICQ's numerous online statuses, and maps between them and Presence objects. + * A single instance of this class is held by the ICQProtocol object. + */ +class OnlineStatusManager +{ +public: + OnlineStatusManager(); + ~OnlineStatusManager(); + ICQ::Presence presenceOf( uint internalStatus ); + Kopete::OnlineStatus onlineStatusOf( const ICQ::Presence &presence ); + Kopete::OnlineStatus connectingStatus(); + Kopete::OnlineStatus unknownStatus(); + Kopete::OnlineStatus waitingForAuth(); + +private: + class Private; + Private *d; +}; + +/** + * @brief An ICQ online presence object + */ +class Presence +{ +public: + /** + * Friendly types this status can be + */ + enum Type { Offline, DoNotDisturb, Occupied, NotAvailable, Away, Online, FreeForChat }; + enum { TypeCount = FreeForChat + 1 }; + + enum Visibility { Invisible, Visible }; + + Presence( Type type, Visibility vis ) : _type(type), _visibility(vis) {} + + Type type() const { return _type; } + Visibility visibility() const { return _visibility; } + + /** + * Generate a Presence object from an online status + */ + static Presence fromOnlineStatus( const Kopete::OnlineStatus &status ); + + /** + * Convert this Presence object to an online status + */ + Kopete::OnlineStatus toOnlineStatus() const; + + /** + * Get the status code to pass to liboscar to set us to this Status. + * @note This is not the opposite of fromOnlineStatus(). The set and get codes don't match. + */ + unsigned long toOscarStatus() const; + + /** + * Get the status a contact is at based on liboscar's view of its status. + * @note This is not the opposite of toOnlineStatus(). + */ + static Presence fromOscarStatus( unsigned long code ); + + bool operator==( const Presence &other ) const { return other._type == _type && other._visibility == _visibility; } + bool operator!=( const Presence &other ) const { return !(*this == other); } + +private: + unsigned long basicOscarStatus() const; + static Type typeFromOscarStatus( unsigned long status ); +private: + Type _type; + Visibility _visibility; +}; + +} + +#if 0 +const unsigned int ICQ_PORT = 5190; + + +const unsigned short ICQ_SEARCHSTATE_OFFLINE = 0; +const unsigned short ICQ_SEARCHSTATE_ONLINE = 1; +const unsigned short ICQ_SEARCHSTATE_DISABLED = 2; + + +// Taken from libicq, not sure if we ever support these requests +const unsigned char PHONEBOOK_SIGN[16] = +{ + 0x90, 0x7C, 0x21, 0x2C, 0x91, 0x4D, 0xD3, 0x11, + 0xAD, 0xEB, 0x00, 0x04, 0xAC, 0x96, 0xAA, 0xB2 +}; + +const unsigned char PLUGINS_SIGN[16] = +{ + 0xF0, 0x02, 0xBF, 0x71, 0x43, 0x71, 0xD3, 0x11, + 0x8D, 0xD2, 0x00, 0x10, 0x4B, 0x06, 0x46, 0x2E +}; + +/* +const unsigned char SHARED_FILES_SIGN[16] = +{ + 0xF0, 0x2D, 0x12, 0xD9, 0x30, 0x91, 0xD3, 0x11, + 0x8D, 0xD7, 0x00, 0x10, 0x4B, 0x06, 0x46, 0x2E +}; +*/ +#endif + +#endif +// vim: set noet ts=4 sts=4 sw=4: +// kate: indent-mode: csands diff --git a/kopete/protocols/oscar/icq/icqprotocol.cpp b/kopete/protocols/oscar/icq/icqprotocol.cpp new file mode 100644 index 00000000..42616e32 --- /dev/null +++ b/kopete/protocols/oscar/icq/icqprotocol.cpp @@ -0,0 +1,820 @@ +/* + icqprotocol.cpp - ICQ Protocol Plugin + + Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org> + Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk> + 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 "config.h" + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#include <netinet/in.h> // for ntohl() + +#include <qcombobox.h> +/* +#include <qhostaddress.h> +#include <qlistbox.h> +#include <qspinbox.h> +#include <qtextedit.h> + +#include <kdatewidget.h>*/ +#include <qvaluelist.h> +#include <kdialogbase.h> +/* +#include <klineedit.h> +#include <kurllabel.h> +*/ +#include <kgenericfactory.h> +#include <kdebug.h> +#include <klocale.h> +#include <ksimpleconfig.h> +#include <kmessagebox.h> + +#include "kopeteglobal.h" +#include "kopeteuiglobal.h" +#include "accountselector.h" +#include "kopeteaccountmanager.h" + +#include "oscartypeclasses.h" + +#include "icqaccount.h" +#include "icqcontact.h" +#include "icqaddcontactpage.h" +#include "icqeditaccountwidget.h" +//#include "icquserinfowidget.h" + + +#include "icqprotocol.h" + +typedef KGenericFactory<ICQProtocol> ICQProtocolFactory; +K_EXPORT_COMPONENT_FACTORY( kopete_icq, ICQProtocolFactory( "kopete_icq" ) ) + +//BEGIN class ICQProtocolHandler + +ICQProtocolHandler::ICQProtocolHandler() : Kopete::MimeTypeHandler(false) +{ + registerAsMimeHandler(QString::fromLatin1("application/x-icq")); +} + +void ICQProtocolHandler::handleURL(const QString &mimeType, const KURL & url) const +{ + if (mimeType != "application/x-icq") + return; + + /** + * File Format usually looks like + * + * [ICQ User] + * UIN=123456789 + * Email= + * NickName= + * FirstName= + * LastName= + */ + + KSimpleConfig file(url.path(), true); + + if (file.hasGroup("ICQ User")) + file.setGroup("ICQ User"); + else if (file.hasGroup("ICQ Message User")) + file.setGroup("ICQ Message User"); + else + return; + + ICQProtocol *proto = ICQProtocol::protocol(); + + QString uin = file.readEntry("UIN"); + if (uin.isEmpty()) + return; + + QString nick = file.readEntry("NickName"); + QString first = file.readEntry("FirstName"); + QString last = file.readEntry("LastName"); + QString email = file.readEntry("Email"); + + Kopete::Account *account = 0; + QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts(proto); + // do not show chooser if we only have one account to "choose" from + if (accounts.count() == 1) + { + QDictIterator<Kopete::Account> it(accounts); + account = it.current(); + } + else + { + KDialogBase *chooser = new KDialogBase(0, "chooser", true, + i18n("Choose Account"), KDialogBase::Ok|KDialogBase::Cancel, + KDialogBase::Ok, false); + AccountSelector *accSelector = new AccountSelector(proto, chooser, + "accSelector"); + chooser->setMainWidget(accSelector); + + int ret = chooser->exec(); + account = accSelector->selectedItem(); + + delete chooser; + if (ret == QDialog::Rejected || account == 0) + { + kdDebug(14153) << k_funcinfo << "Cancelled" << endl; + return; + } + } + + if (!account->isConnected()) + { + kdDebug(14153) << k_funcinfo << "Can't add contact, we are offline!" << endl; + KMessageBox::sorry( Kopete::UI::Global::mainWidget(), i18n("You must be online to add a contact."), i18n("ICQ") ); + return; + } + + QString nickuin = nick.isEmpty() ? + i18n("'%1'").arg(uin) : + i18n("'%1' (%2)").arg(nick, uin); + + if (KMessageBox::questionYesNo(Kopete::UI::Global::mainWidget(), + i18n("Do you want to add %1 to your contact list?").arg(nickuin), QString::null, i18n("Add"), i18n("Do Not Add")) + != KMessageBox::Yes) + { + kdDebug(14153) << k_funcinfo << "Cancelled" << endl; + return; + } + + kdDebug(14153) << k_funcinfo << + "Adding Contact; uin = " << uin << ", nick = '" << nick << + "', firstname = '" << first << "', lastname = '" << last <<"'" << endl; + if (account->addContact(uin, nick, 0L, Kopete::Account::Temporary)) + { + Kopete::Contact *contact = account->contacts()[uin]; + if (!first.isEmpty()) + contact->setProperty(Kopete::Global::Properties::self()->firstName(), first); + if (!last.isEmpty()) + contact->setProperty(Kopete::Global::Properties::self()->lastName(), last); + if (!email.isEmpty()) + contact->setProperty(Kopete::Global::Properties::self()->emailAddress(), email); + } +} + +//END class ICQProtocolHandler + +//BEGIN class ICQProtocol + +ICQProtocol* ICQProtocol::protocolStatic_ = 0L; + +ICQProtocol::ICQProtocol(QObject *parent, const char *name, const QStringList&) +: Kopete::Protocol( ICQProtocolFactory::instance(), parent, name ), + firstName(Kopete::Global::Properties::self()->firstName()), + lastName(Kopete::Global::Properties::self()->lastName()), + awayMessage(Kopete::Global::Properties::self()->awayMessage()), + emailAddress(Kopete::Global::Properties::self()->emailAddress()), + ipAddress("ipAddress", i18n("IP Address") ), + clientFeatures("clientFeatures", i18n("Client Features"), 0, false), + buddyIconHash("iconHash", i18n("Buddy Icon MD5 Hash"), QString::null, true, false, true), + contactEncoding( "contactEncoding", i18n( "Contact Encoding" ), QString::null, true, false, true ) + +{ + if (protocolStatic_) + kdWarning(14153) << k_funcinfo << "ICQ plugin already initialized" << endl; + else + protocolStatic_ = this; + + // must be done after protocolStatic_ is set... + statusManager_ = new ICQ::OnlineStatusManager; + + addAddressBookField("messaging/icq", Kopete::Plugin::MakeIndexField); + + initGenders(); + initLang(); + initCountries(); + initEncodings(); + initMaritals(); + initInterests(); +} + +ICQProtocol::~ICQProtocol() +{ + delete statusManager_; + protocolStatic_ =0L; +} + +void ICQProtocol::initGenders() +{ + mGenders.insert(0, ""); // unspecified + mGenders.insert(1, i18n("Female")); + mGenders.insert(2, i18n("Male")); +} + +void ICQProtocol::initCountries() +{ + mCountries.insert(0, ""); // unspecified + KLocale *kl = KGlobal::locale(); //KLocale(QString::fromLatin1("kopete")); + + mCountries.insert(93, kl->twoAlphaToCountryName("af")); + mCountries.insert(355, kl->twoAlphaToCountryName("al")); + mCountries.insert(213, kl->twoAlphaToCountryName("dz")); + mCountries.insert(684, kl->twoAlphaToCountryName("as")); + mCountries.insert(376, kl->twoAlphaToCountryName("ad")); + mCountries.insert(244, kl->twoAlphaToCountryName("ao")); + mCountries.insert(101, kl->twoAlphaToCountryName("ai")); + mCountries.insert(102, kl->twoAlphaToCountryName("ag")); + mCountries.insert(54, kl->twoAlphaToCountryName("ar")); + mCountries.insert(374, kl->twoAlphaToCountryName("am")); + mCountries.insert(297, kl->twoAlphaToCountryName("aw")); + mCountries.insert(247, i18n("Ascension Island")); + mCountries.insert(61, kl->twoAlphaToCountryName("au")); + mCountries.insert(6721, i18n("Australian Antarctic Territory")); + mCountries.insert(43, kl->twoAlphaToCountryName("at")); + mCountries.insert(994, kl->twoAlphaToCountryName("az")); + mCountries.insert(103, kl->twoAlphaToCountryName("bs")); + mCountries.insert(973, kl->twoAlphaToCountryName("bh")); + mCountries.insert(880, kl->twoAlphaToCountryName("bd")); + mCountries.insert(104, kl->twoAlphaToCountryName("bb")); + mCountries.insert(120, i18n("Barbuda")); + mCountries.insert(375, kl->twoAlphaToCountryName("by")); + mCountries.insert(32, kl->twoAlphaToCountryName("be")); + mCountries.insert(501, kl->twoAlphaToCountryName("bz")); + mCountries.insert(229, kl->twoAlphaToCountryName("bj")); + mCountries.insert(105, kl->twoAlphaToCountryName("bm")); + mCountries.insert(975, kl->twoAlphaToCountryName("bt")); + mCountries.insert(591, kl->twoAlphaToCountryName("bo")); + mCountries.insert(387, kl->twoAlphaToCountryName("ba")); + mCountries.insert(267, kl->twoAlphaToCountryName("bw")); + mCountries.insert(55, kl->twoAlphaToCountryName("br")); + mCountries.insert(106, i18n("British Virgin Islands")); + mCountries.insert(673, kl->twoAlphaToCountryName("bn")); + mCountries.insert(359, kl->twoAlphaToCountryName("bg")); + mCountries.insert(226, kl->twoAlphaToCountryName("bf")); + mCountries.insert(257, kl->twoAlphaToCountryName("bi")); + mCountries.insert(855, kl->twoAlphaToCountryName("kh")); + mCountries.insert(237, kl->twoAlphaToCountryName("cm")); + mCountries.insert(107, kl->twoAlphaToCountryName("ca")); + mCountries.insert(238, kl->twoAlphaToCountryName("cv")); + mCountries.insert(108, kl->twoAlphaToCountryName("ky")); + mCountries.insert(236, kl->twoAlphaToCountryName("cf")); + mCountries.insert(235, kl->twoAlphaToCountryName("td")); + mCountries.insert(56, kl->twoAlphaToCountryName("cl")); + mCountries.insert(86, kl->twoAlphaToCountryName("cn")); + mCountries.insert(672, kl->twoAlphaToCountryName("cx")); + mCountries.insert(6101, kl->twoAlphaToCountryName("c")); + mCountries.insert(57, kl->twoAlphaToCountryName("co")); + mCountries.insert(2691, kl->twoAlphaToCountryName("km")); + mCountries.insert(242, kl->twoAlphaToCountryName("cg")); + mCountries.insert(682, kl->twoAlphaToCountryName("ck")); + mCountries.insert(506, kl->twoAlphaToCountryName("cr")); + mCountries.insert(385, kl->twoAlphaToCountryName("hr")); + mCountries.insert(53, kl->twoAlphaToCountryName("cu")); + mCountries.insert(357, kl->twoAlphaToCountryName("cy")); + mCountries.insert(42, kl->twoAlphaToCountryName("cz")); + mCountries.insert(45, kl->twoAlphaToCountryName("dk")); + mCountries.insert(246, i18n("Diego Garcia")); + mCountries.insert(253, kl->twoAlphaToCountryName("dj")); + mCountries.insert(109, kl->twoAlphaToCountryName("dm")); + mCountries.insert(110, kl->twoAlphaToCountryName("do")); + mCountries.insert(593, kl->twoAlphaToCountryName("ec")); + mCountries.insert(20, kl->twoAlphaToCountryName("eg")); + mCountries.insert(503, kl->twoAlphaToCountryName("sv")); + mCountries.insert(240, kl->twoAlphaToCountryName("gq")); + mCountries.insert(291, kl->twoAlphaToCountryName("er")); + mCountries.insert(372, kl->twoAlphaToCountryName("ee")); + mCountries.insert(251, kl->twoAlphaToCountryName("et")); + mCountries.insert(298, kl->twoAlphaToCountryName("fo")); + mCountries.insert(500, kl->twoAlphaToCountryName("fk")); + mCountries.insert(679, kl->twoAlphaToCountryName("fj")); + mCountries.insert(358, kl->twoAlphaToCountryName("fi")); + mCountries.insert(33, kl->twoAlphaToCountryName("fr")); + mCountries.insert(5901, i18n("French Antilles")); + mCountries.insert(594, kl->twoAlphaToCountryName("gf")); + mCountries.insert(689, kl->twoAlphaToCountryName("pf")); + mCountries.insert(241, kl->twoAlphaToCountryName("ga")); + mCountries.insert(220, kl->twoAlphaToCountryName("gm")); + mCountries.insert(995, kl->twoAlphaToCountryName("ge")); + mCountries.insert(49, kl->twoAlphaToCountryName("de")); + mCountries.insert(233, kl->twoAlphaToCountryName("gh")); + mCountries.insert(350, kl->twoAlphaToCountryName("gi")); + mCountries.insert(30, kl->twoAlphaToCountryName("gr")); + mCountries.insert(299, kl->twoAlphaToCountryName("gl")); + mCountries.insert(111, kl->twoAlphaToCountryName("gd")); + mCountries.insert(590, kl->twoAlphaToCountryName("gp")); + mCountries.insert(671, kl->twoAlphaToCountryName("gu")); + mCountries.insert(5399, i18n("Guantanamo Bay")); + mCountries.insert(502, kl->twoAlphaToCountryName("gt")); + mCountries.insert(224, kl->twoAlphaToCountryName("gn")); + mCountries.insert(245, kl->twoAlphaToCountryName("gw")); + mCountries.insert(592, kl->twoAlphaToCountryName("gy")); + mCountries.insert(509, kl->twoAlphaToCountryName("ht")); + mCountries.insert(504, kl->twoAlphaToCountryName("hn")); + mCountries.insert(852, kl->twoAlphaToCountryName("hk")); + mCountries.insert(36, kl->twoAlphaToCountryName("hu")); + mCountries.insert(871, i18n("INMARSAT (Atlantic-East)")); + mCountries.insert(874, i18n("INMARSAT (Atlantic-West)")); + mCountries.insert(873, i18n("INMARSAT (Indian)")); + mCountries.insert(872, i18n("INMARSAT (Pacific)")); + mCountries.insert(870, i18n("INMARSAT")); + mCountries.insert(354, kl->twoAlphaToCountryName("is")); + mCountries.insert(91, kl->twoAlphaToCountryName("in")); + mCountries.insert(62, kl->twoAlphaToCountryName("id")); + mCountries.insert(800, i18n("International Freephone Service")); + mCountries.insert(98, kl->twoAlphaToCountryName("ir")); + mCountries.insert(964, kl->twoAlphaToCountryName("iq")); + mCountries.insert(353, kl->twoAlphaToCountryName("ie")); + mCountries.insert(972, kl->twoAlphaToCountryName("il")); + mCountries.insert(39, kl->twoAlphaToCountryName("it")); + mCountries.insert(225, i18n("Ivory Coast")); + mCountries.insert(112, kl->twoAlphaToCountryName("jm")); + mCountries.insert(81, kl->twoAlphaToCountryName("jp")); + mCountries.insert(962, kl->twoAlphaToCountryName("jo")); + mCountries.insert(705, kl->twoAlphaToCountryName("kz")); + mCountries.insert(254, kl->twoAlphaToCountryName("ke")); + mCountries.insert(686, kl->twoAlphaToCountryName("ki")); + mCountries.insert(850, kl->twoAlphaToCountryName("kp")); + mCountries.insert(82, kl->twoAlphaToCountryName("kr")); + mCountries.insert(965, kl->twoAlphaToCountryName("kw")); + mCountries.insert(706, kl->twoAlphaToCountryName("kg")); + mCountries.insert(856, kl->twoAlphaToCountryName("la")); + mCountries.insert(371, kl->twoAlphaToCountryName("lv")); + mCountries.insert(961, kl->twoAlphaToCountryName("kb")); + mCountries.insert(266, kl->twoAlphaToCountryName("ls")); + mCountries.insert(231, kl->twoAlphaToCountryName("lr")); + mCountries.insert(218, kl->twoAlphaToCountryName("ly")); + mCountries.insert(4101, kl->twoAlphaToCountryName("li")); + mCountries.insert(370, kl->twoAlphaToCountryName("lt")); + mCountries.insert(352, kl->twoAlphaToCountryName("lu")); + mCountries.insert(853, kl->twoAlphaToCountryName("mo")); + mCountries.insert(261, kl->twoAlphaToCountryName("mg")); + mCountries.insert(265, kl->twoAlphaToCountryName("mw")); + mCountries.insert(60, kl->twoAlphaToCountryName("my")); + mCountries.insert(960, kl->twoAlphaToCountryName("mv")); + mCountries.insert(223, kl->twoAlphaToCountryName("ml")); + mCountries.insert(356, kl->twoAlphaToCountryName("mt")); + mCountries.insert(692, kl->twoAlphaToCountryName("mh")); + mCountries.insert(596, kl->twoAlphaToCountryName("mq")); + mCountries.insert(222, kl->twoAlphaToCountryName("mr")); + mCountries.insert(230, kl->twoAlphaToCountryName("mu")); + mCountries.insert(269, kl->twoAlphaToCountryName("yt")); + mCountries.insert(52, kl->twoAlphaToCountryName("mx")); + mCountries.insert(691, kl->twoAlphaToCountryName("fm")); + mCountries.insert(373, kl->twoAlphaToCountryName("md")); + mCountries.insert(377, kl->twoAlphaToCountryName("mc")); + mCountries.insert(976, kl->twoAlphaToCountryName("mn")); + mCountries.insert(113, kl->twoAlphaToCountryName("ms")); + mCountries.insert(212, kl->twoAlphaToCountryName("ma")); + mCountries.insert(258, kl->twoAlphaToCountryName("mz")); + mCountries.insert(95, kl->twoAlphaToCountryName("mm")); + mCountries.insert(264, kl->twoAlphaToCountryName("na")); + mCountries.insert(674, kl->twoAlphaToCountryName("nr")); + mCountries.insert(977, kl->twoAlphaToCountryName("np")); + mCountries.insert(599, kl->twoAlphaToCountryName("an")); + mCountries.insert(31, kl->twoAlphaToCountryName("nl")); + mCountries.insert(114, i18n("Nevis")); + mCountries.insert(687, kl->twoAlphaToCountryName("nc")); + mCountries.insert(64, kl->twoAlphaToCountryName("nz")); + mCountries.insert(505, kl->twoAlphaToCountryName("ni")); + mCountries.insert(227, kl->twoAlphaToCountryName("ne")); + mCountries.insert(234, kl->twoAlphaToCountryName("ng")); + mCountries.insert(683, kl->twoAlphaToCountryName("nu")); + mCountries.insert(6722, kl->twoAlphaToCountryName("nf")); + mCountries.insert(47, kl->twoAlphaToCountryName("no")); + mCountries.insert(968, kl->twoAlphaToCountryName("om")); + mCountries.insert(92, kl->twoAlphaToCountryName("pk")); + mCountries.insert(680, kl->twoAlphaToCountryName("pw")); + mCountries.insert(507, kl->twoAlphaToCountryName("pa")); + mCountries.insert(675, kl->twoAlphaToCountryName("pg")); + mCountries.insert(595, kl->twoAlphaToCountryName("py")); + mCountries.insert(51, kl->twoAlphaToCountryName("pe")); + mCountries.insert(63, kl->twoAlphaToCountryName("ph")); + mCountries.insert(48, kl->twoAlphaToCountryName("pl")); + mCountries.insert(351, kl->twoAlphaToCountryName("pt")); + mCountries.insert(121, kl->twoAlphaToCountryName("pr")); + mCountries.insert(974, kl->twoAlphaToCountryName("qa")); + mCountries.insert(389, kl->twoAlphaToCountryName("mk")); + mCountries.insert(262, i18n("Reunion Island")); + mCountries.insert(40, kl->twoAlphaToCountryName("ro")); + mCountries.insert(6701, i18n("Rota Island")); + mCountries.insert(7, kl->twoAlphaToCountryName("ru")); + mCountries.insert(250, kl->twoAlphaToCountryName("rw")); + mCountries.insert(122, kl->twoAlphaToCountryName("lc")); + mCountries.insert(670, i18n("Ivory Coast")); + mCountries.insert(378, kl->twoAlphaToCountryName("sm")); + mCountries.insert(239, kl->twoAlphaToCountryName("st")); + mCountries.insert(966, kl->twoAlphaToCountryName("sa")); + mCountries.insert(221, kl->twoAlphaToCountryName("sn")); + mCountries.insert(248, kl->twoAlphaToCountryName("sc")); + mCountries.insert(232, kl->twoAlphaToCountryName("sl")); + mCountries.insert(65, kl->twoAlphaToCountryName("sg")); + mCountries.insert(4201, kl->twoAlphaToCountryName("sk")); + mCountries.insert(386, kl->twoAlphaToCountryName("si")); + mCountries.insert(677, kl->twoAlphaToCountryName("sb")); + mCountries.insert(252, kl->twoAlphaToCountryName("so")); + mCountries.insert(27, kl->twoAlphaToCountryName("za")); + mCountries.insert(34, kl->twoAlphaToCountryName("es")); + mCountries.insert(94, kl->twoAlphaToCountryName("lk")); + mCountries.insert(290, kl->twoAlphaToCountryName("sh")); + mCountries.insert(115, kl->twoAlphaToCountryName("kn")); + mCountries.insert(508, kl->twoAlphaToCountryName("pm")); + mCountries.insert(116, kl->twoAlphaToCountryName("vc")); + mCountries.insert(249, kl->twoAlphaToCountryName("sd")); + mCountries.insert(597, kl->twoAlphaToCountryName("sr")); + mCountries.insert(268, kl->twoAlphaToCountryName("sz")); + mCountries.insert(46, kl->twoAlphaToCountryName("se")); + mCountries.insert(41, kl->twoAlphaToCountryName("ch")); + mCountries.insert(963, kl->twoAlphaToCountryName("sy")); + mCountries.insert(886, kl->twoAlphaToCountryName("tw")); + mCountries.insert(708, kl->twoAlphaToCountryName("tj")); + mCountries.insert(255, kl->twoAlphaToCountryName("tz")); + mCountries.insert(66, kl->twoAlphaToCountryName("th")); + mCountries.insert(6702, i18n("Tinian Island")); + mCountries.insert(228, kl->twoAlphaToCountryName("tg")); // Togo + mCountries.insert(690, kl->twoAlphaToCountryName("tk")); // Tokelau + mCountries.insert(676, kl->twoAlphaToCountryName("to")); // Tonga + mCountries.insert(117, kl->twoAlphaToCountryName("tt")); // Trinidad and Tobago + mCountries.insert(216, kl->twoAlphaToCountryName("tn")); // Tunisia + mCountries.insert(90, kl->twoAlphaToCountryName("tr")); + mCountries.insert(709, kl->twoAlphaToCountryName("tm")); + mCountries.insert(118, kl->twoAlphaToCountryName("tc")); // Turks and Caicos Island + mCountries.insert(688, kl->twoAlphaToCountryName("tv")); // Tuvalu + mCountries.insert(1, kl->twoAlphaToCountryName("us")); // United States of America + mCountries.insert(256, kl->twoAlphaToCountryName("ug")); // Uganda + mCountries.insert(380, kl->twoAlphaToCountryName("ua")); // Ukraine + mCountries.insert(971, kl->twoAlphaToCountryName("ae")); // United Arab Emirates + mCountries.insert(44, kl->twoAlphaToCountryName("gb")); // United Kingdom + mCountries.insert(123, kl->twoAlphaToCountryName("vi")); // United States Virgin Islands + mCountries.insert(598, kl->twoAlphaToCountryName("uy")); // Uruguay + mCountries.insert(711, kl->twoAlphaToCountryName("uz")); // Uzbekistan + mCountries.insert(678, kl->twoAlphaToCountryName("vu")); // Vanuatu + mCountries.insert(379, kl->twoAlphaToCountryName("va")); // Vatican City + mCountries.insert(58, kl->twoAlphaToCountryName("ve")); // Venezuela + mCountries.insert(84, kl->twoAlphaToCountryName("vn")); // Vietnam + mCountries.insert(681, kl->twoAlphaToCountryName("wf")); // Wallis and Futuna Islands + mCountries.insert(685, kl->twoAlphaToCountryName("eh")); + mCountries.insert(967, kl->twoAlphaToCountryName("ye")); + mCountries.insert(381, kl->twoAlphaToCountryName("yu")); + mCountries.insert(243, kl->twoAlphaToCountryName("zr")); + mCountries.insert(260, kl->twoAlphaToCountryName("zm")); + mCountries.insert(263, kl->twoAlphaToCountryName("zw")); +} + +void ICQProtocol::initLang() +{ + + KLocale *kl = KGlobal::locale(); //KLocale(QString::fromLatin1("kopete")); + + mLanguages.insert(0 , ""); + mLanguages.insert(1 , kl->twoAlphaToLanguageName("ar") /*i18n("Arabic")*/); + mLanguages.insert(2 , i18n("Bhojpuri")); + mLanguages.insert(3 , kl->twoAlphaToLanguageName("bg") /*i18n("Bulgarian")*/); + mLanguages.insert(4 , kl->twoAlphaToLanguageName("my") /*i18n("Burmese")*/); + mLanguages.insert(5 , i18n("Cantonese")); + mLanguages.insert(6 , kl->twoAlphaToLanguageName("ca") /*i18n("Catalan")*/); + mLanguages.insert(7 , kl->twoAlphaToLanguageName("zh") /*i18n("Chinese")*/); + mLanguages.insert(8 , kl->twoAlphaToLanguageName("hr") /*i18n("Croatian")*/); + mLanguages.insert(9 , kl->twoAlphaToLanguageName("cs") /*i18n("Czech")*/); + mLanguages.insert(10, kl->twoAlphaToLanguageName("da") /*i18n("Danish")*/); + mLanguages.insert(11, kl->twoAlphaToLanguageName("nl") /*i18n("Dutch")*/); + mLanguages.insert(12, kl->twoAlphaToLanguageName("en") /*i18n("English")*/); + mLanguages.insert(13, kl->twoAlphaToLanguageName("eo") /*i18n("Esperanto")*/); + mLanguages.insert(14, kl->twoAlphaToLanguageName("et") /*i18n("Estonian")*/); + mLanguages.insert(15, i18n("Farsi")); + mLanguages.insert(16, kl->twoAlphaToLanguageName("fi") /*i18n("Finnish")*/); + mLanguages.insert(17, kl->twoAlphaToLanguageName("fr") /*i18n("French")*/); + mLanguages.insert(18, kl->twoAlphaToLanguageName("gd") /*i18n("Gaelic")*/); + mLanguages.insert(19, kl->twoAlphaToLanguageName("de") /*i18n("German")*/); + mLanguages.insert(20, kl->twoAlphaToLanguageName("el") /*i18n("Greek")*/); + mLanguages.insert(21, kl->twoAlphaToLanguageName("he") /*i18n("Hebrew")*/); + mLanguages.insert(22, kl->twoAlphaToLanguageName("hi") /*i18n("Hindi")*/); + mLanguages.insert(23, kl->twoAlphaToLanguageName("hu") /*i18n("Hungarian")*/); + mLanguages.insert(24, kl->twoAlphaToLanguageName("is") /*i18n("Icelandic")*/); + mLanguages.insert(25, kl->twoAlphaToLanguageName("id") /*i18n("Indonesian")*/); + mLanguages.insert(26, kl->twoAlphaToLanguageName("it") /*i18n("Italian")*/); + mLanguages.insert(27, kl->twoAlphaToLanguageName("ja") /*i18n("Japanese")*/); + mLanguages.insert(28, kl->twoAlphaToLanguageName("km") /*i18n("Khmer")*/); + mLanguages.insert(29, kl->twoAlphaToLanguageName("ko") /*i18n("Korean")*/); + mLanguages.insert(30, kl->twoAlphaToLanguageName("lo") /*i18n("Lao")*/); + mLanguages.insert(31, kl->twoAlphaToLanguageName("lv") /*i18n("Latvian")*/); + mLanguages.insert(32, kl->twoAlphaToLanguageName("lt") /*i18n("Lithuanian")*/); + mLanguages.insert(33, kl->twoAlphaToLanguageName("ms") /*i18n("Malay")*/); + mLanguages.insert(34, kl->twoAlphaToLanguageName("no") /*i18n("Norwegian")*/); + mLanguages.insert(35, kl->twoAlphaToLanguageName("pl") /*i18n("Polish")*/); + mLanguages.insert(36, kl->twoAlphaToLanguageName("pt") /*i18n("Portuguese")*/); + mLanguages.insert(37, kl->twoAlphaToLanguageName("ro") /*i18n("Romanian")*/); + mLanguages.insert(38, kl->twoAlphaToLanguageName("ru") /*i18n("Russian")*/); + mLanguages.insert(39, kl->twoAlphaToLanguageName("sr") /*i18n("Serbian")*/); + mLanguages.insert(40, kl->twoAlphaToLanguageName("sk") /*i18n("Slovak")*/); + mLanguages.insert(41, kl->twoAlphaToLanguageName("sl") /*i18n("Slovenian")*/); + mLanguages.insert(42, kl->twoAlphaToLanguageName("so") /*i18n("Somali")*/); + mLanguages.insert(43, kl->twoAlphaToLanguageName("es") /*i18n("Spanish")*/); + mLanguages.insert(44, kl->twoAlphaToLanguageName("sw") /*i18n("Swahili")*/); + mLanguages.insert(45, kl->twoAlphaToLanguageName("sv") /*i18n("Swedish")*/); + mLanguages.insert(46, kl->twoAlphaToLanguageName("tl") /*i18n("Tagalog")*/); + mLanguages.insert(47, kl->twoAlphaToLanguageName("tt") /*i18n("Tatar")*/); + mLanguages.insert(48, kl->twoAlphaToLanguageName("th") /*i18n("Thai")*/); + mLanguages.insert(49, kl->twoAlphaToLanguageName("tr") /*i18n("Turkish")*/); + mLanguages.insert(50, kl->twoAlphaToLanguageName("uk") /*i18n("Ukrainian")*/); + mLanguages.insert(51, kl->twoAlphaToLanguageName("ur") /*i18n("Urdu")*/); + mLanguages.insert(52, kl->twoAlphaToLanguageName("vi") /*i18n("Vietnamese")*/); + mLanguages.insert(53, kl->twoAlphaToLanguageName("yi") /*i18n("Yiddish")*/); + mLanguages.insert(54, kl->twoAlphaToLanguageName("yo") /*i18n("Yoruba")*/); + mLanguages.insert(55, i18n("Taiwanese")); + mLanguages.insert(56, kl->twoAlphaToLanguageName("af") /*i18n("Afrikaans")*/); + mLanguages.insert(57, kl->twoAlphaToLanguageName("fa") /*i18n("Persian")*/); + mLanguages.insert(58, kl->twoAlphaToLanguageName("sq") /*i18n("Albanian")*/); + mLanguages.insert(59, kl->twoAlphaToLanguageName("hy") /*i18n("Armenian")*/); +} + +void ICQProtocol::initEncodings() +{ + mEncodings.insert(2026, i18n("Big5")); + mEncodings.insert(2101, i18n("Big5-HKSCS")); + mEncodings.insert(18, i18n("euc-JP Japanese")); + mEncodings.insert(38, i18n("euc-KR Korean")); + mEncodings.insert(57, i18n("GB-2312 Chinese")); + mEncodings.insert(113, i18n("GBK Chinese")); + mEncodings.insert(114, i18n("GB18030 Chinese")); + + mEncodings.insert(16, i18n("JIS Japanese")); + mEncodings.insert(17, i18n("Shift-JIS Japanese")); + + mEncodings.insert(2084, i18n("KOI8-R Russian")); + mEncodings.insert(2088, i18n("KOI8-U Ukrainian")); + + mEncodings.insert(4, i18n("ISO-8859-1 Western")); + mEncodings.insert(5, i18n("ISO-8859-2 Central European")); + mEncodings.insert(6, i18n("ISO-8859-3 Central European")); + mEncodings.insert(7, i18n("ISO-8859-4 Baltic")); + mEncodings.insert(8, i18n("ISO-8859-5 Cyrillic")); + mEncodings.insert(9, i18n("ISO-8859-6 Arabic")); + mEncodings.insert(10, i18n("ISO-8859-7 Greek")); + mEncodings.insert(11, i18n("ISO-8859-8 Hebrew, visually ordered")); + mEncodings.insert(85, i18n("ISO-8859-8-I Hebrew, logically ordered")); + mEncodings.insert(12, i18n("ISO-8859-9 Turkish")); + mEncodings.insert(13, i18n("ISO-8859-10")); + mEncodings.insert(109, i18n("ISO-8859-13")); + mEncodings.insert(110, i18n("ISO-8859-14")); + mEncodings.insert(111, i18n("ISO-8859-15 Western")); + + mEncodings.insert(2250, i18n("Windows-1250 Central European")); + mEncodings.insert(2251, i18n("Windows-1251 Cyrillic")); + mEncodings.insert(2252, i18n("Windows-1252 Western")); + mEncodings.insert(2253, i18n("Windows-1253 Greek")); + mEncodings.insert(2254, i18n("Windows-1254 Turkish")); + mEncodings.insert(2255, i18n("Windows-1255 Hebrew")); + mEncodings.insert(2256, i18n("Windows-1256 Arabic")); + mEncodings.insert(2257, i18n("Windows-1257 Baltic")); + mEncodings.insert(2258, i18n("Windows-1258 Viet Nam")); + + mEncodings.insert(2009, i18n("IBM 850")); + mEncodings.insert(2085, i18n("IBM 866")); + + mEncodings.insert(2259, i18n("TIS-620 Thai")); + + mEncodings.insert(106, i18n("UTF-8 Unicode")); + mEncodings.insert(1015, i18n("UTF-16 Unicode")); + +/* +Missing ones (copied from qtextcodec doc): +TSCII -- Tamil +utf8 -- Unicode, 8-bit +utf16 -- Unicode +CP874 +Apple Roman +*/ +} +void ICQProtocol::initMaritals() +{ + mMarital.insert(0 , ""); + mMarital.insert(10 , i18n("Single")); + mMarital.insert(11 , i18n("Long term relationship")); + mMarital.insert(12 , i18n("Engaged")); + mMarital.insert(20 , i18n("Married")); + mMarital.insert(30 , i18n("Divorced")); + mMarital.insert(31 , i18n("Separated")); + mMarital.insert(40 , i18n("Widowed")); + +} + +void ICQProtocol::initInterests() +{ + mInterests.insert(0 , ""); + mInterests.insert(100, i18n("Art")); + mInterests.insert(101, i18n("Cars")); + mInterests.insert(102, i18n("Celebrities")); + mInterests.insert(103, i18n("Collections")); + mInterests.insert(104, i18n("Computers")); + mInterests.insert(105, i18n("Culture")); + mInterests.insert(106, i18n("Fitness")); + mInterests.insert(107, i18n("Games")); + mInterests.insert(108, i18n("Hobbies")); + mInterests.insert(109, i18n("ICQ - Help")); + mInterests.insert(110, i18n("Internet")); + mInterests.insert(111, i18n("Lifestyle")); + mInterests.insert(112, i18n("Movies")); + mInterests.insert(113, i18n("Music")); + mInterests.insert(114, i18n("Outdoors")); + mInterests.insert(115, i18n("Parenting")); + mInterests.insert(116, i18n("Pets and animals")); + mInterests.insert(117, i18n("Religion")); + mInterests.insert(118, i18n("Science")); + mInterests.insert(119, i18n("Skills")); + mInterests.insert(120, i18n("Sports")); + mInterests.insert(121, i18n("Web design")); + mInterests.insert(122, i18n("Ecology")); + mInterests.insert(123, i18n("News and media")); + mInterests.insert(124, i18n("Government")); + mInterests.insert(125, i18n("Business")); + mInterests.insert(126, i18n("Mystics")); + mInterests.insert(127, i18n("Travel")); + mInterests.insert(128, i18n("Astronomy")); + mInterests.insert(129, i18n("Space")); + mInterests.insert(130, i18n("Clothing")); + mInterests.insert(131, i18n("Parties")); + mInterests.insert(132, i18n("Women")); + mInterests.insert(133, i18n("Social science")); + mInterests.insert(134, i18n("60's")); + mInterests.insert(135, i18n("70's")); + mInterests.insert(136, i18n("40's")); + mInterests.insert(137, i18n("50's")); + mInterests.insert(138, i18n("Finance and corporate")); + mInterests.insert(139, i18n("Entertainment")); + mInterests.insert(140, i18n("Consumer electronics")); + mInterests.insert(141, i18n("Retail stores")); + mInterests.insert(142, i18n("Health and beauty")); + mInterests.insert(143, i18n("Media")); + mInterests.insert(144, i18n("Household products")); + mInterests.insert(145, i18n("Mail order catalog")); + mInterests.insert(146, i18n("Business services")); + mInterests.insert(147, i18n("Audio and visual")); + mInterests.insert(148, i18n("Sporting and athletic")); + mInterests.insert(149, i18n("Publishing")); + mInterests.insert(150, i18n("Home automation")); + +} + +void ICQProtocol::fillComboFromTable(QComboBox *box, const QMap<int, QString> &map) +{ +// kdDebug(14153) << k_funcinfo << "Called." << endl; + + QStringList list = map.values(); + list.sort(); + box->insertStringList(list); +} + +void ICQProtocol::setComboFromTable(QComboBox *box, const QMap<int, QString> &map, int value) +{ +// kdDebug(14153) << k_funcinfo << "Called." << endl; + QMap<int, QString>::ConstIterator it; + it = map.find(value); + if (!(*it)) + return; + + for(int i=0; i<box->count(); i++) + { + if((*it) == box->text(i)) + { + box->setCurrentItem(i); + return; + } + } +} + +int ICQProtocol::getCodeForCombo(QComboBox *cmb, const QMap<int, QString> &map) +{ + const QString curText = cmb->currentText(); + + QMap<int, QString>::ConstIterator it; + for(it = map.begin(); it != map.end(); ++it) + { + if(it.data() == curText) + return it.key(); + } + return 0; // unspecified is always first 0 +} +#if 0 + +void ICQProtocol::fillTZCombo(QComboBox *combo) +{ + QTime time(12, 0); + QTime done(0, 0); + + while(time > done) + { + combo->insertItem("GMT-" + time.toString("h:mm")); + // subtract 30 minutes + time = time.addSecs(-30 * 60); + } + + time = QTime(0, 0); + done = QTime(12, 0); + + while(time <= done) + { + combo->insertItem("GMT+" + time.toString("h:mm")); + // add 30 minutes + time = time.addSecs(30 * 60); + } +} + +void ICQProtocol::setTZComboValue(QComboBox *combo, const char &tz) +{ + kdDebug(14153) << k_funcinfo << "tz=" << int(tz) << endl; + if ((tz < -24) || (tz > 24)) + combo->setCurrentItem(24); // GMT+0:00 as default + else + combo->setCurrentItem(24 + tz); +} + +char ICQProtocol::getTZComboValue(QComboBox *combo) +{ + char ret = combo->currentItem() - 24; +// kdDebug(14153) << k_funcinfo << "return value=" << int(ret) << endl; + return ret; +} + +#endif +ICQProtocol *ICQProtocol::protocol() +{ + return protocolStatic_; +} + +bool ICQProtocol::canSendOffline() const +{ + return true; +} + +Kopete::Contact *ICQProtocol::deserializeContact( Kopete::MetaContact *metaContact, + const QMap<QString, QString> &serializedData, + const QMap<QString, QString> &/*addressBookData*/ ) +{ + QString accountId = serializedData["accountId"]; + QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts(this); + ICQAccount *account = static_cast<ICQAccount*>(accounts[accountId]); + + if(!account) + { + kdWarning(14153) << k_funcinfo << + "WARNING: Account for contact does not exist, skipping " << accountId << endl; + return 0; + } + + QString contactId=serializedData["contactId"]; + uint ssiGid = 0, ssiBid = 0, ssiType = 0xFFFF; + QString ssiName; + bool ssiWaitingAuth = false; + if ( serializedData.contains( "ssi_name" ) ) + ssiName = serializedData["ssi_name"]; + + if ( serializedData.contains( "ssi_waitingAuth" ) ) + { + QString authStatus = serializedData["ssi_waitingAuth"]; + if ( authStatus == "true" ) + ssiWaitingAuth = true; + } + + if ( serializedData.contains( "ssi_gid" ) ) + ssiGid = serializedData["ssi_gid"].toUInt(); + if ( serializedData.contains( "ssi_bid" ) ) + ssiBid = serializedData["ssi_bid"].toUInt(); + if ( serializedData.contains( "ssi_type" ) ) + ssiType = serializedData["ssi_type"].toUInt(); + + Oscar::SSI item( ssiName, ssiGid, ssiBid, ssiType, QValueList<TLV>(), 0 ); + item.setWaitingAuth( ssiWaitingAuth ); + ICQContact *c = new ICQContact( account, contactId, metaContact, QString::null, item ); + return c; +} + +AddContactPage *ICQProtocol::createAddContactWidget(QWidget *parent, Kopete::Account *account) +{ + return new ICQAddContactPage( static_cast<ICQAccount*>( account ), parent); +} + +KopeteEditAccountWidget *ICQProtocol::createEditAccountWidget(Kopete::Account *account, QWidget *parent) +{ + return new ICQEditAccountWidget(this, account, parent); +} + +Kopete::Account *ICQProtocol::createNewAccount(const QString &accountId) +{ + return new ICQAccount(this, accountId); +} + +ICQ::OnlineStatusManager *ICQProtocol::statusManager() +{ + return statusManager_; +} + +//END class ICQProtocol + +#include "icqprotocol.moc" +// kate: indent-mode csands; +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/protocols/oscar/icq/icqprotocol.h b/kopete/protocols/oscar/icq/icqprotocol.h new file mode 100644 index 00000000..8e3c1be9 --- /dev/null +++ b/kopete/protocols/oscar/icq/icqprotocol.h @@ -0,0 +1,106 @@ +/* + oscarprotocol.h - Oscar Protocol Plugin + + Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org> + Kopete (c) 2003 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. * + * * + ************************************************************************* +*/ + +#ifndef ICQPROTOCOL_H +#define ICQPROTOCOL_H + +#include "kopeteprotocol.h" +#include "kopetemimetypehandler.h" +#include "kopeteonlinestatus.h" + +class QComboBox; +/*class ICQUserInfoWidget; +class ICQContact;*/ + +namespace ICQ { class OnlineStatusManager; } + +class ICQProtocolHandler : public Kopete::MimeTypeHandler +{ +public: + ICQProtocolHandler(); + void handleURL(const QString &mimeType, const KURL & url) const; +}; + + +class ICQProtocol : public Kopete::Protocol +{ +Q_OBJECT + +public: + ICQProtocol(QObject *parent, const char *name, const QStringList &args); + virtual ~ICQProtocol(); + + /** + * Return the active instance of the protocol + */ + static ICQProtocol *protocol(); + + virtual bool canSendOffline() const; + + virtual Kopete::Contact *deserializeContact( Kopete::MetaContact *metaContact, + const QMap<QString, QString> &serializedData, + const QMap<QString, QString> &addressBookData ); + AddContactPage *createAddContactWidget(QWidget *parent, Kopete::Account *account); + KopeteEditAccountWidget *createEditAccountWidget(Kopete::Account *account, QWidget *parent); + Kopete::Account *createNewAccount(const QString &accountId); + + ICQ::OnlineStatusManager *statusManager(); + + + const Kopete::ContactPropertyTmpl firstName; + const Kopete::ContactPropertyTmpl lastName; + const Kopete::ContactPropertyTmpl awayMessage; + const Kopete::ContactPropertyTmpl emailAddress; + const Kopete::ContactPropertyTmpl ipAddress; + const Kopete::ContactPropertyTmpl clientFeatures; + const Kopete::ContactPropertyTmpl buddyIconHash; + const Kopete::ContactPropertyTmpl contactEncoding; + + const QMap<int, QString> &genders() { return mGenders; } + const QMap<int, QString> &countries() { return mCountries; } + const QMap<int, QString> &languages() { return mLanguages; } + const QMap<int, QString> &encodings() { return mEncodings; } + const QMap<int, QString> &maritals() { return mMarital; } + const QMap<int, QString> &interests() { return mInterests; } + + void fillComboFromTable( QComboBox*, const QMap<int, QString>& ); + void setComboFromTable( QComboBox*, const QMap<int, QString>&, int ); + int getCodeForCombo( QComboBox*, const QMap<int, QString>& ); + /* void fillTZCombo(QComboBox *combo); + void setTZComboValue(QComboBox *combo, const char &tz); + char getTZComboValue(QComboBox *combo); */ + +private: + void initGenders(); + void initLang(); + void initCountries(); + void initEncodings(); + void initMaritals(); + void initInterests(); + +private: + static ICQProtocol* protocolStatic_; + ICQ::OnlineStatusManager* statusManager_; + QMap<int, QString> mGenders; + QMap<int, QString> mCountries; + QMap<int, QString> mLanguages; + QMap<int, QString> mEncodings; + QMap<int, QString> mMarital; + QMap<int, QString> mInterests; + ICQProtocolHandler protohandler; +}; +#endif +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/protocols/oscar/icq/icqreadaway.cpp b/kopete/protocols/oscar/icq/icqreadaway.cpp new file mode 100644 index 00000000..94cccafd --- /dev/null +++ b/kopete/protocols/oscar/icq/icqreadaway.cpp @@ -0,0 +1,106 @@ +/* + icqreadaway.cpp - ICQ Protocol Plugin + + Copyright (c) 2003 by Stefan Gehn <metz AT gehn.net> + Kopete (c) 2003 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 "icqreadaway.h" + +#include "icqprotocol.h" +#include "icqaccount.h" +#include "icqcontact.h" + +#include <qvbox.h> + +#include <ktextbrowser.h> +#include <klocale.h> +#include <krun.h> + +#include <assert.h> + + +ICQReadAway::ICQReadAway(ICQContact *c, QWidget *parent, const char* name) + : KDialogBase(parent, name, false, QString::null, Close | User1, + Close, false, i18n("&Fetch Again")) +{ + assert(c); + + mAccount = static_cast<ICQAccount*>(c->account()); + mContact = c; + setCaption(i18n("'%2' Message for %1").arg(c->displayName()).arg(c->onlineStatus().description())); + + QVBox *mMainWidget = makeVBoxMainWidget(); + + awayMessageBrowser = new KTextBrowser(mMainWidget, "userInfoView"); + awayMessageBrowser->setTextFormat(AutoText); + awayMessageBrowser->setNotifyClick(true); + awayMessageBrowser->setText(mContact->awayMessage()); + + QObject::connect( + awayMessageBrowser, SIGNAL(urlClick(const QString&)), + this, SLOT(slotUrlClicked(const QString&))); + QObject::connect( + awayMessageBrowser, SIGNAL(mailClick(const QString&, const QString&)), + this, SLOT(slotMailClicked(const QString&, const QString&))); + + connect(this, SIGNAL(user1Clicked()), + this, SLOT(slotFetchAwayMessage())); + connect(this, SIGNAL(closeClicked()), + this, SLOT(slotCloseClicked())); + + connect(c, SIGNAL(awayMessageChanged()), + this, SLOT(slotAwayMessageChanged())); + + slotFetchAwayMessage(); +} + +void ICQReadAway::slotFetchAwayMessage() +{ + if(!mAccount->isConnected()) + return; + + awayMessageBrowser->setDisabled(true); + enableButton(User1,false); + + mAccount->engine()->requestAwayMessage(mContact); + + setCaption(i18n("Fetching '%2' Message for %1...").arg(mContact->displayName()).arg(mContact->onlineStatus().description())); +} // END slotFetchAwayMessage() + +void ICQReadAway::slotAwayMessageChanged() +{ + setCaption(i18n("'%2' Message for %1").arg(mContact->displayName()).arg(mContact->onlineStatus().description())); + awayMessageBrowser->setText(mContact->awayMessage()); + + awayMessageBrowser->setDisabled(false); + enableButton(User1,true); + +} // END slotAwayMessageChanged() + +void ICQReadAway::slotCloseClicked() +{ + emit closing(); +} + +void ICQReadAway::slotUrlClicked(const QString &url) +{ + new KRun(KURL(url)); +} + +void ICQReadAway::slotMailClicked(const QString&, const QString &address) +{ + new KRun(KURL(address)); +} + +#include "icqreadaway.moc" +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/protocols/oscar/icq/icqreadaway.h b/kopete/protocols/oscar/icq/icqreadaway.h new file mode 100644 index 00000000..7e62588e --- /dev/null +++ b/kopete/protocols/oscar/icq/icqreadaway.h @@ -0,0 +1,52 @@ +/* + icqreadaway.h - ICQ Protocol Plugin + + Copyright (c) 2003 by Stefan Gehn <metz AT gehn.net> + Kopete (c) 2003 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. * + * * + ************************************************************************* +*/ + +#ifndef ICQREADAWAY_H +#define ICQREADAWAY_H + +#include <kdebug.h> +#include <kdialogbase.h> + +class ICQAccount; +class ICQContact; +class KTextBrowser; +class QVBox; + +class ICQReadAway : public KDialogBase +{ + Q_OBJECT + + public: + ICQReadAway(ICQContact *, QWidget *parent = 0, const char* name = "ICQReadAway"); + + private slots: + void slotFetchAwayMessage(); + void slotAwayMessageChanged(); + void slotCloseClicked(); + void slotUrlClicked(const QString &url); + void slotMailClicked(const QString&, const QString &address); + + signals: + void closing(); + + private: + ICQAccount *mAccount; + ICQContact *mContact; + QVBox *mMainWidget; + KTextBrowser *awayMessageBrowser; +}; +#endif +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/protocols/oscar/icq/kopete_icq.desktop b/kopete/protocols/oscar/icq/kopete_icq.desktop new file mode 100644 index 00000000..c774afde --- /dev/null +++ b/kopete/protocols/oscar/icq/kopete_icq.desktop @@ -0,0 +1,78 @@ +[Desktop Entry] +Type=Service +X-Kopete-Version=1000900 +Icon=icq_protocol +ServiceTypes=Kopete/Protocol +X-KDE-Library=kopete_icq +X-Kopete-Messaging-Protocol=messaging/icq +X-KDE-PluginInfo-Author=Kopete Developers +X-KDE-PluginInfo-Email=kopete-devel@kde.org +X-KDE-PluginInfo-Name=kopete_icq +X-KDE-PluginInfo-Version=0.10.0 +X-KDE-PluginInfo-Website=http://kopete.kde.org +X-KDE-PluginInfo-Category=Protocols +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false +Name=ICQ +Name[bn]=আই-সি-কিউ +Name[hi]=आईसीक्यू +Name[ne]=आईसीक्यू +Comment=Protocol to connect to ICQ +Comment[ar]=البرتوكول سيتصل بـ ICQ +Comment[be]=Пратакол ICQ +Comment[bg]=Протокол за връзка с ICQ +Comment[bn]=আই-সি-কিউতে সংযোগ করতে প্রোটোকল +Comment[br]=Komenad kevreañ ouzh AIM +Comment[bs]=ICQ protokol +Comment[ca]=Protocol per a connectar-se a ICQ +Comment[cs]=Protokol k připojení k ICQ +Comment[cy]=Protocol i gysylltu ag ICQ +Comment[da]=Protokol til at forbinde til ICQ +Comment[de]=Protokoll zur Verbindung mit ICQ +Comment[el]=Πρωτόκολλο για σύνδεση στο ICQ +Comment[es]=Protocolo de conexión de ICQ +Comment[et]=Protokoll ühendumiseks ICQ-ga +Comment[eu]=ICQ-ra konektatzeko protokoloa +Comment[fa]=قرارداد برای اتصال به ICQ +Comment[fi]=Yhteyskäytäntö ICQ-verkkoon kytkeytymiseen +Comment[fr]=Protocole pour se connecter sur ICQ +Comment[ga]=Prótacal chun ceangal le ICQ +Comment[gl]=Protocolo para se conectar á rede ICQ +Comment[he]=פרוטוקול התחברות ל- ICQ +Comment[hi]=आईसीक्यू से जुड़ने का प्रोटोकॉल +Comment[hr]=Protokol za povezivanje na ICQ +Comment[hu]=Protokoll az ICQ használatához +Comment[is]=Samskiptamáti til að tengjast ICQ +Comment[it]=Protocollo per connessione a ICQ +Comment[ja]=ICQ に接続するプロトコル +Comment[ka]=ICQ დაკავშირების ოქმი +Comment[kk]=ICQ-ге қосылу протоколы +Comment[km]=ពិធីការភ្ជាប់ទៅ ICQ +Comment[lt]=Protokolas prisijungimui prie ICQ +Comment[mk]=Протокол за поврзување на ICQ +Comment[nb]=Protokoll for å koble til ICQ +Comment[nds]=Protokoll för't Tokoppeln na ICQ +Comment[ne]=आईसीक्यू मा जडान गर्नुपर्ने प्रोटोकल +Comment[nl]=Protocol voor ICQ +Comment[nn]=Protokoll for å kopla til ICQ +Comment[pl]=Protokół połączenia z serwerem ICQ +Comment[pt]=Um protocolo para se ligar ao ICQ +Comment[pt_BR]=Protocolo de conexão ao ICQ +Comment[ro]=Protocol de conectare la ICQ +Comment[ru]=Протокол для подключения к ICQ +Comment[sk]=Protokol pre pripojenie k ICQ +Comment[sl]=Protokol za povezavo na ICQ +Comment[sr]=Протокол за повезивање на ICQ +Comment[sr@Latn]=Protokol za povezivanje na ICQ +Comment[sv]=Protokoll för att ansluta till ICQ +Comment[ta]=ICQ உடன் இணைக்க விதிமுறை +Comment[tg]=Қарордоди пайвастшавӣ ба ICQ +Comment[tr]=ICQ'a bağlantı iletişim kuralı +Comment[uk]=Протокол для з'єднання з ICQ +Comment[uz]=ICQ bilan aloqa oʻrnatish uchun protokol +Comment[uz@cyrillic]=ICQ билан алоқа ўрнатиш учун протокол +Comment[zh_CN]=连接到 ICQ 协议 +Comment[zh_HK]=用來連接至 ICQ 的通訊協定 +Comment[zh_TW]=連線到 ICQ 的協定 + diff --git a/kopete/protocols/oscar/icq/ui/Makefile.am b/kopete/protocols/oscar/icq/ui/Makefile.am new file mode 100644 index 00000000..24a726f2 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/Makefile.am @@ -0,0 +1,17 @@ +METASOURCES = AUTO +AM_CPPFLAGS = -I$(srcdir)/../ \ + -I$(srcdir)/../../ \ + -I$(srcdir)/ui/ \ + -I$(srcdir)/../../liboscar \ + -I$(srcdir)/../../../../ \ + $(KOPETE_INCLUDES) $(all_includes) + +noinst_LTLIBRARIES = libkopeteicqui.la + +libkopeteicqui_la_SOURCES = icqadd.ui icqeditaccountui.ui \ + icqeditaccountwidget.cpp icqgeneralinfo.ui icqotherinfowidget.ui icqworkinfowidget.ui icqinterestinfowidget.ui\ + icquserinfowidget.cpp icqauthreplyui.ui icqauthreplydialog.cpp icqaddcontactpage.cpp \ + icqsearchbase.ui icqsearchdialog.cpp icqsearchdialog.h + +libkopeteicqui_la_LIBADD = $(top_builddir)/kopete/libkopete/libkopete.la + diff --git a/kopete/protocols/oscar/icq/ui/icqadd.ui b/kopete/protocols/oscar/icq/ui/icqadd.ui new file mode 100644 index 00000000..ef793fbb --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqadd.ui @@ -0,0 +1,122 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>icqAddUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>icqAddUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>452</width> + <height>88</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>UIN #:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>uinEdit</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Alternatively, you can search the ICQ Whitepages :</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer8</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>47</width> + <height>26</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>searchButton</cstring> + </property> + <property name="text"> + <string>&Search</string> + </property> + <property name="iconSet"> + <iconset>image0</iconset> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<images> + <image name="image0"> + <data format="PNG" length="736">89504e470d0a1a0a0000000d49484452000000100000001008060000001ff3ff61000002a749444154388d7d91cd4b945114c69f73ef3b33ea7ca838a6a32681501194d2975050b4c82f92dc042e5a550b5bf60744bb16b58a8268218144d026da64da228a0a2b52d1c8c8c48f2c54669c19df79df793fefbd2d662469860e1cb870cef3e339cf2500989b5b88e56cb78b0857f2b6d3e67b0e0b0503baf4e57bdbb21eb8b6fadedf7fda4599a2e999f9bdb66b5fb75db79b3164b8c6b3504af8426852885adff3272dc31cb14c313e38d827fe0568593d77225811b8d8d810475555a89e88e0791e0c330f2515cc7c9e6ccb822f8d6f00964a009e6b5f8ed554211a0d235c5501ce1874c30411414a89582cdc0625c3e964e64c3900b35de768301000e70c1ae7608c81738e80a6a1b2b202b16814cd4d8946ced550b90c98e33a158c113ccf47ceccc3cc5b080534282591d94c637d6d1d5bd92c2ccb3af2f0d1e8bd92135cd7370184018088c0350ec639a291086291083ccf432e6740d3822c994cc54a1c5886f5d1755d48a920a584520a4a291000251508844c3a83baf82e1051e90996e5dc5959fe0d21fd4270424208015184e9ba8e0f139350d050460fd6de7ec80e5786313b3307c33021a484effb104222994c61ecc52b380ec1cbfcc281fcd33dd3379af7ec04d0f497c5ae8977afc77b7acf6262620a7a2e0d2505a0181a1a1388d735209f5a41647504bb833fdcad8de4e896c9864edd5edb00006d9bd49468c4c0406f318b420b2121a440eaf324226d3588b79c0f6a536303d6fc2a9e5d4d5c1bb8bfb6cc769829f7cd2010aaf77741f7dbb095d1517bb81b0dadf57dd1907bf3f1a5448b5656b52d2ea6c62b6bf076ad09355f17cc939d84face736185d10bd9d9541dfbbb5c1010018c1158f14d44205600ad878ebdf9f47cfceec6a6e5b0d6e39a1139d8a5b1e2707878e47f660a15aaddfcb9a4df4a3f79d921abf7f52cda1d737f0030624881b39160420000000049454e44ae426082</data> + </image> +</images> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/kopete/protocols/oscar/icq/ui/icqaddcontactpage.cpp b/kopete/protocols/oscar/icq/ui/icqaddcontactpage.cpp new file mode 100644 index 00000000..b1ab2eb4 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqaddcontactpage.cpp @@ -0,0 +1,126 @@ + /* + icqaddcontactpage.cpp - ICQ Protocol Plugin + + Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net> + Kopete (c) 2002-2003 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 "icqaddcontactpage.h" + +#include <ctype.h> + +#include <qlayout.h> +#include <qpushbutton.h> +#include <qcombobox.h> +#include <qcheckbox.h> +#include <qlineedit.h> +#include <qtabwidget.h> +#include <qlabel.h> + +#include <kdebug.h> +#include <kiconloader.h> +#include <klistview.h> +#include <klocale.h> +#include <kpushbutton.h> +#include <kmessagebox.h> + +#include "icqadd.h" +#include "icqaccount.h" +#include "icqprotocol.h" +#include "icqsearchdialog.h" + + +ICQAddContactPage::ICQAddContactPage(ICQAccount *owner, QWidget *parent, const char *name) + : AddContactPage(parent,name) +{ + kdDebug(14153) << k_funcinfo << "called" << endl; + mAccount = owner; + m_searchDialog = 0L; + + (new QVBoxLayout(this))->setAutoAdd(true); + addUI = new icqAddUI(this); + connect( addUI->searchButton, SIGNAL( clicked() ), this, SLOT( showSearchDialog() ) ); +} + +ICQAddContactPage::~ICQAddContactPage() +{ +} + +void ICQAddContactPage::setUINFromSearch( const QString& uin ) +{ + addUI->uinEdit->setText( uin ); +} + +void ICQAddContactPage::showEvent(QShowEvent *e) +{ +// slotSelectionChanged(); + AddContactPage::showEvent(e); +} + +bool ICQAddContactPage::apply(Kopete::Account* , Kopete::MetaContact *parentContact ) +{ + kdDebug(14153) << k_funcinfo << "called; adding contact..." << endl; + + QString contactId = addUI->uinEdit->text(); + kdDebug(14153) << k_funcinfo << "uin=" << contactId << endl; + return mAccount->addContact(contactId, parentContact, Kopete::Account::ChangeKABC ); + +} + +bool ICQAddContactPage::validateData() +{ + if(!mAccount->isConnected()) + { + // Account currently offline + addUI->searchButton->setEnabled( false ); + addUI->uinEdit->setEnabled( false ); + KMessageBox::sorry( this, i18n("You must be online to add a contact."), i18n("ICQ Plugin") ); + return false; + } + + Q_ULONG uin = addUI->uinEdit->text().toULong(); + if ( uin < 1000 ) + { + // Invalid (or missing) UIN + KMessageBox::sorry( this, i18n("You must enter a valid UIN."), i18n("ICQ Plugin") ); + return false; + } + else + { + // UIN is valid + return true; + } +} + +void ICQAddContactPage::showSearchDialog() +{ + if ( m_searchDialog ) + m_searchDialog->raise(); + else + { + m_searchDialog = new ICQSearchDialog( mAccount, this, "icqSearchDialog" ); + m_searchDialog->show(); + connect( m_searchDialog, SIGNAL( finished() ), this, SLOT( searchDialogDestroyed() ) ); + } +} + +void ICQAddContactPage::searchDialogDestroyed() +{ + QObject::disconnect( this, 0, m_searchDialog, 0 ); + m_searchDialog->delayedDestruct(); + m_searchDialog = NULL; +} + + +#include "icqaddcontactpage.moc" +// vim: set noet ts=4 sts=4 sw=4: +// kate: indent-mode csands; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/icq/ui/icqaddcontactpage.h b/kopete/protocols/oscar/icq/ui/icqaddcontactpage.h new file mode 100644 index 00000000..e9285b79 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqaddcontactpage.h @@ -0,0 +1,60 @@ + /* + icqaddcontactpage.h - ICQ Protocol Plugin + + Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net> + Copyright (c) 2004-2005 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2005 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. * + * * + ************************************************************************* +*/ + +#ifndef ICQADDCONTACTPAGE_H +#define ICQADDCONTACTPAGE_H + +#include <qwidget.h> +#include <addcontactpage.h> + +/** + *@author Matt Rogers + *@author Stefan Gehn + */ +class icqAddUI; +class ICQAccount; +class ICQSearchDialog; + +class ICQAddContactPage : public AddContactPage +{ +Q_OBJECT + +public: + ICQAddContactPage(ICQAccount *owner, QWidget *parent = 0, const char *name = 0); + ~ICQAddContactPage(); + + virtual bool validateData(); + virtual bool apply(Kopete::Account* , Kopete::MetaContact *parentContact); + + void setUINFromSearch( const QString& ); + +protected: + void showEvent(QShowEvent *e); + +private slots: + void showSearchDialog(); + void searchDialogDestroyed(); +private: + + ICQAccount *mAccount; + icqAddUI *addUI; + ICQSearchDialog* m_searchDialog; +}; + +#endif + +//kate: space-indent off; replace-tabs off; indent-mode csands; diff --git a/kopete/protocols/oscar/icq/ui/icqauthreplydialog.cpp b/kopete/protocols/oscar/icq/ui/icqauthreplydialog.cpp new file mode 100644 index 00000000..76b56fba --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqauthreplydialog.cpp @@ -0,0 +1,73 @@ +/* + Kopete Oscar Protocol + icqauthreplydialog.cpp - ICQ authorization reply dialog + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + 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 "icqauthreplydialog.h" +#include "icqauthreplyui.h" + +#include <klocale.h> + +#include <qlabel.h> +#include <qradiobutton.h> +#include <qlineedit.h> + +ICQAuthReplyDialog::ICQAuthReplyDialog( QWidget *parent, const char *name, bool wasRequested ) + : KDialogBase( parent, name, true, i18n( "Authorization Reply" ), KDialogBase::Ok | KDialogBase::Cancel ) +{ + m_ui = new ICQAuthReplyUI( this ); + setMainWidget( m_ui ); + m_wasRequested = wasRequested; + + if ( !m_wasRequested ) + { + m_ui->lblReqReason->hide(); + m_ui->lblRequestReason->hide(); + } + else + { + this->setWFlags( this->getWFlags() | Qt::WDestructiveClose ); + } +} + +ICQAuthReplyDialog::~ICQAuthReplyDialog() +{ +} + +void ICQAuthReplyDialog::setUser( const QString & user ) +{ + if ( m_wasRequested ) + m_ui->lblUserReq->setText( + i18n( "<b>%1</b> requested authorization to add you to his/her contact list." ).arg( user ) ); + else + m_ui->lblUserReq->setText( i18n( "Authorization reply to <b>%1</b>." ).arg( user ) ); +} + +void ICQAuthReplyDialog::setRequestReason( const QString & reason ) +{ + m_ui->lblRequestReason->setText( reason ); +} + +QString ICQAuthReplyDialog::reason() +{ + return m_ui->leReason->text(); +} + +bool ICQAuthReplyDialog::grantAuth() +{ + return m_ui->rbGrant->isChecked(); +} + +#include "icqauthreplydialog.moc" diff --git a/kopete/protocols/oscar/icq/ui/icqauthreplydialog.h b/kopete/protocols/oscar/icq/ui/icqauthreplydialog.h new file mode 100644 index 00000000..da27b241 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqauthreplydialog.h @@ -0,0 +1,45 @@ +/* + Kopete Oscar Protocol + icqauthreplydialog.h - ICQ authorization reply dialog + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + 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. * + * * + ************************************************************************* +*/ +#ifndef ICQAUTHREPLYDIALOG_H +#define ICQAUTHREPLYDIALOG_H + +#include <kdialogbase.h> + +class ICQAuthReplyUI; + +/** + * A dialog to ask user what to do when a contact requests authorization + * @author Gustavo Pichorim Boiko + */ +class ICQAuthReplyDialog : public KDialogBase +{ +Q_OBJECT +public: + ICQAuthReplyDialog(QWidget *parent = 0, const char *name = 0, bool wasRequested = true); + ~ICQAuthReplyDialog(); + + void setUser( const QString& user ); + void setRequestReason( const QString& reason ); + QString reason(); + bool grantAuth(); +private: + bool m_wasRequested; + ICQAuthReplyUI *m_ui; +}; + +#endif diff --git a/kopete/protocols/oscar/icq/ui/icqauthreplyui.ui b/kopete/protocols/oscar/icq/ui/icqauthreplyui.ui new file mode 100644 index 00000000..12607856 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqauthreplyui.ui @@ -0,0 +1,196 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ICQAuthReplyUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ICQAuthReplyUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>412</width> + <height>148</height> + </rect> + </property> + <property name="caption"> + <string>ICQ Authorization Reply</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="3" column="0"> + <property name="name"> + <cstring>layout22</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>lblReason</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Reason:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>leReason</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget" row="2" column="0"> + <property name="name"> + <cstring>layout23</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer14</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>50</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>bgAction</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + <property name="title"> + <string></string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>rbGrant</cstring> + </property> + <property name="text"> + <string>&Grant authorization</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton" row="0" column="1"> + <property name="name"> + <cstring>rbDecline</cstring> + </property> + <property name="text"> + <string>&Decline authorization</string> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer12</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>220</width> + <height>21</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>lblUserReq</cstring> + </property> + <property name="text"> + <string>%1 requested authorization to add you to his/her contact list.</string> + </property> + </widget> + <widget class="QLayoutWidget" row="1" column="0"> + <property name="name"> + <cstring>layout24</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>lblReqReason</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Request Reason:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>lblRequestReason</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Some reason...</string> + </property> + </widget> + </hbox> + </widget> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/oscar/icq/ui/icqeditaccountui.ui b/kopete/protocols/oscar/icq/ui/icqeditaccountui.ui new file mode 100644 index 00000000..3ecc91cb --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqeditaccountui.ui @@ -0,0 +1,486 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ICQEditAccountUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ICQEditAccountUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>592</width> + <height>404</height> + </rect> + </property> + <property name="caption"> + <string>Account Preferences - ICQ</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>tabWidget7</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>&Basic Setup</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>Account Preferences</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>lblAccountId</cstring> + </property> + <property name="text"> + <string>IC&Q UIN:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>edtAccountId</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The user ID of your ICQ account.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The user ID of your ICQ account. This should be in the form of a number (no decimals, no spaces).</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>edtAccountId</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The user ID of your ICQ account.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The user ID of your ICQ account. This should be in the form of a number (no decimals, no spaces).</string> + </property> + </widget> + </hbox> + </widget> + <widget class="Kopete::UI::PasswordWidget"> + <property name="name"> + <cstring>mPasswordWidget</cstring> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>chkAutoLogin</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>E&xclude from connect all</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If you check that case, the account will not be connected when you press the "Connect All" button, or at startup even if you selected to automatically connect at startup</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>chkGlobalIdentity</cstring> + </property> + <property name="text"> + <string>Exclu&de from Global Identity</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox5</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Registration</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>To connect to the ICQ network, you will need an ICQ account.<br><br> +If you do not currently have an ICQ account, please click the button to create one.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonRegister</cstring> + </property> + <property name="text"> + <string>Re&gister New Account</string> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacerBasicSetup</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Accou&nt Preferences</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Connection Preferences</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSpinBox" row="1" column="3"> + <property name="name"> + <cstring>edtServerPort</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maxValue"> + <number>65534</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="value"> + <number>5190</number> + </property> + <property name="toolTip" stdset="0"> + <string>The port on the ICQ server that you would like to connect to.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The port on the ICQ server that you would like to connect to. Normally this is 5190.</string> + </property> + </widget> + <widget class="QLineEdit" row="1" column="2"> + <property name="name"> + <cstring>edtServerAddress</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>login.icq.com</string> + </property> + <property name="toolTip" stdset="0"> + <string>The IP address or hostmask of the ICQ server you wish to connect to.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The IP address or hostmask of the ICQ server you wish to connect to. Normally you will want the default (login.icq.com).</string> + </property> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>lblServerPort</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Po&rt:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>edtServerPort</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The port on the ICQ server that you would like to connect to.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The port on the ICQ server that you would like to connect to. Normally this is 5190.</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>lblServer</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Ser&ver /</string> + </property> + <property name="buddy" stdset="0"> + <cstring>edtServerAddress</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The IP address or hostmask of the ICQ server you wish to connect to.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The IP address or hostmask of the ICQ server you wish to connect to. Normally you will want the default (login.icq.com).</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>optionOverrideServer</cstring> + </property> + <property name="text"> + <string>&Override default server information</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>groupBox65</cstring> + </property> + <property name="title"> + <string>Privacy Options</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>chkRequireAuth</cstring> + </property> + <property name="text"> + <string>&Require authorization before someone can add you to their contact list</string> + </property> + <property name="toolTip" stdset="0"> + <string>Enable authorization requirement, which will not allow users to add you to their contact list without authorization from you.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Enable authorization requirement, which will not allow users to add you to their contact list without authorization from you. Check this box, and you will have to confirm any users who add you to their list before they may see your online status.</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>chkHideIP</cstring> + </property> + <property name="text"> + <string>Hide &IP address</string> + </property> + <property name="toolTip" stdset="0"> + <string>Check this to hide your IP address from people when they view your user info</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Checking this box will not allow people to see what your IP address if they view your ICQ user details such as name, address, or age.</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>chkWebAware</cstring> + </property> + <property name="text"> + <string>Make my status available via &ICQ's unified messaging center</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Check this box to enable Web Aware functionality.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this box to enable ICQ's Web Aware functionality, which allows people to see your online status from ICQ's web page, and send you a message without necessarily having ICQ themselves.</string> + </property> + </widget> + </grid> + </widget> + <spacer row="3" column="0"> + <property name="name"> + <cstring>spacerPreferences</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + <widget class="QComboBox" row="2" column="1"> + <property name="name"> + <cstring>encodingCombo</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Default to the following &encoding for messages:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>encodingCombo</cstring> + </property> + </widget> + </grid> + </widget> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>labelStatusMessage</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>Kopete::UI::PasswordWidget</class> + <header location="local">kopetepasswordwidget.h</header> + <sizehint> + <width>50</width> + <height>50</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>1</hordata> + <verdata>0</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + <signal>changed()</signal> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154388dad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a190172af6a9690000000049454e44ae426082</data> + </image> +</images> +<connections> + <connection> + <sender>optionOverrideServer</sender> + <signal>toggled(bool)</signal> + <receiver>lblServer</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideServer</sender> + <signal>toggled(bool)</signal> + <receiver>lblServerPort</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideServer</sender> + <signal>toggled(bool)</signal> + <receiver>edtServerAddress</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideServer</sender> + <signal>toggled(bool)</signal> + <receiver>edtServerPort</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>tabWidget7</tabstop> + <tabstop>edtAccountId</tabstop> + <tabstop>chkAutoLogin</tabstop> + <tabstop>buttonRegister</tabstop> + <tabstop>optionOverrideServer</tabstop> + <tabstop>edtServerAddress</tabstop> + <tabstop>edtServerPort</tabstop> + <tabstop>chkRequireAuth</tabstop> + <tabstop>chkHideIP</tabstop> + <tabstop>chkWebAware</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kopetepasswordwidget.h</includehint> +</includehints> +</UI> diff --git a/kopete/protocols/oscar/icq/ui/icqeditaccountwidget.cpp b/kopete/protocols/oscar/icq/ui/icqeditaccountwidget.cpp new file mode 100644 index 00000000..e4b308be --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqeditaccountwidget.cpp @@ -0,0 +1,190 @@ +/* + icqeditaccountwidget.cpp - ICQ Account Widget + + Copyright (c) 2003 by Chris TenHarmsel <tenharmsel@staticmethod.net> + Kopete (c) 2003 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 "icqeditaccountwidget.h" +#include "icqeditaccountui.h" + +#include <qlayout.h> +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qlineedit.h> +#include <qtextedit.h> +#include <qspinbox.h> +#include <qpushbutton.h> + +#include <kconfig.h> +#include <kdebug.h> +#include <kdialog.h> +#include <klocale.h> +#include <kiconloader.h> +#include <kjanuswidget.h> +#include <kurllabel.h> +#include <kdatewidget.h> +#include <krun.h> +#include <kpassdlg.h> + +#include "kopetepassword.h" +#include "kopetepasswordwidget.h" + +#include "icqprotocol.h" +#include "icqaccount.h" +#include "icqcontact.h" + +ICQEditAccountWidget::ICQEditAccountWidget(ICQProtocol *protocol, + Kopete::Account *account, QWidget *parent, const char *name) + : QWidget(parent, name), KopeteEditAccountWidget(account) +{ + kdDebug(14153) << k_funcinfo << "Called." << endl; + + mAccount=dynamic_cast<ICQAccount*>(account); + mProtocol=protocol; + + (new QVBoxLayout(this))->setAutoAdd(true); + mAccountSettings = new ICQEditAccountUI( this ); + + mProtocol->fillComboFromTable( mAccountSettings->encodingCombo, mProtocol->encodings() ); + + // Read in the settings from the account if it exists + if(mAccount) + { + mAccountSettings->edtAccountId->setText(mAccount->accountId()); + + // TODO: Remove me after we can change Account IDs (Matt) + mAccountSettings->edtAccountId->setDisabled(true); + mAccountSettings->mPasswordWidget->load(&mAccount->password()); + mAccountSettings->chkAutoLogin->setChecked(mAccount->excludeConnect()); + + QString serverEntry = mAccount->configGroup()->readEntry("Server", "login.oscar.aol.com"); + int portEntry = mAccount->configGroup()->readNumEntry("Port", 5190); + if ( serverEntry != "login.oscar.aol.com" || ( portEntry != 5190) ) + mAccountSettings->optionOverrideServer->setChecked( true ); + + mAccountSettings->edtServerAddress->setText( serverEntry ); + mAccountSettings->edtServerPort->setValue( portEntry ); + + bool configValue = mAccount->configGroup()->readBoolEntry( "RequireAuth", false ); + mAccountSettings->chkRequireAuth->setChecked( configValue ); + + configValue = mAccount->configGroup()->readBoolEntry( "HideIP", true ); + mAccountSettings->chkHideIP->setChecked( configValue ); + + configValue = mAccount->configGroup()->readBoolEntry( "WebAware", false ); + mAccountSettings->chkWebAware->setChecked( configValue ); + + int encodingValue = mAccount->configGroup()->readNumEntry( "DefaultEncoding", 4 ); + mProtocol->setComboFromTable( mAccountSettings->encodingCombo, + mProtocol->encodings(), + encodingValue ); + + // Global Identity + mAccountSettings->chkGlobalIdentity->setChecked( mAccount->configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) ); + } + else + { + mProtocol->setComboFromTable( mAccountSettings->encodingCombo, + mProtocol->encodings(), + 4 ); + } + + QObject::connect(mAccountSettings->buttonRegister, SIGNAL(clicked()), this, SLOT(slotOpenRegister())); + + /* Set tab order to password custom widget correctly */ + QWidget::setTabOrder( mAccountSettings->edtAccountId, mAccountSettings->mPasswordWidget->mRemembered ); + QWidget::setTabOrder( mAccountSettings->mPasswordWidget->mRemembered, mAccountSettings->mPasswordWidget->mPassword ); + QWidget::setTabOrder( mAccountSettings->mPasswordWidget->mPassword, mAccountSettings->chkAutoLogin ); + +} + +Kopete::Account *ICQEditAccountWidget::apply() +{ + kdDebug(14153) << k_funcinfo << "Called." << endl; + + // If this is a new account, create it + if (!mAccount) + { + kdDebug(14153) << k_funcinfo << "Creating a new account" << endl; + mAccount = new ICQAccount(mProtocol, mAccountSettings->edtAccountId->text()); + if(!mAccount) + return NULL; + } + + mAccountSettings->mPasswordWidget->save(&mAccount->password()); + mAccount->setExcludeConnect(mAccountSettings->chkAutoLogin->isChecked()); + + bool configValue = mAccountSettings->chkRequireAuth->isChecked(); + mAccount->configGroup()->writeEntry( "RequireAuth", configValue ); + + configValue = mAccountSettings->chkHideIP->isChecked(); + mAccount->configGroup()->writeEntry( "HideIP", configValue ); + + configValue = mAccountSettings->chkWebAware->isChecked(); + mAccount->configGroup()->writeEntry( "WebAware", configValue ); + + int encodingMib = mProtocol->getCodeForCombo( mAccountSettings->encodingCombo, + mProtocol->encodings() ); + mAccount->configGroup()->writeEntry( "DefaultEncoding", encodingMib ); + + if ( mAccountSettings->optionOverrideServer->isChecked() ) + { + mAccount->setServerAddress(mAccountSettings->edtServerAddress->text()); + mAccount->setServerPort(mAccountSettings->edtServerPort->value()); + } + else + { + mAccount->setServerAddress("login.oscar.aol.com"); + mAccount->setServerPort(5190); + } + + // Global Identity + mAccount->configGroup()->writeEntry( "ExcludeGlobalIdentity", mAccountSettings->chkGlobalIdentity->isChecked() ); + + return mAccount; +} + +bool ICQEditAccountWidget::validateData() +{ + kdDebug(14153) << k_funcinfo << "Called." << endl; + + QString userName = mAccountSettings->edtAccountId->text(); + + if (userName.isEmpty()) + return false; + + for (unsigned int i=0; i<userName.length(); i++) + { + if(!(userName[i]).isNumber()) + return false; + } + + // No need to check port, min and max values are properly defined in .ui + + if (mAccountSettings->edtServerAddress->text().isEmpty()) + return false; + + // Seems good to me + kdDebug(14153) << k_funcinfo << + "Account data validated successfully." << endl; + return true; +} + +void ICQEditAccountWidget::slotOpenRegister() +{ + KRun::runURL( "http://go.icq.com/register/", "text/html" ); +} + +#include "icqeditaccountwidget.moc" +// vim: set noet ts=4 sts=4 sw=4: +// kate: indent-mode csands; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/icq/ui/icqeditaccountwidget.h b/kopete/protocols/oscar/icq/ui/icqeditaccountwidget.h new file mode 100644 index 00000000..fc5c6d38 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqeditaccountwidget.h @@ -0,0 +1,52 @@ +/* + icqeditaccountwidget.h - ICQ Account Widget + + Copyright (c) 2003 by Chris TenHarmsel <tenharmsel@staticmethod.net> + Copyright (c) 2004-2005 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2005 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. * + * * + ************************************************************************* +*/ + +#ifndef ICQEDITACCOUNTWIDGET_H +#define ICQEDITACCOUNTWIDGET_H + +#include <qwidget.h> +#include <qdatetime.h> +#include "editaccountwidget.h" + +namespace Kopete { class Account; } + +class ICQAccount; +class ICQProtocol; +class ICQEditAccountUI; + +class ICQEditAccountWidget : public QWidget, public KopeteEditAccountWidget +{ +Q_OBJECT + +public: + ICQEditAccountWidget(ICQProtocol *, Kopete::Account *, + QWidget *parent=0, const char *name=0); + + virtual bool validateData(); + virtual Kopete::Account *apply(); + +private slots: + void slotOpenRegister(); + +protected: + ICQAccount *mAccount; + ICQProtocol *mProtocol; + ICQEditAccountUI *mAccountSettings; +}; +#endif +// vim: set noet ts=4 sts=4 sw=4: +// kate: indent-mode csands; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/icq/ui/icqgeneralinfo.ui b/kopete/protocols/oscar/icq/ui/icqgeneralinfo.ui new file mode 100644 index 00000000..6383bec1 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqgeneralinfo.ui @@ -0,0 +1,611 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ICQGeneralInfoWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ICQGeneralInfoWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>488</width> + <height>572</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox" row="1" column="0"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Location && Contact Information</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>&City:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>cityEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>&Address:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>addressEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>&Phone:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>phoneEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="2"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>&State:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>stateEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>cityEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="2" column="2"> + <property name="name"> + <cstring>textLabel8</cstring> + </property> + <property name="text"> + <string>Countr&y:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>countryEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="6" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>homepageEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel9</cstring> + </property> + <property name="text"> + <string>&Email:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>emailEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="5" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>emailEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="6" column="0"> + <property name="name"> + <cstring>textLabel10_2</cstring> + </property> + <property name="text"> + <string>&Homepage:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>homepageEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="3" column="3"> + <property name="name"> + <cstring>cellEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="2" column="3"> + <property name="name"> + <cstring>countryEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="1" column="3"> + <property name="name"> + <cstring>stateEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel7_2</cstring> + </property> + <property name="text"> + <string>Fa&x:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>faxEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="4" column="1"> + <property name="name"> + <cstring>faxEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="3" column="1"> + <property name="name"> + <cstring>phoneEdit</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="3" column="2"> + <property name="name"> + <cstring>textLabel6_2</cstring> + </property> + <property name="text"> + <string>Ce&ll:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>cellEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>addressEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>&Zip:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>zipEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>zipEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="0" column="0"> + <property name="name"> + <cstring>groupBox4</cstring> + </property> + <property name="title"> + <string>Personal Information</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit" row="0" column="3"> + <property name="name"> + <cstring>uinEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>fullNameLabel</cstring> + </property> + <property name="text"> + <string>&Full name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>fullNameEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="1" column="3"> + <property name="name"> + <cstring>ipEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>125</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="2" column="3"> + <property name="name"> + <cstring>timezoneEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>nickNameEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>2</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>nickNameLabel</cstring> + </property> + <property name="text"> + <string>&Nickname:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>nickNameEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>uinLabel</cstring> + </property> + <property name="text"> + <string>&UIN #:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>uinEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>birthdayLabel</cstring> + </property> + <property name="text"> + <string>&Birthday:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>birthday</cstring> + </property> + </widget> + <widget class="QSpinBox" row="3" column="3"> + <property name="name"> + <cstring>ageSpinBox</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>Gen&der:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>genderEdit</cstring> + </property> + </widget> + <widget class="QLineEdit" row="3" column="1"> + <property name="name"> + <cstring>birthday</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>fullNameEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>genderEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="1" column="2"> + <property name="name"> + <cstring>ipLabel</cstring> + </property> + <property name="text"> + <string>&IP:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>ipEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="2"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>&Timezone:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>timezoneEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>maritalLabel</cstring> + </property> + <property name="text"> + <string>Marital status:</string> + </property> + </widget> + <widget class="QLabel" row="3" column="2"> + <property name="name"> + <cstring>textLabel10</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>A&ge:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>ageSpinBox</cstring> + </property> + </widget> + <widget class="QLineEdit" row="4" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>marital</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="2" column="0"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>Origin</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>oStateEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>oCountryEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>oCityEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="text"> + <string>City:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="text"> + <string>Country:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>State:</string> + </property> + </widget> + </grid> + </widget> + <spacer row="3" column="0"> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> +</widget> +<tabstops> + <tabstop>nickNameEdit</tabstop> + <tabstop>fullNameEdit</tabstop> + <tabstop>genderEdit</tabstop> + <tabstop>uinEdit</tabstop> + <tabstop>ipEdit</tabstop> + <tabstop>timezoneEdit</tabstop> + <tabstop>ageSpinBox</tabstop> + <tabstop>addressEdit</tabstop> + <tabstop>cityEdit</tabstop> + <tabstop>stateEdit</tabstop> + <tabstop>zipEdit</tabstop> + <tabstop>countryEdit</tabstop> + <tabstop>phoneEdit</tabstop> + <tabstop>faxEdit</tabstop> + <tabstop>cellEdit</tabstop> + <tabstop>emailEdit</tabstop> + <tabstop>homepageEdit</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/oscar/icq/ui/icqinterestinfowidget.ui b/kopete/protocols/oscar/icq/ui/icqinterestinfowidget.ui new file mode 100644 index 00000000..ce4041c9 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqinterestinfowidget.ui @@ -0,0 +1,116 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ICQInterestInfoWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ICQInterestInfoWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>660</width> + <height>572</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup" row="0" column="0"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="title"> + <string>Interests</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>desc1</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>desc2</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>desc3</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="1" column="0"> + <property name="name"> + <cstring>topic2</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="0" column="0"> + <property name="name"> + <cstring>topic1</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="2" column="0"> + <property name="name"> + <cstring>topic3</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="3" column="0"> + <property name="name"> + <cstring>topic4</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="3" column="1"> + <property name="name"> + <cstring>desc4</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>220</height> + </size> + </property> + </spacer> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/oscar/icq/ui/icqotherinfowidget.ui b/kopete/protocols/oscar/icq/ui/icqotherinfowidget.ui new file mode 100644 index 00000000..4e5a3a34 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqotherinfowidget.ui @@ -0,0 +1,68 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ICQOtherInfoWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ICQOtherInfoWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>289</width> + <height>473</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="4" column="1"> + <property name="name"> + <cstring>spacer8</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>30</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel12</cstring> + </property> + <property name="text"> + <string>Email addresses:</string> + </property> + </widget> + <widget class="QListBox" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>emailListBox</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel13</cstring> + </property> + <property name="text"> + <string>Contact notes:</string> + </property> + </widget> + <widget class="QTextEdit" row="3" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>notesEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/oscar/icq/ui/icqsearchbase.ui b/kopete/protocols/oscar/icq/ui/icqsearchbase.ui new file mode 100644 index 00000000..68e59281 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqsearchbase.ui @@ -0,0 +1,493 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ICQSearchBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ICQSearchBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>603</width> + <height>465</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton" row="4" column="1"> + <property name="name"> + <cstring>clearButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>C&lear</string> + </property> + <property name="stdItem" stdset="0"> + <number>10</number> + </property> + <property name="whatsThis" stdset="0"> + <string>Clear the results</string> + </property> + </widget> + <widget class="KPushButton" row="7" column="1"> + <property name="name"> + <cstring>closeButton</cstring> + </property> + <property name="text"> + <string>&Close</string> + </property> + <property name="stdItem" stdset="0"> + <number>13</number> + </property> + <property name="whatsThis" stdset="0"> + <string>Close this dialog</string> + </property> + </widget> + <widget class="KPushButton" row="3" column="1"> + <property name="name"> + <cstring>stopButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Stop</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="stdItem" stdset="0"> + <number>26</number> + </property> + <property name="whatsThis" stdset="0"> + <string>Stops the search</string> + </property> + </widget> + <widget class="KPushButton" row="5" column="1"> + <property name="name"> + <cstring>addButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Add</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="stdItem" stdset="0"> + <number>27</number> + </property> + <property name="whatsThis" stdset="0"> + <string>Add the selected user to your contact list</string> + </property> + </widget> + <widget class="KPushButton" row="6" column="1"> + <property name="name"> + <cstring>userInfoButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>User Info</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>Show information about the selected contact</string> + </property> + </widget> + <spacer row="0" column="1"> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>41</width> + <height>190</height> + </size> + </property> + </spacer> + <widget class="QTabWidget" row="0" column="0"> + <property name="name"> + <cstring>tabWidget3</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>UIN Search</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>&UIN #:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>uin</cstring> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>uin</cstring> + </property> + </widget> + <spacer row="1" column="1"> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>105</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>ICQ Whitepages Search</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit" row="1" column="3"> + <property name="name"> + <cstring>lastName</cstring> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>nickName</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="2"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>&Last name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>lastName</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>&First name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>firstName</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>&Email:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>email</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>&Nickname:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>nickName</cstring> + </property> + </widget> + <widget class="QComboBox" row="3" column="3"> + <property name="name"> + <cstring>country</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel9</cstring> + </property> + <property name="text"> + <string>Lan&guage:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>language</cstring> + </property> + </widget> + <widget class="QComboBox" row="3" column="1"> + <property name="name"> + <cstring>language</cstring> + </property> + </widget> + <widget class="QLineEdit" row="2" column="3"> + <property name="name"> + <cstring>city</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="2"> + <property name="name"> + <cstring>textLabel10</cstring> + </property> + <property name="text"> + <string>&City:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>city</cstring> + </property> + </widget> + <widget class="QLineEdit" row="0" column="3"> + <property name="name"> + <cstring>firstName</cstring> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>email</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel8</cstring> + </property> + <property name="text"> + <string>&Gender:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>gender</cstring> + </property> + </widget> + <widget class="QComboBox" row="2" column="1"> + <property name="name"> + <cstring>gender</cstring> + </property> + </widget> + <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>onlyOnline</cstring> + </property> + <property name="text"> + <string>Only search for online contacts</string> + </property> + </widget> + <widget class="QLabel" row="3" column="2"> + <property name="name"> + <cstring>textLabel11</cstring> + </property> + <property name="text"> + <string>C&ountry:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>country</cstring> + </property> + </widget> + <spacer row="5" column="1"> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <spacer row="4" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>166</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + </widget> + <widget class="KListView" row="1" column="0" rowspan="7" colspan="1"> + <column> + <property name="text"> + <string>UIN</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Nickname</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>First Name</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Last Name</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Email</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Requires Authorization?</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>searchResults</cstring> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>This is where the results from your search are displayed. If you double-click a result, the search window will close and pass the UIN of the contact you wish to add back to the Add Contact Wizard. You can only add one contact at a time.</string> + </property> + </widget> + <widget class="KPushButton" row="1" column="1"> + <property name="name"> + <cstring>searchButton</cstring> + </property> + <property name="text"> + <string>&Search</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Search the ICQ Whitepages with your search criteria</string> + </property> + </widget> + <widget class="KPushButton" row="2" column="1"> + <property name="name"> + <cstring>newSearchButton</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>New Search</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Clears both search fields and results</string> + </property> + </widget> + </grid> +</widget> +<tabstops> + <tabstop>tabWidget3</tabstop> + <tabstop>uin</tabstop> + <tabstop>nickName</tabstop> + <tabstop>firstName</tabstop> + <tabstop>email</tabstop> + <tabstop>lastName</tabstop> + <tabstop>gender</tabstop> + <tabstop>city</tabstop> + <tabstop>language</tabstop> + <tabstop>country</tabstop> + <tabstop>onlyOnline</tabstop> + <tabstop>searchButton</tabstop> + <tabstop>stopButton</tabstop> + <tabstop>clearButton</tabstop> + <tabstop>addButton</tabstop> + <tabstop>userInfoButton</tabstop> + <tabstop>closeButton</tabstop> + <tabstop>searchResults</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>klistview.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/kopete/protocols/oscar/icq/ui/icqsearchdialog.cpp b/kopete/protocols/oscar/icq/ui/icqsearchdialog.cpp new file mode 100644 index 00000000..0010166a --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqsearchdialog.cpp @@ -0,0 +1,320 @@ +/* + Kopete Oscar Protocol + icqsearchdialog.cpp - search for people + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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 "icqsearchdialog.h" + +#include <qlineedit.h> +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qlayout.h> +#include <qtextcodec.h> +#include <qtabwidget.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <klistview.h> +#include <klocale.h> +#include <kpushbutton.h> +#include <kmessagebox.h> + +#include "kopeteuiglobal.h" + +#include "icqaccount.h" +#include "icqaddcontactpage.h" +#include "icqprotocol.h" +#include "icqsearchbase.h" +#include "oscartypes.h" +#include "icqcontact.h" +#include "icquserinfowidget.h" + +ICQSearchDialog::ICQSearchDialog( ICQAccount* account, QWidget* parent, const char* name ) +: KDialogBase( parent, name, true, i18n( "ICQ User Search" ), 0, NoDefault ) +{ + m_account = account; + m_searchUI = new ICQSearchBase( this, name ); + setMainWidget( m_searchUI ); + connect( m_searchUI->searchButton, SIGNAL( clicked() ), this, SLOT( startSearch() ) ); + connect( m_searchUI->searchResults, SIGNAL( selectionChanged() ), this, SLOT( resultSelectionChanged() ) ); + connect( m_searchUI->addButton, SIGNAL( clicked() ), this, SLOT( addContact() ) ); + connect( m_searchUI->clearButton, SIGNAL( clicked() ), this, SLOT( clearResults() ) ); + connect( m_searchUI->stopButton, SIGNAL( clicked() ), this, SLOT( stopSearch() ) ); + connect( m_searchUI->closeButton, SIGNAL( clicked() ), this, SLOT( closeDialog() ) ); + connect( m_searchUI->userInfoButton, SIGNAL( clicked() ), this, SLOT( userInfo() ) ); + connect( m_searchUI->newSearchButton, SIGNAL( clicked() ), this, SLOT( newSearch() ) ); + + ICQProtocol *p = ICQProtocol::protocol(); + p->fillComboFromTable( m_searchUI->gender, p->genders() ); + p->fillComboFromTable( m_searchUI->country, p->countries() ); + p->fillComboFromTable( m_searchUI->language, p->languages() ); + + m_contact = NULL; + m_infoWidget = NULL; + + m_contact = NULL; + m_infoWidget = NULL; +} + + +ICQSearchDialog::~ICQSearchDialog() +{ +} + +void ICQSearchDialog::startSearch() +{ + // Doing the search only if the account is online, otherwise warn the user + if(!m_account->isConnected()) + { + // Account currently offline + m_searchUI->searchButton->setEnabled( false ); + KMessageBox::sorry( this, i18n("You must be online to search the ICQ Whitepages."), i18n("ICQ Plugin") ); + } + else + { + // Account is online + clearResults(); + + m_searchUI->stopButton->setEnabled( true ); + m_searchUI->searchButton->setEnabled( false ); + m_searchUI->newSearchButton->setEnabled( false ); + + connect( m_account->engine(), SIGNAL( gotSearchResults( const ICQSearchResult& ) ), + this, SLOT( newResult( const ICQSearchResult& ) ) ); + connect( m_account->engine(), SIGNAL( endOfSearch( int ) ), + this, SLOT( searchFinished( int ) ) ); + + const QWidget* currentPage = m_searchUI->tabWidget3->currentPage(); + + if ( currentPage == m_searchUI->tab ) + { + if( m_searchUI->uin->text().isEmpty() || m_searchUI->uin->text().toULong() == 0 ) + { + // Invalid UIN + stopSearch(); + clearResults(); + KMessageBox::sorry( this, i18n("You must enter a valid UIN."), i18n("ICQ Plugin") ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Search aborted: invalid UIN " << m_searchUI->uin->text() << endl; + } + else + { + //doing a uin search + m_account->engine()->uinSearch( m_searchUI->uin->text() ); + } + } + else if ( currentPage == m_searchUI->tab_2 ) + { + //create a ICQWPSearchInfo struct and send it + ICQProtocol* p = ICQProtocol::protocol(); + ICQWPSearchInfo info; + QTextCodec* codec = m_account->defaultCodec(); + info.firstName = codec->fromUnicode( m_searchUI->firstName->text() ); + info.lastName = codec->fromUnicode( m_searchUI->lastName->text() ); + info.nickName = codec->fromUnicode( m_searchUI->nickName->text() ); + info.email = codec->fromUnicode( m_searchUI->email->text() ); + info.city = codec->fromUnicode( m_searchUI->city->text() ); // City + info.gender = p->getCodeForCombo(m_searchUI->gender, p->genders()); // Gender + info.language = p->getCodeForCombo(m_searchUI->language, p->languages()); // Lang + info.country =p->getCodeForCombo(m_searchUI->country, p->countries()); // country code + info.onlineOnly = m_searchUI->onlyOnline->isChecked(); + + // Check if the user has actually entered things to search + if( info.firstName.isEmpty() && + info.lastName.isEmpty() && + info.nickName.isEmpty() && + info.email.isEmpty() && + info.city.isEmpty() && + (info.gender == 0) && + (info.language == 0) && + (info.country == 0) + ) + { + // All fields were blank + stopSearch(); + clearResults(); + KMessageBox::information(this, i18n("You must enter search criteria."), i18n("ICQ Plugin") ); + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "Search aborted: all fields were blank" << endl; + } + else + { + // Start the search + m_account->engine()->whitePagesSearch( info ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Starting whitepage search" << endl; + } + } + } +} + +void ICQSearchDialog::stopSearch() +{ + disconnect( m_account->engine(), SIGNAL( gotSearchResults( const ICQSearchResult& ) ), + this, SLOT( newResult( const ICQSearchResult& ) ) ); + disconnect( m_account->engine(), SIGNAL( endOfSearch( int ) ), + this, SLOT( searchFinished( int ) ) ); + + m_searchUI->stopButton->setEnabled( false ); + m_searchUI->searchButton->setEnabled( true ); + m_searchUI->newSearchButton->setEnabled( true ); +} + +void ICQSearchDialog::addContact() +{ + ICQAddContactPage* iacp = dynamic_cast<ICQAddContactPage*>( parent() ); + if ( !iacp ) + { + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "The ICQ ACP is not our parent!!" << endl; + } + else + { + QString uin = m_searchUI->searchResults->selectedItem()->text( 0 ); + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "Passing " << uin << " back to the ACP" << endl; + iacp->setUINFromSearch( uin ); + + // Closing the dialog + closeDialog(); + } +} + +void ICQSearchDialog::userInfo() +{ + // Lookup user info only if the account is online, otherwise warn the user + if(!m_account->isConnected()) + { + // Account currently offline + KMessageBox::sorry( this, i18n("You must be online to display user info."), i18n("ICQ Plugin") ); + } + else + { + // Account currently online + m_contact = new ICQContact( m_account, + m_searchUI->searchResults->selectedItem()->text( 0 ), + NULL); + + m_infoWidget = new ICQUserInfoWidget( Kopete::UI::Global::mainWidget(), "icq info" ); + QObject::connect( m_infoWidget, SIGNAL( finished() ), this, SLOT( closeUserInfo() ) ); + + m_infoWidget->setContact( m_contact ); + m_infoWidget->setModal(true); + m_infoWidget->show(); + if ( m_contact->account()->isConnected() ) + m_account->engine()->requestFullInfo( m_contact->contactId() ); + kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "Displaying user info" << endl; + } +} + +void ICQSearchDialog::closeUserInfo() +{ + // Free the ICQUserInfoWidget + QObject::disconnect( this, 0, m_infoWidget, 0 ); + m_infoWidget->delayedDestruct(); + m_infoWidget = NULL; + + // Free the ICQContact + delete m_contact; + m_contact = NULL; +} + +void ICQSearchDialog::clearResults() +{ + stopSearch(); + m_searchUI->searchResults->clear(); + m_searchUI->addButton->setEnabled( false ); + m_searchUI->userInfoButton->setEnabled( false ); + m_searchUI->searchButton->setEnabled( true ); +} + +void ICQSearchDialog::closeDialog() +{ + stopSearch(); + clearResults(); + clearFields(); + + slotClose(); +} + +void ICQSearchDialog::resultSelectionChanged() +{ + if ( !m_searchUI->searchResults->selectedItem() ) + { + m_searchUI->addButton->setEnabled( false ); + m_searchUI->userInfoButton->setEnabled( false ); + } + else + { + m_searchUI->addButton->setEnabled( true ); + m_searchUI->userInfoButton->setEnabled( true ); + } +} + +void ICQSearchDialog::newResult( const ICQSearchResult& info ) +{ + if ( info.uin == 1 ) + { + //TODO update progress + return; + } + + QTextCodec* codec = m_account->defaultCodec(); + + QListViewItem *item = new QListViewItem( m_searchUI->searchResults, QString::number( info.uin ), + codec->toUnicode( info.nickName ), + codec->toUnicode( info.firstName ), + codec->toUnicode( info.lastName ), + codec->toUnicode( info.email ), + info.auth ? i18n( "Yes" ) : i18n( "No" ) ); + + if ( !item ) + return; + + if ( info.online ) + item->setPixmap( 0, SmallIcon( "icq_online" ) ); + else + item->setPixmap( 0, SmallIcon( "icq_offline" ) ); + +} + +void ICQSearchDialog::searchFinished( int numLeft ) +{ + kdWarning(OSCAR_ICQ_DEBUG) << k_funcinfo << "There are " << numLeft << "contact left out of this search" << endl; + m_searchUI->stopButton->setEnabled( false ); + m_searchUI->clearButton->setEnabled( true ); + m_searchUI->searchButton->setEnabled( true ); + m_searchUI->newSearchButton->setEnabled( true ); +} + +void ICQSearchDialog::clearFields() +{ + m_searchUI->uin->setText( QString::null ); + + m_searchUI->firstName->setText( QString::null ); + m_searchUI->lastName->setText( QString::null ); + m_searchUI->nickName->setText( QString::null ); + m_searchUI->email->setText( QString::null ); + m_searchUI->city->setText( QString::null ); + m_searchUI->gender->setCurrentItem( 0 ); // Unspecified + m_searchUI->country->setCurrentItem( 0 ); + m_searchUI->language->setCurrentItem( 0 ); + m_searchUI->onlyOnline->setChecked( false ); +} + +void ICQSearchDialog::newSearch() +{ + clearResults(); + clearFields(); +} + +//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4; + +#include "icqsearchdialog.moc" diff --git a/kopete/protocols/oscar/icq/ui/icqsearchdialog.h b/kopete/protocols/oscar/icq/ui/icqsearchdialog.h new file mode 100644 index 00000000..b14aa2a1 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqsearchdialog.h @@ -0,0 +1,69 @@ +/* + Kopete Oscar Protocol + icqsearchdialog.h - search for people + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef ICQSEARCHDIALOG_H +#define ICQSEARCHDIALOG_H + +#include <kdialogbase.h> +#include "icquserinfo.h" + +class ICQAccount; +class ICQSearchBase; +class ICQContact; +class ICQUserInfoWidget; +/** +@author Kopete Developers +*/ +class ICQSearchDialog : public KDialogBase +{ +Q_OBJECT +public: + ICQSearchDialog( ICQAccount* account, QWidget* parent = 0, const char* name = 0 ); + ~ICQSearchDialog(); + +private slots: + void startSearch(); + void stopSearch(); + void addContact(); + void clearResults(); + void closeDialog(); + void userInfo(); + void closeUserInfo(); + void newSearch(); + + /// Enable/disable buttons when the selection changes + void resultSelectionChanged(); + + /// Add a search result to the listview + void newResult( const ICQSearchResult& info ); + + /// The search is finished + void searchFinished( int numLeft ); + +private: + ICQAccount* m_account; + ICQSearchBase* m_searchUI; + ICQContact* m_contact; + ICQUserInfoWidget* m_infoWidget; + + void clearFields(); +}; + +#endif + +//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4; diff --git a/kopete/protocols/oscar/icq/ui/icquserinfowidget.cpp b/kopete/protocols/oscar/icq/ui/icquserinfowidget.cpp new file mode 100644 index 00000000..3830e05f --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icquserinfowidget.cpp @@ -0,0 +1,190 @@ +/* + Kopete Oscar Protocol + icquserinfowidget.cpp - Display ICQ user info + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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 "icquserinfowidget.h" + +#include <qlayout.h> +#include <qlineedit.h> +#include <qspinbox.h> +#include <qcombobox.h> +#include <qobject.h> +#include <qtextcodec.h> + +#include <kdatewidget.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <kjanuswidget.h> +#include <klocale.h> + +#include "icqgeneralinfo.h" +#include "icqcontact.h" +#include "icqprotocol.h" +#include "icqworkinfowidget.h" +#include "icqotherinfowidget.h" +#include "icqinterestinfowidget.h" + + +ICQUserInfoWidget::ICQUserInfoWidget( QWidget * parent, const char * name ) +: KDialogBase( KDialogBase::IconList, 0, parent, name, false, i18n( "ICQ User Information" ), Ok ) +{ + kdDebug(14153) << k_funcinfo << "Creating new icq user info widget" << endl; + + QFrame* genInfo = addPage( i18n( "General Info" ), + i18n( "General ICQ Information" ), + KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "identity" ), KIcon::Desktop ) ); + QVBoxLayout* genLayout = new QVBoxLayout( genInfo ); + m_genInfoWidget = new ICQGeneralInfoWidget( genInfo, "Basic Information" ); + genLayout->addWidget( m_genInfoWidget ); + + QFrame* workInfo = addPage( i18n( "Work Info" ), + i18n( "Work Information" ), + KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "attach" ), KIcon::Desktop ) ); + QVBoxLayout* workLayout = new QVBoxLayout( workInfo ); + m_workInfoWidget = new ICQWorkInfoWidget( workInfo, "Work Information" ); + workLayout->addWidget( m_workInfoWidget ); + + QFrame* otherInfo = addPage( i18n( "Other Info" ), + i18n( "Other ICQ Information" ), + KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "email" ), KIcon::Desktop ) ); + QVBoxLayout* otherLayout = new QVBoxLayout( otherInfo ); + m_otherInfoWidget = new ICQOtherInfoWidget( otherInfo, "Other Information" ); + otherLayout->addWidget( m_otherInfoWidget ); + + QFrame* interestInfo = addPage( i18n( "Interest Info" ), + i18n( "Interest" ), + KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "email" ), KIcon::Desktop ) ); + QVBoxLayout* interestLayout = new QVBoxLayout( interestInfo ); + m_interestInfoWidget = new ICQInterestInfoWidget( interestInfo, "Other Information" ); + interestLayout->addWidget( m_interestInfoWidget ); + +} + +void ICQUserInfoWidget::setContact( ICQContact* contact ) +{ + m_contact = contact; + QObject::connect( contact, SIGNAL( haveBasicInfo( const ICQGeneralUserInfo& ) ), + this, SLOT( fillBasicInfo( const ICQGeneralUserInfo& ) ) ); + QObject::connect( contact, SIGNAL( haveWorkInfo( const ICQWorkUserInfo& ) ), + this, SLOT( fillWorkInfo( const ICQWorkUserInfo& ) ) ); + QObject::connect( contact, SIGNAL( haveEmailInfo( const ICQEmailInfo& ) ), + this, SLOT( fillEmailInfo( const ICQEmailInfo& ) ) ); + QObject::connect( contact, SIGNAL( haveMoreInfo( const ICQMoreUserInfo& ) ), + this, SLOT( fillMoreInfo( const ICQMoreUserInfo& ) ) ); + QObject::connect( contact, SIGNAL( haveInterestInfo( const ICQInterestInfo& ) ), + this, SLOT( fillInterestInfo( const ICQInterestInfo& ) ) ); +} + +void ICQUserInfoWidget::fillBasicInfo( const ICQGeneralUserInfo& ui ) +{ + QTextCodec* codec = m_contact->contactCodec(); + m_genInfoWidget->uinEdit->setText( m_contact->contactId() ); + m_genInfoWidget->nickNameEdit->setText( codec->toUnicode( ui.nickname ) ); + m_genInfoWidget->fullNameEdit->setText( codec->toUnicode( ui.firstName ) + " " + codec->toUnicode( ui.lastName ) ); + m_genInfoWidget->ipEdit->setText( m_contact->property( "ipAddress" ).value().toString() ); + m_genInfoWidget->emailEdit->setText( codec->toUnicode( ui.email ) ); + m_genInfoWidget->cityEdit->setText( codec->toUnicode( ui.city ) ); + m_genInfoWidget->stateEdit->setText( codec->toUnicode( ui.state ) ); + m_genInfoWidget->phoneEdit->setText( codec->toUnicode( ui.phoneNumber ) ); + m_genInfoWidget->faxEdit->setText( codec->toUnicode( ui.faxNumber ) ); + m_genInfoWidget->addressEdit->setText( codec->toUnicode( ui.address ) ); + m_genInfoWidget->cellEdit->setText( codec->toUnicode( ui.cellNumber ) ); + m_genInfoWidget->zipEdit->setText( codec->toUnicode( ui.zip ) ); + + QString country = static_cast<ICQProtocol*>( m_contact->protocol() )->countries()[ui.country]; + m_genInfoWidget->countryEdit->setText( country ); +} + +void ICQUserInfoWidget::fillWorkInfo( const ICQWorkUserInfo& ui ) +{ + QTextCodec* codec = m_contact->contactCodec(); + m_workInfoWidget->cityEdit->setText( codec->toUnicode( ui.city ) ); + m_workInfoWidget->stateEdit->setText( codec->toUnicode( ui.state ) ); + m_workInfoWidget->phoneEdit->setText( codec->toUnicode( ui.phone ) ); + m_workInfoWidget->faxEdit->setText( codec->toUnicode( ui.fax ) ); + m_workInfoWidget->addressEdit->setText( codec->toUnicode( ui.address ) ); + m_workInfoWidget->zipEdit->setText( codec->toUnicode( ui.zip ) ); + m_workInfoWidget->companyEdit->setText( codec->toUnicode( ui.company ) ); + m_workInfoWidget->departmentEdit->setText( codec->toUnicode( ui.department ) ); + m_workInfoWidget->positionEdit->setText( codec->toUnicode( ui.position ) ); + m_workInfoWidget->homepageEdit->setText( codec->toUnicode( ui.homepage ) ); + + ICQProtocol* p = static_cast<ICQProtocol*>( m_contact->protocol() ); + QString country = p->countries()[ui.country]; + m_workInfoWidget->countryEdit->setText( country ); + + //TODO handle the occupation +} + +void ICQUserInfoWidget::fillEmailInfo( const ICQEmailInfo& ) +{ +} + +void ICQUserInfoWidget::fillInterestInfo( const ICQInterestInfo& info) +{ + QTextCodec* codec = m_contact->contactCodec(); + if (info.count>0) { + QString topic = static_cast<ICQProtocol*>( m_contact->protocol() )->interests()[info.topics[0]]; + m_interestInfoWidget->topic1->setText( topic ); + m_interestInfoWidget->desc1->setText( codec->toUnicode( info.descriptions[0] ) ); + } + if (info.count>1) { + QString topic = static_cast<ICQProtocol*>( m_contact->protocol() )->interests()[info.topics[1]]; + m_interestInfoWidget->topic2->setText( topic ); + m_interestInfoWidget->desc2->setText( codec->toUnicode( info.descriptions[1] ) ); + } + if (info.count>2) { + QString topic = static_cast<ICQProtocol*>( m_contact->protocol() )->interests()[info.topics[2]]; + m_interestInfoWidget->topic3->setText( topic ); + m_interestInfoWidget->desc3->setText( codec->toUnicode( info.descriptions[2] ) ); + } + if (info.count>3) { + QString topic = static_cast<ICQProtocol*>( m_contact->protocol() )->interests()[info.topics[3]]; + m_interestInfoWidget->topic4->setText( topic ); + m_interestInfoWidget->desc4->setText( codec->toUnicode( info.descriptions[3] ) ); + } +} + +void ICQUserInfoWidget::fillMoreInfo( const ICQMoreUserInfo& ui ) +{ + QTextCodec* codec = m_contact->contactCodec(); + m_genInfoWidget->ageSpinBox->setValue( ui.age ); + if ( ui.birthday.isValid() ) + m_genInfoWidget->birthday->setText( KGlobal::locale()->formatDate( ui.birthday,true ) ); + + QString gender = static_cast<ICQProtocol*>( m_contact->protocol() )->genders()[ui.gender]; + m_genInfoWidget->genderEdit->setText( gender ); + m_genInfoWidget->homepageEdit->setText( codec->toUnicode( ui.homepage ) ); + + QString ms = static_cast<ICQProtocol*>( m_contact->protocol() )->maritals()[ui.marital]; + m_genInfoWidget->marital->setText( ms ); + + m_genInfoWidget->oCityEdit->setText( codec->toUnicode( ui.ocity) ); + m_genInfoWidget->oStateEdit->setText( codec->toUnicode( ui.ostate) ); + + QString ocountry = static_cast<ICQProtocol*>( m_contact->protocol() )->countries()[ui.ocountry]; + m_genInfoWidget->oCountryEdit->setText( ocountry ); + + //TODO languages +} + + +#include "icquserinfowidget.moc" + +//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off; + diff --git a/kopete/protocols/oscar/icq/ui/icquserinfowidget.h b/kopete/protocols/oscar/icq/ui/icquserinfowidget.h new file mode 100644 index 00000000..ef478e59 --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icquserinfowidget.h @@ -0,0 +1,58 @@ +/* + Kopete Oscar Protocol + icquserinfowidget.h - Display ICQ user info + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef _ICQUSERINFOWIDGET_H_ +#define _ICQUSERINFOWIDGET_H_ + +#include <kdialogbase.h> +#include <icquserinfo.h> + +class KJanusWidget; +class ICQGeneralInfoWidget; +class ICQWorkInfoWidget; +class ICQOtherInfoWidget; +class ICQInterestInfoWidget; +class ICQContact; + +class ICQUserInfoWidget : public KDialogBase +{ +Q_OBJECT +public: + ICQUserInfoWidget( QWidget* parent = 0, const char* name = 0 ); + void setContact( ICQContact* contact ); + +public slots: + void fillBasicInfo( const ICQGeneralUserInfo& ); + void fillWorkInfo( const ICQWorkUserInfo& ); + void fillEmailInfo( const ICQEmailInfo& ); + void fillMoreInfo( const ICQMoreUserInfo& ); + void fillInterestInfo( const ICQInterestInfo& ); + +private: + ICQGeneralInfoWidget* m_genInfoWidget; + ICQWorkInfoWidget* m_workInfoWidget; + ICQOtherInfoWidget* m_otherInfoWidget; + ICQInterestInfoWidget * m_interestInfoWidget; + KJanusWidget* m_janusWidget; + ICQContact* m_contact; + +}; + +#endif + +//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/icq/ui/icqworkinfowidget.ui b/kopete/protocols/oscar/icq/ui/icqworkinfowidget.ui new file mode 100644 index 00000000..a31021ba --- /dev/null +++ b/kopete/protocols/oscar/icq/ui/icqworkinfowidget.ui @@ -0,0 +1,249 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ICQWorkInfoWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ICQWorkInfoWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>328</width> + <height>480</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Personal Work Information</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel10</cstring> + </property> + <property name="text"> + <string>Phone:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="2"> + <property name="name"> + <cstring>textLabel11</cstring> + </property> + <property name="text"> + <string>Fax:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>Department:</string> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>departmentEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>Position:</string> + </property> + </widget> + <widget class="QLineEdit" row="0" column="3"> + <property name="name"> + <cstring>positionEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>phoneEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="1" column="3"> + <property name="name"> + <cstring>faxEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="title"> + <string>Company Location Information</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Name:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel8</cstring> + </property> + <property name="text"> + <string>Homepage:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Address:</string> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Zip:</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>State:</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>City:</string> + </property> + </widget> + <widget class="QLabel" row="6" column="0"> + <property name="name"> + <cstring>textLabel9</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Country:</string> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>companyEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>homepageEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>addressEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="3" column="1"> + <property name="name"> + <cstring>cityEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="4" column="1"> + <property name="name"> + <cstring>stateEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="5" column="1"> + <property name="name"> + <cstring>zipEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="6" column="1"> + <property name="name"> + <cstring>countryEdit</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>70</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/oscar/icq/x-icq.desktop b/kopete/protocols/oscar/icq/x-icq.desktop new file mode 100644 index 00000000..1d8b3eb2 --- /dev/null +++ b/kopete/protocols/oscar/icq/x-icq.desktop @@ -0,0 +1,60 @@ +[Desktop Entry] +Comment=ICQ Contact +Comment[ar]=جهة اتصال على ICQ +Comment[be]=ICQ +Comment[bg]=Връзка с ICQ Contact +Comment[bn]=আই-সি-কিউ যোগাযোগ +Comment[br]=Darempred ICQ +Comment[bs]=ICQ kontakt +Comment[ca]=Contacte ICQ +Comment[cs]=ICQ kontakt +Comment[cy]=Cysylltiad ICQ +Comment[da]=ICQ-Kontakt +Comment[de]=ICQ-Kontakt +Comment[el]=Επαφή ICQ +Comment[eo]=ICQ-kontakto +Comment[es]=Contacto de ICQ +Comment[et]=ICQ kontakt +Comment[eu]=ICQ kontaktua +Comment[fa]=تماس ICQ +Comment[fi]=ICQ-kontakti +Comment[fr]=Contact ICQ +Comment[gl]=Contacto ICQ +Comment[he]=איש-קשר ICQ +Comment[hi]=आईसीक्यू सम्पर्क +Comment[hr]=ICQ kontakt +Comment[hu]=ICQ-kapcsolat +Comment[is]=ICQ tengiliður +Comment[it]=Contatto ICQ +Comment[ja]=ICQ コンタクト +Comment[ka]=ICQ მეგობარი +Comment[kk]=ICQ байланыс +Comment[km]=ទំនាក់ទំនង ICQ +Comment[lt]=ICQ kontaktas +Comment[mk]=Контакт на ICQ +Comment[nb]=ICQ kontakt +Comment[nds]=ICQ-Kontakt +Comment[ne]=आईसीक्यू सम्पर्क +Comment[nl]=ICQ contact +Comment[nn]=ICQ-kontakt +Comment[pl]=Kontakt ICQ +Comment[pt]=Contacto de ICQ +Comment[pt_BR]=Contato ICQ +Comment[ru]=Контакт ICQ +Comment[se]=ICQ-oktavuohta +Comment[sk]=Kontakt ICQ +Comment[sl]=Stik ICQ +Comment[sr]=ICQ контакт +Comment[sr@Latn]=ICQ kontakt +Comment[sv]=ICQ-kontakt +Comment[ta]=ICQ தொடர்பு +Comment[tg]=Пайвастшавии ICQ +Comment[tr]=ICQ Bağlantısı +Comment[uk]=Контакт ICQ +Comment[zh_CN]=ICQ 联系人 +Comment[zh_HK]=ICQ 聯絡人 +Comment[zh_TW]=ICQ 聯絡人 +Type=MimeType +MimeType=application/x-icq +Patterns=*.uin;*.icq +Icon=licq diff --git a/kopete/protocols/oscar/liboscar/DESIGN b/kopete/protocols/oscar/liboscar/DESIGN new file mode 100644 index 00000000..3f772e22 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/DESIGN @@ -0,0 +1,12 @@ +This file attempts to detail the design of the liboscar library. It's still a +work in progress. + +liboscar is based off of the libgroupwise library which handles connections to +Novell's Groupwise messenging system. libgroupwise is based off of the libiris +library which is used to interface with the jabber instant messaging network. + +Details of the library: +============================================ + +All the protocol actions are encapsulated in Tasks. + diff --git a/kopete/protocols/oscar/liboscar/HACKING b/kopete/protocols/oscar/liboscar/HACKING new file mode 100644 index 00000000..9bd25476 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/HACKING @@ -0,0 +1,194 @@ +This is the oscar HACKING file. It details the current coding style that is being +used in this plugin. Thanks to Scott Wheeler for providing the skeleton I based this +file on + +================================================================================ +Code Documentation +================================================================================ + +Please add doxygen comments to the header files where appropriate. I don't expect +anyone to add comments for functions that they're overriding from the base class +but comments everywhere would be good. + +Please comment parts of the code that might be unclear, need more thinking about, +reimplementing, etc. It will help people look for things to do if they want to help +out. + +Please don't remove the kdDebug lines from any of the source files. If they're +excessive, either wrap them in an ifdef and put the ifdef in the soon to be +created oscardebug.h file so that they can be enabled and disabled at the will of +other developers or users. I also tend to use kdDebug statements to document +my code in the place of comments for the simpler sections. + +================================================================================ +Indentation +================================================================================ + +I use tabs to indent everything. When I say tabs, I mean the 'tab' character. Please +don't use 8 spaces to indent. Just hit the 'tab' key, and make sure that space indentation +is turned off in whatever editor you use. However, the exception to the indentation +rule is anything that's inside of a namespace block should not be indented. + + +static void foo() +{ + if ( bar() ) <-- 1 tab + baz(); <-- 2 tabs +} + +namespace +{ +class Foo +{ +Q_OBJECT +public: + Foo(); + ~Foo(); +}; +} + + + + +vim or kate modelines that modify the way tabs are displayed are encouraged, as +long as they don't actually change the way tabs are saved to a file. + +================================================================================ +Braces +================================================================================ + +Braces opening classes, structs, namespaces, functions, and conditionals should be +on their own line. Here's an example: + +class Foo +{ + // stuff +}; + +if ( foo == bar ) +{ + // stuff +} + +while ( foo == bar && + baz == quux && + flop == pop ) +{ + // stuff +} + +static void foo() +{ + // stuff +} + +Also conditionals / loops that only contiain one line in their body (but where +the conditional statement fits onto one line) should omit braces: + +if ( foo == bar ) + baz(); + +But: + +if ( baz == quux && + ralf == spot ) +{ + bar(); +} + +================================================================================ +Spaces +================================================================================ + +Spaces should be used between the conditional / loop type and the +conditional statement. They should also not be used after parenthesis. However +the should be to mark of mathematical or comparative operators. + +if ( foo == bar ) + ^ ^ ^ + +is correct. However: + +if(foo == bar) + +is not. + +================================================================================ +Header Organization +================================================================================ + +Member variables should always be private and prefixed with "m_". Accessors may +not be inline in the headers. The organization of the members in a class should be +roughly as follows: + +public: +public slots: +protected: +protected slots: +signals: +private: // member funtions +private slots: +private: // member variables + +If there are no private slots there is no need for two private sections, however +private functions and private variables should be clearly separated. + +The implementations files -- .cpp files -- should follow (when possible) the +same order of function declarations as the header files. + +Virtual functions should always be marked as such even in derived classes where +it is not strictly necessary. + +================================================================================ +Whitespace +================================================================================ + +Whitespace should be used liberally. When blocks of code are logically distinct +I tend to put a blank line between them. This is difficult to explain +systematically but after looking a bit at the current code organization this +ideally will be somewhat clear. + +Parenthesis should be padded by spaces on one side. This is easier to illustrate in +an example: + +void Client::foo() //correct +void Client::foo3( int, int, int ) //correct + +void Client::foo(int, int, int) //incorrect +void Client::foo(int,int,int) //also incorrect + +Operators should be padded by spaces in conditionals. Again, more examples to +illustrate + +if (foo==bar) +m+=(n*2)-3; + +should be: + +if ( foo == bar ) +m += ( n * 2 ) - 3; + +================================================================================ +Pointer and Reference Operators +================================================================================ + +This one is pretty simple. I prefer "Foo* f" to "Foo *f" in function signatures +and declarations. The same goes for "Foo& f" over "Foo &f". + +================================================================================ + +There are likely things missing here and I'll try to add them over time as I +notice things that are often missed. Please let me know if specific points are +ambiguous. + +Also, please note that since this library is based heavily off of Kopete's +libgroupwise library that the coding style in certain files may not match what's +written in this document. Those files that don't match will be corrected eventually. + +To make things easier on you, kate modelines are provided at the end of certain files +to help enforce the coding style. If you're using the new C S&S Indenter that will be in +KDE 3.4, I can provide a patch that will automatically implement the space padding around +parenthesis. Please mail me so I can send it to you. + +Matt Rogers <mattr@kde.org> + diff --git a/kopete/protocols/oscar/liboscar/Makefile.am b/kopete/protocols/oscar/liboscar/Makefile.am new file mode 100644 index 00000000..ea757b69 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/Makefile.am @@ -0,0 +1,26 @@ + +METASOURCES = AUTO +noinst_LTLIBRARIES = liboscar.la + +AM_CPPFLAGS = -I$(top_srcdir)/kopete/libkopete $(all_includes) + + +liboscar_la_SOURCES = oscarutils.cpp client.cpp task.cpp connector.cpp \ + inputprotocolbase.cpp coreprotocol.cpp flapprotocol.cpp snacprotocol.cpp transfer.cpp rtf.cc \ + bytestream.cpp oscarclientstream.cpp safedelete.cpp stream.cpp oscarconnector.cpp \ + oscarbytestream.cpp buffer.cpp md5.c logintask.cpp aimlogintask.cpp icqlogintask.cpp \ + closeconnectiontask.cpp rateclassmanager.cpp serverversionstask.cpp rateinfotask.cpp \ + errortask.cpp locationrightstask.cpp profiletask.cpp blmlimitstask.cpp \ + servicesetuptask.cpp icbmparamstask.cpp ssimanager.cpp rateclass.cpp rateclass.h \ + prmparamstask.cpp ssiparamstask.cpp ssilisttask.cpp ssiactivatetask.cpp \ + clientreadytask.cpp senddcinfotask.cpp sendidletimetask.cpp ownuserinfotask.cpp \ + connection.cpp onlinenotifiertask.cpp userdetails.cpp ssimodifytask.cpp \ + oscartypeclasses.cpp oscarmessage.cpp messagereceivertask.cpp sendmessagetask.cpp icqtask.cpp \ + offlinemessagestask.cpp ssiauthtask.cpp userinfotask.cpp icquserinfo.cpp icquserinfotask.cpp \ + usersearchtask.cpp warningtask.cpp changevisibilitytask.cpp typingnotifytask.cpp \ + buddyicontask.cpp serverredirecttask.cpp oscarsettings.cpp \ + chatnavservicetask.cpp connectionhandler.cpp chatservicetask.cpp + +liboscar_la_LDFLAGS = -no-undefined $(all_libraries) +liboscar_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) + diff --git a/kopete/protocols/oscar/liboscar/TODO b/kopete/protocols/oscar/liboscar/TODO new file mode 100644 index 00000000..1ec9be98 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/TODO @@ -0,0 +1,37 @@ +This is the TODO file for liboscar. Please note that this TODO file is on a +very short timeframe since the goal is to have liboscar done before KDE 3.4. +Realistically, KDE 4 is a better goal, but i want to push hard for KDE 3.4 + +If you're going to be looking at the docs, I suggest downloading the zip file (click the download link) from iserverd.khstu.ru/oscar for +faster loading. + +Misc. Before Merge things +==================================== + +- Don't hardcode the values in SendDCInfoTask. Find a way to get them from the account or something. +- Rename SendDCInfoTask to SendExtInfoTask (rename the files on the server too. contact sysadmin@kde.org to see about this. It may have to wait until the merge) +- Check capabilities handling (the code is from oscarsocket, we need to make sure it will still work ok for liboscar until we come up with something better) +- Test moving contacts from one group to another + + +Direct Connections +==================================== +When/If we get around to it. Matt knows absolutely nothing about direct connections and the only online source of documentation is no longer online. :( +This will definately be one of those things we have to dissect gaim for. :/ + + +SNAC 0x15 parsing +==================================== + +SNAC 0x15 parsing is done. however parts may need to be reworked as things have gotten +very messy. we currently don't do a good job of handling extra data (i.e. i can't call +addInitialData with just the initial data and get the type 1 tlv length right. maybe a +prepareSend( const Buffer& ) function that adds the type one tlv to our packet so we +get the tlv length right. + +also, we may want to implement a removeInitialData function that we can call if the packet +is for us so we don't have to have code in all the icq tasks that get rid of the initial tlv +data that we parse in parse initial data. + + + diff --git a/kopete/protocols/oscar/liboscar/aimlogintask.cpp b/kopete/protocols/oscar/liboscar/aimlogintask.cpp new file mode 100644 index 00000000..69a9c770 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/aimlogintask.cpp @@ -0,0 +1,254 @@ +/* + Kopete Oscar Protocol + aimlogintask.h - Handles logging into to the AIM service + + Copyright (c) 2004 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 "aimlogintask.h" + +#include <stdlib.h> +#include <kdebug.h> +#include <klocale.h> +#include "connection.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" + +#include "md5.h" + +using namespace Oscar; + +AimLoginTask::AimLoginTask( Task* parent ) + : Task ( parent ) +{ +} + +AimLoginTask::~AimLoginTask() +{ +} + +void AimLoginTask::onGo() +{ + //send Snac 17,06 + sendAuthStringRequest(); + //when we have the authKey, login + connect( this, SIGNAL( haveAuthKey() ), this, SLOT( sendLoginRequest() ) ); +} + +bool AimLoginTask::forMe( Transfer* transfer ) const +{ + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + + if (!st) + return false; + + if ( st && st->snacService() == 0x17 ) + { + WORD subtype = st->snacSubtype(); + switch ( subtype ) + { + case 0x0002: + case 0x0003: + case 0x0006: + case 0x0007: + return true; + break; + default: + return false; + break; + } + } + return false; +} + +const QByteArray& AimLoginTask::cookie() const +{ + return m_cookie; +} + +const QString& AimLoginTask::bosHost() const +{ + return m_bosHost; +} + +const QString& AimLoginTask::bosPort() const +{ + return m_bosPort; +} + +bool AimLoginTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + if (!st) + return false; + + WORD subtype = st->snacSubtype(); + switch ( subtype ) + { + case 0x0003: + setTransfer( transfer ); + handleLoginResponse(); + setTransfer( 0 ); + return true; + break; + case 0x0007: + setTransfer( transfer ); + processAuthStringReply(); + setTransfer( 0 ); + return true; + break; + default: + return false; + break; + } + + return false; + } + return false; +} + +void AimLoginTask::sendAuthStringRequest() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo + << "SEND CLI_AUTH_REQUEST, sending login request" << endl; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0017, 0x0006, 0x0000, client()->snacSequence() }; + + Buffer* outbuf = new Buffer; + outbuf->addTLV(0x0001, client()->userId().length(), client()->userId().latin1() ); + outbuf->addDWord(0x004B0000); // empty TLV 0x004B + outbuf->addDWord(0x005A0000); // empty TLV 0x005A + + Transfer* st = createTransfer( f, s, outbuf ); + send( st ); +} + +void AimLoginTask::processAuthStringReply() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got the authorization key" << endl; + Buffer *inbuf = transfer()->buffer(); + WORD keylen = inbuf->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Key length is " << keylen << endl; + m_authKey.duplicate( inbuf->getBlock(keylen) ); + emit haveAuthKey(); +} + +void AimLoginTask::sendLoginRequest() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SEND (CLI_MD5_LOGIN) sending AIM login" << endl; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0017, 0x0002, 0x0000, client()->snacSequence() }; + Buffer *outbuf = new Buffer; + const Oscar::ClientVersion* version = client()->version(); + + outbuf->addTLV(0x0001, client()->userId().length(), client()->userId().latin1()); + + QByteArray digest( 17 ); //apparently MD5 digests are 16 bytes long + encodePassword( digest ); + digest[16] = '\0'; //do this so that addTLV sees a NULL-terminator + + outbuf->addTLV(0x0025, 16, digest); + outbuf->addTLV(0x0003, version->clientString.length(), version->clientString.latin1() ); + outbuf->addTLV16(0x0016, version->clientId ); + outbuf->addTLV16(0x0017, version->major ); + outbuf->addTLV16(0x0018, version->minor ); + outbuf->addTLV16(0x0019, version->point ); + outbuf->addTLV16(0x001a, version->build ); + outbuf->addDWord(0x00140004); //TLV type 0x0014, length 0x0004 + outbuf->addDWord( version->other ); //TLV data for type 0x0014 + outbuf->addTLV(0x000f, version->lang.length(), version->lang.latin1() ); + outbuf->addTLV(0x000e, version->country.length(), version->country.latin1() ); + + //if set, old-style buddy lists will not work... you will need to use SSI + outbuf->addTLV8(0x004a,0x01); + + Transfer *st = createTransfer( f, s, outbuf ); + send( st ); +} + +void AimLoginTask::handleLoginResponse() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "RECV SNAC 0x17, 0x07 - AIM Login Response" << endl; + + SnacTransfer* st = dynamic_cast<SnacTransfer*> ( transfer() ); + + if ( !st ) + { + setError( -1 , QString::null ); + return; + } + + QValueList<TLV> tlvList = st->buffer()->getTLVList(); + + TLV uin = findTLV( tlvList, 0x0001 ); + if ( uin ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(1) [SN], SN=" << QString( uin.data ) << endl; + } + + TLV err = findTLV( tlvList, 0x0008 ); + + if ( err ) + { + WORD errorNum = ( ( err.data[0] << 8 ) | err.data[1] ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << k_funcinfo << "found TLV(8) [ERROR] error= " << + errorNum << endl; + Oscar::SNAC s = { 0, 0, 0, 0 }; + client()->fatalTaskError( s, errorNum ); + setError( errorNum, QString::null ); + return; //if there's an error, we'll need to disconnect anyways + } + + TLV server = findTLV( tlvList, 0x0005 ); + if ( server ) + { + QString ip = QString( server.data ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(5) [SERVER] " << ip << endl; + int index = ip.find( ':' ); + m_bosHost = ip.left( index ); + ip.remove( 0 , index+1 ); //get rid of the colon and everything before it + m_bosPort = ip.left(4); //we only need 4 bytes + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "We should reconnect to server '" << m_bosHost << + "' on port " << m_bosPort << endl; + } + + TLV cookie = findTLV( tlvList, 0x0006 ); + if ( cookie ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(6) [COOKIE]" << endl; + m_cookie.duplicate( cookie.data ); + setSuccess( 0, QString::null ); + } + tlvList.clear(); +} + +void AimLoginTask::encodePassword( QByteArray& digest ) const +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + md5_state_t state; + md5_init( &state ); + md5_append( &state, ( const md5_byte_t* ) m_authKey.data(), m_authKey.size() ); + md5_append( &state, ( const md5_byte_t* ) client()->password().latin1(), client()->password().length() ); + md5_append( &state, ( const md5_byte_t* ) AIM_MD5_STRING, strlen( AIM_MD5_STRING ) ); + md5_finish( &state, ( md5_byte_t* ) digest.data() ); +} + +//kate: indent-mode csands; tab-width 4; + +#include "aimlogintask.moc" diff --git a/kopete/protocols/oscar/liboscar/aimlogintask.h b/kopete/protocols/oscar/liboscar/aimlogintask.h new file mode 100644 index 00000000..66308178 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/aimlogintask.h @@ -0,0 +1,82 @@ +/* + Kopete Oscar Protocol + aimlogintask.h - Handles logging into to the AIM service + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef _OSCAR_AIMLOGINTASK_H_ +#define _OSCAR_AIMLOGINTASK_H_ + +#include "task.h" + +using namespace Oscar; + +class AimLoginTask : public Task +{ +Q_OBJECT +public: + AimLoginTask( Task* parent ); + ~AimLoginTask(); + bool take( Transfer* transfer ); + virtual void onGo(); + + //Protocol specific stuff + const QByteArray& cookie() const; + const QString& bosHost() const; + const QString& bosPort() const; + +protected: + bool forMe( Transfer* transfer ) const; + +signals: + void haveAuthKey(); + +private: + //! Encodes a password using MD5 + void encodePassword( QByteArray& digest ) const; + + //! Send SNAC 0x17, 0x06 + void sendAuthStringRequest(); + + //! Handle SNAC 0x17, 0x07 + void processAuthStringReply(); + + //! Handle SNAC 0x17, 0x03 + void handleLoginResponse(); + + //! Parse the error codes to generate a reason why sign-on failed + //Massive code duplication with CloseConnectionTask + bool parseDisconnectCode( int error, QString& reason ); + +private slots: + //! Send SNAC 0x17, 0x02 + void sendLoginRequest(); + +private: + //! The authorization key to use when encoding the password + QByteArray m_authKey; + + //! The all important connection cookie + QByteArray m_cookie; + + //! The new BOS Host + QString m_bosHost; + + //! The new BOS Port + QString m_bosPort; + +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/blmlimitstask.cpp b/kopete/protocols/oscar/liboscar/blmlimitstask.cpp new file mode 100644 index 00000000..c3fe7e6e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/blmlimitstask.cpp @@ -0,0 +1,94 @@ +/* + Kopete Oscar Protocol + blmlimitstask - Get the BLM service limits + + Copyright (c) 2004 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 "blmlimitstask.h" +#include <kdebug.h> +#include "connection.h" +#include "transfer.h" +#include "oscartypes.h" +#include "oscarutils.h" + +BLMLimitsTask::BLMLimitsTask( Task* parent ) + : Task( parent ) +{ +} + + +BLMLimitsTask::~BLMLimitsTask() +{ +} + + +bool BLMLimitsTask::forMe(const Transfer* transfer) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + + if (!st) + return false; + + if ( st->snacService() == 3 && st->snacSubtype() == 3 ) + return true; + else + return false; +} + +bool BLMLimitsTask::take(Transfer* transfer) +{ + if ( forMe( transfer ) ) + { + Buffer* buffer = transfer->buffer(); + while ( buffer->length() != 0 ) + { + TLV t = buffer->getTLV(); + switch ( t.type ) + { + case 0x0001: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Max BLM entries: " + << t.data << endl; + break; + case 0x0002: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Max watcher entries: " + << t.data << endl; + break; + case 0x0003: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Max online notifications(?): " + << t.data << endl; + break; + } + } + setSuccess( 0, QString::null ); + return true; + } + else + return false; +} + +void BLMLimitsTask::onGo() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending BLM limits request" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0003, 0x0002, 0x0000, client()->snacSequence() }; + + Buffer* buffer = new Buffer(); + buffer->addTLV16( 0x0005, 0x0003 ); + + Transfer *t = createTransfer( f, s, buffer ); + send( t ); +} + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/blmlimitstask.h b/kopete/protocols/oscar/liboscar/blmlimitstask.h new file mode 100644 index 00000000..7ded03a7 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/blmlimitstask.h @@ -0,0 +1,43 @@ +/* + Kopete Oscar Protocol + blmlimitstask.h - Fetch the limits for the BLM service + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ +#ifndef BLMLIMITSTASK_H +#define BLMLIMITSTASK_H + +#include "task.h" + +/** +Fetch the limits for the BLM service + +@author Matt Rogers +*/ +class BLMLimitsTask : public Task +{ +public: + BLMLimitsTask( Task* parent ); + + ~BLMLimitsTask(); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/buddyicontask.cpp b/kopete/protocols/oscar/liboscar/buddyicontask.cpp new file mode 100644 index 00000000..b2a35b1d --- /dev/null +++ b/kopete/protocols/oscar/liboscar/buddyicontask.cpp @@ -0,0 +1,245 @@ +// buddyicontask.cpp + +// 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., 59 Temple Place, Suite 330, Boston, MA +// 02111-1307 USA + +#include "buddyicontask.h" + +#include <qstring.h> +#include <kdebug.h> +#include "buffer.h" +#include "connection.h" +#include "transfer.h" +#include "oscarutils.h" +#include <typeinfo> + +BuddyIconTask::BuddyIconTask( Task* parent ) + :Task( parent ) +{ + m_seq = 0; + m_refNum = -1; + m_iconLength = 0; + m_hashType = 0; +} + +void BuddyIconTask::uploadIcon( WORD length, const QByteArray& data ) +{ + m_iconLength = length; + m_icon = data; + m_action = Send; +} + +void BuddyIconTask::requestIconFor( const QString& user ) +{ + m_user = user; + m_action = Receive; +} + +void BuddyIconTask::setHash( const QByteArray& md5Hash ) +{ + m_hash = md5Hash; +} + +void BuddyIconTask::setHashType( BYTE type ) +{ + m_hashType = type; +} + +void BuddyIconTask::onGo() +{ + if ( m_action == Send && m_icon.count() == 0 ) + return; + + if ( m_action == Receive && ( m_user.isEmpty() || m_hash.count() == 0 ) ) + return; + + if ( m_action == Receive ) + { + if ( client()->isIcq() ) + sendICQBuddyIconRequest(); + else + sendAIMBuddyIconRequest(); + } + else + sendIcon(); +} + +bool BuddyIconTask::forMe( const Transfer* transfer ) +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacRequest() != m_seq ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sequences don't match" << endl; + return false; + } + + if ( st->snacService() == 0x0010 ) + { + switch( st->snacSubtype() ) + { + case 0x0003: + case 0x0005: + case 0x0007: + return true; + break; + default: + return false; + break; + } + } + + return false; +} + +bool BuddyIconTask::take( Transfer* transfer ) +{ + if ( !forMe( transfer ) ) + return false; + + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + if ( !st ) + return false; + + setTransfer( transfer ); + if ( st->snacSubtype() == 0x0003 ) + handleUploadResponse(); + else if ( st->snacSubtype() == 0x0005 ) + handleAIMBuddyIconResponse(); + else + handleICQBuddyIconResponse(); + + setSuccess( 0, QString::null ); + setTransfer( 0 ); + return true; +} + +void BuddyIconTask::sendIcon() +{ + kdDebug(OSCAR_RAW_DEBUG) << "icon length: " << m_iconLength << endl; + FLAP f = { 0x02, 0, 0 }; + m_seq = client()->snacSequence(); + SNAC s = { 0x0010, 0x0002, 0x0000, m_seq }; + Buffer* b = new Buffer; + b->addWord( 1 ); //gaim hard codes it, so will we + b->addWord( m_iconLength ); + b->addString( m_icon ); + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +void BuddyIconTask::handleUploadResponse() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "server acked icon upload" << endl; + Buffer* b = transfer()->buffer(); + b->skipBytes( 4 ); + BYTE iconHashSize = b->getByte(); + QByteArray hash( b->getBlock( iconHashSize ) ); + //check the hash + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "hash " << hash << endl; + setSuccess( 0, QString::null ); +} + + +void BuddyIconTask::sendAIMBuddyIconRequest() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "requesting buddy icon for " << m_user << endl; + FLAP f = { 0x02, 0, 0 }; + m_seq = client()->snacSequence(); + SNAC s = { 0x0010, 0x0004, 0x0000, m_seq }; + Buffer* b = new Buffer; + + b->addBUIN( m_user.latin1() ); //TODO: check encoding + b->addByte( 0x01 ); + b->addWord( 0x0001 ); + b->addByte( m_hashType ); + b->addByte( m_hash.size() ); //MD5 Hash Size + b->addString( m_hash, m_hash.size() ); //MD5 Hash + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +void BuddyIconTask::handleAIMBuddyIconResponse() +{ + Buffer* b = transfer()->buffer(); + QString user = b->getBUIN(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Receiving buddy icon for " << user << endl; + b->skipBytes(2); //unknown field. not used + BYTE iconType = b->getByte(); + Q_UNUSED( iconType ); + BYTE hashSize = b->getByte(); + QByteArray iconHash; + iconHash.duplicate( b->getBlock(hashSize) ); + WORD iconSize = b->getWord(); + QByteArray icon; + icon.duplicate( b->getBlock(iconSize) ); + emit haveIcon( user, icon ); +} + +void BuddyIconTask::sendICQBuddyIconRequest() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "requesting buddy icon for " << m_user << endl; + FLAP f = { 0x02, 0, 0 }; + m_seq = client()->snacSequence(); + SNAC s = { 0x0010, 0x0006, 0x0000, m_seq }; + Buffer* b = new Buffer; + + b->addBUIN( m_user.latin1() ); //TODO: check encoding + b->addByte( 0x01 ); + b->addWord( 0x0001 ); + b->addByte( m_hashType ); + b->addByte( m_hash.size() ); //MD5 Hash Size + b->addString( m_hash, m_hash.size() ); //MD5 Hash + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +void BuddyIconTask::handleICQBuddyIconResponse() +{ + Buffer* b = transfer()->buffer(); + QString user = b->getBUIN(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Receiving buddy icon for " << user << endl; + + b->skipBytes(2); //not used + BYTE iconType = b->getByte(); + Q_UNUSED( iconType ); + + BYTE hashSize = b->getByte(); + QByteArray iconHash; + iconHash.duplicate( b->getBlock(hashSize) ); + + b->skipBytes(1); //not used + b->skipBytes(2); //not used + BYTE iconType2 = b->getByte(); + Q_UNUSED( iconType2 ); + + BYTE hashSize2 = b->getByte(); + QByteArray iconHash2; + iconHash2.duplicate( b->getBlock(hashSize2) ); + + WORD iconSize = b->getWord(); + QByteArray icon; + icon.duplicate( b->getBlock(iconSize) ); + + emit haveIcon( user, icon ); +} + +#include "buddyicontask.moc" + + diff --git a/kopete/protocols/oscar/liboscar/buddyicontask.h b/kopete/protocols/oscar/liboscar/buddyicontask.h new file mode 100644 index 00000000..af7931f0 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/buddyicontask.h @@ -0,0 +1,69 @@ +// buddyicontask.h + +// 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 fdeven 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., 59 Temple Place, Suite 330, Boston, MA +// 02111-1307 USA + +#ifndef BUDDYICONTASK_H +#define BUDDYICONTASK_H + +#include "task.h" +#include <qcstring.h> + +class Transfer; + +class BuddyIconTask : public Task +{ +Q_OBJECT +public: + BuddyIconTask( Task* parent ); + + void uploadIcon( WORD length, const QByteArray& data ); + void setReferenceNum( WORD num ); + + void requestIconFor( const QString& user ); + void setHash( const QByteArray& md5Hash ); + void setHashType( BYTE type ); + + //! Task implementation + void onGo(); + bool forMe( const Transfer* transfer ); + bool take( Transfer* transfer ); + +signals: + void haveIcon( const QString&, QByteArray ); + +private: + void sendIcon(); + void handleUploadResponse(); + void sendAIMBuddyIconRequest(); + void handleAIMBuddyIconResponse(); + void sendICQBuddyIconRequest(); + void handleICQBuddyIconResponse(); + +private: + enum Action { Send, Receive }; + Action m_action; + WORD m_iconLength; + int m_refNum; + QByteArray m_icon; + QString m_user; + QByteArray m_hash; + BYTE m_hashType; + DWORD m_seq; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/buffer.cpp b/kopete/protocols/oscar/liboscar/buffer.cpp new file mode 100644 index 00000000..b04587e7 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/buffer.cpp @@ -0,0 +1,519 @@ +/*************************************************************************** + buffer.cpp - description + ------------------- + begin : Thu Jun 6 2002 + + Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu> + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2003 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 <kdebug.h> +#include <kapplication.h> +#include "buffer.h" + +#include <ctype.h> + +Buffer::Buffer() +{ + mReadPos=0; +} + +Buffer::Buffer( const Buffer& other ) +{ + mBuffer.duplicate( other.mBuffer ); + mReadPos = other.mReadPos; +} + +Buffer::Buffer(const char *b, Q_ULONG len) +{ + mBuffer.duplicate(b, len); + mReadPos=0; +} + +Buffer::Buffer( const QByteArray& data ) +{ + mBuffer.duplicate( data ); + mReadPos = 0; +} + + +Buffer::~Buffer() +{ +} + + +int Buffer::addByte(const BYTE b) +{ + expandBuffer(1); + mBuffer[mBuffer.size()-1] = b; + + return mBuffer.size(); +} + +int Buffer::addLEByte(const BYTE b) +{ + expandBuffer(1); + mBuffer[mBuffer.size()-1] = ((b) & 0xff); + + return mBuffer.size(); +} + + +int Buffer::addWord(const WORD w) +{ + expandBuffer(2); + mBuffer[mBuffer.size()-2] = ((w & 0xff00) >> 8); + mBuffer[mBuffer.size()-1] = (w & 0x00ff); + + return mBuffer.size(); +} + +int Buffer::addLEWord(const WORD w) +{ + expandBuffer(2); + mBuffer[mBuffer.size()-2] = (unsigned char) ((w >> 0) & 0xff); + mBuffer[mBuffer.size()-1] = (unsigned char) ((w >> 8) & 0xff); + + return mBuffer.size(); +} + + +int Buffer::addDWord(const DWORD dw) +{ + expandBuffer(4); + mBuffer[mBuffer.size()-4] = (dw & 0xff000000) >> 24; + mBuffer[mBuffer.size()-3] = (dw & 0x00ff0000) >> 16; + mBuffer[mBuffer.size()-2] = (dw & 0x0000ff00) >> 8; + mBuffer[mBuffer.size()-1] = (dw & 0x000000ff); + + return mBuffer.size(); +} + +int Buffer::addLEDWord(const DWORD dw) +{ + expandBuffer(4); + mBuffer[mBuffer.size()-4] = (unsigned char) ((dw >> 0) & 0xff); + mBuffer[mBuffer.size()-3] = (unsigned char) ((dw >> 8) & 0xff); + mBuffer[mBuffer.size()-2] = (unsigned char) ((dw >> 16) & 0xff); + mBuffer[mBuffer.size()-1] = (unsigned char) ((dw >> 24) & 0xff); + + return mBuffer.size(); +} + +int Buffer::addString(QByteArray s) +{ + unsigned int pos = mBuffer.size(); + int len = s.size(); + expandBuffer(len); + + for ( int i = 0; i < len; i++ ) + mBuffer[pos + i] = s[i]; + + return mBuffer.size(); +} + +int Buffer::addString(QByteArray s, DWORD len) +{ + Q_UNUSED( len ); + return addString( s ); +} + +int Buffer::addString( const char* s, DWORD len ) +{ + QByteArray qba; + qba.duplicate( s, len ); + return addString( qba ); +} + +int Buffer::addString(const unsigned char* s, DWORD len) +{ + QByteArray qba; + qba.duplicate( (const char*) s, len ); + return addString( qba ); +} + +int Buffer::addLEString(const char *s, const DWORD len) +{ + unsigned int pos = mBuffer.size(); + expandBuffer(len); + //concatenate the new string onto the buffer + for(unsigned int i=0; i<len; i++) + { + mBuffer[pos+i]=((s[i]) & 0xff); + } + return mBuffer.size(); +} + + +void Buffer::clear() +{ + mBuffer.truncate( 0 ); + mReadPos=0; +} + +int Buffer::addTLV( const TLV& t ) +{ + return addTLV( t.type, t.length, t.data ); +} + +int Buffer::addTLV(WORD type, WORD len, const char *data) +{ + + addWord(type); + addWord(len); + return addString(data,len); +} + +int Buffer::addLETLV(WORD type, WORD len, const char *data) +{ + addLEWord( type ); + addLEWord( len ); + return addString( data, len ); +} + +BYTE Buffer::getByte() +{ + BYTE thebyte = 0x00; + + if(mReadPos < mBuffer.size()) + { + thebyte = mBuffer[mReadPos]; + mReadPos++; + } + else + kdDebug(14150) << "Buffer::getByte(): mBuffer empty" << endl; + + return thebyte; +} + +void Buffer::skipBytes( int bytesToSkip ) +{ + if (mReadPos < mBuffer.size()) + mReadPos += bytesToSkip; +} + +BYTE Buffer::getLEByte() +{ + BYTE b = getByte(); + return (b & 0xff); +} + +WORD Buffer::getWord() +{ + WORD theword, theword2, retword; + theword = getByte(); + theword2 = getByte(); + retword = (theword << 8) | theword2; + return retword; +} + +WORD Buffer::getLEWord() +{ + WORD theword1, theword2, retword; + theword1 = getLEByte(); + theword2 = getLEByte(); + retword = (theword2 << 8) | theword1; + return retword; +} + +DWORD Buffer::getDWord() +{ + DWORD word1, word2; + DWORD retdword; + word1 = getWord(); + word2 = getWord(); + retdword = (word1 << 16) | word2; + return retdword; +} + +DWORD Buffer::getLEDWord() +{ + DWORD word1, word2, retdword; + word1 = getLEWord(); + word2 = getLEWord(); + retdword = (word2 << 16) | word1; + return retdword; +} + +void Buffer::setBuf(char *b, const WORD len) +{ + kdDebug(14150) << k_funcinfo << "Called." << endl; + + mBuffer.duplicate(b, len); + mReadPos = 0; +} + +QByteArray Buffer::getBlock(WORD len) +{ + QByteArray ch( len ); + for ( int i = 0; i < len; i++ ) + { + ch[i] = getByte(); + } + + return ch; +} + +QByteArray Buffer::getBBlock(WORD len) +{ + QByteArray data; + data.duplicate(mBuffer.data() + mReadPos, len); + mReadPos += len; + return data; +} + + +WORD *Buffer::getWordBlock(WORD len) +{ + kdDebug(14150) << k_funcinfo << "of length " << len << endl; + WORD *ch=new WORD[len+1]; + for (unsigned int i=0; i<len; i++) + { + ch[i]=getWord(); + } + ch[len]=0; + return ch; +} + + +QCString Buffer::getLEBlock(WORD len) +{ + QCString ch; + for (unsigned int i=0;i<len;i++) + ch += getLEByte(); + + return ch; +} + +int Buffer::addTLV16(const WORD type, const WORD data) +{ + addWord(type); + addWord(0x0002); //2 bytes long + return addWord(data); +} + +int Buffer::addLETLV16(const WORD type, const WORD data) +{ + addLEWord(type); + addLEWord(0x0002); //2 bytes long + return addLEWord(data); +} + +int Buffer::addTLV8(const WORD type, const BYTE data) +{ + addWord(type); + addWord(0x0001); //1 byte long + return addByte(data); +} + +int Buffer::addLETLV8(const WORD type, const BYTE data) +{ + addLEWord(type); + addLEWord(0x0001); //1 byte long + return addLEByte(data); +} + +TLV Buffer::getTLV() +{ + TLV t; + if(length() >= 4) + { + t.type = getWord(); + t.length = getWord(); + if ( t ) + t.data = getBlock( t.length ); + /*else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Invalid TLV in buffer" << endl;*/ + } + + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "TLV data is " << t.data << endl; + return t; +} + +QValueList<TLV> Buffer::getTLVList() +{ + QValueList<TLV> ql; + + while (mReadPos < mBuffer.size()) + { + TLV t; + + t = getTLV(); + if ( !t ) + { + kdDebug(14150) << k_funcinfo << "Invalid TLV found" << endl; + continue; + } + + //kdDebug(14150) << k_funcinfo << "got TLV(" << t.type << ")" << endl; + ql.append(t); + } + + return ql; +} + +int Buffer::addChatTLV(const WORD type, const WORD exchange, + const QString &roomname, const WORD instance) +{ + addWord(type); + addWord(0x0005 + roomname.length()); + addWord(exchange); + addByte(roomname.length()); + addString(roomname.latin1(), roomname.length()); // TODO: check encoding + + return addWord(instance); +} + +void Buffer::expandBuffer(unsigned int inc) +{ + mBuffer.resize(mBuffer.size()+inc, QGArray::SpeedOptim); +} + +QCString Buffer::getLNTS() +{ + WORD len = getLEWord(); + QCString qcs; + qcs.duplicate( getBlock(len) ); + return qcs; +} + +QCString Buffer::getLELNTS() +{ + WORD len = getLEWord(); + QCString qcs; + qcs.duplicate( getBlock(len) ); + return qcs; +} + +int Buffer::addLNTS(const char *s) +{ + unsigned int len = strlen(s); + + addLEWord(len+1); + if(len > 0) + addString(s, len); + int ret = addByte(0x00); + return ret; +} + +int Buffer::addLELNTS(const char *s) +{ + unsigned int len = strlen(s); + int ret = addLEWord(len+1); + if(len > 0) + ret = addLEString(s, len); + ret = addByte(0x00); + return ret; +} + +int Buffer::addBSTR(const char * s) +{ + unsigned int len = strlen(s); + int ret = addWord(len); + if(len > 0) + ret = addString(s, len); + return ret; +} + +QByteArray Buffer::getBSTR() +{ + WORD len = getWord(); + QByteArray qba; + qba.duplicate( getBlock(len) ); + return qba; +} + +int Buffer::addBUIN(const char * s) +{ + unsigned int len = strlen(s); + int ret = addByte(len); + ret = addString(s, len); + return ret; +} + +QByteArray Buffer::getBUIN() +{ + BYTE len = getByte(); + QByteArray qba; + qba.duplicate( getBlock(len) ); + return qba; +} + +char *Buffer::buffer() const +{ + return mBuffer.data(); +} + +int Buffer::length() const +{ + return (mBuffer.size() - mReadPos); +} + +QString Buffer::toString() const +{ + // line format: + //00 03 00 0b 00 00 90 b8 f5 9f 09 31 31 33 37 38 |;tJ�..........| + + int i = 0; + QString output = "\n"; + QString hex, ascii; + + QByteArray::ConstIterator it; + for ( it = mBuffer.begin(); it != mBuffer.end(); ++it ) + { + i++; + + unsigned char c = static_cast<unsigned char>(*it); + + if ( c < 0x10 ) + hex.append("0"); + hex.append(QString("%1 ").arg(c, 0, 16)); + + ascii.append(isprint(c) ? c : '.'); + + if (i == 16) + { + output += hex + " |" + ascii + "|\n"; + i=0; + hex=QString::null; + ascii=QString::null; + } + } + + if(!hex.isEmpty()) + output += hex.leftJustify(48, ' ') + " |" + ascii.leftJustify(16, ' ') + '|'; + output.append('\n'); + + return output; +} + +QString Buffer::peekBSTR() +{ + int lastPos = mReadPos; + QByteArray qba = getBSTR(); + mReadPos = lastPos; + return QString( qba ); +} +QString Buffer::peekBUIN() +{ + int lastPos = mReadPos; + QByteArray qba = getBUIN(); + mReadPos = lastPos; + return QString( qba ); +} + +Buffer::operator QByteArray() const +{ + return mBuffer; +} +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/buffer.h b/kopete/protocols/oscar/liboscar/buffer.h new file mode 100644 index 00000000..900ddb50 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/buffer.h @@ -0,0 +1,268 @@ +/*************************************************************************** + buffer.h - description + ------------------- + begin : Thu Jun 6 2002 + + Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu> + Copyright (c) 2003-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. * + * * + ************************************************************************* +*/ + +#ifndef BUFFER_H +#define BUFFER_H + +#include "oscartypes.h" + +#include <qvaluelist.h> +#include <qcstring.h> + +class QString; + +using namespace Oscar; + +/** + * @brief A data buffer + */ +class Buffer +{ + public: + /** Default constructor */ + Buffer(); + Buffer( const Buffer& other ); + + /** + * \brief Create a prefilled buffer + * + * Constructor that creates a prefilled buffer of @p len length + * that contains the data from @p b. + */ + Buffer(const char *b, Q_ULONG len); + + /** + * \brief Create a prefilled buffer + * + * Constructor that creates a prefilled buffer from the QByteArray \p data + */ + Buffer( const QByteArray& data ); + + + /** Default destructor */ + ~Buffer(); + + /** + * returns the raw buffer + */ + char *buffer() const; + + /** + * Returns the remaining length of the buffer past the current read + * position. + */ + int length() const; + + /** + * adds the given string to the buffer (make sure it's NULL-terminated) + */ + int addString(QByteArray); + int addString(QByteArray, DWORD); + int addString(const char*, DWORD); + int addString(const unsigned char*, DWORD); + + /** + * Little-endian version of addString + */ + int addLEString(const char *, const DWORD); + + /** + * adds the given string to the buffer with the length in front of it + * (make sure it's NULL-terminated) + */ + int addLNTS(const char * s); + /** + * Little-endian version of addLNTS + */ + int addLELNTS(const char * s); + + /** + * adds the given DWord to the buffer + */ + int addDWord(const DWORD); + + /** + * adds the given word to the buffer + */ + int addWord(const WORD); + + /** + * adds the given word to the buffer in + * little-endian format as needed by old icq server + */ + int addLEWord(const WORD w); + + /** + * adds the given DWord to the buffer in + * little-endian format as needed by old icq server + */ + int addLEDWord(const DWORD dw); + + /** + * adds the given byte to the buffer + */ + int addByte(const BYTE); + int addLEByte(const BYTE); + + /** + * empties the current buffer. + */ + void clear(); + + /** + * Adds a TLV to the buffer + */ + int addTLV( const TLV& t ); + + /** + * Adds a TLV with the given type and data + */ + int addTLV(WORD, WORD, const char *); + + /** + * Adds a little-endian TLV with the given type and data + */ + int addLETLV(WORD, WORD, const char *); + + /** + * Returns a QString representation of the buffer + */ + QString toString() const; + + /** + * gets a DWord out of the buffer + */ + DWORD getDWord(); + + /** + * Gets a word out of the buffer + */ + WORD getWord(); + + /** + * Gets a byte out of the buffer + * It's not a constant method. It advances the buffer + * to the next BYTE after returning one. + */ + BYTE getByte(); + + /** + * Skip \p bytesToSkip number of bytes in the buffer + * Like getByte() the buffer is advanced when skipping + */ + void skipBytes( int bytesToSkip ); + + /** + * Same as above but returns little-endian + */ + WORD getLEWord(); + DWORD getLEDWord(); + BYTE getLEByte(); + + /** + * Set the buffer to the given values. + */ + void setBuf(char *, const WORD); + + /** + * Allocates memory for and gets a block of buffer bytes + */ + QByteArray getBlock(WORD len); + QByteArray getBBlock(WORD len); + + /** + * Allocates memory for and gets a block of buffer words + */ + WORD *getWordBlock(WORD len); + + /** + * Same as above but returning little-endian + */ + QCString getLEBlock(WORD len); + + /** + * Convenience function that gets a LNTS (long null terminated string) + * from the buffer. Otherwise you'd need a getWord() + getBlock() call :) + */ + QCString getLNTS(); + QCString getLELNTS(); + + /** + * adds a 16-bit long TLV + */ + int addTLV16(const WORD type, const WORD data); + + /** + * adds a 16-bit long little-endian TLV + */ + int addLETLV16(const WORD type, const WORD data); + + /** + * adds the given byte to a TLV + */ + int addTLV8(const WORD type, const BYTE data); + + /** + * adds the given byte to a little-endian TLV + */ + int addLETLV8(const WORD type, const BYTE data); + + /** + * Gets a TLV, storing it in a struct and returning it + */ + TLV getTLV(); + + /** + * Gets a list of TLV's + */ + QValueList<TLV> getTLVList(); + + /** + * Creates a chat data segment for a tlv and calls addTLV with that data + */ + int addChatTLV(const WORD, const WORD, const QString &, const WORD); + + /** + * Similar to the LNTS functions but string is NOT null-terminated + */ + int addBSTR(const char * s); + QByteArray getBSTR(); + QString peekBSTR(); + + int addBUIN(const char * s); + QByteArray getBUIN(); + QString peekBUIN(); + + operator QByteArray() const; + + private: + /** + * Make the buffer bigger by @p inc bytes + */ + void expandBuffer(unsigned int inc); + + private: + QByteArray mBuffer; + unsigned int mReadPos; + +}; + +#endif +// kate: tab-width 4; indent-mode csands; +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/protocols/oscar/liboscar/bytestream.cpp b/kopete/protocols/oscar/liboscar/bytestream.cpp new file mode 100644 index 00000000..7faa803b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/bytestream.cpp @@ -0,0 +1,270 @@ +/* + * bytestream.cpp - base class for bytestreams + * Copyright (C) 2003 Justin Karneges + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include"bytestream.h" + +// CS_NAMESPACE_BEGIN + +//! \class ByteStream bytestream.h +//! \brief Base class for "bytestreams" +//! +//! This class provides a basic framework for a "bytestream", here defined +//! as a bi-directional, asynchronous pipe of data. It can be used to create +//! several different kinds of bytestream-applications, such as a console or +//! TCP connection, or something more abstract like a security layer or tunnel, +//! all with the same interface. The provided functions make creating such +//! classes simpler. ByteStream is a pure-virtual class, so you do not use it +//! on its own, but instead through a subclass such as \a BSocket. +//! +//! The signals connectionClosed(), delayedCloseFinished(), readyRead(), +//! bytesWritten(), and error() serve the exact same function as those from +//! <A HREF="http://doc.trolltech.com/3.1/qsocket.html">QSocket</A>. +//! +//! The simplest way to create a ByteStream is to reimplement isOpen(), close(), +//! and tryWrite(). Call appendRead() whenever you want to make data available for +//! reading. ByteStream will take care of the buffers with regards to the caller, +//! and will call tryWrite() when the write buffer gains data. It will be your +//! job to call tryWrite() whenever it is acceptable to write more data to +//! the underlying system. +//! +//! If you need more advanced control, reimplement read(), write(), bytesAvailable(), +//! and/or bytesToWrite() as necessary. +//! +//! Use appendRead(), appendWrite(), takeRead(), and takeWrite() to modify the +//! buffers. If you have more advanced requirements, the buffers can be accessed +//! directly with readBuf() and writeBuf(). +//! +//! Also available are the static convenience functions ByteStream::appendArray() +//! and ByteStream::takeArray(), which make dealing with byte queues very easy. + +class ByteStream::Private +{ +public: + Private() {} + + QByteArray readBuf, writeBuf; +}; + +//! +//! Constructs a ByteStream object with parent \a parent. +ByteStream::ByteStream(QObject *parent) +:QObject(parent) +{ + d = new Private; +} + +//! +//! Destroys the object and frees allocated resources. +ByteStream::~ByteStream() +{ + delete d; +} + +//! +//! Returns TRUE if the stream is open, meaning that you can write to it. +bool ByteStream::isOpen() const +{ + return false; +} + +//! +//! Closes the stream. If there is data in the write buffer then it will be +//! written before actually closing the stream. Once all data has been written, +//! the delayedCloseFinished() signal will be emitted. +//! \sa delayedCloseFinished() +void ByteStream::close() +{ +} + +//! +//! Writes array \a a to the stream. +void ByteStream::write(const QByteArray &a) +{ + if(!isOpen()) + return; + + bool doWrite = bytesToWrite() == 0 ? true: false; + appendWrite(a); + if(doWrite) + tryWrite(); +} + +//! +//! Reads bytes \a bytes of data from the stream and returns them as an array. If \a bytes is 0, then +//! \a read will return all available data. +QByteArray ByteStream::read(int bytes) +{ + return takeRead(bytes); +} + +//! +//! Returns the number of bytes available for reading. +int ByteStream::bytesAvailable() const +{ + return d->readBuf.size(); +} + +//! +//! Returns the number of bytes that are waiting to be written. +int ByteStream::bytesToWrite() const +{ + return d->writeBuf.size(); +} + +//! +//! Writes string \a cs to the stream. +void ByteStream::write(const QCString &cs) +{ + QByteArray block(cs.length()); + memcpy(block.data(), cs.data(), block.size()); + write(block); +} + +//! +//! Clears the read buffer. +void ByteStream::clearReadBuffer() +{ + d->readBuf.resize(0); +} + +//! +//! Clears the write buffer. +void ByteStream::clearWriteBuffer() +{ + d->writeBuf.resize(0); +} + +//! +//! Appends \a block to the end of the read buffer. +void ByteStream::appendRead(const QByteArray &block) +{ + appendArray(&d->readBuf, block); +} + +//! +//! Appends \a block to the end of the write buffer. +void ByteStream::appendWrite(const QByteArray &block) +{ + appendArray(&d->writeBuf, block); +} + +//! +//! Returns \a size bytes from the start of the read buffer. +//! If \a size is 0, then all available data will be returned. +//! If \a del is TRUE, then the bytes are also removed. +QByteArray ByteStream::takeRead(int size, bool del) +{ + return takeArray(&d->readBuf, size, del); +} + +//! +//! Returns \a size bytes from the start of the write buffer. +//! If \a size is 0, then all available data will be returned. +//! If \a del is TRUE, then the bytes are also removed. +QByteArray ByteStream::takeWrite(int size, bool del) +{ + return takeArray(&d->writeBuf, size, del); +} + +//! +//! Returns a reference to the read buffer. +QByteArray & ByteStream::readBuf() +{ + return d->readBuf; +} + +//! +//! Returns a reference to the write buffer. +QByteArray & ByteStream::writeBuf() +{ + return d->writeBuf; +} + +//! +//! Attempts to try and write some bytes from the write buffer, and returns the number +//! successfully written or -1 on error. The default implementation returns -1. +int ByteStream::tryWrite() +{ + return -1; +} + +//! +//! Append array \a b to the end of the array pointed to by \a a. +void ByteStream::appendArray(QByteArray *a, const QByteArray &b) +{ + int oldsize = a->size(); + a->resize(oldsize + b.size()); + memcpy(a->data() + oldsize, b.data(), b.size()); +} + +//! +//! Returns \a size bytes from the start of the array pointed to by \a from. +//! If \a size is 0, then all available data will be returned. +//! If \a del is TRUE, then the bytes are also removed. +QByteArray ByteStream::takeArray(QByteArray *from, int size, bool del) +{ + QByteArray a; + if(size == 0) { + a = from->copy(); + if(del) + from->resize(0); + } + else { + if(size > (int)from->size()) + size = from->size(); + a.resize(size); + char *r = from->data(); + memcpy(a.data(), r, size); + if(del) { + int newsize = from->size()-size; + memmove(r, r+size, newsize); + from->resize(newsize); + } + } + return a; +} +/* + void connectionClosed(); + void delayedCloseFinished(); + void readyRead(); + void bytesWritten(int); + void error(int); + +//! \fn void ByteStream::connectionClosed() +//! This signal is emitted when the remote end of the stream closes. + +//! \fn void ByteStream::delayedCloseFinished() +//! This signal is emitted when all pending data has been written to the stream +//! after an attempt to close. + +//! \fn void ByteStream::readyRead() +//! This signal is emitted when data is available to be read. + +//! \fn void ByteStream::bytesWritten(int x); +//! This signal is emitted when data has been successfully written to the stream. +//! \a x is the number of bytes written. + +//! \fn void ByteStream::error(int code) +//! This signal is emitted when an error occurs in the stream. The reason for +//! error is indicated by \a code. +*/ +// CS_NAMESPACE_END + +#include "bytestream.moc" diff --git a/kopete/protocols/oscar/liboscar/bytestream.h b/kopete/protocols/oscar/liboscar/bytestream.h new file mode 100644 index 00000000..7f964fbd --- /dev/null +++ b/kopete/protocols/oscar/liboscar/bytestream.h @@ -0,0 +1,78 @@ +/* + * bytestream.h - base class for bytestreams + * Copyright (C) 2003 Justin Karneges + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef CS_BYTESTREAM_H +#define CS_BYTESTREAM_H + +#include <qobject.h> +#include <qcstring.h> + +// CS_NAMESPACE_BEGIN + +// CS_EXPORT_BEGIN +class ByteStream : public QObject +{ + Q_OBJECT +public: + enum Error { ErrRead, ErrWrite, ErrCustom = 10 }; + ByteStream(QObject *parent=0); + virtual ~ByteStream()=0; + + virtual bool isOpen() const; + virtual void close(); + virtual void write(const QByteArray &); + virtual QByteArray read(int bytes=0); + virtual int bytesAvailable() const; + virtual int bytesToWrite() const; + + void write(const QCString &); + + static void appendArray(QByteArray *a, const QByteArray &b); + static QByteArray takeArray(QByteArray *from, int size=0, bool del=true); + +signals: + void connectionClosed(); + void delayedCloseFinished(); + void readyRead(); + void bytesWritten(int); + void error(int); + +protected: + void clearReadBuffer(); + void clearWriteBuffer(); + void appendRead(const QByteArray &); + void appendWrite(const QByteArray &); + QByteArray takeRead(int size=0, bool del=true); + QByteArray takeWrite(int size=0, bool del=true); + QByteArray & readBuf(); + QByteArray & writeBuf(); + virtual int tryWrite(); + +private: +//! \if _hide_doc_ + class Private; + Private *d; +//! \endif +}; +// CS_EXPORT_END + +// CS_NAMESPACE_END + +#endif diff --git a/kopete/protocols/oscar/liboscar/changevisibilitytask.cpp b/kopete/protocols/oscar/liboscar/changevisibilitytask.cpp new file mode 100644 index 00000000..5cb44720 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/changevisibilitytask.cpp @@ -0,0 +1,150 @@ +/* + Kopete Oscar Protocol + changevisibilitytask.cpp - Changes the visibility of the account via SSI + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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 "changevisibilitytask.h" + +#include <qvaluelist.h> +#include <kdebug.h> +#include "buffer.h" +#include "client.h" +#include "connection.h" +#include "oscartypeclasses.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "ssimanager.h" +#include "transfer.h" + + +ChangeVisibilityTask::ChangeVisibilityTask(Task* parent): Task(parent) +{ + m_sequence = 0; + m_visible = true; +} + + +ChangeVisibilityTask::~ChangeVisibilityTask() +{ +} + +void ChangeVisibilityTask::setVisible( bool visible ) +{ + m_visible = visible; +} + +bool ChangeVisibilityTask::forMe(const Transfer* transfer) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + SNAC s = st->snac(); //cheat + if ( s.family == 0x0013 && s.subtype == 0x000E ) + return true; + else + return false; +} + +bool ChangeVisibilityTask::take(Transfer* transfer) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + setSuccess( 0, QString::null ); + setTransfer( 0 ); + return true; + } + else + { + setError( 0, QString::null ); + return false; + } +} + +void ChangeVisibilityTask::onGo() +{ + SSIManager* manager = client()->ssiManager(); + Oscar::SSI item = manager->visibilityItem(); + if ( !item ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Didn't find a visibility item" << endl; + setError( 0, QString::null ); + return; + } + + Buffer c8tlv; + BYTE visibleByte = m_visible ? 0x04 : 0x03; + c8tlv.addByte( visibleByte ); + + QValueList<Oscar::TLV> tList; + tList.append( TLV( 0x00CA, c8tlv.length(), c8tlv.buffer() ) ); + + Oscar::SSI newSSI(item); + if ( Oscar::uptateTLVs( newSSI, tList ) == false ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Visibility didn't change, don't update" << endl; + setSuccess( 0, QString::null ); + return; + } + + //remove the old item and add the new item indicating the + //change in visibility. + manager->removeItem( item ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found visibility item. changing setting" << endl; + manager->newItem( newSSI ); + sendEditStart(); + + Buffer* b = new Buffer(); + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0013, 0x0009, 0x0000, client()->snacSequence() }; + m_sequence = s.id; + b->addWord( 0 ); + b->addWord( newSSI.gid() ); + b->addWord( newSSI.bid() ); + b->addWord( newSSI.type() ); + b->addWord( newSSI.tlvListLength() ); + + QValueList<TLV>::const_iterator it2 = newSSI.tlvList().begin(); + QValueList<TLV>::const_iterator listEnd2 = newSSI.tlvList().end(); + for( ; it2 != listEnd2; ++it2 ) + b->addTLV( ( *it2 ) ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending visibility update" << endl; + Transfer* t = createTransfer( f, s, b ); + send( t ); + sendEditEnd(); +} + +void ChangeVisibilityTask::sendEditStart() +{ + SNAC editStartSnac = { 0x0013, 0x0011, 0x0000, client()->snacSequence() }; + FLAP editStart = { 0x02, 0, 0 }; + Buffer* emptyBuffer = new Buffer; + Transfer* t1 = createTransfer( editStart, editStartSnac, emptyBuffer ); + send( t1 ); +} + +void ChangeVisibilityTask::sendEditEnd() +{ + SNAC editEndSnac = { 0x0013, 0x0012, 0x0000, client()->snacSequence() }; + FLAP editEnd = { 0x02, 0, 0 }; + Buffer* emptyBuffer = new Buffer; + Transfer *t5 = createTransfer( editEnd, editEndSnac, emptyBuffer ); + send( t5 ); +} + +//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4; diff --git a/kopete/protocols/oscar/liboscar/changevisibilitytask.h b/kopete/protocols/oscar/liboscar/changevisibilitytask.h new file mode 100644 index 00000000..0ec5ab04 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/changevisibilitytask.h @@ -0,0 +1,58 @@ +/* + Kopete Oscar Protocol + changevisibilitytask.h - Changes the visibility of the account via SSI + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef CHANGEVISIBILITYTASK_H +#define CHANGEVISIBILITYTASK_H + +#include "task.h" + +/** + * This class provides a way to change how the account user + * appears on everybody else's contact list. It is used to + * implement the invisible online status in ICQ and AIM + * @author Matt Rogers + */ +class ChangeVisibilityTask : public Task +{ +public: + ChangeVisibilityTask( Task* parent ); + ~ChangeVisibilityTask(); + + void setVisible( bool visible = true ); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + +private: + //damnit, this is ugly. time to refactor SSI stuff out into it's own + //class, file, whatever. + //! Send the SSI edit start packet + void sendEditStart(); + + //! Send the SSI edit end packet + void sendEditEnd(); + +private: + bool m_visible; + DWORD m_sequence; +}; + +#endif + +//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4; diff --git a/kopete/protocols/oscar/liboscar/chatnavservicetask.cpp b/kopete/protocols/oscar/liboscar/chatnavservicetask.cpp new file mode 100644 index 00000000..f661d1f4 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/chatnavservicetask.cpp @@ -0,0 +1,355 @@ +/* + Kopete Oscar Protocol - Chat Navigation service handlers + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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 "chatnavservicetask.h" + +#include <kdebug.h> + +#include "transfer.h" +#include "buffer.h" +#include "task.h" +#include "client.h" +#include "connection.h" + + +ChatNavServiceTask::ChatNavServiceTask( Task* parent ) : Task( parent ) +{ + m_type = Limits; +} + + +ChatNavServiceTask::~ChatNavServiceTask() +{ +} + +void ChatNavServiceTask::setRequestType( RequestType rt ) +{ + m_type = rt; +} + +ChatNavServiceTask::RequestType ChatNavServiceTask::requestType() +{ + return m_type; +} + +QValueList<int> ChatNavServiceTask::exchangeList() const +{ + return m_exchanges; +} + +bool ChatNavServiceTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + if ( st->snacService() == 0x000D && st->snacSubtype() == 0x0009 ) + return true; + + return false; +} + +bool ChatNavServiceTask::take( Transfer* transfer ) +{ + if ( !forMe( transfer ) ) + return false; + + setTransfer( transfer ); + Buffer* b = transfer->buffer(); + while ( b->length() > 0 ) + { + TLV t = b->getTLV(); + switch ( t.type ) + { + case 0x0001: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got chat redirect TLV" << endl; + break; + case 0x0002: + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got max concurrent rooms TLV" << endl; + Buffer tlvTwo(t.data); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "max concurrent rooms is " << tlvTwo.getByte() << endl; + break; + } + case 0x0003: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "exchange info TLV found" << endl; + handleExchangeInfo( t ); + //set the exchanges for the client + emit haveChatExchanges( m_exchanges ); + break; + case 0x0004: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "room info TLV found" << endl; + handleBasicRoomInfo( t ); + break; + }; + } + + + setSuccess( 0, QString::null ); + setTransfer( 0 ); + return true; + +} + +void ChatNavServiceTask::onGo() +{ + FLAP f = { 0x02, 0, 0x00 }; + SNAC s = { 0x000D, m_type, 0x0000, client()->snacSequence() }; + Buffer* b = new Buffer(); + + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +void ChatNavServiceTask::createRoom( WORD exchange, const QString& name ) +{ + //most of this comes from gaim. thanks to them for figuring it out + QString cookie = "create"; //hardcoded, seems to be ignored by AOL + QString lang = "en"; + QString charset = "us-ascii"; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x000D, 0x0008, 0x0000, client()->snacSequence() }; + Buffer *b = new Buffer; + + b->addWord( exchange ); + b->addBUIN( cookie.latin1() ); + b->addWord( 0xFFFF ); //assign the last instance + b->addByte( 0x01 ); //detail level + + //just send three TLVs + b->addWord( 0x0003 ); + + //i'm lazy, add TLVs manually + + b->addWord( 0x00D3 ); //type of 0x00D3 - name + b->addWord( name.length() ); + b->addString( name.latin1(), name.length() ); + + b->addWord( 0x00D6 ); //type of 0x00D6 - charset + b->addWord( charset.length() ); + b->addString( charset.latin1(), charset.length() ); + + b->addWord( 0x00D7 ); //type of 0x00D7 - lang + b->addWord( lang.length() ); + b->addString( lang.latin1(), lang.length() ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending join room packet" << endl; + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + + +void ChatNavServiceTask::handleExchangeInfo( const TLV& t ) +{ + kdDebug(OSCAR_RAW_DEBUG) << "Parsing exchange info TLV" << endl; + Buffer b(t.data); + ChatExchangeInfo exchangeInfo; + + exchangeInfo.number = b.getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "exchange id is: " << exchangeInfo.number << endl; + b.getWord(); + while ( b.length() > 0 ) + { + TLV t = b.getTLV(); + Buffer tmp = t.data; + switch (t.type) + { + case 0x02: + //kdDebug(OSCAR_RAW_DEBUG) << "user class is " << t.data << endl; + break; + case 0x03: + exchangeInfo.maxRooms = tmp.getWord(); + kdDebug(OSCAR_RAW_DEBUG) << "max concurrent rooms for the exchange is " << t.data << endl; + break; + case 0x04: + exchangeInfo.maxRoomNameLength = tmp.getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "max room name length is " << exchangeInfo.maxRoomNameLength << endl; + break; + case 0x05: + //kdDebug(OSCAR_RAW_DEBUG) << "received root rooms info" << endl; + break; + case 0x06: + //kdDebug(OSCAR_RAW_DEBUG) << "received search tags" << endl; + break; + case 0xCA: + //kdDebug(OSCAR_RAW_DEBUG) << "have exchange creation time" << endl; + break; + case 0xC9: + //kdDebug(OSCAR_RAW_DEBUG) << "got chat flag" << endl; + break; + case 0xD0: + //kdDebug(OSCAR_RAW_DEBUG) << "got mandantory channels" << endl; + break; + case 0xD1: + exchangeInfo.maxMsgLength = tmp.getWord(); + kdDebug(OSCAR_RAW_DEBUG) << "max message length" << t.data << endl; + break; + case 0xD2: + kdDebug(OSCAR_RAW_DEBUG) << "max occupancy" << t.data << endl; + break; + case 0xD3: + { + QString eName( t.data ); + kdDebug(OSCAR_RAW_DEBUG) << "exchange name: " << eName << endl; + exchangeInfo.description = eName; + break; + } + case 0xD4: + //kdDebug(OSCAR_RAW_DEBUG) << "got optional channels" << endl; + break; + case 0xD5: + exchangeInfo.canCreate = tmp.getByte(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "creation permissions " << exchangeInfo.canCreate << endl; + break; + default: + kdDebug(OSCAR_RAW_DEBUG) << "unknown TLV type " << t.type << endl; + break; + } + } + m_exchanges.append( exchangeInfo.number ); +} + +void ChatNavServiceTask::handleBasicRoomInfo( const TLV& t ) +{ + kdDebug(OSCAR_RAW_DEBUG) << "Parsing room info TLV" << t.length << endl; + Buffer b(t.data); + WORD exchange = b.getWord(); + QByteArray cookie( b.getBlock( b.getByte() ) ); + WORD instance = b.getWord(); + b.getByte(); //detail level, which i'm not sure we need + WORD tlvCount = b.getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "e: " << exchange + << " c: " << cookie << " i: " << instance << endl; + + QValueList<Oscar::TLV> tlvList = b.getTLVList(); + QValueList<Oscar::TLV>::iterator it, itEnd = tlvList.end(); + QString roomName; + for ( it = tlvList.begin(); it != itEnd; ++it ) + { + TLV t = ( *it ); + switch (t.type) + { + case 0x66: + kdDebug(OSCAR_RAW_DEBUG) << "user class is " << t.data << endl; + break; + case 0x67: + kdDebug(OSCAR_RAW_DEBUG) << "user array" << endl; + break; + case 0x68: + kdDebug(OSCAR_RAW_DEBUG) << "evil generated" << t.data << endl; + break; + case 0x69: + kdDebug(OSCAR_RAW_DEBUG) << "evil generated array" << endl; + break; + case 0x6A: + roomName = QString( t.data ); + kdDebug(OSCAR_RAW_DEBUG) << "fully qualified name" << roomName << endl; + break; + case 0x6B: + kdDebug(OSCAR_RAW_DEBUG) << "moderator" << endl; + break; + case 0x6D: + kdDebug(OSCAR_RAW_DEBUG) << "num children" << endl; + break; + case 0x06F: + kdDebug(OSCAR_RAW_DEBUG) << "occupancy" << endl; + break; + case 0x71: + kdDebug(OSCAR_RAW_DEBUG) << "occupant evil" << endl; + break; + case 0x75: + kdDebug(OSCAR_RAW_DEBUG) << "room activity" << endl; + break; + case 0xD0: + kdDebug(OSCAR_RAW_DEBUG) << "got mandantory channels" << endl; + break; + case 0xD1: + kdDebug(OSCAR_RAW_DEBUG) << "max message length" << t.data << endl; + break; + case 0xD2: + kdDebug(OSCAR_RAW_DEBUG) << "max occupancy" << t.data << endl; + break; + case 0xD3: + kdDebug(OSCAR_RAW_DEBUG) << "exchange name" << endl; + break; + case 0xD4: + kdDebug(OSCAR_RAW_DEBUG) << "got optional channels" << endl; + break; + case 0xD5: + kdDebug(OSCAR_RAW_DEBUG) << "creation permissions " << t.data << endl; + break; + default: + kdDebug(OSCAR_RAW_DEBUG) << "unknown TLV type " << t.type << endl; + break; + } + } + + emit connectChat( exchange, cookie, instance, roomName ); +} + +void ChatNavServiceTask::handleCreateRoomInfo( const TLV& t ) +{ + Buffer b( t.data ); + WORD exchange = b.getWord(); + WORD cookieLength = b.getByte(); + QByteArray cookie( b.getBlock( cookieLength ) ); + WORD instance = b.getWord(); + BYTE detailLevel = b.getByte(); + + if ( detailLevel != 0x02 ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "unknown detail level in response" << endl; + return; + } + + WORD numberTlvs = b.getWord(); + QValueList<Oscar::TLV> roomTLVList = b.getTLVList(); + QValueList<Oscar::TLV>::iterator itEnd = roomTLVList.end(); + for ( QValueList<Oscar::TLV>::iterator it = roomTLVList.begin(); + it != itEnd; ++ it ) + { + switch( ( *it ).type ) + { + case 0x006A: + { + QString fqcn = QString( ( *it ).data ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "fqcn: " << fqcn << endl; + break; + } + case 0x00C9: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "flags: " << t.data << endl; + break; + case 0x00CA: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "create time: " << t.data << endl; + break; + case 0x00D1: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "max msg len: " << t.data << endl; + break; + case 0x00D2: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "max occupancy: " << t.data << endl; + break; + case 0x00D3: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "name: " << QString( t.data ) << endl; + break; + case 0x00D5: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "create perms: " << t.data << endl; + break; + }; + } +} + +#include "chatnavservicetask.moc" +//kate: indent-mode csands; tab-width 4; diff --git a/kopete/protocols/oscar/liboscar/chatnavservicetask.h b/kopete/protocols/oscar/liboscar/chatnavservicetask.h new file mode 100644 index 00000000..6b7d8764 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/chatnavservicetask.h @@ -0,0 +1,67 @@ +/* + Kopete Oscar Protocol - Chat Navigation service handlers + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef CHATNAVSERVICETASK_H +#define CHATNAVSERVICETASK_H + +#include "task.h" + +#include <qvaluelist.h> +#include <oscartypes.h> + +class Transfer; + +/** + * @author Matt Rogers + */ +class ChatNavServiceTask : public Task +{ +Q_OBJECT +public: + ChatNavServiceTask( Task* parent ); + ~ChatNavServiceTask(); + + enum RequestType { Limits = 0x0002, Exchange, Room, ExtRoom, Members, + Search, Create }; + + void setRequestType( RequestType ); + RequestType requestType(); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + void createRoom( WORD exchange, const QString& name ); //create a room. sends the packet as well + + QValueList<int> exchangeList() const; + +signals: + void haveChatExchanges( const QValueList<int>& ); + void connectChat( WORD, QByteArray, WORD, const QString& ); + +private: + void handleExchangeInfo( const TLV& t ); + void handleBasicRoomInfo( const TLV& t ); + void handleCreateRoomInfo( const TLV& t ); + +private: + QValueList<int> m_exchanges; + RequestType m_type; +}; + +#endif + +//kate: indent-mode csands; tab-width 4; + diff --git a/kopete/protocols/oscar/liboscar/chatservicetask.cpp b/kopete/protocols/oscar/liboscar/chatservicetask.cpp new file mode 100644 index 00000000..9d07afe8 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/chatservicetask.cpp @@ -0,0 +1,359 @@ +// 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" + diff --git a/kopete/protocols/oscar/liboscar/chatservicetask.h b/kopete/protocols/oscar/liboscar/chatservicetask.h new file mode 100644 index 00000000..90e29300 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/chatservicetask.h @@ -0,0 +1,65 @@ +// Kopete Oscar Protocol - Chat service handling + +// 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 + +#ifndef CHATSERVICETASK_H +#define CHATSERVICETASK_H + +#include "task.h" +#include "oscarmessage.h" + +class Transfer; + +class ChatServiceTask : public Task +{ +Q_OBJECT +public: + ChatServiceTask( Task* parent, Oscar::WORD exchange, const QString& room ); + ~ChatServiceTask(); + + void onGo(); + bool take( Transfer* t ); + + void parseRoomInfo(); + + void parseJoinNotification(); + void parseLeftNotification(); + + void parseChatMessage(); + void parseChatError(); + + void setMessage( const Oscar::Message& msg ); + void setEncoding( const QCString &enc ); + +signals: + void userJoinedChat( Oscar::WORD, const QString& r, const QString& u ); + void userLeftChat( Oscar::WORD, const QString& r, const QString& u ); + void newChatMessage( const Oscar::Message& msg ); + +protected: + bool forMe( const Transfer* t ) const; + +private: + WORD m_exchange; + QString m_room; + QString m_internalRoom; + Oscar::Message m_message; + QCString m_encoding; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/client.cpp b/kopete/protocols/oscar/liboscar/client.cpp new file mode 100644 index 00000000..af967512 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/client.cpp @@ -0,0 +1,1353 @@ +/* + 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; diff --git a/kopete/protocols/oscar/liboscar/client.h b/kopete/protocols/oscar/liboscar/client.h new file mode 100644 index 00000000..f5dc531e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/client.h @@ -0,0 +1,521 @@ +/* + Kopete Oscar Protocol + client.h - The main interface for the Oscar protocol + + Copyright (c) 2004-2005 by 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. * + * * + ************************************************************************* +*/ + + +#ifndef LIBOSCAR_CLIENT_H +#define LIBOSCAR_CLIENT_H + +#include <qobject.h> +#include <qstring.h> +#include "kopete_export.h" +#include "rtf2html.h" +#include "transfer.h" +#include "icquserinfo.h" +#include "userdetails.h" +#include "oscartypeclasses.h" +#include "oscarmessage.h" + +class Connection; +class StageOneLoginTask; +class StageTwoLoginTask; +class SSIManager; +class UserDetails; +class QString; +class Task; +class QTextCodec; + +namespace Oscar +{ +class Settings; +} + +class KOPETE_EXPORT Client : public QObject +{ +Q_OBJECT + +public: + + class CodecProvider { + public: + virtual ~CodecProvider() {} + virtual QTextCodec* codecForContact( const QString& contactName ) const = 0; + virtual QTextCodec* codecForAccount() const = 0; + }; + + enum ErrorCodes { + NoError = 0, + NotConnectedError = 1, + NonFatalProtocolError = 2, + FatalProtocolError = 3 + }; + + enum AIMStatus { Online = 0, Away }; + enum ICQStatus { ICQOnline = 0, ICQAway, ICQNotAvailable, ICQOccupied, ICQDoNotDisturb, ICQFreeForChat }; + + /************* + EXTERNAL API + *************/ + + Client(QObject *parent=0); + ~Client(); + + /** + * Get the settings object for this client instance + */ + Oscar::Settings* clientSettings() const; + + /** + * Start a connection to the server using the supplied @ref ClientStream. + * This is only a transport layer connection. + * @param s initialised connection object to use for the connection. + * @param server the server to connect to - but this is also set on the connector used to construct the clientstream?? + * @param auth indicate whether we're connecting to the authorizer or the bos server + */ + void connectToServer( Connection *c, const QString& server, bool auth = true ); + + /** + * Start the login process for Oscar + * @param host - probably could obtain this back from the connector - used for outgoing tasks to determine destination + * @param user The user name to log in as. + * @param pass The password to use when logging in + */ + void start( const QString &host, const uint port, const QString &userId, const QString &pass ); + + /** Logout and disconnect */ + void close(); + /** Set our status for AIM */ + void setStatus( AIMStatus status, const QString &message = QString::null ); + /** Set our status for ICQ */ + void setStatus( DWORD status, const QString &message = QString::null ); + + /** Retrieve our user info */ + UserDetails ourInfo() const; + + /** + * Remove a group to the contact list + * \param groupName the name of the group to remove + * \return true if the group removal was successful + */ + void removeGroup( const QString& groupName ); + + /** + * Add a group from the contact list + * \param groupName the name of the group to add + * \return true if the group addition was successful + */ + void addGroup( const QString& groupName ); + + /** + * Add a contact to the contact list + * \param contactName the screen name of the new contact to add + * \return true if the contact addition was successful + */ + void addContact( const QString& contactName, const QString& groupName ); + + /** + * Remove a contact from the contact list + * \param contactName the screen name of the contact to remove + * \return true if the contact removal was successful + */ + void removeContact( const QString &contactName ); + + /** + * Rename a group on the contact list + * \param oldGroupName the old group name + * \param newGroupName the new group name + */ + void renameGroup( const QString& oldGroupName, const QString& newGroupName ); + + /** + * Modify an SSI item on the SSI list + * \param item the item to send to the server + */ + void modifySSIItem( const Oscar::SSI& oldItem, const Oscar::SSI& newItem ); + + /** + * Change a contact's group on the server + * \param contact the contact to change + * \param newGroup the new group to move the contact to + */ + void changeContactGroup( const QString& contact, const QString& newGroupName ); + + /** + * Send a message to a contact + * \param msg the message to be sent + * \param auto the message is an autoresponse message, default to false + */ + void sendMessage( const Oscar::Message& msg, bool isAuto = false ); + + /** + * Request authorization from a contact + * \param contactid the id of the contact to request auth from + * \param reason the reason for this authorization request + */ + void requestAuth( const QString& contactid, const QString& reason ); + + /** + * Grant or decline authorization to a contact + * \param contactid the id of the contact to grant/decline authorization + * \param reason the reason to grant/decline authorization + * \param auth grant or decline authorization + */ + void sendAuth( const QString& contactid, const QString& reason, bool auth=true ); + + /** + * Request full user info from an ICQ contact + * \param contactId the UIN of the contact to get info for + */ + void requestFullInfo( const QString& contactId ); + + /** + * Request short info for an ICQ contact + * \param contactId the UIN of the contact to get info for + */ + void requestShortInfo( const QString& contactId ); + + /** + * Send a warning to the OSCAR servers about a contact + * \param contact the contact to send the warning to + * \param anon indicate whether to do it anonymously + */ + void sendWarning( const QString& contact, bool anonymous ); + + /** + * Get the general ICQ info for a client + * \param contact the contact to get info for + */ + ICQGeneralUserInfo getGeneralInfo( const QString& contact ); + + /** + * Get the work info for a contact + * \param contact the contact to get info for + */ + ICQWorkUserInfo getWorkInfo( const QString& contact ); + + /** + * Get the email info for a contact + * \param contact the contact to get info for + */ + ICQEmailInfo getEmailInfo( const QString& contact ); + + /** + * Get the additional info available for a contact + * \param contact the contact to get info for + */ + ICQMoreUserInfo getMoreInfo( const QString& contact ); + + /** + * Get the interest info available for a contact + * \param contact the contact to get info for + */ + ICQInterestInfo getInterestInfo( const QString& contact ); + + /** + * Get the short info available for an icq contact + * \param contact the contact to get info for + */ + ICQShortInfo getShortInfo( const QString& contact ); + + /** + * Get the list of chat room exchanges we have + */ + QValueList<int> chatExchangeList() const; + + /** + * Request the aim profile + * \param contact the contact to get info for + */ + void requestAIMProfile( const QString& contact ); + + /** + * Request the aim away message + * \param contact the contact to get info for + */ + void requestAIMAwayMessage( const QString& contact ); + + /** + * Add the icq away message request to queue + * \param contact the contact to get info for + */ + void addICQAwayMessageRequest( const QString& contact, ICQStatus contactStatus ); + + /** + * Remove the icq away message request from queue + * \param contact the contact to get info for + */ + void removeICQAwayMessageRequest( const QString& contact ); + + /** Request the extended status info */ + void requestStatusInfo( const QString& contact ); + + //! Run a whitepages search + void whitePagesSearch( const ICQWPSearchInfo& info ); + + //! Run a UIN search + void uinSearch( const QString& uin ); + + //! Update the user's AIM profile + void updateProfile( const QString& profile ); + + //! Get buddy icon information for a person + void requestBuddyIcon( const QString& user, const QByteArray& hash, BYTE hashType ); + + //! Start a server redirect for a different service + void requestServerRedirect( WORD family, WORD e = 0, QByteArray c = QByteArray(), + WORD instance = 0, const QString& room = QString::null ); + + //! Start uploading a buddy icon + void sendBuddyIcon( const QByteArray& imageData ); + + void joinChatRoom( const QString& roomName, int exchange ); + + void setIgnore( const QString& user, bool ignore ); + + void setVisibleTo( const QString& user, bool visible ); + + void setInvisibleTo( const QString& user, bool invisible ); + + /** Accessors needed for login */ + QString host(); + int port(); + + /** Send a typing notification */ + void sendTyping( const QString & contact, bool typing ); + + /** Make a connection to the icon server */ + void connectToIconServer(); + + bool hasIconConnection() const; + + /** We've finished chatting in a chat room, disconnect from it */ + void disconnectChatRoom( WORD exchange, const QString& room ); + + /** Set codec provider */ + void setCodecProvider( CodecProvider* codecProvider ); + + /** Set pointer to version info */ + void setVersion( const Oscar::ClientVersion* version ); + + /************* + INTERNAL (FOR USE BY TASKS OR CONNECTIONS) METHODS + *************/ + /** + * Print a debug statement + */ + void debug( const QString &str ); + + /** Have we logged in yet? */ + bool isActive() const; + + /** Accessor for the SSI Manager */ + SSIManager* ssiManager() const; + + /** Return version info */ + const Oscar::ClientVersion* version() const; + + /** The current user's user ID */ + QString userId() const; + + /** The current user's password */ + QString password() const; + + /** The current status message (a.k.a. away message) */ + QString statusMessage() const; + + /** Change the current status message w/o changing status */ + void setStatusMessage( const QString &message ); + + /** ICQ Settings */ + bool isIcq() const; + void setIsIcq( bool isIcq ); + + /** Host's IP address */ + QCString ipAddress() const; + + /** Notify that a task error was received */ + void notifyTaskError( const Oscar::SNAC& s, int errCode, bool fatal ); + + /** Notify that a socket error has occured */ + void notifySocketError( int errCode, const QString& msg ); + +signals: + /** CONNECTION EVENTS */ + + /** Notifies that the login process has succeeded. */ + void loggedIn(); + + /** Notifies that the login process has failed */ + void loginFailed(); + + /** Notifies tasks and account so they can react properly */ + void disconnected(); + + /** We were disconnected because we connected elsewhere */ + void connectedElsewhere(); + + /** We have our own user info */ + void haveOwnInfo(); + + /** We have our SSI list */ + void haveSSIList(); + + /** a user is online. */ + void userIsOnline( const QString& ); + + /** a user is offline. */ + void userIsOffline( const QString& ); + + /** we've received a message */ + void messageReceived( const Oscar::Message& ); + + /** we've received an authorization request */ + void authRequestReceived( const QString& contact, const QString& reason ); + + /** we've received an authorization reply */ + void authReplyReceived( const QString& contact, const QString& reason, bool auth ); + + /** + * we've received an error from a task and need to notify somebody + */ + void taskError( const Oscar::SNAC& s, int errCode, bool fatal ); + + /** + * we've received a socket error and need to notify somebody + */ + void socketError( int errCode, const QString& msg ); + + void receivedIcqShortInfo( const QString& contact ); + void receivedIcqLongInfo( const QString& contact ); + + void receivedProfile( const QString& contact, const QString& profile ); + void receivedAwayMessage( const QString& contact, const QString& message ); + void receivedAwayMessage( const Oscar::Message& message ); + void receivedUserInfo( const QString& contact, const UserDetails& details ); + + /** We warned a user */ + void userWarned( const QString& contact, Q_UINT16 increase, Q_UINT16 newLevel ); + + /** Search signals */ + void gotSearchResults( const ICQSearchResult& ); + void endOfSearch( int); + + /* Typing signals */ + void userStartedTyping( const QString& contact ); + void userStoppedTyping( const QString& contact ); + + /* Buddy icons */ + void haveIconForContact( const QString&, QByteArray iconData ); + void iconServerConnected(); + void iconNeedsUploading(); + + /* Chat rooms */ + void chatNavigationConnected(); + void chatRoomConnected( WORD, const QString& ); + void userJoinedChat( Oscar::WORD, const QString& room, const QString& contact ); + void userLeftChat( Oscar::WORD, const QString& room, const QString& contact ); + + /* service redirection */ + void redirectionFinished( WORD ); + + +protected slots: + // INTERNAL, FOR USE BY TASKS' finished() SIGNALS // + + /** Singleshot timer to start stage two login */ + void startStageTwo(); + + /** + * A login task finished. For stage one, this means we've either errored + * out, or gotten a cookie. For stage two, this means we've either done + * something wrong, or we're successfully connected + */ + void lt_loginFinished(); + + /** Stream connected for stage two login */ + void streamConnected(); + + /** We have our own user info */ + void haveOwnUserInfo(); + + /** Service setup finished */ + void serviceSetupFinished(); + + /** we have icq info for a contact */ + void receivedIcqInfo( const QString& contact, unsigned int type ); + + /** we have normal user info for a contact */ + void receivedInfo( Q_UINT16 sequence ); + + /** received a message of some kind */ + void receivedMessage( const Oscar::Message& msg ); + + void offlineUser( const QString&, const UserDetails& ); + + void haveServerForRedirect( const QString& host, const QByteArray& cookie, WORD family ); + void serverRedirectFinished(); + void checkRedirectionQueue( WORD ); + + void requestChatNavLimits(); + /** + * Set the list of chat room exchanges we have + */ + void setChatExchangeList( const QValueList<int>& exchanges ); + + /** + * set up the connection to a chat room + */ + void setupChatConnection( WORD, QByteArray, WORD, const QString& ); + + + void determineDisconnection( int, const QString& ); + + void nextICQAwayMessageRequest(); + +private: + + /** Initialize some static tasks */ + void initializeStaticTasks(); + + /** Delete the static tasks */ + void deleteStaticTasks(); + + Connection* createConnection( const QString& host, const QString& port ); + + /** + * Request the icq away message + * \param contact the contact to get info for + */ + //TODO only made a default for testing w/o frontend + void requestICQAwayMessage( const QString& contact, ICQStatus contactStatus = ICQAway ); + +private: + class ClientPrivate; + ClientPrivate* d; + + StageOneLoginTask* m_loginTask; + StageTwoLoginTask* m_loginTaskTwo; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; + + diff --git a/kopete/protocols/oscar/liboscar/clientreadytask.cpp b/kopete/protocols/oscar/liboscar/clientreadytask.cpp new file mode 100644 index 00000000..3338f7b3 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/clientreadytask.cpp @@ -0,0 +1,109 @@ +/* + Kopete Oscar Protocol + $FILENAME.cpp + + Copyright (c) 2004 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 "clientreadytask.h" + +#include <kdebug.h> +#include "buffer.h" +#include "connection.h" +#include "rateclass.h" +#include "rateclassmanager.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" + +using namespace Oscar; + +ClientReadyTask::ClientReadyTask(Task* parent): Task(parent) +{ + m_classList = client()->rateManager()->classList(); +} + + +ClientReadyTask::~ClientReadyTask() +{ +} + +void ClientReadyTask::setFamilies( const QValueList<int>& families ) +{ + m_familyList = families; +} + + +void ClientReadyTask::onGo() +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x0002, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending client ready, end of login" << endl; + //nasty nasty nasty hack to get all the packets to work + QValueList<int>::const_iterator rcEnd = m_familyList.constEnd(); + for ( QValueList<int>::const_iterator it = m_familyList.constBegin(); it != rcEnd; ++it ) + { + //I have no idea what any of these values mean. I just copied them from oscarsocket + int i = ( *it ); + buffer->addWord( i ); + switch ( i ) + { + case 0x0001: + buffer->addWord( 0x0003 ); + break; + case 0x0013: + buffer->addWord( client()->isIcq() ? 0x0002 : 0x0003 ); + break; + default: + buffer->addWord( 0x0001 ); + }; + + if ( client()->isIcq() ) + { + if ( i == 0x0002 ) + buffer->addWord( 0x0101 ); + else + buffer->addWord( 0x0110 ); + + //always add 0x047B + buffer->addWord( 0x047B ); + } + else //we're AIM so AOL has us do something completely different! *sigh* + { + switch( i ) + { + case 0x0008: + case 0x000B: + case 0x000C: + buffer->addWord( 0x0104 ); + buffer->addWord( 0x0001 ); + break; + default: + buffer->addWord( 0x0110 ); + buffer->addWord( 0x059B ); + break; + }; + } + } + + //send the damn thing so we can finally be finished + //with the hell that is oscar login. (just wait until you get a message) + Transfer* t = createTransfer( f, s, buffer ); + send( t ); + setSuccess( 0, QString::null ); + +} + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/clientreadytask.h b/kopete/protocols/oscar/liboscar/clientreadytask.h new file mode 100644 index 00000000..4a9ea941 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/clientreadytask.h @@ -0,0 +1,46 @@ +/* + Kopete Oscar Protocol + + Copyright (c) 2004-2005 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. * + * * + ************************************************************************* +*/ +#ifndef CLIENTREADYTASK_H +#define CLIENTREADYTASK_H + +#include "task.h" + +#include "rateclass.h" +#include "qvaluelist.h" + +/** +Fire and forget task to let the server know we're ready + +@author Matt Rogers +*/ +class ClientReadyTask : public Task +{ +public: + ClientReadyTask( Task* parent ); + ~ClientReadyTask(); + virtual void onGo(); + + void setFamilies( const QValueList<int>& families ); + +private: + QValueList<RateClass*> m_classList; + QValueList<int> m_familyList; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/closeconnectiontask.cpp b/kopete/protocols/oscar/liboscar/closeconnectiontask.cpp new file mode 100644 index 00000000..54926949 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/closeconnectiontask.cpp @@ -0,0 +1,146 @@ +/* + Kopete Oscar Protocol + closeconnectiontask.h - Handles the closing of the connection to the server + + Copyright (c) 2004 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 "closeconnectiontask.h" + +#include <qstring.h> +#include <qvaluelist.h> +#include <kdebug.h> +#include <klocale.h> +#include "connection.h" +#include "transfer.h" +#include "oscarutils.h" + +using namespace Oscar; + +CloseConnectionTask::CloseConnectionTask( Task* parent ) + : Task(parent) +{ +} + + +CloseConnectionTask::~CloseConnectionTask() +{ +} + +const QByteArray& CloseConnectionTask::cookie() const +{ + return m_cookie; +} + +const QString& CloseConnectionTask::bosHost() const +{ + return m_bosHost; +} + +const QString& CloseConnectionTask::bosPort() const +{ + return m_bosPort; +} + +bool CloseConnectionTask::take( Transfer* transfer ) +{ + QString errorReason; + WORD errorNum = 0; + if ( forMe( transfer ) ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "RECV (DISCONNECT)" << endl; + + FlapTransfer* ft = dynamic_cast<FlapTransfer*> ( transfer ); + + if ( !ft ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo + << "Could not convert transfer object to type FlapTransfer!!" << endl; + return false; + } + + QValueList<TLV> tlvList = ft->buffer()->getTLVList(); + + TLV uin = findTLV( tlvList, 0x0001 ); + if ( uin ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(1) [UIN], uin=" << QString( uin.data ) << endl; + } + + TLV err = findTLV( tlvList, 0x0008 ); + if ( !err ) + err = findTLV( tlvList, 0x0009 ); + + if ( err.type == 0x0008 || err.type == 0x0009 ) + { + errorNum = ( ( err.data[0] << 8 ) | err.data[1] ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(8) [ERROR] error= " << errorNum << endl; + + Oscar::SNAC s = { 0, 0, 0, 0 }; + client()->fatalTaskError( s, errorNum ); + return true; //if there's an error, we'll need to disconnect anyways + } + + TLV server = findTLV( tlvList, 0x0005 ); + if ( server ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(5) [SERVER] " << QString( server.data ) << endl; + QString ip = server.data; + int index = ip.find( ':' ); + m_bosHost = ip.left( index ); + ip.remove( 0 , index+1 ); //get rid of the colon and everything before it + m_bosPort = ip; + } + + TLV cookie = findTLV( tlvList, 0x0006 ); + if ( cookie ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(6) [COOKIE]" << endl; + m_cookie.duplicate( cookie.data ); + } + + tlvList.clear(); + + if ( m_bosHost.isEmpty() ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "Empty host address!" << endl; + + Oscar::SNAC s = { 0, 0, 0, 0 }; + client()->fatalTaskError( s, 0 ); + return true; + } + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "We should reconnect to server '" + << m_bosHost << "' on port " << m_bosPort << endl; + setSuccess( errorNum, errorReason ); + return true; + } + return false; +} + +bool CloseConnectionTask::forMe( const Transfer* transfer ) const +{ + const FlapTransfer* ft = dynamic_cast<const FlapTransfer*> ( transfer ); + + if (!ft) + return false; + + if ( ft && ft->flapChannel() == 4 ) + return true; + else + return false; +} + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/closeconnectiontask.h b/kopete/protocols/oscar/liboscar/closeconnectiontask.h new file mode 100644 index 00000000..b241b07e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/closeconnectiontask.h @@ -0,0 +1,62 @@ +/* + Kopete Oscar Protocol + closeconnectiontask.h - Handles the closing of the connection to the server + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef CLOSECONNECTIONTASK_H +#define CLOSECONNECTIONTASK_H + +#include <task.h> +#include <qcstring.h> + +class Transfer; +class QString; + +/** +@author Matt Rogers +*/ +class CloseConnectionTask : public Task +{ +public: + CloseConnectionTask(Task* parent); + + ~CloseConnectionTask(); + + virtual bool take(Transfer* transfer); + + //Protocol specific stuff + const QByteArray& cookie() const; + const QString& bosHost() const; + const QString& bosPort() const; + + +protected: + virtual bool forMe(const Transfer* transfer) const; + +private: + bool parseDisconnectCode( int error, QString& reason ); + +private: + QByteArray m_cookie; + QString m_bosHost; + QString m_bosPort; + + +}; + +#endif + +//kate: indent-mode csands; space-indent off; tab-width 4; replace-tabs off; diff --git a/kopete/protocols/oscar/liboscar/connection.cpp b/kopete/protocols/oscar/liboscar/connection.cpp new file mode 100644 index 00000000..c7cbc0fe --- /dev/null +++ b/kopete/protocols/oscar/liboscar/connection.cpp @@ -0,0 +1,248 @@ +/* + Kopete Oscar Protocol + connection.cpp - independent protocol encapsulation + + Copyright (c) 2004-2005 by 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 "connection.h" +#include "client.h" +#include "connector.h" +#include "oscarclientstream.h" +#include "rateclassmanager.h" +#include "task.h" +#include "transfer.h" +#include <kapplication.h> +#include <kdebug.h> + +#include "oscartypeclasses.h" + + +class ConnectionPrivate +{ +public: + DWORD snacSequence; + WORD flapSequence; + + QValueList<int> familyList; + RateClassManager* rateClassManager; + + ClientStream* clientStream; + Connector* connector; + Client* client; + + Task* root; +}; + + + +Connection::Connection( Connector* connector, ClientStream* cs, const char* name ) +: QObject( 0, name ) +{ + d = new ConnectionPrivate(); + d->clientStream = cs; + d->client = 0; + d->connector = connector; + d->rateClassManager = new RateClassManager( this ); + d->root = new Task( this, true /* isRoot */ ); + m_loggedIn = false; + initSequence(); + +} + +Connection::~Connection() +{ + + delete d->rateClassManager; + delete d->clientStream; + delete d->connector; + delete d->root; + delete d; +} + +void Connection::setClient( Client* c ) +{ + d->client = c; + connect( c, SIGNAL( loggedIn() ), this, SLOT( loggedIn() ) ); +} + +void Connection::connectToServer( const QString& host, bool auth ) +{ + connect( d->clientStream, SIGNAL( error( int ) ), this, SLOT( streamSocketError( int ) ) ); + connect( d->clientStream, SIGNAL( readyRead() ), this, SLOT( streamReadyRead() ) ); + connect( d->clientStream, SIGNAL( connected() ), this, SIGNAL( connected() ) ); + d->clientStream->connectToServer( host, auth ); +} + +void Connection::close() +{ + d->clientStream->close(); + reset(); +} + +bool Connection::isSupported( int family ) const +{ + return ( d->familyList.findIndex( family ) != -1 ); +} + +QValueList<int> Connection::supportedFamilies() const +{ + return d->familyList; +} + +void Connection::addToSupportedFamilies( const QValueList<int>& familyList ) +{ + d->familyList += familyList; +} + +void Connection::addToSupportedFamilies( int family ) +{ + d->familyList.append( family ); +} + +void Connection::taskError( const Oscar::SNAC& s, int errCode ) +{ + d->client->notifyTaskError( s, errCode, false /*fatal*/ ); +} + +void Connection::fatalTaskError( const Oscar::SNAC& s, int errCode ) +{ + d->client->notifyTaskError( s, errCode, true /* fatal */ ); +} + +Oscar::Settings* Connection::settings() const +{ + return d->client->clientSettings(); +} + +Q_UINT16 Connection::flapSequence() +{ + d->flapSequence++; + if ( d->flapSequence >= 0x8000 ) //the max flap sequence is 0x8000 ( HEX ) + d->flapSequence = 1; + + return d->flapSequence; +} + +Q_UINT32 Connection::snacSequence() +{ + d->snacSequence++; + return d->snacSequence; +} + +QString Connection::userId() const +{ + return d->client->userId(); +} + +QString Connection::password() const +{ + return d->client->password(); +} + +bool Connection::isIcq() const +{ + return d->client->isIcq(); +} + +Task* Connection::rootTask() const +{ + return d->root; +} + +SSIManager* Connection::ssiManager() const +{ + return d->client->ssiManager(); +} + +const Oscar::ClientVersion* Connection::version() const +{ + return d->client->version(); +} + +bool Connection::isLoggedIn() const +{ + return m_loggedIn; +} + +RateClassManager* Connection::rateManager() const +{ + return d->rateClassManager; +} + +void Connection::send( Transfer* request ) const +{ + if( !d->clientStream ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No stream to write on!" << endl; + return; + } + d->rateClassManager->queue( request ); + +} + +void Connection::forcedSend( Transfer* request ) const +{ + if ( !d->clientStream ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No stream to write on" << endl; + return; + } + d->clientStream->write( request ); +} + +void Connection::initSequence() +{ + d->snacSequence = ( KApplication::random() & 0xFFFF ); + d->flapSequence = ( KApplication::random() & 0xFFFF ); +} + +void Connection::distribute( Transfer * transfer ) const +{ + //d->rateClassManager->recalcRateLevels(); + if( !rootTask()->take( transfer ) ) + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "root task refused transfer" << endl; + + delete transfer; +} + +void Connection::reset() +{ + //clear the family list + d->familyList.clear(); + d->rateClassManager->reset(); +} + +void Connection::streamReadyRead() +{ + // take the incoming transfer and distribute it to the task tree + Transfer * transfer = d->clientStream->read(); + distribute( transfer ); +} + +void Connection::loggedIn() +{ + m_loggedIn = true; +} + +void Connection::streamSocketError( int code ) +{ + emit socketError( code, d->clientStream->errorText() ); +} + +#include "connection.moc" +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/connection.h b/kopete/protocols/oscar/liboscar/connection.h new file mode 100644 index 00000000..4170857e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/connection.h @@ -0,0 +1,209 @@ +/* +Kopete Oscar Protocol +connection.h - independent protocol encapsulation + +Copyright (c) 2004-2005 by Matt Rogers <mattr@kde.org> + +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. * +* * +************************************************************************* +*/ +#ifndef CONNECTION_H +#define CONNECTION_H + +#include <qobject.h> +#include <qvaluelist.h> +#include "oscartypes.h" +#include "rateclass.h" + +class ConnectionPrivate; +class Client; +class ClientStream; +class Connector; +class ByteStream; +class Transfer; +class RateClassManager; +class SSIManager; +class Task; + + +namespace Oscar +{ +class Settings; +} + +/** + * This class encapsulates both the low level network layer code and the high + * level OSCAR protocol code required to create a single independent + * connection to an OSCAR server + * @author Matt Rogers + */ +class Connection : public QObject +{ +Q_OBJECT +public: + + Connection( Connector* connector, ClientStream* cs, const char* name = 0 ); + ~Connection(); + + void setClient( Client* ); + + void connectToServer( const QString& server, bool auth = true ); + /** + * Close the connection and reset the connection data + */ + void close(); + + /** + * Check to see if the family specified by @p family is supported by this + * connection. + * @param family the family number to check + */ + bool isSupported( int family ) const; + + /** + * Get the list of supported families + * @return The list of families supported on this connection + */ + QValueList<int> supportedFamilies() const; + + /** + * Add the SNAC families in \p familyList to the list of supported families for + * this connection + * \param familyList the list of families to add + */ + void addToSupportedFamilies( const QValueList<int>& familyList ); + + /** + * Add the SNAC family in \p family to the list of supported families for + * this connection + * \overload + * \param family the single family to add to the list + */ + void addToSupportedFamilies( int family ); + + /** + * Add the rate classes in \p rateClassList to the list of rate classes packets + * need to be filtered on + * \param rateClassList the list of rate classes to add + */ + void addToRateClasses( const QValueList<RateClass*> rateClassList ); + + /** + * Add the rate class in \p rc to the list of rate classes packets + * need to be filtered on + * \overload + * \param rc the list rate class to add + */ + void addToRateClasses( RateClass* rc ); + + /** + * Indicate to the connection that there has been an error in a task. The + * error won't require us to go offline, but the user should be notified + * about the error + * \param s the SNAC the error occured from + * \param errCode the error code + */ + void taskError( const Oscar::SNAC& s, int errCode ); + + /** + * Indicate to the connection that there has been a fatal error in a task. + * This error will require a disconnection from the OSCAR service and if + * necessary, the user should be prompted to reconnect manually or an + * automatic reconnection should be attempted. + * \param s the SNAC the error occured from + * \param errCode the error code + */ + void fatalTaskError( const Oscar::SNAC& s, int errCode ); + + /** + * Get the chat room name for this connection. + * @return the name of the room or QString::null if not connected to a room + */ + + /** Get the user settings object */ + Oscar::Settings* settings() const; + + /** Get the current FLAP sequence for this connection */ + Q_UINT16 flapSequence(); + + /** Get the current SNAC sequence for this connection */ + Q_UINT32 snacSequence(); + + /** Get the cookie for this connection */ + QByteArray cookie() const; + + QString userId() const; + QString password() const; + bool isIcq() const; + SSIManager* ssiManager() const; + const Oscar::ClientVersion* version() const; + RateClassManager* rateManager() const; + bool isLoggedIn() const; + + /** Convenience function to get the root task for use in Tasks */ + Task* rootTask() const; + + /** Get the raw connector for this connection, in case we need it */ + Connector* connector(); + + /** Get the byte stream for this connection, in case we need it */ + ByteStream* byteStream(); + + void send( Transfer* t ) const; + void forcedSend( Transfer* t ) const; + +signals: + + /** There's data ready to read */ + void readyRead(); + + /** We've connected */ + void connected(); + + /** We were disconnected */ + void disconnected(); + + /** + * There was an error on the socket and we've disconnected + * \param errCode the error code from the operating system + * \param errString the i18n'ed string that describes the error + */ + void socketError( int errCode, const QString& errString ); + + +private: + /** Seed the sequence numbers with random values */ + void initSequence(); + + /** Distribute the transfer among the connection's tasks */ + void distribute( Transfer* t ) const; + +private slots: + /** Reset the data for the connection.*/ + void reset(); + + /** We've got something from the stream */ + void streamReadyRead(); + + /** We've finished logging in */ + void loggedIn(); + + void streamSocketError( int ); + +private: + + ConnectionPrivate* d; + bool m_loggedIn; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; auto-insert-doxygen on; diff --git a/kopete/protocols/oscar/liboscar/connectionhandler.cpp b/kopete/protocols/oscar/liboscar/connectionhandler.cpp new file mode 100644 index 00000000..bf5706ee --- /dev/null +++ b/kopete/protocols/oscar/liboscar/connectionhandler.cpp @@ -0,0 +1,174 @@ +/* + Kopete Oscar Protocol + Oscar Multiple Connection Handling + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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 "connectionhandler.h" +#include <qvaluelist.h> +#include <kdebug.h> +#include "connection.h" +#include "oscartypes.h" + +class ConnectionHandler::Private +{ +public: + QValueList<Connection*> connections; + QMap<Connection*, ConnectionRoomInfo> chatRoomConnections; +}; + +ConnectionHandler::ConnectionHandler() +{ + d = new Private; +} + + +ConnectionHandler::~ConnectionHandler() +{ + delete d; +} + +void ConnectionHandler::append( Connection* c ) +{ + d->connections.append( c ); +} + +void ConnectionHandler::remove( Connection* c ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing connection " + << c << endl; + d->connections.remove( c ); + c->deleteLater(); +} + +void ConnectionHandler::remove( int family ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing all connections " << + "supporting family " << family << endl; + QValueList<Connection*>::iterator it = d->connections.begin(); + QValueList<Connection*>::iterator itEnd = d->connections.end(); + for ( ; it != itEnd; ++it ) + { + if ( ( *it )->isSupported( family ) ) + { + Connection* c = ( *it ); + it = d->connections.remove( it ); + c->deleteLater(); + } + } +} + +void ConnectionHandler::clear() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Clearing all connections" + << endl; + while ( !d->connections.isEmpty() ) + { + Connection *c = d->connections.front(); + d->connections.pop_front(); + c->deleteLater(); + } +} + +Connection* ConnectionHandler::connectionForFamily( int family ) const +{ + QValueList<Connection*>::iterator it = d->connections.begin(); + QValueList<Connection*>::iterator itEnd = d->connections.end(); + int connectionCount = 0; + Connection* lastConnection = 0; + for ( ; it != itEnd; ++it ) + { + if ( ( *it )->isSupported( family ) ) + { + connectionCount++; + lastConnection = ( *it ); + } + } + if ( connectionCount == 1 ) + return lastConnection; + + return 0; +} + +Connection* ConnectionHandler::defaultConnection() const +{ + if ( d->connections.isEmpty() || d->connections.count() > 1 ) + return 0; + + return d->connections.first(); +} + +void ConnectionHandler::addChatInfoForConnection( Connection* c, Oscar::WORD exchange, const QString& room ) +{ + if ( d->connections.findIndex( c ) == -1 ) + d->connections.append( c ); + + ConnectionRoomInfo info = qMakePair( exchange, room ); + d->chatRoomConnections[c] = info; +} + +Connection* ConnectionHandler::connectionForChatRoom( Oscar::WORD exchange, const QString& room ) +{ + ConnectionRoomInfo infoToFind = qMakePair( exchange, room ); + QMap<Connection*, ConnectionRoomInfo>::iterator it, itEnd = d->chatRoomConnections.end(); + for ( it = d->chatRoomConnections.begin(); it != itEnd; ++it ) + { + if ( it.data() == infoToFind ) + { + Connection* c = it.key(); + return c; + } + } + + return 0; +} + +QString ConnectionHandler::chatRoomForConnection( Connection* c ) +{ + if ( d->connections.findIndex( c ) == -1 ) + return QString::null; + + QMap<Connection*, ConnectionRoomInfo>::iterator it, itEnd = d->chatRoomConnections.end(); + for ( it = d->chatRoomConnections.begin(); it != itEnd; ++it ) + { + if ( it.key() == c ) + { + QString room = it.data().second; + return room; + } + } + + return QString::null; +} + +Oscar::WORD ConnectionHandler::exchangeForConnection( Connection* c ) +{ + + if ( d->connections.findIndex( c ) == -1 ) + return 0xFFFF; + + QMap<Connection*, ConnectionRoomInfo>::iterator it, itEnd = d->chatRoomConnections.end(); + for ( it = d->chatRoomConnections.begin(); it != itEnd; ++it ) + { + if ( it.key() == c ) + { + Oscar::WORD exchange = it.data().first; + return exchange; + } + } + + return 0xFFFF; +} + diff --git a/kopete/protocols/oscar/liboscar/connectionhandler.h b/kopete/protocols/oscar/liboscar/connectionhandler.h new file mode 100644 index 00000000..6094cab3 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/connectionhandler.h @@ -0,0 +1,118 @@ +/* + Kopete Oscar Protocol + Oscar Multiple Connection Handling + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef CONNECTIONHANDLER_H +#define CONNECTIONHANDLER_H + +#include "oscartypes.h" +#include <qstring.h> +#include <qpair.h> + + +class Connection; + +typedef QPair<Oscar::WORD, QString> ConnectionRoomInfo; + +/** +@author Kopete Developers +*/ +class ConnectionHandler +{ +public: + ConnectionHandler(); + ~ConnectionHandler(); + + /** + * Add a connection to the handler so that it can be + * tracked and queried for later. + * @param c The connection to add to the handler + */ + void append( Connection* c ); + + /** + * Remove a connection from the handler + * @param c The connection object to remove + */ + void remove( Connection* c ); + + /** + * Remove a connection from the handler + * @param family The SNAC family for the connection to remove + */ + void remove( int family ); + + /** + * Clear all the connections. + */ + void clear(); + + /** + * Get the connection for a particular SNAC family. If there is + * more than one connection for a particular family or there is no + * connection, then zero is returned. + * @return A valid connection object for the family or 0 + */ + Connection* connectionForFamily( int family ) const; + + /** + * Get the default connection. Returns zero when we're handling more than + * one connection. + * @return The only connection object we're tracking or zero if we have + * more than one. + */ + Connection* defaultConnection() const; + + /** + * Add chat room information to a connection so that we can track + * connections by chat room + * @param c The connection to add information to + * @param exchange the exchange the chat room is in + * @param room the name of the chat room + */ + void addChatInfoForConnection( Connection* c, Oscar::WORD exchange, const QString& room ); + + /** + * Get the connection for a particular room name and exchange number. + * @param exchange the chat room exchange the room is on + * @param room the name of the chat room to find a connection for + * @return a Connection for the chat room or 0L if no connection for that room + */ + Connection* connectionForChatRoom( Oscar::WORD exchange, const QString& room ); + + /** + * Get the room name for the chat room based the connection + * @return The name of the chat room that this connection is connected to + * If the connection passed in by @p c is not a chat room connection then + * QString::null is returned. + */ + QString chatRoomForConnection( Connection* c ); + + /** + * Get the exchange number for the chat room based on the connection + * @return The exchange of the chat room that this connection is connected + * to. If the connection passed in by @p c is not a chat room connection + * then 0xFFFF is returned + */ + Oscar::WORD exchangeForConnection( Connection* c ); + +private: + class Private; + Private* d; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/connector.cpp b/kopete/protocols/oscar/liboscar/connector.cpp new file mode 100644 index 00000000..03a61882 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/connector.cpp @@ -0,0 +1,62 @@ +/* + Kopete Oscar Protocol + connector.cpp - the Oscar socket connector + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Based on Iris, Copyright (C) 2003 Justin Karneges + + 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 "connector.h" + +Connector::Connector(QObject *parent) +:QObject(parent) +{ + setPeerAddressNone(); +} + +Connector::~Connector() +{ +} + +bool Connector::havePeerAddress() const +{ + return haveaddr; +} + +QHostAddress Connector::peerAddress() const +{ + return addr; +} + +Q_UINT16 Connector::peerPort() const +{ + return port; +} + +void Connector::setPeerAddressNone() +{ + haveaddr = false; + addr = QHostAddress(); + port = 0; +} + +void Connector::setPeerAddress(const QHostAddress &_addr, Q_UINT16 _port) +{ + haveaddr = true; + addr = _addr; + port = _port; +} + +#include "connector.moc" diff --git a/kopete/protocols/oscar/liboscar/connector.h b/kopete/protocols/oscar/liboscar/connector.h new file mode 100644 index 00000000..fd673163 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/connector.h @@ -0,0 +1,59 @@ +/* + Kopete Oscar Protocol + connector.h - the Oscar socket connector + + Copyright (c) 2004 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-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. * + * * + ************************************************************************* +*/ + +#ifndef LIBOSCAR_CONNECTOR_H +#define LIBOSCAR_CONNECTOR_H + + +#include <qobject.h> +#include "qhostaddress.h" + +class ByteStream; + +class Connector : public QObject +{ + Q_OBJECT +public: + Connector(QObject *parent=0); + virtual ~Connector(); + + virtual void connectToServer(const QString &server)=0; + virtual ByteStream *stream() const=0; + virtual void done()=0; + + bool havePeerAddress() const; + QHostAddress peerAddress() const; + Q_UINT16 peerPort() const; + +signals: + void connected(); + void error(); + +protected: + void setPeerAddressNone(); + void setPeerAddress(const QHostAddress &addr, Q_UINT16 port); + +private: + bool haveaddr; + QHostAddress addr; + Q_UINT16 port; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/coreprotocol.cpp b/kopete/protocols/oscar/liboscar/coreprotocol.cpp new file mode 100644 index 00000000..4f114039 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/coreprotocol.cpp @@ -0,0 +1,285 @@ +/* + Kopete Oscar Protocol + coreprotocol.h- the core Oscar protocol + + Copyright (c) 2004 by 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 + url_escape_string from Gaim src/protocols/novell/nmconn.c + Copyright (c) 2004 Novell, Inc. All Rights Reserved + + 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 "coreprotocol.h" + +#include <qdatastream.h> +#include <qdatetime.h> +#include <qtextstream.h> + +#include <kdebug.h> +#include <ctype.h> + +#include "oscartypes.h" +#include "transfer.h" +#include "flapprotocol.h" +#include "snacprotocol.h" + +static QString toString( const QByteArray& buffer ) +{ + // line format: + //00 03 00 0b 00 00 90 b8 f5 9f 09 31 31 33 37 38 |;tJ�..........| + + int i = 0; + QString output = "\n"; + QString hex, ascii; + + QByteArray::ConstIterator it; + for ( it = buffer.begin(); it != buffer.end(); ++it ) + { + i++; + + unsigned char c = static_cast<unsigned char>(*it); + + if ( c < 0x10 ) + hex.append("0"); + hex.append(QString("%1 ").arg(c, 0, 16)); + + ascii.append(isprint(c) ? c : '.'); + + if (i == 16) + { + output += hex + " |" + ascii + "|\n"; + i=0; + hex=QString::null; + ascii=QString::null; + } + } + + if(!hex.isEmpty()) + output += hex.leftJustify(48, ' ') + " |" + ascii.leftJustify(16, ' ') + '|'; + output.append('\n'); + + return output; +} + + +using namespace Oscar; + +CoreProtocol::CoreProtocol() : QObject() +{ + m_snacProtocol = new SnacProtocol( this, "snacprotocol" ); + m_flapProtocol = new FlapProtocol( this, "flapprotocol" ); +} + +CoreProtocol::~CoreProtocol() +{ +} + +int CoreProtocol::state() +{ + return m_state; +} + +void CoreProtocol::addIncomingData( const QByteArray & incomingBytes ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received " << incomingBytes.count() << " bytes. " << endl; + // store locally + int oldsize = m_in.size(); + m_in.resize( oldsize + incomingBytes.size() ); + memcpy( m_in.data() + oldsize, incomingBytes.data(), incomingBytes.size() ); + m_state = Available; + + // convert every event in the chunk to a Transfer, signalling it back to the clientstream + int parsedBytes = 0; + int transferCount = 0; + // while there is data left in the input buffer, and we are able to parse something out of it + while ( m_in.size() && ( parsedBytes = wireToTransfer( m_in ) ) ) + { + transferCount++; + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "parsed transfer #" << transferCount << " in chunk" << endl; + int size = m_in.size(); + if ( parsedBytes < size ) + { + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "more data in chunk!" << endl; + // copy the unparsed bytes into a new qbytearray and replace m_in with that + QByteArray remainder( size - parsedBytes ); + memcpy( remainder.data(), m_in.data() + parsedBytes, remainder.size() ); + m_in = remainder; + } + else + m_in.truncate( 0 ); + } + + if ( m_state == NeedMore ) + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "message was incomplete, waiting for more..." << endl; + + if ( m_snacProtocol->state() == OutOfSync || m_flapProtocol->state() == OutOfSync ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "protocol thinks it's out of sync. " + << "discarding the rest of the buffer and hoping the server regains sync soon..." << endl; + m_in.truncate( 0 ); + } +// kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "done processing chunk" << endl; +} + +Transfer* CoreProtocol::incomingTransfer() +{ + if ( m_state == Available ) + { + m_state = NoData; + return m_inTransfer; + m_inTransfer = 0; + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "we shouldn't be here!" << kdBacktrace() << endl; + return 0; + } +} + +void cp_dump( const QByteArray &bytes ) +{ +#ifdef OSCAR_COREPROTOCOL_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << "contains: " << bytes.count() << " bytes" << endl; + for ( uint i = 0; i < bytes.count(); ++i ) + { + printf( "%02x ", bytes[ i ] ); + } + printf( "\n" ); +#else + Q_UNUSED( bytes ); +#endif +} + +void CoreProtocol::outgoingTransfer( Transfer* outgoing ) +{ + //kdDebug(OSCAR_RAW_DEBUG) << "CoreProtocol::outgoingTransfer()" << endl; + // Convert the outgoing data into wire format + // pretty leet, eh? + emit outgoingData( outgoing->toWire() ); + delete outgoing; + + return; +} + +int CoreProtocol::wireToTransfer( const QByteArray& wire ) +{ + // processing incoming data and reassembling it into transfers + // may be an event or a response + + BYTE flapStart, flapChannel = 0; + WORD flapLength = 0; + WORD s1, s2 = 0; + uint bytesParsed = 0; + + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Current packet" << toString(wire) << endl; + if ( wire.size() < 6 ) //check for valid flap length + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo + << "packet not long enough! couldn't parse FLAP!" << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "packet size is " << wire.size() << endl; + m_state = NeedMore; + return bytesParsed; + } + + QDataStream din( wire, IO_ReadOnly ); + + // look at first four bytes and decide what to do with the chunk + if ( okToProceed( din ) ) + { + din >> flapStart; + QByteArray packet; + packet.duplicate( wire ); + if ( flapStart == 0x2A ) + { + din >> flapChannel; + din >> flapLength; //discard the first one it's not really the flap length + din >> flapLength; + if ( wire.size() < flapLength ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo + << "Not enough bytes to make a correct transfer. Have " << wire.size() + << " bytes. need " << flapLength + 6 << " bytes" << endl; + m_state = NeedMore; + return bytesParsed; + } + + if ( flapChannel != 2 ) + { + Transfer *t = m_flapProtocol->parse( packet, bytesParsed ); + if ( t ) + { + m_inTransfer = t; + m_state = Available; + emit incomingData(); + } + else + bytesParsed = 0; + } + + if ( flapChannel == 2 ) + { + din >> s1; + din >> s2; + + Transfer * t = m_snacProtocol->parse( packet, bytesParsed ); + if ( t ) + { + m_inTransfer = t; + m_state = Available; + emit incomingData(); + } + else + { + bytesParsed = 0; + m_state = NeedMore; + } + } + } + else + { //unknown wire format + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "unknown wire format detected!" << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "start byte is " << flapStart << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Packet is " << endl << toString( wire ) << endl; + } + + } + return bytesParsed; +} + +void CoreProtocol::reset() +{ + m_in.resize( 0 ); +} + +void CoreProtocol::slotOutgoingData( const QCString &out ) +{ + kdDebug(OSCAR_RAW_DEBUG) << out.data() << endl; +} + +bool CoreProtocol::okToProceed( const QDataStream &din ) +{ + if ( din.atEnd() ) + { + m_state = NeedMore; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Server message ended prematurely!" << endl; + return false; + } + else + return true; +} + +#include "coreprotocol.moc" +//kate: indent-mode csands; tab-width 4; diff --git a/kopete/protocols/oscar/liboscar/coreprotocol.h b/kopete/protocols/oscar/liboscar/coreprotocol.h new file mode 100644 index 00000000..f49396af --- /dev/null +++ b/kopete/protocols/oscar/liboscar/coreprotocol.h @@ -0,0 +1,108 @@ +/* + Kopete Groupwise Protocol + coreprotocol.h- the core GroupWise protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + 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. * + * * + ************************************************************************* +*/ + +#ifndef GW_CORE_PROTOCOL_H +#define GW_CORE_PROTOCOL_H + +#include <qcstring.h> +#include <qobject.h> +#include <qptrlist.h> + +class FlapProtocol; +class SnacProtocol; +class Transfer; + +class CoreProtocol : public QObject +{ +Q_OBJECT +public: + enum State { NeedMore, Available, NoData, OutOfSync }; + + CoreProtocol(); + + virtual ~CoreProtocol(); + + /** + * Reset the protocol, clear buffers + */ + void reset(); + + /** + * Accept data from the network, and buffer it into a useful message + * This requires parsing out each FLAP, etc. from the incoming data + * @param incomingBytes Raw data in wire format. + */ + void addIncomingData( const QByteArray& incomingBytes ); + + /** + * @return the incoming transfer or 0 if none is available. + */ + Transfer* incomingTransfer(); + + /** + * Convert a request into an outgoing transfer + * emits @ref outgoingData() with each part of the transfer + */ + void outgoingTransfer( Transfer* outgoing ); + + /** + * Get the state of the protocol + */ + int state(); + +signals: + /** + * Emitted as the core protocol converts fields to wire ready data + */ + void outgoingData( const QByteArray& ); + + /** + * Emitted when there is incoming data, parsed into a Transfer + */ + void incomingData(); +protected slots: + /** + * Just a debug method to test emitting to the socket, atm - should go to the ClientStream + */ + void slotOutgoingData( const QCString & ); + +protected: + /** + * Check that there is data to read, and set the protocol's state if there isn't any. + */ + bool okToProceed( const QDataStream &din ); + /** + * Convert incoming wire data into a Transfer object and queue it + * @return number of bytes from the input that were parsed into a Transfer + */ + int wireToTransfer( const QByteArray& wire ); + +private: + QByteArray m_in; // buffer containing unprocessed bytes we received + int m_error; + Transfer* m_inTransfer; // the transfer that is being received + int m_state; // represents the protocol's overall state + SnacProtocol* m_snacProtocol; + FlapProtocol* m_flapProtocol; + +}; + +#endif + diff --git a/kopete/protocols/oscar/liboscar/errortask.cpp b/kopete/protocols/oscar/liboscar/errortask.cpp new file mode 100644 index 00000000..9e9ce95b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/errortask.cpp @@ -0,0 +1,66 @@ +/* + Kopete Oscar Protocol + errortask.cpp - handle OSCAR protocol errors + + Copyright (c) 2004 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 "errortask.h" +#include <kdebug.h> +#include "oscartypes.h" +#include "transfer.h" + +ErrorTask::ErrorTask( Task* parent ) + : Task( parent ) +{ +} + + +ErrorTask::~ErrorTask() +{ +} + + +bool ErrorTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + + if (!st) + return false; + + if ( st->flapChannel() == 2 && st->snacSubtype() == 1 ) + return true; + else + return false; +} + +bool ErrorTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + Buffer* buffer = transfer->buffer(); + //get the error code + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Error code is " << buffer->getWord() << endl; + TLV t = buffer->getTLV(); + if ( t.type == 0x0008 && t.length > 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "TLV error subcode is " + << t.data << endl; + } + return true; + } + else + return false; +} + +//kate indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/errortask.h b/kopete/protocols/oscar/liboscar/errortask.h new file mode 100644 index 00000000..f74152db --- /dev/null +++ b/kopete/protocols/oscar/liboscar/errortask.h @@ -0,0 +1,39 @@ +/* + Kopete Oscar Protocol + errortask.h - handle OSCAR protocol errors + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ +#ifndef ERRORTASK_H +#define ERRORTASK_H + +#include <task.h> + +/** +Handles OSCAR protocol errors received from the server on snac subtype 0x01 +@author Matt Rogers +*/ +class ErrorTask : public Task +{ +public: + ErrorTask( Task* parent ); + ~ErrorTask(); + bool take( Transfer* transfer ); + +protected: + bool forMe( const Transfer* transfer ) const; + +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/flapprotocol.cpp b/kopete/protocols/oscar/liboscar/flapprotocol.cpp new file mode 100644 index 00000000..c5dc04ed --- /dev/null +++ b/kopete/protocols/oscar/liboscar/flapprotocol.cpp @@ -0,0 +1,72 @@ +/* + Kopete Oscar Protocol + flapprotocol.cpp - reads the protocol used by Oscar for signaling stuff + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com> + + 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 "flapprotocol.h" + +#include <qcstring.h> +#include <qdatastream.h> +#include <qobject.h> +#include <kdebug.h> + +#include "transfer.h" + +using namespace Oscar; + +FlapProtocol::FlapProtocol(QObject *parent, const char *name) + : InputProtocolBase(parent, name) +{ +} + +FlapProtocol::~FlapProtocol() +{ +} + +Transfer* FlapProtocol::parse( const QByteArray & packet, uint& bytes ) +{ + QDataStream* m_din = new QDataStream( packet, IO_ReadOnly ); + + BYTE b; + WORD w; + + FLAP f; + *m_din >> b; //this should be the start byte + *m_din >> b; + f.channel = b; + *m_din >> w; + f.sequence = w; + *m_din >> w; + f.length = w; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "channel: " << f.channel + << " sequence: " << f.sequence << " length: " << f.length << endl; + //use pointer arithmatic to skip the flap and snac headers + //so we don't have to do double parsing in the tasks + char* charPacket = packet.data(); + char* snacData = charPacket + 6; + Buffer *snacBuffer = new Buffer( snacData, f.length ); + + FlapTransfer* ft = new FlapTransfer( f, snacBuffer ); + bytes = snacBuffer->length() + 6; + delete m_din; + m_din = 0; + return ft; +} + + +#include "flapprotocol.moc" diff --git a/kopete/protocols/oscar/liboscar/flapprotocol.h b/kopete/protocols/oscar/liboscar/flapprotocol.h new file mode 100644 index 00000000..d61cf6c4 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/flapprotocol.h @@ -0,0 +1,46 @@ +/* + Kopete Oscar Protocol + flapprotocol.h - reads the protocol used by Oscar for signaling stuff + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef OSCAR_FLAPPROTOCOL_H +#define OSCAR_FLAPPROTOCOL_H + +#include "inputprotocolbase.h" + +class FlapTransfer; + + +class FlapProtocol : public InputProtocolBase +{ +Q_OBJECT +public: + FlapProtocol( QObject *parent = 0, const char *name = 0 ); + ~FlapProtocol(); + + /** + * Attempt to parse the supplied data into an @ref SnacTransfer object. + * The exact state of the parse attempt can be read using @ref state. + * @param rawData The unparsed data. + * @param bytes An integer used to return the number of bytes read. + * @return A pointer to an FlapTransfer object if successfull, otherwise 0. The caller is responsible for deleting this object. + */ + Transfer * parse( const QByteArray &, uint & bytes ); + +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/icbmparamstask.cpp b/kopete/protocols/oscar/liboscar/icbmparamstask.cpp new file mode 100644 index 00000000..960d4ee5 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icbmparamstask.cpp @@ -0,0 +1,143 @@ +/* + Kopete Oscar Protocol + icbmparamstask.cpp - Get the ICBM parameters + + Copyright (c) 2004 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 "icbmparamstask.h" + +#include <kdebug.h> +#include "connection.h" +#include "oscartypes.h" +#include "transfer.h" +#include "oscarutils.h" +#include "buffer.h" + +ICBMParamsTask::ICBMParamsTask( Task* parent ) + : Task( parent ) +{} + + +ICBMParamsTask::~ICBMParamsTask() +{} + + +bool ICBMParamsTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + + if (!st) + return false; + + if ( st->snacService() == 4 && st->snacSubtype() == 5 ) + return true; + else + return false; +} + +bool ICBMParamsTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + handleICBMParameters(); + setTransfer( 0 ); + return true; + } + return false; +} + +void ICBMParamsTask::onGo() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending ICBM Parameters request" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0004, 0x0004, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + Transfer* st = createTransfer( f, s, buffer ); + send( st ); +} + +void ICBMParamsTask::handleICBMParameters() +{ + Buffer* buffer = transfer()->buffer(); + + WORD channel = buffer->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "channel=" << channel << endl; + + /** + * bit1: messages allowed for specified channel + * bit2: missed calls notifications enabled for specified channel + * bit4: client supports typing notifications + */ + DWORD messageFlags = buffer->getDWord(); + WORD maxMessageSnacSize = buffer->getWord(); + WORD maxSendWarnLvl = buffer->getWord(); // max sender Warning Level + WORD maxRecvWarnLvl = buffer->getWord(); // max Receiver Warning Level + WORD minMsgInterval = buffer->getWord(); // minimum message interval (msec) + + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "messageFlags = " << messageFlags << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "maxMessageSnacSize = " << maxMessageSnacSize << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "maxSendWarnLvl = " << maxSendWarnLvl << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "maxRecvWarnLvl = " << maxRecvWarnLvl << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "minMsgInterval = " << minMsgInterval << endl; + + /*WORD unknown = */buffer->getWord(); + + // Now we set our own parameters. + // The ICBM parameters have to be set up seperately for each channel. + // Some clients (namely Trillian) might refuse sending on channels that were not set up. + sendMessageParams( 0x01 ); + sendMessageParams( 0x02 ); + sendMessageParams( 0x04 ); +} + +void ICBMParamsTask::sendMessageParams( int channel ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending ICBM parameters for channel " << channel << endl; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0004, 0x0002, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + + // the channel for which to set up the parameters + buffer->addWord( channel ); + + //these are all read-write + // channel-flags + // bit 1 : messages allowed (always 1 or you cannot send IMs) + // bit 2 : missed call notifications enabled + // bit 4 : typing notifications enabled + if ( channel == 1 ) + buffer->addDWord(0x0000000B); + else + buffer->addDWord(0x00000003); + + //max message length (8000 bytes) + buffer->addWord(0x1f40); + //max sender warning level (999) + buffer->addWord(0x03e7); + //max receiver warning level (999) + buffer->addWord(0x03e7); + //min message interval limit (0 msec) + buffer->addWord(0x0000); + // unknown parameter + buffer->addWord(0x0000); + + Transfer* st = createTransfer( f, s, buffer ); + send( st ); + setSuccess( 0, QString::null ); +} +//kate: tab-width 4; indent-mode csands; + diff --git a/kopete/protocols/oscar/liboscar/icbmparamstask.h b/kopete/protocols/oscar/liboscar/icbmparamstask.h new file mode 100644 index 00000000..c7bdfb16 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icbmparamstask.h @@ -0,0 +1,56 @@ +/* + Kopete Oscar Protocol + icbmparamstask.h - Get the ICBM parameters + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ +#ifndef ICBMPARAMSTASK_H +#define ICBMPARAMSTASK_H + +#include <task.h> + +/** +Get the parameters we need to follow for instant messages + +@author Matt Rogers +*/ +class ICBMParamsTask : public Task +{ +public: + ICBMParamsTask( Task* parent ); + ~ICBMParamsTask(); + + bool forMe( const Transfer* transfer ) const; + bool take( Transfer* transfer ); + + /** + * Handle the ICBM Parameters we get back from SNAC 0x04, 0x05 + */ + void handleICBMParameters(); + + /** + * Send the message parameters we want back to the server. Only + * appears to occur during login + * @param channel the channel to set up + */ + void sendMessageParams( int channel ); + +protected: + void onGo(); + +}; + +#endif + +//kate: auto-insert-doxygen on; tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/icqlogintask.cpp b/kopete/protocols/oscar/liboscar/icqlogintask.cpp new file mode 100644 index 00000000..c9f5cd52 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icqlogintask.cpp @@ -0,0 +1,117 @@ +/* + Kopete Oscar Protocol + icqlogintask.cpp - Handles logging into to the ICQ service + + Copyright (c) 2004 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 "icqlogintask.h" + +#include <qstring.h> +#include <kdebug.h> +#include "transfer.h" +#include "connection.h" +#include "oscarutils.h" + +using namespace Oscar; + +IcqLoginTask::IcqLoginTask( Task* parent ) + : Task( parent ) +{ +} + +IcqLoginTask::~IcqLoginTask() +{ +} + +bool IcqLoginTask::take( Transfer* transfer ) +{ + Q_UNUSED( transfer ); + return false; +} + +bool IcqLoginTask::forMe( Transfer* transfer ) const +{ + //there shouldn't be a incoming transfer for this task + Q_UNUSED( transfer ); + return false; +} + +void IcqLoginTask::onGo() +{ + FLAP f = { 0x01, 0, 0 }; + DWORD flapVersion = 0x00000001; + Buffer *outbuf = new Buffer(); + + QString encodedPassword = encodePassword( client()->password() ); + const Oscar::ClientVersion* version = client()->version(); + + outbuf->addDWord( flapVersion ); + outbuf->addTLV( 0x0001, client()->userId().length(), client()->userId().latin1() ); + outbuf->addTLV( 0x0002, encodedPassword.length(), encodedPassword.latin1() ); + outbuf->addTLV( 0x0003, version->clientString.length(), version->clientString.latin1() ); + outbuf->addTLV16( 0x0016, version->clientId ); + outbuf->addTLV16( 0x0017, version->major ); + outbuf->addTLV16( 0x0018, version->minor ); + outbuf->addTLV16( 0x0019, version->point ); + outbuf->addTLV16(0x001a, version->build ); + outbuf->addDWord( 0x00140004 ); //TLV type 0x0014, length 0x0004 + outbuf->addDWord( version->other ); //TLV data for type 0x0014 + outbuf->addTLV( 0x000f, version->lang.length(), version->lang.latin1() ); + outbuf->addTLV( 0x000e, version->country.length(), version->country.latin1() ); + + Transfer* ft = createTransfer( f, outbuf ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending ICQ channel 0x01 login packet" << endl; + send( ft ); + emit finished(); +} + + +QString IcqLoginTask::encodePassword( const QString& loginPassword ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Called." << endl; + + // TODO: check if latin1 is the right conversion + const char *password = loginPassword.latin1(); + unsigned int i = 0; + QString encodedPassword = QString::null; + + //encoding table used in ICQ password XOR transformation + unsigned char encoding_table[] = + { + 0xf3, 0x26, 0x81, 0xc4, + 0x39, 0x86, 0xdb, 0x92, + 0x71, 0xa3, 0xb9, 0xe6, + 0x53, 0x7a, 0x95, 0x7c + }; + + for (i = 0; i < 8; i++) + { + if(password[i] == 0) + break; //found a null in the password. don't encode it + encodedPassword.append( password[i] ^ encoding_table[i] ); + } + +#ifdef OSCAR_PWDEBUG + kdDebug(OSCAR_RAW_DEBUG) << " plaintext pw='" << loginPassword << "', length=" << + loginPassword.length() << endl; + + kdDebug(OSCAR_RAW_DEBUG) << " encoded pw='" << encodedPassword << "', length=" << + encodedPassword.length() << endl; +#endif + + return encodedPassword; +} + +#include "icqlogintask.moc" diff --git a/kopete/protocols/oscar/liboscar/icqlogintask.h b/kopete/protocols/oscar/liboscar/icqlogintask.h new file mode 100644 index 00000000..45fb3eb6 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icqlogintask.h @@ -0,0 +1,47 @@ +/* + Kopete Oscar Protocol + icqlogintask.h - Handles logging into to the ICQ service + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef _OSCAR_ICQLOGINTASK_H_ +#define _OSCAR_ICQLOGINTASK_H_ + +#include <oscartypes.h> +#include <task.h> + +class QString; +class Transfer; + +using namespace Oscar; + +class IcqLoginTask : public Task +{ +Q_OBJECT +public: + IcqLoginTask( Task* parent ); + ~IcqLoginTask(); + bool take( Transfer* transfer ); + virtual void onGo(); + +protected: + bool forMe( Transfer* transfer ) const; + +private: + QString encodePassword( const QString& pw ); + +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/icqtask.cpp b/kopete/protocols/oscar/liboscar/icqtask.cpp new file mode 100644 index 00000000..a383922f --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icqtask.cpp @@ -0,0 +1,151 @@ +/* + Kopete Oscar Protocol + icqtask.cpp - SNAC 0x15 parsing + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + 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 "icqtask.h" +#include "buffer.h" +#include "connection.h" + +#include <qstring.h> + +#include <kdebug.h> + +ICQTask::ICQTask( Task * parent ) + : Task( parent ) +{ + m_icquin = client()->userId().toULong(); + m_sequence = 0; + m_requestType = 0xFFFF; + m_requestSubType = 0xFFFF; +} + +ICQTask::~ ICQTask() +{ +} + +void ICQTask::onGo() +{ +} + +bool ICQTask::forMe( const Transfer *t ) const +{ + Q_UNUSED( t ); + return false; +} + +bool ICQTask::take( Transfer* t ) +{ + Q_UNUSED( t ); + return false; +} + +void ICQTask::parseInitialData( Buffer buf ) +{ + int tlvLength = 0; + WORD sequence = 0; + TLV tlv1 = buf.getTLV(); + Buffer tlv1Buffer(tlv1.data, tlv1.length); + tlvLength = tlv1Buffer.getLEWord(); //FIXME handle the data chunk size + m_icquin = tlv1Buffer.getLEDWord(); //nice ICQ UIN + m_requestType = tlv1Buffer.getLEWord(); //request type + sequence = tlv1Buffer.getLEWord(); + if ( m_requestType == 0x07DA ) //there's an extra data subtype + m_requestSubType = tlv1Buffer.getLEWord(); + else + m_requestSubType = 0xFFFF; + +/*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "uin: " << m_icquin << " sequence: " << sequence + <<" request type: 0x" << QString::number( m_requestType, 16 ) + << " request sub type: 0x" << QString::number( m_requestSubType, 16 ) << endl;*/ +} + +Buffer* ICQTask::addInitialData( Buffer* buf ) const +{ + if ( m_requestType == 0xFFFF ) + { //something very wrong here + return 0; + } + + Buffer* tlvData = new Buffer(); + tlvData->addLEDWord( m_icquin ); // UIN + tlvData->addLEWord( m_requestType ); // request type + tlvData->addLEWord( m_sequence ); + + if ( m_requestSubType != 0xFFFF ) + tlvData->addLEWord( m_requestSubType ); + + /*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "uin: " << m_icquin << " sequence: " << sequence + <<" request type: 0x" << QString::number( m_requestType, 16 ) + << " request sub type: 0x" << QString::number( m_requestSubType, 16 ) << endl; */ + if ( buf != 0 ) + tlvData->addString( buf->buffer(), buf->length() ); + + Buffer* newBuffer = new Buffer(); + //add TLV 1 + newBuffer->addWord( 0x0001 ); //TLV 1 + newBuffer->addWord( tlvData->length() + 2 ); //TLV length + newBuffer->addLEWord( tlvData->length() ); // data chunk size + newBuffer->addString( tlvData->buffer(), tlvData->length() ); + + delete tlvData; + + return newBuffer; +} + +DWORD ICQTask::uin() const +{ + return m_icquin; +} + +void ICQTask::setUin( DWORD uin ) +{ + m_icquin = uin; +} + +WORD ICQTask::sequence() const +{ + return m_sequence; +} + +void ICQTask::setSequence( WORD sequence ) +{ + m_sequence = sequence; +} + +DWORD ICQTask::requestType() const +{ + return m_requestType; +} + +void ICQTask::setRequestType( WORD type ) +{ + m_requestType = type; +} + +DWORD ICQTask::requestSubType() const +{ + return m_requestSubType; +} + +void ICQTask::setRequestSubType( WORD subType ) +{ + m_requestSubType = subType; +} + +#include "icqtask.moc" + +//kate: space-indent off; tab-width 4; replace-tabs off; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/icqtask.h b/kopete/protocols/oscar/liboscar/icqtask.h new file mode 100644 index 00000000..7d134b49 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icqtask.h @@ -0,0 +1,63 @@ +/* + Kopete Oscar Protocol + icqtask.h - SNAC 0x15 parsing + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef ICQTASK_H +#define ICQTASK_H + +#include "task.h" + +using namespace Oscar; + +class Buffer; + +class ICQTask : public Task +{ +Q_OBJECT +public: + ICQTask( Task* parent ); + ~ICQTask(); + + virtual void onGo(); + virtual bool forMe( const Transfer* t ) const; + virtual bool take( Transfer* t ); + + void parseInitialData( Buffer buf ); + Buffer* addInitialData( Buffer* buf = 0 ) const; + + DWORD uin() const; + void setUin( DWORD uin ); + + WORD sequence() const; + void setSequence( WORD seqeunce ); + + DWORD requestType() const; + void setRequestType( WORD type ); + + DWORD requestSubType() const; + void setRequestSubType( WORD subType ); + +private: + DWORD m_icquin; //little endian + WORD m_sequence; + WORD m_requestType; //little endian + WORD m_requestSubType; //little endian +}; + +//kate: tab-width 4; indent-mode csands; + +#endif diff --git a/kopete/protocols/oscar/liboscar/icquserinfo.cpp b/kopete/protocols/oscar/liboscar/icquserinfo.cpp new file mode 100644 index 00000000..f853c045 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icquserinfo.cpp @@ -0,0 +1,262 @@ +/* + Kopete Oscar Protocol + icquserinfo.h - ICQ User Info Data Types + + Copyright (c) 2004 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 "icquserinfo.h" +#include "buffer.h" + +#include <kdebug.h> + +ICQShortInfo::ICQShortInfo() +{ + uin = 0; + needsAuth = false; + gender = 0; +} + +void ICQShortInfo::fill( Buffer* buffer ) +{ + if ( buffer->getByte() == 0x0A ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Parsing ICQ short user info packet" << endl; + nickname = buffer->getLELNTS(); + firstName = buffer->getLELNTS(); + lastName = buffer->getLELNTS(); + email = buffer->getLELNTS(); + needsAuth = buffer->getByte(); + buffer->skipBytes( 1 ); //skip the unknown byte + gender = buffer->getByte(); + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Couldn't parse ICQ short user info packet" << endl; +} + +ICQGeneralUserInfo::ICQGeneralUserInfo() +{ + uin = 0; + country = 0; + timezone = 0; + publishEmail = false; + webaware = false; + allowsDC = false; +} + +void ICQGeneralUserInfo::fill( Buffer* buffer ) +{ + if ( buffer->getByte() == 0x0A ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Parsing ICQ basic user info packet" << endl; + nickname = buffer->getLELNTS(); + firstName = buffer->getLELNTS(); + lastName = buffer->getLELNTS(); + email = buffer->getLELNTS(); + city = buffer->getLELNTS(); + state = buffer->getLELNTS(); + phoneNumber = buffer->getLELNTS(); + faxNumber = buffer->getLELNTS(); + address = buffer->getLELNTS(); + cellNumber = buffer->getLELNTS(); + zip = buffer->getLELNTS(); + country = buffer->getLEWord(); + timezone = buffer->getLEByte(); // UTC+(tzcode * 30min) + webaware = ( buffer->getByte() == 0x01 ); + allowsDC = ( buffer->getByte() == 0x01 ); //taken from sim + publishEmail = ( buffer->getByte() == 0x01 ); + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Couldn't parse ICQ basic user info packet" << endl; +} + +ICQWorkUserInfo::ICQWorkUserInfo() +{ + country = 0; + occupation = 0; +} + +void ICQWorkUserInfo::fill( Buffer* buffer ) +{ + if ( buffer->getByte() == 0x0A ) + { + city = buffer->getLELNTS(); + state = buffer->getLELNTS(); + phone = buffer->getLELNTS(); + fax = buffer->getLELNTS(); + address = buffer->getLELNTS(); + zip = buffer->getLELNTS(); + country = buffer->getLEWord(); + company = buffer->getLELNTS(); + department = buffer->getLELNTS(); + position = buffer->getLELNTS(); + occupation = buffer->getLEWord(); + homepage = buffer->getLELNTS(); + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Couldn't parse ICQ work user info packet" << endl; +} + +ICQMoreUserInfo::ICQMoreUserInfo() +{ + age = 0; + gender = 0; + lang1 = 0; + lang2 = 0; + lang3 = 0; + ocountry = 0; + marital = 0; +} + +void ICQMoreUserInfo::fill( Buffer* buffer ) +{ + if ( buffer->getByte() == 0x0A ) + { + age = buffer->getLEWord(); + gender = buffer->getByte(); + homepage = buffer->getLELNTS(); + WORD year = buffer->getLEWord(); + BYTE month = buffer->getByte(); + BYTE day = buffer->getByte(); + + // set birthday to NULL if at least one of the values in the buffer is 0 + if ( year == 0 || month == 0 || day == 0 ) + birthday = QDate(); + else + birthday = QDate( year, month, day ); + + lang1 = buffer->getByte(); + lang2 = buffer->getByte(); + lang3 = buffer->getByte(); + buffer->getLEWord(); //emtpy field + ocity = buffer->getLELNTS(); + ostate = buffer->getLELNTS(); + ocountry = buffer->getLEWord(); + marital = buffer->getLEWord(); + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Couldn't parse ICQ work user info packet" << endl; +} + +ICQEmailInfo::ICQEmailInfo() +{ +} + +void ICQEmailInfo::fill( Buffer* buffer ) +{ + if ( buffer->getByte() == 0x0A ) + { + int numEmails = buffer->getByte(); + QString email; + for ( int i = 0; i < numEmails; i++ ) + { + if ( buffer->getByte() == 0x00 ) + email = buffer->getLELNTS(); + } + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Coudln't parse ICQ email user info packet" << endl; +} + +ICQInterestInfo::ICQInterestInfo() +{ + count=0; +} + +void ICQInterestInfo::fill( Buffer* buffer ) +{ + if ( buffer->getByte() == 0x0A ) + { + count=0; //valid interests + int len= buffer->getByte(); //interests we get + for ( int i = 0; i < len; i++ ) + { + int t=buffer->getLEWord(); + QCString d = buffer->getLELNTS(); + if (t>0) { //there is some topic + if (count<4) { //i think this could not happen, i have never seen more + topics[count]=t; + descriptions[count]=d; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got topic: "<<topics[count]<<" desc: " << topics[count] << endl; + count++; + } else { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got more than four interest infos" << endl; + } + } + } + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "LEN: "<< len << " COUNT: " << count<< endl; + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Coudln't parse ICQ interest user info packet" << endl; +} + +ICQSearchResult::ICQSearchResult() +{ + auth = false; + online = false; + gender = 'U'; +} + +void ICQSearchResult::fill( Buffer* buffer ) +{ + WORD datalength = buffer->getLEWord(); // data length + WORD len = 0; + uin = buffer->getLEDWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Found UIN " << QString::number( uin ) << endl; + len = buffer->getLEWord(); + if ( len > 0 ) + nickName = QCString( buffer->getBlock( len ) ); + + len = buffer->getLEWord(); + if ( len > 0 ) + firstName = QCString( buffer->getBlock( len ) ); + + len = buffer->getLEWord(); + if ( len > 0 ) + lastName = QCString( buffer->getBlock( len ) ); + + len = buffer->getLEWord(); + if ( len > 0 ) + email = QCString( buffer->getBlock( len ) ); + + auth = ( buffer->getByte() != 0x01 ); + online = ( buffer->getLEWord() == 0x0001 ); + switch ( buffer->getByte() ) + { + case 0x00: + gender = 'M'; + break; + case 0x01: + gender = 'F'; + break; + default: + gender = 'U'; + break; + } + age = buffer->getLEWord(); +} + +ICQWPSearchInfo::ICQWPSearchInfo() +{ + age = 0; + gender = 0; + language = 0; + country = 0; + occupation = 0; + onlineOnly = false; +} + + + +//kate: space-indent off; tab-width 4; replace-tabs off; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/icquserinfo.h b/kopete/protocols/oscar/liboscar/icquserinfo.h new file mode 100644 index 00000000..ac054721 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icquserinfo.h @@ -0,0 +1,213 @@ +/* + Kopete Oscar Protocol + icquserinfo.h - ICQ User Info Data Types + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef _ICQUSERINFO_H_ +#define _ICQUSERINFO_H_ + +#include <qcstring.h> +#include <qvaluelist.h> +#include <qdatetime.h> +#include "kopete_export.h" + +class Buffer; + +/** + * @file icquserinfo.h + * Classes encapsulating user data retrieved from the server + */ + +class KOPETE_EXPORT ICQInfoBase +{ +public: + + ICQInfoBase() : m_sequence( 0 ) {} + virtual ~ICQInfoBase() {} + virtual void fill( Buffer* buffer ) = 0; + + void setSequenceNumber( int number ) { m_sequence = number; } + int sequenceNumber() { return m_sequence; } + +private: + int m_sequence; +}; + + +class KOPETE_EXPORT ICQShortInfo : public ICQInfoBase +{ +public: + ICQShortInfo(); + ~ICQShortInfo() {} + void fill( Buffer* buffer ); + +public: + unsigned long uin; + QCString nickname; + QCString firstName; + QCString lastName; + QCString email; + bool needsAuth; + unsigned int gender; // 0=offline, 1=online, 2=not webaware +}; + +class KOPETE_EXPORT ICQGeneralUserInfo : public ICQInfoBase +{ +public: + ICQGeneralUserInfo(); + ~ICQGeneralUserInfo() {} + void fill( Buffer* buffer ); + +public: + unsigned long uin; + QCString nickname; + QCString firstName; + QCString lastName; + QCString email; + QCString city; + QCString state; + QCString phoneNumber; + QCString faxNumber; + QCString address; + QCString cellNumber; + QCString zip; + int country; + char timezone; + bool publishEmail; + bool allowsDC; + bool webaware; +}; + +class KOPETE_EXPORT ICQWorkUserInfo : public ICQInfoBase +{ +public: + ICQWorkUserInfo(); + ~ICQWorkUserInfo() {} + void fill( Buffer* buffer ); + +public: + QCString city; + QCString state; + QCString phone; + QCString fax; + QCString address; + QCString zip; + int country; + QCString company; + QCString department; + QCString position; + int occupation; + QCString homepage; +}; + +class KOPETE_EXPORT ICQMoreUserInfo : public ICQInfoBase +{ +public: + ICQMoreUserInfo(); + ~ICQMoreUserInfo() {} + void fill( Buffer* buffer ); + +public: + int age; + unsigned int gender; + QCString homepage; + QDate birthday; + unsigned int lang1; + unsigned int lang2; + unsigned int lang3; + QCString ocity; + QCString ostate; + int ocountry; + int marital; +}; + +class KOPETE_EXPORT ICQEmailInfo : public ICQInfoBase +{ +public: + ICQEmailInfo(); + ~ICQEmailInfo() {} + void fill( Buffer* buffer ); + +public: + QValueList<QCString> emailList; +}; + +class KOPETE_EXPORT ICQInterestInfo : public ICQInfoBase +{ +public: + ICQInterestInfo(); + ~ICQInterestInfo() {} + void fill( Buffer* buffer ); + +public: + int count; + int topics[4]; + QCString descriptions[4]; +}; + + +class KOPETE_EXPORT ICQSearchResult +{ +public: + ICQSearchResult(); + void fill( Buffer* buffer ); + Q_UINT32 uin; + QCString firstName; + QCString lastName; + QCString nickName; + QCString email; + bool auth; + bool online; + char gender; + Q_UINT16 age; +}; + +class KOPETE_EXPORT ICQWPSearchInfo +{ +public: + ICQWPSearchInfo(); + + QCString firstName; + QCString lastName; + QCString nickName; + QCString email; + int age; + int gender; + int language; + QCString city; + QCString state; + int country; + QCString company; + QCString department; + QCString position; + int occupation; + bool onlineOnly; +}; + +/* +class ICQInfoItem +{ +public: + int category; + QCString description; +}; + + +typedef QValueList<ICQInfoItem> ICQInfoItemList; +*/ + +#endif +//kate: space-indent off; tab-width 4; replace-tabs off; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/icquserinfotask.cpp b/kopete/protocols/oscar/liboscar/icquserinfotask.cpp new file mode 100644 index 00000000..068ac273 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icquserinfotask.cpp @@ -0,0 +1,234 @@ +/* + Kopete Oscar Protocol + icqtask.h - SNAC 0x15 parsing + + Copyright (c) 2004 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 "icquserinfotask.h" +#include <kdebug.h> +#include "connection.h" +#include "transfer.h" +#include "buffer.h" + + +ICQUserInfoRequestTask::ICQUserInfoRequestTask( Task* parent ) : ICQTask( parent ) +{ + //by default, request short info. it saves bandwidth + m_type = Short; +} + + +ICQUserInfoRequestTask::~ICQUserInfoRequestTask() +{ +} + + +bool ICQUserInfoRequestTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer * st = dynamic_cast<const SnacTransfer*>( transfer ); + + if ( !st ) + return false; + + if ( st->snacService() != 0x0015 || st->snacSubtype() != 0x0003 ) + return false; + + Buffer buf( *( st->buffer() ) ); + const_cast<ICQUserInfoRequestTask*>( this )->parseInitialData( buf ); + + if ( requestType() == 0x07DA ) + { + switch ( requestSubType() ) + { + case 0x00C8: + case 0x00D2: + case 0x00DC: + case 0x00E6: + case 0x00EB: + case 0x00F0: + case 0x00FA: + case 0x0104: + case 0x010E: + return true; + default: + return false; + } + } + + return false; +} + +bool ICQUserInfoRequestTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + ICQGeneralUserInfo genInfo; + ICQWorkUserInfo workInfo; + ICQMoreUserInfo moreInfo; + ICQEmailInfo emailInfo; + ICQShortInfo shortInfo; + ICQInterestInfo interestInfo; + + setTransfer( transfer ); + TLV tlv1 = transfer->buffer()->getTLV(); + Buffer* buffer = new Buffer( tlv1.data, tlv1.length ); + + //FIXME this is silly. parseInitialData should take care of this for me. + buffer->skipBytes( 8 ); + WORD seq = buffer->getLEWord(); // request sequence number + buffer->getLEWord(); // request data sub type + QString contactId = m_contactSequenceMap[seq]; + + switch ( requestSubType() ) + { + case 0x00C8: //basic user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received basic info" << endl; + genInfo.setSequenceNumber( seq ); + genInfo.fill( buffer ); + m_genInfoMap[seq] = genInfo; + break; + case 0x00D2: //work user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received work info" << endl; + workInfo.setSequenceNumber( seq ); + workInfo.fill( buffer ); + m_workInfoMap[seq] = workInfo; + break; + case 0x00DC: //more user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received more info" << endl; + moreInfo.setSequenceNumber( seq ); + moreInfo.fill( buffer ); + m_moreInfoMap[seq] = moreInfo; + break; + case 0x00E6: //notes user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Got Notes info, but we don't support it yet" << endl; + break; + case 0x00EB: //email user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received email info" << endl; + emailInfo.setSequenceNumber( seq ); + emailInfo.fill( buffer ); + m_emailInfoMap[seq] = emailInfo; + break; + case 0x00F0: //interests user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received interest info" << endl; + interestInfo.setSequenceNumber( seq ); + interestInfo.fill( buffer ); + m_interestInfoMap[seq] = interestInfo; + break; + case 0x00FA: //affliations user info + //affliations seems to be the last info we get, so be hacky and only emit the signal once + emit receivedInfoFor( contactId, Long ); + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Got affliations info, but we don't support it yet" << endl; + break; + case 0x0104: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received short user info" << endl; + shortInfo.setSequenceNumber( seq ); + shortInfo.fill( buffer ); + m_shortInfoMap[seq] = shortInfo; + break; + case 0x010E: //homepage category user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Got homepage category info, but we don't support it yet" << endl; + break; + default: + break; + } + + + if ( m_type == Short ) + emit receivedInfoFor( contactId, Short ); + + setTransfer( 0 ); + return true; + } + return false; +} + +void ICQUserInfoRequestTask::onGo() +{ + if ( m_userToRequestFor.isNull() ) + return; + + Buffer* sendBuf = 0L; + Buffer b; + if ( m_type != Short ) + { + setRequestSubType( 0x04D0 ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Requesting full user info for " << m_userToRequestFor << endl; + } + else + { + setRequestSubType( 0x04BA ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Requesting short user info for " << m_userToRequestFor << endl; + } + + setSequence( client()->snacSequence() ); + setRequestType( 0x07D0 ); + b.addLEDWord( m_userToRequestFor.toULong() ); + sendBuf = addInitialData( &b ); + + m_contactSequenceMap[sequence()] = m_userToRequestFor; + m_reverseContactMap[m_userToRequestFor] = sequence(); + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0015, 0x0002, 0, client()->snacSequence() }; + Transfer* t = createTransfer( f, s, sendBuf ); + send( t ); +} + +ICQGeneralUserInfo ICQUserInfoRequestTask::generalInfoFor( const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_genInfoMap[seq]; +} + +ICQWorkUserInfo ICQUserInfoRequestTask::workInfoFor( const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_workInfoMap[seq]; +} + +ICQMoreUserInfo ICQUserInfoRequestTask::moreInfoFor( const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_moreInfoMap[seq]; +} + +ICQEmailInfo ICQUserInfoRequestTask::emailInfoFor(const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_emailInfoMap[seq]; +} + +ICQShortInfo ICQUserInfoRequestTask::shortInfoFor( const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_shortInfoMap[seq]; +} + +ICQInterestInfo ICQUserInfoRequestTask::interestInfoFor( const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_interestInfoMap[seq]; +} + +QString ICQUserInfoRequestTask::notesInfoFor( const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_notesInfoMap[seq]; +} + + +#include "icquserinfotask.moc" + +//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/liboscar/icquserinfotask.h b/kopete/protocols/oscar/liboscar/icquserinfotask.h new file mode 100644 index 00000000..eba81b57 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icquserinfotask.h @@ -0,0 +1,77 @@ +/* + Kopete Oscar Protocol + icquserinfotask.h - SNAC 0x15 parsing for user info + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef ICQUSERINFOTASK_H +#define ICQUSERINFOTASK_H + +#include <qmap.h> +#include <qstring.h> + +#include "icqtask.h" +#include "icquserinfo.h" + +class Transfer; + +/** +@author Kopete Developers +*/ +class ICQUserInfoRequestTask : public ICQTask +{ +Q_OBJECT +public: + ICQUserInfoRequestTask( Task* parent ); + ~ICQUserInfoRequestTask(); + + enum { Long = 0, Short }; + + void setUser( const QString& user ) { m_userToRequestFor = user; } + void setType( unsigned int type ) { m_type = type; } + void setInfoToRequest( unsigned int type ); + + ICQGeneralUserInfo generalInfoFor( const QString& contact ); + ICQEmailInfo emailInfoFor( const QString& contact ); + ICQMoreUserInfo moreInfoFor( const QString& contact ); + ICQWorkUserInfo workInfoFor( const QString& contact ); + QString notesInfoFor( const QString& contact ); + ICQShortInfo shortInfoFor( const QString& contact ); + ICQInterestInfo interestInfoFor( const QString& contact ); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + +signals: + void receivedInfoFor( const QString& contact, unsigned int type ); + +private: + QMap<int, ICQGeneralUserInfo> m_genInfoMap; + QMap<int, ICQEmailInfo> m_emailInfoMap; + QMap<int, ICQMoreUserInfo> m_moreInfoMap; + QMap<int, ICQWorkUserInfo> m_workInfoMap; + QMap<int, ICQShortInfo> m_shortInfoMap; + QMap<int, ICQInterestInfo> m_interestInfoMap; + QMap<int, QString> m_notesInfoMap; + QMap<int, QString> m_contactSequenceMap; + QMap<QString, int> m_reverseContactMap; + unsigned int m_type; + QString m_userToRequestFor; + +}; +#endif + +//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/liboscar/inputprotocolbase.cpp b/kopete/protocols/oscar/liboscar/inputprotocolbase.cpp new file mode 100644 index 00000000..abd34e53 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/inputprotocolbase.cpp @@ -0,0 +1,100 @@ +/* + Kopete Groupwise Protocol + inputprotocolbase.cpp - Ancestor of all protocols used for reading GroupWise input + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + 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 "inputprotocolbase.h" + +InputProtocolBase::InputProtocolBase(QObject *parent, const char *name) + : QObject(parent, name) +{ + m_state = NeedMore; + m_bytes = 0; +} + + +InputProtocolBase::~InputProtocolBase() +{ +} + +uint InputProtocolBase::state() const +{ + return m_state; +} + +bool InputProtocolBase::readString( QString &message ) +{ + uint len; + QCString rawData; + if ( !safeReadBytes( rawData, len ) ) + return false; + message = QString::fromUtf8( rawData.data(), len - 1 ); + return true; +} + + +bool InputProtocolBase::okToProceed() +{ + if ( m_din ) + { + if ( m_din->atEnd() ) + { + m_state = NeedMore; + qDebug( "InputProtocol::okToProceed() - Server message ended prematurely!" ); + } + else + return true; + } + return false; +} + +bool InputProtocolBase::safeReadBytes( QCString & data, uint & len ) +{ + // read the length of the bytes + Q_UINT32 val; + if ( !okToProceed() ) + return false; + *m_din >> val; + m_bytes += sizeof( Q_UINT32 ); + if ( val > 1024 ) + return false; + //qDebug( "EventProtocol::safeReadBytes() - expecting %i bytes", val ); + QCString temp( val ); + if ( val != 0 ) + { + if ( !okToProceed() ) + return false; + // if the server splits packets here we are in trouble, + // as there is no way to see how much data was actually read + m_din->readRawBytes( temp.data(), val ); + // the rest of the string will be filled with FF, + // so look for that in the last position instead of \0 + // this caused a crash - guessing that temp.length() is set to the number of bytes actually read... + // if ( (Q_UINT8)( * ( temp.data() + ( temp.length() - 1 ) ) ) == 0xFF ) + if ( temp.length() < ( val - 1 ) ) + { + qDebug( "InputProtocol::safeReadBytes() - string broke, giving up, only got: %i bytes out of %i", temp.length(), val ); + m_state = NeedMore; + return false; + } + } + data = temp; + len = val; + m_bytes += val; + return true; +} + +#include "inputprotocolbase.moc" diff --git a/kopete/protocols/oscar/liboscar/inputprotocolbase.h b/kopete/protocols/oscar/liboscar/inputprotocolbase.h new file mode 100644 index 00000000..7bea895f --- /dev/null +++ b/kopete/protocols/oscar/liboscar/inputprotocolbase.h @@ -0,0 +1,72 @@ +/* + Kopete Groupwise Protocol + inputprotocolbase.h - Ancestor of all protocols used for reading GroupWise input + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + 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. * + * * + ************************************************************************* +*/ + +#ifndef INPUTPROTOCOLBASE_H +#define INPUTPROTOCOLBASE_H + +#include <qobject.h> + +class Transfer; +/** +Defines a basic interface for protocols dealing with input from the GroupWise server. + +@author Matt Rogers +*/ +class InputProtocolBase : public QObject +{ +Q_OBJECT +public: + enum EventProtocolState { Success, NeedMore, OutOfSync, ProtocolError }; + InputProtocolBase(QObject *parent = 0, const char *name = 0); + ~InputProtocolBase(); + /** + * Returns a value describing the state of the object. + * If the object is given data to parse that does not begin with a recognised event code, + * it will become OutOfSync, to indicate that the input data probably contains leftover data not processed during a previous parse. + */ + uint state() const; + /** + * Attempt to parse the supplied data into a Transfer object + * @param bytes this will be set to the number of bytes that were successfully parsed. It is no indication of the success of the whole procedure + * @return On success, a Transfer object that the caller is responsible for deleting. It will be either an EventTransfer or a Response, delete as appropriate. On failure, returns 0. + */ + virtual Transfer * parse( const QByteArray &, uint & bytes ) = 0 ; +protected: + /** + * Reads an arbitrary string + * updates the bytes parsed counter + */ + bool readString( QString &message ); + /** + * Check that there is data to read, and set the protocol's state if there isn't any. + */ + bool okToProceed(); + /** + * read a Q_UINT32 giving the number of following bytes, then a string of that length + * updates the bytes parsed counter + * @return false if the string was broken or there was no data available at all + */ + bool safeReadBytes( QCString & data, uint & len ); + +protected: + uint m_state; + uint m_bytes; + QDataStream * m_din; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/locationrightstask.cpp b/kopete/protocols/oscar/liboscar/locationrightstask.cpp new file mode 100644 index 00000000..5aae9a5e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/locationrightstask.cpp @@ -0,0 +1,87 @@ +/* + Kopete Oscar Protocol + locationrightstask.cpp - Set up the service limitations + + Copyright (c) 2004 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 "locationrightstask.h" +#include <kdebug.h> +#include "buffer.h" +#include "transfer.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "connection.h" + +using namespace Oscar; + +LocationRightsTask::LocationRightsTask( Task* parent ) + : Task( parent ) +{ +} + + +LocationRightsTask::~LocationRightsTask() +{ +} + + +bool LocationRightsTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + + if (!st) + return false; + + if ( st->snacService() == 2 && st->snacSubtype() == 3 ) + return true; + else + return false; +} + +bool LocationRightsTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + handleLocationRightsResponse(); + setTransfer( 0 ); + return true; + } + else + return false; +} + +void LocationRightsTask::onGo() +{ + sendLocationRightsRequest(); +} + +void LocationRightsTask::sendLocationRightsRequest() +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0002, 0x0002, 0x0000, client()->snacSequence() }; + Buffer* b = new Buffer(); + Transfer* st = createTransfer( f, s, b ); + send( st ); +} + +void LocationRightsTask::handleLocationRightsResponse() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Ignoring location rights response" << endl; + setSuccess( 0, QString::null ); +} + + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/locationrightstask.h b/kopete/protocols/oscar/liboscar/locationrightstask.h new file mode 100644 index 00000000..a3e82767 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/locationrightstask.h @@ -0,0 +1,57 @@ +/* + Kopete Oscar Protocol + locationrightstask.h - Set up the service limitations + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef LOCATIONRIGHTSTASK_H +#define LOCATIONRIGHTSTASK_H + +#include <task.h> + +class Transfer; + +using namespace Oscar; + +/** +This task handles location rights. +This task implements the following SNACS: + \li 0x02, 0x02 + \li 0x02, 0x03 + +@author Kopete Developers +*/ +class LocationRightsTask : public Task +{ +public: + LocationRightsTask( Task* parent ); + ~LocationRightsTask(); + bool take( Transfer* transfer ); + +protected: + bool forMe( const Transfer* transfer ) const; + void onGo(); + +private: + //! Send the location rights request ( SNAC 0x02, 0x02 ) + void sendLocationRightsRequest(); + + //! Handle the location rights reply ( SNAC 0x02, 0x03 ) + void handleLocationRightsResponse(); +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/logintask.cpp b/kopete/protocols/oscar/liboscar/logintask.cpp new file mode 100644 index 00000000..962b2e1a --- /dev/null +++ b/kopete/protocols/oscar/liboscar/logintask.cpp @@ -0,0 +1,218 @@ +/* + Kopete Oscar Protocol + logintask.cpp - Handles logging into to the AIM or ICQ service + + Copyright (c) 2004 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 "logintask.h" + +#include <qtimer.h> +#include <kdebug.h> +#include <klocale.h> + +#include "aimlogintask.h" +#include "connection.h" +#include "closeconnectiontask.h" +#include "icqlogintask.h" +#include "oscarutils.h" +#include "rateinfotask.h" +#include "serverversionstask.h" +#include "transfer.h" + + + +/** + * Stage One Task Implementation + */ + +StageOneLoginTask::StageOneLoginTask( Task* parent ) + : Task ( parent ) +{ + m_aimTask = 0L; + m_icqTask = 0L; + m_closeTask = 0L; +} + +StageOneLoginTask::~StageOneLoginTask() +{ + delete m_aimTask; + delete m_icqTask; + delete m_closeTask; +} + +bool StageOneLoginTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + if ( client()->isIcq() ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Starting ICQ login" << endl; + m_icqTask = new IcqLoginTask( client()->rootTask() ); + m_closeTask = new CloseConnectionTask( client()->rootTask() ); + + //connect finished signal + connect( m_closeTask, SIGNAL( finished() ), this, SLOT( closeTaskFinished() ) ); + m_icqTask->go( true ); + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Starting AIM login" << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending the FLAP version back" << endl; + + //send the flap version response + FLAP f = { 0x01, 0 , 0 }; + Buffer *outbuf = new Buffer; + outbuf->addDWord(0x00000001); //flap version + f.length = outbuf->length(); + Transfer* ft = createTransfer( f, outbuf ); + send( ft ); + + m_aimTask = new AimLoginTask( client()->rootTask() ); + connect( m_aimTask, SIGNAL( finished() ), this, SLOT( aimTaskFinished() ) ); + m_aimTask->go( true ); + } + return true; + } + return false; +} + +void StageOneLoginTask::closeTaskFinished() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + m_cookie = m_closeTask->cookie(); + m_bosPort = m_closeTask->bosPort(); + m_bosServer = m_closeTask->bosHost(); + m_closeTask->safeDelete(); + setSuccess( m_closeTask->statusCode(), m_closeTask->statusString() ); +} + +void StageOneLoginTask::aimTaskFinished() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + m_cookie = m_aimTask->cookie(); + m_bosPort = m_aimTask->bosPort(); + m_bosServer = m_aimTask->bosHost(); + + setSuccess( m_aimTask->statusCode(), m_aimTask->statusString() ); +} + +bool StageOneLoginTask::forMe( Transfer* transfer ) const +{ + FlapTransfer* ft = dynamic_cast<FlapTransfer*> ( transfer ); + + if (!ft) + return false; + + return ( ft && ft->flapChannel() == 1 ); +} + +const QByteArray& StageOneLoginTask::loginCookie() const +{ + return m_cookie; +} + +const QString& StageOneLoginTask::bosServer() const +{ + return m_bosServer; +} + +const QString& StageOneLoginTask::bosPort() const +{ + return m_bosPort; +} + + +/** + * Stage Two Task Implementation + */ +StageTwoLoginTask::StageTwoLoginTask( Task* parent ) + : Task( parent ) +{ + //Create our tasks + Task* rootTask = client()->rootTask(); + m_versionTask = new ServerVersionsTask( rootTask ); + m_rateTask = new RateInfoTask( rootTask ); + + QObject::connect( m_versionTask, SIGNAL( finished() ), this, SLOT( versionTaskFinished() ) ); + QObject::connect( m_rateTask, SIGNAL( finished() ), this, SLOT( rateTaskFinished() ) ); +} + +StageTwoLoginTask::~StageTwoLoginTask() +{ + delete m_versionTask; +} + +bool StageTwoLoginTask::take( Transfer* transfer ) +{ + bool yes = forMe( transfer ); + return yes; +} + +bool StageTwoLoginTask::forMe( Transfer* transfer ) const +{ + FlapTransfer* ft = dynamic_cast<FlapTransfer*>( transfer ); + + if (!ft) + return false; + + int channel = ft->flapChannel(); + if ( channel == 1 ) + return true; + else + return false; +} + +void StageTwoLoginTask::onGo() +{ + if ( !m_cookie.isEmpty() ) + { + //send the flap back + FLAP f = { 0x01, 0, 0 }; + Buffer* outbuf = new Buffer(); + outbuf->addDWord( 0x00000001 ); + outbuf->addTLV( 0x06, m_cookie.size(), m_cookie.data() ); + Transfer* ft = createTransfer( f, outbuf ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending the login cookie back" << endl; + send( ft ); + } + else + setError( -1, QString::null ); + return; +} + +void StageTwoLoginTask::setCookie( const QByteArray& newCookie ) +{ + m_cookie.duplicate( newCookie ); +} + +const QByteArray& StageTwoLoginTask::cookie() +{ + return m_cookie; +} + +void StageTwoLoginTask::versionTaskFinished() +{ + //start the rate info task + m_rateTask->go(true); +} + +void StageTwoLoginTask::rateTaskFinished() +{ + setSuccess( 0, QString::null ); +} + +#include "logintask.moc" + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/logintask.h b/kopete/protocols/oscar/liboscar/logintask.h new file mode 100644 index 00000000..12061821 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/logintask.h @@ -0,0 +1,144 @@ +/* + Kopete Oscar Protocol + logintask.h - Handles logging into to the AIM or ICQ service + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef _OSCAR_LOGINTASK_H_ +#define _OSCAR_LOGINTASK_H_ + +#include <qcstring.h> +#include "oscartypes.h" +#include "task.h" + +#include "aimlogintask.h" +#include "icqlogintask.h" +#include "closeconnectiontask.h" + +using namespace Oscar; + +class QString; +class Transfer; + + +/** + * \short Handle OSCAR login - stage 1 + * + * OSCAR login is divided into two stages. The first stage handles the connection negotiation + * so that we can get a BOS server to connect to and start stage two of the login process + * This class handles the first stage of the OSCAR login process + * For more info about the OSCAR login process, visit http://iserverd1.khstu.ru/oscar + */ +class StageOneLoginTask : public Task +{ +Q_OBJECT +public: + StageOneLoginTask( Task* parent ); + ~StageOneLoginTask(); + bool take( Transfer* transfer ); + + //Protocol specific stuff + + //! Get the BOS cookie + const QByteArray& loginCookie() const; + + //! Get the BOS server + const QString& bosServer() const; + + //! Get the BOS port + const QString& bosPort() const; + + //! Get the error code, if there is one + int errorCode() const; + + //! Get the error reason so it can be displayed + const QString& errorReason() const; + + +public slots: + void closeTaskFinished(); + void aimTaskFinished(); + +protected: + bool forMe( Transfer* transfer ) const; + +private: + + //Tasks we want to control + AimLoginTask* m_aimTask; + IcqLoginTask* m_icqTask; + CloseConnectionTask* m_closeTask; + + //Private data we get from the tasks + QByteArray m_cookie; + QString m_bosServer; + QString m_bosPort; + +}; + +/** + * \short Handle OSCAR Login - stage 2 + * + * Oscar login is divided into two stages. The first stage handles the connection negotiation + * so that we can get a BOS server to connect to for the second stage. This class handles the + * second stage of Oscar login that establishes various things like rate limits, contact lists, + * and SNAC family versions + */ + +class ServerVersionsTask; +class RateInfoTask; + +class StageTwoLoginTask : public Task +{ +Q_OBJECT +public: + StageTwoLoginTask( Task* parent ); + ~StageTwoLoginTask(); + bool take( Transfer* transfer ); + void onGo(); + + //protocol specifics + //! Set the cookie to send to the server + void setCookie( const QByteArray& newCookie ); + + //! Get the cookie to send to the server + const QByteArray& cookie(); + + QString host() const; + QString port() const; + +public slots: + + //! Start the rate info task + void versionTaskFinished(); + + //! The rate info task is finished. Start the other ones + void rateTaskFinished(); + +protected: + bool forMe( Transfer* transfer ) const; + +private: + QByteArray m_cookie; + QString m_host, m_port; + + //tasks + ServerVersionsTask* m_versionTask; + RateInfoTask* m_rateTask; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/md5.c b/kopete/protocols/oscar/liboscar/md5.c new file mode 100644 index 00000000..e6273585 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/md5.c @@ -0,0 +1,392 @@ +/* + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321. + It is derived directly from the text of the RFC and not from the + reference implementation. + + The original and principal author of md5.c is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include <string.h> + +#ifdef TEST +/* + * Compile with -DTEST to create a self-contained executable test program. + * The test program should print out the same values as given in section + * A.5 of RFC 1321, reproduced below. + */ +#include <string.h> +main() +{ + static const char *const test[7] = { + "", /*d41d8cd98f00b204e9800998ecf8427e*/ + "945399884.61923487334tuvga", /*0cc175b9c0f1b6a831c399e269772661*/ + "abc", /*900150983cd24fb0d6963f7d28e17f72*/ + "message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/ + "abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + /*d174ab98d277d9f5a5611c2c9f419d9f*/ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/ + }; + int i; + + for (i = 0; i < 7; ++i) { + md5_state_t state; + md5_byte_t digest[16]; + int di; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i])); + md5_finish(&state, digest); + printf("MD5 (\"%s\") = ", test[i]); + for (di = 0; di < 16; ++di) + printf("%02x", digest[di]); + printf("\n"); + } + return 0; +} +#endif /* TEST */ + + +/* + * For reference, here is the program that computed the T values. + */ +#if 0 +#include <math.h> +main() +{ + int i; + for (i = 1; i <= 64; ++i) { + unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i))); + printf("#define T%d 0x%08lx\n", i, v); + } + return 0; +} +#endif +/* + * End of T computation program. + */ +#define T1 0xd76aa478 +#define T2 0xe8c7b756 +#define T3 0x242070db +#define T4 0xc1bdceee +#define T5 0xf57c0faf +#define T6 0x4787c62a +#define T7 0xa8304613 +#define T8 0xfd469501 +#define T9 0x698098d8 +#define T10 0x8b44f7af +#define T11 0xffff5bb1 +#define T12 0x895cd7be +#define T13 0x6b901122 +#define T14 0xfd987193 +#define T15 0xa679438e +#define T16 0x49b40821 +#define T17 0xf61e2562 +#define T18 0xc040b340 +#define T19 0x265e5a51 +#define T20 0xe9b6c7aa +#define T21 0xd62f105d +#define T22 0x02441453 +#define T23 0xd8a1e681 +#define T24 0xe7d3fbc8 +#define T25 0x21e1cde6 +#define T26 0xc33707d6 +#define T27 0xf4d50d87 +#define T28 0x455a14ed +#define T29 0xa9e3e905 +#define T30 0xfcefa3f8 +#define T31 0x676f02d9 +#define T32 0x8d2a4c8a +#define T33 0xfffa3942 +#define T34 0x8771f681 +#define T35 0x6d9d6122 +#define T36 0xfde5380c +#define T37 0xa4beea44 +#define T38 0x4bdecfa9 +#define T39 0xf6bb4b60 +#define T40 0xbebfbc70 +#define T41 0x289b7ec6 +#define T42 0xeaa127fa +#define T43 0xd4ef3085 +#define T44 0x04881d05 +#define T45 0xd9d4d039 +#define T46 0xe6db99e5 +#define T47 0x1fa27cf8 +#define T48 0xc4ac5665 +#define T49 0xf4292244 +#define T50 0x432aff97 +#define T51 0xab9423a7 +#define T52 0xfc93a039 +#define T53 0x655b59c3 +#define T54 0x8f0ccc92 +#define T55 0xffeff47d +#define T56 0x85845dd1 +#define T57 0x6fa87e4f +#define T58 0xfe2ce6e0 +#define T59 0xa3014314 +#define T60 0x4e0811a1 +#define T61 0xf7537e82 +#define T62 0xbd3af235 +#define T63 0x2ad7d2bb +#define T64 0xeb86d391 + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; + +#ifndef ARCH_IS_BIG_ENDIAN +# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */ +#endif +#if ARCH_IS_BIG_ENDIAN + + /* + * On big-endian machines, we must arrange the bytes in the right + * order. (This also works on machines of unknown byte order.) + */ + md5_word_t X[16]; + const md5_byte_t *xp = data; + int i; + + for (i = 0; i < 16; ++i, xp += 4) + X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + +#else /* !ARCH_IS_BIG_ENDIAN */ + + /* + * On little-endian machines, we can process properly aligned data + * without copying it. + */ + md5_word_t xbuf[16]; + const md5_word_t *X; + + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } +#endif + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = 0xefcdab89; + pms->abcd[2] = 0x98badcfe; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/kopete/protocols/oscar/liboscar/md5.h b/kopete/protocols/oscar/liboscar/md5.h new file mode 100644 index 00000000..501cdbe1 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/md5.h @@ -0,0 +1,93 @@ +/* + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321. + It is derived directly from the text of the RFC and not from the + reference implementation. + + The original and principal author of md5.h is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke <purschke@bnl.gov>. + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This code has some adaptations for the Ghostscript environment, but it + * will compile and run correctly in any environment with 8-bit chars and + * 32-bit ints. Specifically, it assumes that if the following are + * defined, they have the same meaning as in Ghostscript: P1, P2, P3, + * ARCH_IS_BIG_ENDIAN. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +#ifdef P1 +void md5_init(P1(md5_state_t *pms)); +#else +void md5_init(md5_state_t *pms); +#endif + +/* Append a string to the message. */ +#ifdef P3 +void md5_append(P3(md5_state_t *pms, const md5_byte_t *data, int nbytes)); +#else +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); +#endif + +/* Finish the message and return the digest. */ +#ifdef P2 +void md5_finish(P2(md5_state_t *pms, md5_byte_t digest[16])); +#else +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); +#endif + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/kopete/protocols/oscar/liboscar/messagereceivertask.cpp b/kopete/protocols/oscar/liboscar/messagereceivertask.cpp new file mode 100644 index 00000000..2db05eb1 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/messagereceivertask.cpp @@ -0,0 +1,461 @@ +/* + 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 <qtextcodec.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 = QString( b->getBlock( screenNameLength ) ); + setTransfer( transfer ); + handleAutoResponse(); + setTransfer( 0 ); + return true; + } + } + return false; +} + +void MessageReceiverTask::handleType1Message() +{ + Oscar::Message msg; + QValueList<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 ); + QValueList<TLV> innerTLVList = messageBuffer.getTLVList(); + QValueList<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; + QByteArray 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( QDateTime::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; + QValueList<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 ( QString::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; + QString 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; + }; + + QCString 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( QDateTime::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; + + QCString 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; + + QByteArray 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( QDateTime::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; + } +} + +QTextCodec* MessageReceiverTask::guessCodec( const QCString& string ) +{ + Q_UNUSED( string ); + return 0; +} + +#include "messagereceivertask.moc" +//kate: indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/messagereceivertask.h b/kopete/protocols/oscar/liboscar/messagereceivertask.h new file mode 100644 index 00000000..b50a133f --- /dev/null +++ b/kopete/protocols/oscar/liboscar/messagereceivertask.h @@ -0,0 +1,79 @@ +/* + messagereceivertask.h - 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. * + * * + ************************************************************************* +*/ +#ifndef MESSAGERECEIVERTASK_H +#define MESSAGERECEIVERTASK_H + +#include "task.h" +#include <qstring.h> +#include <qcstring.h> +#include "oscarmessage.h" +#include "oscartypeclasses.h" +#include "oscarmessage.h" + +class QTextCodec; + +/** + * Handles receiving messages. + * @author Matt Rogers +*/ +class MessageReceiverTask : public Task +{ +Q_OBJECT +public: + + MessageReceiverTask( Task* parent ); + ~MessageReceiverTask(); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + +signals: + + void receivedMessage( const Oscar::Message& ); + +private: + + //!Handles messages from channel 1 (type 1 messages) + void handleType1Message(); + + //!Handles messages from channel 2 (type 2 messages) + void handleType2Message(); + + //!Handles messages from channel 4 (type 4 messages) + void handleType4Message(); + + //!Handles client auto responses (SNAC 0x04/0x0B) + void handleAutoResponse(); + + //!Parses Rendezvous data in Buffer and puts the information into Message + void parseRendezvousData( Buffer* b, Oscar::Message* msg ); + + QTextCodec* guessCodec( const QCString& string ); + +private: + + QByteArray m_icbmCookie; + int m_channel; + QString m_fromUser; + int m_currentSnacSubtype; + int m_charSet; + int m_subCharSet; + +}; + +#endif + +//kate: indent-mode csands; tab-width 4; diff --git a/kopete/protocols/oscar/liboscar/offlinemessagestask.cpp b/kopete/protocols/oscar/liboscar/offlinemessagestask.cpp new file mode 100644 index 00000000..d97da7a3 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/offlinemessagestask.cpp @@ -0,0 +1,166 @@ +/* + Kopete Oscar Protocol + offlinemessagestask.cpp - Offline messages handling + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + 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 "config.h" +#include "offlinemessagestask.h" + +#include <time.h> + +#include "transfer.h" +#include "buffer.h" +#include "connection.h" + +#include <kdebug.h> + +OfflineMessagesTask::OfflineMessagesTask( Task* parent ) + : ICQTask( parent ) +{ + tzset(); + m_sequence = 0; +} + +OfflineMessagesTask::~OfflineMessagesTask() +{ +} + +void OfflineMessagesTask::onGo() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Requesting offline messages" << endl; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0015, 0x0002, 0x0000, client()->snacSequence() }; + + setRequestType( 0x003c ); //offline message request + setSequence( f.sequence ); + Buffer* buf = addInitialData(); + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + +bool OfflineMessagesTask::forMe( const Transfer* t ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( t ); + + if ( !st ) + return false; + + if ( st->snacService() != 0x0015 || st->snacSubtype() != 0x0003 ) + return false; + + Buffer buf( st->buffer()->buffer(), st->buffer()->length() ); + const_cast<OfflineMessagesTask*>(this)->parseInitialData( buf ); + + if ( requestType() == 0x0041 || requestType() == 0x0042 ) + return true; + + return false; +} + +bool OfflineMessagesTask::take( Transfer* t ) +{ + if ( forMe( t ) ) + { + setTransfer( t ); + + if ( requestType() == 0x0041 ) // Offline message + handleOfflineMessage(); + else if ( requestType() == 0x0042 ) // end-of-offline messages + endOfMessages(); + + setTransfer( 0 ); + return true; + } + return false; +} + +void OfflineMessagesTask::handleOfflineMessage() +{ + TLV tlv1 = transfer()->buffer()->getTLV(); + Buffer* buffer = new Buffer( tlv1.data, tlv1.length ); + + buffer->getLEWord(); // data chunk size + DWORD receiverUin = buffer->getLEDWord(); // target uin + buffer->getLEWord(); // request type + buffer->getLEWord(); // request sequence number: 0x0002 + + DWORD senderUin = buffer->getLEDWord(); + WORD year = buffer->getLEWord(); + BYTE month = buffer->getByte(); + BYTE day = buffer->getByte(); + BYTE hour = buffer->getByte(); + BYTE minute = buffer->getByte(); + + BYTE type = buffer->getByte(); // msg type + BYTE flags = buffer->getByte(); // msg flags + + WORD msgLength = buffer->getLEWord(); + QByteArray msg = buffer->getBlock( msgLength ); + + QDate date(year, month, day); + QTime time(hour,minute); +#ifndef HAVE_TM_GMTOFF + int tz = -( ::timezone ); +#else + int tz; + time_t now; + struct tm *tm; + now = ::time(NULL); + tm = ::localtime(&now); + /* daylight = tm->tm_isdst; // another linuxism */ + tz = (tm->tm_gmtoff) / (60 * 60); +#endif + time = time.addSecs( tz ); + + QDateTime hackyTime( date, time ); + Oscar::Message message( Oscar::Message::UserDefined, msg, type, flags, hackyTime ); + message.setSender( QString::number( senderUin ) ); + message.setReceiver( QString::number( receiverUin ) ); + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received offline message '" << msg.data() << "' from " << senderUin << endl; + + emit receivedOfflineMessage( message ); +} + +void OfflineMessagesTask::endOfMessages() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "End of Offline Messages" << endl; + + TLV tlv1 = transfer()->buffer()->getTLV(); + Buffer* buffer = new Buffer( tlv1.data, tlv1.length ); + + buffer->skipBytes( 8 ); + m_sequence = buffer->getLEWord(); + + deleteOfflineMessages(); + + setSuccess( true ); +} + +void OfflineMessagesTask::deleteOfflineMessages() +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0015, 0x0002, 0x0000, client()->snacSequence() }; + + + setRequestType( 0x003E ); //delete offline messages + setSequence( m_sequence ); + Buffer* buf = addInitialData(); + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + +#include "offlinemessagestask.moc" diff --git a/kopete/protocols/oscar/liboscar/offlinemessagestask.h b/kopete/protocols/oscar/liboscar/offlinemessagestask.h new file mode 100644 index 00000000..da2454d3 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/offlinemessagestask.h @@ -0,0 +1,54 @@ +/* + Kopete Oscar Protocol + offlinemessagestask.h - Offline messages handling + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef OFFLINEMESSAGESTASK_H +#define OFFLINEMESSAGESTASK_H + +#include "icqtask.h" +#include "oscarmessage.h" + +/** +ICQ Offline messages handling + +@author Gustavo Pichorim Boiko +*/ +class OfflineMessagesTask : public ICQTask +{ +Q_OBJECT +public: + OfflineMessagesTask( Task* parent ); + + ~OfflineMessagesTask(); + + virtual void onGo(); + virtual bool forMe( const Transfer* t ) const; + virtual bool take( Transfer* t ); + +signals: + void receivedOfflineMessage( const Oscar::Message& msg ); + +private: + void handleOfflineMessage(); + void endOfMessages(); + void deleteOfflineMessages(); + +private: + int m_sequence; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/onlinenotifiertask.cpp b/kopete/protocols/oscar/liboscar/onlinenotifiertask.cpp new file mode 100644 index 00000000..785e23f7 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/onlinenotifiertask.cpp @@ -0,0 +1,99 @@ +/* + Kopete Oscar Protocol + onlinenotifiertask.cpp - handles all the status notifications + + Copyright (c) 2004 by 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-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 "onlinenotifiertask.h" +#include "buffer.h" +#include "connection.h" +#include "oscartypes.h" +#include "transfer.h" + +#include <kdebug.h> + +OnlineNotifierTask::OnlineNotifierTask( Task* parent ) : Task( parent ) +{ +} + + +OnlineNotifierTask::~OnlineNotifierTask() +{ +} + + +bool OnlineNotifierTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0003 ) + { + switch ( st->snacSubtype() ) + { + case 0x000B: + case 0x000C: + return true; + }; + } + return false; +} + +bool OnlineNotifierTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + if ( st ) + { + setTransfer( transfer ); + + if ( st->snacSubtype() == 0x000B ) + userOnline(); + else + userOffline(); + + setTransfer( 0 ); + } + return true; + } + return false; +} + +void OnlineNotifierTask::userOnline() +{ + Buffer* buffer = transfer()->buffer(); + UserDetails ud; + ud.fill( buffer ); + QString user = ud.userId(); + //kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << user << " is now online" << endl; + emit userIsOnline( user, ud ); +} + +void OnlineNotifierTask::userOffline() +{ + Buffer* buffer = transfer()->buffer(); + UserDetails ud; + ud.fill( buffer ); + QString user = ud.userId(); + //kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << user << " is now offline" << endl; + emit userIsOffline( user, ud ); +} + +#include "onlinenotifiertask.moc" +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/onlinenotifiertask.h b/kopete/protocols/oscar/liboscar/onlinenotifiertask.h new file mode 100644 index 00000000..2ec58e5a --- /dev/null +++ b/kopete/protocols/oscar/liboscar/onlinenotifiertask.h @@ -0,0 +1,60 @@ +/* + Kopete Oscar Protocol + onlinenotifiertask.h - handles all the status notifications + + Copyright (c) 2004 by 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-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. * + * * + ************************************************************************* +*/ +#ifndef ONLINENOTIFIERTASK_H +#define ONLINENOTIFIERTASK_H + +#include <task.h> +#include "userdetails.h" + +class Transfer; +class QString; +/** +Tracks status notifications (online, offline, etc.) for contacts +Implements SNACS (0x03, 0x11) and (0x03, 0x12) + +@author Matt Rogers +*/ +class OnlineNotifierTask : public Task +{ +Q_OBJECT +public: + OnlineNotifierTask( Task* parent ); + + ~OnlineNotifierTask(); + + virtual bool take( Transfer* transfer ); + +protected: + virtual bool forMe( const Transfer* transfer ) const; + +signals: + void userIsOnline( const QString& user, const UserDetails& ud ); + void userIsOffline( const QString& user, const UserDetails& ud ); + +private: + void userOnline(); + void userOffline(); + +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/oscarbytestream.cpp b/kopete/protocols/oscar/liboscar/oscarbytestream.cpp new file mode 100644 index 00000000..ea090442 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarbytestream.cpp @@ -0,0 +1,141 @@ + +/*************************************************************************** + gwbytestream.cpp - Byte Stream using KNetwork sockets + ------------------- + begin : Wed Jul 7 2004 + copyright : (C) 2004 by Till Gerken <till@tantalo.net> + + Kopete (C) 2004 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 <qobject.h> +#include <kbufferedsocket.h> +#include <kdebug.h> +#include <kresolver.h> + +#include "oscarbytestream.h" + +KNetworkByteStream::KNetworkByteStream( QObject *parent, const char */*name*/ ) + : ByteStream ( parent ) +{ + kdDebug( 14151 ) << k_funcinfo << "Instantiating new KNetwork byte stream." << endl; + + // reset close tracking flag + mClosing = false; + + mSocket = new KNetwork::KBufferedSocket; + + // make sure we get a signal whenever there's data to be read + mSocket->enableRead( true ); + + // connect signals and slots + QObject::connect( mSocket, SIGNAL ( gotError ( int ) ), this, SLOT ( slotError ( int ) ) ); + QObject::connect( mSocket, SIGNAL ( connected ( const KResolverEntry& ) ), this, SLOT ( slotConnected () ) ); + QObject::connect( mSocket, SIGNAL ( closed () ), this, SLOT ( slotConnectionClosed () ) ); + QObject::connect( mSocket, SIGNAL ( readyRead () ), this, SLOT ( slotReadyRead () ) ); + QObject::connect( mSocket, SIGNAL ( bytesWritten ( int ) ), this, SLOT ( slotBytesWritten ( int ) ) ); +} + +bool KNetworkByteStream::connect( QString host, QString service ) +{ + kdDebug( 14151 ) << k_funcinfo << "Connecting to " << host << ", service " << service << endl; + + return socket()->connect( host, service ); +} + +bool KNetworkByteStream::isOpen() const +{ + // determine if socket is open + return socket()->isOpen(); +} + +void KNetworkByteStream::close () +{ +#ifdef OSCAR_EXCESSIVE_DEBUG + kdDebug ( 14151 ) << k_funcinfo << "Closing stream." << endl; +#endif + // close the socket and set flag that we are closing it ourselves + mClosing = true; + socket()->close(); +} + +int KNetworkByteStream::tryWrite () +{ + // send all data from the buffers to the socket + QByteArray writeData = takeWrite(); +#ifdef OSCAR_EXCESSIVE_DEBUG + kdDebug(14151) << k_funcinfo << "writing " << writeData.size() << " bytes." << endl; +#endif + socket()->writeBlock( writeData.data (), writeData.size () ); + return writeData.size(); +} + +KNetwork::KBufferedSocket *KNetworkByteStream::socket() const +{ + return mSocket; +} + +KNetworkByteStream::~KNetworkByteStream() +{ + delete mSocket; +} + +void KNetworkByteStream::slotConnected() +{ + emit connected(); +} + +void KNetworkByteStream::slotConnectionClosed() +{ + kdDebug( 14151 ) << k_funcinfo << "Socket has been closed." << endl; + + // depending on who closed the socket, emit different signals + if ( mClosing ) + { + kdDebug( 14151 ) << "..by ourselves!" << endl; + kdDebug( 14151 ) << "socket error is " << socket()->errorString( socket()->error() ) << endl; + emit connectionClosed (); + } + else + { + kdDebug( 14151 ) << "..by the other end" << endl; + emit delayedCloseFinished (); + } +} + +void KNetworkByteStream::slotReadyRead() +{ + // stuff all available data into our buffers + QByteArray readBuffer( socket()->bytesAvailable () ); + + socket()->readBlock( readBuffer.data (), readBuffer.size () ); + + appendRead( readBuffer ); + + emit readyRead(); +} + +void KNetworkByteStream::slotBytesWritten( int bytes ) +{ + emit bytesWritten( bytes ); +} + +void KNetworkByteStream::slotError( int code ) +{ + kdDebug( 14151 ) << k_funcinfo << "Socket error " << code << endl; + + emit error( code ); +} + +#include "oscarbytestream.moc" + +// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off; diff --git a/kopete/protocols/oscar/liboscar/oscarbytestream.h b/kopete/protocols/oscar/liboscar/oscarbytestream.h new file mode 100644 index 00000000..bd618666 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarbytestream.h @@ -0,0 +1,72 @@ + +/*************************************************************************** + gwbytestream.h - Byte Stream using KNetwork sockets + adapted from jabberbytestream.h + ------------------- + begin : Wed Jul 7 2004 + copyright : (C) 2004 by Till Gerken <till@tantalo.net> + + Kopete (C) 2004 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. * + * * + ***************************************************************************/ + +#ifndef KNETWORKBYTESTREAM_H +#define KNETWORKBYTESTREAM_H + +#include <kbufferedsocket.h> + +#include "bytestream.h" + + +/** + * Low level socket class, using KDE's KNetwork socket classes + * @author Till Gerken + */ + +class KNetworkByteStream : public ByteStream +{ + +Q_OBJECT + +public: + KNetworkByteStream ( QObject *parent = 0, const char *name = 0 ); + + ~KNetworkByteStream (); + + bool connect ( QString host, QString service ); + virtual bool isOpen () const; + virtual void close (); + + KNetwork::KBufferedSocket *socket () const; + +signals: + void connected (); + +protected: + virtual int tryWrite (); + +private slots: + void slotConnected (); + void slotConnectionClosed (); + void slotReadyRead (); + void slotBytesWritten ( int ); + void slotError ( int ); + +private: + KNetwork::KBufferedSocket *mSocket; + bool mClosing; + +}; + +#endif + +// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off; + diff --git a/kopete/protocols/oscar/liboscar/oscarclientstream.cpp b/kopete/protocols/oscar/liboscar/oscarclientstream.cpp new file mode 100644 index 00000000..e607a24b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarclientstream.cpp @@ -0,0 +1,437 @@ +/* + oscarclientstream.cpp - Kopete Oscar Protocol + + Copyright (c) 2004 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-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 "oscarclientstream.h" + +#include <qguardedptr.h> +#include <qobject.h> +#include <qptrqueue.h> +#include <qtimer.h> + +#include <kdebug.h> + +#include "bytestream.h" +#include "connection.h" +#include "connector.h" +#include "coreprotocol.h" +#include "rateclassmanager.h" +#include "transfer.h" + +#define LIBOSCAR_DEBUG 0 + +void cs_dump( const QByteArray &bytes ); + +enum { + Idle, + Connecting, + Active, + Closing +}; + +enum { + ClientMode, + ServerMode +}; + +class ClientStream::Private +{ +public: + Private() + { + conn = 0; + bs = 0; + connection = 0; + + username = QString::null; + password = QString::null; + server = QString::null; + haveLocalAddr = false; + doBinding = true; + + reset(); + } + void reset() + { + state = Idle; + notify = 0; + newTransfers = false; + } + + QString username; + QString password; + QString server; + bool doAuth; //send the initial login sequences to get the cookie + bool haveLocalAddr; + QHostAddress localAddr; + Q_UINT16 localPort; + bool doBinding; + + Connector *conn; + ByteStream *bs; + CoreProtocol client; + Connection* connection; + + QString defRealm; + + int mode; + int state; + int notify; + bool newTransfers; + + int errCond; + QString errText; + + QPtrQueue<Transfer> in; + + QTimer noopTimer; // used to send icq keepalive + int noop_time; +}; + +ClientStream::ClientStream(Connector *conn, QObject *parent) +:Stream(parent) +{ + //qDebug("CLIENTSTREAM::ClientStream"); + + d = new Private; + d->mode = ClientMode; + d->conn = conn; + connect( d->conn, SIGNAL(connected()), SLOT(cr_connected()) ); + connect( d->conn, SIGNAL(error()), SLOT(cr_error()) ); + connect( &d->client, SIGNAL( outgoingData( const QByteArray& ) ), SLOT ( cp_outgoingData( const QByteArray & ) ) ); + connect( &d->client, SIGNAL( incomingData() ), SLOT ( cp_incomingData() ) ); + + d->noop_time = 0; + connect(&d->noopTimer, SIGNAL(timeout()), SLOT(doNoop())); +} + +ClientStream::~ClientStream() +{ + reset(); + delete d; +} + +void ClientStream::reset(bool all) +{ + d->reset(); + d->noopTimer.stop(); + + // client + if(d->mode == ClientMode) + { + // reset connector + if ( d->bs ) + { + d->bs->close(); + d->bs = 0; + } + if ( d->conn ) + d->conn->done(); + + // reset state machine + d->client.reset(); + } + if(all) + d->in.clear(); +} + +void ClientStream::connectToServer(const QString& server, bool auth) +{ + reset(true); + d->state = Connecting; + d->doAuth = auth; + d->server = server; + + d->conn->connectToServer( d->server ); +} + +void ClientStream::continueAfterWarning() +{ +/* unneeded? + if(d->state == WaitVersion) { + d->state = Connecting; + processNext(); + } + else if(d->state == WaitTLS) { + d->state = Connecting; + processNext(); + } +*/ +} + +void ClientStream::accept() +{ + +} + +bool ClientStream::isActive() const +{ + return (d->state != Idle); +} + +bool ClientStream::isAuthenticated() const +{ + return (d->state == Active); +} + +void ClientStream::setNoopTime(int mills) +{ + d->noop_time = mills; + + if(d->noop_time == 0) { + d->noopTimer.stop(); + return; + } + + if( d->state != Active ) + return; + + d->noopTimer.start( d->noop_time ); +} + +void ClientStream::setLocalAddr(const QHostAddress &addr, Q_UINT16 port) +{ + d->haveLocalAddr = true; + d->localAddr = addr; + d->localPort = port; +} + +int ClientStream::errorCondition() const +{ + return d->errCond; +} + +QString ClientStream::errorText() const +{ + return d->errText; +} + +void ClientStream::close() +{ + if(d->state == Active) { + d->state = Closing; +// d->client.shutdown(); + processNext(); + } + else if(d->state != Idle && d->state != Closing) { + reset(); + } +} + +void ClientStream::setConnection( Connection *c ) +{ + d->connection = c; +} + +Connection* ClientStream::connection() const +{ + return d->connection; +} + + +bool ClientStream::transfersAvailable() const +{ + return ( !d->in.isEmpty() ); +} + +Transfer* ClientStream::read() +{ + if(d->in.isEmpty()) + return 0; //first from queue... + else + return d->in.dequeue(); +} + +void ClientStream::write( Transfer *request ) +{ + d->client.outgoingTransfer( request ); +} + +void cs_dump( const QByteArray &bytes ) +{ +#if 0 + qDebug( "contains: %i bytes ", bytes.count() ); + uint count = 0; + while ( count < bytes.count() ) + { + int dword = 0; + for ( int i = 0; i < 8; ++i ) + { + if ( count + i < bytes.count() ) + printf( "%02x ", bytes[ count + i ] ); + else + printf( " " ); + if ( i == 3 ) + printf( " " ); + } + printf(" | "); + dword = 0; + for ( int i = 0; i < 8; ++i ) + { + if ( count + i < bytes.count() ) + { + int j = bytes [ count + i ]; + if ( j >= 0x20 && j <= 0x7e ) + printf( "%2c ", j ); + else + printf( "%2c ", '.' ); + } + else + printf( " " ); + if ( i == 3 ) + printf( " " ); + } + printf( "\n" ); + count += 8; + } + printf( "\n" ); +#endif + Q_UNUSED( bytes ); +} + +void ClientStream::cp_outgoingData( const QByteArray& outgoingBytes ) +{ + // take formatted bytes from CoreProtocol and put them on the wire + d->bs->write( outgoingBytes ); +} + +void ClientStream::cp_incomingData() +{ + Transfer * incoming = d->client.incomingTransfer(); + if ( incoming ) + { + d->in.enqueue( incoming ); + d->newTransfers = true; + doReadyRead(); + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << + "client signalled incomingData but none was available, state is: " << + d->client.state() << endl; +} + +void ClientStream::cr_connected() +{ + d->bs = d->conn->stream(); + connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed())); + connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished())); + connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead())); + connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int))); + connect(d->bs, SIGNAL(error(int)), SLOT(bs_error(int))); + + d->state = Active; + if ( d->noop_time ) + d->noopTimer.start( d->noop_time ); + + QByteArray spare = d->bs->read(); + + QGuardedPtr<QObject> self = this; + emit connected(); + if(!self) + return; +} + +void ClientStream::cr_error() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + reset(); + emit error(ErrConnection); +} + +void ClientStream::bs_connectionClosed() +{ + reset(); + emit connectionClosed(); +} + +void ClientStream::bs_delayedCloseFinished() +{ + // we don't care about this (we track all important data ourself) +} + +void ClientStream::bs_error(int) +{ + // TODO +} + +void ClientStream::bs_readyRead() +{ + QByteArray a; + //qDebug( "size of storage for incoming data is %i bytes.", a.size() ); + a = d->bs->read(); + +#if LIBOSCAR_DEBUG + QCString cs(a.data(), a.size()+1); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "recv: " << a.size() << "bytes" << endl; + cs_dump( a ); +#endif + + d->client.addIncomingData(a); +} + +void ClientStream::bs_bytesWritten(int bytes) +{ +#if LIBOSCAR_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << bytes << " bytes written" << endl; + Q_UNUSED( bytes ); +#else + Q_UNUSED( bytes ); +#endif +} + +void ClientStream::srvProcessNext() +{ +} + +void ClientStream::doReadyRead() +{ + emit readyRead(); +} + +void ClientStream::processNext() +{ + if( !d->in.isEmpty() ) + { + QTimer::singleShot(0, this, SLOT(doReadyRead())); + } +} + +bool ClientStream::handleNeed() +{ + return false; +} + +void ClientStream::doNoop() +{ + if ( d->state != Active ) + return; + + FLAP f = { 0x05, d->connection->flapSequence(), 0 }; + Buffer* b = new Buffer(); //deleted in Transfer destructor + Transfer* t = new FlapTransfer( f, b ); //deleted after being sent + write( t ); +} + +void ClientStream::handleError() +{ +} + +#include "oscarclientstream.moc" + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/oscarclientstream.h b/kopete/protocols/oscar/liboscar/oscarclientstream.h new file mode 100644 index 00000000..f8b4d2b6 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarclientstream.h @@ -0,0 +1,164 @@ +/* + oscarclientstream.h - Kopete Oscar Protocol + + Copyright (c) 2004 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-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. * + * * + ************************************************************************* +*/ + +#ifndef OSCAR_CLIENTSTREAM_H +#define OSCAR_CLIENTSTREAM_H + +#include "stream.h" + +// forward defines +class ByteStream; +class Client; +class Connector; +class Connection; +class Transfer; +class QHostAddress; + +class ClientStream : public Stream +{ + Q_OBJECT +public: + enum Error { + ErrConnection = ErrCustom, // Connection error, ask Connector-subclass what's up + ErrNeg, // Negotiation error, see condition + ErrAuth, // Auth error, see condition + ErrBind // Resource binding error + }; + + enum Warning { + WarnOldVersion, // server uses older XMPP/Jabber "0.9" protocol // can be customised for novell versions + WarnNoTLS // there is no chance for TLS at this point + }; + + enum NegCond { + HostGone, // host no longer hosted + HostUnknown, // unknown host + RemoteConnectionFailed, // unable to connect to a required remote resource + SeeOtherHost, // a 'redirect', see errorText() for other host + UnsupportedVersion // unsupported XMPP version + }; + + enum AuthCond { + GenericAuthError, // all-purpose "can't login" error + NoMech, // No appropriate auth mech available + BadProto, // Bad SASL auth protocol + BadServ, // Server failed mutual auth + InvalidUserId, // bad user id + InvalidMech, // bad mechanism + InvalidRealm, // bad realm + MechTooWeak, // can't use mech with this authzid + NotAuthorized, // bad user, bad password, bad creditials + TemporaryAuthFailure // please try again later! + }; + + enum BindCond { + BindNotAllowed, // not allowed to bind a resource + BindConflict // resource in-use + }; + + ClientStream(Connector *conn, QObject *parent=0); + ~ClientStream(); + + void connectToServer(const QString& server, bool auth=true); + void accept(); // server + bool isActive() const; + bool isAuthenticated() const; + + // login params + void setUsername(const QString &s); + void setPassword(const QString &s); + + void setLocalAddr(const QHostAddress &addr, Q_UINT16 port); + + void close(); + + /** Connection related stuff */ + void setConnection( Connection* c ); + Connection* connection() const; + + + /** + * Are there any messages waiting to be read + */ + bool transfersAvailable() const; + + /** + * Read a message received from the server + */ + Transfer * read(); + + /** + * Send a message to the server + */ + void write( Transfer* request ); + + int errorCondition() const; + QString errorText() const; + + // extrahttp://bugs.kde.org/show_bug.cgi?id=85158 +/*# void writeDirect(const QString &s); // must be for debug testing*/ + void setNoopTime(int mills); + +signals: + void connected(); + void securityLayerActivated(int); + void authenticated(); // this signal is ordinarily emitted in processNext + void warning(int); +public slots: + void continueAfterWarning(); + +private slots: + void cr_connected(); + void cr_error(); + /** + * collects wire ready outgoing data from the core protocol and sends + */ + void cp_outgoingData( const QByteArray& ); + /** + * collects parsed incoming data as a transfer from the core protocol and queues + */ + void cp_incomingData(); + + void bs_connectionClosed(); + void bs_delayedCloseFinished(); + void bs_error(int); // server only + void bs_readyRead(); + void bs_bytesWritten(int); + + void doNoop(); + void doReadyRead(); + +private: + class Private; + Private *d; + + void reset(bool all=false); + void processNext(); + bool handleNeed(); + void handleError(); + void srvProcessNext(); + + /** + * convert internal method representation to wire + */ + static char* encode_method(Q_UINT8 method); +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/oscarconnector.cpp b/kopete/protocols/oscar/liboscar/oscarconnector.cpp new file mode 100644 index 00000000..6fcef197 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarconnector.cpp @@ -0,0 +1,108 @@ + +/*************************************************************************** + gwconnector.cpp - Socket Connector for KNetwork + ------------------- + begin : Wed Jul 7 2004 + copyright : (C) 2004 by Till Gerken <till@tantalo.net> + + Kopete (C) 2004 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 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. * + * * + ***************************************************************************/ + +#include <kbufferedsocket.h> +#include <kdebug.h> +#include <kresolver.h> + +#include "oscarconnector.h" +#include "oscarbytestream.h" + +KNetworkConnector::KNetworkConnector( QObject *parent, const char */*name*/ ) + : Connector( parent ) +{ + kdDebug( 14151 ) << k_funcinfo << "New KNetwork connector." << endl; + + mErrorCode = KNetwork::KSocketBase::NoError; + + mByteStream = new KNetworkByteStream( this ); + + connect( mByteStream, SIGNAL ( connected () ), this, SLOT ( slotConnected () ) ); + connect( mByteStream, SIGNAL ( error ( int ) ), this, SLOT ( slotError ( int ) ) ); + mPort = 0; +} + +KNetworkConnector::~KNetworkConnector() +{ + delete mByteStream; +} + +void KNetworkConnector::connectToServer( const QString &server ) +{ + kdDebug( 14151 ) << k_funcinfo << "Initiating connection to " << mHost << endl; + Q_ASSERT( !mHost.isNull() ); + Q_ASSERT( mPort ); + + mErrorCode = KNetwork::KSocketBase::NoError; + + if ( !mByteStream->connect ( mHost, QString::number ( mPort ) ) ) + { + // Houston, we have a problem + mErrorCode = mByteStream->socket()->error(); + emit error(); + } +} + +void KNetworkConnector::slotConnected() +{ + kdDebug( 14151 ) << k_funcinfo << "We are connected." << endl; + + // FIXME: setPeerAddress() is something different, find out correct usage later + //KInetSocketAddress inetAddress = mStreamSocket->address().asInet().makeIPv6 (); + //setPeerAddress ( QHostAddress ( inetAddress.ipAddress().addr () ), inetAddress.port () ); + + emit connected (); +} + +void KNetworkConnector::slotError( int code ) +{ + kdDebug( 14151 ) << k_funcinfo << "Error detected: " << code << endl; + + mErrorCode = code; + emit error (); +} + +int KNetworkConnector::errorCode() +{ + return mErrorCode; +} + +ByteStream *KNetworkConnector::stream() const +{ + return mByteStream; +} + +void KNetworkConnector::done() +{ + kdDebug ( 14151 ) << k_funcinfo << endl; + mByteStream->close (); +} + +void KNetworkConnector::setOptHostPort( const QString &host, Q_UINT16 port ) +{ + kdDebug ( 14151 ) << k_funcinfo << "Manually specifying host " << host << " and port " << port << endl; + + mHost = host; + mPort = port; + +} + +#include "oscarconnector.moc" + +// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off; diff --git a/kopete/protocols/oscar/liboscar/oscarconnector.h b/kopete/protocols/oscar/liboscar/oscarconnector.h new file mode 100644 index 00000000..d130f7da --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarconnector.h @@ -0,0 +1,69 @@ + +/*************************************************************************** + oscarconnector.h - Socket Connector for KNetwork + ------------------- + begin : Wed Jul 7 2004 + copyright : (C) 2004 by Till Gerken <till@tantalo.net> + (C) 2004 by Matt Rogers <mattr@kde.org> + + Kopete (C) 2004 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 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. * + * * + ***************************************************************************/ + +#ifndef OSCARCONNECTOR_H +#define OSCARCONNECTOR_H + +#include "oscarbytestream.h" + +#include "connector.h" + +class ByteStream; +class KNetworkByteStream; +class KResolverEntry; + +/** +@author Till Gerken +@author Matt Rogers +*/ +class KNetworkConnector : public Connector +{ + +Q_OBJECT + +public: + KNetworkConnector( QObject *parent = 0, const char *name = 0 ); + + virtual ~KNetworkConnector(); + + virtual void connectToServer( const QString &server ); + virtual ByteStream *stream() const; + virtual void done(); + + void setOptHostPort( const QString &host, Q_UINT16 port ); + + int errorCode(); + +private slots: + void slotConnected(); + void slotError( int ); + +private: + QString mHost; + Q_UINT16 mPort; + int mErrorCode; + + KNetworkByteStream *mByteStream; + +}; + +#endif + +// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off; diff --git a/kopete/protocols/oscar/liboscar/oscardebug.h b/kopete/protocols/oscar/liboscar/oscardebug.h new file mode 100644 index 00000000..9c2d7e16 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscardebug.h @@ -0,0 +1,35 @@ +// oscardebug.h + +// 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 + +#ifndef OSCARDEBUG_H +#define OSCARDEBUG_H + +//OSCAR debugging definitions + +//uncomment to get debug output for user info parsing +//#define OSCAR_USERINFO_DEBUG + +//uncomment to get excessive amounts of debug info from +//various places in the code +//#define OSCAR_EXCESSIVE_DEBUG + +//uncomment to get packet dumps in the debug output +//#define OSCAR_PACKET_PARSING_DEBUG + +#endif diff --git a/kopete/protocols/oscar/liboscar/oscarmessage.cpp b/kopete/protocols/oscar/liboscar/oscarmessage.cpp new file mode 100644 index 00000000..f4f512d2 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarmessage.cpp @@ -0,0 +1,301 @@ +/* + Kopete Oscar Protocol + oscarmessage.cpp - Oscar Message Object + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + Copyright (c) 2005 Conrad Hoffmann <conrausch@gmx.de> + + 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 "oscarmessage.h" + +#include <qdeepcopy.h> +#include <qtextcodec.h> + + +Oscar::Message::Message() +: m_channel( -1 ), + m_properties( -1 ), + m_messageType( 0 ), + m_protocolVersion( 0 ), + m_channel2Counter( 0 ), + m_encoding( UserDefined ) +{ +} + +Oscar::Message::Message( Encoding messageEncoding, const QByteArray& messageText, int channel, int properties, QDateTime timestamp ) +: m_channel( channel ), + m_properties( properties ), + m_messageType( 0 ), + m_protocolVersion( 0 ), + m_channel2Counter( 0 ), + m_textArray( messageText ), + m_timestamp( timestamp ), + m_encoding( messageEncoding ) +{ +} + +Oscar::Message::Message( Encoding messageEncoding, const QCString& messageText, int channel, int properties, QDateTime timestamp ) +: m_channel( channel ), + m_properties( properties ), + m_messageType( 0 ), + m_protocolVersion( 0 ), + m_channel2Counter( 0 ), + m_timestamp( timestamp ), + m_encoding( messageEncoding ) +{ + setTextArray( messageText ); +} + +Oscar::Message::Message( Encoding messageEncoding, const QString& messageText, int channel, int properties, QDateTime timestamp, QTextCodec* codec ) +: m_channel( channel ), + m_properties( properties ), + m_messageType( 0 ), + m_protocolVersion( 0 ), + m_channel2Counter( 0 ), + m_timestamp( timestamp ) +{ + setText( messageEncoding, messageText, codec ); +} + +QString Oscar::Message::sender() const +{ + return m_sender; +} + +void Oscar::Message::setSender( const QString& sender ) +{ + m_sender = sender; +} + +QString Oscar::Message::receiver() const +{ + return m_receiver; +} + +void Oscar::Message::setReceiver( const QString& receiver ) +{ + m_receiver = receiver; +} + +QByteArray Oscar::Message::textArray() const +{ + return m_textArray; +} + +QString Oscar::Message::text( QTextCodec *codec ) const +{ + switch ( m_encoding ) + { + case Oscar::Message::UserDefined: + return codec->toUnicode( m_textArray ); + case Oscar::Message::UTF8: + return QString::fromUtf8( m_textArray.data(), m_textArray.size() ); + case Oscar::Message::UCS2: + { + uint len = m_textArray.size() / 2; + QString result; + result.setLength( len ); + QByteArray::ConstIterator p = m_textArray.begin(); + for ( uint i = 0; i < len; i++) + { + char row = *p++; + char cell = *p++; + result[i] = QChar( cell, row ); + } + //check if last character isn't null + if ( result[len-1].isNull() ) + result.setLength( len - 1 ); + + return result; + } + default: + break; // Should never happen. + } + return QString::null; + //FIXME: warn at least with kdWarning if an unrecognised encoding style was seen. +} + +void Oscar::Message::setText( Oscar::Message::Encoding newEncoding, const QString& newText, QTextCodec* codec ) +{ + uint len; + switch ( newEncoding ) + { + case Oscar::Message::UserDefined: + // Oscar::Message::setTextArray( const QCString& ) + // strips trailing null byte automatically. + setTextArray( codec->fromUnicode( newText ) ); + break; + case Oscar::Message::UTF8: + // Oscar::Message::setTextArray( const QCString& ) + // strips trailing null byte automatically. + setTextArray( newText.utf8() ); + break; + case Oscar::Message::UCS2: + { + len = newText.length(); + m_textArray.resize( len * 2 ); + QByteArray::Iterator p = m_textArray.begin(); + for ( uint i = 0; i < len; i++) + { + *p++ = newText[i].row(); + *p++ = newText[i].cell(); + } + break; + } + default: + break; // Should never happen. + } + m_encoding = newEncoding; +} + +void Oscar::Message::setTextArray( const QByteArray& newTextArray ) +{ + m_textArray.duplicate( newTextArray ); +} + +void Oscar::Message::setTextArray( const QCString& newTextArray ) +{ + m_textArray.duplicate( newTextArray ); + uint len = m_textArray.size(); + if ( len > 0 ) + { + --len; + if ( m_textArray[len] == '\0' ) + { + // Strip trailing null byte. + m_textArray.resize( len ); + } + } +} + +int Oscar::Message::properties() const +{ + return m_properties; +} + +void Oscar::Message::addProperty( int prop ) +{ + if ( m_properties == -1 ) + m_properties = 0; + + m_properties = m_properties | prop; +} + +bool Oscar::Message::hasProperty( int prop ) const +{ + if ( m_properties == -1 ) + return false; + if ( ( m_properties & prop ) == 0 ) + return false; + else + return true; +} + +int Oscar::Message::type() const +{ + return m_channel; +} + +void Oscar::Message::setType( int newType ) +{ + m_channel = newType; +} + +QDateTime Oscar::Message::timestamp() const +{ + return m_timestamp; +} + +void Oscar::Message::setTimestamp( QDateTime ts ) +{ + m_timestamp = ts; +} + +QByteArray Oscar::Message::icbmCookie() const +{ + return m_icbmCookie; +} + +void Oscar::Message::setIcbmCookie( const QByteArray& cookie ) +{ + m_icbmCookie.duplicate( cookie ); +} + +int Oscar::Message::protocolVersion() const +{ + return m_protocolVersion; +} + +void Oscar::Message::setProtocolVersion( int version ) +{ + m_protocolVersion = version; +} + +int Oscar::Message::channel2Counter() const +{ + return m_channel2Counter; +} + +void Oscar::Message::setChannel2Counter( int value ) +{ + m_channel2Counter = value; +} + +int Oscar::Message::messageType() const +{ + return m_messageType; +} + +void Oscar::Message::setMessageType( int type ) +{ + m_messageType = type; +} + +Oscar::WORD Oscar::Message::exchange() const +{ + return m_exchange; +} + +void Oscar::Message::setExchange( Oscar::WORD exchange ) +{ + m_exchange = exchange; +} + +QString Oscar::Message::chatRoom() const +{ + return m_chatRoom; +} + +void Oscar::Message::setChatRoom( const QString& room ) +{ + m_chatRoom = room; +} + +Oscar::Message::Encoding Oscar::Message::encoding() const +{ + return m_encoding; +} + +void Oscar::Message::setEncoding( Oscar::Message::Encoding newEncoding ) +{ + m_encoding = newEncoding; +} + +Oscar::Message::operator bool() const +{ + return m_channel != -1 && m_properties != -1; +} + +//kate: indent-mode csands; auto-insert-doxygen on; tab-width 4; + diff --git a/kopete/protocols/oscar/liboscar/oscarmessage.h b/kopete/protocols/oscar/liboscar/oscarmessage.h new file mode 100644 index 00000000..7f081054 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarmessage.h @@ -0,0 +1,182 @@ +/* + Kopete Oscar Protocol + oscarmessage.h - Oscar Message Object + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + Copyright (c) 2005 Conrad Hoffmann <conrausch@gmx.de> + Copyright (c) 2005 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef _OSCARMESSAGE_H_ +#define _OSCARMESSAGE_H_ + +#include <qglobal.h> +#include <qstring.h> +#include <qcstring.h> +#include <qdatetime.h> +#include "kopete_export.h" +#include "oscartypes.h" + +class QTextCodec; + +namespace Oscar +{ + +/** + * This class is responsible for holding all the details + * of a message and includes the following: + * \li channel ( type ) + * \li encoding + */ + +class KOPETE_EXPORT Message +{ +public: + + enum { + Normal = 0x0000, + AutoResponse = 0x0001, + WWP = 0x0002, + EMail = 0x0004, + ChatRoom = 0x0008, + Request = 0x0100, + StatusMessageRequest = 0x0200 + }; + + enum Encoding { + UserDefined, + UTF8, + UCS2 + }; + + Message(); + + Message( Encoding messageEncoding, const QByteArray& messageText, int channel, int properties, QDateTime timestamp ); + Message( Encoding messageEncoding, const QCString& messageText, int channel, int properties, QDateTime timestamp ); + Message( Encoding messageEncoding, const QString& messageText, int channel, int properties, QDateTime timestamp, QTextCodec* codec = 0 ); + + /** Get the sender of the message */ + QString sender() const; + + /** Set the sender of the message */ + void setSender( const QString& sender ); + + /** Get the receiver of the message */ + QString receiver() const; + + /** Set the receiver of the message */ + void setReceiver( const QString& receiver); + + /** get the message text */ + QString text( QTextCodec* codec ) const; + + /** set the message text */ + void setText( Encoding newEncoding, const QString& newText, QTextCodec* codec = 0); + + /** get the message text as a bytearray for decoding */ + QByteArray textArray() const; + + /** set the message text as a bytearray for decoding */ + void setTextArray( const QByteArray& newTextArray ); + + /** set the mesasge text as a bytearray for decoding */ + void setTextArray( const QCString& newTextArray ); + + /** get the message properties */ + int properties() const; + + /** ask about a specific property */ + bool hasProperty( int prop ) const; + + /** add a property to the message */ + void addProperty( int prop ); + + /** get the channel ( type ) of the message */ + int type() const; + + /** set the channel ( type ) of the message */ + void setType( int newType ); + + /** get the timestamp of the message */ + QDateTime timestamp() const; + + /** set the timestamp of the message */ + void setTimestamp( QDateTime ts ); + + /** get the ICBM cookie of the message */ + QByteArray icbmCookie() const; + + /** set the ICBM cookie of the message */ + void setIcbmCookie( const QByteArray& cookie ); + + /** get the protocol version (channel 2 messages only) */ + int protocolVersion() const; + + /** prepare for handling of different protocol versions */ + void setProtocolVersion( int version ); + + /** get the channel 2 counter value of the message */ + int channel2Counter() const; // channel 2 message have an additional counter whose value needs be kept in a request response + + /** set the channel 2 counter value */ + void setChannel2Counter( int value ); + + /** get the message (content) type */ + int messageType() const; + + /** set the message (content) type */ + void setMessageType( int type ); + + /** get the exchange for the chat room this message is for */ + Oscar::WORD exchange() const; + + /** set the exchange for the chat room this message is for */ + void setExchange( Oscar::WORD ); + + /** get the chat room that this message is for */ + QString chatRoom() const; + + /** set the chat room that this message is for */ + void setChatRoom( const QString& ); + + /** get the message encoding */ + Encoding encoding() const; + + /** set the message encoding */ + void setEncoding( Encoding newEncoding ); + + operator bool() const; + +private: + //TODO d-pointer + QString m_sender; + QString m_receiver; + int m_channel; + int m_properties; + int m_messageType; + int m_protocolVersion; + int m_channel2Counter; + QByteArray m_icbmCookie; + QByteArray m_textArray; + QDateTime m_timestamp; + Oscar::WORD m_exchange; + QString m_chatRoom; + Encoding m_encoding; +}; + +} + +//kate: indent-mode csands; auto-insert-doxygen on; tab-width 4; + +#endif diff --git a/kopete/protocols/oscar/liboscar/oscarsettings.cpp b/kopete/protocols/oscar/liboscar/oscarsettings.cpp new file mode 100644 index 00000000..36b0bb12 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarsettings.cpp @@ -0,0 +1,69 @@ +/* + Kopete Oscar Protocol + Oscar Backend Setting Storage + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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 "oscarsettings.h" + +namespace Oscar +{ + +Settings::Settings() +{ +} + + +Settings::~Settings() +{ +} + +void Settings::setWebAware( bool aware ) +{ + m_webAware = aware; +} + +bool Settings::webAware() const +{ + return m_webAware; +} + +void Settings::setRequireAuth( bool require ) +{ + m_requireAuth = require; +} + +bool Settings::requireAuth() const +{ + return m_requireAuth; +} + +void Settings::setHideIP( bool hide ) +{ + m_hideIP = hide; +} + +bool Settings::hideIP() const +{ + return m_hideIP; +} + + + + + +} + +//kate: indent-mode csands; tab-width 4; + diff --git a/kopete/protocols/oscar/liboscar/oscarsettings.h b/kopete/protocols/oscar/liboscar/oscarsettings.h new file mode 100644 index 00000000..12ece2e6 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarsettings.h @@ -0,0 +1,62 @@ +/* + Kopete Oscar Protocol + Oscar Backend Setting Storage + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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. * + * * + ************************************************************************* +*/ +#ifndef OSCARSETTINGS_H +#define OSCARSETTINGS_H + +#include "kopete_export.h" + +namespace Oscar +{ + +/** +* This class is used to keep track of various persistant settings that the backend will always +* need to get from the frontend. This is the interface and storage class that will handle the +* settings. +* @author Matt Rogers +*/ +class KOPETE_EXPORT Settings +{ +public: + Settings(); + ~Settings(); + + /* Web awareness settings */ + void setWebAware( bool webAware ); + bool webAware() const; + + /* Authorization settings */ + void setRequireAuth( bool require ); + bool requireAuth() const; + + /* Hide IP Settings */ + void setHideIP( bool hide ); + bool hideIP() const; + +private: + + bool m_webAware; + bool m_requireAuth; + bool m_hideIP; +}; + +} + +#endif + +//kate: indent-mode csands; tab-width 4; + diff --git a/kopete/protocols/oscar/liboscar/oscartypeclasses.cpp b/kopete/protocols/oscar/liboscar/oscartypeclasses.cpp new file mode 100644 index 00000000..cd9e9619 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscartypeclasses.cpp @@ -0,0 +1,284 @@ +/* + Kopete Oscar Protocol + oscartypeclasses.cpp - Oscar Type Definitions + + Copyright (c) 2004 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 "oscartypeclasses.h" +#include <qdeepcopy.h> +#include <qvaluelist.h> +#include <kdebug.h> +#include "oscarutils.h" +#include "buffer.h" + + +// using namespace Oscar; + +Oscar::TLV::TLV() +{ + type = 0; + length = 0; +} + +Oscar::TLV::TLV( Q_UINT16 newType, Q_UINT16 newLength, char* newData ) +{ + type = newType; + length = newLength; + data.truncate(0); + data.duplicate( newData, length ); +} + +Oscar::TLV::TLV( Q_UINT16 newType, Q_UINT16 newLength, const QByteArray& newData ) +{ + type = newType; + length = newLength; + data.duplicate( newData ); +} + +Oscar::TLV::TLV( const TLV& t ) +{ + type = t.type; + length = t.length; + data.truncate(0); + data.duplicate( t.data ); +} + +Oscar::TLV::operator bool() const +{ + return type != 0; +} + + +Oscar::SSI::SSI() +{ + m_gid = 0; + m_bid = 0; + m_type = 0xFFFF; + m_tlvLength = 0; + m_waitingAuth = false; +} + +Oscar::SSI::SSI( const QString &name, int gid, int bid, int type, const QValueList<TLV> &tlvlist, int tlvLength ) +{ + m_name = name; + m_gid = gid; + m_bid = bid; + m_type = type; + m_tlvLength = tlvLength; + + //deepcopy the tlvs + m_tlvList = QDeepCopy< QValueList<TLV> >( tlvlist ); + + if ( m_tlvLength == 0 && !m_tlvList.isEmpty() ) + refreshTLVLength(); + + checkTLVs(); +} + +Oscar::SSI::SSI( const Oscar::SSI& other ) +{ + m_name = other.m_name; + m_gid = other.m_gid; + m_bid = other.m_bid; + m_type = other.m_type; + m_tlvLength = other.m_tlvLength; + m_alias = other.m_alias; + m_waitingAuth = other.m_waitingAuth; + + //deepcopy the tlvs + m_tlvList = QDeepCopy< QValueList<TLV> >( other.m_tlvList ); + + if ( m_tlvLength == 0 && !m_tlvList.isEmpty() ) + refreshTLVLength(); +} + +bool Oscar::SSI::isValid() const +{ + return m_type != 0xFFFF; +} + +QString Oscar::SSI::name() const +{ + return m_name; +} + +Q_UINT16 Oscar::SSI::gid() const +{ + return m_gid; +} + +Q_UINT16 Oscar::SSI::bid() const +{ + return m_bid; +} + +Q_UINT16 Oscar::SSI::type() const +{ + return m_type; +} + +const QValueList<TLV>& Oscar::SSI::tlvList() const +{ + return m_tlvList; +} + +void Oscar::SSI::setTLVListLength( Q_UINT16 newLength ) +{ + m_tlvLength = newLength; +} + +Q_UINT16 Oscar::SSI::tlvListLength() const +{ + return m_tlvLength; +} + +void Oscar::SSI::setTLVList( QValueList<TLV> list ) +{ + //deepcopy the tlvs + m_tlvList = QDeepCopy< QValueList<TLV> >( list ); + refreshTLVLength(); + checkTLVs(); +} + +void Oscar::SSI::refreshTLVLength() +{ + m_tlvLength = 0; + QValueList<TLV>::iterator it = m_tlvList.begin(); + for( ; it != m_tlvList.end(); ++it ) + { + m_tlvLength += 4; + m_tlvLength += (*it).length; + } +} + +void Oscar::SSI::checkTLVs() +{ + //check for the auth TLV + TLV authTLV = findTLV( m_tlvList, 0x0066 ); + if ( authTLV ) + { + kdDebug(14151) << k_funcinfo << "Need auth for contact " << m_name << endl; + m_waitingAuth = true; + } + else + m_waitingAuth = false; + + //check for the alias TLV + TLV aliasTLV = findTLV( m_tlvList, 0x0131 ); + if ( aliasTLV ) + { + m_alias = QString::fromUtf8( aliasTLV.data, aliasTLV.length ); + kdDebug( 14151 ) << k_funcinfo << "Got an alias '" << m_alias << "' for contact '" << m_name << "'" << endl; + } + + TLV privacyTLV = findTLV( m_tlvList, 0x00CA ); + if ( privacyTLV ) + kdDebug(14151) << k_funcinfo << "Found privacy settings " << privacyTLV.data << endl; + + TLV infoTLV = findTLV( m_tlvList, 0x00CC ); + if ( infoTLV ) + kdDebug(14151) << k_funcinfo << "Found 'allow others to see...' options " << infoTLV.data << endl; +} + +QString Oscar::SSI::alias() const +{ + return m_alias; +} + +void Oscar::SSI::setAlias( const QString& newAlias ) +{ + m_alias = newAlias; +} + +bool Oscar::SSI::waitingAuth() const +{ + return m_waitingAuth; +} + +void Oscar::SSI::setWaitingAuth( bool waiting ) +{ + m_waitingAuth = waiting; +} + +void Oscar::SSI::setIconHash( QByteArray hash ) +{ + m_hash.duplicate( hash ); +} + +QByteArray Oscar::SSI::iconHash( ) const +{ + return m_hash; +} + +QString Oscar::SSI::toString() const +{ + QString ssiString = QString::fromLatin1( "name: " ); + ssiString += m_name; + ssiString += " gid: "; + ssiString += QString::number( m_gid ); + ssiString += " bid: "; + ssiString += QString::number( m_bid ); + ssiString += " type: "; + ssiString += QString::number( m_type ); + ssiString += " tlv length: "; + ssiString += QString::number( m_tlvLength ); + return ssiString; +} + +bool Oscar::SSI::operator==( const SSI& item ) const +{ + if ( m_name == item.name() && m_gid == item.gid() && m_bid == item.bid() && m_type == item.type() ) + return true; + else + return false; +} + +Oscar::SSI::operator bool() const +{ + return isValid(); +} + +Oscar::SSI::operator QByteArray() const +{ + Buffer b; + QCString name( m_name.utf8() ); + uint namelen = name.length(); + const char *namedata = name; + b.addWord( namelen ); + // Using namedata instead of name because + // Buffer::addString(QByteArray, DWORD) ignores it's second argument, + // while Buffer::addString(const char*, DWORD) does not ignore it. + // We must provide the explicit length argument to addString() because + // we don't need trailing null byte to be added when automatic + // conversion from QCString to QByteArray is performed. + // This hack will not be needed with Qt 4. + b.addString( namedata, namelen ); + b.addWord( m_gid ); + b.addWord( m_bid ); + b.addWord( m_type ); + b.addWord( m_tlvLength ); + QValueList<Oscar::TLV>::const_iterator it = m_tlvList.begin(); + for( ; it != m_tlvList.end(); ++it ) + { + b.addWord( (*it).type ); + b.addWord( (*it).length ); + b.addString( (*it).data, (*it).data.size() ); + } + + return (QByteArray) b; +} + + +//kate: indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/oscartypeclasses.h b/kopete/protocols/oscar/liboscar/oscartypeclasses.h new file mode 100644 index 00000000..94e0c910 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscartypeclasses.h @@ -0,0 +1,144 @@ +/* + Kopete Oscar Protocol + oscartypeclasses.h - Oscar Type Definitions + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef _OSCARTYPECLASSES_H_ +#define _OSCARTYPECLASSES_H_ + +#include <qglobal.h> +#include <qstring.h> +#include <qcstring.h> +#include <qvaluelist.h> +#include "kopete_export.h" + +namespace Oscar +{ +class KOPETE_EXPORT TLV +{ +public: + + TLV(); + TLV( Q_UINT16, Q_UINT16, char* data ); + TLV( Q_UINT16, Q_UINT16, const QByteArray& ); + TLV( const TLV& t ); + + operator bool() const; + + Q_UINT16 type; + Q_UINT16 length; + QByteArray data; + +}; + +class KOPETE_EXPORT SSI +{ +public: + SSI(); + SSI( const QString &name, int gid, int bid, int type, const QValueList<TLV>& tlvlist, int tlvLength = 0 ); + SSI( const SSI& other ); + + /** Get the validity of this item */ + bool isValid() const; + + /** \brief The name of this SSI item. + * This is usually the screenname, ICQ number, or group name. */ + QString name() const; + + /** \brief The group id of the SSI item */ + Q_UINT16 gid() const; + + /** \brief The buddy id of the SSI item */ + Q_UINT16 bid() const; + + /** + * \brief The type of the SSI Item. + * see ROSTER_* defines on oscartypes.h + * Use a value of 0xFFFF for an SSI item not on the server list + */ + Q_UINT16 type() const; + + /** \brief the TLV list for the item */ + const QValueList<TLV>& tlvList() const; + + /** \brief Set the TLV list for the item */ + void setTLVList( QValueList<TLV> ); + + /** + * \brief Set the length of the TLV list + * + * This is not the number of items in the list!! It's the aggregation of the + * sizes of the TLVs + */ + void setTLVListLength( Q_UINT16 newLength ); + + /** \brief Get the TLV list length */ + Q_UINT16 tlvListLength() const; + + /** + * Get the alias for the SSI item + * This is TLV 0x0131 in an SSI item + */ + QString alias() const; + + /** + * Set the alias for the SSI item + * This should be done after a successful modification of the item + * on the server + */ + void setAlias( const QString& newAlias ); + + /** \brief Indicates we're awaiting authorization from this item */ + bool waitingAuth() const; + + /** Set whether we are waiting authorization or not from this item */ + void setWaitingAuth( bool waiting ); + + void setIconHash( QByteArray hash ); + + QByteArray iconHash() const; + + /** \brief String representation of our SSI object */ + QString toString() const; + + bool operator==( const SSI& item ) const; + operator bool() const; + + operator QByteArray() const; + + void refreshTLVLength(); + + //! parse the TLVs checking for aliases and auth and stuff like that + void checkTLVs(); + +private: + QString m_name; + int m_gid; + int m_bid; + int m_type; + QValueList<TLV> m_tlvList; + int m_tlvLength; + bool m_waitingAuth; + QString m_alias; + QByteArray m_hash; +}; + +} + +//kate: indent-mode csands; auto-insert-doxygen on; tab-width 4; + +#endif diff --git a/kopete/protocols/oscar/liboscar/oscartypes.h b/kopete/protocols/oscar/liboscar/oscartypes.h new file mode 100644 index 00000000..b7d4f55b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscartypes.h @@ -0,0 +1,292 @@ +/* + Kopete Oscar Protocol + oscartypes.h - Oscar Type Definitions + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef _OSCARTYPES_H_ +#define _OSCARTYPES_H_ + +#include "oscartypeclasses.h" +#include <qglobal.h> +#include <qdatetime.h> +#include <qstring.h> + +//! Debug Areas +const int OSCAR_RAW_DEBUG = 14151; +const int OSCAR_GEN_DEBUG = 14150; +const int OSCAR_AIM_DEBUG = 14152; +const int OSCAR_ICQ_DEBUG = 14153; + +namespace Oscar +{ +//! Capabilities +enum Capabilities +{ + CAP_CHAT = 0, CAP_VOICE, CAP_SENDFILE, CAP_ISICQ, CAP_IMIMAGE, CAP_BUDDYICON, CAP_SAVESTOCKS, + CAP_GETFILE, CAP_ICQSERVERRELAY, CAP_GAMES, CAP_GAMES2, CAP_SENDBUDDYLIST, CAP_RTFMSGS, CAP_IS_2001, + CAP_TRILLIAN, CAP_TRILLIANCRYPT, CAP_APINFO, CAP_UTF8, CAP_TYPING, CAP_INTEROPERATE, CAP_KOPETE, CAP_MICQ, + CAP_MACICQ, CAP_SIMOLD, CAP_SIMNEW, CAP_XTRAZ, CAP_STR_2001, CAP_STR_2002, CAP_LAST +}; + +typedef unsigned char cap[16]; +const cap oscar_caps[] = +{ + //CAP_CHAT, + {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + //CAP_VOICE, + {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_SENDFILE, + {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_ISICQ, + {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_IMIMAGE, + {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_BUDDYICON, + {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_SAVESTOCKS, + {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_GETFILE, + {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_ICQSERVERRELAY, + {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_GAMES, + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_GAMES2, + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_SENDBUDDYLIST, + {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_RTFMSGS, + {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, + 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}, + + // CAP_IS_2001, + {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8, + 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}, + + // CAP_TRILLIAN + {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, + 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}, + + // CAP_TRILLIANCRYPT + {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb, + 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}, + + // CAP_APINFO, + {0xAA, 0x4A, 0x32, 0xB5, 0xF8, 0x84, 0x48, 0xc6, + 0xA3, 0xD7, 0x8C, 0x50, 0x97, 0x19, 0xFD, 0x5B}, + + // CAP_UTF8, + {0x09, 0x46, 0x13, 0x4E, 0x4C, 0x7F, 0x11, 0xD1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_TYPING - client supports mini typing notifications + {0x56, 0x3F, 0xC8, 0x09, 0x0B, 0x6f, 0x41, 0xBD, + 0x9F, 0x79, 0x42, 0x26, 0x09, 0xDF, 0xA2, 0xF3}, + + // CAP_INTEROPERATE, + {0x09, 0x46, 0x13, 0x4D, 0x4C, 0x7F, 0x11, 0xD1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_KOPETE, + // last 4 bytes determine version + // NOTE change with each Kopete Release! + // first number, major version + // second number, minor version + // third number, point version 100+ + // fourth number, point version 0-99 + {'K', 'o', 'p', 'e', 't', 'e', ' ', 'I', + 'C', 'Q', ' ', ' ', 0, 12, 0, 7}, + + // CAP_MICQ + // last 4 bytes determine version + {0x6d, 0x49, 0x43, 0x51, 0x20, 0xa9, 0x20, 0x52, + 0x2e, 0x4b, 0x2e, 0x20, 0x00, 0x00, 0x00, 0x00}, + + // CAP_MACICQ + {0xDD, 0x16, 0xF2, 0x02, 0x84, 0xE6, 0x11, 0xD4, + 0x90, 0xDB, 0x00, 0x10, 0x4B, 0x9B, 0x4B, 0x7D}, + + // CAP_SIMOLD + // last byte determines version + // (major + 1) << 6 + minor + {0x97, 0xB1, 0x27, 0x51, 0x24, 0x3C, 0x43, 0x34, + 0xAD, 0x22, 0xD6, 0xAB, 0xF7, 0x3F, 0x14, 0x00}, + + // CAP_SIMNEW + // last 4 bytes determine version (US-ASCII encoded) + {'S', 'I', 'M', ' ', 'c', 'l', 'i', 'e', + 'n', 't', ' ', ' ', 0 , 0 , 0 , 0}, + + // CAP_XTRAZ + {0x1A, 0x09, 0x3C, 0x6C, 0xD7, 0xFD, 0x4E, 0xC5, + 0x9D, 0x51, 0xA6, 0x47, 0x4E, 0x34, 0xF5, 0xA0}, + + // CAP_STR_2001 + {0xA0, 0xE9, 0x3F, 0x37, 0x4C, 0x7F, 0x11, 0xD1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_STR_2002 + {0x10, 0xCF, 0x40, 0xD1, 0x4C, 0x7F, 0x11, 0xD1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_LAST, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +//! Oscar Data Types +typedef Q_UINT8 BYTE; +typedef Q_UINT16 WORD; +typedef Q_UINT32 DWORD; + + +struct FLAP +{ + BYTE channel; + WORD sequence; + WORD length; +}; + +struct SNAC +{ + WORD family; + WORD subtype; + WORD flags; + DWORD id; +}; + +struct RateInfo +{ + WORD classId; + DWORD windowSize; + DWORD initialLevel; + DWORD clearLevel; + DWORD alertLevel; + DWORD limitLevel; + DWORD disconnectLevel; + DWORD currentLevel; + DWORD maxLevel; + DWORD lastTime; + BYTE currentState; +}; + +struct ChatExchangeInfo +{ + WORD number; + WORD maxRooms; + WORD maxRoomNameLength; + WORD maxMsgLength; + BYTE flags; + QString description; + BYTE canCreate; + QString charset1; + QString charset2; + QString lang1; + QString lang2; +}; + +struct ChatRoomInfo +{ + WORD exchange; + QByteArray cookie; + WORD instance; + QString description; + WORD maxMsgLength; + QString name; +}; + +struct ClientVersion +{ + QString clientString; + WORD clientId; + WORD major; + WORD minor; + WORD point; + WORD build; + DWORD other; + QString country; + QString lang; +}; + + /* ICQ Version Characteristics */ + const unsigned char ICQ_TCP_VERSION = 0x0008; + + /* AIM Version Characteristics */ + const char AIM_MD5_STRING[] = "AOL Instant Messenger (SM)"; + + /* SSI types */ + const WORD ROSTER_CONTACT = 0x0000; // a normal contact + const WORD ROSTER_GROUP = 0x0001; // a group of contacts + const WORD ROSTER_VISIBLE = 0x0002; // a contact on the visible list + const WORD ROSTER_INVISIBLE = 0x0003; // a contact on the invisible list + const WORD ROSTER_VISIBILITY = 0x0004; // this entry contains visibility setting TLV(0xca)=TLV(202) + const WORD ROSTER_PRESENCE = 0x0005; // Presence info (if others can see your idle status, etc) + const WORD ROSTER_ICQSHORTCUT = 0x0009; // Unknown or ICQ2k shortcut bar items + const WORD ROSTER_IGNORE = 0x000e; // a contact on the ignore list + const WORD ROSTER_LASTUPDATE = 0x000F; // Last update date (name: "LastUpdateDate") + const WORD ROSTER_NONICQ = 0x0010; // a non-icq contact, no UIN, used to send SMS + const WORD ROSTER_IMPORTTIME = 0x0013; // roster import time (name: "Import time") + const WORD ROSTER_BUDDYICONS = 0x0014; // Buddy icon info. (names: from "0" and incrementing by one) + + /* User classes/statuses */ + const WORD CLASS_UNCONFIRMED = 0x0001; // AOL Unconfirmed user + const WORD CLASS_ADMINISTRATOR = 0x0002; // AOL Administrator + const WORD CLASS_AOL = 0x0004; // AOL Staff + const WORD CLASS_COMMERCIAL = 0x0008; // AOL commercial account + const WORD CLASS_FREE = 0x0010; // ICQ non-commerical account + const WORD CLASS_AWAY = 0x0020; // Away status + const WORD CLASS_ICQ = 0x0040; // ICQ user + const WORD CLASS_WIRELESS = 0x0080; // AOL wireless user + const WORD CLASS_UNKNOWN100 = 0x0100; // Unknown + const WORD CLASS_UNKNOWN400 = 0x0400; // Unknown + const WORD CLASS_UNKNOWN800 = 0x0800; // Unknown + + const WORD STATUS_ONLINE = 0x0000; // Online + const WORD STATUS_AWAY = 0x0001; // Away + const WORD STATUS_DND = 0x0002; // Do not Disturb + const WORD STATUS_NA = 0x0004; // Not Available + const WORD STATUS_OCCUPIED = 0x0010; // Occupied (BUSY/BISY) + const WORD STATUS_FREE4CHAT = 0x0020; // Free for chat + const WORD STATUS_INVISIBLE = 0x0100; // Invisible +} + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/oscarutils.cpp b/kopete/protocols/oscar/liboscar/oscarutils.cpp new file mode 100644 index 00000000..13e28d9e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarutils.cpp @@ -0,0 +1,300 @@ +/* + Kopete Oscar Protocol + oscarutils.cpp - Oscar Utility Functions + + Copyright (c) 2004 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 "oscarutils.h" +#include <qhostaddress.h> +#include <kapplication.h> +#include <kdebug.h> + + +using namespace Oscar; + +QString Oscar::normalize( const QString& contact ) +{ + QString normal = contact.lower(); + normal.remove( ' ' ); + return normal; +} + +bool Oscar::operator==( TLV a, TLV b ) +{ + if ( a.type == b.type && a.length == b.length ) + return true; + else + return false; +} + +TLV Oscar::findTLV( const QValueList<TLV>& list, int type ) +{ + TLV t; + QValueList<TLV>::const_iterator it; + for ( it = list.begin(); it != list.end(); ++it ) + { + if ( ( *it ).type == type ) + return ( *it ); + } + + return t; +} + +bool Oscar::uptateTLVs( SSI& item, const QValueList<TLV>& list ) +{ + bool changed = false; + QValueList<TLV> tList( item.tlvList() ); + + QValueList<TLV>::const_iterator it; + for ( it = list.begin(); it != list.end(); ++it ) + { + TLV t = Oscar::findTLV( tList, ( *it ).type ); + if ( t && t.length == ( *it ).length && + memcmp( t.data.data(), ( *it ).data.data(), t.length ) == 0 ) + continue; + + if ( t ) + tList.remove( t ); + + tList.append( *it ); + changed = true; + } + + if ( changed ) + item.setTLVList( tList ); + + return changed; +} + +int Oscar::parseCap( char* cap ) +{ + int capflag = -1; + for (int i = 0; i < CAP_LAST; i++) + { + if (memcmp(&oscar_caps[i], cap, 16) == 0) + { + capflag = i; + break; // should only match once... + } + } + return capflag; +} + +const QString Oscar::capToString( char* cap ) +{ + QString dbg; + + dbg.sprintf( "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + cap[0], cap[1], cap[2], cap[3], cap[4], cap[5], cap[6], cap[7], cap[8], cap[9], + cap[10], cap[11], cap[12], cap[13], cap[14], cap[15] ); + + return dbg; +} + +DWORD Oscar::parseCapabilities( Buffer &inbuf, QString &versionString ) +{ + // + // FIXME: port capabilities array to some qt based list class, makes usage of memcmp obsolete + // + DWORD capflags = 0; + QString dbgCaps = "CAPS: "; + + while(inbuf.length() >= 16) + { + QByteArray cap; + cap.duplicate( inbuf.getBlock(16) ); + + for (int i=0; i < CAP_LAST; i++) + { + if (i == CAP_KOPETE) + { + if (memcmp(&oscar_caps[i], cap.data(), 12) == 0) + { + capflags |= (1 << i); + versionString.sprintf( "%d.%d.%d%d", cap[12], cap[13], cap[14], cap[15] ); + versionString.insert( 0, "Kopete " ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Kopete version - " << versionString << endl; + } + } + else if (i == CAP_MICQ) + { + if (memcmp(&oscar_caps[i], cap.data(), 12) == 0) + { + kdDebug(14150) << k_funcinfo << "MICQ version : <" << + (int)cap[12] << ":" << (int)cap[13] << ":" << + (int)cap[14] << ":" << (int)cap[15] << ">" << endl; + + capflags |= (1 << i); + + // FIXME: how to decode this micq version mess? [mETz - 08.06.2004] + /*versionString.sprintf("%d.%d.%d%d", + cap[12], cap[13], cap[14], cap[15]);*/ + break; + } + } + else if (i == CAP_SIMNEW) + { + if (memcmp(&oscar_caps[i], cap, 12) == 0) + { + kdDebug(14150) << k_funcinfo << "SIM version : <" << + (unsigned int)cap[12] << ":" << (unsigned int)cap[13] << ":" << + (unsigned int)cap[14] << ":" << (unsigned int)cap[15] << ">" << endl; + capflags |= (1 << i); + versionString.sprintf("%d.%d.%d%d", + cap[12], cap[13], cap[14], cap[15]); + versionString.insert( 0, "SIM " ); + break; + } + } + else if (i == CAP_SIMOLD) + { + if (memcmp(&oscar_caps[i], cap, 15) == 0) + { + int hiVersion = (cap[15] >> 6) - 1; + unsigned loVersion = cap[15] & 0x1F; + kdDebug(14150) << k_funcinfo << "OLD SIM version : <" << + hiVersion << ":" << loVersion << endl; + capflags |= (1 << i); + versionString.sprintf("%d.%d", (unsigned int)hiVersion, loVersion); + versionString.insert( 0, "SIM " ); + break; + } + } + else if (memcmp(&oscar_caps[i], cap.data(), 16) == 0) + { + capflags |= (1 << i); + dbgCaps += capName(i); + break; + } // END if(memcmp... + } // END for... + } + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << dbgCaps << endl; + return capflags; +} + +const QString Oscar::capName( int capNumber ) +{ + QString capString; + + switch ( capNumber ) + { + case CAP_VOICE: + capString = "CAP_VOICE "; + break; + case CAP_BUDDYICON: + capString = "CAP_BUDDYICON "; + break; + case CAP_IMIMAGE: + capString = "CAP_IMIMAGE "; + break; + case CAP_CHAT: + capString = "CAP_CHAT "; + break; + case CAP_GETFILE: + capString = "CAP_GETFILE "; + break; + case CAP_SENDFILE: + capString = "CAP_SENDFILE "; + break; + case CAP_GAMES2: + case CAP_GAMES: + capString = "CAP_GAMES "; + break; + case CAP_SAVESTOCKS: + capString = "CAP_SAVESTOCKS "; + break; + case CAP_SENDBUDDYLIST: + capString = "CAP_SENDBUDDYLIST "; + break; + case CAP_ISICQ: + capString = "CAP_ISICQ "; + break; + case CAP_APINFO: + capString = "CAP_APINFO "; + break; + case CAP_RTFMSGS: + capString = "CAP_RTFMSGS "; + break; + case CAP_ICQSERVERRELAY: + capString = "CAP_ICQSERVERRELAY "; + break; + case CAP_IS_2001: + capString = "CAP_IS_2001 "; + break; + case CAP_TRILLIAN: + capString = "CAP_TRILLIAN "; + break; + case CAP_TRILLIANCRYPT: + capString = "CAP_TRILLIANCRYPT "; + break; + case CAP_UTF8: + capString = "CAP_UTF8 "; + break; + case CAP_TYPING: + capString = "CAP_TYPING "; + break; + case CAP_INTEROPERATE: + capString = "CAP_INTEROPERATE "; + break; + case CAP_KOPETE: + capString = "CAP_KOPETE "; + break; + case CAP_MICQ: + capString = "CAP_MICQ "; + break; + case CAP_MACICQ: + capString = "CAP_MACICQ "; + break; + case CAP_SIMOLD: + capString = "CAP_SIMOLD "; + break; + case CAP_SIMNEW: + capString = "CAP_SIMNEW "; + break; + case CAP_XTRAZ: + capString = "CAP_XTRAZ "; + break; + case CAP_STR_2001: + capString = "CAP_STR_2001 "; + break; + case CAP_STR_2002: + capString = "CAP_STR_2002 "; + break; + default: + capString = "UNKNOWN CAP "; + } // END switch + + return capString; +} + +DWORD Oscar::getNumericalIP(const QString &address) +{ + QHostAddress addr; + if ( addr.setAddress( address ) == false ) + return 0; + + return (DWORD)addr.toIPv4Address(); +} + +QString Oscar::getDottedDecimal( DWORD address ) +{ + QHostAddress addr; + addr.setAddress((Q_UINT32)address); + return addr.toString(); +} + + + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/oscarutils.h b/kopete/protocols/oscar/liboscar/oscarutils.h new file mode 100644 index 00000000..bf8b5aba --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarutils.h @@ -0,0 +1,93 @@ +/* + Kopete Oscar Protocol + oscarutils.h - Oscar Utility Functions + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef _OSCARUTILS_H_ +#define _OSCARUTILS_H_ + +#include <qglobal.h> +#include <qvaluelist.h> +#include <qstring.h> +#include "oscartypes.h" +#include "buffer.h" + +namespace Oscar +{ + +///Normalize the contact name to all lowercase and no spaces +KOPETE_EXPORT QString normalize( const QString& ); + +///compare TLVs for equality +KOPETE_EXPORT bool operator==( TLV, TLV ); + +/** + * Find the TLV corresponding to the type in the list + */ +KOPETE_EXPORT TLV findTLV( const QValueList<TLV>&, int type ); + +/** + * Update TLVs of SSI item from TLV list if necessary + * \return true if something was updated + */ +KOPETE_EXPORT bool uptateTLVs( SSI& item, const QValueList<TLV>& list ); + +/** + * Get the value of the capability that corresponds to the value + * in the Capabilities enum + * \return -1 if the capability isn't found + * \return a non-negative number corresponding to the value of the + * capability in the Capabilities enum + */ +int parseCap( char* cap ); + +/** + * Convert the capability to a string we can print + */ +const QString capToString(char *cap); + +/** + * Parse the character array for validness and a version string + * \param buffer the buffer we'll be parsing for capabilities + * \param versionString a QString reference that will contain the + * version string of the detected client. Will be QString::null if + * no client is found + * \return a DWORD containg a bit array of the capabilities we found + */ +DWORD parseCapabilities(Buffer &inbuf, QString &versionString); + +/** + * Get the name of the capability from its number + */ +const QString capName( int capNumber ); + +/** + * Convert an IP address in dotted decimal notation to a + * numerical constant + */ +DWORD getNumericalIP( const QString& address ); + +/** + * Convert a numerical constant that is an IP address to + * dotted decimal format + */ +QString getDottedDecimal( DWORD address ); + +} + +#endif + +//kate: auto-insert-doxygen on; tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ownuserinfotask.cpp b/kopete/protocols/oscar/liboscar/ownuserinfotask.cpp new file mode 100644 index 00000000..a1baf073 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ownuserinfotask.cpp @@ -0,0 +1,137 @@ +/* + Kopete Oscar Protocol + aimlogintask.h - Handles logging into to the AIM service + + Copyright (c) 2004 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 "ownuserinfotask.h" +#include <qcstring.h> +#include <kdebug.h> +#include "buffer.h" +#include "connection.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" +#include "userdetails.h" +#include "ssimanager.h" + + +using namespace Oscar; + +OwnUserInfoTask::OwnUserInfoTask( Task* parent ) : Task( parent ) +{ +} + + +OwnUserInfoTask::~OwnUserInfoTask() +{ +} + + +bool OwnUserInfoTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + else + { + if ( st->snacService() == 0x01 && + ( st->snacSubtype() == 0x0F || st->snacSubtype() == 0x21 ) ) + return true; + else + return false; + } + +} + +bool OwnUserInfoTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + if ( !st ) + return false; + + Buffer* b = transfer->buffer(); + if ( st->snacSubtype() == 0x0F ) + { + UserDetails ud; + ud.fill( b ); + m_details = ud; + emit gotInfo(); + setSuccess( 0, QString::null ); + return true; + } + else + { + bool needUpload = false; + WORD infoType = b->getWord(); + if ( infoType == 0x0000 || infoType == 0x0001 ) + { + BYTE flags = b->getByte(); + if ( flags == 0x41 ) //we need to do a buddy upload when bit 8 = 1 + needUpload = true; + + QByteArray qba; + if ( b->length() != 0 ) + { //buffer might be empty if flags bit 8 = 1 + BYTE checksumLength = b->getByte(); + qba.duplicate( b->getBlock( checksumLength ) ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Self icon checksum: " << qba << endl; + } + + if ( needUpload ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Buddy icon upload requested" << endl; + emit buddyIconUploadRequested(); + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "no item for hash found" << endl; + } + } + + if ( infoType == 0x0002 ) + { + QString availableMsg( b->getBSTR() ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "self available message: " << endl; + } + + setSuccess( 0, QString::null ); + return true; + } + + } + + return false; +} + +void OwnUserInfoTask::onGo() +{ + //Send SNAC( 0x01, 0x0E ) + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x000E, 0x0000, client()->snacSequence() }; + Buffer *b = new Buffer(); //empty snac data + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +UserDetails OwnUserInfoTask::getInfo() const +{ + return m_details; +} + +//kate: tab-width 4; indent-mode csands; + +#include "ownuserinfotask.moc" diff --git a/kopete/protocols/oscar/liboscar/ownuserinfotask.h b/kopete/protocols/oscar/liboscar/ownuserinfotask.h new file mode 100644 index 00000000..30a169db --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ownuserinfotask.h @@ -0,0 +1,59 @@ +/* + Kopete Oscar Protocol + aimlogintask.h - Handles logging into to the AIM service + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ +#ifndef OWNUSERINFOTASK_H +#define OWNUSERINFOTASK_H + +#include "task.h" +#include "userdetails.h" + +/** +Request our user info from the server and handle our user info when it comes back + +@author Kopete Developers +*/ +class OwnUserInfoTask : public Task +{ +Q_OBJECT +public: + OwnUserInfoTask( Task* parent ); + + ~OwnUserInfoTask(); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + + UserDetails getInfo() const; + +signals: + /** Emitted when user info is recieved. Needed because succeeded() is only emitted once. */ + void gotInfo(); + + void haveAvailableMessage( const QString& ); + + void haveIconChecksum( const QString& ); + + void buddyIconUploadRequested(); + +private: + UserDetails m_details; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/prmparamstask.cpp b/kopete/protocols/oscar/liboscar/prmparamstask.cpp new file mode 100644 index 00000000..3668c73b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/prmparamstask.cpp @@ -0,0 +1,72 @@ +/* + Kopete Oscar Protocol + prmparamstask.h - handle OSCAR protocol errors + + Copyright (c) 2004 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 "prmparamstask.h" +#include <kdebug.h> +#include "connection.h" +#include "transfer.h" +#include "oscartypes.h" +#include "oscarutils.h" + +using namespace Oscar; + +PRMParamsTask::PRMParamsTask( Task* parent ) + : Task( parent ) +{ +} + + +PRMParamsTask::~PRMParamsTask() +{ +} + + +bool PRMParamsTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0009 && st->snacSubtype() == 0x0003 ) + return true; + + return false; +} + +bool PRMParamsTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Ignoring PRM Parameters. We don't use them" << endl; + setSuccess( 0, QString::null ); + return true; + } + + return false; +} + +void PRMParamsTask::onGo() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending PRM Parameters request" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0009, 0x0002, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + Transfer *t = createTransfer( f, s, buffer ); + send( t ); +} + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/prmparamstask.h b/kopete/protocols/oscar/liboscar/prmparamstask.h new file mode 100644 index 00000000..eebfdc61 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/prmparamstask.h @@ -0,0 +1,42 @@ +/* + Kopete Oscar Protocol + prmparamstask.h - handle OSCAR protocol errors + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ +#ifndef PRMPARAMSTASK_H +#define PRMPARAMSTASK_H + +#include <task.h> + +class Transfer; + +/** +@author Matt Rogers +*/ +class PRMParamsTask : public Task +{ +public: + PRMParamsTask( Task* parent ); + ~PRMParamsTask(); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + +}; + +#endif + +// kate: space-indent on; tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/profiletask.cpp b/kopete/protocols/oscar/liboscar/profiletask.cpp new file mode 100644 index 00000000..d64d5dbe --- /dev/null +++ b/kopete/protocols/oscar/liboscar/profiletask.cpp @@ -0,0 +1,119 @@ +/* + Kopete Oscar Protocol + profiletask.h - Update the user's profile on the server + + Copyright (c) 2004 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 "profiletask.h" + +#include <qstring.h> +#include <kdebug.h> + +#include "transfer.h" +#include "connection.h" +#include "oscartypes.h" +#include "oscarutils.h" + +using namespace Oscar; + +ProfileTask::ProfileTask( Task* parent ) + : Task( parent ) +{ +} + + +ProfileTask::~ProfileTask() +{ +} + + +bool ProfileTask::forMe( const Transfer* transfer ) const +{ + Q_UNUSED( transfer ); + return false; +} + +bool ProfileTask::take( Transfer* transfer ) +{ + Q_UNUSED( transfer ); + return false; +} + +void ProfileTask::onGo() +{ + sendProfileUpdate(); +} + +void ProfileTask::setProfileText( const QString& text ) +{ + m_profileText = text; +} + +void ProfileTask::setAwayMessage( const QString& text ) +{ + m_awayMessage = text; +} + +void ProfileTask::sendProfileUpdate() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SEND (CLI_SETUSERINFO/CLI_SET_LOCATION_INFO)" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0002, 0x0004, 0x0000, client()->snacSequence() }; + Buffer *buffer = new Buffer(); + Buffer capBuf; + + if ( !m_profileText.isNull() && !client()->isIcq() ) + { + static const QString defencoding = "text/aolrtf; charset=\"us-ascii\""; + buffer->addTLV(0x0001, defencoding.length(), defencoding.latin1()); + buffer->addTLV(0x0002, m_profileText.length(), m_profileText.local8Bit()); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting profile = " << m_profileText << endl; + } + + if ( !m_awayMessage.isNull() && !client()->isIcq() ) + { + static const QString defencoding = "text/aolrtf; charset=\"us-ascii\""; + buffer->addTLV(0x0003, defencoding.length(), defencoding.latin1()); + buffer->addTLV(0x0004, m_awayMessage.length(), m_awayMessage.local8Bit()); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting away message = " << m_awayMessage << endl; + } + + if ( client()->isIcq() ) + { + capBuf.addString( oscar_caps[CAP_ICQSERVERRELAY], 16 ); // we support type-2 messages + capBuf.addString( oscar_caps[CAP_UTF8], 16 ); // we can send/receive UTF encoded messages + capBuf.addString( oscar_caps[CAP_ISICQ], 16 ); // I think this is an icq client, but maybe I'm wrong + capBuf.addString( oscar_caps[CAP_KOPETE], 16 ); // we are the borg, resistance is futile + //capBuf.addString( oscar_caps[CAP_RTFMSGS], 16 ); // we do incoming RTF messages + capBuf.addString( oscar_caps[CAP_TYPING], 16 ); // we know you're typing something to us! + capBuf.addString( oscar_caps[CAP_BUDDYICON], 16 ); //can you take my picture? + } + else + { + capBuf.addString( oscar_caps[CAP_UTF8], 16 ); //we can send/receive UTF encoded messages + capBuf.addString( oscar_caps[CAP_KOPETE], 16 ); // we are the borg, resistance is futile + capBuf.addString( oscar_caps[CAP_TYPING], 16 ); // we know you're typing something to us! + capBuf.addString( oscar_caps[CAP_BUDDYICON], 16 ); //can you take my picture? + } + + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "adding capabilities, size=" << capBuf.length() << endl; + buffer->addTLV(0x0005, capBuf.length(), capBuf.buffer()); + Transfer* st = createTransfer( f, s , buffer ); + send( st ); + setSuccess( 0, QString::null ); + +} + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/profiletask.h b/kopete/protocols/oscar/liboscar/profiletask.h new file mode 100644 index 00000000..23555105 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/profiletask.h @@ -0,0 +1,56 @@ +/* + Kopete Oscar Protocol + profiletask.h - Update the user's profile on the server + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ +#ifndef PROFILETASK_H +#define PROFILETASK_H + +#include "task.h" + +/** +Task that sets the profile and away message on the server (AIM only). +Also takes care of updating the capabilities supported by the client (AIM and ICQ). + +The profile will be updated only if the profile text has been set non-null. +The away message will be set only if the away message has been set non-null. + +@author Matt Rogers +*/ +class ProfileTask : public Task +{ +public: + ProfileTask( Task* parent ); + ~ProfileTask(); + + bool forMe( const Transfer* transfer ) const; + bool take( Transfer* transfer ); + void onGo(); + + void setProfileText( const QString& text ); + void setAwayMessage( const QString& text ); + +private: + + void sendProfileUpdate(); + +private: + QString m_profileText; + QString m_awayMessage; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/rateclass.cpp b/kopete/protocols/oscar/liboscar/rateclass.cpp new file mode 100644 index 00000000..7fee4239 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rateclass.cpp @@ -0,0 +1,246 @@ +/* + rateclass.cpp - Rate Limiting Implementation + + Copyright (c) 2004 by Tom Linsky <thomas.linsky@cwru.edu> + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2003 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 "rateclass.h" +#include <qtimer.h> +#include <kdebug.h> +#include "transfer.h" + +using namespace Oscar; + +RateClass::RateClass( QObject* parent ) +: QObject( parent, 0 ) +{ + m_waitingToSend = false; + m_packetTimer.start(); +} + +RateClass::~ RateClass() +{ + dumpQueue(); + m_members.clear(); +} + +WORD RateClass::id() const +{ + return m_rateInfo.classId; +} + +void RateClass::setRateInfo( RateInfo newRateInfo ) +{ + m_rateInfo.classId = newRateInfo.classId; + m_rateInfo.windowSize = newRateInfo.windowSize; + m_rateInfo.clearLevel = newRateInfo.clearLevel; + m_rateInfo.alertLevel = newRateInfo.alertLevel; + m_rateInfo.limitLevel = newRateInfo.limitLevel; + m_rateInfo.disconnectLevel = newRateInfo.disconnectLevel; + m_rateInfo.currentLevel = newRateInfo.currentLevel; + m_rateInfo.initialLevel = newRateInfo.initialLevel; + m_rateInfo.maxLevel = newRateInfo.maxLevel; + m_rateInfo.lastTime = newRateInfo.lastTime; + m_rateInfo.currentState = newRateInfo.currentState; +} + +void RateClass::addMember( const SNAC& s ) +{ + addMember( s.family, s.subtype ); +} + +void RateClass::addMember( WORD family, WORD subtype ) +{ + SnacPair snacPair; + snacPair.family = family; + snacPair.subtype = subtype; + m_members.append( snacPair ); +} + +bool RateClass::isMember(const SNAC &s) const +{ + QValueList<SnacPair>::const_iterator it; + QValueList<SnacPair>::const_iterator spEnd = m_members.constEnd(); + for ( it = m_members.constBegin(); it != spEnd; ++it ) + { + if ( ( *it ).family == s.family && ( *it ).subtype == s.subtype ) + return true; + } + return false; +} + +bool RateClass::isMember( WORD family, WORD subtype ) const +{ + + QValueList<SnacPair>::const_iterator it; + QValueList<SnacPair>::const_iterator spEnd = m_members.constEnd(); + for ( it = m_members.constBegin(); it != spEnd; ++it ) + { + if ( ( *it ).family == family && ( *it ).subtype == subtype ) + { + return true; + } + } + return false; +} + +void RateClass::enqueue( Transfer* t ) +{ + m_packetQueue.push_back( t ); + /*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Send queue length is now: " + << m_packetQueue.count() << endl;*/ + setupTimer(); +} + +void RateClass::dequeue() +{ + m_packetQueue.pop_front(); +} + +bool RateClass::queueIsEmpty() const +{ + return m_packetQueue.isEmpty(); +} + +int RateClass::timeToInitialLevel() +{ + DWORD newLevel = 0; + + //get time elapsed since the last packet was sent + int timeDiff = m_packetTimer.elapsed(); + + newLevel = calcNewLevel( timeDiff ); + + if ( newLevel < m_rateInfo.initialLevel ) + { + int waitTime = ( m_rateInfo.initialLevel * m_rateInfo.windowSize ) - ( ( m_rateInfo.windowSize - 1 ) * m_rateInfo.currentLevel ); + return waitTime; + } + + return 0; +} + +int RateClass::timeToNextSend() +{ + + DWORD newLevel = 0; + + //get time elapsed since the last packet was sent + int timeDiff = m_packetTimer.elapsed(); + + DWORD windowSize = m_rateInfo.windowSize; + newLevel = calcNewLevel( timeDiff ); + + //The maximum level at which we can safely send a packet + DWORD maxPacket = m_rateInfo.alertLevel + RATE_SAFETY_TIME; + +/*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Rate Information:" + << "\nWindow Size: " << windowSize + << "\nWindow Size - 1: " << windowSize - 1 + << "\nOld Level: " << m_rateInfo.currentLevel + << "\nAlert Level: " << m_rateInfo.alertLevel + << "\nLimit Level: " << m_rateInfo.limitLevel + << "\nDisconnect Level: " << m_rateInfo.disconnectLevel + << "\nNew Level: " << newLevel + << "\nTime elapsed: " << timeDiff + << "\nMax level to send: " << maxPacket << endl; */ + + //If we are one packet or less away from being blocked, wait to send + if ( newLevel < maxPacket || newLevel < m_rateInfo.disconnectLevel ) + { + int waitTime = ( windowSize * maxPacket ) - ( ( windowSize - 1 ) * m_rateInfo.currentLevel ); + kdDebug(OSCAR_RAW_DEBUG) << "We're sending too fast. Will wait " << waitTime << "ms before sending" << endl; + return waitTime; + } + + return 0; +} + +DWORD RateClass::calcNewLevel( int timeDifference ) const +{ + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Time since last packet: " + // << timeDifference << endl; + //Calculate new rate level + //NewLevel = ( ( Window - 1 ) * OldLevel + TimeDiff )/Window + //add 1 because we never want to round down or there will be problems + uint newLevel = ( ( ( m_rateInfo.windowSize - 1 ) * m_rateInfo.currentLevel ) + timeDifference ) / m_rateInfo.windowSize; + if ( newLevel > m_rateInfo.initialLevel ) + newLevel = m_rateInfo.initialLevel; + + return newLevel; +} + +void RateClass::setupTimer() +{ + if ( !m_waitingToSend ) + { + m_waitingToSend = true; + + int ttns = timeToNextSend(); + if ( ttns <= 0 ) + { + slotSend(); //send now + } + else + { + QTimer::singleShot( ttns, this, SLOT( slotSend() ) ); //or send later + } + } +} + +void RateClass::slotSend() +{ + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + + if ( m_packetQueue.isEmpty() ) + return; + + //send then pop off the list + emit dataReady( m_packetQueue.first() ); + dequeue(); + + updateRateInfo(); + + m_waitingToSend = false; + + // check if we still have packets to send + if ( !m_packetQueue.isEmpty() ) + setupTimer(); +} + +void RateClass::updateRateInfo() +{ + //Update rate info + DWORD newLevel = calcNewLevel( m_packetTimer.elapsed() ); + m_rateInfo.currentLevel = newLevel; + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Current Level = " << newLevel << endl; + + //restart the timer + m_packetTimer.restart(); +} + +void RateClass::dumpQueue() +{ + QValueList<Transfer*>::iterator it = m_packetQueue.begin(); + while ( it != m_packetQueue.end() && m_packetQueue.count() > 0 ) + { + Transfer* t = ( *it ); + it = m_packetQueue.remove( it ); + delete t; + } +} + +#include "rateclass.moc" + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/rateclass.h b/kopete/protocols/oscar/liboscar/rateclass.h new file mode 100644 index 00000000..1bb86f03 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rateclass.h @@ -0,0 +1,132 @@ +/* + rateclass.h - Oscar Rate Limiting Implementation + + Copyright (c) 2004 by Tom Linsky <twl6@po.cwru.edu> + Copyright (c) 2004 by Matt Rogers <mattr@k + Kopete (c) 2002-2003 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. * + * * + ************************************************************************* +*/ + +#ifndef RATECLASS_H +#define RATECLASS_H + +#include "oscartypes.h" +#include <qobject.h> +#include <qvaluelist.h> +#include <qdatetime.h> +#include <qpair.h> + +const int RATE_SAFETY_TIME = 50; + +struct SnacPair +{ + int family; + int subtype; +}; + +class Transfer; + +class RateClass : public QObject +{ + Q_OBJECT +public: + RateClass( QObject* parent = 0 ); + ~RateClass(); + + /** Accessor for classid */ + Oscar::WORD id() const; + + /** Sets rate information */ + void setRateInfo( Oscar::RateInfo newRateInfo ); + + /** Add a SNAC to the rate class */ + void addMember( const Oscar::SNAC& s ); + + /** Adds rate class members */ + void addMember( Oscar::WORD family, Oscar::WORD subtype ); + + /** Tells whether the passed snac is a member of this rate class */ + bool isMember( const Oscar::SNAC& s ) const; + + /** + * Tells whether the passed family and subtype combo is a member + * of this rate class + */ + bool isMember( Oscar::WORD family, Oscar::WORD subtype ) const; + + /** Add a packet to the queue */ + void enqueue( Transfer* ); + + /** Takes a packet off the front of the queue */ + void dequeue(); + + /** Check if the queue is empty */ + bool queueIsEmpty() const; + + /** + * Calulate the time until we can send again + * Uses the first packet on the queue to determine the time since that's + * the packet that will get sent. + * \return the time in milliseconds that we need to wait + */ + int timeToNextSend(); + + /** + * Calulate the time until we get to initial level + * \return the time in milliseconds that we need to wait + */ + int timeToInitialLevel(); + + /** + * Calculates a new rate level and updates the rate class' current level + * to match + */ + void updateRateInfo(); + + /** + * Dump the current packet queue. These packets will not be sent. Used + * on disconnection + */ + void dumpQueue(); + +signals: + + /** Tell the rate class manager we're ready to send */ + void dataReady( Transfer* ); + +private: + + /** Calculate our new rate level */ + Oscar::DWORD calcNewLevel( int timeDifference ) const; + + /** sets up the timer for the transfer just added to the queue */ + void setupTimer(); + +private slots: + /** + * Send the packet. Basically emits dataReady for the first transfer + */ + void slotSend(); + +private: + + Oscar::RateInfo m_rateInfo; + QValueList<SnacPair> m_members; + QValueList<Transfer*> m_packetQueue; + QTime m_packetTimer; + + // we are waiting for the QTimer::singleShot() to send + bool m_waitingToSend; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/rateclassmanager.cpp b/kopete/protocols/oscar/liboscar/rateclassmanager.cpp new file mode 100644 index 00000000..8b306c0b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rateclassmanager.cpp @@ -0,0 +1,177 @@ +/* + Kopete Oscar Protocol + rateclassmanager.cpp - Manages the rates we get from the OSCAR server + + Copyright (c) 2004 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 <qvaluelist.h> +#include <kdebug.h> + + +#include "rateclassmanager.h" +#include "transfer.h" +#include "connection.h" +#include "rateclass.h" + + +class RateClassManagerPrivate +{ +public: + //! The list of rate classes owned by this manager + QValueList<RateClass*> classList; + Connection* client; +}; + +RateClassManager::RateClassManager( Connection* parent, const char* name ) +: QObject( parent, name ) +{ + d = new RateClassManagerPrivate(); + d->client = parent; +} + +RateClassManager::~RateClassManager() +{ + reset(); + delete d; +} + +void RateClassManager::reset() +{ + QValueList<RateClass*>::iterator it = d->classList.begin(); + while ( it != d->classList.end() && d->classList.count() > 0) + { + RateClass* rc = ( *it ); + it = d->classList.remove( it ); + delete rc; + } +} + +void RateClassManager::registerClass( RateClass* rc ) +{ + QObject::connect( rc, SIGNAL( dataReady( Transfer* ) ), this, SLOT( transferReady( Transfer* ) ) ); + d->classList.append( rc ); +} + +bool RateClassManager::canSend( Transfer* t ) const +{ + SnacTransfer* st = dynamic_cast<SnacTransfer*>( t ); + + if ( !st ) //no snac transfer, no rate limiting + { kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Not sending a snac" << endl; + return true; + } + + RateClass* rc = findRateClass( st ); + if ( rc ) + { + if ( rc->timeToNextSend() == 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "rate class " << rc->id() << " said it's okay to send" << endl; + return true; + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "rate class " << rc->id() << " said it's not okay to send yet" << endl; + return false; + } + } + else // no rate class + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "no rate class. doing no rate limiting" << endl; + return true; + } +} + +void RateClassManager::queue( Transfer* t ) +{ + SnacTransfer* st = dynamic_cast<SnacTransfer*>( t ); + if ( !st ) + { //we're not sending a snac + transferReady( t ); + return; + } + + RateClass* rc = findRateClass( st ); + if ( rc ) + rc->enqueue( st ); + else + transferReady( t ); +} + +QValueList<RateClass*> RateClassManager::classList() const +{ + return d->classList; +} + +void RateClassManager::transferReady( Transfer* t ) +{ + //tell the client to send it again. We should be + //able to send it now + FlapTransfer* ft = dynamic_cast<FlapTransfer*>( t ); + + if ( ft ) + ft->setFlapSequence( d->client->flapSequence() ); + + d->client->forcedSend( t ); +} + + +RateClass* RateClassManager::findRateClass( SnacTransfer* st ) const +{ + SNAC s = st->snac(); + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Looking for SNAC " << s.family << ", " << s.subtype << endl; + RateClass* rc = 0L; + QValueList<RateClass*>::const_iterator it; + QValueList<RateClass*>::const_iterator rcEnd = d->classList.constEnd(); + + for ( it = d->classList.constBegin(); it != rcEnd; ++it ) + { + if ( ( *it )->isMember( s.family, s.subtype ) ) + { + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Found SNAC(" << s.family << ", " << s.subtype << ") in class" << endl; + rc = ( *it ); + break; + } + } + + return rc; +} + +void RateClassManager::recalcRateLevels() +{ + QValueList<RateClass*>::iterator it; + QValueList<RateClass*>::iterator rcEnd = d->classList.end(); + for ( it = d->classList.begin(); it != rcEnd; ++it ) + ( *it )->updateRateInfo(); +} + +int RateClassManager::timeToInitialLevel( SNAC s ) +{ + QValueList<RateClass*>::const_iterator it; + QValueList<RateClass*>::const_iterator rcEnd = d->classList.constEnd(); + + for ( it = d->classList.constBegin(); it != rcEnd; ++it ) + { + if ( ( *it )->isMember( s.family, s.subtype ) ) + { + return ( *it )->timeToInitialLevel(); + } + } + return 0; +} + +#include "rateclassmanager.moc" + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/rateclassmanager.h b/kopete/protocols/oscar/liboscar/rateclassmanager.h new file mode 100644 index 00000000..4b972ede --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rateclassmanager.h @@ -0,0 +1,83 @@ +/* + Kopete Oscar Protocol + rateclassmanager.h - Manages the rates we get from the OSCAR server + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef RATECLASSMANAGER_H +#define RATECLASSMANAGER_H + +#include <qobject.h> +#include <qvaluelist.h> +#include <qmap.h> +#include "oscartypes.h" + +class Transfer; +class SnacTransfer; +class RateClass; +class Connection; +class RateClassManagerPrivate; + + +class RateClassManager : public QObject +{ +Q_OBJECT +public: + RateClassManager( Connection* parent, const char* name = 0 ); + ~RateClassManager(); + + /** Reset the rate manager */ + void reset(); + + /** Tell the rate manager about the new class */ + void registerClass( RateClass* ); + + //! Check if we can send the packet right away + bool canSend( Transfer* t ) const; + + //! Queue a transfer for sending later + void queue( Transfer* t ); + + /** Get the list of rate classes */ + QValueList<RateClass*> classList() const; + + /** Recalculate the rate levels for all the classes */ + void recalcRateLevels(); + + /** + * Find the rate class for the snac and + * calculate time until we get to initial level + * \return the time in milliseconds that we need to wait + */ + int timeToInitialLevel( Oscar::SNAC s ); + +public slots: + + void transferReady( Transfer* ); + +private: + + /** Find the rate class for the transfer */ + RateClass* findRateClass( SnacTransfer* st ) const; + +private: + + RateClassManagerPrivate* d; + +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/rateinfotask.cpp b/kopete/protocols/oscar/liboscar/rateinfotask.cpp new file mode 100644 index 00000000..f19cf792 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rateinfotask.cpp @@ -0,0 +1,173 @@ +/* + Kopete Oscar Protocol + rateinfotask.cpp - Fetch the rate class information + + Copyright (c) 2004 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 "rateinfotask.h" + +#include <qvaluelist.h> +#include <kdebug.h> +#include "rateclass.h" +#include "rateclassmanager.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" +#include "connection.h" + +using namespace Oscar; + +RateInfoTask::RateInfoTask( Task* parent ) + : Task( parent ) +{ + connect( this, SIGNAL( gotRateLimits() ), this, SLOT( sendRateInfoAck() ) ); +} + + +RateInfoTask::~RateInfoTask() +{ + +} + + +bool RateInfoTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( st && st->snacService() == 1 && st->snacSubtype() == 7 ) + return true; + else + return false; +} + +bool RateInfoTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + handleRateInfoResponse(); + setTransfer( 0 ); + return true; + } + return false; +} + +void RateInfoTask::onGo() +{ + sendRateInfoRequest(); +} + +void RateInfoTask::sendRateInfoRequest() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending rate info request (SNAC 0x01, 0x06)" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x0006, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + Transfer* st = createTransfer( f, s, buffer ); + send( st ); +} + +void RateInfoTask::handleRateInfoResponse() +{ + QValueList<RateClass*> rates; + Oscar::RateInfo ri; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "handling rate info response (SNAC 0x01, 0x07)" << endl; + Buffer* buffer = transfer()->buffer(); + + int numClasses = buffer->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got " << numClasses << " rate classes" << endl; + for ( int i = 0; i < numClasses; i++ ) + { + RateClass* newClass = new RateClass( client()->rateManager() ); + //parse rate classes and put them somewhere + ri.classId = buffer->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Rate class: " << ri.classId << endl; + //discard the rest (for right now) + ri.windowSize = buffer->getDWord(); //window size + ri.clearLevel = buffer->getDWord(); //clear level + ri.alertLevel = buffer->getDWord(); //alert level + ri.limitLevel = buffer->getDWord(); //limit level + ri.disconnectLevel = buffer->getDWord(); //disconnect level + ri.currentLevel = buffer->getDWord(); //current level + ri.initialLevel = ri.currentLevel; + ri.maxLevel = buffer->getDWord(); //max level + ri.lastTime = buffer->getDWord(); //last time + ri.currentState = buffer->getByte(); //current state + + newClass->setRateInfo( ri ); + rates.append( newClass ); + } + + int groupNum = 0; + int numGroupPairs = 0; + + for ( int i = 0; i < numClasses; i++ ) + { + groupNum = buffer->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding snac members to group " << groupNum << endl; + + RateClass* rc = 0L; + QValueList<RateClass*>::iterator it = rates.begin(); + for ( ; it != rates.end(); ++it ) + { + if ( ( *it )->id() == groupNum ) + { + rc = ( *it ); + break; + } + } + + m_rateGroups.append( groupNum ); + numGroupPairs = buffer->getWord(); + for ( int j = 0; j < numGroupPairs; j++ ) + { + WORD family = buffer->getWord(); + WORD subtype = buffer->getWord(); + rc->addMember( family, subtype ); + } + } + + QValueList<RateClass*>::iterator it = rates.begin(); + QValueList<RateClass*>::iterator rcEnd = rates.end(); + for ( ; it != rcEnd; ++it ) + client()->rateManager()->registerClass( ( *it ) ); + + emit gotRateLimits(); +} + +void RateInfoTask::sendRateInfoAck() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending rate info acknowledgement" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x0008, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + + QValueListConstIterator<int> cit = m_rateGroups.begin(); + QValueListConstIterator<int> end = m_rateGroups.end(); + for ( cit = m_rateGroups.begin(); cit != end; ++cit ) + { + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding rate " << (*cit) << " to rate ack" << endl; + buffer->addWord( (*cit) ); + } + + Transfer* st = createTransfer( f, s, buffer ); + send( st ); + setSuccess( 0, QString::null ); +} + +#include "rateinfotask.moc" + +//kate: tab-width 4; indent-mode csands; + diff --git a/kopete/protocols/oscar/liboscar/rateinfotask.h b/kopete/protocols/oscar/liboscar/rateinfotask.h new file mode 100644 index 00000000..3964f0ea --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rateinfotask.h @@ -0,0 +1,64 @@ +/* + Kopete Oscar Protocol + rateinfotask.h - Fetch the rate class information + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef RATEINFOTASK_H +#define RATEINFOTASK_H + +#include "task.h" +#include <qvaluelist.h> + +using namespace Oscar; + +/** +@author Matt Rogers +*/ +class RateInfoTask : public Task +{ +Q_OBJECT +public: + RateInfoTask( Task* parent ); + ~RateInfoTask(); + bool take( Transfer* transfer ); + +protected: + + bool forMe( const Transfer* transfer ) const; + void onGo(); + +signals: + void gotRateLimits(); + +private slots: + + //! Send the rate info request (SNAC 0x01, 0x06) + void sendRateInfoRequest(); + + //! Handle the rate info response (SNAC 0x01, 0x07) + void handleRateInfoResponse(); + + //! Acknowledge the rate information + void sendRateInfoAck(); + +private: + bool m_needRateAck; + QValueList<int> m_rateGroups; +}; + +//kate: tab-width 4; indent-mode csands; + +#endif diff --git a/kopete/protocols/oscar/liboscar/rtf.cc b/kopete/protocols/oscar/liboscar/rtf.cc new file mode 100644 index 00000000..6daa636e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rtf.cc @@ -0,0 +1,2427 @@ +#define yy_create_buffer rtf_create_buffer +#define yy_delete_buffer rtf_delete_buffer +#define yy_scan_buffer rtf_scan_buffer +#define yy_scan_string rtf_scan_string +#define yy_scan_bytes rtf_scan_bytes +#define yy_flex_debug rtf_flex_debug +#define yy_init_buffer rtf_init_buffer +#define yy_flush_buffer rtf_flush_buffer +#define yy_load_buffer_state rtf_load_buffer_state +#define yy_switch_to_buffer rtf_switch_to_buffer +#define yyin rtfin +#define yyleng rtfleng +#define yylex rtflex +#define yyout rtfout +#define yyrestart rtfrestart +#define yytext rtftext +#define yywrap rtfwrap + +#line 20 "rtf.cc" +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header$ + */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include <stdio.h> + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include <stdlib.h> +#include <unistd.h> + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include <io.h> +#include <stdlib.h> +#define YY_USE_CONST +#define YY_USE_PROTOS +#endif + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int yy_size_t; + + +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart YY_PROTO(( FILE *input_file )); + +void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); +void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); +void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); +#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) + +YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size )); +YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str )); +YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len )); + +static void *yy_flex_alloc YY_PROTO(( yy_size_t )); +static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )); +static void yy_flex_free YY_PROTO(( void * )); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) + +typedef unsigned char YY_CHAR; +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; +typedef int yy_state_type; +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state YY_PROTO(( void )); +static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); +static int yy_get_next_buffer YY_PROTO(( void )); +static void yy_fatal_error YY_PROTO(( yyconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 10 +#define YY_END_OF_BUFFER 11 +static yyconst short int yy_accept[33] = + { 0, + 0, 0, 11, 8, 8, 9, 9, 1, 2, 8, + 0, 0, 5, 3, 5, 0, 0, 5, 5, 5, + 0, 6, 5, 7, 5, 5, 5, 4, 5, 5, + 5, 0 + } ; + +static yyconst int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 1, 4, 1, 1, 1, 5, 1, + 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 1, 1, 7, + 1, 8, 9, 1, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 1, 12, 1, 1, 1, 1, 10, 10, 10, 10, + + 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 13, 11, 11, 11, + 11, 11, 14, 1, 15, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst int yy_meta[16] = + { 0, + 1, 1, 2, 1, 1, 2, 3, 4, 1, 2, + 2, 3, 2, 3, 3 + } ; + +static yyconst short int yy_base[37] = + { 0, + 0, 14, 45, 0, 0, 39, 25, 59, 59, 0, + 38, 0, 2, 59, 14, 0, 3, 59, 16, 21, + 25, 59, 28, 59, 38, 23, 19, 59, 17, 12, + 5, 59, 47, 51, 1, 55 + } ; + +static yyconst short int yy_def[37] = + { 0, + 33, 33, 32, 34, 34, 32, 32, 32, 32, 34, + 32, 32, 35, 32, 35, 36, 32, 32, 32, 32, + 36, 32, 32, 32, 32, 32, 25, 32, 25, 25, + 25, 0, 32, 32, 32, 32 + } ; + +static yyconst short int yy_nxt[75] = + { 0, + 32, 5, 13, 32, 18, 17, 6, 19, 22, 17, + 19, 7, 22, 8, 9, 5, 18, 31, 18, 20, + 6, 19, 30, 18, 29, 7, 23, 8, 9, 12, + 18, 28, 24, 25, 13, 13, 14, 15, 14, 14, + 26, 16, 11, 27, 32, 32, 28, 4, 4, 4, + 4, 10, 10, 32, 10, 21, 21, 21, 3, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32 + } ; + +static yyconst short int yy_chk[75] = + { 0, + 0, 1, 35, 0, 13, 12, 1, 13, 17, 12, + 31, 1, 17, 1, 1, 2, 15, 30, 19, 15, + 2, 19, 29, 20, 27, 2, 20, 2, 2, 7, + 23, 26, 21, 23, 7, 7, 7, 7, 7, 7, + 25, 11, 6, 25, 3, 0, 25, 33, 33, 33, + 33, 34, 34, 0, 34, 36, 36, 36, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "rtf.ll" +#define INITIAL 0 +#line 2 "rtf.ll" +/* + rtf.ll - A simple RTF Parser (Flex code) + + Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code) + Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port) + Kopete (c) 2002-2003 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. * + * * + ************************************************************************* + +update rtf.cc: +flex -olex.yy.c `test -f rtf.ll || echo './'`rtf.ll +sed '/^#/ s|lex.yy\.c|rtf.cc|' lex.yy.c >rtf.cc +rm -f lex.yy.c + +*/ + +#define UP 1 +#define DOWN 2 +#define CMD 3 +#define TXT 4 +#define HEX 5 +#define IMG 6 +#define UNICODE_CHAR 7 +#define SKIP 8 +#define SLASH 9 +#define S_TXT 10 + +#define YY_NEVER_INTERACTIVE 1 +#define YY_ALWAYS_INTERACTIVE 0 +#define YY_MAIN 0 + +#define YY_NO_UNPUT 1 +#define YY_STACK_USED 0 +#line 447 "rtf.cc" + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap YY_PROTO(( void )); +#else +extern int yywrap YY_PROTO(( void )); +#endif +#endif + +#ifndef YY_NO_UNPUT +static void yyunput YY_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen YY_PROTO(( yyconst char * )); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif +#endif + +#if YY_STACK_USED +static int yy_start_stack_ptr = 0; +static int yy_start_stack_depth = 0; +static int *yy_start_stack = 0; +#ifndef YY_NO_PUSH_STATE +static void yy_push_state YY_PROTO(( int new_state )); +#endif +#ifndef YY_NO_POP_STATE +static void yy_pop_state YY_PROTO(( void )); +#endif +#ifndef YY_NO_TOP_STATE +static int yy_top_state YY_PROTO(( void )); +#endif + +#else +#define YY_NO_PUSH_STATE 1 +#define YY_NO_POP_STATE 1 +#define YY_NO_TOP_STATE 1 +#endif + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include <stdlib.h> +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( yy_current_buffer->yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \ + && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int yylex YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +YY_DECL + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 46 "rtf.ll" + + +#line 601 "rtf.cc" + + if ( yy_init ) + { + yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! yy_current_buffer ) + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 33 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 59 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 48 "rtf.ll" +{ return UP; } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 49 "rtf.ll" +{ return DOWN; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 50 "rtf.ll" +{ return SLASH; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 51 "rtf.ll" +{ return UNICODE_CHAR; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 52 "rtf.ll" +{ return CMD; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 53 "rtf.ll" +{ return HEX; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 54 "rtf.ll" +{ return IMG; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 55 "rtf.ll" +{ return TXT; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 56 "rtf.ll" +{ return TXT; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 57 "rtf.ll" +ECHO; + YY_BREAK +#line 734 "rtf.cc" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_current_buffer->yy_n_chars = yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = + (int) (yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yy_flex_realloc( (void *) b->yy_ch_buf, + b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +static yy_state_type yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 33 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) +#else +static yy_state_type yy_try_NUL_trans( yy_current_state ) +yy_state_type yy_current_state; +#endif + { + register int yy_is_jam; + register char *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 33 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 32); + + return yy_is_jam ? 0 : yy_current_state; + } + + +#ifndef YY_NO_UNPUT +#ifdef YY_USE_PROTOS +static void yyunput( int c, register char *yy_bp ) +#else +static void yyunput( c, yy_bp ) +int c; +register char *yy_bp; +#endif + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + yy_current_buffer->yy_n_chars = + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + + yytext_ptr = yy_bp; + yy_hold_char = *yy_cp; + yy_c_buf_p = yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +#ifdef __cplusplus +static int yyinput() +#else +static int input() +#endif + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yy_c_buf_p - yytext_ptr; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + return EOF; + + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + + return c; + } + + +#ifdef YY_USE_PROTOS +void yyrestart( FILE *input_file ) +#else +void yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void yy_load_buffer_state( void ) +#else +void yy_load_buffer_state() +#endif + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; + } + + +#ifdef YY_USE_PROTOS +void yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yy_flex_free( (void *) b->yy_ch_buf ); + + yy_flex_free( (void *) b ); + } + + +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int isatty YY_PROTO(( int )); +#endif +#endif + +#ifdef YY_USE_PROTOS +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + + { + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + +#if YY_ALWAYS_INTERACTIVE + b->yy_is_interactive = 1; +#else +#if YY_NEVER_INTERACTIVE + b->yy_is_interactive = 0; +#else + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef YY_USE_PROTOS +void yy_flush_buffer( YY_BUFFER_STATE b ) +#else +void yy_flush_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == yy_current_buffer ) + yy_load_buffer_state(); + } + + +#ifndef YY_NO_SCAN_BUFFER +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size ) +#else +YY_BUFFER_STATE yy_scan_buffer( base, size ) +char *base; +yy_size_t size; +#endif + { + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef YY_NO_SCAN_STRING +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str ) +#else +YY_BUFFER_STATE yy_scan_string( yy_str ) +yyconst char *yy_str; +#endif + { + int len; + for ( len = 0; yy_str[len]; ++len ) + ; + + return yy_scan_bytes( yy_str, len ); + } +#endif + + +#ifndef YY_NO_SCAN_BYTES +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len ) +#else +YY_BUFFER_STATE yy_scan_bytes( bytes, len ) +yyconst char *bytes; +int len; +#endif + { + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) yy_flex_alloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef YY_NO_PUSH_STATE +#ifdef YY_USE_PROTOS +static void yy_push_state( int new_state ) +#else +static void yy_push_state( new_state ) +int new_state; +#endif + { + if ( yy_start_stack_ptr >= yy_start_stack_depth ) + { + yy_size_t new_size; + + yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yy_start_stack_depth * sizeof( int ); + + if ( ! yy_start_stack ) + yy_start_stack = (int *) yy_flex_alloc( new_size ); + + else + yy_start_stack = (int *) yy_flex_realloc( + (void *) yy_start_stack, new_size ); + + if ( ! yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + yy_start_stack[yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef YY_NO_POP_STATE +static void yy_pop_state() + { + if ( --yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yy_start_stack[yy_start_stack_ptr]); + } +#endif + + +#ifndef YY_NO_TOP_STATE +static int yy_top_state() + { + return yy_start_stack[yy_start_stack_ptr - 1]; + } +#endif + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +#ifdef YY_USE_PROTOS +static void yy_fatal_error( yyconst char msg[] ) +#else +static void yy_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +#ifdef YY_USE_PROTOS +static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) +#else +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +yyconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef YY_NEED_STRLEN +#ifdef YY_USE_PROTOS +static int yy_flex_strlen( yyconst char *s ) +#else +static int yy_flex_strlen( s ) +yyconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *yy_flex_alloc( yy_size_t size ) +#else +static void *yy_flex_alloc( size ) +yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *yy_flex_realloc( void *ptr, yy_size_t size ) +#else +static void *yy_flex_realloc( ptr, size ) +void *ptr; +yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void yy_flex_free( void *ptr ) +#else +static void yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + +#if YY_MAIN +int main() + { + yylex(); + return 0; + } +#endif +#line 57 "rtf.ll" + + +#include "rtf2html.h" + +void ParStyle::clearFormatting() +{ + // For now, do nothing. + // dir is not a formatting item. +} + +QString RTF2HTML::quoteString(const QString &_str, quoteMode mode) +{ + QString str = _str; + str.replace(QRegExp("&"), "&"); + str.replace(QRegExp("<"), "<"); + str.replace(QRegExp(">"), ">"); + str.replace(QRegExp("\""), """); + str.replace(QRegExp("\r"), ""); + switch (mode){ + case quoteHTML: + str.replace(QRegExp("\n"), "<br>\n"); + break; + case quoteXML: + str.replace(QRegExp("\n"), "<br/>\n"); + break; + default: + break; + } + QRegExp re(" +"); + int len; + int pos = 0; + + while ((pos = re.search(str, pos)) != -1) { + len = re.matchedLength(); + + if (len == 1) + continue; + QString s = " "; + for (int i = 1; i < len; i++) + s += " "; + str.replace(pos, len, s); + } + return str; +} + +RTF2HTML::RTF2HTML() + : cur_level(this) +{ + rtf_ptr = NULL; + bExplicitParagraph = false; +} + +OutTag* RTF2HTML::getTopOutTag(TagEnum tagType) +{ + vector<OutTag>::iterator it, it_end; + for(it = oTags.begin(), it_end = oTags.end(); it != it_end; ++it) + if (it->tag == tagType) + return &(*it); + return NULL; +} + +void RTF2HTML::FlushOutTags() +{ + vector<OutTag>::iterator iter; + for (iter = oTags.begin(); iter != oTags.end(); iter++) + { + OutTag &t = *iter; + switch (t.tag){ + case TAG_FONT_COLOR: + { + // RTF colors are 1-based; colors[] is a 0-based array. + if (t.param > colors.size() || t.param == 0) + break; + QColor &c = colors[t.param-1]; + PrintUnquoted("<span style=\"color:#%02X%02X%02X\">", c.red(), c.green(), c.blue()); + } + break; + case TAG_FONT_SIZE: + PrintUnquoted("<span style=\"font-size:%upt\">", t.param); + break; + case TAG_FONT_FAMILY: + { + FontDef &f = fonts[t.param-1]; + string name = (!f.nonTaggedName.empty()) ? f.nonTaggedName : f.taggedName; + PrintUnquoted("<span style=\"font-family:%s\">", name.c_str()); + } + break; + case TAG_BG_COLOR:{ + if (t.param > colors.size()) + break; + QColor &c = colors[t.param]; + PrintUnquoted("<span style=\"background-color:#%02X%02X%02X;\">", c.red(), c.green(), c.blue()); + break; + } + case TAG_BOLD: + PrintUnquoted("<b>"); + break; + case TAG_ITALIC: + PrintUnquoted("<i>"); + break; + case TAG_UNDERLINE: + PrintUnquoted("<u>"); + break; + default: + break; + } + } + oTags.clear(); +} + +// This function will close the already-opened tag 'tag'. It will take +// care of closing the tags which 'tag' contains first (ie. it will unroll +// the stack till the point where 'tag' is). +void Level::resetTag(TagEnum tag) +{ + // A stack which'll keep tags we had to close in order to reach 'tag'. + // After we close 'tag', we will reopen them. + stack<TagEnum> s; + + while (p->tags.size() > m_nTagsStartPos){ // Don't go further than the point where this level starts. + + TagEnum nTag = p->tags.top(); + + /* A tag will be located in oTags if it still wasn't printed out. + A tag will get printed out only if necessary (e.g. <I></I> will + be optimized away). + Thus, for each tag we remove from the actual tag stack, we also + try to remove a yet-to-be-printed tag, and only if there are no + yet-to-be-printed tags left, we start closing the tags we pop. + The tags have one space - needed for umlaute (�) and .utf8() + */ + if (p->oTags.empty()){ + switch (nTag){ + case TAG_FONT_COLOR: + case TAG_FONT_SIZE: + case TAG_BG_COLOR: + case TAG_FONT_FAMILY: + p->PrintUnquoted(" </span>"); + break; + case TAG_BOLD: + p->PrintUnquoted(" </b>"); + break; + case TAG_ITALIC: + p->PrintUnquoted(" </i>"); + break; + case TAG_UNDERLINE: + p->PrintUnquoted(" </u>"); + break; + default: + break; + } + }else{ + p->oTags.pop_back(); + } + + p->tags.pop(); + if (nTag == tag) break; // if we reached the tag we were looking to close. + s.push(nTag); // remember to reopen this tag + } + + if (tag == TAG_ALL) return; + + while (!s.empty()){ + TagEnum nTag = s.top(); + switch (nTag){ + case TAG_FONT_COLOR:{ + unsigned nFontColor = m_nFontColor; + m_nFontColor = 0; + setFontColor(nFontColor); + break; + } + case TAG_FONT_SIZE:{ + unsigned nFontSize = m_nFontSize; + m_nFontSize = 0; + setFontSize(nFontSize); + break; + } + case TAG_BG_COLOR:{ + unsigned nFontBgColor = m_nFontBgColor; + m_nFontBgColor = 0; + setFontBgColor(nFontBgColor); + break; + } + case TAG_FONT_FAMILY:{ + unsigned nFont = m_nFont; + m_nFont = 0; + setFont(nFont); + break; + } + case TAG_BOLD:{ + bool nBold = m_bBold; + m_bBold = false; + setBold(nBold); + break; + } + case TAG_ITALIC:{ + bool nItalic = m_bItalic; + m_bItalic = false; + setItalic(nItalic); + break; + } + case TAG_UNDERLINE:{ + bool nUnderline = m_bUnderline; + m_bUnderline = false; + setUnderline(nUnderline); + break; + } + default: + break; + } + s.pop(); + } +} + +Level::Level(RTF2HTML *_p) : + p(_p), + m_bFontTbl(false), + m_bColors(false), + m_bFontName(false), + m_bTaggedFontNameOk(false), + m_nFont(0), + m_nEncoding(0) +{ + m_nTagsStartPos = p->tags.size(); + Init(); +} + +Level::Level(const Level &l) : + p(l.p), + m_bFontTbl(l.m_bFontTbl), + m_bColors(l.m_bColors), + m_bFontName(false), + m_bTaggedFontNameOk(l.m_bTaggedFontNameOk), + m_nFont(l.m_nFont), + m_nEncoding(l.m_nEncoding) +{ + m_nTagsStartPos = p->tags.size(); + Init(); +} + +void Level::Init() +{ + m_nFontColor = 0; + m_nFontBgColor = 0; + m_nFontSize = 0; + m_bFontName = false; + m_bBold = false; + m_bItalic = false; + m_bUnderline = false; +} + +void RTF2HTML::PrintUnquoted(const char *str, ...) +{ + char buff[1024]; + va_list ap; + va_start(ap, str); + vsnprintf(buff, sizeof(buff), str, ap); + va_end(ap); + sParagraph += buff; +} + +void RTF2HTML::PrintQuoted(const QString &str) +{ + sParagraph += quoteString(str); +} + +void RTF2HTML::FlushParagraph() +{ + if (!bExplicitParagraph || sParagraph.isEmpty()) + return; + + /* + s += "<p dir=\""; + // Note: Lower-case 'ltr' and 'rtl' are important for Qt. + s += (parStyle.dir == ParStyle::DirRTL ? "rtl" : "ltr"); + s += "\">"; + s += sParagraph; + s += "</p>"; + */ + + s += sParagraph; + s += "<br>"; + + // Clear up the paragraph members + sParagraph = ""; + bExplicitParagraph = false; +} + +void Level::setFont(unsigned nFont) +{ + if (nFont <= 0) + return; + + if (m_bFontTbl){ + if (nFont > p->fonts.size() +1){ + kdDebug(14200) << "Invalid font index (" << + nFont << ") while parsing font table." << endl; + return; + } + if (nFont > p->fonts.size()){ + FontDef f; + f.charset = 0; + p->fonts.push_back(f); + } + m_nFont = nFont; + } + else + { + if (nFont > p->fonts.size()) + { + kdDebug(14200) << "Invalid font index (" << + nFont << ")." << endl; + return; + } + if (m_nFont == nFont) + return; + m_nFont = nFont; + if (m_nFont) resetTag(TAG_FONT_FAMILY); + m_nEncoding = p->fonts[nFont-1].charset; + p->oTags.push_back(OutTag(TAG_FONT_FAMILY, nFont)); + p->PutTag(TAG_FONT_FAMILY); + } +} + +void Level::setFontName() +{ + // This function is only valid during font table parsing. + if (m_bFontTbl){ + if ((m_nFont > 0) && (m_nFont <= p->fonts.size())) + // Be prepared to accept a font name. + m_bFontName = true; + } +} + +void Level::setEncoding(unsigned nEncoding) +{ + if (m_bFontTbl){ + if ((m_nFont > 0) && (m_nFont <= p->fonts.size())) + p->fonts[m_nFont-1].charset = nEncoding; + return; + } + m_nEncoding = nEncoding; +} + +void Level::setBold(bool bBold) +{ + if (m_bBold == bBold) return; + if (m_bBold) resetTag(TAG_BOLD); + m_bBold = bBold; + if (!m_bBold) return; + p->oTags.push_back(OutTag(TAG_BOLD, 0)); + p->PutTag(TAG_BOLD); +} + +void Level::setItalic(bool bItalic) +{ + if (m_bItalic == bItalic) return; + if (m_bItalic) resetTag(TAG_ITALIC); + m_bItalic = bItalic; + if (!m_bItalic) return; + p->oTags.push_back(OutTag(TAG_ITALIC, 0)); + p->PutTag(TAG_ITALIC); +} + +void Level::setUnderline(bool bUnderline) +{ + if (m_bUnderline == bUnderline) return; + if (m_bUnderline) resetTag(TAG_UNDERLINE); + m_bUnderline = bUnderline; + if (!m_bUnderline) return; + p->oTags.push_back(OutTag(TAG_UNDERLINE, 0)); + p->PutTag(TAG_UNDERLINE); +} + +void Level::setFontColor(unsigned short nColor) +{ + if (m_nFontColor == nColor) return; + if (m_nFontColor) resetTag(TAG_FONT_COLOR); + if (nColor > p->colors.size()) return; + m_nFontColor = nColor; + p->oTags.push_back(OutTag(TAG_FONT_COLOR, m_nFontColor)); + p->PutTag(TAG_FONT_COLOR); +} + +void Level::setFontBgColor(unsigned short nColor) +{ + if (m_nFontBgColor == nColor) return; + if (m_nFontBgColor != 0) resetTag(TAG_BG_COLOR); + if (nColor > p->colors.size()) return; + m_nFontBgColor = nColor; + p->oTags.push_back(OutTag(TAG_BG_COLOR, m_nFontBgColor)); + p->PutTag(TAG_BG_COLOR); +} + +void Level::setFontSizeHalfPoints(unsigned short nSize) +{ + setFontSize(nSize / 2); +} + +void Level::setFontSize(unsigned short nSize) +{ + if (m_nFontSize == nSize) return; + if (m_nFontSize) resetTag(TAG_FONT_SIZE); + p->oTags.push_back(OutTag(TAG_FONT_SIZE, nSize)); + p->PutTag(TAG_FONT_SIZE); + m_nFontSize = nSize; +} + +void Level::startParagraph() +{ + // Whatever tags we have open now, close them. + // We cannot carry let character formatting tags wrap paragraphs, + // since a formatting tag can close at any time and we cannot + // close the paragraph any time we want. + resetTag(TAG_ALL); + + // Flush the current paragraph HTML to the document HTML. + p->FlushParagraph(); + + // Mark this new paragraph as an explicit one (from \par etc.). + p->bExplicitParagraph = true; + + // Restore character formatting + p->oTags.push_back(OutTag(TAG_FONT_SIZE, m_nFontSize)); + p->PutTag(TAG_FONT_SIZE); + p->oTags.push_back(OutTag(TAG_FONT_COLOR, m_nFontColor)); + p->PutTag(TAG_FONT_COLOR); + p->oTags.push_back(OutTag(TAG_FONT_FAMILY, m_nFont)); + p->PutTag(TAG_FONT_FAMILY); + if (m_nFontBgColor != 0) + { + p->oTags.push_back(OutTag(TAG_BG_COLOR, m_nFontBgColor)); + p->PutTag(TAG_BG_COLOR); + } + if (m_bBold) + { + p->oTags.push_back(OutTag(TAG_BOLD, 0)); + p->PutTag(TAG_BOLD); + } + if (m_bItalic) + { + p->PutTag(TAG_ITALIC); + p->oTags.push_back(OutTag(TAG_ITALIC, 0)); + } + if (m_bUnderline) + { + p->oTags.push_back(OutTag(TAG_UNDERLINE, 0)); + p->PutTag(TAG_UNDERLINE); + } +} + +bool Level::isParagraphOpen() const +{ + return p->bExplicitParagraph; +} + +void Level::clearParagraphFormatting() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + // Since we don't implement any of the paragraph formatting tags (e.g. alignment), + // we don't clean up anything here. Note that \pard does NOT clean character + // formatting (such as font size, font weight, italics...). + p->parStyle.clearFormatting(); +} + +void Level::setParagraphDirLTR() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + p->parStyle.dir = ParStyle::DirLTR; +} + +void Level::setParagraphDirRTL() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + p->parStyle.dir = ParStyle::DirRTL; +} + +void Level::addLineBreak() +{ + p->PrintUnquoted("<br/>"); +} + +void Level::reset() +{ + resetTag(TAG_ALL); + if (m_bColors){ + if (m_bColorInit){ + QColor c(m_nRed, m_nGreen, m_nBlue); + p->colors.push_back(c); + resetColors(); + } + return; + } +} + +void Level::setText(const char *str) +{ + if (m_bColors) + { + reset(); + } + else if (m_bFontTbl) + { + if ((m_nFont <= 0) || (m_nFont > p->fonts.size())) + return; + + FontDef& def = p->fonts[m_nFont-1]; + + char *pp = strchr(str, ';'); + unsigned size; + if (pp != NULL) + size = (pp - str); + else + size = strlen(str); + + if (m_bFontName) + { + def.nonTaggedName.append(str, size); + // We know we have the entire name + if (pp != NULL) + m_bFontName = false; + } + else if (!m_bTaggedFontNameOk) + { + def.taggedName.append(str, size); + if (pp != NULL) + m_bTaggedFontNameOk = true; + } + } + else + { + for (; *str; str++) + if ((unsigned char)(*str) >= ' ') break; + if (!*str) return; + p->FlushOutTags(); + text += str; + } +} + +void Level::flush() +{ + if (text.length() == 0) return; + // TODO: Make encoding work in Kopete + /* + const char *encoding = NULL; + if (m_nEncoding){ + for (const ENCODING *c = ICQPlugin::core->encodings; c->language; c++){ + if (!c->bMain) + continue; + if ((unsigned)c->rtf_code == m_nEncoding){ + encoding = c->codec; + break; + } + } + } + if (encoding == NULL) + encoding = p->encoding; + + QTextCodec *codec = ICQClient::_getCodec(encoding); + */ + //p->PrintQuoted(codec->toUnicode(text.c_str(), text.length())); + p->PrintQuoted(text.c_str()); + text = ""; +} + +const unsigned FONTTBL = 0; +const unsigned COLORTBL = 1; +const unsigned RED = 2; +const unsigned GREEN = 3; +const unsigned BLUE = 4; +const unsigned CF = 5; +const unsigned FS = 6; +const unsigned HIGHLIGHT = 7; +const unsigned PARD = 8; +const unsigned PAR = 9; +const unsigned I = 10; +const unsigned B = 11; +const unsigned UL = 12; +const unsigned F = 13; +const unsigned FCHARSET = 14; +const unsigned FNAME = 15; +const unsigned ULNONE = 16; +const unsigned LTRPAR = 17; +const unsigned RTLPAR = 18; +const unsigned LINE = 19; + +static char cmds[] = + "fonttbl\x00" + "colortbl\x00" + "red\x00" + "green\x00" + "blue\x00" + "cf\x00" + "fs\x00" + "highlight\x00" + "pard\x00" + "par\x00" + "i\x00" + "b\x00" + "ul\x00" + "f\x00" + "fcharset\x00" + "fname\x00" + "ulnone\x00" + "ltrpar\x00" + "rtlpar\x00" + "line\x00" + "\x00"; + +int yywrap() { return 1; } + +static char h2d(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return (c - 'A') + 10; + if ((c >= 'a') && (c <= 'f')) + return (c - 'a') + 10; + return 0; +} + +QString RTF2HTML::Parse(const char *rtf, const char *_encoding) +{ + encoding = _encoding; + YY_BUFFER_STATE yy_current_buffer = yy_scan_string(rtf); + rtf_ptr = rtf; + for (;;){ + int res = yylex(); + if (!res) break; + switch (res){ + case UP:{ + cur_level.flush(); + levels.push(cur_level); + break; + } + case DOWN:{ + if (!levels.empty()){ + cur_level.flush(); + cur_level.reset(); + cur_level = levels.top(); + levels.pop(); + } + break; + } + case IMG:{ + cur_level.flush(); + const char ICQIMAGE[] = "icqimage"; + const char *smiles[] = { ":-)" , ":-O" , ":-|" , ":-/" , // 0-3 + ":-(" , ":-*" , ":-/" , ":'(" , // 4-7 + ";-)" , ":-@" , ":-$" , ":-X" , // 8-B + ":-P" , "8-)" , "O:)" , ":-D" }; // C-F + const char *p = yytext + 3; + if ((strlen(p) > strlen(ICQIMAGE)) && !memcmp(p, ICQIMAGE, strlen(ICQIMAGE))){ + unsigned n = 0; + for (p += strlen(ICQIMAGE); *p; p++){ + if ((*p >= '0') && (*p <= '9')){ + n = n << 4; + n += (*p - '0'); + continue; + } + if ((*p >= 'A') && (*p <= 'F')){ + n = n << 4; + n += (*p - 'A') + 10; + continue; + } + if ((*p >= 'a') && (*p <= 'f')){ + n = n << 4; + n += (*p - 'a') + 10; + continue; + } + break; + } + if (n < 16) + PrintUnquoted(" %s ", smiles[n] ); + }else{ + kdDebug(14200) << "Unknown image " << yytext << endl; + } + break; + } + case SKIP: + break; + case SLASH: + cur_level.setText(yytext+1); + break; + case TXT: + cur_level.setText(yytext); + break; + case UNICODE_CHAR:{ + cur_level.flush(); + sParagraph += QChar((unsigned short)(atol(yytext + 2))); + break; + } + case HEX:{ + char s[2]; + s[0] = (h2d(yytext[2]) << 4) + h2d(yytext[3]); + s[1] = 0; + cur_level.setText(s); + break; + } + case CMD: + { + cur_level.flush(); + const char *cmd = yytext + 1; + unsigned n_cmd = 0; + unsigned cmd_size = 0; + int cmd_value = -1; + const char *p; + for (p = cmd; *p; p++, cmd_size++) + if (((*p >= '0') && (*p <= '9')) || (*p == ' ')) break; + if (*p && (*p != ' ')) cmd_value = atol(p); + for (p = cmds; *p; p += strlen(p) + 1, n_cmd++){ + if (strlen(p) > cmd_size) continue; + if (!memcmp(p, cmd, cmd_size)) break; + } + cmd += strlen(p); + switch (n_cmd){ + case FONTTBL: // fonttbl + cur_level.setFontTbl(); + break; + case COLORTBL: + cur_level.setColors(); + break; + case RED: + cur_level.setRed(cmd_value); + break; + case GREEN: + cur_level.setGreen(cmd_value); + break; + case BLUE: + cur_level.setBlue(cmd_value); + break; + case CF: + cur_level.setFontColor(cmd_value); + break; + case FS: + cur_level.setFontSizeHalfPoints(cmd_value); + break; + case HIGHLIGHT: + cur_level.setFontBgColor(cmd_value); + break; + case PARD: + cur_level.clearParagraphFormatting(); + break; + case PAR: + cur_level.startParagraph(); + break; + case I: + cur_level.setItalic(cmd_value != 0); + break; + case B: + cur_level.setBold(cmd_value != 0); + break; + case UL: + cur_level.setUnderline(cmd_value != 0); + break; + case ULNONE: + cur_level.setUnderline(false); + break; + case F: + // RTF fonts are 0-based; our font index is 1-based. + cur_level.setFont(cmd_value+1); + break; + case FCHARSET: + cur_level.setEncoding(cmd_value); + break; + case FNAME: + cur_level.setFontName(); + break; + case LTRPAR: + cur_level.setParagraphDirLTR(); + break; + case RTLPAR: + cur_level.setParagraphDirRTL(); + break; + case LINE: + cur_level.addLineBreak(); + } + break; + } + } + } + yy_delete_buffer(yy_current_buffer); + yy_current_buffer = NULL; + FlushParagraph(); + return s; +} + +/* +bool ICQClient::parseRTF(const char *rtf, const char *encoding, QString &res) +{ + char _RTF[] = "{\\rtf"; + if ((strlen(rtf) > strlen(_RTF)) && !memcmp(rtf, _RTF, strlen(_RTF))){ + RTF2HTML p; + res = p.Parse(rtf, encoding); + return true; + } + QTextCodec *codec = ICQClient::_getCodec(encoding); + res = codec->toUnicode(rtf, strlen(rtf)); + return false; +} +*/ diff --git a/kopete/protocols/oscar/liboscar/rtf.ll b/kopete/protocols/oscar/liboscar/rtf.ll new file mode 100644 index 00000000..d982234b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rtf.ll @@ -0,0 +1,864 @@ +%{ +/* + rtf.ll - A simple RTF Parser (Flex code) + + Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code) + Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port) + Kopete (c) 2002-2003 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. * + * * + ************************************************************************* + +update rtf.cc: +flex -olex.yy.c `test -f rtf.ll || echo './'`rtf.ll +sed '/^#/ s|lex.yy\.c|rtf.cc|' lex.yy.c >rtf.cc +rm -f lex.yy.c + +*/ + +#define UP 1 +#define DOWN 2 +#define CMD 3 +#define TXT 4 +#define HEX 5 +#define IMG 6 +#define UNICODE_CHAR 7 +#define SKIP 8 +#define SLASH 9 +#define S_TXT 10 + +#define YY_NEVER_INTERACTIVE 1 +#define YY_ALWAYS_INTERACTIVE 0 +#define YY_MAIN 0 + +%} + +%option nounput +%option nostack +%option prefix="rtf" + +%% + +"{" { return UP; } +"}" { return DOWN; } +"\\"[\\\{\}] { return SLASH; } +"\\u"[0-9]{3,7}[ ]?"?" { return UNICODE_CHAR; } +"\\"[A-Za-z]+[0-9]*[ ]? { return CMD; } +"\\'"[0-9A-Fa-f][0-9A-Fa-f] { return HEX; } +"<##"[^>]+">" { return IMG; } +[^\\{}<]+ { return TXT; } +. { return TXT; } +%% + +#include "rtf2html.h" + +void ParStyle::clearFormatting() +{ + // For now, do nothing. + // dir is not a formatting item. +} + +QString RTF2HTML::quoteString(const QString &_str, quoteMode mode) +{ + QString str = _str; + str.replace(QRegExp("&"), "&"); + str.replace(QRegExp("<"), "<"); + str.replace(QRegExp(">"), ">"); + str.replace(QRegExp("\""), """); + str.replace(QRegExp("\r"), ""); + switch (mode){ + case quoteHTML: + str.replace(QRegExp("\n"), "<br>\n"); + break; + case quoteXML: + str.replace(QRegExp("\n"), "<br/>\n"); + break; + default: + break; + } + QRegExp re(" +"); + int len; + int pos = 0; + + while ((pos = re.search(str, pos)) != -1) { + len = re.matchedLength(); + + if (len == 1) + continue; + QString s = " "; + for (int i = 1; i < len; i++) + s += " "; + str.replace(pos, len, s); + } + return str; +} + +RTF2HTML::RTF2HTML() + : cur_level(this) +{ + rtf_ptr = NULL; + bExplicitParagraph = false; +} + +OutTag* RTF2HTML::getTopOutTag(TagEnum tagType) +{ + vector<OutTag>::iterator it, it_end; + for(it = oTags.begin(), it_end = oTags.end(); it != it_end; ++it) + if (it->tag == tagType) + return &(*it); + return NULL; +} + +void RTF2HTML::FlushOutTags() +{ + vector<OutTag>::iterator iter; + for (iter = oTags.begin(); iter != oTags.end(); iter++) + { + OutTag &t = *iter; + switch (t.tag){ + case TAG_FONT_COLOR: + { + // RTF colors are 1-based; colors[] is a 0-based array. + if (t.param > colors.size() || t.param == 0) + break; + QColor &c = colors[t.param-1]; + PrintUnquoted("<span style=\"color:#%02X%02X%02X\">", c.red(), c.green(), c.blue()); + } + break; + case TAG_FONT_SIZE: + PrintUnquoted("<span style=\"font-size:%upt\">", t.param); + break; + case TAG_FONT_FAMILY: + { + FontDef &f = fonts[t.param-1]; + string name = (!f.nonTaggedName.empty()) ? f.nonTaggedName : f.taggedName; + PrintUnquoted("<span style=\"font-family:%s\">", name.c_str()); + } + break; + case TAG_BG_COLOR:{ + if (t.param > colors.size()) + break; + QColor &c = colors[t.param]; + PrintUnquoted("<span style=\"background-color:#%02X%02X%02X;\">", c.red(), c.green(), c.blue()); + break; + } + case TAG_BOLD: + PrintUnquoted("<b>"); + break; + case TAG_ITALIC: + PrintUnquoted("<i>"); + break; + case TAG_UNDERLINE: + PrintUnquoted("<u>"); + break; + default: + break; + } + } + oTags.clear(); +} + +// This function will close the already-opened tag 'tag'. It will take +// care of closing the tags which 'tag' contains first (ie. it will unroll +// the stack till the point where 'tag' is). +void Level::resetTag(TagEnum tag) +{ + // A stack which'll keep tags we had to close in order to reach 'tag'. + // After we close 'tag', we will reopen them. + stack<TagEnum> s; + + while (p->tags.size() > m_nTagsStartPos){ // Don't go further than the point where this level starts. + + TagEnum nTag = p->tags.top(); + + /* A tag will be located in oTags if it still wasn't printed out. + A tag will get printed out only if necessary (e.g. <I></I> will + be optimized away). + Thus, for each tag we remove from the actual tag stack, we also + try to remove a yet-to-be-printed tag, and only if there are no + yet-to-be-printed tags left, we start closing the tags we pop. + The tags have one space - needed for umlaute (�) and .utf8() + */ + if (p->oTags.empty()){ + switch (nTag){ + case TAG_FONT_COLOR: + case TAG_FONT_SIZE: + case TAG_BG_COLOR: + case TAG_FONT_FAMILY: + p->PrintUnquoted(" </span>"); + break; + case TAG_BOLD: + p->PrintUnquoted(" </b>"); + break; + case TAG_ITALIC: + p->PrintUnquoted(" </i>"); + break; + case TAG_UNDERLINE: + p->PrintUnquoted(" </u>"); + break; + default: + break; + } + }else{ + p->oTags.pop_back(); + } + + p->tags.pop(); + if (nTag == tag) break; // if we reached the tag we were looking to close. + s.push(nTag); // remember to reopen this tag + } + + if (tag == TAG_ALL) return; + + while (!s.empty()){ + TagEnum nTag = s.top(); + switch (nTag){ + case TAG_FONT_COLOR:{ + unsigned nFontColor = m_nFontColor; + m_nFontColor = 0; + setFontColor(nFontColor); + break; + } + case TAG_FONT_SIZE:{ + unsigned nFontSize = m_nFontSize; + m_nFontSize = 0; + setFontSize(nFontSize); + break; + } + case TAG_BG_COLOR:{ + unsigned nFontBgColor = m_nFontBgColor; + m_nFontBgColor = 0; + setFontBgColor(nFontBgColor); + break; + } + case TAG_FONT_FAMILY:{ + unsigned nFont = m_nFont; + m_nFont = 0; + setFont(nFont); + break; + } + case TAG_BOLD:{ + bool nBold = m_bBold; + m_bBold = false; + setBold(nBold); + break; + } + case TAG_ITALIC:{ + bool nItalic = m_bItalic; + m_bItalic = false; + setItalic(nItalic); + break; + } + case TAG_UNDERLINE:{ + bool nUnderline = m_bUnderline; + m_bUnderline = false; + setUnderline(nUnderline); + break; + } + default: + break; + } + s.pop(); + } +} + +Level::Level(RTF2HTML *_p) : + p(_p), + m_bFontTbl(false), + m_bColors(false), + m_bFontName(false), + m_bTaggedFontNameOk(false), + m_nFont(0), + m_nEncoding(0) +{ + m_nTagsStartPos = p->tags.size(); + Init(); +} + +Level::Level(const Level &l) : + p(l.p), + m_bFontTbl(l.m_bFontTbl), + m_bColors(l.m_bColors), + m_bFontName(false), + m_bTaggedFontNameOk(l.m_bTaggedFontNameOk), + m_nFont(l.m_nFont), + m_nEncoding(l.m_nEncoding) +{ + m_nTagsStartPos = p->tags.size(); + Init(); +} + +void Level::Init() +{ + m_nFontColor = 0; + m_nFontBgColor = 0; + m_nFontSize = 0; + m_bFontName = false; + m_bBold = false; + m_bItalic = false; + m_bUnderline = false; +} + +void RTF2HTML::PrintUnquoted(const char *str, ...) +{ + char buff[1024]; + va_list ap; + va_start(ap, str); + vsnprintf(buff, sizeof(buff), str, ap); + va_end(ap); + sParagraph += buff; +} + +void RTF2HTML::PrintQuoted(const QString &str) +{ + sParagraph += quoteString(str); +} + +void RTF2HTML::FlushParagraph() +{ + if (!bExplicitParagraph || sParagraph.isEmpty()) + return; + + /* + s += "<p dir=\""; + // Note: Lower-case 'ltr' and 'rtl' are important for Qt. + s += (parStyle.dir == ParStyle::DirRTL ? "rtl" : "ltr"); + s += "\">"; + s += sParagraph; + s += "</p>"; + */ + + s += sParagraph; + s += "<br>"; + + // Clear up the paragraph members + sParagraph = ""; + bExplicitParagraph = false; +} + +void Level::setFont(unsigned nFont) +{ + if (nFont <= 0) + return; + + if (m_bFontTbl){ + if (nFont > p->fonts.size() +1){ + kdDebug(14200) << "Invalid font index (" << + nFont << ") while parsing font table." << endl; + return; + } + if (nFont > p->fonts.size()){ + FontDef f; + f.charset = 0; + p->fonts.push_back(f); + } + m_nFont = nFont; + } + else + { + if (nFont > p->fonts.size()) + { + kdDebug(14200) << "Invalid font index (" << + nFont << ")." << endl; + return; + } + if (m_nFont == nFont) + return; + m_nFont = nFont; + if (m_nFont) resetTag(TAG_FONT_FAMILY); + m_nEncoding = p->fonts[nFont-1].charset; + p->oTags.push_back(OutTag(TAG_FONT_FAMILY, nFont)); + p->PutTag(TAG_FONT_FAMILY); + } +} + +void Level::setFontName() +{ + // This function is only valid during font table parsing. + if (m_bFontTbl){ + if ((m_nFont > 0) && (m_nFont <= p->fonts.size())) + // Be prepared to accept a font name. + m_bFontName = true; + } +} + +void Level::setEncoding(unsigned nEncoding) +{ + if (m_bFontTbl){ + if ((m_nFont > 0) && (m_nFont <= p->fonts.size())) + p->fonts[m_nFont-1].charset = nEncoding; + return; + } + m_nEncoding = nEncoding; +} + +void Level::setBold(bool bBold) +{ + if (m_bBold == bBold) return; + if (m_bBold) resetTag(TAG_BOLD); + m_bBold = bBold; + if (!m_bBold) return; + p->oTags.push_back(OutTag(TAG_BOLD, 0)); + p->PutTag(TAG_BOLD); +} + +void Level::setItalic(bool bItalic) +{ + if (m_bItalic == bItalic) return; + if (m_bItalic) resetTag(TAG_ITALIC); + m_bItalic = bItalic; + if (!m_bItalic) return; + p->oTags.push_back(OutTag(TAG_ITALIC, 0)); + p->PutTag(TAG_ITALIC); +} + +void Level::setUnderline(bool bUnderline) +{ + if (m_bUnderline == bUnderline) return; + if (m_bUnderline) resetTag(TAG_UNDERLINE); + m_bUnderline = bUnderline; + if (!m_bUnderline) return; + p->oTags.push_back(OutTag(TAG_UNDERLINE, 0)); + p->PutTag(TAG_UNDERLINE); +} + +void Level::setFontColor(unsigned short nColor) +{ + if (m_nFontColor == nColor) return; + if (m_nFontColor) resetTag(TAG_FONT_COLOR); + if (nColor > p->colors.size()) return; + m_nFontColor = nColor; + p->oTags.push_back(OutTag(TAG_FONT_COLOR, m_nFontColor)); + p->PutTag(TAG_FONT_COLOR); +} + +void Level::setFontBgColor(unsigned short nColor) +{ + if (m_nFontBgColor == nColor) return; + if (m_nFontBgColor != 0) resetTag(TAG_BG_COLOR); + if (nColor > p->colors.size()) return; + m_nFontBgColor = nColor; + p->oTags.push_back(OutTag(TAG_BG_COLOR, m_nFontBgColor)); + p->PutTag(TAG_BG_COLOR); +} + +void Level::setFontSizeHalfPoints(unsigned short nSize) +{ + setFontSize(nSize / 2); +} + +void Level::setFontSize(unsigned short nSize) +{ + if (m_nFontSize == nSize) return; + if (m_nFontSize) resetTag(TAG_FONT_SIZE); + p->oTags.push_back(OutTag(TAG_FONT_SIZE, nSize)); + p->PutTag(TAG_FONT_SIZE); + m_nFontSize = nSize; +} + +void Level::startParagraph() +{ + // Whatever tags we have open now, close them. + // We cannot carry let character formatting tags wrap paragraphs, + // since a formatting tag can close at any time and we cannot + // close the paragraph any time we want. + resetTag(TAG_ALL); + + // Flush the current paragraph HTML to the document HTML. + p->FlushParagraph(); + + // Mark this new paragraph as an explicit one (from \par etc.). + p->bExplicitParagraph = true; + + // Restore character formatting + p->oTags.push_back(OutTag(TAG_FONT_SIZE, m_nFontSize)); + p->PutTag(TAG_FONT_SIZE); + p->oTags.push_back(OutTag(TAG_FONT_COLOR, m_nFontColor)); + p->PutTag(TAG_FONT_COLOR); + p->oTags.push_back(OutTag(TAG_FONT_FAMILY, m_nFont)); + p->PutTag(TAG_FONT_FAMILY); + if (m_nFontBgColor != 0) + { + p->oTags.push_back(OutTag(TAG_BG_COLOR, m_nFontBgColor)); + p->PutTag(TAG_BG_COLOR); + } + if (m_bBold) + { + p->oTags.push_back(OutTag(TAG_BOLD, 0)); + p->PutTag(TAG_BOLD); + } + if (m_bItalic) + { + p->PutTag(TAG_ITALIC); + p->oTags.push_back(OutTag(TAG_ITALIC, 0)); + } + if (m_bUnderline) + { + p->oTags.push_back(OutTag(TAG_UNDERLINE, 0)); + p->PutTag(TAG_UNDERLINE); + } +} + +bool Level::isParagraphOpen() const +{ + return p->bExplicitParagraph; +} + +void Level::clearParagraphFormatting() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + // Since we don't implement any of the paragraph formatting tags (e.g. alignment), + // we don't clean up anything here. Note that \pard does NOT clean character + // formatting (such as font size, font weight, italics...). + p->parStyle.clearFormatting(); +} + +void Level::setParagraphDirLTR() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + p->parStyle.dir = ParStyle::DirLTR; +} + +void Level::setParagraphDirRTL() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + p->parStyle.dir = ParStyle::DirRTL; +} + +void Level::addLineBreak() +{ + p->PrintUnquoted("<br/>"); +} + +void Level::reset() +{ + resetTag(TAG_ALL); + if (m_bColors){ + if (m_bColorInit){ + QColor c(m_nRed, m_nGreen, m_nBlue); + p->colors.push_back(c); + resetColors(); + } + return; + } +} + +void Level::setText(const char *str) +{ + if (m_bColors) + { + reset(); + } + else if (m_bFontTbl) + { + if ((m_nFont <= 0) || (m_nFont > p->fonts.size())) + return; + + FontDef& def = p->fonts[m_nFont-1]; + + char *pp = strchr(str, ';'); + unsigned size; + if (pp != NULL) + size = (pp - str); + else + size = strlen(str); + + if (m_bFontName) + { + def.nonTaggedName.append(str, size); + // We know we have the entire name + if (pp != NULL) + m_bFontName = false; + } + else if (!m_bTaggedFontNameOk) + { + def.taggedName.append(str, size); + if (pp != NULL) + m_bTaggedFontNameOk = true; + } + } + else + { + for (; *str; str++) + if ((unsigned char)(*str) >= ' ') break; + if (!*str) return; + p->FlushOutTags(); + text += str; + } +} + +void Level::flush() +{ + if (text.length() == 0) return; + // TODO: Make encoding work in Kopete + /* + const char *encoding = NULL; + if (m_nEncoding){ + for (const ENCODING *c = ICQPlugin::core->encodings; c->language; c++){ + if (!c->bMain) + continue; + if ((unsigned)c->rtf_code == m_nEncoding){ + encoding = c->codec; + break; + } + } + } + if (encoding == NULL) + encoding = p->encoding; + + QTextCodec *codec = ICQClient::_getCodec(encoding); + */ + //p->PrintQuoted(codec->toUnicode(text.c_str(), text.length())); + p->PrintQuoted(text.c_str()); + text = ""; +} + +const unsigned FONTTBL = 0; +const unsigned COLORTBL = 1; +const unsigned RED = 2; +const unsigned GREEN = 3; +const unsigned BLUE = 4; +const unsigned CF = 5; +const unsigned FS = 6; +const unsigned HIGHLIGHT = 7; +const unsigned PARD = 8; +const unsigned PAR = 9; +const unsigned I = 10; +const unsigned B = 11; +const unsigned UL = 12; +const unsigned F = 13; +const unsigned FCHARSET = 14; +const unsigned FNAME = 15; +const unsigned ULNONE = 16; +const unsigned LTRPAR = 17; +const unsigned RTLPAR = 18; +const unsigned LINE = 19; + +static char cmds[] = + "fonttbl\x00" + "colortbl\x00" + "red\x00" + "green\x00" + "blue\x00" + "cf\x00" + "fs\x00" + "highlight\x00" + "pard\x00" + "par\x00" + "i\x00" + "b\x00" + "ul\x00" + "f\x00" + "fcharset\x00" + "fname\x00" + "ulnone\x00" + "ltrpar\x00" + "rtlpar\x00" + "line\x00" + "\x00"; + +int yywrap() { return 1; } + +static char h2d(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return (c - 'A') + 10; + if ((c >= 'a') && (c <= 'f')) + return (c - 'a') + 10; + return 0; +} + +QString RTF2HTML::Parse(const char *rtf, const char *_encoding) +{ + encoding = _encoding; + YY_BUFFER_STATE yy_current_buffer = yy_scan_string(rtf); + rtf_ptr = rtf; + for (;;){ + int res = yylex(); + if (!res) break; + switch (res){ + case UP:{ + cur_level.flush(); + levels.push(cur_level); + break; + } + case DOWN:{ + if (!levels.empty()){ + cur_level.flush(); + cur_level.reset(); + cur_level = levels.top(); + levels.pop(); + } + break; + } + case IMG:{ + cur_level.flush(); + const char ICQIMAGE[] = "icqimage"; + const char *smiles[] = { ":-)" , ":-O" , ":-|" , ":-/" , // 0-3 + ":-(" , ":-*" , ":-/" , ":'(" , // 4-7 + ";-)" , ":-@" , ":-$" , ":-X" , // 8-B + ":-P" , "8-)" , "O:)" , ":-D" }; // C-F + const char *p = yytext + 3; + if ((strlen(p) > strlen(ICQIMAGE)) && !memcmp(p, ICQIMAGE, strlen(ICQIMAGE))){ + unsigned n = 0; + for (p += strlen(ICQIMAGE); *p; p++){ + if ((*p >= '0') && (*p <= '9')){ + n = n << 4; + n += (*p - '0'); + continue; + } + if ((*p >= 'A') && (*p <= 'F')){ + n = n << 4; + n += (*p - 'A') + 10; + continue; + } + if ((*p >= 'a') && (*p <= 'f')){ + n = n << 4; + n += (*p - 'a') + 10; + continue; + } + break; + } + if (n < 16) + PrintUnquoted(" %s ", smiles[n] ); + }else{ + kdDebug(14200) << "Unknown image " << yytext << endl; + } + break; + } + case SKIP: + break; + case SLASH: + cur_level.setText(yytext+1); + break; + case TXT: + cur_level.setText(yytext); + break; + case UNICODE_CHAR:{ + cur_level.flush(); + sParagraph += QChar((unsigned short)(atol(yytext + 2))); + break; + } + case HEX:{ + char s[2]; + s[0] = (h2d(yytext[2]) << 4) + h2d(yytext[3]); + s[1] = 0; + cur_level.setText(s); + break; + } + case CMD: + { + cur_level.flush(); + const char *cmd = yytext + 1; + unsigned n_cmd = 0; + unsigned cmd_size = 0; + int cmd_value = -1; + const char *p; + for (p = cmd; *p; p++, cmd_size++) + if (((*p >= '0') && (*p <= '9')) || (*p == ' ')) break; + if (*p && (*p != ' ')) cmd_value = atol(p); + for (p = cmds; *p; p += strlen(p) + 1, n_cmd++){ + if (strlen(p) > cmd_size) continue; + if (!memcmp(p, cmd, cmd_size)) break; + } + cmd += strlen(p); + switch (n_cmd){ + case FONTTBL: // fonttbl + cur_level.setFontTbl(); + break; + case COLORTBL: + cur_level.setColors(); + break; + case RED: + cur_level.setRed(cmd_value); + break; + case GREEN: + cur_level.setGreen(cmd_value); + break; + case BLUE: + cur_level.setBlue(cmd_value); + break; + case CF: + cur_level.setFontColor(cmd_value); + break; + case FS: + cur_level.setFontSizeHalfPoints(cmd_value); + break; + case HIGHLIGHT: + cur_level.setFontBgColor(cmd_value); + break; + case PARD: + cur_level.clearParagraphFormatting(); + break; + case PAR: + cur_level.startParagraph(); + break; + case I: + cur_level.setItalic(cmd_value != 0); + break; + case B: + cur_level.setBold(cmd_value != 0); + break; + case UL: + cur_level.setUnderline(cmd_value != 0); + break; + case ULNONE: + cur_level.setUnderline(false); + break; + case F: + // RTF fonts are 0-based; our font index is 1-based. + cur_level.setFont(cmd_value+1); + break; + case FCHARSET: + cur_level.setEncoding(cmd_value); + break; + case FNAME: + cur_level.setFontName(); + break; + case LTRPAR: + cur_level.setParagraphDirLTR(); + break; + case RTLPAR: + cur_level.setParagraphDirRTL(); + break; + case LINE: + cur_level.addLineBreak(); + } + break; + } + } + } + yy_delete_buffer(yy_current_buffer); + yy_current_buffer = NULL; + FlushParagraph(); + return s; +} + +/* +bool ICQClient::parseRTF(const char *rtf, const char *encoding, QString &res) +{ + char _RTF[] = "{\\rtf"; + if ((strlen(rtf) > strlen(_RTF)) && !memcmp(rtf, _RTF, strlen(_RTF))){ + RTF2HTML p; + res = p.Parse(rtf, encoding); + return true; + } + QTextCodec *codec = ICQClient::_getCodec(encoding); + res = codec->toUnicode(rtf, strlen(rtf)); + return false; +} +*/ diff --git a/kopete/protocols/oscar/liboscar/rtf2html.h b/kopete/protocols/oscar/liboscar/rtf2html.h new file mode 100644 index 00000000..a305b89d --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rtf2html.h @@ -0,0 +1,207 @@ +/* + rtf2html.h - A simple RTF Parser + + Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code) + Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port) + Kopete (c) 2002-2003 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. * + * * + ************************************************************************* +*/ + +#ifndef RTF2HTML_H +#define RTF2HTML_H + +#include <qstring.h> +#include <stdio.h> + +#include <qtextcodec.h> +#include <qcolor.h> +#include <qregexp.h> +#include <kdebug.h> + +#include <vector> +#include <stack> +#include <string> +#include <stdarg.h> + +using namespace std; + +struct FontDef +{ + int charset; + string taggedName; + string nonTaggedName; +}; + +class RTF2HTML; + +enum TagEnum +{ + TAG_ALL = 0, + TAG_FONT_SIZE, + TAG_FONT_COLOR, + TAG_FONT_FAMILY, + TAG_BG_COLOR, + TAG_BOLD, + TAG_ITALIC, + TAG_UNDERLINE +}; + +class ParStyle +{ +public: + ParStyle() { dir = DirLTR; } + void clearFormatting(); + +public: + enum {DirLTR, DirRTL} dir; +}; + +class Level +{ +public: + Level(RTF2HTML *_p); + Level(const Level&); + void setText(const char* str); + void setFontTbl() { m_bFontTbl = true; } + void setColors() { m_bColors = true; resetColors(); } + void setRed(unsigned char val) { setColor(val, &m_nRed); } + void setGreen(unsigned char val) { setColor(val, &m_nGreen); } + void setBlue(unsigned char val) { setColor(val, &m_nBlue); } + void setFont(unsigned nFont); + void setEncoding(unsigned nFont); + void setFontName(); + void setFontColor(unsigned short color); + void setFontBgColor(unsigned short color); + void setFontSizeHalfPoints(unsigned short sizeInHalfPoints); + void setFontSize(unsigned short sizeInPoints); + void setBold(bool); + void setItalic(bool); + void setUnderline(bool); + void startParagraph(); + bool isParagraphOpen() const; + void clearParagraphFormatting(); + void setParagraphDirLTR(); + void setParagraphDirRTL(); + void addLineBreak(); + void flush(); + void reset(); + void resetTag(TagEnum tag); +protected: + string text; + void Init(); + RTF2HTML *p; + void resetColors() { m_nRed = m_nGreen = m_nBlue = 0; m_bColorInit = false; } + void setColor(unsigned char val, unsigned char *p) + { *p = val; m_bColorInit=true; } + + // Marks the position in m_tags where this level begun. + unsigned m_nTagsStartPos; + + // True when parsing the fonts table + bool m_bFontTbl; + // True when parsing the colors table. + bool m_bColors; + // True when inside a 'fname' block. + bool m_bFontName; + // False until we get the tagged font name. + bool m_bTaggedFontNameOk; + + unsigned char m_nRed; + unsigned char m_nGreen; + unsigned char m_nBlue; + bool m_bColorInit; + unsigned m_nFont; // 1-based + unsigned m_nEncoding; + unsigned m_nFontColor; // 1-based + unsigned m_nFontSize; + unsigned m_nFontBgColor; // 1-based + bool m_bBold; + bool m_bItalic; + bool m_bUnderline; +}; + +class OutTag +{ +public: + OutTag(TagEnum _tag, unsigned _param) : tag(_tag), param(_param) {} + TagEnum tag; + unsigned param; +}; + +enum quoteMode +{ + quoteHTML, + quoteXML, + quoteNOBR +}; + +class RTF2HTML +{ + friend class Level; + +public: + RTF2HTML(); + QString Parse(const char *rtf, const char *encoding); + + // Paragraph-specific functions: + + QString quoteString(const QString &_str, quoteMode mode = quoteHTML); + // Appends a string with formatting into the paragraph buffer. + void PrintUnquoted(const char *str, ...); + // Quotes and appends a string to the paragraph buffer. + void PrintQuoted(const QString &str); + // Writes down the tags from oTags into the paragraph buffer. + void FlushOutTags(); + // Retrieves the top not-yet-written tag. + OutTag* getTopOutTag(TagEnum tagType); + // Writes down the paragraph buffer and resets the paragraph state. + void FlushParagraph(); + + // Document-wide functions: + + void PutTag(TagEnum n) + { + tags.push(n); + } + +protected: + +// Paragraph members + + // True if the paragraph was opened explicitly. + bool bExplicitParagraph; + // The paragraph's HTML buffer. + QString sParagraph; + // Defines the paragraph's formatting. + ParStyle parStyle; + // Tags which weren't yet printed out. + vector<OutTag> oTags; + +// Document members + + // The document HTML buffer. + QString s; + // Fonts table. + vector<FontDef> fonts; + // Colors table. + vector<QColor> colors; + // Stack of tags (across all levels, not just current level) + stack<TagEnum> tags; + +// RTF parser internals + + const char *rtf_ptr; + const char *encoding; + Level cur_level; + stack<Level> levels; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/safedelete.cpp b/kopete/protocols/oscar/liboscar/safedelete.cpp new file mode 100644 index 00000000..703e8ed3 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/safedelete.cpp @@ -0,0 +1,139 @@ +/* + safedelete.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + 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 "safedelete.h" + +#include <qtimer.h> + +//---------------------------------------------------------------------------- +// SafeDelete +//---------------------------------------------------------------------------- +SafeDelete::SafeDelete() +{ + lock = 0; +} + +SafeDelete::~SafeDelete() +{ + if(lock) + lock->dying(); +} + +void SafeDelete::deleteLater(QObject *o) +{ + if(!lock) + deleteSingle(o); + else + list.append(o); +} + +void SafeDelete::unlock() +{ + lock = 0; + deleteAll(); +} + +void SafeDelete::deleteAll() +{ + if(list.isEmpty()) + return; + + QObjectListIt it(list); + for(QObject *o; (o = it.current()); ++it) + deleteSingle(o); + list.clear(); +} + +void SafeDelete::deleteSingle(QObject *o) +{ +#if QT_VERSION < 0x030000 + // roll our own QObject::deleteLater() + SafeDeleteLater *sdl = SafeDeleteLater::ensureExists(); + sdl->deleteItLater(o); +#else + o->deleteLater(); +#endif +} + +//---------------------------------------------------------------------------- +// SafeDeleteLock +//---------------------------------------------------------------------------- +SafeDeleteLock::SafeDeleteLock(SafeDelete *sd) +{ + own = false; + if(!sd->lock) { + _sd = sd; + _sd->lock = this; + } + else + _sd = 0; +} + +SafeDeleteLock::~SafeDeleteLock() +{ + if(_sd) { + _sd->unlock(); + if(own) + delete _sd; + } +} + +void SafeDeleteLock::dying() +{ + _sd = new SafeDelete(*_sd); + own = true; +} + +//---------------------------------------------------------------------------- +// SafeDeleteLater +//---------------------------------------------------------------------------- +SafeDeleteLater *SafeDeleteLater::self = 0; + +SafeDeleteLater *SafeDeleteLater::ensureExists() +{ + if(!self) + new SafeDeleteLater(); + return self; +} + +SafeDeleteLater::SafeDeleteLater() +{ + list.setAutoDelete(true); + self = this; + QTimer::singleShot(0, this, SLOT(explode())); +} + +SafeDeleteLater::~SafeDeleteLater() +{ + list.clear(); + self = 0; +} + +void SafeDeleteLater::deleteItLater(QObject *o) +{ + list.append(o); +} + +void SafeDeleteLater::explode() +{ + delete this; +} + +#include "safedelete.moc" + diff --git a/kopete/protocols/oscar/liboscar/safedelete.h b/kopete/protocols/oscar/liboscar/safedelete.h new file mode 100644 index 00000000..e8215c06 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/safedelete.h @@ -0,0 +1,79 @@ +/* + gwclientstream.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + 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. * + * * + ************************************************************************* +*/ + +#ifndef SAFEDELETE_H +#define SAFEDELETE_H + +#include<qobject.h> +#include<qobjectlist.h> + +class SafeDelete; +class SafeDeleteLock +{ +public: + SafeDeleteLock(SafeDelete *sd); + ~SafeDeleteLock(); + +private: + SafeDelete *_sd; + bool own; + friend class SafeDelete; + void dying(); +}; + +class SafeDelete +{ +public: + SafeDelete(); + ~SafeDelete(); + + void deleteLater(QObject *o); + + // same as QObject::deleteLater() + static void deleteSingle(QObject *o); + +private: + QObjectList list; + void deleteAll(); + + friend class SafeDeleteLock; + SafeDeleteLock *lock; + void unlock(); +}; + +class SafeDeleteLater : public QObject +{ + Q_OBJECT +public: + static SafeDeleteLater *ensureExists(); + void deleteItLater(QObject *o); + +private slots: + void explode(); + +private: + SafeDeleteLater(); + ~SafeDeleteLater(); + + QObjectList list; + friend class SafeDelete; + static SafeDeleteLater *self; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/senddcinfotask.cpp b/kopete/protocols/oscar/liboscar/senddcinfotask.cpp new file mode 100644 index 00000000..af80e191 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/senddcinfotask.cpp @@ -0,0 +1,107 @@ +/* + Kopete Oscar Protocol + senddcinfotask.cpp - Send the DC info to the server + + Copyright (c) 2004 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 "senddcinfotask.h" + +#include <kdebug.h> +#include "connection.h" +#include "buffer.h" +#include "oscarsettings.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" + +SendDCInfoTask::SendDCInfoTask(Task* parent, DWORD status): Task(parent), mStatus(status) +{ +} + + +SendDCInfoTask::~SendDCInfoTask() +{ +} + + +void SendDCInfoTask::onGo() +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x001E, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending DC Info" << endl; + + /** \TODO Support something more than online in the status flags + * \TODO Support something more than DC Disabled in the status flags + */ + /* + if (status & ICQ_STATUS_SET_INVIS) + sendChangeVisibility(0x03); + else + sendChangeVisibility(0x04); + */ + + /* This is TLV 0x06 */ + buffer->addWord( 0x0006 ); + buffer->addWord( 0x0004 ); + //### Don't hardcode this value + //Right now, it's always coded to not support DC + DWORD statusFlag = 0x01000000; + if ( client()->settings()->webAware() ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting web aware on" << endl; + statusFlag |= 0x00010000; + } + if ( client()->settings()->hideIP() ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting hide ip on" << endl; + statusFlag |= 0x10000000; // Direct connection upon authorization, hides IP + } + + buffer->addDWord( statusFlag | mStatus ); + + /* Fill in the DC Info + * We don't support Direct Connection yet. So fill in some + * dummy values + */ + buffer->addWord( 0x000C ); //TLV Type 0x0C + buffer->addWord( 0x0025 ); + + buffer->addDWord( 0x00000000 ); + buffer->addWord( 0x0000 ); + buffer->addWord( 0x0000 ); + + buffer->addByte( 0x00 ); // Mode, TODO: currently fixed to "Direct Connection disabled" + buffer->addWord( ICQ_TCP_VERSION ); // icq tcp protocol version, v8 currently + + buffer->addDWord( 0x00000000 ); // Direct Connection Cookie + buffer->addDWord( 0x00000050 ); // web front port + buffer->addDWord( 0x00000003 ); // number of following client features + buffer->addDWord( 0x00000000 ); // InfoUpdateTime + buffer->addDWord( 0x00000000 ); // PhoneStatusTime + buffer->addDWord( 0x00000000 ); // PhoneBookTime + buffer->addWord( 0x0000 ); + + buffer->addWord( 0x0008 ); // TLV(8) + buffer->addWord( 0x0002 ); // length 2 + buffer->addWord( 0x0000 ); // error code - 0 + + Transfer* t = createTransfer( f, s, buffer ); + send( t ); + setSuccess( 0, QString::null ); +} + + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/senddcinfotask.h b/kopete/protocols/oscar/liboscar/senddcinfotask.h new file mode 100644 index 00000000..d130cc40 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/senddcinfotask.h @@ -0,0 +1,41 @@ +/* + Kopete Oscar Protocol + $FILENAME.h + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ +#ifndef SENDDCINFOTASK_H +#define SENDDCINFOTASK_H + +#include <task.h> + +/** +Fire and forget task that sends our direct connection info + +@author Matt Rogers +*/ +class SendDCInfoTask : public Task +{ +public: + SendDCInfoTask( Task* parent, DWORD status ); + ~SendDCInfoTask(); + virtual void onGo(); + +private: + DWORD mStatus; +}; + +#endif + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/sendidletimetask.cpp b/kopete/protocols/oscar/liboscar/sendidletimetask.cpp new file mode 100644 index 00000000..f0601e22 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/sendidletimetask.cpp @@ -0,0 +1,57 @@ +/* + Kopete Oscar Protocol + sendidletimetask.cpp - Sends the idle time to the server + + Copyright (c) 2004 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 "sendidletimetask.h" + +#include <kdebug.h> +#include "connection.h" +#include "buffer.h" +#include "oscarutils.h" +#include "transfer.h" + +SendIdleTimeTask::SendIdleTimeTask( Task* parent ) : Task( parent ) +{ + m_idleTime = 0; +} + + +SendIdleTimeTask::~SendIdleTimeTask() +{ + +} + + +void SendIdleTimeTask::onGo() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending idle time of " << m_idleTime << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x0011, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + buffer->addDWord( m_idleTime ); + + Transfer *t = createTransfer( f, s, buffer ); + send( t ); + setSuccess( 0, QString::null ); + +} + +void SendIdleTimeTask::setIdleTime( DWORD newTime ) +{ + m_idleTime = newTime; +} + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/sendidletimetask.h b/kopete/protocols/oscar/liboscar/sendidletimetask.h new file mode 100644 index 00000000..beba74c2 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/sendidletimetask.h @@ -0,0 +1,46 @@ +/* + Kopete Oscar Protocol + sendidletimetask.h - Send the idle time to the server + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ +#ifndef SENDIDLETIMETASK_H +#define SENDIDLETIMETASK_H + +#include "task.h" +#include "oscartypes.h" + +/** +Sends the idle time to the server + +@author Matt Rogers +*/ +class SendIdleTimeTask : public Task +{ +public: + SendIdleTimeTask( Task* parent ); + ~SendIdleTimeTask(); + virtual void onGo(); + + //! Set the idle time to send + void setIdleTime( DWORD ); + +private: + + DWORD m_idleTime; +}; + +#endif + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/sendmessagetask.cpp b/kopete/protocols/oscar/liboscar/sendmessagetask.cpp new file mode 100644 index 00000000..48509595 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/sendmessagetask.cpp @@ -0,0 +1,447 @@ +/* + sendmessagetask.h - Outgoing 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 "sendmessagetask.h" + +#include <kapplication.h> +#include <kdebug.h> +#include "connection.h" +#include "oscartypes.h" +#include "transfer.h" + + +SendMessageTask::SendMessageTask(Task* parent): Task(parent) +{ + m_autoResponse = false; + m_cookieCount = 0x7FFF; +} + + +SendMessageTask::~SendMessageTask() +{ +} + +void SendMessageTask::setMessage( const Oscar::Message& msg ) +{ + m_message = msg; +} + +void SendMessageTask::setAutoResponse( bool autoResponse ) +{ + m_autoResponse = autoResponse; +} + +void SendMessageTask::onGo() +{ + if ( m_message.textArray().isEmpty() && m_message.type() == 1 ) // at least channel 2 needs to send empty messages + { + setError(-1, "No message to send"); + return; + } + + // Check Message to see what SNAC to use + int snacSubfamily = 0x0006; + if ( ( m_message.type() == 2 ) && m_message.hasProperty( Oscar::Message::AutoResponse ) ) + { // an auto response is send for ack of channel 2 messages + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending SNAC 0x0B instead of 0x06 " << endl; + snacSubfamily = 0x000B; + } + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0004, snacSubfamily, 0x0000, client()->snacSequence() }; + Buffer* b = new Buffer(); + + if ( snacSubfamily == 0x0006 ) + { + DWORD cookie1 = KApplication::random(); + DWORD cookie2 = KApplication::random(); + + b->addDWord( cookie1 ); + b->addDWord( cookie2 ); + } + else + { + b->addString( m_message.icbmCookie() ); // in automated response, we need the same cookie as in the request + } + + b->addWord( m_message.type() ); + + b->addByte( m_message.receiver().length() ); + b->addString( m_message.receiver().latin1(), m_message.receiver().length() ); + + + if ( snacSubfamily == 0x0006 ) + { + /* send a regular message */ + switch ( m_message.type() ) + { + case 1: + addChannel1Data( b ); + break; + case 2: + addChannel2Data( b ); + break; + case 4: + addChannel4Data( b ); + break; + } + + // Add the TLV to indicate if this is an autoresponse: 0x00040000 + // Right now, only supported for the AIM client, I'm not sure about ICQ + // For some reason you can't have both a 0x0004 and 0x0003 TLV in the same + // SNAC, if you do the AIM server complains + if ( !client()->isIcq() && (m_autoResponse == true) ) + { + TLV tlv4( 0x0004, 0, NULL); + b->addTLV( tlv4 ); + } + else + { + b->addDWord( 0x00030000 ); //empty TLV 3 to get an ack from the server + } + + if ( client()->isIcq() && m_message.type() != 2 && ! m_message.hasProperty( Oscar::Message::StatusMessageRequest ) ) + b->addDWord( 0x00060000 ); //empty TLV 6 to store message on the server if not online + } + else + { + /* send an autoresponse */ + b->addWord( 0x0003 ); // reason code: 1: channel not supported; 2: busted payload; 3: channel specific; + //TODO: i hardcoded it for now, since we don't suppoert error messages atm anyway + addRendezvousMessageData( b ); + } + + Transfer* t = createTransfer( f, s, b ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SENDING: " << t->toString() << endl; + send( t ); + + setSuccess(true); +} + + +void SendMessageTask::addBasicTLVs( Buffer* b ) +{ + //TODO add stuff like user class, user status, online time, etc TLVS +} + + +void SendMessageTask::addChannel1Data( Buffer* b ) +{ + Buffer tlv2buffer; + + //Send features TLV using data from gaim. Features are different + //depending on whether we're ICQ or AIM + if ( client()->isIcq() ) + { + tlv2buffer.addDWord( 0x05010002 ); //TLV 0x0501, length 2 + tlv2buffer.addWord( 0x0106 ); //TLV 0x0501 data + } + else + { + tlv2buffer.addDWord( 0x05010004 ); //TLV 0x0501, length 4 + tlv2buffer.addDWord( 0x01010102 ); //TLV 0x0501 data. + } + //we only send one message part. There's only one client that actually uses + //them and it's quite old and infrequently used + tlv2buffer.addWord( 0x0101 ); //add TLV(0x0101) also known as TLV(257) + tlv2buffer.addWord( m_message.textArray().size() + 4 ); // add TLV length + + if ( m_message.encoding() == Oscar::Message::UserDefined ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending outgoing message in " + << "per-contact encoding" << endl; + tlv2buffer.addWord( 0x0000 ); + tlv2buffer.addWord( 0x0000 ); + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending outgoing message as " + << "UCS-2" << endl; + tlv2buffer.addWord( 0x0002 ); + tlv2buffer.addWord( 0x0000 ); + } + tlv2buffer.addString( m_message.textArray() ); + + TLV tlv2( 0x0002, tlv2buffer.length(), tlv2buffer.buffer() ); + b->addTLV( tlv2 ); +} + +void SendMessageTask::addChannel2Data( Buffer* b ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Trying to send type 2 message!" << endl; + + Buffer tlv5buffer; + + tlv5buffer.addWord( 0 ); // 0 = request; other possibilities: 1 = cancel; 2 = accept; + //TODO: i hardcoded it for now, don't yet what to use the other stuff for + + // message id cookie. needs to be the same one as above, thus copy first eight bytes of buffer + Buffer* tmp = new Buffer(b->buffer(), 8); + tlv5buffer.addString( tmp->buffer(), 8 ); + delete tmp; + + /* send our client capability. oscardocs say this one means we support type 2 messages, + ethereal say it means we support server relay. however, it's what most clients send, + even official ones... + */ + + // too lazy to think about byte order :) + tlv5buffer.addByte( 0x09 ); + tlv5buffer.addByte( 0x46 ); + tlv5buffer.addByte( 0x13 ); + tlv5buffer.addByte( 0x49 ); + tlv5buffer.addByte( 0x4C ); + tlv5buffer.addByte( 0x7F ); + tlv5buffer.addByte( 0x11 ); + tlv5buffer.addByte( 0xD1 ); + tlv5buffer.addByte( 0x82 ); + tlv5buffer.addByte( 0x22 ); + tlv5buffer.addByte( 0x44 ); + tlv5buffer.addByte( 0x45 ); + tlv5buffer.addByte( 0x53 ); + tlv5buffer.addByte( 0x54 ); + tlv5buffer.addByte( 0x00 ); + tlv5buffer.addByte( 0x00 ); + + // These are optional, would probably be a god ide to start using them, though + + // add TLV 03: internal ip +// tlv5buffer.addWord( 0x0003 ); // TLV Type +// tlv5buffer.addWord( 0x0004 ); // TLV Length +// tlv5buffer.addDWord( 0x00000000 ); // TLV Data: Internal IP + + // add TLV 05: listening port +// tlv5buffer.addWord( 0x0005 ); // TLV Type +// tlv5buffer.addWord( 0x0002 ); // TLV Length +// tlv5buffer.addWord( 0x0000 ); // TLV Data: listening port + + // add TLV 0A: acktype (1 = normal message) + tlv5buffer.addWord( 0x000A ); // TLV Type + tlv5buffer.addWord( 0x0002 ); // TLV Length + tlv5buffer.addWord( 0x0001 ); // TLV Data: unknown, usually 1 + + // add TLV 0B: unknown +// tlv5buffer.addWord( 0x000B ); // TLV Type +// tlv5buffer.addWord( 0x0002 ); // TLV Length +// tlv5buffer.addWord( 0x0000 ); // TLV Data: unknown + + // add TLV 0F: unknown + tlv5buffer.addWord( 0x000F ); // TLV Type + tlv5buffer.addWord( 0x0000 ); // TLV Length + // TLV Data: empty + + + + /* now comes the important TLV 0x2711 */ + + Buffer tlv2711buffer; + addRendezvousMessageData( &tlv2711buffer ); + TLV tlv2711( 0x2711, tlv2711buffer.length(), tlv2711buffer.buffer() ); + tlv5buffer.addTLV( tlv2711 ); + + TLV tlv5( 0x0005, tlv5buffer.length(), tlv5buffer.buffer() ); + b->addTLV( tlv5 ); +} + +void SendMessageTask::addChannel4Data( Buffer* b ) +{ + //TODO +} + +void SendMessageTask::addRendezvousMessageData( Buffer* b ) +{ + // first data segment + b->addLEWord( 0x001B ); // length of this data segment, always 27 + + // protocol version + // miranda,licq use 8, gaim,icq5 use 9, icq2003b uses 10. + // 9 seems to make things a litle difficult, 10 seems a little more like 8, but still more difficult + b->addLEWord( 0x0008 ); // so stick with 8 for now :) + + for ( int i = 0; i < 16; i++) + { + b->addByte( 0x00 ); // pluginID or all zeros (see oscar docs) + } + + b->addWord( 0x0000 ); // unknown + b->addLEDWord( 0x00000003 ); // FIXME client capabilities: not sure, but should be ICQ Server Relay + b->addByte( 0x0000 ); // unknown + + // channel 2 counter: in auto response, use original message value. s/t else otherwise (most anythig will work) + int channel2Counter = 0xBEEF; // just some number for now + if ( m_message.hasProperty( Oscar::Message::AutoResponse ) ) + channel2Counter = m_message.channel2Counter(); + else + channel2Counter = (m_cookieCount--) & 0x7FFF; + + b->addLEWord( channel2Counter ); // channel 2 counter + + // second data segment + b->addLEWord( 0x000E ); // length of this data segment, always 14 + b->addLEWord( channel2Counter ); // channel 2 counter + + for ( int i = 0; i < 12; i++) + { + b->addByte( 0x00 ); // unknown, usually all zeros + } + + // actual message data segment + + // Message type + if ( m_message.messageType() == 0x00 ) + b->addByte( 0x01 ); + else + b->addByte( m_message.messageType() ); + + int messageFlags = 0x00; // Normal + if ( m_message.hasProperty( Oscar::Message::StatusMessageRequest ) ) + messageFlags = 0x03; // Auto message. required for both requesting and sending status messages + else if ( m_message.hasProperty( Oscar::Message::AutoResponse ) ) + messageFlags = 0x00; // A regular type 2 msg ack requires 0x00 here... + b->addByte( messageFlags ); + + // status code, priority: + // common (ICQ) practice seems to be: both 1 when requesting away message, both 0 otherwise + // miranda sends 256/0 in away message request. it works, but i don't see the point... + // other then that, don't yet really know what they are for. + if ( m_message.hasProperty( Oscar::Message::StatusMessageRequest ) && ( ! m_message.hasProperty( Oscar::Message::AutoResponse ) ) ) + { + b->addLEWord( 0x0001 ); // status (?) + b->addLEWord( 0x0001 ); // priority (?) + } + else + { + b->addLEWord( 0x0000 ); // status (?) + b->addLEWord( 0x0000 ); // priority (?) + } + + + b->addLEWord( m_message.textArray().size() + 1 ); // length of string + zero termination + b->addString( m_message.textArray() ); // string itself + b->addByte( 0x00 ); // zero termination + b->addLEDWord( 0x00000000 ); // foreground + b->addLEDWord( 0x00FFFFFF ); // foreground + + if ( m_message.encoding() == Oscar::Message::UTF8 ) + { + b->addLEDWord( 38 ); + b->addString( "{0946134E-4C7F-11D1-8222-444553540000}", 38 ); + } +} + + + +/* Old oscarsocket code, which is here for reference in case this doesn't work +QTextCodec *codec = 0L; +WORD charset = 0x0000; // default to ascii +WORD charsubset = 0x0000; +int length = message.length(); +unsigned char *utfMessage = 0L; + +codec=QTextCodec::codecForMib(3); // US-ASCII + +if(codec) +{ + if(codec->canEncode(message)) // this returns true for some accented western european chars but kopete can't decode on receipt + { + //kdDebug(14151) << k_funcinfo << "Going to encode as US-ASCII" << endl; + // We are forcing kopete to send messages using ISO-8859-1 + // It's a hack and should be reimplemented in a better way + charset=0x0003; + codec=QTextCodec::codecForMib(4); + //kdDebug(14151) << k_funcinfo << "Now trying ISO-8859-1" << endl; + } + else + { + codec=0L; // we failed encoding it as US-ASCII + //kdDebug(14151) << k_funcinfo << "Cannot encode as US-ASCII" << endl; + } +} + +// if we couldn't encode it as ascii, and either the client says it can do UTF8, or we have no +// contact specific encoding set, might as well send it as UTF-16BE as as ISO-8859-1 +if ( !codec && ( contact->hasCap(CAP_UTF8) || !contact->encoding() ) ) +{ + // use UTF is peer supports it and encoding as US-ASCII failed + length=message.length()*2; + utfMessage=new unsigned char[length]; + for(unsigned int l=0; l<message.length(); l++) + { + utfMessage[l*2]=message.unicode()[l].row(); + utfMessage[(l*2)+1]=message.unicode()[l].cell(); + } + charset=0x0002; // send UTF-16BE +} + +// no codec and no charset and per-contact encoding set +if(!codec && charset != 0x0002 && contact->encoding() != 0) +{ + codec=QTextCodec::codecForMib(contact->encoding()); + if(codec) + charset=0x0003; //send as ISO-8859-1 +} + +if(!codec && charset != 0x0002) // it's neither unicode nor did we find a codec so far! +{ + kdDebug(14151) << k_funcinfo << + "Couldn't find suitable encoding for outgoing message, " << + "encoding using ISO-8859-1, prepare for receiver getting unreadable text :)" << endl; + charset=0x0003; + codec=QTextCodec::codecForMib(4); // ISO-8859-1 +} + +tlv2.addWord(0x0101); //add TLV(0x0101) also known as TLV(257) +tlv2.addWord(length + 0x04); // add TLV length +tlv2.addWord(charset); // normal char set +tlv2.addWord(charsubset); // normal char set + +if(utfMessage) +{ + kdDebug(14151) << k_funcinfo << "Outgoing message encoded as 'UTF-16BE'" << endl; + tlv2.addString(utfMessage, length); // the actual message + delete [] utfMessage; +} +else +{ + kdDebug(14151) << k_funcinfo << "Outgoing message encoded as '" << codec->name() << "'" << endl; + QCString outgoingMessage=codec->fromUnicode(message); + tlv2.addString(outgoingMessage, length); // the actual message +} +// ==================================================================================== + +outbuf.addTLV(0x0002, tlv2.length(), tlv2.buffer()); + +if(isAuto) // No clue about this stuff, probably AIM-specific [mETz] +{ + outbuf.addWord(0x0004); + outbuf.addWord(0x0000); +} + +if(mIsICQ) +{ + //outbuf.addWord(0x0003); // TLV.Type(0x03) - request an ack from server + //outbuf.addWord(0x0000); + + outbuf.addWord(0x0006); // TLV.Type(0x06) - store message if recipient offline + outbuf.addWord(0x0000); +} + +sendBuf(outbuf,0x02); +*/ + + + + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/sendmessagetask.h b/kopete/protocols/oscar/liboscar/sendmessagetask.h new file mode 100644 index 00000000..0eaff13f --- /dev/null +++ b/kopete/protocols/oscar/liboscar/sendmessagetask.h @@ -0,0 +1,55 @@ +/* + sendmessagetask.h - Outgoing 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. * + * * + ************************************************************************* +*/ + +#ifndef SENDMESSAGETASK_H +#define SENDMESSAGETASK_H + +#include "task.h" +#include <qstring.h> +#include "oscarmessage.h" +/** +@author Kopete Developers +*/ +class SendMessageTask : public Task +{ +public: + SendMessageTask( Task* parent ); + ~SendMessageTask(); + + //! Set the message to be sent + void setMessage( const Oscar::Message& msg ); + + //! Are we sending an auto response + void setAutoResponse( bool autoResponse ); + + virtual void onGo(); + +private: + void addBasicTLVs( Buffer* b ); + void addChannel1Data( Buffer* b ); + void addChannel2Data( Buffer* b ); + void addChannel4Data( Buffer* b ); + void addRendezvousMessageData( Buffer* b ); + +private: + Oscar::Message m_message; + bool m_autoResponse; + uint m_cookieCount; +}; + +#endif + +//kate: indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/serverredirecttask.cpp b/kopete/protocols/oscar/liboscar/serverredirecttask.cpp new file mode 100644 index 00000000..cccad909 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/serverredirecttask.cpp @@ -0,0 +1,173 @@ +// Kopete Oscar Protocol - Server redirections + +// 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., 59 Temple Place, Suite 330, Boston, MA +// 02111-1307 USA + +#include "serverredirecttask.h" + +#include <kdebug.h> + +#include "buffer.h" +#include "connection.h" +#include "transfer.h" + +ServerRedirectTask::ServerRedirectTask( Task* parent ) + :Task( parent ), m_service( 0 ) +{ + +} + +void ServerRedirectTask::setService( WORD family ) +{ + m_service = family; +} + +void ServerRedirectTask::setChatParams( WORD exchange, QByteArray cookie, WORD instance ) +{ + m_chatExchange = exchange; + m_chatCookie.duplicate( cookie ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "cookie is" << m_chatCookie << endl; + m_chatInstance = instance; +} + +void ServerRedirectTask::setChatRoom( const QString& roomName ) +{ + m_chatRoom = roomName; +} + + +void ServerRedirectTask::onGo() +{ + if ( m_service != 0 ) + requestNewService(); +} + +bool ServerRedirectTask::forMe( const Transfer* transfer ) +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 1 && st->snacSubtype() == 0x0005 ) + return true; + else + return false; +} + +bool ServerRedirectTask::take( Transfer* transfer ) +{ + if ( !forMe( transfer ) ) + return false; + + setTransfer( transfer ); + bool value = handleRedirect(); + setSuccess( 0, QString::null ); + setTransfer( 0 ); + return value; +} + + +void ServerRedirectTask::requestNewService() +{ + FLAP f = { 0x02, 0, 0x00 }; + SNAC s = { 0x0001, 0x0004, 0x0000, client()->snacSequence() }; + Buffer* b = new Buffer(); + b->addWord( m_service ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Requesting server for service " << m_service << endl; + if ( m_service == 0x000E ) + { + b->addWord( 0x0001 ); + b->addWord( m_chatCookie.size() + 5 ); + b->addWord( m_chatExchange ); + b->addByte( m_chatCookie.size() ); + b->addString( m_chatCookie ); + b->addWord( m_chatInstance ); + } + + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +bool ServerRedirectTask::handleRedirect() +{ + //TLVs 0x0D, 0x05, 0x06 + //family id + //server + //auth cookie + Buffer* b = transfer()->buffer(); + WORD typeD = b->getWord(); + WORD typeDLen = b->getWord(); + if ( typeD == 0x000D && typeDLen == 0x0002) + { + WORD realService = b->getWord(); + if ( realService != m_service ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "wrong service for this task" << endl; + kdDebug(OSCAR_RAW_DEBUG ) << k_funcinfo << "should be " << m_service << " is " + << realService << endl; + return false; + } + } + else + return false; + + TLV server = b->getTLV(); + m_newHost = QString( server.data ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Host for service " << m_service + << " is " << m_newHost << endl; + if ( m_newHost.isEmpty() ) + return false; + + TLV cookie = b->getTLV(); + + if ( cookie.length == 0 || cookie.data.isEmpty() ) + return false; + else + m_cookie = cookie.data; + + emit haveServer( m_newHost, m_cookie, m_service ); + return true; +} + +QByteArray ServerRedirectTask::cookie() const +{ + return m_cookie; +} + +QString ServerRedirectTask::newHost() const +{ + return m_newHost; +} + +WORD ServerRedirectTask::service() const +{ + return m_service; +} + +WORD ServerRedirectTask::chatExchange() const +{ + return m_chatExchange; +} + +QString ServerRedirectTask::chatRoomName() const +{ + return m_chatRoom; +} + +#include "serverredirecttask.moc" +//kate: indent-mode csands; tab-width 4; + diff --git a/kopete/protocols/oscar/liboscar/serverredirecttask.h b/kopete/protocols/oscar/liboscar/serverredirecttask.h new file mode 100644 index 00000000..19f14073 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/serverredirecttask.h @@ -0,0 +1,72 @@ +// Kopete Oscar Protocol - Server redirections + +// 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., 59 Temple Place, Suite 330, Boston, MA +// 02111-1307 USA + + +#ifndef SERVERREDIRECTTASK_H +#define SERVERREDIRECTTASK_H + +#include "task.h" + +#include <qcstring.h> + +#include "oscartypes.h" + +class Transfer; + +class ServerRedirectTask : public Task +{ +Q_OBJECT +public: + ServerRedirectTask( Task* parent ); + + void setService( WORD family ); + void setChatParams( WORD exchange, QByteArray cookie, WORD instance ); + void setChatRoom( const QString& roomName ); + + WORD chatExchange() const; + QString chatRoomName() const; + + //Task implementation + void onGo(); + bool forMe( const Transfer* transfer ); + bool take( Transfer* transfer ); + + void requestNewService(); + bool handleRedirect(); + + QByteArray cookie() const; + QString newHost() const; + WORD service() const; + +signals: + void haveServer( const QString&, const QByteArray&, WORD ); + +private: + WORD m_service; + QString m_newHost; + QByteArray m_cookie; + + WORD m_chatExchange; + QByteArray m_chatCookie; + WORD m_chatInstance; + QString m_chatRoom; +}; + + +#endif diff --git a/kopete/protocols/oscar/liboscar/serverversionstask.cpp b/kopete/protocols/oscar/liboscar/serverversionstask.cpp new file mode 100644 index 00000000..e4186f18 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/serverversionstask.cpp @@ -0,0 +1,169 @@ +/* + Kopete Oscar Protocol + serverversionstask.cpp - Handles the snac family versions + + Copyright (c) 2004 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 "serverversionstask.h" + +#include <kdebug.h> + +#include "connection.h" +#include "buffer.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" + + +using namespace Oscar; + +ServerVersionsTask::ServerVersionsTask( Task* parent ) + : Task( parent ) +{ + m_family = 0; +} + + +ServerVersionsTask::~ServerVersionsTask() +{ +} + + +bool ServerVersionsTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*> ( transfer ); + + if (!st) + return false; + + if ( st->snacService() == 1 ) + { + switch ( st->snacSubtype() ) + { + case 0x03: + case 0x17: + case 0x18: + return true; + break; + default: + return false; + } + } + return false; +} + +bool ServerVersionsTask::take( Transfer* transfer ) +{ + SnacTransfer* st = dynamic_cast<SnacTransfer*> ( transfer ); + if (!st) + return false; + + if ( forMe( transfer ) ) + { + switch ( st->snacSubtype() ) + { + case 0x03: + setTransfer( transfer ); + handleFamilies(); + setTransfer( 0 ); + return true; + break; + case 0x18: + setTransfer( transfer ); + handleServerVersions(); + setTransfer( 0 ); + return true; + break; + default: + return false; + } + } + return false; +} + +void ServerVersionsTask::handleFamilies() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo + << "RECV SNAC 0x01, 0x03 - got the list of families server supports" << endl; + + Buffer* outbuf = transfer()->buffer(); + if ( outbuf->length() % 2 != 0 ) + { + setError( -1, QString::null ); + return; + } + + while ( outbuf->length () != 0 ) + { + m_familiesList.append( outbuf->getWord() ); + } + client()->addToSupportedFamilies( m_familiesList ); + requestFamilyVersions(); // send back a CLI_FAMILIES packet +} + +void ServerVersionsTask::requestFamilyVersions() +{ + bool isIcq = client()->isIcq(); + int listLength = m_familiesList.count(); + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x0017, 0x0000, client()->snacSequence() }; + WORD val; + Buffer* outbuf = new Buffer(); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SEND SNAC 0x01, 0x17 - Snac family versions we want" << endl; + + for ( int i = 0; i < listLength; i++ ) + { + outbuf->addWord( m_familiesList[i] ); + if ( m_familiesList[i] == 0x0001 ) + val = 0x0003; + else + { + if ( m_familiesList[i] == 0x0013 ) + { + if ( isIcq ) + val = 0x0004; // for ICQ2002 + else + val = 0x0003; + } + else + val = 0x0001; + } + + outbuf->addWord(val); + } + + Transfer* st = createTransfer( f, s, outbuf ); + st->toString(); + send( st ); +} + +void ServerVersionsTask::handleServerVersions() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << + "RECV SNAC 0x01, 0x18, got list of families this server understands" << endl; + + Buffer* buffer = transfer()->buffer(); + int numFamilies = m_familiesList.count(); + for ( int srvFamCount = 0; srvFamCount < numFamilies; srvFamCount++ ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "server version=" << buffer->getWord() + << ", server family=" << buffer->getWord() << endl; + } + setSuccess( 0, QString::null ); +} + +#include "serverversionstask.moc" diff --git a/kopete/protocols/oscar/liboscar/serverversionstask.h b/kopete/protocols/oscar/liboscar/serverversionstask.h new file mode 100644 index 00000000..a9c56f35 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/serverversionstask.h @@ -0,0 +1,59 @@ +/* + Kopete Oscar Protocol + serverversionstask.h - Handles the snac family versions + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef SERVERVERSIONSTASK_H +#define SERVERVERSIONSTASK_H + +#include "task.h" +#include <qvaluelist.h> +#include "oscartypes.h" + +class Transfer; + +/** +@author Matt Rogers +*/ +class ServerVersionsTask : public Task +{ +Q_OBJECT +public: + ServerVersionsTask( Task* parent ); + + ~ServerVersionsTask(); + + bool forMe(const Transfer* transfer) const; + bool take(Transfer* transfer); + + +private: + //! Handles the families the server supports + void handleFamilies(); + + //! Handles the version of each family the server supports + void handleServerVersions(); + + //! Request the versions we want for each snac family the + //! the server supports + void requestFamilyVersions(); + +private: + QValueList<int> m_familiesList; + WORD m_family; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/servicesetuptask.cpp b/kopete/protocols/oscar/liboscar/servicesetuptask.cpp new file mode 100644 index 00000000..13e30101 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/servicesetuptask.cpp @@ -0,0 +1,135 @@ +/* + Kopete Oscar Protocol + servicesetuptask.cpp - Set up the services for the BOS connection + + Copyright (c) 2004 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 "servicesetuptask.h" + +#include <kdebug.h> +#include "blmlimitstask.h" +#include "connection.h" +#include "clientreadytask.h" +#include "icbmparamstask.h" +#include "locationrightstask.h" +#include "ownuserinfotask.h" +#include "prmparamstask.h" +#include "profiletask.h" +#include "senddcinfotask.h" +#include "sendidletimetask.h" +#include "ssiactivatetask.h" +#include "ssilisttask.h" +#include "ssimanager.h" +#include "ssiparamstask.h" +#include "transfer.h" + +ServiceSetupTask::ServiceSetupTask( Task* parent ) + : Task( parent ) +{ + m_finishedTaskCount = 0; + m_locRightsTask = new LocationRightsTask( parent ); + m_profileTask = new ProfileTask( parent ); + m_blmLimitsTask = new BLMLimitsTask( parent ); + m_icbmTask = new ICBMParamsTask( parent ); + m_prmTask = new PRMParamsTask( parent ); + m_ssiParamTask = new SSIParamsTask( parent ); + m_ssiListTask = new SSIListTask( parent ); + m_ssiActivateTask = new SSIActivateTask( parent ); + + QObject::connect( m_ssiListTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_ssiParamTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_prmTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_icbmTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_blmLimitsTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_profileTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_locRightsTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_ssiActivateTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); +} + + +ServiceSetupTask::~ServiceSetupTask() +{ + delete m_locRightsTask; + delete m_profileTask; + delete m_blmLimitsTask; + delete m_icbmTask; + //delete m_prmTask; + //delete m_ssiParamTask; + delete m_ssiListTask; +} + + +bool ServiceSetupTask::forMe( const Transfer* transfer ) const +{ + Q_UNUSED( transfer ); + return false; +} + +bool ServiceSetupTask::take( Transfer* transfer ) +{ + Q_UNUSED( transfer ); + return false; +} + +void ServiceSetupTask::childTaskFinished() +{ + m_finishedTaskCount++; + +// kdDebug( OSCAR_RAW_DEBUG ) << "Finished count is " << m_finishedTaskCount << endl; + + if ( m_finishedTaskCount == 7 ) + { + if ( client()->ssiManager()->listComplete() ) + m_ssiActivateTask->go( true ); + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending DC info and client ready" << endl; + SendIdleTimeTask* sitt = new SendIdleTimeTask( client()->rootTask() ); + QValueList<int> familyList; + familyList.append( 0x0001 ); + familyList.append( 0x0002 ); + familyList.append( 0x0003 ); + familyList.append( 0x0004 ); + familyList.append( 0x0006 ); + familyList.append( 0x0008 ); + familyList.append( 0x0009 ); + familyList.append( 0x000A ); + familyList.append( 0x0013 ); + ClientReadyTask* crt = new ClientReadyTask( client()->rootTask() ); + crt->setFamilies( familyList ); + sitt->go( true ); + crt->go( true ); //autodelete + } + + if ( m_finishedTaskCount == 8 ) + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Service setup finished" << endl; + setSuccess( 0, QString::null ); + } +} + + +void ServiceSetupTask::onGo() +{ + m_locRightsTask->go(); + m_profileTask->go(); + m_blmLimitsTask->go(); + m_icbmTask->go(); + m_prmTask->go( true ); + m_ssiParamTask->go( true ); + m_ssiListTask->go(); +} + +//kate: tab-width 4; indent-mode csands; + +#include "servicesetuptask.moc" diff --git a/kopete/protocols/oscar/liboscar/servicesetuptask.h b/kopete/protocols/oscar/liboscar/servicesetuptask.h new file mode 100644 index 00000000..c5bf5a70 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/servicesetuptask.h @@ -0,0 +1,69 @@ +/* + Kopete Oscar Protocol + servicesetuptask.h - Set up the services for the BOS connection + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ + +#ifndef SERVICESETUPTASK_H +#define SERVICESETUPTASK_H + +#include "task.h" + +class LocationRightsTask; +class ProfileTask; +class BLMLimitsTask; +class ICBMParamsTask; +class PRMParamsTask; +class SSIParamsTask; +class SSIListTask; +class SSIActivateTask; + + +/** +Set up the various services for the BOS connection +@author Matt Rogers +*/ +class ServiceSetupTask : public Task +{ +Q_OBJECT +public: + ServiceSetupTask( Task* parent ); + ~ServiceSetupTask(); + + bool forMe( const Transfer* transfer ) const; + bool take( Transfer* transfer ); + void onGo(); + +public slots: + void childTaskFinished(); + +private: + + /** Tracks how many tasks have finished */ + int m_finishedTaskCount; + + LocationRightsTask* m_locRightsTask; + ProfileTask* m_profileTask; + BLMLimitsTask* m_blmLimitsTask; + ICBMParamsTask* m_icbmTask; + PRMParamsTask* m_prmTask; + SSIParamsTask* m_ssiParamTask; + SSIListTask* m_ssiListTask; + SSIActivateTask* m_ssiActivateTask; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/snacprotocol.cpp b/kopete/protocols/oscar/liboscar/snacprotocol.cpp new file mode 100644 index 00000000..3bf16bbc --- /dev/null +++ b/kopete/protocols/oscar/liboscar/snacprotocol.cpp @@ -0,0 +1,109 @@ +/* + Kopete Oscar Protocol + snacprotocol.cpp - reads the protocol used by Oscar for signalling stuff + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com> + + 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 "snacprotocol.h" + +#include <qcstring.h> +#include <qdatastream.h> +#include <qobject.h> +#include <kdebug.h> +#include <stdlib.h> +#include "transfer.h" + + +using namespace Oscar; + +SnacProtocol::SnacProtocol(QObject *parent, const char *name) + : InputProtocolBase(parent, name) +{ +} + +SnacProtocol::~SnacProtocol() +{ +} + +Transfer* SnacProtocol::parse( const QByteArray & packet, uint& bytes ) +{ + BYTE b; + WORD w; + DWORD dw; + + FLAP f; + SNAC s; + SnacTransfer *st; + QDataStream* m_din = new QDataStream( packet, IO_ReadOnly ); + + //flap parsing + *m_din >> b; //this should be the start byte + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "start byte is " << b << endl; + *m_din >> b; + f.channel = b; + *m_din >> w; + f.sequence = w; + *m_din >> w; + f.length = w; + + if ( ( f.length + 6 ) > packet.size() ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Packet not big enough to parse!" << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "packet size is " << packet.size() + << " we need " << f.length + 6 << endl; + return 0; + } + //snac parsing + *m_din >> w; + s.family = w; + *m_din >> w; + s.subtype = w; + *m_din >> w; + s.flags = w; + *m_din >> dw; + s.id = dw; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "family: " << s.family + << " subtype: " << s.subtype << " flags: " << s.flags + << " id: " << s.id << endl; + + //use pointer arithmatic to skip the flap and snac headers + //so we don't have to do double parsing in the tasks + char* charPacket = packet.data(); + char* snacData; + int snacOffset = 10; //default + if ( s.flags >= 0x8000 ) //skip the next 8 bytes, we don't care about the snac version ATM + { + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "skipping snac version" << endl; + snacOffset = 18; + snacData = charPacket + 24; + } + else + { + snacOffset = 10; + snacData = charPacket + 16; + } + + Buffer *snacBuffer = new Buffer( snacData, f.length - snacOffset ); + st = new SnacTransfer( f, s, snacBuffer ); + bytes = f.length + 6; + delete m_din; + m_din = 0; + return st; +} + + +#include "snacprotocol.moc" diff --git a/kopete/protocols/oscar/liboscar/snacprotocol.h b/kopete/protocols/oscar/liboscar/snacprotocol.h new file mode 100644 index 00000000..eea5c032 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/snacprotocol.h @@ -0,0 +1,46 @@ +/* + Kopete Oscar Protocol + snacprotocol.h - reads the protocol used by Oscar for signalling stuff + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef OSCAR_SNACPROTOCOL_H +#define OSCAR_SNACPROTOCOL_H + +#include "inputprotocolbase.h" + +class SnacTransfer; + + +class SnacProtocol : public InputProtocolBase +{ +Q_OBJECT +public: + SnacProtocol( QObject *parent = 0, const char *name = 0 ); + ~SnacProtocol(); + + /** + * Attempt to parse the supplied data into an @ref SnacTransfer object. + * The exact state of the parse attempt can be read using @ref state. + * @param rawData The unparsed data. + * @param bytes An integer used to return the number of bytes read. + * @return A pointer to an EventTransfer object if successfull, otherwise 0. The caller is responsible for deleting this object. + */ + Transfer * parse( const QByteArray &, uint & bytes ); + +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/ssiactivatetask.cpp b/kopete/protocols/oscar/liboscar/ssiactivatetask.cpp new file mode 100644 index 00000000..1e7a17d6 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssiactivatetask.cpp @@ -0,0 +1,50 @@ +/* + Kopete Oscar Protocol + ssiactivatetask.cpp - Send the SNAC for SSI activation + + Copyright (c) 2004 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 <kdebug.h> +#include "ssiactivatetask.h" +#include "connection.h" +#include "buffer.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" + + + +SSIActivateTask::SSIActivateTask(Task* parent): Task(parent) +{ +} + + +SSIActivateTask::~SSIActivateTask() +{ +} + + +void SSIActivateTask::onGo() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending SSI activate" << endl; + FLAP f = { 0x02, 0, 0 } ; + SNAC s = { 0x0013, 0x0007, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + Transfer* t = createTransfer( f, s, buffer ); + send( t ); + setSuccess( 0, QString::null ); +} + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssiactivatetask.h b/kopete/protocols/oscar/liboscar/ssiactivatetask.h new file mode 100644 index 00000000..66f0a67b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssiactivatetask.h @@ -0,0 +1,38 @@ +/* + Kopete Oscar Protocol + ssiactivatetask.h - Send the SNAC for SSI activation + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ +#ifndef SSIACTIVATETASK_H +#define SSIACTIVATETASK_H + +#include "task.h" + +/** +A fire and forget task to send the activation SNAC for the SSI list to the server. + +@author Matt Rogers +*/ +class SSIActivateTask : public Task +{ +public: + SSIActivateTask( Task* parent ); + ~SSIActivateTask(); + void onGo(); +}; + +#endif + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssiauthtask.cpp b/kopete/protocols/oscar/liboscar/ssiauthtask.cpp new file mode 100644 index 00000000..59188d2b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssiauthtask.cpp @@ -0,0 +1,188 @@ +/* + Kopete Oscar Protocol + ssiauthtask.cpp - SSI Authentication Task + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + 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 "ssiauthtask.h" +#include "ssimanager.h" +#include "transfer.h" +#include "buffer.h" +#include "connection.h" +#include "oscarutils.h" + +#include <kdebug.h> + +SSIAuthTask::SSIAuthTask( Task* parent ) + : Task( parent ) +{ + m_manager = parent->client()->ssiManager(); +} + +SSIAuthTask::~SSIAuthTask() +{ +} + +bool SSIAuthTask::forMe( const Transfer* t ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*> ( t ); + + if ( !st ) + return false; + + if ( st->snacService() != 0x0013 ) + return false; + + switch ( st->snacSubtype() ) + { + case 0x0015: // Future authorization granted + case 0x0019: // Authorization request + case 0x001b: // Authorization reply + case 0x001c: // "You were added" message + return true; + break; + default: + return false; + } +} + +bool SSIAuthTask::take( Transfer* t ) +{ + if ( forMe( t ) ) + { + setTransfer( t ); + SnacTransfer* st = dynamic_cast<SnacTransfer*> ( t ); + + switch ( st->snacSubtype() ) + { + case 0x0015: // Future authorization granted + handleFutureAuthGranted(); + break; + case 0x0019: // Authorization request + handleAuthRequested(); + break; + case 0x001b: // Authorization reply + handleAuthReplied(); + break; + case 0x001c: // "You were added" message + handleAddedMessage(); + break; + } + setTransfer( 0 ); + return true; + } + return false; +} + +void SSIAuthTask::grantFutureAuth( const QString& uin, const QString& reason ) +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0013, 0x0014, 0x0000, client()->snacSequence() }; + + Buffer* buf = new Buffer(); + buf->addBUIN( uin.latin1() ); + buf->addBSTR( reason.utf8() ); + buf->addWord( 0x0000 ); // Unknown + + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + +void SSIAuthTask::sendAuthRequest( const QString& uin, const QString& reason ) +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0013, 0x0018, 0x0000, client()->snacSequence() }; + + Buffer* buf = new Buffer(); + buf->addBUIN( uin.latin1() ); + buf->addBSTR( reason.utf8() ); + buf->addWord( 0x0000 ); // Unknown + + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + +void SSIAuthTask::sendAuthReply( const QString& uin, const QString& reason, bool auth ) +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0013, 0x001A, 0x0000, client()->snacSequence() }; + + Buffer* buf = new Buffer(); + buf->addBUIN( uin.latin1() ); + buf->addByte( auth ? 0x01 : 0x00 ); // accepted / declined + buf->addBSTR( reason.utf8() ); + + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + +void SSIAuthTask::handleFutureAuthGranted() +{ + Buffer* buf = transfer()->buffer(); + + QString uin = Oscar::normalize( buf->getBUIN() ); + QByteArray reason = buf->getBSTR(); + + buf->getWord(); // 0x0000 - Unknown + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Future authorization granted from " << uin << endl; + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Reason: " << reason << endl; + emit futureAuthGranted( uin, QString::fromUtf8( reason.data(), reason.size() ) ); +} + +void SSIAuthTask::handleAuthRequested() +{ + Buffer* buf = transfer()->buffer(); + + QString uin = Oscar::normalize( buf->getBUIN() ); + QByteArray reason = buf->getBSTR(); + + buf->getWord(); // 0x0000 - Unknown + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Authorization requested from " << uin << endl; + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Reason: " << reason << endl; + + emit authRequested( uin, QString::fromUtf8( reason.data(), reason.size() ) ); +} + +void SSIAuthTask::handleAuthReplied() +{ + Buffer* buf = transfer()->buffer(); + + QString uin = Oscar::normalize( buf->getBUIN() ); + bool accepted = buf->getByte(); + QByteArray reason = buf->getBSTR(); + + if ( accepted ) + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Authorization request accepted by " << uin << endl; + else + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Authorization request declined by " << uin << endl; + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Reason: " << reason << endl; + emit authReplied( uin, QString::fromUtf8( reason.data(), reason.size() ), accepted ); +} + +void SSIAuthTask::handleAddedMessage() +{ + Buffer* buf = transfer()->buffer(); + + QString uin = Oscar::normalize( buf->getBUIN() ); + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "User " << uin << " added you to the contact list" << endl; + emit contactAddedYou( uin ); +} + +#include "ssiauthtask.moc" +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssiauthtask.h b/kopete/protocols/oscar/liboscar/ssiauthtask.h new file mode 100644 index 00000000..d470cfe9 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssiauthtask.h @@ -0,0 +1,60 @@ +/* + Kopete Oscar Protocol + ssiauthtask.h - SSI Authentication Task + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef SSIAUTHTASK_H +#define SSIAUTHTASK_H + +#include <task.h> + +class SSIManager; + +/** +@author Kopete Developers +*/ +class SSIAuthTask : public Task +{ +Q_OBJECT +public: + SSIAuthTask( Task* parent ); + + ~SSIAuthTask(); + + virtual bool forMe( const Transfer* t ) const; + virtual bool take( Transfer* t ); + + void grantFutureAuth( const QString& uin, const QString& reason ); + void sendAuthRequest( const QString& uin, const QString& reason ); + void sendAuthReply( const QString& uin, const QString& reason, bool auth ); +signals: + void futureAuthGranted( const QString& uin, const QString& reason ); + void authRequested( const QString& uin, const QString& reason ); + void authReplied( const QString& uin, const QString& reason, bool auth ); + void contactAddedYou( const QString& uin ); +private: + void handleFutureAuthGranted(); + void handleAuthRequested(); + void handleAuthReplied(); + void handleAddedMessage(); + +private: + SSIManager* m_manager; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssilisttask.cpp b/kopete/protocols/oscar/liboscar/ssilisttask.cpp new file mode 100644 index 00000000..fe2a981d --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssilisttask.cpp @@ -0,0 +1,174 @@ +/* + Kopete Oscar Protocol + ssilisttask.cpp - handles all operations dealing with the whole SSI list + + Copyright (c) 2004 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 "ssilisttask.h" + +#include <kdebug.h> +#include "connection.h" +#include "oscarutils.h" +#include "ssimanager.h" +#include "transfer.h" + +SSIListTask::SSIListTask( Task* parent ) : Task( parent ) +{ + m_ssiManager = client()->ssiManager(); + QObject::connect( this, SIGNAL( newContact( const Oscar::SSI& ) ), m_ssiManager, SLOT( newContact( const Oscar::SSI& ) ) ); + QObject::connect( this, SIGNAL( newGroup( const Oscar::SSI& ) ), m_ssiManager, SLOT( newGroup( const Oscar::SSI& ) ) ); + QObject::connect( this, SIGNAL( newItem( const Oscar::SSI& ) ), m_ssiManager, SLOT( newItem( const Oscar::SSI& ) ) ); +} + + +SSIListTask::~SSIListTask() +{} + +bool SSIListTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer * st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0013 ) + { + switch ( st->snacSubtype() ) + { + case 0x0006: + case 0x000F: + return true; + default: + return false; + }; + } + + return false; +} + +bool SSIListTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + SnacTransfer * st = dynamic_cast<SnacTransfer*>( transfer ); + if ( st->snacSubtype() == 0x0006 ) + { + setTransfer( transfer ); + handleSSIListReply(); + setTransfer( 0 ); + return true; + } + else if ( st->snacSubtype() == 0x000F ) + { + setTransfer( transfer ); + handleSSIUpToDate(); + setTransfer( 0 ); + return true; + } + } + + return false; +} + +void SSIListTask::onGo() +{ + checkSSITimestamp(); +} + +void SSIListTask::handleSSIListReply() +{ + QValueList<TLV> tlvList; + + Buffer* buffer = transfer()->buffer(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SSI Protocol version: " << buffer->getByte() << endl; + WORD ssiItems = buffer->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Number of items in this SSI packet: " << ssiItems << endl; + WORD parsedItems; + for ( parsedItems = 1; parsedItems <= ssiItems; ++parsedItems ) + { + tlvList.clear(); + WORD strlength = buffer->getWord(); + QString itemName = QString::fromUtf8( buffer->getBlock( strlength ), strlength ); + WORD groupId = buffer->getWord(); + WORD itemId = buffer->getWord(); + WORD itemType = buffer->getWord(); + WORD tlvLength = buffer->getWord(); + for ( int i = 0; i < tlvLength; ) + { + TLV t = buffer->getTLV(); + i += 4; + i += t.length; + tlvList.append( t ); + } + + if ( itemType == ROSTER_CONTACT ) + itemName = Oscar::normalize( itemName ); + + Oscar::SSI s( itemName, groupId, itemId, itemType, tlvList ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got SSI Item: " << s.toString() << endl; + if ( s.type() == ROSTER_GROUP ) + emit newGroup( s ); + + if ( s.type() == ROSTER_CONTACT ) + emit newContact( s ); + + if ( s.type() != ROSTER_CONTACT && s.type() != ROSTER_GROUP ) + emit newItem( s ); + } + + if ( buffer->length() > 0 ) + { + client()->ssiManager()->setLastModificationTime( buffer->getDWord() ); + //check the snac flags for another packet + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer() ); + if ( st && st->snacFlags() == 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SSI List complete" << endl; + client()->ssiManager()->setListComplete( true ); + setSuccess( 0, QString::null ); + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Awaiting another SSI packet" << endl; + } + +} + +void SSIListTask::handleSSIUpToDate() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Our SSI List is up to date" << endl; + Buffer* buffer = transfer()->buffer(); + + client()->ssiManager()->setLastModificationTime( buffer->getDWord() ); + WORD ssiItems = buffer->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Number of items in SSI list: " << ssiItems << endl; + + client()->ssiManager()->setListComplete( true ); + setSuccess( 0, QString::null ); +} + +void SSIListTask::checkSSITimestamp() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Checking the timestamp of the SSI list" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0013, 0x0005, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + buffer->addDWord( client()->ssiManager()->lastModificationTime() ); + buffer->addDWord( client()->ssiManager()->numberOfItems() ); + Transfer* t = createTransfer( f, s, buffer ); + send( t ); +} + +#include "ssilisttask.moc" + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssilisttask.h b/kopete/protocols/oscar/liboscar/ssilisttask.h new file mode 100644 index 00000000..96a4c3d8 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssilisttask.h @@ -0,0 +1,106 @@ +/* + Kopete Oscar Protocol + ssilisttask.h - handles all operations dealing with the whole SSI list + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ +#ifndef SSILISTTASK_H +#define SSILISTTASK_H + +#include <task.h> + +class SSI; +class SSIManager; + +/** + * This task handles all the operations dealing with the whole SSI list + * All SNACs handled by this task are in family 13. Subtypes 5, 6, and 7 + * are handled by individual functions. Subtype F is handled in the take() + * function. We don't use subtype 4 because the same thing can be accomplished + * using subtypes 5 and 6 together. + * + * @author Matt Rogers +*/ +class SSIListTask : public Task +{ +Q_OBJECT +public: + SSIListTask( Task* parent ); + ~SSIListTask(); + + virtual bool take( Transfer* transfer ); + +protected: + virtual bool forMe( const Transfer* transfer ) const; + virtual void onGo(); + +signals: + /** We have a new group */ + void newGroup( const Oscar::SSI& ); + + /** We have a new contact */ + void newContact( const Oscar::SSI& ); + + /** + * We have a new contact and they're on the visible list + */ + void newVisibleItem( const Oscar::SSI& ); + + /** + * We have a new contact and they're on the invisible list + */ + void newInvisibleItem( const Oscar::SSI& ); + + /** + * We have a new item + * Used for items we don't explicitly handle yet + */ + void newItem( const Oscar::SSI& ); + +private: + + /** + * Handle the list we get from the server + * This is SNAC( 0x13, 0x06 ) + */ + void handleSSIListReply(); + + /** + * Check the timestamp of the local SSI copy + * If it's up to date, we'll get SNAC( 13, 06 ) + * If it's out of date, we'll get SNAC( 13, 0F ) + * This is SNAC( 0x13, 0x05 ) + */ + void checkSSITimestamp(); + + /** + * The timestamp of the SSI is up to date + * This is SNAC( 0x13, 0x0F ) + */ + void handleSSIUpToDate(); + + +private: + /** + * Pointer to the SSI manager so we don't have to keep + * calling client()->ssiManager(). It's guaranteed to + * exist. + */ + SSIManager* m_ssiManager; + +}; + +#endif + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssimanager.cpp b/kopete/protocols/oscar/liboscar/ssimanager.cpp new file mode 100644 index 00000000..066e93fa --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssimanager.cpp @@ -0,0 +1,658 @@ +/* + Kopete Oscar Protocol + ssimanager.cpp - SSI management + + Copyright ( c ) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + Copyright ( c ) 2004 Matt Rogers <mattr@kde.org> + + Kopete ( c ) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + based on ssidata.h and ssidata.cpp ( c ) 2002 Tom Linsky <twl6@po.cwru.edu> + + ************************************************************************* + * * + * 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 "ssimanager.h" +#include <kdebug.h> +#include "oscarutils.h" + +// ------------------------------------------------------------------- + +class SSIManagerPrivate +{ +public: + QValueList<Oscar::SSI> SSIList; + QValueList<WORD> groupIdList; + QValueList<WORD> itemIdList; + bool complete; + DWORD lastModTime; + WORD maxContacts; + WORD maxGroups; + WORD maxVisible; + WORD maxInvisible; + WORD maxIgnore; + WORD nextContactId; + WORD nextGroupId; +}; + +SSIManager::SSIManager( QObject *parent, const char *name ) + : QObject( parent, name ) +{ + d = new SSIManagerPrivate; + d->complete = false; + d->lastModTime = 0; + d->nextContactId = 0; + d->nextGroupId = 0; + d->maxContacts = 999; + d->maxGroups = 999; + d->maxIgnore = 999; + d->maxInvisible = 999; + d->maxVisible = 999; +} + + +SSIManager::~SSIManager() +{ + clear(); + delete d; +} + +void SSIManager::clear() +{ + //delete all SSIs from the list + if ( d->SSIList.count() > 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Clearing the SSI list" << endl; + QValueList<Oscar::SSI>::iterator it = d->SSIList.begin(); + + while ( it != d->SSIList.end() && d->SSIList.count() > 0 ) + it = d->SSIList.remove( it ); + }; + + d->itemIdList.clear(); + d->groupIdList.clear(); + d->complete = false; + d->lastModTime = 0; + d->nextContactId = 0; + d->nextGroupId = 0; +} + +WORD SSIManager::nextContactId() +{ + if ( d->nextContactId == 0 ) + d->nextContactId++; + + d->nextContactId = findFreeId( d->itemIdList, d->nextContactId ); + + if ( d->nextContactId == 0xFFFF ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "No free id!" << endl; + return 0xFFFF; + } + + if ( d->itemIdList.contains( d->nextContactId ) == 0 ) + d->itemIdList.append( d->nextContactId ); + + return d->nextContactId++; +} + +WORD SSIManager::nextGroupId() +{ + if ( d->nextGroupId == 0 ) + d->nextGroupId++; + + d->nextGroupId = findFreeId( d->groupIdList, d->nextGroupId ); + + if ( d->nextGroupId == 0xFFFF ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "No free group id!" << endl; + return 0xFFFF; + } + + if ( d->groupIdList.contains( d->nextGroupId ) == 0 ) + d->groupIdList.append( d->nextGroupId ); + + return d->nextGroupId++; +} + +WORD SSIManager::numberOfItems() const +{ + return d->SSIList.count(); +} + +DWORD SSIManager::lastModificationTime() const +{ + return d->lastModTime; +} + +void SSIManager::setLastModificationTime( DWORD lastTime ) +{ + d->lastModTime = lastTime; +} + +void SSIManager::setParameters( WORD maxContacts, WORD maxGroups, WORD maxVisible, WORD maxInvisible, WORD maxIgnore ) +{ + //I'm not using k_funcinfo for these debug statements because of + //the function's long signature + QString funcName = QString::fromLatin1( "[void SSIManager::setParameters] " ); + kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of contacts allowed in SSI: " + << maxContacts << endl; + kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of groups allowed in SSI: " + << maxGroups << endl; + kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of contacts allowed on visible list: " + << maxVisible << endl; + kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of contacts allowed on invisible list: " + << maxInvisible << endl; + kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of contacts allowed on ignore list: " + << maxIgnore << endl; + + d->maxContacts = maxContacts; + d->maxGroups = maxGroups; + d->maxInvisible = maxInvisible; + d->maxVisible = maxVisible; + d->maxIgnore = maxIgnore; +} + +void SSIManager::loadFromExisting( const QValueList<Oscar::SSI*>& newList ) +{ + Q_UNUSED( newList ); + //FIXME: NOT Implemented! +} + +bool SSIManager::hasItem( const Oscar::SSI& item ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + { + Oscar::SSI s = ( *it ); + if ( s == item ) + return true; + } + + return false; +} + +Oscar::SSI SSIManager::findGroup( const QString &group ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_GROUP && (*it ).name().lower() == group.lower() ) + return ( *it ); + + + return m_dummyItem; +} + +Oscar::SSI SSIManager::findGroup( int groupId ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_GROUP && (*it ).gid() == groupId ) + return ( *it ); + + return m_dummyItem; +} + +Oscar::SSI SSIManager::findContact( const QString &contact, const QString &group ) const +{ + + if ( contact.isNull() || group.isNull() ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << + "Passed NULL name or group string, aborting!" << endl; + + return m_dummyItem; + } + + Oscar::SSI gr = findGroup( group ); // find the parent group + if ( gr.isValid() ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "gr->name= " << gr.name() << + ", gr->gid= " << gr.gid() << + ", gr->bid= " << gr.bid() << + ", gr->type= " << gr.type() << endl; + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + { + if ( ( *it ).type() == ROSTER_CONTACT && (*it ).name() == contact && (*it ).gid() == gr.gid() ) + { + //we have found our contact + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << + "Found contact " << contact << " in SSI data" << endl; + return ( *it ); + } + } + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << + "ERROR: Group '" << group << "' not found!" << endl; + } + return m_dummyItem; +} + +Oscar::SSI SSIManager::findContact( const QString &contact ) const +{ + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_CONTACT && (*it ).name() == contact ) + return ( *it ); + + return m_dummyItem; +} + +Oscar::SSI SSIManager::findContact( int contactId ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it!= listEnd; ++it ) + if ( ( *it ).type() == ROSTER_CONTACT && ( *it ).bid() == contactId ) + return ( *it ); + + return m_dummyItem; +} + +Oscar::SSI SSIManager::findItemForIcon( QByteArray iconHash ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it!= listEnd; ++it ) + { + if ( ( *it ).type() == ROSTER_BUDDYICONS ) + { + TLV t = Oscar::findTLV( ( *it ).tlvList(), 0x00D5 ); + Buffer b(t.data); + b.skipBytes(1); //don't care about flags + BYTE iconSize = b.getByte(); + QByteArray hash( b.getBlock( iconSize ) ); + if ( hash == iconHash ) + { + Oscar::SSI s = ( *it ); + return s; + } + } + } + return m_dummyItem; +} + +Oscar::SSI SSIManager::findItemForIconByRef( int ref ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it!= listEnd; ++it ) + { + if ( ( *it ).type() == ROSTER_BUDDYICONS ) + { + if ( ( *it ).name().toInt() == ref ) + { + Oscar::SSI s = ( *it ); + return s; + } + } + } + return m_dummyItem; +} + +Oscar::SSI SSIManager::findItem( const QString &contact, int type ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it!= listEnd; ++it ) + if ( ( *it ).type() == type && ( *it ).name() == contact ) + return ( *it ); + + return m_dummyItem; +} + +QValueList<Oscar::SSI> SSIManager::groupList() const +{ + QValueList<Oscar::SSI> list; + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_GROUP ) + list.append( ( *it ) ); + + return list; +} + +QValueList<Oscar::SSI> SSIManager::contactList() const +{ + QValueList<Oscar::SSI> list; + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_CONTACT ) + list.append( ( *it ) ); + + return list; +} + +QValueList<Oscar::SSI> SSIManager::visibleList() const +{ + QValueList<Oscar::SSI> list; + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_VISIBLE ) + list.append( ( *it ) ); + + return list; +} + +QValueList<Oscar::SSI> SSIManager::invisibleList() const +{ + QValueList<Oscar::SSI> list; + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_INVISIBLE ) + list.append( ( *it ) ); + + return list; +} + +QValueList<Oscar::SSI> SSIManager::contactsFromGroup( const QString &group ) const +{ + QValueList<Oscar::SSI> list; + + Oscar::SSI gr = findGroup( group ); + if ( gr.isValid() ) + { + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_CONTACT && (*it ).gid() == gr.gid() ) + list.append( ( *it ) ); + } + return list; +} + +QValueList<Oscar::SSI> SSIManager::contactsFromGroup( int groupId ) const +{ + QValueList<Oscar::SSI> list; + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_CONTACT && (*it ).gid() == groupId ) + list.append( ( *it ) ); + + return list; +} + +Oscar::SSI SSIManager::visibilityItem() const +{ + Oscar::SSI item = m_dummyItem; + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + { + if ( ( *it ).type() == 0x0004 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Found visibility setting" << endl; + item = ( *it ); + return item; + } + } + + return item; +} + +void SSIManager::setListComplete( bool complete ) +{ + d->complete = complete; +} + +bool SSIManager::listComplete() const +{ + return d->complete; +} + +bool SSIManager::newGroup( const Oscar::SSI& group ) +{ + //trying to find the group by its ID + QValueList<Oscar::SSI>::iterator it, listEnd = d->SSIList.end(); + if ( findGroup( group.name() ).isValid() ) + return false; + + if ( !group.name().isEmpty() ) //avoid the group with gid 0 and bid 0 + { // the group is really new + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding group '" << group.name() << "' to SSI list" << endl; + + d->SSIList.append( group ); + addID( group ); + emit groupAdded( group ); + return true; + } + return false; +} + +bool SSIManager::updateGroup( const Oscar::SSI& group ) +{ + Oscar::SSI oldGroup = findGroup( group.name() ); + + if ( oldGroup.isValid() ) + { + removeID( oldGroup ); + d->SSIList.remove( oldGroup ); + } + + if ( d->SSIList.findIndex( group ) != -1 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "New group is already in list." << endl; + return false; + } + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Updating group '" << group.name() << "' in SSI list" << endl; + d->SSIList.append( group ); + addID( group ); + emit groupUpdated( group ); + + return true; +} + +bool SSIManager::removeGroup( const Oscar::SSI& group ) +{ + QString groupName = group.name(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing group " << group.name() << endl; + int remcount = d->SSIList.remove( group ); + removeID( group ); + + if ( remcount == 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No groups removed" << endl; + return false; + } + + emit groupRemoved( groupName ); + return true; +} + +bool SSIManager::removeGroup( const QString &group ) +{ + Oscar::SSI gr = findGroup( group ); + + if ( gr.isValid() && removeGroup( gr ) ) + { + return true; + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Group " << group << " not found." << endl; + + return false; +} + +bool SSIManager::newContact( const Oscar::SSI& contact ) +{ + if ( d->SSIList.findIndex( contact ) == -1 ) + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding contact '" << contact.name() << "' to SSI list" << endl; + addID( contact ); + d->SSIList.append( contact ); + emit contactAdded( contact ); + } + else + return false; + return true; +} + +bool SSIManager::updateContact( const Oscar::SSI& contact ) +{ + Oscar::SSI oldContact = findContact( contact.name() ); + + if ( oldContact.isValid() ) + { + removeID( oldContact ); + d->SSIList.remove( oldContact ); + } + + if ( d->SSIList.findIndex( contact ) != -1 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "New contact is already in list." << endl; + return false; + } + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Updating contact '" << contact.name() << "' in SSI list" << endl; + addID( contact ); + d->SSIList.append( contact ); + emit contactUpdated( contact ); + + return true; +} + +bool SSIManager::removeContact( const Oscar::SSI& contact ) +{ + QString contactName = contact.name(); + int remcount = d->SSIList.remove( contact ); + removeID( contact ); + + if ( remcount == 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No contacts were removed." << endl; + return false; + } + + emit contactRemoved( contactName ); + return true; +} + +bool SSIManager::removeContact( const QString &contact ) +{ + Oscar::SSI ct = findContact( contact ); + + if ( ct.isValid() && removeContact( ct ) ) + return true; + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact " << contact << " not found." << endl; + + return false; +} + +bool SSIManager::newItem( const Oscar::SSI& item ) +{ + if ( d->SSIList.findIndex( item ) != -1 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Item is already in list." << endl; + return false; + } + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding item " << item.toString() << endl; + d->SSIList.append( item ); + addID( item ); + return true; +} + +bool SSIManager::updateItem( const Oscar::SSI& item ) +{ + Oscar::SSI oldItem = findItem( item.name(), item.type() ); + + if ( oldItem.isValid() ) + { + removeID( oldItem ); + d->SSIList.remove( oldItem ); + } + + if ( d->SSIList.findIndex( item ) != -1 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "New item is already in list." << endl; + return false; + } + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Updating item in SSI list" << endl; + addID( item ); + d->SSIList.append( item ); + return true; +} + +bool SSIManager::removeItem( const Oscar::SSI& item ) +{ + int remcount = d->SSIList.remove( item ); + removeID( item ); + + if ( remcount == 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No items were removed." << endl; + return false; + } + + return true; +} + +void SSIManager::addID( const Oscar::SSI& item ) +{ + if ( item.type() == ROSTER_GROUP ) + { + if ( d->groupIdList.contains( item.gid() ) == 0 ) + d->groupIdList.append( item.gid() ); + } + else + { + if ( d->itemIdList.contains( item.bid() ) == 0 ) + d->itemIdList.append( item.bid() ); + } +} + +void SSIManager::removeID( const Oscar::SSI& item ) +{ + if ( item.type() == ROSTER_GROUP ) + { + d->groupIdList.remove( item.gid() ); + + if ( d->nextGroupId > item.gid() ) + d->nextGroupId = item.gid(); + } + else + { + d->itemIdList.remove( item.bid() ); + + if ( d->nextContactId > item.bid() ) + d->nextContactId = item.bid(); + } +} + +WORD SSIManager::findFreeId( const QValueList<WORD>& idList, WORD fromId ) const +{ + for ( WORD id = fromId; id < 0x8000; id++ ) + { + if ( idList.contains( id ) == 0 ) + return id; + } + + return 0xFFFF; +} + +#include "ssimanager.moc" + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssimanager.h b/kopete/protocols/oscar/liboscar/ssimanager.h new file mode 100644 index 00000000..24e87c6a --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssimanager.h @@ -0,0 +1,154 @@ +/* + Kopete Oscar Protocol + ssimanager.h - SSI management + + Copyright ( c ) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + Copyright ( c ) 2004 Matt Rogers <mattr@kde.org> + + Kopete ( c ) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + based on ssidata.h and ssidata.cpp ( c ) 2002 Tom Linsky <twl6@po.cwru.edu> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef SSIMANAGER_H +#define SSIMANAGER_H + +#include <qobject.h> +#include <qvaluelist.h> + +#include "oscartypes.h" +#include "oscartypeclasses.h" + +using namespace Oscar; + +class SSIManagerPrivate; + +/** +SSI management + +@author Gustavo Pichorim Boiko +@author Matt Rogers +*/ +class KOPETE_EXPORT SSIManager : public QObject +{ + Q_OBJECT +public: + SSIManager( QObject* parent = 0, const char* name = 0 ); + + ~SSIManager(); + + /** Clear the internal SSI list */ + void clear(); + + /** Get the next buddy id for an SSI item */ + WORD nextContactId(); + + /** Get the next group id for an SSI item */ + WORD nextGroupId(); + + /** Get the number of items in the SSI list. */ + WORD numberOfItems() const; + + /** Get the timestamp the list was last modified */ + DWORD lastModificationTime() const; + + /** Set the timestamp of the last modification time */ + void setLastModificationTime( DWORD lastTime ); + + /** Set the parameters we should use for SSI */ + void setParameters( WORD maxContacts, WORD maxGroups, WORD maxVisible, + WORD maxInvisible, WORD maxIgnore ); + + /** + * Load an existing list from SSI objects. + * The current SSI list will be overwritten and it's contents + * replaced with the data from the new list + */ + void loadFromExisting( const QValueList<Oscar::SSI*>& newList ); + + bool hasItem( const Oscar::SSI& item ) const; + + Oscar::SSI findGroup( const QString& group ) const; + Oscar::SSI findGroup( int groupId ) const; + + + Oscar::SSI findContact( const QString& contact, const QString& group ) const; + Oscar::SSI findContact( const QString& contact ) const; + Oscar::SSI findContact( int contactId ) const; + + Oscar::SSI findItemForIcon( QByteArray iconHash ) const; + Oscar::SSI findItemForIconByRef( int ) const; + + Oscar::SSI findItem( const QString &contact, int type ) const; + + QValueList<Oscar::SSI> groupList() const; + QValueList<Oscar::SSI> contactList() const; + QValueList<Oscar::SSI> visibleList() const; + QValueList<Oscar::SSI> invisibleList() const; + QValueList<Oscar::SSI> contactsFromGroup( const QString& group ) const; + QValueList<Oscar::SSI> contactsFromGroup( int groupId ) const; + + Oscar::SSI visibilityItem() const; + + void setListComplete( bool complete ); + bool listComplete() const; + +public slots: + bool newGroup( const Oscar::SSI& group ); + bool updateGroup( const Oscar::SSI& group ); + bool removeGroup( const Oscar::SSI& group ); + bool removeGroup( const QString& group ); + + bool newContact( const Oscar::SSI& contact ); + bool updateContact( const Oscar::SSI& contact ); + bool removeContact( const Oscar::SSI& contact ); + bool removeContact( const QString& contact ); + + bool newItem( const Oscar::SSI& item ); + bool updateItem( const Oscar::SSI& item ); + bool removeItem( const Oscar::SSI& item ); + + void addID( const Oscar::SSI& item ); + void removeID( const Oscar::SSI& item ); + +signals: + + //! Emitted when we've added a new contact to the list + void contactAdded( const Oscar::SSI& ); + + //! Emitted when we've updated a contact in the list + void contactUpdated( const Oscar::SSI& ); + + //! Emitted when we've removed a contact from the list + void contactRemoved( const QString& contactName ); + + //! Emitted when we've added a new group to the list + void groupAdded( const Oscar::SSI& ); + + //! Emitted when we've updated a group in the list + void groupUpdated( const Oscar::SSI& ); + + //! Emitted when we've removed a group from the ssi list + void groupRemoved( const QString& groupName ); + + void modifyError( const QString& error ); + +private: + WORD findFreeId( const QValueList<WORD>& idList, WORD fromId ) const; + + SSIManagerPrivate* d; + Oscar::SSI m_dummyItem; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssimodifytask.cpp b/kopete/protocols/oscar/liboscar/ssimodifytask.cpp new file mode 100644 index 00000000..2091fca8 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssimodifytask.cpp @@ -0,0 +1,637 @@ +/* + Kopete Oscar Protocol + ssimodifytask.cpp - Handles all the ssi modification stuff + + Copyright (c) 2004 by Kopete Developers <kopete-devel@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-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 "ssimodifytask.h" + +#include <kdebug.h> +#include <klocale.h> +#include <qstring.h> +#include "connection.h" +#include "oscarutils.h" +#include "transfer.h" + + +SSIModifyTask::SSIModifyTask( Task* parent, bool staticTask ) : Task( parent ) +{ + m_ssiManager = parent->client()->ssiManager(); + m_static = staticTask; + m_opType = NoType; + m_opSubject = NoSubject; + m_id = 0; +} + + +SSIModifyTask::~SSIModifyTask() +{ +} + +void SSIModifyTask::onGo() +{ + sendSSIUpdate(); +} + +bool SSIModifyTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + if ( st ) + { + setTransfer( transfer ); + + if ( st->snacSubtype() == 0x0008 ) + handleSSIAdd(); + else if ( st->snacSubtype() == 0x0009 ) + handleSSIUpdate(); + else if ( st->snacSubtype() == 0x000A ) + handleSSIRemove(); + else if ( st->snacSubtype() == 0x000E ) + handleSSIAck(); + + setTransfer( 0 ); + } + return true; + } + else + return false; +} + +bool SSIModifyTask::addContact( const QString& contact, const QString& group, bool requiresAuth ) +{ + m_opType = Add; + m_opSubject = Contact; + + QString newContact = Oscar::normalize( contact ); + + Oscar::SSI oldItem = m_ssiManager->findContact( newContact ); + Oscar::SSI groupItem = m_ssiManager->findGroup( group ); + + if ( !groupItem ) + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "group " << group << " does not exist on SSI. Aborting" << endl; + return false; + } + + //create new SSI item and populate the TLV list + QValueList<TLV> tlvList; + if ( requiresAuth ) + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "This contact requires auth. adding appropriate tlv" << endl; + TLV t( 0x0066, 0, 0 ); + tlvList.append( t ); + } + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "creating new SSI item for " << contact << " in group " << group << endl; + Oscar::SSI newItem( newContact, groupItem.gid(), m_ssiManager->nextContactId(), ROSTER_CONTACT, tlvList ); + m_newItem = newItem; + return true; +} + +bool SSIModifyTask::removeContact( const QString& contact ) +{ + m_opType = Remove; + m_opSubject = Contact; + m_oldItem = m_ssiManager->findContact( Oscar::normalize( contact ) ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Scheduling" << m_oldItem.name() << " for removal" << endl; + return true; +} + +bool SSIModifyTask::changeGroup( const QString& contact, const QString& newGroup ) +{ + m_opType = Change; + m_opSubject = Group; + m_oldItem = m_ssiManager->findContact( Oscar::normalize( contact ) ); + Oscar::SSI oldGroupItem; + if ( m_oldItem.isValid() ) + oldGroupItem = m_ssiManager->findGroup( newGroup ); + else + return false; + + if ( m_oldItem.gid() == oldGroupItem.gid() ) + { //buddy already exists in this group + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "contact " << contact << " already exists in group " << oldGroupItem.name() << ". Aborting." << endl; + return false; + } + + m_groupItem = m_ssiManager->findGroup( newGroup ); + if ( !m_groupItem ) + { //couldn't find group + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "new group " << newGroup << " not found in SSI. Aborting" << endl; + return false; + } + + //create a new SSI item for the buddy in the new group + Oscar::SSI newItem( m_oldItem.name(), m_groupItem.gid(), m_oldItem.bid(), ROSTER_CONTACT, m_oldItem.tlvList() ); + m_newItem = newItem; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Moving '" << m_oldItem.name() << "' to group " << m_groupItem.name() << endl; + return true; +} + +bool SSIModifyTask::addGroup( const QString& groupName ) +{ + m_opType = Add; + m_opSubject = Group; + m_newItem = m_ssiManager->findGroup( groupName ); + QValueList<TLV> dummy; + Oscar::SSI newItem( groupName, m_ssiManager->nextGroupId(), 0, ROSTER_GROUP, dummy ); + m_newItem = newItem; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding group '" << m_newItem.name() << "' to SSI" << endl; + return true; +} + +bool SSIModifyTask::removeGroup( const QString& groupName ) +{ + m_opType = Remove; + m_opSubject = Group; + m_oldItem = m_ssiManager->findGroup( groupName ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Scheduling group '" << m_oldItem.name() << "' for removal. " << endl; + return true; +} + +bool SSIModifyTask::renameGroup( const QString& oldName, const QString & newName ) +{ + m_opType = Rename; + m_opSubject = Group; + if ( oldName == newName ) + return false; + + m_oldItem = m_ssiManager->findGroup( oldName ); + Oscar::SSI newItem( newName, m_oldItem.gid(), m_oldItem.bid(), ROSTER_GROUP, m_oldItem.tlvList() ); + m_newItem = newItem; + return true; +} + +bool SSIModifyTask::addItem( const Oscar::SSI& item ) +{ + m_opType = Add; + m_opSubject = NoSubject; + m_newItem = item; + return true; +} + +bool SSIModifyTask::removeItem( const Oscar::SSI& item ) +{ + m_opType = Remove; + m_opSubject = NoSubject; + m_oldItem = item; + return true; +} + +bool SSIModifyTask::modifyItem( const Oscar::SSI& oldItem, const Oscar::SSI& newItem ) +{ + if ( !m_ssiManager->hasItem( oldItem ) ) + return false; + + //make sure there are some common things between the two items + if ( oldItem.type() != newItem.type() ) + return false; + + m_oldItem = oldItem; + m_newItem = newItem; + m_opType = Change; + m_opSubject = NoSubject; + return true; +} + +bool SSIModifyTask::forMe( const Transfer * transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0013 ) + { + WORD subtype = st->snacSubtype(); + if ( m_static ) + { + if ( subtype == 0x0008 || subtype == 0x0009 || subtype == 0x000A ) + return true; + } + else + { + if ( subtype == 0x000E && m_id == st->snac().id ) + return true; + } + } + + return false; +} + +void SSIModifyTask::handleSSIAck() +{ + Buffer* b = transfer()->buffer(); + int numItems = b->length() / 2; + for( int i = 0; i < numItems; ++i ) + { + WORD ackCode = b->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << "Acknowledgement code is " << ackCode << endl; + + if ( ackCode != 0x0000 ) + freeIdOnError(); + + switch( ackCode ) + { + case 0x0000: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "SSI Update successful" << endl; + updateSSIManager(); + break; + case 0x0002: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Item to modify not found in list" << endl; + setSuccess( 0, QString::null ); + break; + case 0x0003: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Item already exists in SSI" << endl; + setSuccess( 0, QString::null ); + break; + case 0x000A: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Error adding item ( invalid id, already in list, invalid data )" << endl; + setSuccess( 0, QString::null ); + break; + case 0x000C: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Can't add item. Limit exceeded." << endl; + setSuccess( 0, QString::null ); + break; + case 0x000D: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Can't add ICQ item to AIM list ( and vice versa )" << endl; + setSuccess( 0, QString::null ); + break; + case 0x000E: + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Can't add item because contact requires authorization" << endl; + Oscar::SSI groupItem = m_ssiManager->findGroup( m_newItem.gid() ); + QString groupName = groupItem.name(); + addContact( m_newItem.name(), groupName, true ); + go(); + break; + } + default: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Unknown acknowledgement code" << endl; + setSuccess( 0, QString::null ); + break; + } + }; + + +} + +void SSIModifyTask::sendSSIUpdate() +{ + //what type of update are we sending? + if ( m_opSubject == Group && m_opType == Change ) + changeGroupOnServer(); + + //add an item to the ssi list + if ( m_opType == Add ) + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding an item to the SSI list" << endl; + sendEditStart(); + + //add the item + FLAP f1 = { 0x02, 0, 0 }; + m_id = client()->snacSequence(); + SNAC s1 = { 0x0013, 0x0008, 0x0000, m_id }; + Buffer* ssiBuffer = new Buffer; + ssiBuffer->addString( m_newItem ); + Transfer* t2 = createTransfer( f1, s1, ssiBuffer ); + send( t2 ); + + sendEditEnd(); + } + + //remove an item + if ( m_opType == Remove ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << m_oldItem.name() << " from SSI" << endl; + sendEditStart(); + + //remove the item + FLAP f1 = { 0x02, 0, 0 }; + m_id = client()->snacSequence(); + SNAC s1 = { 0x0013, 0x000A, 0x0000, m_id }; + Buffer* ssiBuffer = new Buffer; + ssiBuffer->addString( m_oldItem ); + Transfer* t2 = createTransfer( f1, s1, ssiBuffer ); + send( t2 ); + + sendEditEnd(); + } + + //modify an item + //we use rename for group and change for other items + if ( m_opType == Rename || ( m_opType == Change && m_opSubject != Group ) ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Modifying the item: " << m_oldItem.toString() << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "changing it to: " << m_newItem.toString() << endl; + sendEditStart(); + + //change the group name + FLAP f1 = { 0x02, 0, 0 }; + m_id = client()->snacSequence(); + SNAC s1 = { 0x0013, 0x0009, 0x0000, m_id }; + Buffer* ssiBuffer = new Buffer; + ssiBuffer->addString( m_newItem ); + Transfer* t2 = createTransfer( f1, s1, ssiBuffer ); + send( t2 ); + + sendEditEnd(); + } + +} + +void SSIModifyTask::changeGroupOnServer() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Moving a contact from one group to another" << endl; + + sendEditStart(); + + //remove the old buddy from the list + FLAP f1 = { 0x02, 0, 0 }; + SNAC s1 = { 0x0013, 0x000A, 0x0000, client()->snacSequence() }; + Buffer* b1 = new Buffer; + b1->addBSTR( m_oldItem.name().latin1() ); + b1->addWord( m_oldItem.gid() ); + b1->addWord( m_oldItem.bid() ); + b1->addWord( m_oldItem.type() ); + b1->addWord( 0 ); + Transfer* t2 = createTransfer( f1, s1, b1 ); + send( t2 ); + + //add the buddy to the list with a different group + FLAP f2 = { 0x02, 0, 0 }; + m_id = client()->snacSequence(); //we don't care about the first ack + SNAC s2 = { 0x0013, 0x0008, 0x0000, m_id }; + Buffer* b2 = new Buffer; + addItemToBuffer( m_newItem, b2 ); + + Transfer* t3 = createTransfer( f2, s2, b2 ); + send( t3 ); + + //find the old group so we can change it's list of buddy ids + //what a kludge + Oscar::SSI oldGroupItem = m_ssiManager->findGroup( m_oldItem.gid() ); + /* not checking the existance of oldGroupItem because if we got here + it has to exist */ + + //Change the 0x00C8 TLV in the old group item to remove the bid we're + //moving to a different group + QValueList<TLV> list = oldGroupItem.tlvList(); + TLV oldIds = Oscar::findTLV( list, 0x00C8 ); + if ( oldIds.type == 0x00C8 ) + { + Buffer newTLVData; + Buffer tlvBuffer( oldIds.data, oldIds.length ); + while ( tlvBuffer.length() != 0 ) + { + WORD id = tlvBuffer.getWord(); + if ( id != m_oldItem.bid() ) + newTLVData.addWord( id ); + } + + TLV newGroupTLV( 0x00C8, newTLVData.length(), newTLVData.buffer() ); + + list.remove( oldIds ); + list.append( newGroupTLV ); + oldGroupItem.setTLVList( list ); + } + + + //Change the 0x00C8 TLV in the new group item to add the bid we're + //adding to this group + QValueList<TLV> list2 = m_groupItem.tlvList(); + TLV oldIds2 = Oscar::findTLV( list2, 0x00C8 ); + TLV newGroupTLV; + if ( oldIds2.type == 0x00C8 ) + { + Buffer tlvBuffer( oldIds2.data, oldIds2.length ); + tlvBuffer.addWord( m_newItem.bid() ); + + TLV newGroupTLV( 0x00C8, tlvBuffer.length(), tlvBuffer.buffer() ); + list2.remove( oldIds ); + list2.append( newGroupTLV ); + m_groupItem.setTLVList( list2 ); + } + + //change the group properties + FLAP f3 = { 0x02, 0, 0 }; + SNAC s3 = { 0x0013, 0x0009, 0x0000, client()->snacSequence() }; + Buffer* b3 = new Buffer; + addItemToBuffer( oldGroupItem, b3 ); + addItemToBuffer( m_groupItem, b3 ); + + Transfer* t4 = createTransfer( f3, s3, b3 ); //we get no ack from this packet + send( t4 ); + + sendEditEnd(); +} + +void SSIModifyTask::updateSSIManager() +{ + if ( m_oldItem.isValid() && m_newItem.isValid() ) + { + if ( m_opSubject == Contact ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << m_oldItem.name() << endl; + m_ssiManager->removeContact( m_oldItem.name() ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "and adding " << m_newItem.name() << " to SSI manager" << endl; + m_ssiManager->newContact( m_newItem ); + } + else if ( m_opSubject == Group ) + { + if ( m_opType == Rename ) + m_ssiManager->updateGroup( m_newItem ); + else if ( m_opType == Change ) + m_ssiManager->updateContact( m_newItem ); + } + else if ( m_opSubject == NoSubject ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << m_oldItem.name() << endl; + m_ssiManager->removeItem( m_oldItem ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "and adding " << m_newItem.name() << " to SSI manager" << endl; + m_ssiManager->newItem( m_newItem ); + } + setSuccess( 0, QString::null ); + return; + } + + if ( m_oldItem.isValid() && !m_newItem ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << m_oldItem.name() << " from SSI manager" << endl; + if ( m_opSubject == Group ) + m_ssiManager->removeGroup( m_oldItem.name() ); + else if ( m_opSubject == Contact ) + m_ssiManager->removeContact( m_oldItem.name() ); + else if ( m_opSubject == NoSubject ) + m_ssiManager->removeItem( m_oldItem ); + setSuccess( 0, QString::null ); + return; + } + + if ( m_newItem.isValid() && !m_oldItem ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << m_newItem.name() << " to SSI manager" << endl; + if ( m_opSubject == Group ) + m_ssiManager->newGroup( m_newItem ); + else if ( m_opSubject == Contact ) + m_ssiManager->newContact( m_newItem ); + else if ( m_opSubject == NoSubject ) + m_ssiManager->newItem( m_newItem ); + setSuccess( 0, QString::null ); + return; + } + + setSuccess( 0, QString::null ); +} + +void SSIModifyTask::freeIdOnError() +{ + if ( m_oldItem.isValid() && m_newItem.isValid() ) + { + if ( m_opSubject == Contact || m_opSubject == NoSubject ) + { + if ( m_oldItem.bid() != m_newItem.bid() ) + m_ssiManager->removeID( m_newItem ); + } + else if ( m_opSubject == Group ) + { + if ( m_oldItem.gid() != m_newItem.gid() ) + m_ssiManager->removeID( m_newItem ); + } + } + else if ( m_newItem.isValid() && !m_oldItem ) + { + if ( m_opSubject == Group || m_opSubject == Contact || + m_opSubject == NoSubject ) + { + m_ssiManager->removeID( m_newItem ); + } + } +} + +void SSIModifyTask::sendEditStart() +{ + SNAC editStartSnac = { 0x0013, 0x0011, 0x0000, client()->snacSequence() }; + FLAP editStart = { 0x02, 0, 10 }; + Buffer* emptyBuffer = new Buffer; + Transfer* t1 = createTransfer( editStart, editStartSnac, emptyBuffer ); + send( t1 ); +} + +void SSIModifyTask::sendEditEnd() +{ + SNAC editEndSnac = { 0x0013, 0x0012, 0x0000, client()->snacSequence() }; + FLAP editEnd = { 0x02, 0, 10 } ; + Buffer* emptyBuffer = new Buffer; + Transfer *t5 = createTransfer( editEnd, editEndSnac, emptyBuffer ); + send( t5 ); +} + +void SSIModifyTask::addItemToBuffer( Oscar::SSI item, Buffer* buffer ) +{ + buffer->addBSTR( item.name().latin1() ); + buffer->addWord( item.gid() ); + buffer->addWord( item.bid() ); + buffer->addWord( item.type() ); + buffer->addWord( item.tlvListLength() ); + + QValueList<TLV>::const_iterator it = item.tlvList().begin(); + QValueList<TLV>::const_iterator listEnd = item.tlvList().end(); + for( ; it != listEnd; ++it ) + buffer->addTLV( ( *it ) ); +} + +Oscar::SSI SSIModifyTask::getItemFromBuffer( Buffer* buffer ) const +{ + QValueList<TLV> tlvList; + + WORD strlength = buffer->getWord(); + QString itemName = QString::fromUtf8( buffer->getBlock( strlength ), strlength ); + WORD groupId = buffer->getWord(); + WORD itemId = buffer->getWord(); + WORD itemType = buffer->getWord(); + WORD tlvLength = buffer->getWord(); + for ( int i = 0; i < tlvLength; ) + { + TLV t = buffer->getTLV(); + i += 4; + i += t.length; + tlvList.append( t ); + } + + if ( itemType == ROSTER_CONTACT ) + itemName = Oscar::normalize( itemName ); + + return Oscar::SSI( itemName, groupId, itemId, itemType, tlvList ); +} + +void SSIModifyTask::handleSSIAdd() +{ + Buffer* b = transfer()->buffer(); + + while ( b->length() > 0 ) + { + Oscar::SSI item = getItemFromBuffer( b ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << item.name() << " to SSI manager" << endl; + + if ( item.type() == ROSTER_GROUP ) + m_ssiManager->newGroup( item ); + else if ( item.type() == ROSTER_CONTACT ) + m_ssiManager->newContact( item ); + else + m_ssiManager->newItem( item ); + } +} + +void SSIModifyTask::handleSSIUpdate() +{ + Buffer* b = transfer()->buffer(); + + while ( b->length() > 0 ) + { + Oscar::SSI item = getItemFromBuffer( b ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Updating " << item.name() << " in SSI manager" << endl; + + if ( item.type() == ROSTER_GROUP ) + m_ssiManager->updateGroup( item ); + else if ( item.type() == ROSTER_CONTACT ) + m_ssiManager->updateContact( item ); + else + m_ssiManager->updateItem( item ); + } +} + +void SSIModifyTask::handleSSIRemove() +{ + Buffer* b = transfer()->buffer(); + + while ( b->length() > 0 ) + { + Oscar::SSI item = getItemFromBuffer( b ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << item.name() << " from SSI manager" << endl; + + if ( item.type() == ROSTER_GROUP ) + m_ssiManager->removeGroup( item ); + else if ( item.type() == ROSTER_CONTACT ) + m_ssiManager->removeContact( item ); + else + m_ssiManager->removeItem( item ); + } +} + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssimodifytask.h b/kopete/protocols/oscar/liboscar/ssimodifytask.h new file mode 100644 index 00000000..2c32dda3 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssimodifytask.h @@ -0,0 +1,156 @@ +/* + Kopete Oscar Protocol + ssimodifytask.h - Handles all the ssi modification stuff + + Copyright (c) 2004 by Kopete Developers <kopete-devel@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-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. * + * * + ************************************************************************* +*/ +#ifndef SSIMODIFYTASK_H +#define SSIMODIFYTASK_H + +#include "task.h" +#include "oscartypes.h" +#include "ssimanager.h" + + +class Buffer; + +/** +This class takes care of any SSI list modifications that need to be made. This includes: +@li adds +@li edits +@li removes +@li group changes +@li alias changes +@li authorization changes +etc. + +This task implements the following SNACs from the SSI family (0x0013): +@li 0x0008 +@li 0x0009 +@li 0x000A +@li 0x000E +@li 0x0011 +@li 0x0012 + +@author Matt Rogers +*/ +class SSIModifyTask : public Task +{ +public: + SSIModifyTask( Task* parent, bool staticTask = false ); + ~SSIModifyTask(); + + virtual void onGo(); + virtual bool take( Transfer* transfer ); + + /* Contact properties */ + enum OperationType { NoType = 0x00, Add = 0x10, Remove = 0x20, Rename = 0x40, Change = 0x80 }; + enum OperationSubject { NoSubject = 0x000, Contact = 0x100, Group = 0x200, Visibility = 0x400, Invisibility = 0x800 }; + + //! Set up the stuff needed to add a contact. + //! @return true if we can send the packet + bool addContact( const QString& contact, const QString& group, bool requiresAuth = false ); + + //! Set up the stuff needed to remove a contact. + //! @return true if we can send the packet + bool removeContact( const QString& contact ); + + //! Set up the stuff needed to change groups + //! @return true if we can send the packet + bool changeGroup( const QString& contact, const QString& newGroup ); + + /* Group properties */ + + //! Add a new group to the SSI list + //! @return true if we can send the packet + bool addGroup( const QString& groupName ); + + //! Remove a group from the SSI list + //! @return true if we can send the packet + bool removeGroup( const QString& groupName ); + + //! Rename a group on the SSI list + //! @return true if we can send the packet + bool renameGroup( const QString& oldName, const QString& newName ); + + //! Add an item to the SSI list + //! Should be used for other items we don't have explicit functions for + //! like icon hashs, privacy settings, non-icq contacts, etc. + bool addItem( const SSI& item ); + + //! Remove an item from the SSI list + //! Should be used for other items we don't have explicit functions for + //! like icon hashs, privacy settings, non-icq contacts, etc. + bool removeItem( const SSI& item ); + + //! Modify an item on the SSI list + //! Should be used for other items we don't have explicit functions for + //! like icon hashs, privacy settings, non-icq contacts, etc. + bool modifyItem( const SSI& oldItem, const SSI& newItem ); + +protected: + virtual bool forMe( const Transfer* transfer ) const; + +private: + //! Handle the acknowledgement from the server + void handleSSIAck(); + + //! Construct and send the packet to send to the server + void sendSSIUpdate(); + + //! Helper function to change the group on the server + void changeGroupOnServer(); + + //! Update the SSI Manager with the new data + void updateSSIManager(); + + //! Helper function to free id on error + void freeIdOnError(); + + //! Send the SSI edit start packet + void sendEditStart(); + + //! Send the SSI edit end packet + void sendEditEnd(); + + void addItemToBuffer( Oscar::SSI item, Buffer* buffer ); + Oscar::SSI getItemFromBuffer( Buffer* buffer ) const; + + //! Handle server request to add item + void handleSSIAdd(); + + //! Handle server request to update item + void handleSSIUpdate(); + + //! Handle server request to remove item + void handleSSIRemove(); + +private: + SSI m_oldItem; + SSI m_newItem; + SSI m_groupItem; + OperationType m_opType; + OperationSubject m_opSubject; + WORD m_id; + SSIManager* m_ssiManager; + bool m_static; + +}; + +#endif + +//kate: tab-width 4; indent-mode csands diff --git a/kopete/protocols/oscar/liboscar/ssiparamstask.cpp b/kopete/protocols/oscar/liboscar/ssiparamstask.cpp new file mode 100644 index 00000000..0be172e8 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssiparamstask.cpp @@ -0,0 +1,102 @@ +/* + Kopete Oscar Protocol + ssiparamstask.cpp - Get the SSI parameters so we can use them + + Copyright (c) 2004 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 "ssiparamstask.h" +#include <kdebug.h> +#include "buffer.h" +#include "connection.h" +#include "transfer.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "ssimanager.h" + +using namespace Oscar; + +SSIParamsTask::SSIParamsTask(Task* parent): Task(parent) +{ +} + + +SSIParamsTask::~SSIParamsTask() +{ +} + + +bool SSIParamsTask::forMe(const Transfer* transfer) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0013 && st->snacSubtype() == 0x0003 ) + return true; + + return false; +} + +bool SSIParamsTask::take(Transfer* transfer) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + handleParamReply(); + setTransfer( 0 ); + return true; + } + + return false; +} + +void SSIParamsTask::onGo() +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0013, 0x0002, 0x0000, client()->snacSequence() }; + + Buffer* buffer = new Buffer(); + buffer->addTLV16( 0x000B, 0x000F ); + + Transfer* t = createTransfer( f, s, buffer ); + send( t ); +} + +void SSIParamsTask::handleParamReply() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Getting SSI parameters" << endl; + Buffer* buf = transfer()->buffer(); + //manually parse the TLV out of the packet, since we only want certain things + if ( buf->getWord() != 0x0004 ) + { + setError( -1, QString::null ); + return; //no TLV of type 0x0004, bad packet. do nothing. + } + else + { + buf->skipBytes( 2 ); //the tlv length + WORD maxContacts = buf->getWord(); + WORD maxGroups = buf->getWord(); + WORD maxVisible = buf->getWord(); + WORD maxInvisible = buf->getWord(); + buf->skipBytes( 20 ); + WORD maxIgnore = buf->getWord(); + client()->ssiManager()->setParameters( maxContacts, maxGroups, maxVisible, maxInvisible, maxIgnore ); + } + setSuccess( 0, QString::null ); +} + +// kate: tab-width 4; indent-mode csands; + diff --git a/kopete/protocols/oscar/liboscar/ssiparamstask.h b/kopete/protocols/oscar/liboscar/ssiparamstask.h new file mode 100644 index 00000000..abf12aa2 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssiparamstask.h @@ -0,0 +1,43 @@ +/* + Kopete Oscar Protocol + ssiparamstask.h - Get the SSI parameters so we can use them + + Copyright (c) 2004 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. * + * * + ************************************************************************* +*/ +#ifndef SSIPARAMSTASK_H +#define SSIPARAMSTASK_H + +#include "task.h" + +/** +@author Kopete Developers +*/ +class SSIParamsTask : public Task +{ +public: + SSIParamsTask(Task* parent); + + ~SSIParamsTask(); + + virtual bool forMe(const Transfer* transfer) const; + virtual bool take(Transfer* transfer); + virtual void onGo(); + +private: + void handleParamReply(); +}; + +#endif + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/stream.cpp b/kopete/protocols/oscar/liboscar/stream.cpp new file mode 100644 index 00000000..02967416 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/stream.cpp @@ -0,0 +1,31 @@ +/* + stream.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + 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 "stream.h" + +Stream::Stream(QObject *parent) +:QObject(parent) +{ +} + +Stream::~Stream() +{ +} + +#include "stream.moc" diff --git a/kopete/protocols/oscar/liboscar/stream.h b/kopete/protocols/oscar/liboscar/stream.h new file mode 100644 index 00000000..9fbacbda --- /dev/null +++ b/kopete/protocols/oscar/liboscar/stream.h @@ -0,0 +1,75 @@ +/* + stream.h - Kopete Groupwise Protocol + + Copyright (c) 2004 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-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 <qobject.h> + +#ifndef OSCAR_STREAM_H +#define OSCAR_STREAM_H + +class Transfer; + +class Stream : public QObject +{ + Q_OBJECT +public: + enum Error { ErrParse, ErrProtocol, ErrStream, ErrCustom = 10 }; + enum StreamCond { + GenericStreamError, + Conflict, + ConnectionTimeout, + InternalServerError, + InvalidFrom, +/*# InvalidXml, // not required*/ + PolicyViolation, + ResourceConstraint, + SystemShutdown + }; + + Stream(QObject *parent=0); + virtual ~Stream(); + + virtual void close()=0; + virtual int errorCondition() const=0; + virtual QString errorText() const=0; + + /** + * Are there any messages waiting to be read + */ + virtual bool transfersAvailable() const = 0; // adapt to messages + /** + * Read a message received from the server + */ + virtual Transfer* read() = 0; + + /** + * Send a message to the server + */ + virtual void write( Transfer *request) = 0; + + +signals: + void connectionClosed(); + void delayedCloseFinished(); + void readyRead(); //signals that there is a transfer ready to be read +// void stanzaWritten(); + void error(int); +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/task.cpp b/kopete/protocols/oscar/liboscar/task.cpp new file mode 100644 index 00000000..2c7628d7 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/task.cpp @@ -0,0 +1,291 @@ +/* + task.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + 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 <qtimer.h> + +#include "connection.h" +#include "transfer.h" +#include "safedelete.h" +#include "buffer.h" +#include "task.h" + + +class Task::TaskPrivate +{ +public: + TaskPrivate() {} + + Q_UINT32 id; + bool success; + int statusCode; + QString statusString; + Connection* client; + bool insignificant, deleteme, autoDelete; + bool done; + Transfer* transfer; +}; + +Task::Task(Task *parent) +:QObject(parent) +{ + init(); + d->client = parent->client(); + connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected())); +} + +Task::Task(Connection* parent, bool) +:QObject(0) +{ + init(); + d->client = parent; + connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected())); +} + +Task::~Task() +{ + delete d->transfer; + delete d; +} + +void Task::init() +{ + d = new TaskPrivate; + d->success = false; + d->insignificant = false; + d->deleteme = false; + d->autoDelete = false; + d->done = false; + d->transfer = 0; + d->id = 0; +} + +Task *Task::parent() const +{ + return (Task *)QObject::parent(); +} + +Connection *Task::client() const +{ + return d->client; +} + +Transfer * Task::transfer() const +{ + return d->transfer; +} + +void Task::setTransfer( Transfer * transfer ) +{ + d->transfer = transfer; +} + +long Task::id() const +{ + return d->id; +} + +bool Task::success() const +{ + return d->success; +} + +int Task::statusCode() const +{ + return d->statusCode; +} + +const QString & Task::statusString() const +{ + return d->statusString; +} + +void Task::go(bool autoDelete) +{ + d->autoDelete = autoDelete; + + onGo(); +} + +bool Task::take( Transfer * transfer) +{ + const QObjectList *p = children(); + if(!p) + return false; + + // pass along the transfer to our children + QObjectListIt it(*p); + Task *t; + for(; it.current(); ++it) { + QObject *obj = it.current(); + if(!obj->inherits("Task")) + continue; + + t = static_cast<Task*>(obj); + + if(t->take( transfer )) + { + //qDebug( "Transfer ACCEPTED by: %s", t->className() ); + return true; + } + //else + //qDebug( "Transfer refused by: %s", t->className() ); + } + + return false; +} + +void Task::safeDelete() +{ + if(d->deleteme) + return; + + d->deleteme = true; + if(!d->insignificant) + SafeDelete::deleteSingle(this); +} + +void Task::onGo() +{ + qDebug( "ERROR: calling default NULL onGo() for this task, you should reimplement this!"); +} + +void Task::onDisconnect() +{ + if(!d->done) { + d->success = false; + d->statusCode = ErrDisc; + d->statusString = tr("Disconnected"); + + // delay this so that tasks that react don't block the shutdown + QTimer::singleShot(0, this, SLOT(done())); + } +} + +void Task::send( Transfer * request ) +{ + client()->send( request ); +} + +void Task::setSuccess(int code, const QString &str) +{ + if(!d->done) { + d->success = true; + d->statusCode = code; + d->statusString = str; + done(); + } +} + +void Task::setError(int code, const QString &str) +{ + if(!d->done) { + d->success = false; + d->statusCode = code; + d->statusString = str; + done(); + } +} + +void Task::done() +{ + debug("Task::done()"); + if(d->done || d->insignificant) + return; + d->done = true; + + if(d->deleteme || d->autoDelete) + d->deleteme = true; + + d->insignificant = true; + debug("emitting finished"); + finished(); + d->insignificant = false; + + if(d->deleteme) + SafeDelete::deleteSingle(this); +} + +void Task::clientDisconnected() +{ + onDisconnect(); +} + +Transfer* Task::createTransfer( struct FLAP f, struct SNAC s, Buffer* buffer ) +{ + return new SnacTransfer( f, s, buffer ); +} + +Transfer* Task::createTransfer( struct FLAP f, Buffer* buffer ) +{ + return new FlapTransfer( f, buffer ); +} + +Transfer* Task::createTransfer( Buffer* buffer ) +{ + return new Transfer( buffer ); +} + + +// void Task::debug(const char *fmt, ...) +// { +// char *buf; +// QString str; +// int size = 1024; +// int r; +// +// do { +// buf = new char[size]; +// va_list ap; +// va_start(ap, fmt); +// r = vsnprintf(buf, size, fmt, ap); +// va_end(ap); +// +// if(r != -1) +// str = QString(buf); +// +// delete [] buf; +// +// size *= 2; +// } while(r == -1); +// +// debug(str); +// } + +void Task::debug(const QString &str) +{ + //black hole + Q_UNUSED( str ); + //client()->debug(QString("%1: ").arg(className()) + str); +} + +bool Task::forMe( const Transfer * transfer ) const +{ + Q_UNUSED( transfer ); + return false; +} + +void Task::setId( Q_UINT32 id ) +{ + d->id = id; +} + +#include "task.moc" + +//kate: tab-width 4; indent-mode csands; + diff --git a/kopete/protocols/oscar/liboscar/task.h b/kopete/protocols/oscar/liboscar/task.h new file mode 100644 index 00000000..e48e02de --- /dev/null +++ b/kopete/protocols/oscar/liboscar/task.h @@ -0,0 +1,116 @@ +/* + task.h - Kopete Oscar Protocol + + Copyright (c) 2004 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-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. * + * * + ************************************************************************* +*/ + +#ifndef OSCAR_TASK_H +#define OSCAR_TASK_H + +#include <qobject.h> + +#include "oscartypes.h" + + +class QString; +class Buffer; +class Connection; +class Transfer; + +using namespace Oscar; + + +class Task : public QObject +{ + Q_OBJECT +public: + enum { ErrDisc }; + Task(Task *parent); + Task( Connection*, bool isRoot ); + virtual ~Task(); + + Task *parent() const; + Connection* client() const; + Transfer *transfer() const; + + long id() const; + void setId(); + + bool success() const; + int statusCode() const; + const QString & statusString() const; + + void go( bool autoDelete = false ); + + /** + * Allows a task to examine an incoming Transfer and decide whether to 'take' it + * for further processing. + */ + virtual bool take( Transfer* transfer ); + void safeDelete(); + + /** + * Direct setter for Tasks which don't have any fields + */ + void setTransfer( Transfer * transfer ); + +signals: + void finished(); + +protected: + virtual void onGo(); + virtual void onDisconnect(); + void setId( Q_UINT32 id ); + void send( Transfer * request ); + void setSuccess( int code=0, const QString &str="" ); + void setError( int code=0, const QString &str="" ); +// void debug( const char *, ... ); + void debug( const QString & ); + + /** + * Used in take() to check if the offered transfer is for this Task + * @return true if this Task should take the Transfer. Default impl always returns false. + */ + virtual bool forMe( const Transfer * transfer ) const; + + /** + * Creates a transfer with the given flap, snac, and buffer + */ + Transfer* createTransfer( FLAP f, SNAC s, Buffer* buffer ); + + /** + * Creates a transfer with the given flap and buffer + */ + Transfer* createTransfer( FLAP f, Buffer* buffer ); + + /** + * Creates a transfer with the given buffer + */ + Transfer* createTransfer( Buffer* buffer ); + +private slots: + void clientDisconnected(); + void done(); + +private: + void init(); + + class TaskPrivate; + TaskPrivate *d; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/Makefile.am b/kopete/protocols/oscar/liboscar/tests/Makefile.am new file mode 100644 index 00000000..9dc6b292 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/Makefile.am @@ -0,0 +1,28 @@ +INCLUDES = $(all_includes) $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/protocols/oscar/liboscar +METASOURCES = AUTO +check_PROGRAMS = kunittest clientstream_test logintest userinfotest ssigrouptest redirecttest ipaddrtest + +kunittest_SOURCES = main.cpp kunittest.cpp chatnavtests.cpp +kunittest_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kunittest_LDADD = $(LIB_KDECORE) ../liboscar.la + +clientstream_test_SOURCES = clientstream_test.cpp +clientstream_test_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la + +logintest_SOURCES = logintest.cpp +logintest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la + +userinfotest_SOURCES = userinfotest.cpp +userinfotest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la + +ssigrouptest_SOURCES = ssigrouptest.cpp +ssigrouptest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la + +redirecttest_SOURCES = redirecttest.cpp +redirecttest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la + +ipaddrtest_SOURCES = ipaddrtest.cpp +ipaddrtest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la + +check: kunittest + @./kunittest 2>&1 | grep "tests:" diff --git a/kopete/protocols/oscar/liboscar/tests/chatnavtests.cpp b/kopete/protocols/oscar/liboscar/tests/chatnavtests.cpp new file mode 100644 index 00000000..07a89f98 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/chatnavtests.cpp @@ -0,0 +1,305 @@ +/* + Kopete Oscar Protocol - Chat Navigation parsing tests + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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 "chatnavtests.h" + +#include <iostream> + +#include "buffer.h" +#include "oscartypeclasses.h" + +using namespace std; +using namespace Oscar; + + +ChatNavTests::ChatNavTests() +{ + m_buffer = 0; +} + + +ChatNavTests::~ChatNavTests() +{ +} + +void ChatNavTests::setupExchangeTestBuffer() +{ + delete m_buffer; + m_buffer = 0; + + m_buffer = new Buffer(); + //TLV 0x02 + m_buffer->addDWord(0x00020001); + m_buffer->addByte(0x03); + //TLV 0x03 + m_buffer->addDWord(0x0003003C); + m_buffer->addDWord(0x0001000a); + m_buffer->addDWord(0x00030001); + m_buffer->addDWord(0x14000400); + m_buffer->addDWord(0x02200000); + m_buffer->addDWord(0xC9000200); + m_buffer->addDWord(0x4400CA00); + m_buffer->addDWord(0x04000000); + m_buffer->addDWord(0x0000D000); + m_buffer->addDWord(0x0000D100); + m_buffer->addDWord(0x0207D000); + m_buffer->addDWord(0xD2000200); + m_buffer->addDWord(0x2F00D400); + m_buffer->addDWord(0x0000D500); + m_buffer->addDWord(0x010100DA); + m_buffer->addDWord(0x00020066); +} + +void ChatNavTests::setupRoomInfoTestBuffer() +{ + + delete m_buffer; + m_buffer = 0; + + m_buffer = new Buffer(); + //TLV 0x04 + m_buffer->addDWord(0x000400F8); + m_buffer->addWord(0x0004); //exchange + m_buffer->addByte(0x28); //cookie length + m_buffer->addByte(0x21); //start of cookie + m_buffer->addDWord(0x616F6C3A); + m_buffer->addDWord(0x2F2F3237); + m_buffer->addDWord(0x31393A31); + m_buffer->addDWord(0x302D342D); + m_buffer->addDWord(0x63686174); + m_buffer->addDWord(0x37343739); + m_buffer->addDWord(0x33333134); + m_buffer->addDWord(0x30313137); + m_buffer->addDWord(0x37393435); + m_buffer->addDWord(0x36363500); + m_buffer->addDWord(0x00020016); + m_buffer->addDWord(0x00660002); + m_buffer->addDWord(0x00000068); + m_buffer->addDWord(0x00040000); + m_buffer->addDWord(0x0000006A); + m_buffer->addDWord(0x00176368); + m_buffer->addDWord(0x61743734); + m_buffer->addDWord(0x37393333); + m_buffer->addDWord(0x31343031); + m_buffer->addDWord(0x31373739); + m_buffer->addDWord(0x34353636); + m_buffer->addDWord(0x35006D00); + m_buffer->addDWord(0x02000000); + m_buffer->addDWord(0x6E000200); + m_buffer->addDWord(0x00006F00); + m_buffer->addDWord(0x02000000); + m_buffer->addDWord(0x71000200); + m_buffer->addDWord(0x00007500); + m_buffer->addDWord(0x04000000); + m_buffer->addDWord(0x0000C900); + m_buffer->addDWord(0x02004000); + m_buffer->addDWord(0xCA000442); + m_buffer->addDWord(0xBEF90500); + m_buffer->addDWord(0xD0000200); + m_buffer->addDWord(0x0300D100); + m_buffer->addDWord(0x0207D000); + m_buffer->addDWord(0xD2000200); + m_buffer->addDWord(0x2600D300); + m_buffer->addDWord(0x17636861); + m_buffer->addDWord(0x74373437); + m_buffer->addDWord(0x39333331); + m_buffer->addDWord(0x34303131); + m_buffer->addDWord(0x37373934); + m_buffer->addDWord(0x35363635); + m_buffer->addDWord(0x00D40000); + m_buffer->addDWord(0x00D50001); + m_buffer->addDWord(0x0100D600); + m_buffer->addDWord(0x0875732D); + m_buffer->addDWord(0x61736369); + m_buffer->addDWord(0x6900D700); + m_buffer->addDWord(0x02656E00); + m_buffer->addDWord(0xD8000875); + m_buffer->addDWord(0x732D6173); + m_buffer->addDWord(0x63696900); + m_buffer->addDWord(0xD9000265); + m_buffer->addDWord(0x6E00DB00); + m_buffer->addDWord(0x0D756578); + m_buffer->addDWord(0x742F782D); + m_buffer->addDWord(0x616F6C72); + m_buffer->addDWord(0x746600DA); + m_buffer->addDWord(0x000200E8); +} + +void ChatNavTests::allTests() +{ + exchangeParsingTest(); + roominfoParsingTest(); +} + +void ChatNavTests::exchangeParsingTest() +{ + setupExchangeTestBuffer(); + Buffer testBuffer(*m_buffer); + CHECK( testBuffer.length() != 0, true ); + while ( testBuffer.length() != 0 ) + { + TLV t = testBuffer.getTLV(); + if ( t.type == 0x0002 ) + { +// cout << "Max concurrent rooms: " << t.data << endl; + } + + t = testBuffer.getTLV(); + if ( t.type == 0x0003 ) + { +// cout << "TLV of type 3 with length " << t.length << endl; + Buffer b(t.data); + WORD id = b.getWord(); + CHECK( id > 0, true ); + int tlvCount = b.getWord(); + int realCount = 0; +// cout << "Expecting " << tlvCount << " TLVs" << endl; + while ( b.length() > 0 ) + { + TLV t = b.getTLV(); + CHECK( t.type != 0, true ); + switch (t.type) + { + case 0x02: + CHECK( t.length == 2, true ); + CHECK( t.data.count() == 2, true ); + break; + case 0x03: + CHECK( t.length == 1, true ); + CHECK( t.data.count() == 1, true ); + CHECK( t.data[0] > 0, true ); + break; + case 0x04: + CHECK( t.length == 2, true ); + CHECK( t.data.count() == 2, true ); + //CHECK( t.data[0] > 0, true ); + break; + case 0x05: + CHECK( t.length > 1, true ); + break; + case 0x06: + CHECK( t.length > 2, true ); + break; + case 0xCA: + CHECK( t.length == 4, true ); + break; + case 0xD1: + CHECK( t.length == 2, true ); + break; + case 0xD2: + CHECK( t.length == 2, true ); + break; + case 0xD3: + CHECK( t.length > 0, true ); + CHECK( t.data.count() == t.length, true ); + break; + case 0xD5: + CHECK( t.length == 1, true ); + break; + default: +// ios::fmtflags origFlags = cout.flags(ios::hex|ios::showbase); +// cout << "unknown TLV type " << t.type << endl; +// cout.flags(origFlags); + break; + } + realCount++; + } + CHECK( tlvCount == realCount, true ); + } + CHECK( testBuffer.length() == 0, true ); + } +} + +void ChatNavTests::roominfoParsingTest() +{ + setupRoomInfoTestBuffer(); + Buffer testBuffer(*m_buffer); + CHECK( testBuffer.length() != 0, true ); + while ( testBuffer.length() != 0 ) + { + TLV t = testBuffer.getTLV(); + +// cout << "TLV of type " << t.type << " with length " << t.length << endl; + + + CHECK( t.type == 0x04, true ); + CHECK( t.length > 8, true ); + Buffer b( t.data ); + CHECK( b.getWord() > 0, true ); + BYTE cookieLength = b.getByte(); + b.skipBytes( cookieLength ); + CHECK( b.getWord() == 0, true ); + CHECK( b.getByte() > 0, true ); + int tlvCount = b.getWord(); + int realCount = 0; +// cout << "Expecting " << tlvCount << " TLVs" << endl; + while ( b.length() > 0 ) + { + TLV t = b.getTLV(); +// ios::fmtflags origFlags = cout.flags(ios::hex|ios::showbase); +// cout << "TLV of type " << t.type << endl; +// cout.flags(origFlags); + CHECK( t.type != 0, true ); + switch (t.type) + { + case 0x02: + CHECK( t.length == 2, true ); + CHECK( t.data.count() == 2, true ); + break; + case 0x03: + CHECK( t.length == 1, true ); + CHECK( t.data.count() == 1, true ); + CHECK( t.data[0] > 0, true ); + break; + case 0x04: + CHECK( t.length == 2, true ); + CHECK( t.data.count() == 2, true ); + //CHECK( t.data[0] > 0, true ); + break; + case 0x05: + CHECK( t.length > 1, true ); + break; + case 0x06: + CHECK( t.length > 2, true ); + break; + case 0xCA: + CHECK( t.length == 4, true ); + break; + case 0xD1: + CHECK( t.length == 2, true ); + break; + case 0xD2: + CHECK( t.length == 2, true ); + break; + case 0xD3: + CHECK( t.length > 0, true ); + CHECK( t.data.count() == t.length, true ); + break; + case 0xD5: + CHECK( t.length == 1, true ); + break; + default: + break; + } + realCount++; + } + CHECK( tlvCount == realCount, true ); + } +} + +//kate: indent-mode csands; tab-width 4; + + diff --git a/kopete/protocols/oscar/liboscar/tests/chatnavtests.h b/kopete/protocols/oscar/liboscar/tests/chatnavtests.h new file mode 100644 index 00000000..9899682f --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/chatnavtests.h @@ -0,0 +1,50 @@ +/* + Kopete Oscar Protocol - Chat Navigation parsing tests + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef CHATNAVTESTS_H +#define CHATNAVTESTS_H + +#include "tester.h" + +class Buffer; + +/** +@author Kopete Developers +*/ +class ChatNavTests : public Tester +{ +public: + ChatNavTests(); + ~ChatNavTests(); + + void allTests(); + +// void limitsParsingTest(); + void exchangeParsingTest(); + void roominfoParsingTest(); +// void extRoomInfoParsingTest(); +// void memberListParsingTest(); +// void searchInfoParsingTest(); +// void createRoomParsingTest(); + + void setupExchangeTestBuffer(); + void setupRoomInfoTestBuffer(); + +private: + Buffer* m_buffer; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/clientstream_test.cpp b/kopete/protocols/oscar/liboscar/tests/clientstream_test.cpp new file mode 100644 index 00000000..59f392de --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/clientstream_test.cpp @@ -0,0 +1,49 @@ +//Licensed under the GNU General Public License + +#include "clientstream_test.h" + +ClientStreamTest::ClientStreamTest(int argc, char ** argv) : QApplication( argc, argv ) +{ + // set up client stream + myConnector = new KNetworkConnector( 0 ); + //myConnector->setOptHostPort( "localhost", 8300 ); + myConnector->setOptHostPort( "login.oscar.aol.com", 5190 ); + myTestObject = new ClientStream( myConnector, myConnector); + // notify when the transport layer is connected + connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) ); + // notify and start sending + //connect( myTestObject, SIGNAL( warning(int) ), SLOT( slotWarning(int) ) ); + + // do test once the event loop is running + QTimer::singleShot( 0, this, SLOT( slotDoTest() ) ); + connected = false; +} + +ClientStreamTest::~ClientStreamTest() +{ + delete myTestObject; + delete myConnector; +} + +void ClientStreamTest::slotDoTest() +{ + QString server = QString::fromLatin1("login.oscar.aol.com"); + // connect to server + qDebug( "connecting to server "); + myTestObject->connectToServer( server, true ); // fine up to here... +} + +void ClientStreamTest::slotConnected() +{ + qDebug( "connection is up"); + connected = true; +} + +int main(int argc, char ** argv) +{ + ClientStreamTest a( argc, argv ); + a.exec(); + return 0; +} + +#include "clientstream_test.moc" diff --git a/kopete/protocols/oscar/liboscar/tests/clientstream_test.h b/kopete/protocols/oscar/liboscar/tests/clientstream_test.h new file mode 100644 index 00000000..32a0e3a9 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/clientstream_test.h @@ -0,0 +1,49 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef clientstream_test_h +#define clientstream_test_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "oscarclientstream.h" +#include "oscarconnector.h" + +#include "coreprotocol.h" + +#define QT_FATAL_ASSERT 1 + +class ClientStreamTest : public QApplication +{ +Q_OBJECT +public: + ClientStreamTest(int argc, char ** argv); + + ~ClientStreamTest(); + + bool isConnected(); + +public slots: + void slotDoTest(); + + void slotConnected(); + + //void slotWarning(int warning); + + //void slotsend(int layer); + +private: + KNetworkConnector *myConnector; + ClientStream *myTestObject; + bool connected; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/ipaddrtest.cpp b/kopete/protocols/oscar/liboscar/tests/ipaddrtest.cpp new file mode 100644 index 00000000..4f4e8df2 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/ipaddrtest.cpp @@ -0,0 +1,58 @@ +//Licensed under the GNU General Public License + +#include <iostream> +#include "ipaddrtest.h" +#include <qstring.h> + +using namespace std; +IPAddrTest::IPAddrTest(int argc, char ** argv) +: QApplication( argc, argv ) +{ +} + +IPAddrTest::~IPAddrTest() +{ +} + +bool IPAddrTest::testDottedDecimal() +{ + DWORD address = 1096652712; + return ( Oscar::getDottedDecimal( address ) == "65.93.151.168" ); +} + +bool IPAddrTest::testAllZeroDotted() +{ + DWORD address = 0; + return ( Oscar::getDottedDecimal( address ) == "0.0.0.0" ); +} + +bool IPAddrTest::testNumericalIP() +{ + QString address = "65.93.151.168"; + return ( Oscar::getNumericalIP( address ) == 1096652712 ); +} + +bool IPAddrTest::testAllZeroNumerical() +{ + QString address = "0.0.0.0"; + return ( Oscar::getNumericalIP( address ) == 0 ); +} + +void IPAddrTest::CheckTest(bool TestPassed) +{ + if ( TestPassed ) + cout << "passed" << endl; + else + cout << "failed" << endl; +} + +int main(int argc, char ** argv) +{ + IPAddrTest a( argc, argv ); + + a.CheckTest(a.testDottedDecimal()); + a.CheckTest(a.testNumericalIP()); + a.CheckTest(a.testAllZeroDotted() ); + a.CheckTest( a.testAllZeroNumerical() ); +} + diff --git a/kopete/protocols/oscar/liboscar/tests/ipaddrtest.h b/kopete/protocols/oscar/liboscar/tests/ipaddrtest.h new file mode 100644 index 00000000..9452ad82 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/ipaddrtest.h @@ -0,0 +1,35 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef IPADDRTEST_H +#define IPADDRTEST_H + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "oscarutils.h" + +#define QT_FATAL_ASSERT 1 + +class IPAddrTest : public QApplication +{ +public: + IPAddrTest(int argc, char ** argv); + ~IPAddrTest(); + + bool testDottedDecimal(); + bool testNumericalIP(); + bool testAllZeroDotted(); + bool testAllZeroNumerical(); + + void CheckTest(bool TestPassed); +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/kunittest.cpp b/kopete/protocols/oscar/liboscar/tests/kunittest.cpp new file mode 100644 index 00000000..9f7ba693 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/kunittest.cpp @@ -0,0 +1,167 @@ +/** + * kunittest.cpp + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "kunittest.h" + +#include "tester.h" +#include "chatnavtests.h" + +#include <qapplication.h> +#include <qtimer.h> + +#include <iostream> +using namespace std; + +void KUnitTest::registerTests() +{ + ADD_TEST( ChatNavTests ); +// ADD_TEST( SampleTest ); +// ADD_TEST( OnePassTest ); +// ADD_TEST( TwoPassTest ); +// ADD_TEST( MultiFailTest ); +// ADD_TEST( ExpectedFailureTest ); +// ADD_TEST( UnexpectedPassTest ); +// ADD_TEST( OnlyUnexpectedPassTest ); +// ADD_TEST( SkipLogTest ); +// ADD_TEST( SkipWithFailTest ); +} + +KUnitTest::KUnitTest() +{ + QTimer::singleShot( 0, this, SLOT(checkRun()) ); + + m_tests.setAutoDelete( TRUE ); +// m_qtests.setAutoDelete( TRUE ); + + registerTests(); +} + +void KUnitTest::checkRun() +{ +// if ( m_qtests.isEmpty() ) +// qApp->exit(); +} + +int KUnitTest::runTests() +{ + int globalSteps = 0; + int globalPasses = 0; + int globalFails = 0; + int globalXFails = 0; + int globalXPasses = 0; + int globalSkipped = 0; + + cout << "# Running normal tests... #" << endl << endl; + QAsciiDictIterator<Tester> it( m_tests ); + + for( ; it.current(); ++it ) { + Tester* test = it.current(); + test->allTests(); + cout << it.currentKey() << " - "; + int numPass = test->testsFinished() - ( test->errorList().count() + test->xfailList().count() + test->skipList().count() ); + int numFail = test->errorList().count() + test->xfailList().count(); + int numXFail = test->xfailList().count(); + int numXPass = test->xpassList().count(); + int numSkip = test->skipList().count(); + + globalSteps += test->testsFinished(); + globalPasses += numPass; + globalFails += numFail; + globalXFails += numXFail; + globalXPasses += numXPass; + globalSkipped += numSkip; + + cout << numPass << " test" << ( ( 1 == numPass )?"":"s") << " passed"; + if ( 0 < test->xpassList().count() ) { + cout << " (" << numXPass << " unexpected pass" << ( ( 1 == numXPass )?"":"es") << ")"; + } + cout << ", " << numFail << " test" << ( ( 1 == numFail )?"":"s") << " failed"; + if ( 0 < numXFail ) { + cout << " (" << numXFail << " expected failure" << ( ( 1 == numXFail )?"":"s") << ")"; + } + if ( 0 < numSkip ) { + cout << "; also " << numSkip << " skipped"; + } + cout << endl; + + if ( 0 < numXPass ) { + cout << " Unexpected pass" << ( ( 1 == numXPass )?"":"es") << ":" << endl; + QStringList list = test->xpassList(); + for ( QStringList::Iterator itr = list.begin(); itr != list.end(); ++itr ) { + cout << "\t" << (*itr).latin1() << endl; + } + } + if ( 0 < (numFail - numXFail) ) { + cout << " Unexpected failure" << ( ( 1 == numFail )?"":"s") << ":" << endl; + QStringList list = test->errorList(); + for ( QStringList::Iterator itr = list.begin(); itr != list.end(); ++itr ) { + cout << "\t" << (*itr).latin1() << endl; + } + } + if ( 0 < numXFail ) { + cout << " Expected failure" << ( ( 1 == numXFail)?"":"s") << ":" << endl; + QStringList list = test->xfailList(); + for ( QStringList::Iterator itr = list.begin(); itr != list.end(); ++itr ) { + cout << "\t" << (*itr).latin1() << endl; + } + } + if ( 0 < numSkip ) { + cout << " Skipped test" << ( ( 1 == numSkip )?"":"s") << ":" << endl; + QStringList list = test->skipList(); + for ( QStringList::Iterator itr = list.begin(); itr != list.end(); ++itr ) { + cout << "\t" << (*itr).latin1() << endl; + } + } + cout << endl; + } + + cout << "# Done with normal tests:" << endl; + cout << " Total test cases: " << m_tests.count() << endl; + cout << " Total test steps : " << globalSteps << endl; + cout << " Total passed test steps (including unexpected) : " << globalPasses << endl; + cout << " Total unexpected passed test steps : " << globalXPasses << endl; + cout << " Total failed test steps (including expected) : " << globalFails << endl; + cout << " Total expected failed test steps : " << globalXFails << endl; + cout << " Total skipped test steps : " << globalSkipped << endl; + + return m_tests.count(); +} + +//void KUnitTest::addTester( QTester *test ) +//{ +// m_qtests.insert( test, test ); +// connect( test, SIGNAL(destroyed(QObject*)), +// SLOT(qtesterDone(QObject* )) ); +//} + +void KUnitTest::qtesterDone( QObject *obj ) +{ +// m_qtests.remove( obj ); +// if ( m_qtests.isEmpty() ) +// qApp->quit(); +} + +#include "kunittest.moc" diff --git a/kopete/protocols/oscar/liboscar/tests/kunittest.h b/kopete/protocols/oscar/liboscar/tests/kunittest.h new file mode 100644 index 00000000..5148a82a --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/kunittest.h @@ -0,0 +1,71 @@ +/** + * kunittest.h + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef KUNITTEST_H +#define KUNITTEST_H + +#include "tester.h" + +#include <qobject.h> +#include <qasciidict.h> +#include <qptrdict.h> + +#define ADD_TEST(x) addTester( #x, new x ) +#define ADD_QTEST(x) addTester( new x ) + +class KUnitTest : public QObject +{ + Q_OBJECT +public: + KUnitTest(); + + int runTests(); +public: + void addTester( const char *name, Tester* test ) + { + m_tests.insert( name, test ); + } +// void addTester( QTester *test ); + +private slots: + void qtesterDone( QObject *obj ); + void checkRun(); + +private: + void registerTests(); + +private: + QAsciiDict<Tester> m_tests; +// QPtrDict<QTester> m_qtests; + int globalTests; + int globalPasses; + int globalFails; + int globalXFails; + int globalXPasses; + int globalSkipped; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/logintest.cpp b/kopete/protocols/oscar/liboscar/tests/logintest.cpp new file mode 100644 index 00000000..6dbc9646 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/logintest.cpp @@ -0,0 +1,56 @@ +//Licensed under the GNU General Public License + +#include "logintest.h" + +LoginTest::LoginTest(int argc, char ** argv) : QApplication( argc, argv ) +{ + // set up client stream + myConnector = new KNetworkConnector( 0 ); + myConnector->setOptHostPort( "login.oscar.aol.com", 5190 ); + myTestObject = new ClientStream( myConnector, myConnector); + + // notify when the transport layer is connected + //connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) ); + myClient = new Client(); + + myConnection = new Connection( myConnector, myTestObject, "AUTHORIZER" ); + myConnection->setClient( myClient ); + // do test once the event loop is running + QTimer::singleShot( 0, this, SLOT( slotDoTest() ) ); + connected = false; +} + +LoginTest::~LoginTest() +{ + delete myTestObject; + delete myConnector; + delete myClient; +} + +void LoginTest::slotDoTest() +{ + QString server = QString::fromLatin1("login.oscar.aol.com"); + // connect to server + qDebug( "connecting to server "); + + myClient->setIsIcq( true ); + myClient->start( server, 5190, "userid", "password" ); + myClient->connectToServer( myConnection, server, true ); + connected = true; +} + +void LoginTest::slotConnected() +{ + qDebug( "connection is up"); + connected = true; +} + +int main(int argc, char ** argv) +{ + LoginTest a( argc, argv ); + a.exec(); + if ( !a.isConnected() ) + return 0; +} + +#include "logintest.moc" diff --git a/kopete/protocols/oscar/liboscar/tests/logintest.h b/kopete/protocols/oscar/liboscar/tests/logintest.h new file mode 100644 index 00000000..898a3d99 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/logintest.h @@ -0,0 +1,53 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef logintest_h +#define logintest_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "oscarclientstream.h" +#include "oscarconnector.h" +#include "client.h" +#include "connection.h" +#include "coreprotocol.h" + +#define QT_FATAL_ASSERT 1 + +class LoginTest : public QApplication +{ +Q_OBJECT +public: + LoginTest(int argc, char ** argv); + + ~LoginTest(); + + bool isConnected() { return connected; } + +public slots: + void slotDoTest(); + + void slotConnected(); + + //void slotWarning(int warning); + + //void slotsend(int layer); + +private: + KNetworkConnector *myConnector; + ClientStream *myTestObject; + Client* myClient; + Connection* myConnection; + + bool connected; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/main.cpp b/kopete/protocols/oscar/liboscar/tests/main.cpp new file mode 100644 index 00000000..49966924 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/main.cpp @@ -0,0 +1,35 @@ +/** + * main.cpp + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "kunittest.h" + +int main( int argc, char** argv ) +{ + Q_UNUSED( argc ); + Q_UNUSED( argv ); + KUnitTest tests; + return tests.runTests(); +} diff --git a/kopete/protocols/oscar/liboscar/tests/redirecttest.cpp b/kopete/protocols/oscar/liboscar/tests/redirecttest.cpp new file mode 100644 index 00000000..a220e13e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/redirecttest.cpp @@ -0,0 +1,117 @@ +//Licensed under the GNU General Public License + +#include <iostream> +#include "redirecttest.h" +#include <qcstring.h> + +using namespace std; +RedirectTest::RedirectTest(int argc, char ** argv) +: QApplication( argc, argv ), + m_transfer(0), + m_task(0) +{ + m_root = new Task(0, true); +} + +RedirectTest::~RedirectTest() +{ + delete m_root; +} + +void RedirectTest::Setup() +{ + m_transfer = new SnacTransfer; + m_task = new ServerRedirectTask( m_root ); +} + +void RedirectTest::Teardown() +{ + delete m_task; + m_task = 0; + m_transfer = 0; +} + +bool RedirectTest::testHandleRedirect() +{ + Buffer* b = SetupBuffer(0x0010, "REDF$"); + m_transfer->setBuffer(b); + + m_task->setService(0x0010); + m_task->setTransfer(m_transfer); + return m_task->handleRedirect(); +} + +bool RedirectTest::testInvalidService() +{ + Buffer* b = SetupBuffer(0x4321, "REDF$"); + m_transfer->setBuffer(b); + + m_task->setService(0x0010); + m_task->setTransfer(m_transfer); + return !m_task->handleRedirect(); +} + +bool RedirectTest::testInvalidCookie() +{ + Buffer* b = SetupBuffer(0x0010, ""); + m_transfer->setBuffer(b); + + m_task->setService(0x0010); + m_task->setTransfer(m_transfer); + return !m_task->handleRedirect(); +} + +bool RedirectTest::testCookieIsSet() +{ + Buffer* b = SetupBuffer(0x0010, "grouch"); + m_transfer->setBuffer(b); + + m_task->setService(0x0010); + m_task->setTransfer(m_transfer); + m_task->handleRedirect(); + + return !m_task->cookie().isEmpty(); +} + +Buffer* RedirectTest::SetupBuffer(WORD Service, QString Cookie) +{ + Buffer* b = new Buffer; + b->addTLV16(0x000D, Service); + b->addWord(0x0005); + b->addWord(0x0010); + b->addString("65.86.43.45:5190", 16); + b->addWord(0x0006); + b->addWord(Cookie.length()); + b->addString(Cookie.latin1(), Cookie.length()); + return b; +} + +void RedirectTest::CheckTest(bool TestPassed) +{ + if ( TestPassed ) + cout << "passed" << endl; + else + cout << "failed" << endl; +} + +int main(int argc, char ** argv) +{ + RedirectTest a( argc, argv ); + + a.Setup(); + a.CheckTest(a.testHandleRedirect()); + a.Teardown(); + + a.Setup(); + a.CheckTest(a.testInvalidService()); + a.Teardown(); + + a.Setup(); + a.CheckTest(a.testInvalidCookie()); + a.Teardown(); + + a.Setup(); + a.CheckTest(a.testCookieIsSet()); + a.Teardown(); +} + diff --git a/kopete/protocols/oscar/liboscar/tests/redirecttest.h b/kopete/protocols/oscar/liboscar/tests/redirecttest.h new file mode 100644 index 00000000..eda5d67a --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/redirecttest.h @@ -0,0 +1,51 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef RedirectTest_h +#define RedirectTest_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "transfer.h" +#include "oscartypes.h" +#include "serverredirecttask.h" +#include "task.h" + +#define QT_FATAL_ASSERT 1 + +class RedirectTest : public QApplication +{ +public: + RedirectTest(int argc, char ** argv); + ~RedirectTest(); + + bool testHandleRedirect(); + bool testInvalidService(); + bool testInvalidCookie(); + bool testCookieIsSet(); + + void Setup(); + void Teardown(); + + void CheckTest(bool TestPassed); + +private: + //Helper functions + Buffer* SetupBuffer(WORD Service, QString Cookie); + + Task *m_root; + SnacTransfer * m_transfer; + ServerRedirectTask* m_task; + + bool connected; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/ssigrouptest.cpp b/kopete/protocols/oscar/liboscar/tests/ssigrouptest.cpp new file mode 100644 index 00000000..a1a9e754 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/ssigrouptest.cpp @@ -0,0 +1,73 @@ +//Licensed under the GNU General Public License + +#include "ssigrouptest.h" + +LoginTest::LoginTest(int argc, char ** argv) : QApplication( argc, argv ) +{ + // set up client stream + myConnector = new KNetworkConnector( 0 ); + myConnector->setOptHostPort( "login.oscar.aol.com", 5190 ); + myTestObject = new ClientStream( myConnector, myConnector); + + // notify when the transport layer is connected + //connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) ); + myClient = new Client(); + + myConnection = new Connection( myConnector, myTestObject, "AUTHORIZER" ); + myConnection->setClient( myClient ); + // do test once the event loop is running + QTimer::singleShot( 0, this, SLOT( slotDoTest() ) ); + connected = false; +} + +LoginTest::~LoginTest() +{ + delete myTestObject; + delete myConnector; + delete myClient; +} + +void LoginTest::slotDoTest() +{ + QString server = QString::fromLatin1("login.oscar.aol.com"); + // connect to server + qDebug( "connecting to server "); + + myClient->setIsIcq( true ); + myClient->start( server, 5190, "userid", "password" ); + myClient->connectToServer( myConnection, server, true ); + QTimer::singleShot( 10000, this, SLOT(runAddGroupTest() ) ); + connected = true; +} + +void LoginTest::slotConnected() +{ + qDebug( "connection is up"); + connected = true; +} + +int main(int argc, char ** argv) +{ + LoginTest a( argc, argv ); + a.exec(); + if ( !a.isConnected() ) + return 0; +} + +void LoginTest::runAddGroupTest() +{ + qDebug( "running ssi group add test" ); + QString group = QString::fromLatin1( "dummygroup" ); + myClient->addGroup( group ); + QTimer::singleShot( 5000, this, SLOT( runDelGroupTest() ) ); +} + +void LoginTest::runDelGroupTest() +{ + qDebug( "running ssi group del test" ); + QString group = QString::fromLatin1( "dummygroup" ); + myClient->removeGroup( group ); +} + + +#include "ssigrouptest.moc" diff --git a/kopete/protocols/oscar/liboscar/tests/ssigrouptest.h b/kopete/protocols/oscar/liboscar/tests/ssigrouptest.h new file mode 100644 index 00000000..361c053b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/ssigrouptest.h @@ -0,0 +1,54 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef logintest_h +#define logintest_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "oscarclientstream.h" +#include "oscarconnector.h" +#include "client.h" +#include "connection.h" +#include "coreprotocol.h" + +#define QT_FATAL_ASSERT 1 + +class LoginTest : public QApplication +{ +Q_OBJECT +public: + LoginTest(int argc, char ** argv); + + ~LoginTest(); + + bool isConnected() { return connected; } + +public slots: + void slotDoTest(); + void runAddGroupTest(); + void runDelGroupTest(); + void slotConnected(); + + //void slotWarning(int warning); + + //void slotsend(int layer); + +private: + KNetworkConnector *myConnector; + ClientStream *myTestObject; + Client* myClient; + Connection* myConnection; + + bool connected; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/ssitest.cpp b/kopete/protocols/oscar/liboscar/tests/ssitest.cpp new file mode 100644 index 00000000..d8e05b36 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/ssitest.cpp @@ -0,0 +1,111 @@ +//Licensed under the GNU General Public License + +#include "ssitest.h" + +#include <qstring.h> + +SSITest::SSITest(int argc, char ** argv) : QApplication( argc, argv ) +{ + m_manager = new SSIManager(this); + + testIt(); + +} + +SSITest::~SSITest() +{ + delete m_manager; +} + +void SSITest::testIt() +{ + QPtrList<TLV> tlvs; + + //add three groups + SSI *ssi = new SSI( "FirstGroup", 1, 1, ROSTER_GROUP, tlvs); + m_manager->newGroup(ssi); + + ssi = new SSI( "SecondGroup", 2, 2, ROSTER_GROUP, tlvs); + m_manager->newGroup(ssi); + + ssi = new SSI( "ThirdGroup", 3, 3, ROSTER_GROUP, tlvs); + m_manager->newGroup(ssi); + + //add six contacts + ssi = new SSI( "FirstContact", 1, 4, ROSTER_CONTACT, tlvs); + m_manager->newContact(ssi); + + ssi = new SSI( "SecondContact", 1, 5, ROSTER_CONTACT, tlvs); + m_manager->newContact(ssi); + + ssi = new SSI( "ThirdContact", 1, 6, ROSTER_CONTACT, tlvs); + m_manager->newContact(ssi); + + ssi = new SSI( "FourthContact", 2, 7, ROSTER_CONTACT, tlvs); + m_manager->newContact(ssi); + + ssi = new SSI( "FifthContact", 2, 8, ROSTER_CONTACT, tlvs); + m_manager->newContact(ssi); + + ssi = new SSI( "SixthContact", 3, 9, ROSTER_CONTACT, tlvs); + m_manager->newContact(ssi); + + //try to find a group by name + ssi = m_manager->findGroup("SecondGroup"); + if ( ssi ) + qDebug( QString("Found group SecondGroup with gid=%1").arg( ssi->gid() ).latin1()); + else + qDebug( "Oops, group SecondGroup not found" ); + + //try to find a group by gid + ssi = m_manager->findGroup( 3 ); + if ( ssi ) + qDebug( QString("Found group 3 with name=%1").arg( ssi->name() ).latin1() ); + else + qDebug( "Oops, group 3 not found" ); + + //try to find a contact by name + ssi = m_manager->findContact("ThirdContact"); + if ( ssi ) + qDebug( QString( "Found contact ThirdContact with gid=%1" ).arg( ssi->gid() ).latin1() ); + else + qDebug( "Oops, contact ThirdContact not found" ); + + //try to find a contact using the name and the group name + ssi = m_manager->findContact("FourthContact","SecondGroup"); + if ( ssi ) + qDebug( QString("Found contact FourthContact with bid=%1").arg( ssi->bid() ).latin1() ); + else + qDebug( "Oops, contact FourthContact not found" ); + + + //try to find an invalid group + ssi = m_manager->findGroup("InvalidGroup"); + if ( !ssi ) + qDebug( "Good! It has detected the group is invalid :)" ); + + //contacts from a group + QValueList<SSI*> list = m_manager->contactsFromGroup("FirstGroup"); + QValueList<SSI*>::iterator it; + qDebug( "Contacts from group FirtsGroup:" ); + for ( it = list.begin(); it != list.end(); ++it) + qDebug( QString(" name=%1").arg( (*it)->name() ).latin1() ); + + //the group list + QValueList<SSI*> list2 = m_manager->groupList(); + qDebug( "Group list:" ); + for ( it = list2.begin(); it != list2.end(); ++it) + qDebug( QString(" name=%1").arg( (*it)->name() ).latin1() ); + + //remove a group - this shouldn't report any debug line + m_manager->removeGroup( "SecondGroup" ); + +} + +int main(int argc, char ** argv) +{ + SSITest a( argc, argv ); + a.exec(); +} + +#include "ssitest.moc" diff --git a/kopete/protocols/oscar/liboscar/tests/ssitest.h b/kopete/protocols/oscar/liboscar/tests/ssitest.h new file mode 100644 index 00000000..19206465 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/ssitest.h @@ -0,0 +1,34 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef ssitest_h +#define ssitest_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "ssimanager.h" + +#define QT_FATAL_ASSERT 1 + +class SSITest : public QApplication +{ +Q_OBJECT +public: + SSITest(int argc, char ** argv); + + ~SSITest(); + + void testIt(); +private: + SSIManager *m_manager; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/tester.h b/kopete/protocols/oscar/liboscar/tests/tester.h new file mode 100644 index 00000000..2cb1f3af --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/tester.h @@ -0,0 +1,121 @@ +/** + * tester.h + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef TESTER_H +#define TESTER_H + +#include <qstringlist.h> + +#define CHECK( x, y ) check( __FILE__, __LINE__, #x, x, y, false ) +#define XFAIL( x, y ) check( __FILE__, __LINE__, #x, x, y, true ) +#define SKIP( x ) skip( __FILE__, __LINE__, #x ) + +class Tester +{ +public: + Tester() + : m_tests( 0 ) + { + } + virtual ~Tester() {} + +public: + virtual void allTests() = 0; + +public: + int testsFinished() const { + return m_tests; + } + + QStringList errorList() const { + return m_errorList; + } + + QStringList xfailList() const { + return m_xfailList; + } + + QStringList xpassList() const { + return m_xpassList; + } + + QStringList skipList() const { + return m_skipList; + } + + void skip( const char *file, int line, QString msg ) + { + QString skipEntry; + QTextStream ts( &skipEntry, IO_WriteOnly ); + ts << file << "["<< line <<"]: " << msg; + m_skipList.append( skipEntry ); + + ++m_tests; + } + +protected: + template<typename T> + void check( const char *file, int line, const char *str, + const T &result, const T &expectedResult, + bool expectedFailure ) + { + if ( result != expectedResult ) { + QString error; + QTextStream ts( &error, IO_WriteOnly ); + ts << file << "["<< line <<"]:" + <<" failed on \""<< str <<"\"" + << "\n\t\t result = '" + << result + << "', expected = '"<< expectedResult<<"'"; + if ( expectedFailure ) { + m_xfailList.append( error ); + } else { + m_errorList.append( error ); + } + } else { + // then the test passed, but we want to record it if + // we were expecting a failure + if (expectedFailure) { + QString error; + QTextStream ts( &error, IO_WriteOnly ); + ts << file << "["<< line <<"]:" + <<" unexpectedly passed on \"" + << str <<"\""; + m_xpassList.append( error ); + } + } + ++m_tests; + } + +private: + QStringList m_errorList; + QStringList m_xfailList; + QStringList m_xpassList; + QStringList m_skipList; + int m_tests; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/userinfotest.cpp b/kopete/protocols/oscar/liboscar/tests/userinfotest.cpp new file mode 100644 index 00000000..72ef5acb --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/userinfotest.cpp @@ -0,0 +1,67 @@ +//Licensed under the GNU General Public License + +#include "userinfotest.h" + +LoginTest::LoginTest(int argc, char ** argv) : QApplication( argc, argv ) +{ + // set up client stream + myConnector = new KNetworkConnector( 0 ); + myConnector->setOptHostPort( "login.oscar.aol.com", 5190 ); + myTestObject = new ClientStream( myConnector, myConnector); + + // notify when the transport layer is connected + //connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) ); + myClient = new Client(); + + myConnection = new Connection( myConnector, myTestObject, "AUTHORIZER" ); + myConnection->setClient( myClient ); + // do test once the event loop is running + QTimer::singleShot( 0, this, SLOT( slotDoTest() ) ); + connected = false; +} + +LoginTest::~LoginTest() +{ + delete myTestObject; + delete myConnector; + delete myClient; +} + +void LoginTest::slotDoTest() +{ + QString server = QString::fromLatin1("login.oscar.aol.com"); + // connect to server + qDebug( "connecting to server "); + + myClient->setIsIcq( true ); + myClient->start( server, 5190, "userid", "password" ); + myClient->connectToServer( myConnection, server, true ); + //QObject::connect( myClient, SIGNAL( userIsOnline( const QString& ) ), this, SLOT( runUserInfoTest())); + //QTimer::singleShot( 6000, this, SLOT(runUserInfoTest() ) ); + connected = true; +} + +void LoginTest::slotConnected() +{ + qDebug( "connection is up"); + connected = true; +} + +int main(int argc, char ** argv) +{ + LoginTest a( argc, argv ); + a.exec(); + if ( !a.isConnected() ) + return 0; +} + +void LoginTest::runUserInfoTest() +{ + qDebug( "running user info test" ); + QString contact = QString::fromLatin1( "userid" ); + myClient->requestFullInfo( contact ); + +} + + +#include "userinfotest.moc" diff --git a/kopete/protocols/oscar/liboscar/tests/userinfotest.h b/kopete/protocols/oscar/liboscar/tests/userinfotest.h new file mode 100644 index 00000000..433a6c48 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/userinfotest.h @@ -0,0 +1,53 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef logintest_h +#define logintest_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "oscarclientstream.h" +#include "oscarconnector.h" +#include "client.h" +#include "connection.h" +#include "coreprotocol.h" + +#define QT_FATAL_ASSERT 1 + +class LoginTest : public QApplication +{ +Q_OBJECT +public: + LoginTest(int argc, char ** argv); + + ~LoginTest(); + + bool isConnected() { return connected; } + +public slots: + void slotDoTest(); + void runUserInfoTest(); + void slotConnected(); + + //void slotWarning(int warning); + + //void slotsend(int layer); + +private: + KNetworkConnector *myConnector; + ClientStream *myTestObject; + Client* myClient; + Connection* myConnection; + + bool connected; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/transfer.cpp b/kopete/protocols/oscar/liboscar/transfer.cpp new file mode 100644 index 00000000..b442a97c --- /dev/null +++ b/kopete/protocols/oscar/liboscar/transfer.cpp @@ -0,0 +1,367 @@ +/* + transfer.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com> + + 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 "transfer.h" +#include <ctype.h> +#include <qdeepcopy.h> +#include <kdebug.h> + +Transfer::Transfer() +{ + m_isBufferValid = false; +} + +Transfer::Transfer( Buffer* buf ) +{ + m_buffer = buf; + m_isBufferValid = true; +} + +Transfer::TransferType Transfer::type() const +{ + return Transfer::RawTransfer; +} + +QByteArray Transfer::toWire() +{ + m_wireFormat.duplicate( m_buffer->buffer(), m_buffer->length() ); + QByteArray wire = QDeepCopy<QByteArray>( m_wireFormat ); + return wire; +} + +Transfer::~Transfer() +{ + delete m_buffer; + m_buffer = 0; +} + +void Transfer::setBuffer( Buffer* buffer ) +{ + m_buffer = buffer; +} + +Buffer* Transfer::buffer() +{ + return m_buffer; +} + +const Buffer* Transfer::buffer() const +{ + return m_buffer; +} + +bool Transfer::dataValid() const +{ + return m_isBufferValid; +} + +QString Transfer::toString() const +{ + // line format: + //00 03 00 0b 00 00 90 b8 f5 9f 09 31 31 33 37 38 |;tJ�..........| + + int i = 0; + QString output = "\n"; + QString hex, ascii; + + QByteArray::ConstIterator it; + QByteArray::ConstIterator end = m_wireFormat.end(); + for ( it = m_wireFormat.begin(); it != end; ++it ) + { + i++; + + unsigned char c = static_cast<unsigned char>(*it); + + if(c < 0x10) + hex.append("0"); + hex.append(QString("%1 ").arg(c, 0, 16)); + + ascii.append(isprint(c) ? c : '.'); + + if (i == 16) + { + output += hex + " |" + ascii + "|\n"; + i=0; + hex=QString::null; + ascii=QString::null; + } + } + + if(!hex.isEmpty()) + output += hex.leftJustify(48, ' ') + " |" + ascii.leftJustify(16, ' ') + '|'; + output.append('\n'); + + return output; +} + +void Transfer::populateWireBuffer( int offset, const QByteArray& buffer ) +{ + int j; + for ( uint i = 0; i < buffer.size(); ++i ) + { + j = i + offset; + m_wireFormat[j] = buffer[i]; + } +} + + +FlapTransfer::FlapTransfer() + : Transfer() +{ + m_isFlapValid = false; +} + +FlapTransfer::FlapTransfer( struct FLAP f, Buffer* buffer ) + : Transfer( buffer ) +{ + m_flapChannel = f.channel; + m_flapSequence = f.sequence; + m_flapLength = f.length; + + if ( m_flapChannel == 0 || m_flapLength < 6 ) + m_isFlapValid = false; + else + m_isFlapValid = true; + +} + +FlapTransfer::FlapTransfer( Buffer* buffer, BYTE chan, WORD seq, WORD len ) + : Transfer( buffer ) +{ + m_flapChannel = chan; + m_flapSequence = seq; + m_flapLength = len; + + if ( m_flapChannel == 0 || m_flapLength < 6 ) + m_isFlapValid = false; + else + m_isFlapValid = true; +} + +FlapTransfer::~FlapTransfer() +{ + +} + +QByteArray FlapTransfer::toWire() +{ + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Buffer length is " << m_buffer.length() << endl; + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Buffer is " << m_buffer.toString() << endl; + + m_wireFormat.truncate( 0 ); + QByteArray useBuf; + useBuf.duplicate( m_buffer->buffer(), m_buffer->length() ); + m_flapLength = useBuf.size(); + m_wireFormat.resize( 6 + m_flapLength ); + m_wireFormat[0] = 0x2A; + m_wireFormat[1] = m_flapChannel; + m_wireFormat[2] = (m_flapSequence & 0xFF00) >> 8; + m_wireFormat[3] = (m_flapSequence & 0x00FF); + m_wireFormat[4] = (m_flapLength & 0xFF00) >> 8; + m_wireFormat[5] = (m_flapLength & 0x00FF); + + //deepcopy the high-level buffer to the wire format buffer + populateWireBuffer( 6, useBuf ); + QByteArray wire = QDeepCopy<QByteArray>( m_wireFormat ); + return wire; +} + +void FlapTransfer::setFlapChannel( BYTE channel ) +{ + if ( channel != 0 ) + { + m_flapChannel = channel; + m_isFlapValid = true; + } +} + + +BYTE FlapTransfer::flapChannel() const +{ + return m_flapChannel; +} + + +void FlapTransfer::setFlapSequence( WORD seq ) +{ + m_flapSequence = seq; +} + + +WORD FlapTransfer::flapSequence() const +{ + return m_flapSequence; +} + +void FlapTransfer::setFlapLength( WORD len ) +{ + m_flapLength = len; +} + +WORD FlapTransfer::flapLength() const +{ + return m_flapLength; +} + +bool FlapTransfer::flapValid() const +{ + return m_isFlapValid; +} + +Transfer::TransferType FlapTransfer::type() const +{ + return Transfer::FlapTransfer; +} + + + +SnacTransfer::SnacTransfer() + : FlapTransfer() +{ + m_isSnacValid = false; +} + + +SnacTransfer::SnacTransfer( Buffer* buffer, BYTE chan, WORD seq, WORD len, WORD service, + WORD subtype, WORD flags, DWORD reqId ) + : FlapTransfer( buffer, chan, seq, len ) +{ + m_snacService = service; + m_snacSubtype = subtype; + m_snacFlags = flags; + m_snacReqId = reqId; + + if ( m_snacService == 0 || m_snacSubtype == 0 ) + m_isSnacValid = false; + else + m_isSnacValid = true; + +} + +SnacTransfer::SnacTransfer( struct FLAP f, struct SNAC s, Buffer* buffer ) + : FlapTransfer( f, buffer ) +{ + m_snacService = s.family; + m_snacSubtype = s.subtype; + m_snacFlags = s.flags; + m_snacReqId = s.id; + + if ( m_snacService == 0 || m_snacSubtype == 0 ) + m_isSnacValid = false; + else + m_isSnacValid = true; +} + +SnacTransfer::~SnacTransfer() +{ + +} + +QByteArray SnacTransfer::toWire() +{ + + m_wireFormat.truncate( 0 ); + QByteArray useBuf; + useBuf.duplicate( m_buffer->buffer(), m_buffer->length() ); + setFlapLength( useBuf.size() + 10 ); + m_wireFormat.resize( 16 + useBuf.size() ); + + //Transfer the flap - 6 bytes + m_wireFormat[0] = 0x2A; + m_wireFormat[1] = flapChannel(); + m_wireFormat[2] = (flapSequence() & 0xFF00) >> 8; + m_wireFormat[3] = (flapSequence() & 0x00FF); + m_wireFormat[4] = (flapLength() & 0xFF00) >> 8; + m_wireFormat[5] = (flapLength() & 0x00FF); + + //Transfer the Snac - 10 bytes + m_wireFormat[6] = (m_snacService & 0xFF00) >> 8; + m_wireFormat[7] = (m_snacService & 0x00FF); + m_wireFormat[8] = (m_snacSubtype & 0xFF00) >> 8; + m_wireFormat[9] = (m_snacSubtype & 0x00FF); + m_wireFormat[10] = (m_snacFlags & 0xFF00) >> 8; + m_wireFormat[11] = (m_snacFlags & 0x00FF); + m_wireFormat[12] = (m_snacReqId & 0xFF000000) >> 24; + m_wireFormat[13] = (m_snacReqId & 0x00FF0000) >> 16; + m_wireFormat[14] = (m_snacReqId & 0x0000FF00) >> 8; + m_wireFormat[15] = (m_snacReqId & 0x000000FF); + + //deepcopy the high-level buffer to the wire format buffer + populateWireBuffer( 16, useBuf ); + QByteArray wire = QDeepCopy<QByteArray>( m_wireFormat ); + return wire; +} + +Transfer::TransferType SnacTransfer::type() const +{ + return Transfer::SnacTransfer; +} + +bool SnacTransfer::snacValid() const +{ + return m_isSnacValid; +} + +void SnacTransfer::setSnacService( WORD service ) +{ + m_snacService = service; +} + +WORD SnacTransfer::snacService() const +{ + return m_snacService; +} + +void SnacTransfer::setSnacSubtype( WORD subtype ) +{ + m_snacSubtype = subtype; +} + +WORD SnacTransfer::snacSubtype() const +{ + return m_snacSubtype; +} + +void SnacTransfer::setSnacFlags( WORD flags ) +{ + m_snacFlags = flags; +} + +WORD SnacTransfer::snacFlags() const +{ + return m_snacFlags; +} + +void SnacTransfer::setSnacRequest( DWORD id ) +{ + m_snacReqId = id; +} + +DWORD SnacTransfer::snacRequest() const +{ + return m_snacReqId; +} + +SNAC SnacTransfer::snac() const +{ + SNAC s = { m_snacService, m_snacSubtype, m_snacFlags, m_snacReqId }; + return s; +} + + diff --git a/kopete/protocols/oscar/liboscar/transfer.h b/kopete/protocols/oscar/liboscar/transfer.h new file mode 100644 index 00000000..f42b1e83 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/transfer.h @@ -0,0 +1,169 @@ +/* + transfer.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + 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. * + * * + ************************************************************************* +*/ + +#ifndef TRANSFER_H +#define TRANSFER_H + +#include "oscartypes.h" +#include "buffer.h" + + +using namespace Oscar; + +class Transfer +{ +public: + enum TransferType { RawTransfer, FlapTransfer, SnacTransfer, DIMTransfer, FileTransfer }; + Transfer(); + Transfer( Buffer* buf ); + virtual ~Transfer(); + + virtual TransferType type() const; + + virtual QByteArray toWire(); + + //! Set the data buffer + void setBuffer( Buffer* buffer ); + + //! Get the data buffer + Buffer* buffer(); + + const Buffer* buffer() const; //used for const transfer objects + + //! Get the validity of the data after the flap header + bool dataValid() const; + + QString toString() const; + + void populateWireBuffer( int offset, const QByteArray& buffer ); + +protected: + //! The wire-format representation of our buffer + QByteArray m_wireFormat; + + //! The high-level representation of our data + Buffer* m_buffer; + +private: + + //! Flag to indicate whether we're a valid transfer + bool m_isBufferValid; + +}; + +class FlapTransfer : public Transfer +{ +public: + + FlapTransfer( Buffer* buffer, BYTE chan = 0, WORD seq = 0, WORD len = 0 ); + FlapTransfer( FLAP f, Buffer* buffer ); + FlapTransfer(); + virtual ~FlapTransfer(); + + virtual TransferType type() const; + virtual QByteArray toWire(); + + + //! Set the FLAP channel + void setFlapChannel( BYTE channel ); + + //! Get the FLAP channel + BYTE flapChannel() const; + + //! Set the FLAP sequence + void setFlapSequence( WORD seq ); + + //! Get the FLAP sequence + WORD flapSequence() const; + + //! Set the length of the data after the FLAP + void setFlapLength( WORD len ); + + //! Get the length of the data after the FLAP + WORD flapLength() const; + + //! Get the validity of the FLAP header + bool flapValid() const; + +private: + BYTE m_flapChannel; + WORD m_flapSequence; + WORD m_flapLength; + + bool m_isFlapValid; + +}; + +/** +@author Matt Rogers +*/ +class SnacTransfer : public FlapTransfer +{ +public: + + /*SnacTransfer();*/ + SnacTransfer( Buffer*, BYTE chan = 0, WORD seq = 0, WORD len = 0, WORD service = 0, + WORD subtype = 0, WORD flags = 0, DWORD reqId = 0 ); + SnacTransfer( struct FLAP f, struct SNAC s, Buffer* buffer ); + SnacTransfer(); + virtual ~SnacTransfer(); + + TransferType type() const; + virtual QByteArray toWire(); + + + //! Set the SNAC service + void setSnacService( WORD service ); + + //! Get the SNAC service + WORD snacService() const; + + //! Set the SNAC subtype + void setSnacSubtype( WORD subtype ); + + //! Get the SNAC subtype + WORD snacSubtype() const; + + //! Set the SNAC flags + void setSnacFlags( WORD flags ); + + //! Get the SNAC flags + WORD snacFlags() const; + + //! Set the SNAC request id + void setSnacRequest( DWORD id ); + + //! Get the SNAC request id + DWORD snacRequest() const; + + //! Get the validity of the SNAC header + bool snacValid() const; + + //! Get the SNAC header + SNAC snac() const; + +private: + + WORD m_snacService; + WORD m_snacSubtype; + WORD m_snacFlags; + WORD m_snacReqId; + + bool m_isSnacValid; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/typingnotifytask.cpp b/kopete/protocols/oscar/liboscar/typingnotifytask.cpp new file mode 100644 index 00000000..76503116 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/typingnotifytask.cpp @@ -0,0 +1,124 @@ +/* + typingnotifytask.h - Send/Recieve typing notifications + + 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 "typingnotifytask.h" + +#include <qstring.h> +#include <kdebug.h> +#include "transfer.h" +#include "buffer.h" +#include "connection.h" + + + + +TypingNotifyTask::TypingNotifyTask( Task* parent ) +: Task( parent ) +{ + m_notificationType = 0x0000; +} + +TypingNotifyTask::~TypingNotifyTask() +{ +} + +bool TypingNotifyTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0004 && st->snacSubtype() == 0x0014 ) + return true; + else + return false; +} + +bool TypingNotifyTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + handleNotification(); + setTransfer( 0 ); + return true; + } + + return false; +} + +void TypingNotifyTask::onGo() +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0004, 0x0014, 0x0000, client()->snacSequence() }; + Buffer* b = new Buffer(); + + //notification id cookie. it's a quad-word + b->addDWord( 0x00000000 ); + b->addDWord( 0x00000000 ); + + b->addWord( 0x0001 ); //mtn messages are always sent as type 1 messages + + b->addBUIN( m_contact.latin1() ); + + b->addWord( m_notificationType ); + + Transfer* t = createTransfer( f, s, b ); + send( t ); + + setSuccess( 0, QString::null ); +} + +void TypingNotifyTask::handleNotification() +{ + /* NB ICQ5 (windows) seems to only send 0x0002 and 0x0001, so I'm interpreting 0x001 as typing finished here - Will */ + Buffer* b = transfer()->buffer(); + + //I don't care about the QWORD or the channel + b->skipBytes( 10 ); + + QString contact( b->getBUIN() ); + + Q_UINT32 word = b->getWord(); + switch ( word ) + { + case 0x0000: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << " has finished typing" << endl; + emit typingFinished( contact ); + break; + case 0x0001: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << " has typed a word" << endl; + emit typingFinished( contact ); + break; + case 0x0002: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << " has started typing" << endl; + emit typingStarted( contact ); + break; + default: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << " typed an unknown typing notification - " << word << endl; + } +} + +void TypingNotifyTask::setParams( const QString& contact, int notifyType ) +{ + m_contact = contact; + m_notificationType = notifyType; +} + +#include "typingnotifytask.moc" + +// kate: indent-mode csands; space-indent off; replace-tabs off; + diff --git a/kopete/protocols/oscar/liboscar/typingnotifytask.h b/kopete/protocols/oscar/liboscar/typingnotifytask.h new file mode 100644 index 00000000..b2c9bfef --- /dev/null +++ b/kopete/protocols/oscar/liboscar/typingnotifytask.h @@ -0,0 +1,62 @@ +/* + typingnotifytask.h - Send/Recieve typing notifications + + 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. * + * * + ************************************************************************* +*/ + +#ifndef _TYPINGNOTIFYTASK_H_ +#define _TYPINGNOTIFYTASK_H_ + +#include "task.h" +#include <qstring.h> +#include "oscartypeclasses.h" + +/** + * Handles sending and receiving mini typing notifications + * @author Matt Rogers + */ +class TypingNotifyTask : public Task +{ +Q_OBJECT +public: + enum { Finished = 0x0000, Typed = 0x0001, Begin = 0x0002 }; + + TypingNotifyTask( Task* parent ); + ~TypingNotifyTask(); + + virtual bool forMe( const Transfer* transfer) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + + void setParams( const QString & contact, int notifyType ); + +signals: + //! somebody started typing on the other end + void typingStarted( const QString& contact ); + + //! somebody finished typing + void typingFinished( const QString& contact ); + +private: + + //! Parse the incoming SNAC(0x04, 0x14) + void handleNotification(); + +private: + WORD m_notificationType; + QString m_contact; +}; + +#endif + +//kate: indent-mode csands; space-indent off; replace-tabs off; 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; diff --git a/kopete/protocols/oscar/liboscar/userdetails.h b/kopete/protocols/oscar/liboscar/userdetails.h new file mode 100644 index 00000000..fad79172 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/userdetails.h @@ -0,0 +1,121 @@ +/* + Kopete Oscar Protocol + userdetails.h - 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. * + * * + ************************************************************************* +*/ +#ifndef USERDETAILS_H +#define USERDETAILS_H + +#include <ksocketaddress.h> +#include "oscartypes.h" +#include "kopete_export.h" + +class Buffer; +using namespace Oscar; + +/** + * Holds information from the extended user info packet + * @author Matt Rogers + */ +class KOPETE_EXPORT UserDetails +{ +public: + UserDetails(); + ~UserDetails(); + + QString userId() const; //! User ID accessor + int warningLevel() const; //! Warning level accessor + WORD idleTime() const; //! Idle time accessor + KNetwork::KIpAddress dcInternalIp() const; //! DC local IP accessor + KNetwork::KIpAddress dcExternalIp() const; //! DC outside IP accessor + DWORD dcPort() const; //! DC port number + QDateTime onlineSinceTime() const; //! Online since accessor + QDateTime memberSinceTime() const; //! Member since accessor + int userClass() const; //! User class accessor + DWORD extendedStatus() const; //!User status accessor + BYTE iconCheckSumType() const; //!Buddy icon hash type + QByteArray buddyIconHash() const; //! Buddy icon md5 hash accessor + QString clientName() const; //! Client name and version + bool hasCap( int capNumber ) const; //! Tell if we have this capability + + /** + * Fill the class with data from a buffer + * It only updates what's available. + */ + void fill( Buffer* buffer ); + + /** + * Merge only those data from another UserDetails + * which are marked as specified. + */ + void merge( const UserDetails& ud ); + + bool userClassSpecified() const { return m_userClassSpecified; } + bool memberSinceSpecified() const { return m_memberSinceSpecified; } + bool onlineSinceSpecified() const { return m_onlineSinceSpecified; } + bool numSecondsOnlineSpecified() const { return m_numSecondsOnlineSpecified; } + bool idleTimeSpecified() const { return m_idleTimeSpecified; } + bool extendedStatusSpecified() const { return m_extendedStatusSpecified; } + bool capabilitiesSpecified() const { return m_capabilitiesSpecified; } + bool dcOutsideSpecified() const { return m_dcOutsideSpecified; } + bool dcInsideSpecified() const { return m_dcInsideSpecified; } + bool iconSpecified() const { return m_iconSpecified; } +private: + //! Do client detection + void detectClient(); + + +private: + QString m_userId; /// the screename/uin of the contact + int m_warningLevel; /// the warning level of the contact + int m_userClass; /// the class of the user - TLV 0x01 + QDateTime m_memberSince; /// how long the user's been a member - TLV 0x05 + QDateTime m_onlineSince; /// how long the contact's been online - TLV 0x03 + DWORD m_numSecondsOnline; /// how long the contact's been online in seconds + WORD m_idleTime; /// the idle time of the contact - TLV 0x0F + DWORD m_extendedStatus; /// the extended status of the contact - TLV 0x06 + DWORD m_capabilities; //TLV 0x05 + QString m_clientVersion; /// the version of client they're using + QString m_clientName; /// the name of the client they're using + KNetwork::KIpAddress m_dcOutsideIp; /// DC Real IP Address - TLV 0x0A + KNetwork::KIpAddress m_dcInsideIp; /// DC Internal IP Address - TLV 0x0C + DWORD m_dcPort; /// DC Port - TLV 0x0C + BYTE m_dcType; /// DC Type - TLV 0x0C + WORD m_dcProtoVersion; /// DC Protocol Version - TLV 0x0C + DWORD m_dcAuthCookie; /// DC Authorization Cookie - TLV 0x0C + DWORD m_dcWebFrontPort; /// DC Web Front Port - TLV 0x0C + DWORD m_dcClientFeatures; /// DC client features( whatever they are ) - TLV 0x0C + DWORD m_dcLastInfoUpdateTime; /// DC last info update time - TLV 0x0C + DWORD m_dcLastExtInfoUpdateTime; /// DC last exteneded info update time - TLV 0x0C + DWORD m_dcLastExtStatusUpdateTime; /// DC last extended status update time - TLV 0x0C + BYTE m_iconChecksumType; /// The OSCAR checksum type for the buddy icon TLV 0x1D + QByteArray m_md5IconHash; /// Buddy Icon MD5 Hash - TLV 0x1D + QString m_availableMessage; /// Message a person can have when available - TLV 0x0D + + bool m_userClassSpecified; + bool m_memberSinceSpecified; + bool m_onlineSinceSpecified; + bool m_numSecondsOnlineSpecified; + bool m_idleTimeSpecified; + bool m_extendedStatusSpecified; + bool m_capabilitiesSpecified; + bool m_dcOutsideSpecified; + bool m_dcInsideSpecified; + bool m_iconSpecified; + +}; + +#endif +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/userinfotask.cpp b/kopete/protocols/oscar/liboscar/userinfotask.cpp new file mode 100644 index 00000000..a204c475 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/userinfotask.cpp @@ -0,0 +1,156 @@ +/* +Kopete Oscar Protocol +userinfotask.h - Handle sending and receiving info requests for users + +Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org> + +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 "userinfotask.h" + +#include <kdebug.h> + +#include "buffer.h" +#include "connection.h" +#include "transfer.h" +#include "userdetails.h" + + + +UserInfoTask::UserInfoTask( Task* parent ) +: Task( parent ) +{ +} + + +UserInfoTask::~UserInfoTask() +{ +} + +bool UserInfoTask::forMe( const Transfer * transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0002 && st->snacSubtype() == 0x0006 ) + { + if ( m_contactSequenceMap.find( st->snacRequest() ) == m_contactSequenceMap.end() ) + return false; + else + { + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Found sequence. taking packet" << endl; + return true; + } + } + else + return false; +} + +bool UserInfoTask::take( Transfer * transfer ) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + Q_UINT16 seq = 0; + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + if ( st ) + seq = st->snacRequest(); + + if ( seq != 0 ) + { + //AFAIK location info packets always have user info + Buffer* b = transfer->buffer(); + UserDetails ud; + ud.fill( b ); + m_sequenceInfoMap[seq] = ud; + emit gotInfo( seq ); + + QValueList<TLV> list = b->getTLVList(); + QValueList<TLV>::iterator it = list.begin(); + QString profile; + QString away; + for ( ; ( *it ); ++it ) + { + switch( ( *it ).type ) + { + case 0x0001: //profile text encoding + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "text encoding is " << QString( ( *it ).data )<< endl; + break; + case 0x0002: //profile text + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "The profile is '" << QString( ( *it ).data ) << "'" << endl; + profile = QString( ( *it ).data ); // aim always seems to use us-ascii encoding + emit receivedProfile( m_contactSequenceMap[seq], profile ); + break; + case 0x0003: //away message encoding + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Away message encoding is " << QString( ( *it ).data ) << endl; + break; + case 0x0004: //away message + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Away message is '" << QString( ( *it ).data ) << "'" << endl; + away = QString( (*it ).data ); // aim always seems to use us-ascii encoding + emit receivedAwayMessage( m_contactSequenceMap[seq], away ); + break; + case 0x0005: //capabilities + break; + default: //unknown + kdDebug(14151) << k_funcinfo << "Unknown user info type " << ( *it ).type << endl; + break; + }; + } + list.clear(); + } + setTransfer( 0 ); + return true; + } + return false; +} + +void UserInfoTask::onGo() +{ + if ( m_contactSequenceMap[m_seq].isEmpty() ) + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Info requested for empty contact!" << endl; + return; + } + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0002, 0x0005, 0, m_seq }; + Buffer* buffer = new Buffer(); + + buffer->addWord( m_typesSequenceMap[m_seq] ); + buffer->addBUIN( m_contactSequenceMap[m_seq].local8Bit() ); + + Transfer* t = createTransfer( f, s, buffer ); + send( t ); +} + +void UserInfoTask::requestInfoFor( const QString& contact, unsigned int types ) +{ + Q_UINT16 seq = client()->snacSequence(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting sequence " << seq << " for contact " << contact << endl; + m_contactSequenceMap[seq] = contact; + m_typesSequenceMap[seq] = types; + m_seq = seq; + onGo(); +} + +UserDetails UserInfoTask::getInfoFor( Q_UINT16 sequence ) const +{ + return m_sequenceInfoMap[sequence]; +} + + + +//kate: indent-mode csands; tab-width 4; + + +#include "userinfotask.moc" diff --git a/kopete/protocols/oscar/liboscar/userinfotask.h b/kopete/protocols/oscar/liboscar/userinfotask.h new file mode 100644 index 00000000..063eedd7 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/userinfotask.h @@ -0,0 +1,69 @@ +/* + Kopete Oscar Protocol + userinfotask.h - Handle sending and receiving info requests for users + + Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org> + + 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. * + * * + ************************************************************************* +*/ +#ifndef USERINFOTASK_H +#define USERINFOTASK_H + +#include "task.h" + +#include <qstring.h> +#include "userdetails.h" + +class Transfer; + +/** +Handles user information requests that are done via SNAC 02,05 and 02,06 + +@author Kopete Developers +*/ +class UserInfoTask : public Task +{ +Q_OBJECT +public: + UserInfoTask( Task* parent ); + ~UserInfoTask(); + + enum { Profile = 0x0001, General = 0x0002, AwayMessage = 0x0003, Capabilities = 0x0004 }; + + //! Task implementation + bool forMe( const Transfer* transfer ) const; + bool take( Transfer* transfer ); + void onGo(); + + void requestInfoFor( const QString& userId, unsigned int types ); + UserDetails getInfoFor( Q_UINT16 sequence ) const; + QString contactForSequence( Q_UINT16 sequence ) const; + + +signals: + void gotInfo( Q_UINT16 seqNumber ); + void receivedProfile( const QString& contact, const QString& profile ); + void receivedAwayMessage( const QString& contact, const QString& message ); + +private: + QMap<Q_UINT16, UserDetails> m_sequenceInfoMap; + QMap<Q_UINT16, QString> m_contactSequenceMap; + QMap<Q_UINT16, unsigned int> m_typesSequenceMap; + Q_UINT16 m_seq; + +}; + + + +#endif + +//kate: indent-mode csands; tab-width 4; diff --git a/kopete/protocols/oscar/liboscar/usersearchtask.cpp b/kopete/protocols/oscar/liboscar/usersearchtask.cpp new file mode 100644 index 00000000..3fd31010 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/usersearchtask.cpp @@ -0,0 +1,315 @@ +/* + Kopete Oscar Protocol + usersearchtask.cpp - Search for contacts + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + 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 "usersearchtask.h" + +#include "transfer.h" +#include "buffer.h" +#include "connection.h" + +UserSearchTask::UserSearchTask( Task* parent ) + : ICQTask( parent ) +{ +} + + +UserSearchTask::~UserSearchTask() +{ +} + +void UserSearchTask::onGo() +{ +} + +bool UserSearchTask::forMe( const Transfer* t ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( t ); + + if ( !st ) + return false; + + if ( st->snacService() != 0x0015 || st->snacSubtype() != 0x0003 ) + return false; + + Buffer buf( st->buffer()->buffer(), st->buffer()->length() ); + const_cast<UserSearchTask*>(this)->parseInitialData( buf ); + + if ( requestType() == 0x07da && ( requestSubType() == 0x01a4 || requestSubType() == 0x01ae ) ) + return true; + + return false; +} + +bool UserSearchTask::take( Transfer* t ) +{ + if ( forMe( t ) ) + { + setTransfer( t ); + + Q_UINT16 seq = 0; + SnacTransfer* st = dynamic_cast<SnacTransfer*>( t ); + if ( st ) + seq = st->snacRequest(); + + TLV tlv1 = transfer()->buffer()->getTLV(); + + if ( seq == 0 ) + { + setTransfer( 0 ); + return false; + } + + Buffer* buffer = new Buffer( tlv1.data, tlv1.length ); + ICQSearchResult result; + buffer->getLEWord(); // data chunk size + /*DWORD receiverUin =*/ buffer->getLEDWord(); // target uin + buffer->getLEWord(); // request type + buffer->getLEWord(); // request sequence number: 0x0002 + buffer->getLEWord(); // request subtype + + BYTE success = buffer->getByte(); // Success byte: always 0x0a + + if ( ( success == 0x32 ) || ( success == 0x14 ) || ( success == 0x1E ) ) + result.uin = 1; + else + result.fill( buffer ); + + m_results.append( result ); + + emit foundUser( result ); + + // Last user found reply + if ( requestSubType() == 0x01ae ) + { + int moreUsersCount = buffer->getLEDWord(); + emit searchFinished( moreUsersCount ); + setSuccess( 0, QString::null ); + } + setTransfer( 0 ); + } + return true; +} + +void UserSearchTask::searchUserByUIN( const QString& uin ) +{ + //create a new result list + m_type = UINSearch; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0015, 0x0002, 0x0000, client()->snacSequence() }; + + setRequestType( 0x07D0 ); //meta-information request + setRequestSubType( 0x0569 ); //subtype: META_SEARCH_BY_UIN + setSequence( f.sequence ); + Buffer* tlvdata = new Buffer(); + tlvdata->addLEWord( 0x0136 ); //tlv of type 0x0136 with length 4. all little endian + tlvdata->addLEWord( 0x0004 ); + tlvdata->addLEDWord( uin.toULong() ); + Buffer* buf = addInitialData( tlvdata ); + delete tlvdata; + + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + +void UserSearchTask::searchWhitePages( const ICQWPSearchInfo& info ) +{ + m_type = WhitepageSearch; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0015, 0x0002, 0x0000, client()->snacSequence() }; + + setRequestType( 0x07D0 ); + setRequestSubType( 0x055F ); + setSequence( f.sequence ); + Buffer* tlvData = new Buffer(); + /* + search.addLEWord(0x0533); // subtype: 1331 + + //LNTS FIRST + search.addLEWord(first.length()); + if(first.length()>0) + search.addLEString(first.latin1(), first.length()); + + // LNTS LAST + search.addLEWord(last.length()); + if(last.length()>0) + search.addLEString(last.latin1(), last.length()); + + // LNTS NICK + search.addLEWord(nick.length()); + if(nick.length()>0) + search.addLEString(nick.latin1(), nick.length()); + + // LNTS EMAIL + search.addLEWord(mail.length()); + if(mail.length()>0) + search.addLEString(mail.latin1(), mail.length()); + + // WORD.L MINAGE + search.addLEWord(minage); + + // WORD.L MAXAGE + search.addLEWord(maxage); + + // BYTE xx SEX 1=fem, 2=mal, 0=dontcare + if (sex==1) + search.addLEByte(0x01); + else if(sex==2) + search.addLEByte(0x02); + else + search.addLEByte(0x00); + + // BYTE xx LANGUAGE + search.addLEByte(lang); + + // LNTS CITY + search.addLEWord(city.length()); + if(city.length()>0) + search.addLEString(city.latin1(), city.length()); + + // LNTS STATE + search.addLEWord(state.length()); + if(state.length()>0) + search.addLEString(state.latin1(), state.length()); + + // WORD.L xx xx COUNTRY + search.addLEWord(country); + + // LNTS COMPANY + search.addLEWord(company.length()); + if(company.length()>0) + search.addLEString(company.latin1(), company.length()); + + // LNTS DEPARTMENT + search.addLEWord(department.length()); + if(department.length()>0) + search.addLEString(department.latin1(), department.length()); + + // LNTS POSITION + search.addLEWord(position.length()); + if(position.length()>0) + search.addLEString(position.latin1(), position.length()); + + // BYTE xx OCCUPATION + search.addLEByte(occupation); + + //WORD.L xx xx PAST + search.addLEWord(0x0000); + + //LNTS PASTDESC - The past description to search for. + search.addLEWord(0x0000); + + // WORD.L xx xx INTERESTS - The interests category to search for. + search.addLEWord(0x0000); + + // LNTS INTERDESC - The interests description to search for. + search.addLEWord(0x0000); + + // WORD.L xx xx AFFILIATION - The affiliation to search for. + search.addLEWord(0x0000); + + // LNTS AFFIDESC - The affiliation description to search for. + search.addLEWord(0x0000); + + // WORD.L xx xx HOMEPAGE - The home page category to search for. + search.addLEWord(0x0000); + + // LNTS HOMEDESC - The home page description to search for. + search.addLEWord(0x0000); + + // BYTE xx ONLINE 1=online onliners, 0=dontcare + if(onlineOnly) + search.addLEByte(0x01); + else + search.addLEByte(0x00); + */ + if ( !info.firstName.isEmpty() ) + { + Buffer bufFileName; + bufFileName.addLEWord( info.firstName.length() ); + bufFileName.addLEString( info.firstName, info.firstName.length() ); + tlvData->addLETLV( 0x0140, bufFileName.length(), bufFileName.buffer() ); + } + + if ( !info.lastName.isEmpty() ) + { + Buffer bufLastName; + bufLastName.addLEWord( info.lastName.length() ); + bufLastName.addLEString( info.lastName, info.lastName.length() ); + tlvData->addLETLV( 0x014A, bufLastName.length(), bufLastName.buffer() ); + } + + if ( !info.nickName.isEmpty() ) + { + Buffer bufNickName; + bufNickName.addLEWord( info.nickName.length() ); + bufNickName.addLEString( info.nickName, info.nickName.length() ); + tlvData->addLETLV( 0x0154, bufNickName.length(), bufNickName.buffer() ); + } + + if ( !info.email.isEmpty() ) + { + Buffer bufEmail; + bufEmail.addLEWord( info.email.length() ); + bufEmail.addLEString( info.email, info.email.length() ); + tlvData->addLETLV( 0x015E, bufEmail.length(), bufEmail.buffer() ); + } + + if ( info.age > 0 ) + { + Buffer bufAge; + bufAge.addWord( info.age ); + bufAge.addWord( info.age ); + tlvData->addLETLV( 0x0168, bufAge.length(), bufAge.buffer() ); + } + + if ( info.gender > 0 ) + tlvData->addLETLV8( 0x017C, info.gender ); + + if ( info.language > 0 ) + tlvData->addLETLV16( 0x0186, info.language ); + + if ( info.country > 0 ) + tlvData->addLETLV16( 0x01A4, info.country ); + + if ( !info.city.isEmpty() ) + { + Buffer bufCity; + bufCity.addLEWord( info.city.length() ); + bufCity.addLEString( info.city, info.city.length() ); + tlvData->addLETLV( 0x0190, bufCity.length(), bufCity.buffer() ); + } + + if ( info.occupation > 0 ) + tlvData->addLETLV16( 0x01CC, info.occupation ); + + if ( info.onlineOnly ) + tlvData->addLETLV8( 0x0230, 0x01 ); + + Buffer* buf = addInitialData( tlvData ); + delete tlvData; //we're done with it + + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + + +#include "usersearchtask.moc" + +//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/liboscar/usersearchtask.h b/kopete/protocols/oscar/liboscar/usersearchtask.h new file mode 100644 index 00000000..239efe36 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/usersearchtask.h @@ -0,0 +1,61 @@ +/* + Kopete Oscar Protocol + usersearchtask.h - Search for contacts + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef USERSEARCHTASK_H +#define USERSEARCHTASK_H + +#include "icqtask.h" +#include <qstring.h> +#include "icquserinfo.h" + +/** +Search for contacts + +@author Kopete Developers +*/ +class UserSearchTask : public ICQTask +{ +Q_OBJECT +public: + UserSearchTask( Task* parent ); + + ~UserSearchTask(); + + enum SearchType { UINSearch, WhitepageSearch }; + + virtual void onGo(); + virtual bool forMe( const Transfer* t ) const; + virtual bool take( Transfer* t ); + + /** Search by UIN */ + void searchUserByUIN( const QString& uin ); + + void searchWhitePages( const ICQWPSearchInfo& info ); + +signals: + void foundUser( const ICQSearchResult& result ); + void searchFinished( int ); + +private: + QValueList<ICQSearchResult> m_results; + QString m_uin; + Q_UINT16 m_seq; + SearchType m_type; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/warningtask.cpp b/kopete/protocols/oscar/liboscar/warningtask.cpp new file mode 100644 index 00000000..56051dc8 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/warningtask.cpp @@ -0,0 +1,96 @@ +/* + Kopete Oscar Protocol + warningtask.cpp - send warnings to aim users + + Copyright (c) 2005 by Matt Rogers <mattr@kde.org> + + 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 "warningtask.h" + +#include <qstring.h> +#include <kdebug.h> +#include "transfer.h" +#include "connection.h" + +WarningTask::WarningTask( Task* parent ): Task( parent ) +{ +} + + +WarningTask::~WarningTask() +{ +} + +void WarningTask::setContact( const QString& contact ) +{ + m_contact = contact; +} + +void WarningTask::setAnonymous( bool anon ) +{ + m_sendAnon = anon; +} + +bool WarningTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + if ( st->snacService() == 0x04 && st->snacSubtype() == 0x09 && st->snacRequest() == m_sequence ) + return true; + + return false; +} + +bool WarningTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + Buffer *b = transfer->buffer(); + m_increase = b->getWord(); + m_newLevel = b->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got warning ack for " << m_contact << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Warning level increased " << m_increase + << " to " << m_newLevel << endl; + emit userWarned( m_contact, m_increase, m_newLevel ); + setSuccess( 0, QString::null ); + setTransfer( 0 ); + return true; + } + else + { + setError( 0, QString::null ); + return false; + } +} + +void WarningTask::onGo() +{ + FLAP f = { 0x0002, 0, 0 }; + SNAC s = { 0x0004, 0x0008, 0x0000, client()->snacSequence() }; + Buffer* b = new Buffer; + if ( m_sendAnon ) + b->addWord( 0x0001 ); + else + b->addWord( 0x0000 ); + + b->addBUIN( m_contact.latin1() ); //TODO i should probably check the encoding here. nyeh + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4; + +#include "warningtask.moc" diff --git a/kopete/protocols/oscar/liboscar/warningtask.h b/kopete/protocols/oscar/liboscar/warningtask.h new file mode 100644 index 00000000..1280fab9 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/warningtask.h @@ -0,0 +1,59 @@ +/* + Kopete Oscar Protocol + warningtask.cpp - send warnings to aim users + + Copyright (c) 2005 by Matt Rogers <mattr@kde.org> + + 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. * + * * + ************************************************************************* +*/ + +#ifndef WARNINGTASK_H +#define WARNINGTASK_H + +#include "task.h" +#include <qmap.h> +#include "oscartypes.h" + +/** +@author Matt Rogers +*/ +class WarningTask : public Task +{ +Q_OBJECT +public: + WarningTask( Task* parent ); + ~WarningTask(); + + void setContact( const QString& contact ); + void setAnonymous( bool anon ); + + WORD levelIncrease(); + WORD newLevel(); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + +signals: + void userWarned( const QString&, Q_UINT16, Q_UINT16 ); + +private: + QString m_contact; + bool m_sendAnon; + WORD m_sequence; + WORD m_increase; + WORD m_newLevel; +}; + +#endif + +//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4; diff --git a/kopete/protocols/oscar/oscaraccount.cpp b/kopete/protocols/oscar/oscaraccount.cpp new file mode 100644 index 00000000..353e3201 --- /dev/null +++ b/kopete/protocols/oscar/oscaraccount.cpp @@ -0,0 +1,914 @@ +/* + oscaraccount.cpp - Oscar Account Class + + Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu> + Copyright (c) 2002 by Chris TenHarmsel <tenharmsel@staticmethod.net> + 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 "oscaraccount.h" + +#include "kopetepassword.h" +#include "kopeteprotocol.h" +#include "kopeteaway.h" +#include "kopetemetacontact.h" +#include "kopetecontactlist.h" +#include "kopeteawaydialog.h" +#include "kopetegroup.h" +#include "kopeteuiglobal.h" +#include "kopetecontactlist.h" +#include "kopetecontact.h" +#include "kopetechatsession.h" + +#include <assert.h> + +#include <qapplication.h> +#include <qregexp.h> +#include <qstylesheet.h> +#include <qtimer.h> +#include <qptrlist.h> +#include <qtextcodec.h> +#include <qimage.h> +#include <qfile.h> + +#include <kdebug.h> +#include <kconfig.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpassivepopup.h> +#include <kstandarddirs.h> + +#include "client.h" +#include "connection.h" +#include "oscartypeclasses.h" +#include "oscarmessage.h" +#include "oscarutils.h" +#include "oscarclientstream.h" +#include "oscarconnector.h" +#include "ssimanager.h" +#include "oscarlistnonservercontacts.h" +#include "oscarversionupdater.h" + +class OscarAccountPrivate : public Client::CodecProvider +{ + // Backreference + OscarAccount& account; +public: + OscarAccountPrivate( OscarAccount& a ): account( a ) {} + + //The liboscar hook for the account + Client* engine; + + Q_UINT32 ssiLastModTime; + + //contacts waiting on SSI add ack and their metacontact + QMap<QString, Kopete::MetaContact*> addContactMap; + + //contacts waiting on their group to be added + QMap<QString, QString> contactAddQueue; + QMap<QString, QString> contactChangeQueue; + + OscarListNonServerContacts* olnscDialog; + + unsigned int versionUpdaterStamp; + bool versionAlreadyUpdated; + + virtual QTextCodec* codecForContact( const QString& contactName ) const + { + return account.contactCodec( Oscar::normalize( contactName ) ); + } + + virtual QTextCodec* codecForAccount() const + { + return account.defaultCodec(); + } +}; + +OscarAccount::OscarAccount(Kopete::Protocol *parent, const QString &accountID, const char *name, bool isICQ) +: Kopete::PasswordedAccount( parent, accountID, isICQ ? 8 : 16, name ) +{ + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << " accountID='" << accountID << + "', isICQ=" << isICQ << endl; + + d = new OscarAccountPrivate( *this ); + d->engine = new Client( this ); + d->engine->setIsIcq( isICQ ); + + d->versionAlreadyUpdated = false; + d->versionUpdaterStamp = OscarVersionUpdater::self()->stamp(); + if ( isICQ ) + d->engine->setVersion( OscarVersionUpdater::self()->getICQVersion() ); + else + d->engine->setVersion( OscarVersionUpdater::self()->getAIMVersion() ); + + d->engine->setCodecProvider( d ); + d->olnscDialog = 0L; + QObject::connect( d->engine, SIGNAL( loggedIn() ), this, SLOT( loginActions() ) ); + QObject::connect( d->engine, SIGNAL( messageReceived( const Oscar::Message& ) ), + this, SLOT( messageReceived(const Oscar::Message& ) ) ); + QObject::connect( d->engine, SIGNAL( socketError( int, const QString& ) ), + this, SLOT( slotSocketError( int, const QString& ) ) ); + QObject::connect( d->engine, SIGNAL( taskError( const Oscar::SNAC&, int, bool ) ), + this, SLOT( slotTaskError( const Oscar::SNAC&, int, bool ) ) ); + QObject::connect( d->engine, SIGNAL( userStartedTyping( const QString& ) ), + this, SLOT( userStartedTyping( const QString& ) ) ); + QObject::connect( d->engine, SIGNAL( userStoppedTyping( const QString& ) ), + this, SLOT( userStoppedTyping( const QString& ) ) ); + QObject::connect( d->engine, SIGNAL( iconNeedsUploading() ), + this, SLOT( slotSendBuddyIcon() ) ); +} + +OscarAccount::~OscarAccount() +{ + OscarAccount::disconnect(); + delete d; +} + +Client* OscarAccount::engine() +{ + return d->engine; +} + +void OscarAccount::logOff( Kopete::Account::DisconnectReason reason ) +{ + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "accountId='" << accountId() << "'" << endl; + //disconnect the signals + Kopete::ContactList* kcl = Kopete::ContactList::self(); + QObject::disconnect( kcl, SIGNAL( groupRenamed( Kopete::Group*, const QString& ) ), + this, SLOT( kopeteGroupRenamed( Kopete::Group*, const QString& ) ) ); + QObject::disconnect( kcl, SIGNAL( groupRemoved( Kopete::Group* ) ), + this, SLOT( kopeteGroupRemoved( Kopete::Group* ) ) ); + QObject::disconnect( d->engine->ssiManager(), SIGNAL( contactAdded( const Oscar::SSI& ) ), + this, SLOT( ssiContactAdded( const Oscar::SSI& ) ) ); + QObject::disconnect( d->engine->ssiManager(), SIGNAL( groupAdded( const Oscar::SSI& ) ), + this, SLOT( ssiGroupAdded( const Oscar::SSI& ) ) ); + QObject::disconnect( d->engine->ssiManager(), SIGNAL( groupUpdated( const Oscar::SSI& ) ), + this, SLOT( ssiGroupUpdated( const Oscar::SSI& ) ) ); + QObject::disconnect( d->engine->ssiManager(), SIGNAL( contactUpdated( const Oscar::SSI& ) ), + this, SLOT( ssiContactUpdated( const Oscar::SSI& ) ) ); + + d->engine->close(); + myself()->setOnlineStatus( Kopete::OnlineStatus::Offline ); + + d->contactAddQueue.clear(); + d->contactChangeQueue.clear(); + + disconnected( reason ); +} + +void OscarAccount::disconnect() +{ + logOff( Kopete::Account::Manual ); +} + +bool OscarAccount::passwordWasWrong() +{ + return password().isWrong(); +} + +void OscarAccount::loginActions() +{ + password().setWrong( false ); + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "processing SSI list" << endl; + processSSIList(); + + //start a chat nav connection + if ( !engine()->isIcq() ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "sending request for chat nav service" << endl; + d->engine->requestServerRedirect( 0x000D ); + } + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending request for icon service" << endl; + d->engine->requestServerRedirect( 0x0010 ); + +} + +void OscarAccount::processSSIList() +{ + //disconnect signals so we don't attempt to add things to SSI! + Kopete::ContactList* kcl = Kopete::ContactList::self(); + QObject::disconnect( kcl, SIGNAL( groupRenamed( Kopete::Group*, const QString& ) ), + this, SLOT( kopeteGroupRenamed( Kopete::Group*, const QString& ) ) ); + QObject::disconnect( kcl, SIGNAL( groupRemoved( Kopete::Group* ) ), + this, SLOT( kopeteGroupRemoved( Kopete::Group* ) ) ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + + SSIManager* listManager = d->engine->ssiManager(); + + //first add groups + QValueList<SSI> groupList = listManager->groupList(); + QValueList<SSI>::const_iterator git = groupList.constBegin(); + QValueList<SSI>::const_iterator listEnd = groupList.constEnd(); + //the protocol dictates that there is at least one group that has contacts + //so i don't have to check for an empty group list + + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Adding " << groupList.count() << " groups to contact list" << endl; + for( ; git != listEnd; ++git ) + { //add all the groups. + kdDebug( OSCAR_GEN_DEBUG ) << k_funcinfo << "Adding SSI group'" << ( *git ).name() + << "' to the kopete contact list" << endl; + kcl->findGroup( ( *git ).name() ); + } + + //then add contacts + QValueList<SSI> contactList = listManager->contactList(); + QValueList<SSI>::const_iterator bit = contactList.constBegin(); + QValueList<SSI>::const_iterator blistEnd = contactList.constEnd(); + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Adding " << contactList.count() << " contacts to contact list" << endl; + for ( ; bit != blistEnd; ++bit ) + { + SSI groupForAdd = listManager->findGroup( ( *bit ).gid() ); + Kopete::Group* group; + if ( groupForAdd.isValid() ) + group = kcl->findGroup( groupForAdd.name() ); //add if not present + else + group = kcl->findGroup( i18n( "Buddies" ) ); + + kdDebug( OSCAR_GEN_DEBUG ) << k_funcinfo << "Adding contact '" << ( *bit ).name() << "' to kopete list in group " << + group->displayName() << endl; + OscarContact* oc = dynamic_cast<OscarContact*>( contacts()[( *bit ).name()] ); + if ( oc ) + { + Oscar::SSI item = ( *bit ); + oc->setSSIItem( item ); + } + else + addContact( ( *bit ).name(), QString::null, group, Kopete::Account::DontChangeKABC ); + } + + QObject::connect( kcl, SIGNAL( groupRenamed( Kopete::Group*, const QString& ) ), + this, SLOT( kopeteGroupRenamed( Kopete::Group*, const QString& ) ) ); + QObject::connect( kcl, SIGNAL( groupRemoved( Kopete::Group* ) ), + this, SLOT( kopeteGroupRemoved( Kopete::Group* ) ) ); + QObject::connect( listManager, SIGNAL( contactAdded( const Oscar::SSI& ) ), + this, SLOT( ssiContactAdded( const Oscar::SSI& ) ) ); + QObject::connect( listManager, SIGNAL( groupAdded( const Oscar::SSI& ) ), + this, SLOT( ssiGroupAdded( const Oscar::SSI& ) ) ); + QObject::connect( listManager, SIGNAL( groupUpdated( const Oscar::SSI& ) ), + this, SLOT( ssiGroupUpdated( const Oscar::SSI& ) ) ); + QObject::connect( listManager, SIGNAL( contactUpdated( const Oscar::SSI& ) ), + this, SLOT( ssiContactUpdated( const Oscar::SSI& ) ) ); + + //TODO: check the kopete contact list and handle non server side contacts appropriately. + QDict<Kopete::Contact> nonServerContacts = contacts(); + QDictIterator<Kopete::Contact> it( nonServerContacts ); + QStringList nonServerContactList; + for ( ; it.current(); ++it ) + { + OscarContact* oc = dynamic_cast<OscarContact*>( ( *it ) ); + if ( !oc ) + continue; + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << oc->contactId() << " contact ssi type: " << oc->ssiItem().type() << endl; + if ( !oc->isOnServer() ) + nonServerContactList.append( ( *it )->contactId() ); + } + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "the following contacts are not on the server side list" + << nonServerContactList << endl; + bool showMissingContactsDialog = configGroup()->readBoolEntry(QString::fromLatin1("ShowMissingContactsDialog"), true); + if ( !nonServerContactList.isEmpty() && showMissingContactsDialog ) + { + d->olnscDialog = new OscarListNonServerContacts( Kopete::UI::Global::mainWidget() ); + QObject::connect( d->olnscDialog, SIGNAL( closing() ), + this, SLOT( nonServerAddContactDialogClosed() ) ); + d->olnscDialog->addContacts( nonServerContactList ); + d->olnscDialog->show(); + } +} + +void OscarAccount::nonServerAddContactDialogClosed() +{ + if ( !d->olnscDialog ) + return; + + if ( d->olnscDialog->result() == QDialog::Accepted ) + { + //start adding contacts + kdDebug(OSCAR_GEN_DEBUG) << "adding non server contacts to the contact list" << endl; + //get the contact list. get the OscarContact object, then the group + //check if the group is on ssi, if not, add it + //if so, add the contact. + QStringList offliners = d->olnscDialog->nonServerContactList(); + QStringList::iterator it, itEnd = offliners.end(); + for ( it = offliners.begin(); it != itEnd; ++it ) + { + OscarContact* oc = dynamic_cast<OscarContact*>( contacts()[( *it )] ); + if ( !oc ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "no OscarContact object available for" + << ( *it ) << endl; + continue; + } + + Kopete::MetaContact* mc = oc->metaContact(); + if ( !mc ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "no metacontact object available for" + << ( oc->contactId() ) << endl; + continue; + } + + Kopete::Group* group = mc->groups().first(); + if ( !group ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "no metacontact object available for" + << ( oc->contactId() ) << endl; + continue; + } + + addContactToSSI( ( *it ), group->displayName(), true ); + } + + + } + + bool showOnce = d->olnscDialog->onlyShowOnce(); + configGroup()->writeEntry( QString::fromLatin1("ShowMissingContactsDialog") , !showOnce); + configGroup()->sync(); + + d->olnscDialog->delayedDestruct(); + d->olnscDialog = 0L; +} + +void OscarAccount::slotGoOffline() +{ + OscarAccount::disconnect(); +} + +void OscarAccount::slotGoOnline() +{ + //do nothing +} + +void OscarAccount::kopeteGroupRemoved( Kopete::Group* group ) +{ + if ( isConnected() ) + d->engine->removeGroup( group->displayName() ); +} + +void OscarAccount::kopeteGroupAdded( Kopete::Group* group ) +{ + if ( isConnected() ) + d->engine->addGroup( group->displayName() ); +} + +void OscarAccount::kopeteGroupRenamed( Kopete::Group* group, const QString& oldName ) +{ + if ( isConnected() ) + d->engine->renameGroup( oldName, group->displayName() ); +} + +void OscarAccount::messageReceived( const Oscar::Message& message ) +{ + //the message isn't for us somehow + if ( Oscar::normalize( message.receiver() ) != Oscar::normalize( accountId() ) ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got a message but we're not the receiver: " + << message.textArray() << endl; + return; + } + + /* Logic behind this: + * If we don't have the contact yet, create it as a temporary + * Create the message manager + * Get the sanitized message back + * Append to the chat window + */ + QString sender = Oscar::normalize( message.sender() ); + if ( !contacts()[sender] ) + { + kdDebug(OSCAR_RAW_DEBUG) << "Adding '" << sender << "' as temporary contact" << endl; + addContact( sender, QString::null, 0, Kopete::Account::Temporary ); + } + + OscarContact* ocSender = static_cast<OscarContact *> ( contacts()[sender] ); //should exist now + + if ( !ocSender ) + { + kdWarning(OSCAR_RAW_DEBUG) << "Temporary contact creation failed for '" + << sender << "'! Discarding message: " << message.textArray() << endl; + return; + } + else + { + if ( ( message.properties() & Oscar::Message::WWP ) == Oscar::Message::WWP ) + ocSender->setNickName( i18n("ICQ Web Express") ); + if ( ( message.properties() & Oscar::Message::EMail ) == Oscar::Message::EMail ) + ocSender->setNickName( i18n("ICQ Email Express") ); + } + + Kopete::ChatSession* chatSession = ocSender->manager( Kopete::Contact::CanCreate ); + chatSession->receivedTypingMsg( ocSender, false ); //person is done typing + + + //decode message + QString realText( message.text( contactCodec( ocSender ) ) ); + + //sanitize; + QString sanitizedMsg = sanitizedMessage( realText ); + + Kopete::ContactPtrList me; + me.append( myself() ); + Kopete::Message chatMessage( message.timestamp(), ocSender, me, sanitizedMsg, + Kopete::Message::Inbound, Kopete::Message::RichText ); + + chatSession->appendMessage( chatMessage ); +} + + +void OscarAccount::setServerAddress(const QString &server) +{ + configGroup()->writeEntry( QString::fromLatin1( "Server" ), server ); +} + +void OscarAccount::setServerPort(int port) +{ + if ( port > 0 ) + configGroup()->writeEntry( QString::fromLatin1( "Port" ), port ); + else //set to default 5190 + configGroup()->writeEntry( QString::fromLatin1( "Port" ), 5190 ); +} + +QTextCodec* OscarAccount::defaultCodec() const +{ + return QTextCodec::codecForMib( configGroup()->readNumEntry( "DefaultEncoding", 4 ) ); +} + +QTextCodec* OscarAccount::contactCodec( const OscarContact* contact ) const +{ + if ( contact ) + return contact->contactCodec(); + else + return defaultCodec(); +} + +QTextCodec* OscarAccount::contactCodec( const QString& contactName ) const +{ + // XXX Need const_cast because Kopete::Account::contacts() + // XXX method is not const for some strange reason. + OscarContact* contact = static_cast<OscarContact *> ( const_cast<OscarAccount *>(this)->contacts()[contactName] ); + return contactCodec( contact ); +} + +void OscarAccount::setBuddyIcon( KURL url ) +{ + if ( url.path().isEmpty() ) + { + myself()->removeProperty( Kopete::Global::Properties::self()->photo() ); + } + else + { + QImage image( url.path() ); + if ( image.isNull() ) + return; + + const QSize size = ( d->engine->isIcq() ) ? QSize( 52, 64 ) : QSize( 48, 48 ); + + image = image.smoothScale( size, QImage::ScaleMax ); + if( image.width() > size.width()) + image = image.copy( ( image.width() - size.width() ) / 2, 0, size.width(), image.height() ); + + if( image.height() > size.height()) + image = image.copy( 0, ( image.height() - size.height() ) / 2, image.width(), size.height() ); + + QString newlocation( locateLocal( "appdata", "oscarpictures/"+ accountId() + ".jpg" ) ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Saving buddy icon: " << newlocation << endl; + if ( !image.save( newlocation, "JPEG" ) ) + return; + + myself()->setProperty( Kopete::Global::Properties::self()->photo() , newlocation ); + } + + emit buddyIconChanged(); +} + +bool OscarAccount::addContactToSSI( const QString& contactName, const QString& groupName, bool autoAddGroup ) +{ + SSIManager* listManager = d->engine->ssiManager(); + if ( !listManager->findGroup( groupName ) ) + { + if ( !autoAddGroup ) + return false; + + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "adding non-existant group " + << groupName << endl; + + d->contactAddQueue[Oscar::normalize( contactName )] = groupName; + d->engine->addGroup( groupName ); + } + else + { + d->engine->addContact( contactName, groupName ); + } + + return true; +} + +bool OscarAccount::changeContactGroupInSSI( const QString& contact, const QString& newGroupName, bool autoAddGroup ) +{ + SSIManager* listManager = d->engine->ssiManager(); + if ( !listManager->findGroup( newGroupName ) ) + { + if ( !autoAddGroup ) + return false; + + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "adding non-existant group " + << newGroupName << endl; + + d->contactChangeQueue[Oscar::normalize( contact )] = newGroupName; + d->engine->addGroup( newGroupName ); + } + else + { + d->engine->changeContactGroup( contact, newGroupName ); + } + + return true; +} + +Connection* OscarAccount::setupConnection( const QString& server, uint port ) +{ + //set up the connector + KNetworkConnector* knc = new KNetworkConnector( 0 ); + knc->setOptHostPort( server, port ); + + //set up the clientstream + ClientStream* cs = new ClientStream( knc, 0 ); + + Connection* c = new Connection( knc, cs, "AUTHORIZER" ); + c->setClient( d->engine ); + + return c; +} + + +bool OscarAccount::createContact(const QString &contactId, + Kopete::MetaContact *parentContact) +{ + /* We're not even online or connecting + * (when getting server contacts), so don't bother + */ + if ( !engine()->isActive() ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Can't add contact, we are offline!" << endl; + return false; + } + + /* Logic for SSI additions + If the contact is temporary, no SSI addition at all. Just create the contact and be done with it + If the contact is not temporary, we need to do the following: + 1. Check if contact already exists in the SSI manager, if so, just create the contact + 2. If contact doesn't exist: + 2.a. create group on SSI if needed + 2.b. create contact on SSI + 2.c. create kopete contact + */ + + QValueList<TLV> dummyList; + if ( parentContact->isTemporary() ) + { + SSI tempItem( contactId, 0, 0, 0xFFFF, dummyList, 0 ); + return createNewContact( contactId, parentContact, tempItem ); + } + + SSI ssiItem = d->engine->ssiManager()->findContact( contactId ); + if ( ssiItem ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Have new SSI entry. Finding contact" << endl; + if ( contacts()[ssiItem.name()] ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Found contact in list. Updating SSI item" << endl; + OscarContact* oc = static_cast<OscarContact*>( contacts()[ssiItem.name()] ); + oc->setSSIItem( ssiItem ); + return true; + } + else + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Didn't find contact in list, creating new contact" << endl; + return createNewContact( contactId, parentContact, ssiItem ); + } + } + else + { //new contact, check temporary, if temporary, don't add to SSI. otherwise, add. + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "New contact '" << contactId << "' not in SSI." + << " Creating new contact" << endl; + + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Adding " << contactId << " to server side list" << endl; + + QString groupName; + Kopete::GroupList kopeteGroups = parentContact->groups(); //get the group list + + if ( kopeteGroups.isEmpty() || kopeteGroups.first() == Kopete::Group::topLevel() ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Contact with NO group. " << "Adding to group 'Buddies'" << endl; + groupName = i18n("Buddies"); + } + else + { + //apparently kopeteGroups.first() can be invalid. Attempt to prevent + //crashes in SSIData::findGroup(const QString& name) + groupName = kopeteGroups.first() ? kopeteGroups.first()->displayName() : i18n("Buddies"); + + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Contact with group." << " No. of groups = " << kopeteGroups.count() << + " Name of first group = " << groupName << endl; + } + + if( groupName.isEmpty() ) + { // emergency exit, should never occur + kdWarning(OSCAR_GEN_DEBUG) << k_funcinfo << "Could not add contact because no groupname was given" << endl; + return false; + } + + d->addContactMap[Oscar::normalize( contactId )] = parentContact; + addContactToSSI( Oscar::normalize( contactId ), groupName, true ); + return true; + } +} + +void OscarAccount::updateVersionUpdaterStamp() +{ + d->versionUpdaterStamp = OscarVersionUpdater::self()->stamp(); +} + +void OscarAccount::ssiContactAdded( const Oscar::SSI& item ) +{ + if ( d->addContactMap.contains( Oscar::normalize( item.name() ) ) ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Received confirmation from server. adding " << item.name() + << " to the contact list" << endl; + createNewContact( item.name(), d->addContactMap[Oscar::normalize( item.name() )], item ); + } + else if ( contacts()[item.name()] ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Received confirmation from server. modifying " << item.name() << endl; + OscarContact* oc = static_cast<OscarContact*>( contacts()[item.name()] ); + oc->setSSIItem( item ); + } + else + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Got addition for contact we weren't waiting on" << endl; +} + +void OscarAccount::ssiGroupAdded( const Oscar::SSI& item ) +{ + //check the contact add queue for any contacts matching the + //group name we just added + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Looking for contacts to be added in group " << item.name() << endl; + QMap<QString,QString>::iterator it; + for ( it = d->contactAddQueue.begin(); it != d->contactAddQueue.end(); ++it ) + { + if ( Oscar::normalize( it.data() ) == Oscar::normalize( item.name() ) ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "starting delayed add of contact '" << it.key() + << "' to group " << item.name() << endl; + + d->engine->addContact( Oscar::normalize( it.key() ), item.name() ); + d->contactAddQueue.remove( it ); + } + } + + for ( it = d->contactChangeQueue.begin(); it != d->contactChangeQueue.end(); ++it ) + { + if ( Oscar::normalize( it.data() ) == Oscar::normalize( item.name() ) ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "starting delayed change of contact '" << it.key() + << "' to group " << item.name() << endl; + + d->engine->changeContactGroup( it.key(), item.name() ); + d->contactChangeQueue.remove( it ); + } + } +} + +void OscarAccount::ssiContactUpdated( const Oscar::SSI& item ) +{ + Kopete::Contact* contact = contacts()[item.name()]; + if ( !contact ) + return; + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Updating SSI Item" << endl; + OscarContact* oc = static_cast<OscarContact*>( contact ); + oc->setSSIItem( item ); + } +} + +void OscarAccount::userStartedTyping( const QString & contact ) +{ + Kopete::Contact * ct = contacts()[ Oscar::normalize( contact ) ]; + if ( ct && contact != accountId() ) + { + OscarContact * oc = static_cast<OscarContact *>( ct ); + oc->startedTyping(); + } +} + +void OscarAccount::userStoppedTyping( const QString & contact ) +{ + Kopete::Contact * ct = contacts()[ Oscar::normalize( contact ) ]; + if ( ct && contact != accountId() ) + { + OscarContact * oc = static_cast<OscarContact *>( ct ); + oc->stoppedTyping(); + } +} + +void OscarAccount::slotSocketError( int errCode, const QString& errString ) +{ + Q_UNUSED( errCode ); + KPassivePopup::message( i18n( "account has been disconnected", "%1 disconnected" ).arg( accountId() ), + errString, + myself()->onlineStatus().protocolIcon(), + Kopete::UI::Global::mainWidget() ); + logOff( Kopete::Account::ConnectionReset ); +} + +void OscarAccount::slotTaskError( const Oscar::SNAC& s, int code, bool fatal ) +{ + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "error recieived from task" << endl; + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "service: " << s.family + << " subtype: " << s.subtype << " code: " << code << endl; + + QString message; + if ( s.family == 0 && s.subtype == 0 ) + { + message = getFLAPErrorMessage( code ); + KPassivePopup::message( i18n( "account has been disconnected", "%1 disconnected" ).arg( accountId() ), + message, myself()->onlineStatus().protocolIcon(), + Kopete::UI::Global::mainWidget() ); + switch ( code ) + { + case 0x0000: + logOff( Kopete::Account::Unknown ); + break; + case 0x0004: + case 0x0005: + logOff( Kopete::Account::BadPassword ); + break; + case 0x0007: + case 0x0008: + case 0x0009: + case 0x0011: + logOff( Kopete::Account::BadUserName ); + break; + default: + logOff( Kopete::Account::Manual ); + } + return; + } + if ( !fatal ) + message = i18n("There was an error in the protocol handling; it was not fatal, so you will not be disconnected."); + else + message = i18n("There was an error in the protocol handling; automatic reconnection occurring."); + + KPassivePopup::message( i18n("OSCAR Protocol error"), message, myself()->onlineStatus().protocolIcon(), + Kopete::UI::Global::mainWidget() ); + if ( fatal ) + logOff( Kopete::Account::ConnectionReset ); +} + +void OscarAccount::slotSendBuddyIcon() +{ + //need to disconnect because we could end up with many connections + QObject::disconnect( engine(), SIGNAL( iconServerConnected() ), this, SLOT( slotSendBuddyIcon() ) ); + QString photoPath = myself()->property( Kopete::Global::Properties::self()->photo() ).value().toString(); + if ( photoPath.isEmpty() ) + return; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << photoPath << endl; + QFile iconFile( photoPath ); + + if ( iconFile.open( IO_ReadOnly ) ) + { + if ( !engine()->hasIconConnection() ) + { + //will send icon when we connect to icon server + QObject::connect( engine(), SIGNAL( iconServerConnected() ), + this, SLOT( slotSendBuddyIcon() ) ); + return; + } + QByteArray imageData = iconFile.readAll(); + engine()->sendBuddyIcon( imageData ); + } +} + +QString OscarAccount::getFLAPErrorMessage( int code ) +{ + bool isICQ = d->engine->isIcq(); + QString acctType = isICQ ? i18n("ICQ") : i18n("AIM"); + QString acctDescription = isICQ ? i18n("ICQ user id", "UIN") : i18n("AIM user id", "screen name"); + QString reason; + //FLAP errors are always fatal + //negative codes are things added by liboscar developers + //to indicate generic errors in the task + switch ( code ) + { + case 0x0001: + if ( isConnected() ) // multiple logins (on same UIN) + { + reason = i18n( "You have logged in more than once with the same %1," \ + " account %2 is now disconnected.") + .arg( acctDescription ).arg( accountId() ); + } + else // error while logging in + { + reason = i18n( "Sign on failed because either your %1 or " \ + "password are invalid. Please check your settings for account %2.") + .arg( acctDescription ).arg( accountId() ); + + } + break; + case 0x0002: // Service temporarily unavailable + case 0x0014: // Reservation map error + reason = i18n("The %1 service is temporarily unavailable. Please try again later.") + .arg( acctType ); + break; + case 0x0004: // Incorrect nick or password, re-enter + case 0x0005: // Mismatch nick or password, re-enter + reason = i18n("Could not sign on to %1 with account %2 because the " \ + "password was incorrect.").arg( acctType ).arg( accountId() ); + break; + case 0x0007: // non-existant ICQ# + case 0x0008: // non-existant ICQ# + reason = i18n("Could not sign on to %1 with nonexistent account %2.") + .arg( acctType ).arg( accountId() ); + break; + case 0x0009: // Expired account + reason = i18n("Sign on to %1 failed because your account %2 expired.") + .arg( acctType ).arg( accountId() ); + break; + case 0x0011: // Suspended account + reason = i18n("Sign on to %1 failed because your account %2 is " \ + "currently suspended.").arg( acctType ).arg( accountId() ); + break; + case 0x0015: // too many clients from same IP + case 0x0016: // too many clients from same IP + case 0x0017: // too many clients from same IP (reservation) + reason = i18n("Could not sign on to %1 as there are too many clients" \ + " from the same computer.").arg( acctType ); + break; + case 0x0018: // rate exceeded (turboing) + if ( isConnected() ) + { + reason = i18n("Account %1 was blocked on the %2 server for" \ + " sending messages too quickly." \ + " Wait ten minutes and try again." \ + " If you continue to try, you will" \ + " need to wait even longer.") + .arg( accountId() ).arg( acctType ); + } + else + { + reason = i18n("Account %1 was blocked on the %2 server for" \ + " reconnecting too quickly." \ + " Wait ten minutes and try again." \ + " If you continue to try, you will" \ + " need to wait even longer.") + .arg( accountId() ).arg( acctType) ; + } + break; + case 0x001C: + OscarVersionUpdater::self()->update( d->versionUpdaterStamp ); + if ( !d->versionAlreadyUpdated ) + { + reason = i18n("Sign on to %1 with your account %2 failed.") + .arg( acctType ).arg( accountId() ); + + d->versionAlreadyUpdated = true; + } + else + { + reason = i18n("The %1 server thinks the client you are using is " \ + "too old. Please report this as a bug at http://bugs.kde.org") + .arg( acctType ); + } + break; + case 0x0022: // Account suspended because of your age (age < 13) + reason = i18n("Account %1 was disabled on the %2 server because " \ + "of your age (less than 13).") + .arg( accountId() ).arg( acctType ); + break; + default: + if ( !isConnected() ) + { + reason = i18n("Sign on to %1 with your account %2 failed.") + .arg( acctType ).arg( accountId() ); + } + break; + } + return reason; +} + +#include "oscaraccount.moc" +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/oscaraccount.h b/kopete/protocols/oscar/oscaraccount.h new file mode 100644 index 00000000..aa8e806d --- /dev/null +++ b/kopete/protocols/oscar/oscaraccount.h @@ -0,0 +1,204 @@ +/* + oscaraccount.h - Oscar Account Class + + Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu> + Copyright (c) 2002 by Chris TenHarmsel <tenharmsel@staticmethod.net> + Kopete (c) 2002-2003 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. * + * * + ************************************************************************* +*/ + +#ifndef OSCARACCOUNT_H +#define OSCARACCOUNT_H + +#include <qdict.h> +#include <qstring.h> +#include <qwidget.h> + +#include "kopetepasswordedaccount.h" +#include "oscartypeclasses.h" +#include "oscarcontact.h" + + +namespace Kopete +{ +class Contact; +class Group; +} + +class Client; +class Connection; +class OscarContact; +class OscarAccountPrivate; +class QTextCodec; + +class KDE_EXPORT OscarAccount : public Kopete::PasswordedAccount +{ + Q_OBJECT + +public: + OscarAccount(Kopete::Protocol *parent, const QString &accountID, const char *name=0L, bool isICQ=false); + virtual ~OscarAccount(); + + /** Provide the derived accounts and contacts with access to the backend */ + Client* engine(); + + /** Disconnects this account */ + virtual void disconnect(); + + /** + * Handle the various ways we can be logged off the oscar service + * and handle the passthrough of the disconnection through the API. + */ + void logOff( Kopete::Account::DisconnectReason ); + + /** + * Was the password wrong last time we tried to connect? + */ + bool passwordWasWrong(); + + /** + * Sets the account away + */ + virtual void setAway( bool away, const QString &awayMessage = QString::null ) = 0; + + /** + * Accessor method for the action menu + */ + virtual KActionMenu* actionMenu() = 0; + + /** Set the server address */ + void setServerAddress( const QString& server ); + + /** Set the server port */ + void setServerPort( int port ); + + /** Returns codec for account's default encoding */ + QTextCodec* defaultCodec() const; + + /** + * Returns codec for given contact's encoding or default one + * if contact has no encoding + */ + QTextCodec* contactCodec( const OscarContact* contact ) const; + + /** + * Returns codec for given contact's encoding or default one + * if contact has no encoding + */ + QTextCodec* contactCodec( const QString& contactName ) const; + + /** + * Sets buddy icon + */ + void setBuddyIcon( KURL url ); + + /** + * Add a contact to the server site list + * \param contactName the screen name of the new contact to add + * \param groupName the group of the new contact + * \param autoAddGroup if the group doesn't exist add that group + * \return true if the contact will be added + */ + bool addContactToSSI( const QString& contactName, const QString& groupName, bool autoAddGroup ); + + /** + * Change a contact's group on the server + * \param contact the contact to change + * \param newGroup the new group to move the contact to + * \param autoAddGroup if the new group doesn't exist add that group + * \return true if the contact will be added + */ + bool changeContactGroupInSSI( const QString& contact, const QString& newGroupName, bool autoAddGroup ); + +public slots: + void slotGoOffline(); + + void slotGoOnline(); + +protected: + /** + * Setup a connection for a derived account based on the host and port + */ + Connection* setupConnection( const QString& server, uint port ); + + /** + * Adds a contact to a meta contact + */ + virtual bool createContact(const QString &contactId, + Kopete::MetaContact *parentContact ); + + /** + * Protocols using Oscar must implement this to perform the instantiation + * of their contact for Kopete. Called by @ref createContact(). + * @param contactId theprotocol unique id of the contact + * @param parentContact the parent metacontact + * @return whether the creation succeeded or not + */ + virtual OscarContact *createNewContact( const QString &contactId, Kopete::MetaContact *parentContact, const SSI& ssiItem ) = 0; + + virtual QString sanitizedMessage( const QString& message ) = 0; + + void updateVersionUpdaterStamp(); + +protected slots: + + //! do stuff on login + virtual void loginActions(); + + void processSSIList(); + + void kopeteGroupRemoved( Kopete::Group* g ); + void kopeteGroupAdded( Kopete::Group* g ); + void kopeteGroupRenamed( Kopete::Group* g, const QString& oldName ); + + virtual void messageReceived( const Oscar::Message& message ); + + void ssiGroupAdded( const Oscar::SSI& ); + void ssiGroupUpdated( const Oscar::SSI& ) {} + void ssiGroupRemoved( const Oscar::SSI& ) {} + void ssiContactAdded( const Oscar::SSI& ); + void ssiContactUpdated( const Oscar::SSI& ); + void ssiContactRemoved( const Oscar::SSI& ) {} + + /* slots for receiving typing notifications, and notify the appropriate OscarContact */ + void userStartedTyping( const QString & contact ); + void userStoppedTyping( const QString & contact ); + + void nonServerAddContactDialogClosed(); + +signals: + + void accountDisconnected( Kopete::Account::DisconnectReason reason ); + + void buddyIconChanged(); + +private: + QString getFLAPErrorMessage( int code ); + +private slots: + /** Handler from socket errors from a connection */ + void slotSocketError( int, const QString& ); + + /** Handle task errors from the client */ + void slotTaskError( const Oscar::SNAC& s, int errCode, bool fatal ) ; + + /** Sends buddy icon to server */ + void slotSendBuddyIcon(); + +private: + OscarAccountPrivate *d; + +}; + +#endif + +//kate: tab-width 4; indent-mode csands; + diff --git a/kopete/protocols/oscar/oscarcontact.cpp b/kopete/protocols/oscar/oscarcontact.cpp new file mode 100644 index 00000000..660a82e6 --- /dev/null +++ b/kopete/protocols/oscar/oscarcontact.cpp @@ -0,0 +1,237 @@ +/* + oscarcontact.cpp - Oscar Protocol Plugin + + Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu> + Kopete (c) 2002-2003 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 "oscarcontact.h" + +#include <time.h> + +#include <qapplication.h> +#include <qtextcodec.h> + +#include <kaction.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> + +#include <kdeversion.h> + +#include "kopeteaccount.h" +#include "kopetechatsessionmanager.h" +#include "kopetemetacontact.h" +#include "kopetecontactlist.h" +#include "kopetegroup.h" +#include "kopeteuiglobal.h" +#include <kopeteglobal.h> + +#include "oscaraccount.h" +#include "client.h" +#include "ssimanager.h" +#include "oscarutils.h" + +#include <assert.h> + +OscarContact::OscarContact( Kopete::Account* account, const QString& name, + Kopete::MetaContact* parent, const QString& icon, const SSI& ssiItem ) +: Kopete::Contact( account, name, parent, icon ) +{ + mAccount = static_cast<OscarAccount*>(account); + mName = name; + mMsgManager = 0L; + m_ssiItem = ssiItem; + connect( this, SIGNAL( updatedSSI() ), this, SLOT( updateSSIItem() ) ); +} + +OscarContact::~OscarContact() +{ +} + +void OscarContact::serialize(QMap<QString, QString> &serializedData, + QMap<QString, QString> &/*addressBookData*/) +{ + serializedData["ssi_name"] = m_ssiItem.name(); + serializedData["ssi_type"] = QString::number( m_ssiItem.type() ); + serializedData["ssi_gid"] = QString::number( m_ssiItem.gid() ); + serializedData["ssi_bid"] = QString::number( m_ssiItem.bid() ); + serializedData["ssi_alias"] = m_ssiItem.alias(); + serializedData["ssi_waitingAuth"] = m_ssiItem.waitingAuth() ? QString::fromLatin1( "true" ) : QString::fromLatin1( "false" ); +} + +bool OscarContact::isOnServer() const +{ + SSIManager* serverList = mAccount->engine()->ssiManager(); + SSI ssi = serverList->findContact( Oscar::normalize( contactId() ) ); + + return ( ssi && ssi.type() != 0xFFFF ); +} + +void OscarContact::setSSIItem( const Oscar::SSI& ssiItem ) +{ + m_ssiItem = ssiItem; + + if ( !m_ssiItem.alias().isEmpty() ) + setProperty( Kopete::Global::Properties::self()->nickName(), m_ssiItem.alias() ); + + emit updatedSSI(); +} + +Oscar::SSI OscarContact::ssiItem() const +{ + return m_ssiItem; +} + +Kopete::ChatSession* OscarContact::manager( CanCreateFlags canCreate ) +{ + if ( !mMsgManager && canCreate ) + { + /*kdDebug(14190) << k_funcinfo << + "Creating new ChatSession for contact '" << displayName() << "'" << endl;*/ + + QPtrList<Kopete::Contact> theContact; + theContact.append(this); + + mMsgManager = Kopete::ChatSessionManager::self()->create(account()->myself(), theContact, protocol()); + + // This is for when the user types a message and presses send + connect(mMsgManager, SIGNAL( messageSent( Kopete::Message&, Kopete::ChatSession * ) ), + this, SLOT( slotSendMsg( Kopete::Message&, Kopete::ChatSession * ) ) ); + + // For when the message manager is destroyed + connect(mMsgManager, SIGNAL( destroyed() ), + this, SLOT( chatSessionDestroyed() ) ); + + connect(mMsgManager, SIGNAL( myselfTyping( bool ) ), + this, SLOT( slotTyping( bool ) ) ); + } + return mMsgManager; +} + +void OscarContact::deleteContact() +{ + mAccount->engine()->removeContact( contactId() ); + deleteLater(); +} + +void OscarContact::chatSessionDestroyed() +{ + mMsgManager = 0L; +} + +// Called when the metacontact owning this contact has changed groups +void OscarContact::sync(unsigned int flags) +{ + /* + * If the contact has changed groups, then we update the server + * adding the group if it doesn't exist, changing the ssi item + * contained in the client and updating the contact's ssi item + * Otherwise, we don't do much + */ + + if( !metaContact() || metaContact()->isTemporary() ) + return; + + if ( (flags & Kopete::Contact::MovedBetweenGroup) == Kopete::Contact::MovedBetweenGroup ) + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Moving a contact between groups" << endl; + SSIManager* ssiManager = mAccount->engine()->ssiManager(); + + SSI oldGroup = ssiManager->findGroup( m_ssiItem.gid() ); + Kopete::Group* newGroup = metaContact()->groups().first(); + if ( newGroup->displayName() == oldGroup.name() ) + return; //we didn't really move + + if ( m_ssiItem.isValid() ) + mAccount->changeContactGroupInSSI( contactId(), newGroup->displayName(), true ); + else + mAccount->addContactToSSI( contactId(), newGroup->displayName(), true ); + } + return; +} + +void OscarContact::userInfoUpdated( const QString& contact, const UserDetails& details ) +{ + Q_UNUSED( contact ); + setProperty( Kopete::Global::Properties::self()->onlineSince(), details.onlineSinceTime() ); + setIdleTime( details.idleTime() ); + m_warningLevel = details.warningLevel(); + m_details.merge( details ); + + QStringList capList; + // Append client name and version in case we found one + if ( m_details.userClass() & 0x0080 /* WIRELESS */ ) + capList << i18n( "Mobile AIM Client" ); + else + { + if ( !m_details.clientName().isEmpty() ) + { + capList << i18n( "Translators: client name and version", + "%1").arg( m_details.clientName() ); + } + } + + // and now for some general informative capabilities + if ( m_details.hasCap( CAP_BUDDYICON ) ) + capList << i18n( "Buddy icons" ); + if ( m_details.hasCap( CAP_UTF8 ) ) + capList << i18n( "UTF-8" ); + if ( m_details.hasCap( CAP_RTFMSGS ) ) + capList << i18n( "Rich text messages" ); + if ( m_details.hasCap( CAP_CHAT ) ) + capList << i18n( "Group chat" ); + if ( m_details.hasCap( CAP_VOICE ) ) + capList << i18n( "Voice chat" ); + if ( m_details.hasCap( CAP_IMIMAGE ) ) + capList << i18n( "DirectIM/IMImage" ); + if ( m_details.hasCap( CAP_SENDBUDDYLIST ) ) + capList << i18n( "Send buddy list" ); + if ( m_details.hasCap( CAP_SENDFILE ) ) + capList << i18n( "File transfers" ); + if ( m_details.hasCap( CAP_GAMES ) || m_details.hasCap( CAP_GAMES2 ) ) + capList << i18n( "Games" ); + if ( m_details.hasCap( CAP_TRILLIAN ) ) + capList << i18n( "Trillian user" ); + + m_clientFeatures = capList.join( ", " ); + emit featuresUpdated(); +} + +void OscarContact::startedTyping() +{ + if ( mMsgManager ) + mMsgManager->receivedTypingMsg( this, true ); +} + +void OscarContact::stoppedTyping() +{ + if ( mMsgManager ) + mMsgManager->receivedTypingMsg( this, false ); +} + +void OscarContact::slotTyping( bool typing ) +{ + if ( this != account()->myself() ) + account()->engine()->sendTyping( contactId(), typing ); +} + +QTextCodec* OscarContact::contactCodec() const +{ + if ( hasProperty( "contactEncoding" ) ) + return QTextCodec::codecForMib( property( "contactEncoding" ).value().toInt() ); + else + return mAccount->defaultCodec(); +} + +#include "oscarcontact.moc" +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/oscarcontact.h b/kopete/protocols/oscar/oscarcontact.h new file mode 100644 index 00000000..51c31dd2 --- /dev/null +++ b/kopete/protocols/oscar/oscarcontact.h @@ -0,0 +1,140 @@ +/* + oscarcontact.h - Oscar Protocol Plugin + + Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu> + Copyright (c) 2004 by Matt Rogers <matt.rogers@kdemail.net> + 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. * + * * + ************************************************************************* + */ + +#ifndef OSCARCONTACT_H +#define OSCARCONTACT_H + +#include <qwidget.h> +#include <qdatetime.h> +#include "kopetecontact.h" +#include "kopetemessage.h" +#include "userdetails.h" +#include "client.h" +#include "oscartypeclasses.h" + +namespace Kopete +{ +class ChatSession; +class OnlineStatus; +} + +class OscarAccount; +class QTimer; +class QTextCodec; +class KToggleAction; + +/** + * Contact for oscar protocol + * @author Matt Rogers + * @TODO Reimplement functions to do the following + * \li get the idle time + * \li get the real IP for the contact ( DC ) + * \li get the local IP for the contact + * \li get the port for the DC + * \li get the sign on time for the contact + * \li get the status of authorization for the contact + * \li get the status of authoziation for the contact + * \li get the user info for the contact + * \li check if the contact has a certain capability + * \li request authorization from the contact + * \li get and set the custom encoding for the contact + * \li get and set the SSI group id for the contact + * \li get and set whether the contact is server side or not + * \li get/set the ignore setting for the contact + * \li get/set the visibility setting for the contact ( i.e. are we visible to the contact ) + */ +class KDE_EXPORT OscarContact : public Kopete::Contact +{ +Q_OBJECT + +public: + OscarContact( Kopete::Account* account, const QString& name, + Kopete::MetaContact* parent, const QString& icon = QString::null, const Oscar::SSI& ssiItem = Oscar::SSI() ); + + virtual ~OscarContact(); + + virtual void serialize(QMap<QString, QString>&, QMap<QString, QString>&); + + virtual Kopete::ChatSession *manager( Kopete::Contact::CanCreateFlags canCreate = Kopete::Contact::CanCreate ); + + const QString &contactName() const { return mName; }; + OscarAccount *account() const { return mAccount; }; + + bool isOnServer() const; + + void setSSIItem( const Oscar::SSI& item ); + Oscar::SSI ssiItem() const; + + /** we received a typing notification from this contact, tell any message manager */ + void startedTyping(); + void stoppedTyping(); + + /** + * Returns codec for contact's encoding or default one + * if contact has no encoding + */ + QTextCodec *contactCodec() const; + +public slots: + /** slot so that properties can be updated based on a new SSI item */ + virtual void updateSSIItem() = 0; + + /** Remove this contact from the server. Reimplemented from Kopete::Contact */ + virtual void deleteContact(); + + /** the metacontact owning this contact changed */ + virtual void sync(unsigned int flags); + + /** our user info has been updated */ + virtual void userInfoUpdated( const QString& contact, const UserDetails& details ); + + /** a user is online */ + virtual void userOnline( const QString& ) = 0; + + /** a user is offline */ + virtual void userOffline( const QString& ) = 0; + +protected slots: + void slotTyping( bool typing ); + virtual void updateFeatures() = 0; + +signals: + void updatedSSI(); + void featuresUpdated(); + +protected: + OscarAccount *mAccount; + QString mName; + Kopete::ChatSession *mMsgManager; + UserDetails m_details; + SSI m_ssiItem; + int m_warningLevel; + QString m_clientFeatures; + +private: + void initActions(); + +protected slots: + virtual void slotSendMsg( Kopete::Message& msg, Kopete::ChatSession* session) = 0; + +private slots: + void chatSessionDestroyed(); + +}; + +#endif +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/oscarencodingselectionbase.ui b/kopete/protocols/oscar/oscarencodingselectionbase.ui new file mode 100644 index 00000000..1388b2d2 --- /dev/null +++ b/kopete/protocols/oscar/oscarencodingselectionbase.ui @@ -0,0 +1,58 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>OscarEncodingBaseUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>OscarEncodingBaseUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>290</width> + <height>55</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Use this &encoding when chatting with this contact:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>encodingCombo</cstring> + </property> + </widget> + <widget class="QComboBox" row="1" column="0"> + <property name="name"> + <cstring>encodingCombo</cstring> + </property> + </widget> + <spacer row="2" column="0"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Maximum</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/oscar/oscarencodingselectiondialog.cpp b/kopete/protocols/oscar/oscarencodingselectiondialog.cpp new file mode 100644 index 00000000..72e9081a --- /dev/null +++ b/kopete/protocols/oscar/oscarencodingselectiondialog.cpp @@ -0,0 +1,121 @@ +// oscarencodingselectiondialog.cpp + +// Copyright (C) 2005 Matt Rogers <mattr@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. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "oscarencodingselectionbase.h" +#include "oscarencodingselectiondialog.h" + +#include <kdebug.h> +#include <qcombobox.h> +#include <klocale.h> + +OscarEncodingSelectionDialog::OscarEncodingSelectionDialog( QWidget* parent, int initialEncoding ) + : KDialogBase( parent, 0, false, i18n( "Select Encoding" ), Ok | Cancel ) +{ + int initialEncodingIndex; + + clearWFlags( QWidget::WDestructiveClose ); + m_encodingUI = new OscarEncodingBaseUI( this ); + //fill the encoding combo boxes + m_encodings.insert(0, i18n("Default")); + m_encodings.insert(2026, i18n("Big5")); + m_encodings.insert(2101, i18n("Big5-HKSCS")); + m_encodings.insert(18, i18n("euc-JP Japanese")); + m_encodings.insert(38, i18n("euc-KR Korean")); + m_encodings.insert(57, i18n("GB-2312 Chinese")); + m_encodings.insert(113, i18n("GBK Chinese")); + m_encodings.insert(114, i18n("GB18030 Chinese")); + + m_encodings.insert(16, i18n("JIS Japanese")); + m_encodings.insert(17, i18n("Shift-JIS Japanese")); + + m_encodings.insert(2084, i18n("KOI8-R Russian")); + m_encodings.insert(2088, i18n("KOI8-U Ukrainian")); + + m_encodings.insert(4, i18n("ISO-8859-1 Western")); + m_encodings.insert(5, i18n("ISO-8859-2 Central European")); + m_encodings.insert(6, i18n("ISO-8859-3 Central European")); + m_encodings.insert(7, i18n("ISO-8859-4 Baltic")); + m_encodings.insert(8, i18n("ISO-8859-5 Cyrillic")); + m_encodings.insert(9, i18n("ISO-8859-6 Arabic")); + m_encodings.insert(10, i18n("ISO-8859-7 Greek")); + m_encodings.insert(11, i18n("ISO-8859-8 Hebrew, visually ordered")); + m_encodings.insert(85, i18n("ISO-8859-8-I Hebrew, logically ordered")); + m_encodings.insert(12, i18n("ISO-8859-9 Turkish")); + m_encodings.insert(13, i18n("ISO-8859-10")); + m_encodings.insert(109, i18n("ISO-8859-13")); + m_encodings.insert(110, i18n("ISO-8859-14")); + m_encodings.insert(111, i18n("ISO-8859-15 Western")); + + m_encodings.insert(2250, i18n("Windows-1250 Central European")); + m_encodings.insert(2251, i18n("Windows-1251 Cyrillic")); + m_encodings.insert(2252, i18n("Windows-1252 Western")); + m_encodings.insert(2253, i18n("Windows-1253 Greek")); + m_encodings.insert(2254, i18n("Windows-1254 Turkish")); + m_encodings.insert(2255, i18n("Windows-1255 Hebrew")); + m_encodings.insert(2256, i18n("Windows-1256 Arabic")); + m_encodings.insert(2257, i18n("Windows-1257 Baltic")); + m_encodings.insert(2258, i18n("Windows-1258 Viet Nam")); + + m_encodings.insert(2009, i18n("IBM 850")); + m_encodings.insert(2085, i18n("IBM 866")); + + m_encodings.insert(2259, i18n("TIS-620 Thai")); + + m_encodings.insert(106, i18n("UTF-8 Unicode")); + m_encodings.insert(1015, i18n("UTF-16 Unicode")); + + m_encodingUI->encodingCombo->insertStringList( m_encodings.values() ); + if( (initialEncodingIndex = m_encodings.keys().findIndex(initialEncoding)) == -1 ) + { + kdWarning() << k_funcinfo << "Requested encoding mib " << initialEncoding + << " not in encoding list - defaulting to first encoding item" + << " in list to be shown in combobox initially" << endl; + /* initialEncodingIndex = position in combobox, value 0 currently + * corresponds to ISO-8859-1, generally to the first item in combobox, + * which usually is the default + */ + initialEncodingIndex = 0; + } + m_encodingUI->encodingCombo->setCurrentItem( initialEncodingIndex ); + setMainWidget( m_encodingUI ); +} + + +int OscarEncodingSelectionDialog::selectedEncoding() const +{ + QString encoding = m_encodingUI->encodingCombo->currentText(); + int mib = m_encodings.keys()[ m_encodings.values().findIndex(encoding) ]; + + if( mib == -1 ) + return 0; + return mib; +} + +void OscarEncodingSelectionDialog::slotOk() +{ + emit closing( QDialog::Accepted ); +} + +void OscarEncodingSelectionDialog::slotCancel() +{ + emit closing( QDialog::Rejected ); +} + +#include "oscarencodingselectiondialog.moc" + diff --git a/kopete/protocols/oscar/oscarencodingselectiondialog.h b/kopete/protocols/oscar/oscarencodingselectiondialog.h new file mode 100644 index 00000000..f99655e4 --- /dev/null +++ b/kopete/protocols/oscar/oscarencodingselectiondialog.h @@ -0,0 +1,48 @@ +// oscarencodingselectiondialog.h +// Copyright (C) 2005 Matt Rogers <mattr@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. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef OSCARENCODINGSELECTIONDIALOG_H +#define OSCARENCODINGSELECTIONDIALOG_H + +#include <kdialogbase.h> +#include "kopete_export.h" + +class OscarEncodingBaseUI; + +class KOPETE_EXPORT OscarEncodingSelectionDialog : public KDialogBase +{ +Q_OBJECT +public: + OscarEncodingSelectionDialog( QWidget* parent = 0, int initialEncoding = 4); + ~OscarEncodingSelectionDialog() {} + + int selectedEncoding() const; + +signals: + void closing( int ); + +protected slots: + void slotOk(); + void slotCancel(); + +private: + OscarEncodingBaseUI* m_encodingUI; + QMap<int, QString> m_encodings; +}; + +#endif diff --git a/kopete/protocols/oscar/oscarlistcontactsbase.ui b/kopete/protocols/oscar/oscarlistcontactsbase.ui new file mode 100644 index 00000000..7a6d8f85 --- /dev/null +++ b/kopete/protocols/oscar/oscarlistcontactsbase.ui @@ -0,0 +1,49 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>OscarListContactsBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>OscarListContactsBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>478</width> + <height>361</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>The following contacts are not on your contact list. Would you like to add them?</string> + </property> + </widget> + <widget class="QListBox"> + <property name="name"> + <cstring>notServerContacts</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>doNotShowAgain</cstring> + </property> + <property name="text"> + <string>Do &not ask again</string> + </property> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/oscar/oscarlistnonservercontacts.cpp b/kopete/protocols/oscar/oscarlistnonservercontacts.cpp new file mode 100644 index 00000000..804849a4 --- /dev/null +++ b/kopete/protocols/oscar/oscarlistnonservercontacts.cpp @@ -0,0 +1,71 @@ +// oscarlistnonservercontacts.cpp + +// Copyright (C) 2005 Matt Rogers <mattr@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. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + + +#include "oscarlistnonservercontacts.h" +#include "oscarlistcontactsbase.h" +#include <qstringlist.h> +#include <qcheckbox.h> +#include <klocale.h> + +OscarListNonServerContacts::OscarListNonServerContacts(QWidget* parent) + : KDialogBase( parent, 0, false, i18n( "Add Contacts to Server List" ), + Ok | Cancel ) +{ + m_contactsList = new OscarListContactsBase( this ); + setMainWidget( m_contactsList ); + setButtonText( Ok, i18n( "&Add" ) ); + setButtonText( Cancel, i18n( "Do &Not Add" ) ); +} + +OscarListNonServerContacts::~OscarListNonServerContacts() +{ + +} + +void OscarListNonServerContacts::addContacts( const QStringList& contactList ) +{ + m_nonServerContacts = contactList; + m_contactsList->notServerContacts->insertStringList( contactList ); +} + +QStringList OscarListNonServerContacts::nonServerContactList() const +{ + return m_nonServerContacts; +} + +bool OscarListNonServerContacts::onlyShowOnce() +{ + return m_contactsList->doNotShowAgain->isChecked(); +} + + +void OscarListNonServerContacts::slotCancel() +{ + KDialogBase::slotCancel(); + emit closing(); +} + +void OscarListNonServerContacts::slotOk() +{ + KDialogBase::slotOk(); + emit closing(); +} + +#include "oscarlistnonservercontacts.moc" diff --git a/kopete/protocols/oscar/oscarlistnonservercontacts.h b/kopete/protocols/oscar/oscarlistnonservercontacts.h new file mode 100644 index 00000000..7f3fb4f8 --- /dev/null +++ b/kopete/protocols/oscar/oscarlistnonservercontacts.h @@ -0,0 +1,52 @@ +// oscarlistnonservercontacts.h +// Copyright (C) 2005 Matt Rogers <mattr@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. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#ifndef OSCARLISTNONSERVERCONTACTS_H +#define OSCARLISTNONSERVERCONTACTS_H + +#include <kdialogbase.h> +#include "kopete_export.h" + +class OscarListContactsBase; +class QStringList; + +class KOPETE_EXPORT OscarListNonServerContacts : public KDialogBase +{ +Q_OBJECT +public: + OscarListNonServerContacts( QWidget* parent ); + ~OscarListNonServerContacts(); + + void addContacts( const QStringList& contactList ); + QStringList nonServerContactList() const; + + bool onlyShowOnce(); + +protected: + virtual void slotOk(); + virtual void slotCancel(); + +signals: + void closing(); + +private: + OscarListContactsBase* m_contactsList; + QStringList m_nonServerContacts; + +}; +#endif diff --git a/kopete/protocols/oscar/oscarmyselfcontact.cpp b/kopete/protocols/oscar/oscarmyselfcontact.cpp new file mode 100644 index 00000000..e234cf0f --- /dev/null +++ b/kopete/protocols/oscar/oscarmyselfcontact.cpp @@ -0,0 +1,60 @@ +/* + oscarmyselfcontact.cpp - Oscar Protocol Plugin Myself Contact + + Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk> + Kopete (c) 2002-2003 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 <klocale.h> + +#include "kopetecontactlist.h" + +#include "oscartypes.h" +#include "oscaraccount.h" + +#include "oscarmyselfcontact.h" + + +OscarMyselfContact::OscarMyselfContact( OscarAccount* account ) +: Kopete::Contact( account, account->accountId(), Kopete::ContactList::self()->myself() ) +{ + QObject::connect( account->engine(), SIGNAL( haveOwnInfo() ), this, SLOT( userInfoUpdated() ) ); +} + +OscarMyselfContact::~OscarMyselfContact() +{ +} + +bool OscarMyselfContact::isReachable() +{ + return false; +} + +Kopete::ChatSession* OscarMyselfContact::manager(CanCreateFlags canCreate ) +{ + return 0; +} + +UserDetails OscarMyselfContact::details() +{ + OscarAccount *acct = static_cast<OscarAccount*>(account()); + return acct->engine()->ourInfo(); +} + +void OscarMyselfContact::deleteContact() +{ + kdWarning( OSCAR_GEN_DEBUG ) << k_funcinfo << "called on myself contact! Ignoring." << endl << kdBacktrace() << endl; +} + +#include "oscarmyselfcontact.moc" + +//kate: indent-mode csands; diff --git a/kopete/protocols/oscar/oscarmyselfcontact.h b/kopete/protocols/oscar/oscarmyselfcontact.h new file mode 100644 index 00000000..a8f7b1f8 --- /dev/null +++ b/kopete/protocols/oscar/oscarmyselfcontact.h @@ -0,0 +1,59 @@ +/* + oscarmyselfcontact.h - Oscar Protocol Plugin Myself Contact + + Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk> + 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. * + * * + ************************************************************************* + */ + +#ifndef OSCARMYSELFCONTACT_H +#define OSCARMYSELFCONTACT_H + +#include "kopetecontact.h" +#include "userdetails.h" + +namespace Kopete +{ +class ChatSession; +class OnlineStatus; +} + +class OscarAccount; +class QTimer; +class KToggleAction; + +/** + * myself() contact for oscar protocol + * @author Richard Smith + */ +class KDE_EXPORT OscarMyselfContact : public Kopete::Contact +{ +Q_OBJECT + +public: + OscarMyselfContact( OscarAccount* account ); + virtual ~OscarMyselfContact(); + + virtual bool isReachable(); + virtual Kopete::ChatSession *manager( CanCreateFlags canCreate ); + + UserDetails details(); + +public slots: + /** our user info has been updated */ + virtual void userInfoUpdated() = 0; + + /** I'm sorry Dave, I can't let you do that... */ + virtual void deleteContact(); +}; + +#endif +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/oscarversionupdater.cpp b/kopete/protocols/oscar/oscarversionupdater.cpp new file mode 100644 index 00000000..6e2cea09 --- /dev/null +++ b/kopete/protocols/oscar/oscarversionupdater.cpp @@ -0,0 +1,295 @@ +/* + oscarversionupdater.cpp - Version Updater + + Copyright (c) 2006 by Roman Jarosz <kedgedev@centrum.cz> + Kopete (c) 2006 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 "oscarversionupdater.h" + +#include <qdom.h> +#include <qmutex.h> + +#include <kdebug.h> +#include <kio/job.h> +#include <kconfig.h> +#include <kglobal.h> + + +QMutex updateMutex; +OscarVersionUpdater *OscarVersionUpdater::versionUpdaterStatic = 0L; + +OscarVersionUpdater::OscarVersionUpdater() +: mStamp( 1 ), mUpdating( false ) +{ + initICQVersionInfo(); + initAIMVersionInfo(); +} + +OscarVersionUpdater::~OscarVersionUpdater() +{ +} + +OscarVersionUpdater *OscarVersionUpdater::self() +{ + if ( !versionUpdaterStatic ) + versionUpdaterStatic = new OscarVersionUpdater(); + + return versionUpdaterStatic; +} + +bool OscarVersionUpdater::update( unsigned int stamp ) +{ + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << endl; + bool doUpdate = false; + bool isUpdating = false; + + updateMutex.lock(); + if ( !mUpdating && stamp == mStamp ) + { + doUpdate = true; + mUpdating = true; + } + isUpdating = mUpdating; + updateMutex.unlock(); + + if ( doUpdate ) + { + mVersionData.resize( 0 ); + + KConfigGroup config( KGlobal::config(), "Oscar" ); + QString url = config.readEntry( "NewVersionURL", "http://kopete.kde.org/oscarversions.xml" ); + mTransferJob = KIO::get ( url ); + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Download version info from server."<< endl; + + connect ( mTransferJob, SIGNAL ( result ( KIO::Job* ) ), + this, SLOT ( slotTransferResult ( KIO::Job* ) ) ); + connect ( mTransferJob, SIGNAL ( data ( KIO::Job*, const QByteArray& ) ), + this, SLOT ( slotTransferData ( KIO::Job*, const QByteArray& ) ) ); + } + return isUpdating; +} + +unsigned int OscarVersionUpdater::stamp() const +{ + return mStamp; +} + +void OscarVersionUpdater::initICQVersionInfo() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + KConfigGroup config( KGlobal::config(), "ICQVersion" ); + + mICQVersion.clientString = config.readEntry( "ClientString", "ICQBasic" ); + mICQVersion.clientId = config.readEntry( "ClientId", "0x010A" ).toUShort( 0, 0 ); + mICQVersion.major = config.readEntry( "Major", "0x0006" ).toUShort( 0, 0 ); + mICQVersion.minor = config.readEntry( "Minor", "0x0000" ).toUShort( 0, 0 ); + mICQVersion.point = config.readEntry( "Point", "0x0000" ).toUShort( 0, 0 ); + mICQVersion.build = config.readEntry( "Build", "0x17AB" ).toUShort( 0, 0 ); + mICQVersion.other = config.readEntry( "Other", "0x00007535" ).toUInt( 0, 0 ); + mICQVersion.country = config.readEntry( "Country", "us" ); + mICQVersion.lang = config.readEntry( "Lang", "en" ); +} + +void OscarVersionUpdater::initAIMVersionInfo() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + + KConfigGroup config( KGlobal::config(), "AIMVersion" ); + + mAIMVersion.clientString = config.readEntry( "ClientString", "AOL Instant Messenger (SM), version 5.1.3036/WIN32" ); + mAIMVersion.clientId = config.readEntry( "ClientId", "0x0109" ).toUShort( 0, 0 ); + mAIMVersion.major = config.readEntry( "Major", "0x0005" ).toUShort( 0, 0 ); + mAIMVersion.minor = config.readEntry( "Minor", "0x0001" ).toUShort( 0, 0 ); + mAIMVersion.point = config.readEntry( "Point", "0x0000" ).toUShort( 0, 0 ); + mAIMVersion.build = config.readEntry( "Build", "0x0bdc" ).toUShort( 0, 0 ); + mAIMVersion.other = config.readEntry( "Other", "0x000000d2" ).toUInt( 0, 0 ); + mAIMVersion.country = config.readEntry( "Country", "us" ); + mAIMVersion.lang = config.readEntry( "Lang", "en" ); +} + +void OscarVersionUpdater::printDebug() +{ + kdDebug(OSCAR_RAW_DEBUG) << "*************** AIM VERSION INFO ***************" << endl; + kdDebug(OSCAR_RAW_DEBUG) << "client string: " << mAIMVersion.clientString << endl; + kdDebug(OSCAR_RAW_DEBUG) << "client id: " << QString::number( mAIMVersion.clientId, 16 ) << endl; + kdDebug(OSCAR_RAW_DEBUG) << "major: " << QString::number( mAIMVersion.major, 16 ) << endl; + kdDebug(OSCAR_RAW_DEBUG) << "minor: " << QString::number( mAIMVersion.minor, 16 ) << endl; + kdDebug(OSCAR_RAW_DEBUG) << "point: " << QString::number( mAIMVersion.point, 16 ) << endl; + kdDebug(OSCAR_RAW_DEBUG) << "build: " << QString::number( mAIMVersion.build, 16 ) << endl; + kdDebug(OSCAR_RAW_DEBUG) << "other: " << QString::number( mAIMVersion.other, 16 ) << endl; + kdDebug(OSCAR_RAW_DEBUG) << "country: " << mAIMVersion.country << endl; + kdDebug(OSCAR_RAW_DEBUG) << "lang: " << mAIMVersion.lang << endl; + kdDebug(OSCAR_RAW_DEBUG) << "************************************************" << endl; + + kdDebug(OSCAR_RAW_DEBUG) << "*************** ICQ VERSION INFO ***************" << endl; + kdDebug(OSCAR_RAW_DEBUG) << "client string: " << mICQVersion.clientString << endl; + kdDebug(OSCAR_RAW_DEBUG) << "client id: " << QString::number( mICQVersion.clientId, 16 ) << endl; + kdDebug(OSCAR_RAW_DEBUG) << "major: " << QString::number( mICQVersion.major, 16 ) << endl; + kdDebug(OSCAR_RAW_DEBUG) << "minor: " << QString::number( mICQVersion.minor, 16 ) << endl; + kdDebug(OSCAR_RAW_DEBUG) << "point: " << QString::number( mICQVersion.point, 16 ) << endl; + kdDebug(OSCAR_RAW_DEBUG) << "build: " << QString::number( mICQVersion.build, 16 ) << endl; + kdDebug(OSCAR_RAW_DEBUG) << "other: " << QString::number( mICQVersion.other, 16 ) << endl; + kdDebug(OSCAR_RAW_DEBUG) << "country: " << mICQVersion.country << endl; + kdDebug(OSCAR_RAW_DEBUG) << "lang: " << mICQVersion.lang << endl; + kdDebug(OSCAR_RAW_DEBUG) << "************************************************" << endl; +} + +void OscarVersionUpdater::slotTransferData ( KIO::Job */*job*/, const QByteArray &data ) +{ + unsigned oldSize = mVersionData.size(); + mVersionData.resize ( oldSize + data.size() ); + memcpy ( &mVersionData.data()[oldSize], data.data(), data.size() ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Data size " << mVersionData.size() << endl; +} + +void OscarVersionUpdater::slotTransferResult ( KIO::Job *job ) +{ + bool bUpdate = false; + if ( job->error() || mTransferJob->isErrorPage() ) + { + //TODO show error + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Download of version info has faild!" << endl; + } + else + { + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Updating version info" << endl; + + QDomDocument doc; + if ( doc.setContent ( mVersionData ) ) + { + Oscar::ClientVersion tmpICQ = mICQVersion; + Oscar::ClientVersion tmpAIM = mAIMVersion; + + parseDocument( doc ); + + if ( !equal( tmpICQ, mICQVersion ) ) + { + storeVersionInfo( "ICQVersion", mICQVersion ); + bUpdate = true; + } + + if ( !equal( tmpAIM, mAIMVersion ) ) + { + storeVersionInfo( "AIMVersion", mAIMVersion ); + bUpdate = true; + } + } + } + + // clear + mVersionData.resize( 0 ); + mTransferJob = 0; + + updateMutex.lock(); + if ( bUpdate ) + mStamp++; + mUpdating = false; + updateMutex.unlock(); +} + +void OscarVersionUpdater::parseDocument( QDomDocument& doc ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + + QDomElement root = doc.documentElement(); + if ( root.tagName() != "oscar" ) + return; + + QDomElement versionElement = root.firstChild().toElement(); + while( !versionElement.isNull() ) + { + if ( versionElement.tagName() == "icq" ) + parseVersion( mICQVersion, versionElement ); + else if ( versionElement.tagName() == "aim" ) + parseVersion( mAIMVersion, versionElement ); + + versionElement = versionElement.nextSibling().toElement(); + } +} + +bool OscarVersionUpdater::parseVersion( Oscar::ClientVersion& version, QDomElement& element ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + + // clear structure + version.clientString = QString::null; + version.clientId = 0x0000; + version.major = 0x0000; + version.minor = 0x0000; + version.point = 0x0000; + version.build = 0x0000; + version.other = 0x00000000; + version.country = QString::null; + version.lang = QString::null; + + QDomElement versionChild = element.firstChild().toElement(); + while ( !versionChild.isNull() ) + { + if ( versionChild.tagName() == "client" ) + version.clientString = versionChild.text(); + else if ( versionChild.tagName() == "clientId" ) + version.clientId = versionChild.text().toUShort( 0, 0); + else if ( versionChild.tagName() == "major" ) + version.major = versionChild.text().toUShort( 0, 0 ); + else if ( versionChild.tagName() == "minor" ) + version.minor = versionChild.text().toUShort( 0, 0 ); + else if ( versionChild.tagName() == "point" ) + version.point = versionChild.text().toUShort( 0, 0 ); + else if ( versionChild.tagName() == "build" ) + version.build = versionChild.text().toUShort( 0, 0 ); + else if ( versionChild.tagName() == "other" ) + version.other = versionChild.text().toUInt( 0, 0 ); + else if ( versionChild.tagName() == "country" ) + version.country = versionChild.text(); + else if ( versionChild.tagName() == "lang" ) + version.lang = versionChild.text(); + + versionChild = versionChild.nextSibling().toElement(); + } + + return true; +} + +void OscarVersionUpdater::storeVersionInfo( const QString& group, const Oscar::ClientVersion& version ) const +{ + kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Storing version info to group: " << group << endl; + KConfigGroup config( KGlobal::config(), group ); + + config.writeEntry( "ClientString", version.clientString ); + config.writeEntry( "ClientId", version.clientId ); + config.writeEntry( "Major", version.major ); + config.writeEntry( "Minor", version.minor ); + config.writeEntry( "Point", version.point ); + config.writeEntry( "Build", version.build ); + config.writeEntry( "Other", version.other ); + config.writeEntry( "Country", version.country ); + config.writeEntry( "Lang", version.lang ); + config.sync(); +} + +bool OscarVersionUpdater::equal( const Oscar::ClientVersion& a, const Oscar::ClientVersion& b ) const +{ + if ( a.clientString != b.clientString || a.clientId != b.clientId || + a.major != b.major|| a.minor != b.minor || + a.point != b.point || a.build != b.build || + a.other != b.other || a.country != b.country || + a.lang != b.lang ) + { + return false; + } + + return true; +} + +#include "oscarversionupdater.moc" diff --git a/kopete/protocols/oscar/oscarversionupdater.h b/kopete/protocols/oscar/oscarversionupdater.h new file mode 100644 index 00000000..d6851f73 --- /dev/null +++ b/kopete/protocols/oscar/oscarversionupdater.h @@ -0,0 +1,120 @@ +/* + oscarversionupdater.h - Version Updater + + Copyright (c) 2006 by Roman Jarosz <kedgedev@centrum.cz> + Kopete (c) 2006 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. * + * * + ************************************************************************* +*/ + +#ifndef OSCARVERSIONUPDATER_H +#define OSCARVERSIONUPDATER_H + +#include <qobject.h> + +#include <oscartypes.h> + +namespace KIO +{ +class Job; +class TransferJob; +} + +class QDomElement; +class QDomDocument; + +/** + @author Roman Jarosz <kedgedev@centrum.cz> +*/ + +class OscarVersionUpdater : public QObject +{ + Q_OBJECT + +public: + OscarVersionUpdater(); + ~OscarVersionUpdater(); + + static OscarVersionUpdater* self(); + + /** + * Update version info from server. + * @param stamp is update number. + */ + bool update( unsigned int stamp ); + + /** + * Update version info from server. + * @return true if update is in progress or starts. + */ + unsigned int stamp() const; + + /** + * Return structure with version info for ICQ. + * @return Oscar::ClientVersion. + */ + const Oscar::ClientVersion* getICQVersion() const { return &mICQVersion; } + + /** + * Return structure with version info for AIM. + * @return Oscar::ClientVersion. + */ + const Oscar::ClientVersion* getAIMVersion() const { return &mAIMVersion; } + + /** + * Set structure with ICQ version info to default. + */ + void initICQVersionInfo(); + + /** + * Set structure with AIM version info to default. + */ + void initAIMVersionInfo(); + + /** + * Print debug info. + */ + void printDebug(); + +private slots: + void slotTransferData( KIO::Job *job, const QByteArray &data ); + void slotTransferResult( KIO::Job *job ); + +private: + void parseDocument( QDomDocument& doc ); + bool parseVersion( Oscar::ClientVersion& version, QDomElement& element ); + + /** + * Store version info structure to KConfigGroup + * @param group is the group name. + * @param version is version info structure. + */ + void storeVersionInfo( const QString& group, const Oscar::ClientVersion& version ) const; + + /** + * Compare two versions. + * @return true if a and b is equal. + */ + bool equal( const Oscar::ClientVersion& a, const Oscar::ClientVersion& b ) const; + +private: + static OscarVersionUpdater *versionUpdaterStatic; + + Oscar::ClientVersion mICQVersion; + Oscar::ClientVersion mAIMVersion; + + KIO::TransferJob *mTransferJob; + QByteArray mVersionData; + + unsigned int mStamp; + bool mUpdating; +}; + +#endif diff --git a/kopete/protocols/oscar/oscarvisibilitybase.ui b/kopete/protocols/oscar/oscarvisibilitybase.ui new file mode 100644 index 00000000..a6b98799 --- /dev/null +++ b/kopete/protocols/oscar/oscarvisibilitybase.ui @@ -0,0 +1,170 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>OscarVisibilityBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>OscarVisibilityBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>594</width> + <height>409</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Always visible:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Contacts:</string> + </property> + </widget> + <widget class="QListBox" row="1" column="0" rowspan="9" colspan="1"> + <property name="name"> + <cstring>contacts</cstring> + </property> + </widget> + <spacer row="1" column="1"> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>40</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="2" column="1"> + <property name="name"> + <cstring>visibleAdd</cstring> + </property> + <property name="text"> + <string>Add</string> + </property> + </widget> + <widget class="QPushButton" row="3" column="1"> + <property name="name"> + <cstring>visibleRemove</cstring> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + <spacer row="4" column="1"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>40</height> + </size> + </property> + </spacer> + <widget class="QListBox" row="1" column="2" rowspan="4" colspan="1"> + <property name="name"> + <cstring>visibleContacts</cstring> + </property> + </widget> + <widget class="QPushButton" row="8" column="1"> + <property name="name"> + <cstring>invisibleRemove</cstring> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + <spacer row="9" column="1"> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>40</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="7" column="1"> + <property name="name"> + <cstring>invisibleAdd</cstring> + </property> + <property name="text"> + <string>Add</string> + </property> + </widget> + <widget class="QListBox" row="6" column="2" rowspan="4" colspan="1"> + <property name="name"> + <cstring>invisibleContacts</cstring> + </property> + </widget> + <spacer row="6" column="1"> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>40</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="5" column="2"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Always invisible:</string> + </property> + </widget> + </grid> +</widget> +<tabstops> + <tabstop>contacts</tabstop> + <tabstop>visibleAdd</tabstop> + <tabstop>visibleRemove</tabstop> + <tabstop>invisibleAdd</tabstop> + <tabstop>invisibleRemove</tabstop> + <tabstop>visibleContacts</tabstop> + <tabstop>invisibleContacts</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/oscar/oscarvisibilitydialog.cpp b/kopete/protocols/oscar/oscarvisibilitydialog.cpp new file mode 100644 index 00000000..7958432c --- /dev/null +++ b/kopete/protocols/oscar/oscarvisibilitydialog.cpp @@ -0,0 +1,135 @@ +/* + oscarvisibilitydialog.cpp - Visibility Dialog + + Copyright (c) 2005 by Roman Jarosz <kedgedev@centrum.cz> + Kopete (c) 2005 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 "oscarvisibilitydialog.h" + +#include <qstringlist.h> +#include <qpushbutton.h> + +#include <klocale.h> + +#include "oscarvisibilitybase.h" +#include "client.h" + + +OscarVisibilityDialog::OscarVisibilityDialog( Client* client, QWidget* parent ) + : KDialogBase( parent, 0, false, i18n( "Add Contacts to Visible or Invisible List" ), + Ok | Cancel ), m_client( client ) +{ + m_visibilityUI = new OscarVisibilityBase( this ); + setMainWidget( m_visibilityUI ); + + QObject::connect(m_visibilityUI->visibleAdd, SIGNAL(clicked()), + this, SLOT(slotAddToVisible())); + QObject::connect(m_visibilityUI->visibleRemove, SIGNAL(clicked()), + this, SLOT(slotRemoveFromVisible())); + QObject::connect(m_visibilityUI->invisibleAdd, SIGNAL(clicked()), + this, SLOT(slotAddToInvisible())); + QObject::connect(m_visibilityUI->invisibleRemove, SIGNAL(clicked()), + this, SLOT(slotRemoveFromInvisible())); +} + +void OscarVisibilityDialog::addContacts( const ContactMap& contacts ) +{ + m_contactMap = contacts; + + ContactMap::Iterator it, cEnd = m_contactMap.end(); + for ( it = m_contactMap.begin(); it != cEnd; ++it ) + m_visibilityUI->contacts->insertItem( it.key() ); + +} + +void OscarVisibilityDialog::addVisibleContacts( const QStringList& contactList ) +{ + m_visibilityUI->visibleContacts->insertStringList( contactList ); +} + +void OscarVisibilityDialog::addInvisibleContacts( const QStringList& contactList ) +{ + m_visibilityUI->invisibleContacts->insertStringList( contactList ); +} + +void OscarVisibilityDialog::slotAddToVisible() +{ + QListBoxItem *itm = m_visibilityUI->contacts->selectedItem(); + if ( !itm ) return; + + QString contactId = m_contactMap[itm->text()]; + m_visibleListChangesMap[contactId] = Add; + + if ( !m_visibilityUI->visibleContacts->findItem( itm->text(), Qt::CaseSensitive | Qt::ExactMatch ) ) + m_visibilityUI->visibleContacts->insertItem( itm->text() ); +} + +void OscarVisibilityDialog::slotRemoveFromVisible() +{ + QListBoxItem *itm = m_visibilityUI->visibleContacts->selectedItem(); + if ( !itm ) return; + + QString contactId = m_contactMap[itm->text()]; + m_visibleListChangesMap[contactId] = Remove; + + int visIdx = m_visibilityUI->visibleContacts->index( itm ); + m_visibilityUI->visibleContacts->removeItem( visIdx ); +} + +void OscarVisibilityDialog::slotAddToInvisible() +{ + QListBoxItem *itm = m_visibilityUI->contacts->selectedItem(); + if ( !itm ) return; + + QString contactId = m_contactMap[itm->text()]; + m_invisibleListChangesMap[contactId] = Add; + + if ( !m_visibilityUI->invisibleContacts->findItem( itm->text(), Qt::CaseSensitive | Qt::ExactMatch ) ) + m_visibilityUI->invisibleContacts->insertItem( itm->text() ); +} + +void OscarVisibilityDialog::slotRemoveFromInvisible() +{ + QListBoxItem *itm = m_visibilityUI->invisibleContacts->selectedItem(); + if ( !itm ) return; + + QString contactId = m_contactMap[itm->text()]; + m_invisibleListChangesMap[contactId] = Remove; + + int visIdx = m_visibilityUI->invisibleContacts->index( itm ); + m_visibilityUI->invisibleContacts->removeItem( visIdx ); +} + +void OscarVisibilityDialog::slotOk() +{ + ChangeMap::Iterator it, cEnd = m_visibleListChangesMap.end(); + for ( it = m_visibleListChangesMap.begin(); it != cEnd; ++it ) { + m_client->setVisibleTo( it.key(), it.data() ); + } + + cEnd = m_invisibleListChangesMap.end(); + for ( it = m_invisibleListChangesMap.begin(); it != cEnd; ++it ) { + m_client->setInvisibleTo( it.key(), it.data() ); + } + + KDialogBase::slotOk(); + emit closing(); +} + +void OscarVisibilityDialog::slotCancel() +{ + KDialogBase::slotCancel(); + emit closing(); +} + +#include "oscarvisibilitydialog.moc" diff --git a/kopete/protocols/oscar/oscarvisibilitydialog.h b/kopete/protocols/oscar/oscarvisibilitydialog.h new file mode 100644 index 00000000..fc45039f --- /dev/null +++ b/kopete/protocols/oscar/oscarvisibilitydialog.h @@ -0,0 +1,70 @@ +/* + oscarvisibilitydialog.h - Visibility Dialog + + Copyright (c) 2005 by Roman Jarosz <kedgedev@centrum.cz> + Kopete (c) 2005 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. * + * * + ************************************************************************* +*/ + +#ifndef OSCARVISIBILITYDIALOG_H +#define OSCARVISIBILITYDIALOG_H + +#include <kdialogbase.h> +#include "kopete_export.h" + +/** + @author Roman Jarosz <kedgedev@centrum.cz> +*/ +class OscarVisibilityBase; +class QStringList; +class Client; + +class KOPETE_EXPORT OscarVisibilityDialog : public KDialogBase +{ + Q_OBJECT +public: + typedef QMap<QString, QString> ContactMap; + + OscarVisibilityDialog( Client* client, QWidget* parent ); + ~OscarVisibilityDialog() {} + + void addContacts( const ContactMap& contacts ); + void addVisibleContacts( const QStringList& contactList ); + void addInvisibleContacts( const QStringList& contactList ); + +signals: + void closing(); + +protected: + virtual void slotOk(); + virtual void slotCancel(); + +protected slots: + void slotAddToVisible(); + void slotRemoveFromVisible(); + void slotAddToInvisible(); + void slotRemoveFromInvisible(); + +private: + enum Action{ Remove = 0, Add }; + typedef QMap<QString, Action> ChangeMap; + + //maps with changes that should be send to server + ChangeMap m_visibleListChangesMap; + ChangeMap m_invisibleListChangesMap; + + ContactMap m_contactMap; + + OscarVisibilityBase* m_visibilityUI; + Client* m_client; +}; + +#endif |