2 oscarcontact.cpp - Oscar Protocol Plugin
4 Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu>
5 Kopete (c) 2002-2008 by the Kopete developers <kopete-devel@kde.org>
7 *************************************************************************
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
14 *************************************************************************
17 #include "oscarcontact.h"
21 #include <qapplication.h>
22 #include <qtextcodec.h>
30 #include <kmessagebox.h>
31 #include <kstandarddirs.h>
32 #include <kinputdialog.h>
34 #include <kdeversion.h>
35 #include <kfiledialog.h>
37 #include "kopeteaccount.h"
38 #include "kopetechatsessionmanager.h"
39 #include "kopetemetacontact.h"
40 #include "kopetecontactlist.h"
41 #include "kopetegroup.h"
42 #include "kopeteuiglobal.h"
43 #include <kopeteglobal.h>
44 #include "kopetetransfermanager.h"
45 #include "kopeteavatarmanager.h"
47 #include "oscaraccount.h"
49 #include "contactmanager.h"
50 #include "oscarutils.h"
51 #include "oscarprotocol.h"
52 #include "oscarencodingselectiondialog.h"
53 #include "oscarstatusmanager.h"
54 #include "filetransferhandler.h"
58 OscarContact::OscarContact( Kopete::Account
* account
, const QString
& name
,
59 Kopete::MetaContact
* parent
, const QString
& icon
)
60 : Kopete::Contact( account
, name
, parent
, icon
)
62 mAccount
= static_cast<OscarAccount
*>(account
);
65 m_buddyIconDirty
= false;
68 setFileCapable( true );
70 QObject::connect( mAccount
->engine(), SIGNAL(haveIconForContact(const QString
&, QByteArray
)),
71 this, SLOT(haveIcon(const QString
&, QByteArray
)) );
72 QObject::connect( mAccount
->engine(), SIGNAL(iconServerConnected()),
73 this, SLOT(requestBuddyIcon()) );
74 QObject::connect( mAccount
->engine(), SIGNAL(receivedAwayMessage(const QString
&, const QString
& )),
75 this, SLOT(receivedStatusMessage(const QString
&, const QString
&)) );
76 QObject::connect( mAccount
->engine(), SIGNAL(messageAck(const QString
&, uint
)),
77 this, SLOT(messageAck(const QString
&, uint
)) );
78 QObject::connect( mAccount
->engine(), SIGNAL(messageError(const QString
&, uint
)),
79 this, SLOT(messageError(const QString
&, uint
)) );
82 OscarContact::~OscarContact()
86 void OscarContact::serialize(QMap
<QString
, QString
> &serializedData
,
87 QMap
<QString
, QString
> &/*addressBookData*/)
89 serializedData
["ssi_name"] = m_ssiItem
.name();
90 serializedData
["ssi_type"] = QString::number( m_ssiItem
.type() );
91 serializedData
["ssi_gid"] = QString::number( m_ssiItem
.gid() );
92 serializedData
["ssi_bid"] = QString::number( m_ssiItem
.bid() );
93 serializedData
["ssi_alias"] = m_ssiItem
.alias();
94 serializedData
["ssi_waitingAuth"] = m_ssiItem
.waitingAuth() ? QString::fromLatin1( "true" ) : QString::fromLatin1( "false" );
95 serializedData
["ssi_metaInfoId"] = m_ssiItem
.metaInfoId().toHex();
98 bool OscarContact::isOnServer() const
100 ContactManager
* serverList
= mAccount
->engine()->ssiManager();
101 OContact ssi
= serverList
->findContact( Oscar::normalize( contactId() ) );
103 return ( ssi
&& ssi
.type() != 0xFFFF );
106 void OscarContact::setSSIItem( const OContact
& ssiItem
)
108 if ( !ssiItem
.alias().isEmpty() )
109 setProperty( Kopete::Global::Properties::self()->nickName(), ssiItem
.alias() );
114 OContact
OscarContact::ssiItem() const
119 Kopete::ChatSession
* OscarContact::manager( CanCreateFlags canCreate
)
121 if ( !mMsgManager
&& canCreate
)
124 "Creating new ChatSession for contact '" << displayName() << "'" << endl;*/
126 QList
<Kopete::Contact
*> theContact
;
127 theContact
.append(this);
129 mMsgManager
= Kopete::ChatSessionManager::self()->create(account()->myself(), theContact
, protocol());
131 // This is for when the user types a message and presses send
132 connect(mMsgManager
, SIGNAL( messageSent( Kopete::Message
&, Kopete::ChatSession
* ) ),
133 this, SLOT( slotSendMsg( Kopete::Message
&, Kopete::ChatSession
* ) ) );
135 // For when the message manager is destroyed
136 connect(mMsgManager
, SIGNAL( destroyed() ),
137 this, SLOT( chatSessionDestroyed() ) );
139 connect(mMsgManager
, SIGNAL( myselfTyping( bool ) ),
140 this, SLOT( slotTyping( bool ) ) );
145 void OscarContact::deleteContact()
147 mAccount
->engine()->removeContact( contactId() );
151 void OscarContact::chatSessionDestroyed()
156 // Called when the metacontact owning this contact has changed groups
157 void OscarContact::sync(unsigned int flags
)
160 * If the contact has changed groups, then we update the server
161 * adding the group if it doesn't exist, changing the ssi item
162 * contained in the client and updating the contact's ssi item
163 * Otherwise, we don't do much
166 if( !metaContact() || metaContact()->isTemporary() )
169 if ( (flags
& Kopete::Contact::MovedBetweenGroup
) == Kopete::Contact::MovedBetweenGroup
)
172 kDebug(OSCAR_GEN_DEBUG
) << "Moving a contact between groups";
173 ContactManager
* ssiManager
= mAccount
->engine()->ssiManager();
175 OContact oldGroup
= ssiManager
->findGroup( m_ssiItem
.gid() );
176 Kopete::Group
* newGroup
= metaContact()->groups().first();
177 if ( newGroup
->displayName() == oldGroup
.name() )
178 return; //we didn't really move
180 if ( m_ssiItem
.isValid() )
181 mAccount
->changeContactGroupInSSI( contactId(), newGroup
->displayName(), true );
183 mAccount
->addContactToSSI( contactId(), newGroup
->displayName(), true );
188 void OscarContact::userInfoUpdated( const QString
& contact
, const UserDetails
& details
)
192 if ( details
.buddyIconHash().size() > 0 && details
.buddyIconHash() != m_details
.buddyIconHash() )
194 OscarProtocol
*p
= static_cast<OscarProtocol
*>(protocol());
195 if ( property( p
->buddyIconHash
).value().toByteArray() != details
.buddyIconHash() )
197 m_buddyIconDirty
= true;
199 if ( !mAccount
->engine()->hasIconConnection() )
201 mAccount
->engine()->connectToIconServer();
205 int time
= ( KRandom::random() % 10 ) * 1000;
206 kDebug(OSCAR_GEN_DEBUG
) << "updating buddy icon in "
207 << time
/1000 << " seconds" << endl
;
208 QTimer::singleShot( time
, this, SLOT( requestBuddyIcon() ) );
213 setProperty( Kopete::Global::Properties::self()->onlineSince(), details
.onlineSinceTime() );
214 setIdleTime( details
.idleTime() );
215 m_warningLevel
= details
.warningLevel();
216 m_details
.merge( details
);
218 setFileCapable( m_details
.hasCap( CAP_SENDFILE
) );
221 // Append client name and version in case we found one
222 //if ( m_details.userClass() & 0x0080 /* WIRELESS */ )
223 // capList << i18n( "Mobile AIM Client" );
226 // if ( !m_details.clientName().isEmpty() )
228 // capList << i18nc( "Translators: client name and version",
229 // "%1", m_details.clientName() );
233 // and now for some general informative capabilities
234 if ( m_details
.hasCap( CAP_BUDDYICON
) )
235 capList
<< i18n( "Buddy icons" );
236 if ( m_details
.hasCap( CAP_UTF8
) )
237 capList
<< i18n( "UTF-8" );
238 if ( m_details
.hasCap( CAP_RTFMSGS
) )
239 capList
<< i18n( "Rich text messages" );
240 if ( m_details
.hasCap( CAP_CHAT
) )
241 capList
<< i18n( "Group chat" );
242 if ( m_details
.hasCap( CAP_VOICE
) )
243 capList
<< i18n( "Voice chat" );
244 if ( m_details
.hasCap( CAP_IMIMAGE
) )
245 capList
<< i18n( "DirectIM/IMImage" );
246 if ( m_details
.hasCap( CAP_SENDBUDDYLIST
) )
247 capList
<< i18n( "Send buddy list" );
248 if ( m_details
.hasCap( CAP_SENDFILE
) )
249 capList
<< i18n( "File transfers" );
250 if ( m_details
.hasCap( CAP_GAMES
) || m_details
.hasCap( CAP_GAMES2
) )
251 capList
<< i18n( "Games" );
253 m_clientFeatures
= capList
.join( ", " );
254 setProperty( static_cast<OscarProtocol
*>(protocol())->clientFeatures
, m_clientFeatures
);
256 setProperty( static_cast<OscarProtocol
*>(protocol())->memberSince
, details
.memberSinceTime() );
257 setProperty( static_cast<OscarProtocol
*>(protocol())->client
, details
.clientName() );
258 setProperty( static_cast<OscarProtocol
*>(protocol())->protocolVersion
, QString::number(details
.dcProtoVersion()) );
261 void OscarContact::startedTyping()
264 mMsgManager
->receivedTypingMsg( this, true );
267 void OscarContact::stoppedTyping()
270 mMsgManager
->receivedTypingMsg( this, false );
273 void OscarContact::slotTyping( bool typing
)
275 if ( this != account()->myself() )
276 account()->engine()->sendTyping( contactId(), typing
);
279 void OscarContact::messageAck( const QString
& contact
, uint messageId
)
281 if ( Oscar::normalize( contact
) != Oscar::normalize( contactId() ) )
284 Kopete::ChatSession
* chatSession
= manager();
286 chatSession
->receivedMessageState( messageId
, Kopete::Message::StateSent
);
289 void OscarContact::messageError( const QString
& contact
, uint messageId
)
291 if ( Oscar::normalize( contact
) != Oscar::normalize( contactId() ) )
294 Kopete::ChatSession
* chatSession
= manager();
296 chatSession
->receivedMessageState( messageId
, Kopete::Message::StateError
);
299 QTextCodec
* OscarContact::contactCodec() const
301 if ( hasProperty( "contactEncoding" ) )
303 QTextCodec
* codec
= QTextCodec::codecForMib( property( "contactEncoding" ).value().toInt() );
308 return QTextCodec::codecForMib( 4 );
311 return mAccount
->defaultCodec();
314 bool OscarContact::hasCap( int capNumber
) const
316 return m_details
.hasCap( capNumber
);
319 void OscarContact::setPresenceTarget( const Oscar::Presence
&presence
)
321 OscarProtocol
* p
= static_cast<OscarProtocol
*>(protocol());
322 setOnlineStatus( p
->statusManager()->onlineStatusOf( presence
) );
325 void OscarContact::setEncoding( int mib
)
327 OscarProtocol
* p
= static_cast<OscarProtocol
*>( protocol() );
330 kDebug(OSCAR_GEN_DEBUG
) << "setting encoding mib to " << mib
<< endl
;
331 setProperty( p
->contactEncoding
, m_oesd
->selectedEncoding() );
335 kDebug(OSCAR_GEN_DEBUG
) << "setting encoding to default" << endl
;
336 removeProperty( p
->contactEncoding
);
340 //here's where a filetransfer usually begins
341 //could be called by a KAction or our dcop code or something
342 void OscarContact::sendFile( const KUrl
&sourceURL
, const QString
&altFileName
, uint fileSize
)
344 kDebug(OSCAR_GEN_DEBUG
) << "file: '" << sourceURL
345 << "' '" << altFileName
<< "' size " << fileSize
<< endl
;
348 //If the file location is null, then get it from a file open dialog
349 if( !sourceURL
.isValid() )
350 files
= KFileDialog::getOpenFileNames( KUrl() ,"*", 0l , i18n( "Kopete File Transfer" ));
352 files
<< sourceURL
.path(KUrl::RemoveTrailingSlash
);
354 if( files
.isEmpty() )
356 kDebug(OSCAR_GEN_DEBUG
) << "files empty, assuming cancel";
359 kDebug(OSCAR_GEN_DEBUG
) << "files: '" << files
<< "' ";
361 FileTransferHandler
*ftHandler
= mAccount
->engine()->createFileTransfer( mName
, files
);
363 Kopete::TransferManager
*transferManager
= Kopete::TransferManager::transferManager();
364 Kopete::Transfer
*transfer
= transferManager
->addTransfer( this, files
, ftHandler
->totalSize(), mName
, Kopete::FileTransferInfo::Outgoing
);
366 connect( transfer
, SIGNAL(transferCanceled()), ftHandler
, SLOT(cancel()) );
368 connect( ftHandler
, SIGNAL(transferCancelled()), transfer
, SLOT(slotCancelled()) );
369 connect( ftHandler
, SIGNAL(transferError(int, const QString
&)), transfer
, SLOT(slotError(int, const QString
&)) );
370 connect( ftHandler
, SIGNAL(transferProcessed(unsigned int)), transfer
, SLOT(slotProcessed(unsigned int)) );
371 connect( ftHandler
, SIGNAL(transferFinished()), transfer
, SLOT(slotComplete()) );
372 connect( ftHandler
, SIGNAL(transferNextFile(const QString
&, const QString
&)),
373 transfer
, SLOT(slotNextFile(const QString
&, const QString
&)) );
378 void OscarContact::setAwayMessage( const QString
&message
)
380 kDebug(OSCAR_AIM_DEBUG
) <<
381 "Called for '" << contactId() << "', away msg='" << message
<< "'" << endl
;
383 if ( !message
.isEmpty() )
384 setProperty( static_cast<OscarProtocol
*>( protocol() )->statusMessage
, filterAwayMessage( message
) );
386 removeProperty( static_cast<OscarProtocol
*>( protocol() )->statusMessage
);
388 emit
statusMessageChanged();
391 void OscarContact::changeContactEncoding()
396 OscarProtocol
* p
= static_cast<OscarProtocol
*>( protocol() );
397 m_oesd
= new OscarEncodingSelectionDialog( Kopete::UI::Global::mainWidget(), property(p
->contactEncoding
).value().toInt() );
398 connect( m_oesd
, SIGNAL(closing(int)), this, SLOT(changeEncodingDialogClosed(int)) );
402 void OscarContact::requestAuthorization()
404 QString info
= i18n("The user %1 requires authorization before being added to a contact list. "
405 "Do you want to send an authorization request?\n\nReason for requesting authorization:",
406 ( !nickName().isEmpty() ) ? nickName() : contactId() );
408 QString reason
= KInputDialog::getText( i18n("Request Authorization"), info
,
409 i18n("Please authorize me so I can add you to my contact list") );
410 if ( !reason
.isNull() )
411 mAccount
->engine()->requestAuth( contactId(), reason
);
414 void OscarContact::changeEncodingDialogClosed( int result
)
416 if ( result
== QDialog::Accepted
)
417 setEncoding( m_oesd
->selectedEncoding() );
421 m_oesd
->deleteLater();
426 void OscarContact::requestBuddyIcon()
428 if ( m_buddyIconDirty
&& m_details
.buddyIconHash().size() > 0 )
430 account()->engine()->requestBuddyIcon( contactId(), m_details
.buddyIconHash(),
431 m_details
.iconType(), m_details
.iconCheckSumType() );
435 void OscarContact::haveIcon( const QString
& user
, QByteArray icon
)
437 if ( Oscar::normalize( user
) != Oscar::normalize( contactId() ) )
440 kDebug(OSCAR_GEN_DEBUG
) << "Updating icon for " << contactId();
442 KMD5
buddyIconHash( icon
);
443 if ( memcmp( buddyIconHash
.rawDigest(), m_details
.buddyIconHash().data(), 16 ) == 0 )
446 img
.loadFromData(icon
);
447 Kopete::AvatarManager::AvatarEntry entry
;
448 entry
.name
= contactId();
449 entry
.category
= Kopete::AvatarManager::Contact
;
450 entry
.contact
= this;
452 entry
= Kopete::AvatarManager::self()->add(entry
);
454 setProperty( static_cast<OscarProtocol
*>(protocol())->buddyIconHash
, m_details
.buddyIconHash() );
455 if (!entry
.path
.isNull())
457 removeProperty( Kopete::Global::Properties::self()->photo() );
458 setProperty( Kopete::Global::Properties::self()->photo(), entry
.path
);
461 m_buddyIconDirty
= false;
465 kDebug(14153) << "Buddy icon hash does not match!";
466 removeProperty( Kopete::Global::Properties::self()->photo() );
470 void OscarContact::receivedStatusMessage( const QString
& contact
, const QString
& message
)
472 if ( Oscar::normalize( contact
) != Oscar::normalize( contactId() ) )
475 setAwayMessage( message
);
478 QString
OscarContact::filterAwayMessage( const QString
&message
) const
480 QString filteredMessage
= message
;
481 filteredMessage
.replace(
482 QRegExp(QString::fromLatin1("<[hH][tT][mM][lL].*>(.*)</[hH][tT][mM][lL]>")),
483 QString::fromLatin1("\\1"));
484 filteredMessage
.replace(
485 QRegExp(QString::fromLatin1("<[bB][oO][dD][yY].*>(.*)</[bB][oO][dD][yY]>")),
486 QString::fromLatin1("\\1") );
487 QRegExp
fontRemover( QString::fromLatin1("<[fF][oO][nN][tT].*>(.*)</[fF][oO][nN][tT]>") );
488 fontRemover
.setMinimal(true);
489 while ( filteredMessage
.indexOf( fontRemover
) != -1 )
490 filteredMessage
.replace( fontRemover
, QString::fromLatin1("\\1") );
491 return filteredMessage
;
494 #include "oscarcontact.moc"
495 //kate: tab-width 4; indent-mode csands;