/*************************************************************************** contactbase.cpp - description ------------------- begin : Thu Jan 16 2003 copyright : (C) 2003 by Mike K. Bennett email : mkb137b@hotmail.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 "contactbase.h" #include <kdebug.h> #include <kstandarddirs.h> #include <qimage.h> #include <qfile.h> #include <qstylesheet.h> #include "../emoticon.h" #include "../kmessdebug.h" #include "../network/applications/applicationlist.h" // The constructor ContactBase::ContactBase(QString handle, QString friendlyName, uint capabilities) : capabilities_(capabilities) , friendlyName_(friendlyName) , handle_(handle) , applicationList_(0) { } // The destructor ContactBase::~ContactBase() { delete applicationList_; } // Add a custom emoticon definition void ContactBase::addEmoticonDefinition(const QString &emoticonCode, const QString &msnObjectDataHash ) { #ifdef KMESSDEBUG_CONTACTBASE kdDebug() << "ContactBase::addEmoticonDefinition() - adding '" << emoticonCode << "' to pending replacements" << endl; #endif // Escape the code so the HTML parser won't be fooled. QString shortcut = emoticonCode; shortcut.replace( "&", "&" ) .replace( "<", "<" ) .replace( ">", ">" ) .replace( "'", "'" ) .replace( '"', """ ); // Add emoticon to pending list, until Msn object is received. pendingEmoticons_.insert(msnObjectDataHash, shortcut, true); // Escape the code to insert it safely into a regular expression QString escapedCode = QRegExp::escape( shortcut ); // Update regexp pattern if( pendingRegExp_.isEmpty() ) { pendingRegExp_ = QRegExp( escapedCode ); } else { pendingRegExp_ = QRegExp( pendingRegExp_.pattern() + "|" + escapedCode ); } #ifdef KMESSTEST ASSERT( pendingRegExp_.isValid() ); #endif } // Add a custom emoticon file void ContactBase::addEmoticonFile(const QString &msnObjectDataHash, const QString &filename) { // Check whether the mapping is available. if( ! pendingEmoticons_.contains(msnObjectDataHash) ) { kdWarning() << "Contact::addEmoticonFile: Received an custom emoticon, but no emoticon code found!" << endl; return; } // Get the emoticon code from pending list, remove it QString emoticonCode = pendingEmoticons_[msnObjectDataHash]; // no QString& because record gets removed. pendingEmoticons_.remove(msnObjectDataHash); #ifdef KMESSDEBUG_CONTACTBASE kdDebug() << "ContactBase::addEmoticonFile() - linking '" << emoticonCode << "' to received file." << endl; #endif // Check if filename exists, attempt to read it. if( ! QFile::exists(filename) ) { kdWarning() << "Contact::addEmoticonFile: Could not read emoticon file: file not found!" << endl; return; } QImage emoticonData(filename); if( emoticonData.isNull() ) { kdWarning() << "Contact::addEmoticonFile: Could not read emoticon file!" << endl; return; } #ifdef KMESSDEBUG_CONTACT_GENERAL kdDebug() << "Contact::addEmoticonFile: Setting image for '" << emoticonCode << " to " << filename << endl; #endif // Escape the code to insert it safely into a regular expression QString escapedCode = QRegExp::escape( emoticonCode ); // Update regexp pattern if( emoticonRegExp_.isEmpty() ) { emoticonRegExp_ = QRegExp( escapedCode ); } else { emoticonRegExp_ = QRegExp( emoticonRegExp_.pattern() + "|" + escapedCode ); } // Update pending regexp pattern. pendingRegExp_ = QRegExp(pendingRegExp_.pattern().remove( QRegExp("(^|\\|)" + escapedCode) )); #ifdef KMESSTEST ASSERT( emoticonRegExp_.isValid() ); ASSERT( pendingRegExp_.isValid() ); #endif // Get the image dimensions from the file int originalWidth = emoticonData.width(); int originalHeight = emoticonData.height(); // Resize the displayed image to match KMess' maximum emoticon size int width = originalWidth; // QMIN( EMOTICONS_MAX_SIZE, originalWidth ); // Require only a max height int height = QMIN( EMOTICONS_MAX_SIZE, originalHeight ); if( originalWidth > originalHeight ) { height = ( width * originalHeight ) / originalWidth; } else { width = ( height * originalWidth ) / originalHeight; } // Add replacement. QString replacement = "<img src='" + filename + "' alt='" + emoticonCode + "' width='" + QString::number( width ) + "' height='" + QString::number( height ) + "' valign='middle'" + " class='customEmoticon' />"; customEmoticons_.insert(emoticonCode, replacement); } // Register the contact as participant in the chat session void ContactBase::addSwitchboardConnection( const MsnSwitchboardConnection *connection ) { #ifdef KMESSDEBUG_CONTACTBASE kdDebug() << "ContactBase::addSwitchboardConnection() - adding connection to list." << endl; #endif if( chatSessions_.contains(connection) ) { kdWarning() << "ContactBase::addSwitchboardConnection: chat session is already added." << endl; return; } chatSessions_.append( connection ); } // Initialize an application list for the contact. ApplicationList * ContactBase::createApplicationList() { if( applicationList_ != 0 ) { kdWarning() << "ContactBase::createApplicationList: application list already created for contact '" << handle_ << "'." << endl; } else { applicationList_ = new ApplicationList( handle_ ); connect( applicationList_, SIGNAL( applicationsAborted(const QString &) ), this, SLOT ( slotApplicationListAborted() ) ); } return applicationList_; } // Return the application list of the contact ApplicationList * ContactBase::getApplicationList() const { return applicationList_; } // The capabilities of the client uint ContactBase::getCapabilities() const { return capabilities_; } // Return the default contact picture path QString ContactBase::getContactDefaultPicturePath() const { KStandardDirs *dirs = KGlobal::dirs(); return dirs->findResource( "data", "kmess/pics/unknown.png" ); } // Return the custom emoticon code for an msn object const QString & ContactBase::getEmoticonCode(const QString &msnObjectDataHash) const { return pendingEmoticons_[msnObjectDataHash]; } // Return the custom emoticon search pattern. const QRegExp & ContactBase::getEmoticonPattern() const { return emoticonRegExp_; } // Return the custom emoticon replacements. const QMap<QString,QString>& ContactBase::getEmoticonReplacements() const { return customEmoticons_; } // Return the contact's friendly name QString ContactBase::getFriendlyName() const { return friendlyName_; } // Return the contact's handle QString ContactBase::getHandle() const { return handle_; } // Return the search pattern for pending custom emoticons. const QRegExp & ContactBase::getPendingEmoticonPattern() const { return pendingRegExp_; } // Return the list of switchboard connections const QPtrList<MsnSwitchboardConnection> ContactBase::getSwitchboardConnections() const { return chatSessions_; } // Returns whether the contact has an application list initialized. bool ContactBase::hasApplicationList() const { return (applicationList_ != 0); } // Returns whether the contact's client supports the given MSNP2P version bool ContactBase::hasP2PSupport( MsnClientCapabilities minimalVersion ) const { #ifdef KMESSTEST ASSERT( minimalVersion != 0 && (minimalVersion & 0x0fffffff) == 0 ); // don't pass some other capability. #endif return capabilities_ >= (uint) minimalVersion; } // Return true if the contact is offline bool ContactBase::isOffline() const { return getStatus() == "FLN"; } // Return true if the contact is online bool ContactBase::isOnline() const { return getStatus() != "FLN"; } // Unregister the contact as participant, contact has left the chat. void ContactBase::removeSwitchboardConnection( const MsnSwitchboardConnection *connection, bool userInitiated ) { #ifdef KMESSDEBUG_CONTACTBASE kdDebug() << "ContactBase::removeSwitchboardConnection() - removing connection, userInitiated=" << userInitiated << endl; #endif bool found = chatSessions_.remove( connection ); if( ! found ) { kdWarning() << "ContactBase::removeChatSession: chat session not found." << endl; } if( chatSessions_.isEmpty() ) { #ifdef KMESSDEBUG_CONTACTBASE kdDebug() << "ContactBase::removeSwitchboardConnection() - " << handle_ << " is no longer in any other chat conversation, cleaning up." << endl; #endif // Inform the ApplicationList if we have one. // It's possible some sessions need to be terminated now. if( applicationList_ != 0 ) { bool hasAbortingApps = applicationList_->contactLeftChat( userInitiated ); // If there aren't any applications aborting, the object can be deleted now. // Also avoid deleting the object if there is still a direct connection. // It's possible a switchboard is replaced with another one (with WLM -> background chat -> normal chat). // Note the object could be destroyed from slotApplicationListAborted() too. if( ! hasAbortingApps && applicationList_ != 0 && ! applicationList_->hasAuthorizedDirectConnection() ) { #ifdef KMESSDEBUG_CONTACTBASE kdDebug() << "ContactBase::removeSwitchboardConnection() - no applications to abort, deleting ApplicationList object." << endl; #endif delete applicationList_; applicationList_ = 0; } } // This will be used by CurrentAccount to clean up temporary InvitedContact objects. emit leftAllChats( this ); } } // Set the client capabilities void ContactBase::setCapabilities(uint capabilities) { capabilities_ = capabilities; } // The application list completed aborting it's applications. void ContactBase::slotApplicationListAborted() { // Avoid killing an object if it was filled with new entries again if( ! applicationList_->isEmpty() || applicationList_->hasAuthorizedDirectConnection() ) { #ifdef KMESSDEBUG_CONTACTBASE kdDebug() << "ContactBase::slotApplicationListAborted() - requested abortion completed, but ApplicationList object has object references: not deleting object." << endl; #endif return; } #ifdef KMESSDEBUG_CONTACTBASE kdDebug() << "ContactBase::slotApplicationListAborted() - requested abortion completed, deleting ApplicationList object." << endl; #endif // Use deleteLater() because this is called from the signal loop. applicationList_->deleteLater(); applicationList_ = 0; } #include "contactbase.moc"