/* aimcontact.cpp - Oscar Protocol Plugin Copyright (c) 2003 by Will Stephenson Kopete (c) 2002-2004 by the Kopete developers ************************************************************************* * * * 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 #include #include #include #include #include #include #include #include #include #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 TQString& name, Kopete::MetaContact* parent, const TQString& icon, const Oscar::SSI& ssiItem ) : OscarContact(account, name, parent, icon, ssiItem ) { mProtocol=static_cast(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 = TQDateTime::currentDateTime().addDays(-1); TQObject::connect( mAccount->engine(), TQ_SIGNAL( receivedUserInfo( const TQString&, const UserDetails& ) ), this, TQ_SLOT( userInfoUpdated( const TQString&, const UserDetails& ) ) ); TQObject::connect( mAccount->engine(), TQ_SIGNAL( userIsOffline( const TQString& ) ), this, TQ_SLOT( userOffline( const TQString& ) ) ); TQObject::connect( mAccount->engine(), TQ_SIGNAL( receivedAwayMessage( const TQString&, const TQString& ) ), this, TQ_SLOT( updateAwayMessage( const TQString&, const TQString& ) ) ); TQObject::connect( mAccount->engine(), TQ_SIGNAL( receivedProfile( const TQString&, const TQString& ) ), this, TQ_SLOT( updateProfile( const TQString&, const TQString& ) ) ); TQObject::connect( mAccount->engine(), TQ_SIGNAL( userWarned( const TQString&, TQ_UINT16, TQ_UINT16 ) ), this, TQ_SLOT( gotWarning( const TQString&, TQ_UINT16, TQ_UINT16 ) ) ); TQObject::connect( mAccount->engine(), TQ_SIGNAL( haveIconForContact( const TQString&, TQByteArray ) ), this, TQ_SLOT( haveIcon( const TQString&, TQByteArray ) ) ); TQObject::connect( mAccount->engine(), TQ_SIGNAL( iconServerConnected() ), this, TQ_SLOT( requestBuddyIcon() ) ); TQObject::connect( this, TQ_SIGNAL( featuresUpdated() ), this, TQ_SLOT( updateFeatures() ) ); } AIMContact::~AIMContact() { } bool AIMContact::isReachable() { return true; } TQPtrList *AIMContact::customContextMenuActions() { TQPtrList *actionCollection = new TQPtrList(); if ( !m_warnUserAction ) { m_warnUserAction = new TDEAction( i18n( "&Warn User" ), 0, this, TQ_SLOT( warnUser() ), this, "warnAction" ); } m_actionVisibleTo = new TDEToggleAction(i18n("Always &Visible To"), "", 0, this, TQ_SLOT(slotVisibleTo()), this, "actionVisibleTo"); m_actionInvisibleTo = new TDEToggleAction(i18n("Always &Invisible To"), "", 0, this, TQ_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 TQString AIMContact::awayMessage() { return property(mProtocol->awayMessage).value().toString(); } void AIMContact::setAwayMessage(const TQString &message) { kdDebug(14152) << k_funcinfo << "Called for '" << contactId() << "', away msg='" << message << "'" << endl; TQString filteredMessage = message; filteredMessage.replace( TQRegExp(TQString::fromLatin1("<[hH][tT][mM][lL].*>(.*)")), TQString::fromLatin1("\\1")); filteredMessage.replace( TQRegExp(TQString::fromLatin1("<[bB][oO][dD][yY].*>(.*)")), TQString::fromLatin1("\\1") ); TQRegExp fontRemover( TQString::fromLatin1("<[fF][oO][nN][tT].*>(.*)") ); fontRemover.setMinimal(true); while ( filteredMessage.find( fontRemover ) != -1 ) filteredMessage.replace( fontRemover, TQString::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( protocol() )->statusOffline ); } } void AIMContact::slotUserInfo() { if ( !m_infoDialog) { m_infoDialog = new AIMUserInfoDialog( this, static_cast( account() ), false, Kopete::UI::Global::mainWidget(), 0 ); if( !m_infoDialog ) return; connect( m_infoDialog, TQ_SIGNAL( finished() ), this, TQ_SLOT( closeUserInfoDialog() ) ); m_infoDialog->show(); if ( mAccount->isConnected() ) { mAccount->engine()->requestAIMProfile( contactId() ); mAccount->engine()->requestAIMAwayMessage( contactId() ); } } else m_infoDialog->raise(); } void AIMContact::userInfoUpdated( const TQString& 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. TQString 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 = ( TDEApplication::random() % 10 ) * 1000; kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "updating buddy icon in " << time/1000 << " seconds" << endl; TQTimer::singleShot( time, this, TQ_SLOT( requestBuddyIcon() ) ); } OscarContact::userInfoUpdated( contact, details ); } void AIMContact::userOnline( const TQString& 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 TQString& userId ) { if ( Oscar::normalize( userId ) == Oscar::normalize( contactId() ) ) { setOnlineStatus( mProtocol->statusOffline ); removeProperty( mProtocol->awayMessage ); } } void AIMContact::updateAwayMessage( const TQString& contact, const TQString& 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 TQString& contact, const TQString& profile ) { if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) ) return; setProperty( mProtocol->clientProfile, profile ); emit updatedProfile(); } void AIMContact::gotWarning( const TQString& contact, TQ_UINT16 increase, TQ_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 TQString& user, TQByteArray icon ) { if ( Oscar::normalize( user ) != Oscar::normalize( contactId() ) ) return; kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Updating icon for " << contactId() << endl; TQImage buddyIcon( icon ); if ( buddyIcon.isNull() ) { kdWarning(OSCAR_AIM_DEBUG) << k_funcinfo << "Failed to convert buddy icon to TQImage" << endl; return; } setProperty( Kopete::Global::Properties::self()->photo(), buddyIcon ); } void AIMContact::closeUserInfoDialog() { m_infoDialog->delayedDestruct(); m_infoDialog = 0L; } void AIMContact::warnUser() { TQString nick = property( Kopete::Global::Properties::self()->nickName() ).value().toString(); TQString message = i18n( "Would you like to warn %1 anonymously or with your name?
" \ "(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.)
" ).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; TQString 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 -> //font-weight:600 -> (anything > 400 should be , 400 is not bold) //text-decoration:underline -> //font-family: -> //font-size:xxpt -> s=message.escapedBody(); s.replace ( TQRegExp( TQString::fromLatin1("([^<]*)")), TQString::fromLatin1("")); s.replace ( TQRegExp( TQString::fromLatin1("")), TQString::fromLatin1("")); s.replace ( TQRegExp( TQString::fromLatin1("")), TQString::fromLatin1("")); s.replace ( TQRegExp( TQString::fromLatin1("")), TQString::fromLatin1("")); s.replace ( TQRegExp( TQString::fromLatin1("")), TQString::fromLatin1("")); s.replace ( TQRegExp( TQString::fromLatin1("")), TQString::fromLatin1("")); s.replace ( TQRegExp( TQString::fromLatin1("")), TQString::fromLatin1("")); s.replace ( TQRegExp( TQString::fromLatin1("")), TQString::fromLatin1("\\2")); //okay now change the to //0-9 are size 1 s.replace ( TQRegExp ( TQString::fromLatin1("")), TQString::fromLatin1("")); //10-11 are size 2 s.replace ( TQRegExp ( TQString::fromLatin1("")), TQString::fromLatin1("")); //12-13 are size 3 s.replace ( TQRegExp ( TQString::fromLatin1("")), TQString::fromLatin1("")); //14-16 are size 4 s.replace ( TQRegExp ( TQString::fromLatin1("")), TQString::fromLatin1("")); //17-22 are size 5 s.replace ( TQRegExp ( TQString::fromLatin1("")), TQString::fromLatin1("")); //23-29 are size 6 s.replace ( TQRegExp ( TQString::fromLatin1("")),TQString::fromLatin1("")); //30- (and any I missed) are size 7 s.replace ( TQRegExp ( TQString::fromLatin1("")),TQString::fromLatin1("")); // strip left over line break s.remove(TQRegExp(TQString::fromLatin1("]*>$"))); s.replace ( TQRegExp ( TQString::fromLatin1("")), TQString::fromLatin1("
") ); // strip left over line break s.remove( TQRegExp( TQString::fromLatin1( "
$" ) ) ); 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(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( TQDateTime::currentDateTime() ); kdDebug(14152) << k_funcinfo << "Last autoresponse time: " << m_lastAutoresponseTime << endl; kdDebug(14152) << k_funcinfo << "Current time: " << TQDateTime::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 { TQTextCodec* 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 = TQDateTime::currentDateTime(); } else { kdDebug(14152) << k_funcinfo << "Not enough time since last autoresponse, NOT sending" << endl; } } #include "aimcontact.moc"