1 /*
2    aimaccount.cpp  -  Oscar Protocol Plugin, AIM part
3 
4    Kopete    (c) 2002-2007 by the Kopete developers  <kopete-devel@kde.org>
5 
6  *************************************************************************
7  *                                                                       *
8  * This program is free software; you can redistribute it and/or modify  *
9  * it under the terms of the GNU General Public License as published by  *
10  * the Free Software Foundation; either version 2 of the License, or     *
11  * (at your option) any later version.                                   *
12  *                                                                       *
13  *************************************************************************
14  */
15 
16 #include "aimaccount.h"
17 
18 #include <QPointer>
19 
20 #include <kdebug.h>
21 #include <kconfig.h>
22 #include <kdialog.h>
23 #include <KLocalizedString>
24 
25 #include <kactionmenu.h>
26 #include <kmessagebox.h>
27 #include <ktoggleaction.h>
28 #include <kicon.h>
29 
30 #include "kopetepassword.h"
31 #include "kopetestdaction.h"
32 #include "kopeteuiglobal.h"
33 #include "kopetecontactlist.h"
34 #include "kopetemetacontact.h"
35 #include "kopeteprotocol.h"
36 #include "kopetechatsessionmanager.h"
37 #include "kopeteview.h"
38 
39 #include "aimprotocol.h"
40 #include "aimchatsession.h"
41 #include "aimcontact.h"
42 #include "icqcontact.h"
43 #include "aimuserinfo.h"
44 #include "aimjoinchat.h"
45 #include "oscarmyselfcontact.h"
46 
47 #include "oscarutils.h"
48 #include "client.h"
49 #include "contactmanager.h"
50 #include "oscarsettings.h"
51 #include "oscarstatusmanager.h"
52 
53 const Oscar::DWORD AIM_ONLINE = 0x0;
54 const Oscar::DWORD AIM_AWAY = 0x1;
55 
56 namespace Kopete { class MetaContact; }
57 
AIMMyselfContact(AIMAccount * acct)58 AIMMyselfContact::AIMMyselfContact( AIMAccount *acct )
59 : OscarMyselfContact( acct )
60 {
61 	m_acct = acct;
62 }
63 
userInfoUpdated()64 void AIMMyselfContact::userInfoUpdated()
65 {
66 	Oscar::DWORD extendedStatus = details().extendedStatus();
67 	kDebug( OSCAR_AIM_DEBUG ) << "extendedStatus is " << QString::number( extendedStatus, 16 );
68 
69 	AIMProtocol* p = static_cast<AIMProtocol *>(protocol());
70 	Oscar::Presence presence = p->statusManager()->presenceOf( extendedStatus, details().userClass() );
71 
72 	setOnlineStatus( p->statusManager()->onlineStatusOf( presence ) );
73 	setStatusMessage( static_cast<AIMAccount*>( account() )->engine()->statusMessage() );
74 }
75 
setOwnProfile(const QString & newProfile)76 void AIMMyselfContact::setOwnProfile( const QString& newProfile )
77 {
78 	m_profileString = newProfile;
79 	if ( m_acct->isConnected() )
80 		m_acct->engine()->updateProfile( newProfile );
81 }
82 
userProfile()83 QString AIMMyselfContact::userProfile()
84 {
85 	return m_profileString;
86 }
87 
manager(Kopete::Contact::CanCreateFlags canCreate)88 Kopete::ChatSession* AIMMyselfContact::manager( Kopete::Contact::CanCreateFlags canCreate )
89 {
90 	return manager( canCreate, 0, QString() );
91 }
92 
manager(Kopete::Contact::CanCreateFlags canCreate,Oscar::WORD exchange,const QString & room)93 Kopete::ChatSession* AIMMyselfContact::manager( Kopete::Contact::CanCreateFlags canCreate,
94 		Oscar::WORD exchange, const QString& room )
95 {
96 	kDebug(OSCAR_AIM_DEBUG) ;
97 	Kopete::ContactPtrList chatMembers;
98 	chatMembers.append( this );
99 	Kopete::ChatSession* genericManager = nullptr;
100 	genericManager = Kopete::ChatSessionManager::self()->findChatSession( account()->myself(), chatMembers, protocol() );
101 	AIMChatSession* session = dynamic_cast<AIMChatSession*>( genericManager );
102 
103 	if ( !session && canCreate == Contact::CanCreate )
104 	{
105 		session = new AIMChatSession( this, chatMembers, account()->protocol(), exchange, room );
106 		session->setEngine( m_acct->engine() );
107 
108 		connect(session, &AIMChatSession::messageSent, this, &AIMMyselfContact::sendMessage);
109 		m_chatRoomSessions.append( session );
110 	}
111 	return session;
112 }
113 
chatSessionDestroyed(Kopete::ChatSession * session)114 void AIMMyselfContact::chatSessionDestroyed( Kopete::ChatSession* session )
115 {
116 	m_chatRoomSessions.removeAll( session );
117 }
118 
sendMessage(Kopete::Message & message,Kopete::ChatSession * session)119 void AIMMyselfContact::sendMessage( Kopete::Message& message, Kopete::ChatSession* session )
120 {
121 	kDebug(OSCAR_AIM_DEBUG) << "sending a message";
122 	//TODO: remove duplication - factor into a message utils class or something
123 	Oscar::Message msg;
124 	QString s;
125 
126 	if (message.plainBody().isEmpty()) // no text, do nothing
127 		return;
128 	//okay, now we need to change the message.escapedBody from real HTML to aimhtml.
129 	//looking right now for docs on that "format".
130 	//looks like everything except for alignment codes comes in the format of spans
131 
132 	//font-style:italic -> <i>
133 	//font-weight:600 -> <b> (anything > 400 should be <b>, 400 is not bold)
134 	//text-decoration:underline -> <u>
135 	//font-family: -> <font face="">
136 	//font-size:xxpt -> <font ptsize=xx>
137 
138 	s=message.escapedBody();
139 	s.replace ( QRegExp( QString::fromLatin1("<span style=\"([^\"]*)\">([^<]*)</span>")),
140 			QString::fromLatin1("<style>\\1;\"\\2</style>"));
141 
142 	s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-style:italic;([^\"]*)\"([^<]*)</style>")),
143 			QString::fromLatin1("<i><style>\\1\\2\"\\3</style></i>"));
144 
145 	s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-weight:600;([^\"]*)\"([^<]*)</style>")),
146 			QString::fromLatin1("<b><style>\\1\\2\"\\3</style></b>"));
147 
148 	s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)text-decoration:underline;([^\"]*)\"([^<]*)</style>")),
149 			QString::fromLatin1("<u><style>\\1\\2\"\\3</style></u>"));
150 
151 	s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-family:([^;]*);([^\"]*)\"([^<]*)</style>")),
152 			QString::fromLatin1("<font face=\"\\2\"><style>\\1\\3\"\\4</style></font>"));
153 
154 	s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-size:([^p]*)pt;([^\"]*)\"([^<]*)</style>")),
155 			QString::fromLatin1("<font ptsize=\"\\2\"><style>\\1\\3\"\\4</style></font>"));
156 
157 	s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)background-color:([^;]*);([^\"]*)\"([^<]*)</style>")),
158 	            QString::fromLatin1("<font back=\"\\2\"><style>\\1\\3\"\\4</style></font>"));
159 
160 	s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)color:([^;]*);([^\"]*)\"([^<]*)</style>")),
161 			QString::fromLatin1("<font color=\"\\2\"><style>\\1\\3\"\\4</style></font>"));
162 
163 	s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)\"([^<]*)</style>")),
164 			QString::fromLatin1("\\2"));
165 
166 	//okay now change the <font ptsize="xx"> to <font size="xx">
167 
168 	//0-9 are size 1
169 	s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"\\d\">")),
170 			QString::fromLatin1("<font size=\"1\">"));
171 	//10-11 are size 2
172 	s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[01]\">")),
173 			QString::fromLatin1("<font size=\"2\">"));
174 	//12-13 are size 3
175 	s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[23]\">")),
176 			QString::fromLatin1("<font size=\"3\">"));
177 	//14-16 are size 4
178 	s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[456]\">")),
179 			QString::fromLatin1("<font size=\"4\">"));
180 	//17-22 are size 5
181 	s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"(?:1[789]|2[012])\">")),
182 			QString::fromLatin1("<font size=\"5\">"));
183 	//23-29 are size 6
184 	s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"2[3456789]\">")),QString::fromLatin1("<font size=\"6\">"));
185 	//30- (and any I missed) are size 7
186 	s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"[^\"]*\">")),QString::fromLatin1("<font size=\"7\">"));
187 
188 	s.replace ( QRegExp ( QString::fromLatin1("<br[ /]*>")), QString::fromLatin1("<br>") );
189 
190 	kDebug(14190) << "sending "
191 		<< s << endl;
192 
193 	msg.setSender( contactId() );
194 	msg.setText( Oscar::Message::UserDefined, s, m_acct->defaultCodec() );
195 	msg.setTimestamp(message.timestamp());
196 	msg.setChannel(0x03);
197 	msg.addProperty( Oscar::Message::ChatRoom );
198 
199 	AIMChatSession* aimSession = dynamic_cast<AIMChatSession*>( session );
200 	if ( !aimSession )
201 	{
202 		kWarning(OSCAR_AIM_DEBUG) << "couldn't convert to AIM chat room session!";
203 		session->messageSucceeded();
204 		return;
205 	}
206 	msg.setExchange( aimSession->exchange() );
207 	msg.setChatRoom( aimSession->roomName() );
208 
209 	m_acct->engine()->sendMessage( msg );
210 	//session->appendMessage( message );
211 	session->messageSucceeded();
212 }
213 
AIMAccount(Kopete::Protocol * parent,QString accountID)214 AIMAccount::AIMAccount(Kopete::Protocol *parent, QString accountID)
215 	: OscarAccount(parent, accountID, false)
216 {
217 	kDebug(14152) << accountID << ": Called.";
218 	AIMMyselfContact* mc = new AIMMyselfContact( this );
219 	setMyself( mc );
220 	mc->setOnlineStatus( protocol()->statusManager()->onlineStatusOf( Oscar::Presence( Oscar::Presence::Offline ) ) );
221 
222 	QString profile = configGroup()->readEntry( "Profile",
223 			i18n( "Visit the Kopete website at <a href=\"http://kopete.kde.org\">http://kopete.kde.org</a>") );
224 	mc->setOwnProfile( profile );
225 	mInitialStatusMessage.clear();
226 
227 	m_joinChatDialog = 0;
228 	QObject::connect( engine(), SIGNAL(chatRoomConnected(Oscar::WORD,QString)),
229 	                  this, SLOT(connectedToChatRoom(Oscar::WORD,QString)) );
230 
231 	QObject::connect( engine(), SIGNAL(userJoinedChat(Oscar::WORD,QString,QString)),
232 			this, SLOT(userJoinedChat(Oscar::WORD,QString,QString)) );
233 
234 	QObject::connect( engine(), SIGNAL(userLeftChat(Oscar::WORD,QString,QString)),
235 			this, SLOT(userLeftChat(Oscar::WORD,QString,QString)) );
236 
237 	// Create actions
238 	mJoinChatAction = new QAction( i18n( "Join Chat..." ), this );
239 	QObject::connect(mJoinChatAction, &QAction::triggered, this, &AIMAccount::slotJoinChat);
240 
241     mEditInfoAction = new QAction( QIcon::fromTheme("user-properties"), i18n( "Edit User Info..." ), this );
242 	QObject::connect(mEditInfoAction, &QAction::triggered, this, &AIMAccount::slotEditInfo);
243 
244 	mActionInvisible = new KToggleAction( i18n( "In&visible" ), this );
245 	QObject::connect(mActionInvisible, &KToggleAction::triggered, this, &AIMAccount::slotToggleInvisible);
246 }
247 
~AIMAccount()248 AIMAccount::~AIMAccount()
249 {
250 }
251 
protocol() const252 AIMProtocol* AIMAccount::protocol() const
253 {
254 	return static_cast<AIMProtocol*>(OscarAccount::protocol());
255 }
256 
presence()257 Oscar::Presence AIMAccount::presence()
258 {
259 	return protocol()->statusManager()->presenceOf( myself()->onlineStatus() );
260 }
261 
createNewContact(const QString & contactId,Kopete::MetaContact * parentContact,const OContact & ssiItem)262 OscarContact *AIMAccount::createNewContact( const QString &contactId, Kopete::MetaContact *parentContact, const OContact& ssiItem )
263 {
264 	if ( QRegExp("[\\d]+").exactMatch( contactId ) )
265 	{
266 		ICQContact* contact = new ICQContact( this, contactId, parentContact, QString() );
267 		contact->setSSIItem( ssiItem );
268 
269 		if ( engine()->isActive() )
270 			contact->loggedIn();
271 
272 		return contact;
273 	}
274 	else
275 	{
276 		AIMContact* contact = new AIMContact( this, contactId, parentContact, QString() );
277 		contact->setSSIItem( ssiItem );
278 
279 		return contact;
280 	}
281 }
282 
fillActionMenu(KActionMenu * actionMenu)283 void AIMAccount::fillActionMenu( KActionMenu *actionMenu )
284 {
285 	Kopete::Account::fillActionMenu( actionMenu );
286 
287 	actionMenu->addSeparator();
288 
289 	mJoinChatAction->setEnabled( isConnected() );
290 	actionMenu->addAction( mJoinChatAction );
291 
292 	mEditInfoAction->setEnabled( isConnected() );
293 	actionMenu->addAction( mEditInfoAction );
294 
295 	Oscar::Presence pres( presence().type(), presence().flags() | Oscar::Presence::Invisible );
296     mActionInvisible->setIcon( QIcon( protocol()->statusManager()->onlineStatusOf( pres ).iconFor( this ) ) );
297 	mActionInvisible->setChecked( (presence().flags() & Oscar::Presence::Invisible) == Oscar::Presence::Invisible );
298 	actionMenu->addAction( mActionInvisible );
299 }
300 
setPresenceFlags(Oscar::Presence::Flags flags,const QString & message)301 void AIMAccount::setPresenceFlags( Oscar::Presence::Flags flags, const QString &message )
302 {
303 	Oscar::Presence pres = presence();
304 	kDebug(OSCAR_AIM_DEBUG) << "new flags=" << (int)flags << ", old type="
305 	                        << (int)pres.flags() << ", new message=" << message << endl;
306 	setPresenceTarget( Oscar::Presence( pres.type(), flags ), message );
307 }
308 
setPresenceType(Oscar::Presence::Type type,const QString & message)309 void AIMAccount::setPresenceType( Oscar::Presence::Type type, const QString &message )
310 {
311 	Oscar::Presence pres = presence();
312 	kDebug(OSCAR_AIM_DEBUG) << "new type=" << (int)type << ", old type="
313 	                        << (int)pres.type() << ", new message=" << message << endl;
314 	setPresenceTarget( Oscar::Presence( type, pres.flags() ), message );
315 }
316 
setPresenceTarget(const Oscar::Presence & newPres,const QString & message)317 void AIMAccount::setPresenceTarget( const Oscar::Presence &newPres, const QString &message )
318 {
319 	bool targetIsOffline = (newPres.type() == Oscar::Presence::Offline);
320 	bool accountIsOffline = ( presence().type() == Oscar::Presence::Offline ||
321 	                          myself()->onlineStatus() == protocol()->statusManager()->connectingStatus() );
322 
323 	if ( targetIsOffline )
324 	{
325 		OscarAccount::disconnect();
326 		// allow toggling invisibility when offline
327 		myself()->setOnlineStatus( protocol()->statusManager()->onlineStatusOf( newPres ) );
328 	}
329 	else if ( accountIsOffline )
330 	{
331 		mInitialStatusMessage = message;
332 		OscarAccount::connect( protocol()->statusManager()->onlineStatusOf( newPres ) );
333 	}
334 	else
335 	{
336 		engine()->setStatus( protocol()->statusManager()->oscarStatusOf( newPres ), message );
337 	}
338 }
339 
setOnlineStatus(const Kopete::OnlineStatus & status,const Kopete::StatusMessage & reason,const OnlineStatusOptions & options)340 void AIMAccount::setOnlineStatus( const Kopete::OnlineStatus& status, const Kopete::StatusMessage &reason, const OnlineStatusOptions& options )
341 {
342 	if ( status.status() == Kopete::OnlineStatus::Invisible )
343 	{
344 		// called from outside, i.e. not by our custom action menu entry...
345 
346 		if ( presence().type() == Oscar::Presence::Offline )
347 		{
348 			// ...when we are offline go online invisible.
349 			setPresenceTarget( Oscar::Presence( Oscar::Presence::Online, Oscar::Presence::Invisible ) );
350 		}
351 		else
352 		{
353 			// ...when we are not offline set invisible.
354 			setPresenceFlags( Oscar::Presence::Invisible );
355 		}
356 	}
357 	else
358 	{
359 		Oscar::Presence pres = protocol()->statusManager()->presenceOf( status );
360 		if ( options & Kopete::Account::KeepSpecialFlags )
361 			pres.setFlags( presence().flags() );
362 
363 		setPresenceTarget( pres, reason.message() );
364 	}
365 }
366 
setStatusMessage(const Kopete::StatusMessage & statusMessage)367 void AIMAccount::setStatusMessage( const Kopete::StatusMessage& statusMessage )
368 {
369 	setOnlineStatus( myself()->onlineStatus(), statusMessage, Kopete::Account::KeepSpecialFlags );
370 }
371 
setUserProfile(const QString & profile)372 void AIMAccount::setUserProfile(const QString &profile)
373 {
374 	kDebug(14152) << "called.";
375 	AIMMyselfContact* aimmc = dynamic_cast<AIMMyselfContact*>( myself() );
376 	if ( aimmc )
377 		aimmc->setOwnProfile( profile );
378 	configGroup()->writeEntry( QString::fromLatin1( "Profile" ), profile );
379 }
380 
slotEditInfo()381 void AIMAccount::slotEditInfo()
382 {
383 	if ( !isConnected() )
384 	{
385 		KMessageBox::sorry( Kopete::UI::Global::mainWidget(),
386 				i18n( "Editing your user info is not possible because "
387 					"you are not connected." ),
388 				i18n( "Unable to edit user info" ) );
389 		return;
390 	}
391 	QPointer <AIMUserInfoDialog> myInfo = new AIMUserInfoDialog(static_cast<AIMContact *>( myself() ), this);
392 	myInfo->exec(); // This is a modal dialog
393 	delete myInfo;
394 }
395 
slotToggleInvisible()396 void AIMAccount::slotToggleInvisible()
397 {
398 	using namespace AIM;
399 	if ( (presence().flags() & Presence::Invisible) == Presence::Invisible )
400 		setPresenceFlags( presence().flags() & ~Presence::Invisible );
401 	else
402 		setPresenceFlags( presence().flags() | Presence::Invisible );
403 }
404 
slotJoinChat()405 void AIMAccount::slotJoinChat()
406 {
407 	if ( !isConnected() )
408 	{
409 		KMessageBox::sorry( Kopete::UI::Global::mainWidget(),
410 				i18n( "Joining an AIM chat room is not possible because "
411 					"you are not connected." ),
412 				i18n( "Unable to Join AIM Chat Room" ) );
413 		return;
414 	}
415 
416 	//get the exchange info
417 	//create the dialog
418 	//join the chat room
419 	if ( !m_joinChatDialog )
420 	{
421 		m_joinChatDialog = new AIMJoinChatUI( this, Kopete::UI::Global::mainWidget() );
422 		QObject::connect(m_joinChatDialog, &AIMJoinChatUI::closing, this, &AIMAccount::joinChatDialogClosed);
423 		QList<int> list = engine()->chatExchangeList();
424 		m_joinChatDialog->setExchangeList( list );
425 		m_joinChatDialog->show();
426 	}
427 	else
428 		m_joinChatDialog->raise();
429 }
430 
joinChatDialogClosed(int code)431 void AIMAccount::joinChatDialogClosed( int code )
432 {
433 	if ( code == QDialog::Accepted )
434 	{
435 		//join the chat
436 		kDebug(14152) << "chat accepted.";
437 		engine()->joinChatRoom( m_joinChatDialog->roomName(),
438 				m_joinChatDialog->exchange().toInt() );
439 	}
440 
441 	m_joinChatDialog->delayedDestruct();
442 	m_joinChatDialog = nullptr;
443 }
444 
loginActions()445 void AIMAccount::loginActions()
446 {
447 	OscarAccount::loginActions();
448 
449 	using namespace AIM::PrivacySettings;
450 	int privacySetting = this->configGroup()->readEntry( "PrivacySetting", int(AllowAll) );
451 	this->setPrivacySettings( privacySetting );
452 }
453 
disconnected(DisconnectReason reason)454 void AIMAccount::disconnected( DisconnectReason reason )
455 {
456 	kDebug( OSCAR_AIM_DEBUG ) << "Attempting to set status offline";
457 	Oscar::Presence pres( Oscar::Presence::Offline, presence().flags() );
458 	myself()->setOnlineStatus( protocol()->statusManager()->onlineStatusOf( pres ) );
459 
460 	QHash<QString, Kopete::Contact*> contactList = contacts();
461 	foreach( Kopete::Contact* c, contactList )
462 	{
463 		OscarContact* oc = dynamic_cast<OscarContact*>( c );
464 		if ( oc )
465 			oc->userOffline( oc->contactId() );
466 	}
467 
468 	OscarAccount::disconnected( reason );
469 }
470 
messageReceived(const Oscar::Message & message)471 void AIMAccount::messageReceived( const Oscar::Message& message )
472 {
473 	kDebug(14152) << " Got a message, calling OscarAccount::messageReceived";
474 	// Want to call the parent to do everything else
475 	if ( message.channel() != 0x0003 )
476 	{
477 		OscarAccount::messageReceived(message);
478 
479 		// Check to see if our status is away, and send an away message
480 		// Might be duplicate code from the parent class to get some needed information
481 		// Perhaps a refactoring is needed.
482 		kDebug(14152) << "Checking to see if I'm online..";
483 		if( myself()->onlineStatus().status() == Kopete::OnlineStatus::Away ||
484 			myself()->onlineStatus().status() == Kopete::OnlineStatus::Busy)
485 		{
486 			QString sender = Oscar::normalize( message.sender() );
487 			AIMContact* aimSender = dynamic_cast<AIMContact *> ( contacts().value( sender ) ); //should exist now
488 			if ( !aimSender )
489 			{
490 				kWarning(OSCAR_RAW_DEBUG) << "For some reason, could not get the contact "
491 					<< "That this message is from: " << message.sender() << ", Discarding message" << endl;
492 				return;
493 			}
494 			// Create, or get, a chat session with the contact
495 			Kopete::ChatSession* chatSession = aimSender->manager( Kopete::Contact::CanCreate );
496 			Q_UNUSED(chatSession);
497 
498 			// get the away message we have set
499 			QString msg = engine()->statusMessage();
500 			kDebug(14152) << "Got away message: " << msg;
501 			// Create the message
502 			Kopete::Message chatMessage( myself(), aimSender );
503 			chatMessage.setHtmlBody( msg );
504 			chatMessage.setDirection( Kopete::Message::Outbound );
505 
506 			kDebug(14152) << "Sending autoresponse";
507 			// Send the message
508 			aimSender->sendAutoResponse( chatMessage );
509 		}
510 	}
511 	else
512 	{
513 		kDebug(OSCAR_AIM_DEBUG) << "have chat message";
514 		//handle chat room messages separately
515 		QList<Kopete::ChatSession*> chats = Kopete::ChatSessionManager::self()->sessions();
516 		QList<Kopete::ChatSession*>::iterator it,  itEnd = chats.end();
517 		for ( it = chats.begin(); it != itEnd; ++it )
518 		{
519 			Kopete::ChatSession* kcs = ( *it );
520 			AIMChatSession* session = dynamic_cast<AIMChatSession*>( kcs );
521 			if ( !session )
522 				continue;
523 
524 			if ( session->exchange() == message.exchange() &&
525 					Oscar::normalize( session->roomName() ) ==
526 					Oscar::normalize( message.chatRoom() ) )
527 			{
528 				kDebug(OSCAR_AIM_DEBUG) << "found chat session for chat room";
529 				OscarContact* ocSender = static_cast<OscarContact*>(contacts().value( Oscar::normalize( message.sender() ) ));
530 				//sanitize;
531 				QString sanitizedMsg = sanitizedMessage( message.text( defaultCodec() ) );
532 
533 				Kopete::Message chatMessage( ocSender, myself() );
534 				chatMessage.setDirection( Kopete::Message::Inbound );
535 				chatMessage.setHtmlBody( sanitizedMsg );
536 				chatMessage.setTimestamp( message.timestamp() );
537 
538 				session->appendMessage( chatMessage );
539 			}
540 		}
541 	}
542 }
543 
connectedToChatRoom(Oscar::WORD exchange,const QString & room)544 void AIMAccount::connectedToChatRoom( Oscar::WORD exchange, const QString& room )
545 {
546 	kDebug(OSCAR_AIM_DEBUG) << "Creating chat room session";
547 	Kopete::ContactPtrList emptyList;
548 	AIMMyselfContact* me = static_cast<AIMMyselfContact*>( myself() );
549 	AIMChatSession* session = static_cast<AIMChatSession*>( me->manager( Kopete::Contact::CanCreate,
550 				exchange, room ) );
551 	session->setDisplayName( room );
552 	if ( session->view( true ) )
553 		session->raiseView();
554 }
555 
userJoinedChat(Oscar::WORD exchange,const QString & room,const QString & contact)556 void AIMAccount::userJoinedChat( Oscar::WORD exchange, const QString& room, const QString& contact )
557 {
558 	if ( Oscar::normalize( contact ) == Oscar::normalize( myself()->contactId() ) )
559 		return;
560 
561 	kDebug(OSCAR_AIM_DEBUG) << "user " << contact << " has joined the chat";
562 	QList<Kopete::ChatSession*> chats = Kopete::ChatSessionManager::self()->sessions();
563 	QList<Kopete::ChatSession*>::iterator it, itEnd = chats.end();
564 	for ( it = chats.begin(); it != itEnd; ++it )
565 	{
566 		Kopete::ChatSession* kcs = ( *it );
567 		AIMChatSession* session = dynamic_cast<AIMChatSession*>( kcs );
568 		if ( !session )
569 			continue;
570 
571 		kDebug(OSCAR_AIM_DEBUG) << session->exchange() << " " << exchange;
572 		kDebug(OSCAR_AIM_DEBUG) << session->roomName() << " " << room;
573 		if ( session->exchange() == exchange && session->roomName() == room )
574 		{
575 			kDebug(OSCAR_AIM_DEBUG) << "found correct chat session";
576 			Kopete::Contact* c = contacts().value( Oscar::normalize( contact ) );
577 			if ( !c )
578 			{
579 				Kopete::MetaContact* mc = addContact( Oscar::normalize( contact ),
580 						contact, 0, Kopete::Account::Temporary );
581 				if ( !mc )
582 					kWarning(OSCAR_AIM_DEBUG) << "Unable to add contact for chat room";
583 
584 				c = mc->contacts().first();
585 				c->setNickName( contact );
586 			}
587 
588 			kDebug(OSCAR_AIM_DEBUG) << "adding contact";
589 			Kopete::OnlineStatus status = protocol()->statusManager()->onlineStatusOf( Oscar::Presence( Oscar::Presence::Online ) );
590 			session->addContact( c, status, true /* suppress */ );
591 		}
592 	}
593 }
594 
userLeftChat(Oscar::WORD exchange,const QString & room,const QString & contact)595 void AIMAccount::userLeftChat( Oscar::WORD exchange, const QString& room, const QString& contact )
596 {
597 	if ( Oscar::normalize( contact ) == Oscar::normalize( myself()->contactId() ) )
598 		return;
599 
600 	QList<Kopete::ChatSession*> chats = Kopete::ChatSessionManager::self()->sessions();
601 	QList<Kopete::ChatSession*>::iterator it, itEnd = chats.end();
602 	for ( it = chats.begin(); it != itEnd; ++it )
603 	{
604 		Kopete::ChatSession* kcs = ( *it );
605 		AIMChatSession* session = dynamic_cast<AIMChatSession*>( kcs );
606 		if ( !session )
607 			continue;
608 
609 		if ( session->exchange() == exchange && session->roomName() == room )
610 		{
611 			//delete temp contact
612 			Kopete::Contact* c = contacts().value( Oscar::normalize( contact ) );
613 			if ( !c )
614 			{
615 				kWarning(OSCAR_AIM_DEBUG) << "couldn't find the contact that's left the chat!";
616 				continue;
617 			}
618 			session->removeContact( c );
619 			Kopete::MetaContact* mc = c->metaContact();
620 			if ( mc->isTemporary() )
621 			{
622 				mc->removeContact( c );
623 				delete c;
624 				delete mc;
625 			}
626 		}
627 	}
628 }
629 
connectWithPassword(const QString & password)630 void AIMAccount::connectWithPassword( const QString &password )
631 {
632 	if ( password.isNull() )
633 		return;
634 
635 	kDebug(14152) << "accountId='" << accountId() << "'";
636 
637 	Kopete::OnlineStatus status = initialStatus();
638 	if ( status == Kopete::OnlineStatus() && status.status() == Kopete::OnlineStatus::Unknown )
639 		//use default online in case of invalid online status for connecting
640 		status = Kopete::OnlineStatus( Kopete::OnlineStatus::Online );
641 
642 	Oscar::Presence pres = protocol()->statusManager()->presenceOf( status );
643 	bool accountIsOffline = ( presence().type() == Oscar::Presence::Offline ||
644 	                          myself()->onlineStatus() == protocol()->statusManager()->connectingStatus() );
645 
646 	if ( accountIsOffline )
647 	{
648 		kDebug(14152) << "Logging in as " << accountId();
649 		myself()->setOnlineStatus( protocol()->statusManager()->connectingStatus() );
650 
651 		// Get the screen name for this account
652 		QString screenName = accountId();
653 		QString server = configGroup()->readEntry( "Server", QString::fromLatin1( "login.oscar.aol.com" ) );
654 		uint port = configGroup()->readEntry( "Port", 5190 );
655 
656 		//set up the settings for the account
657 		Oscar::Settings* oscarSettings = engine()->clientSettings();
658 		oscarSettings->setFileProxy( configGroup()->readEntry( "FileProxy", true ) );
659 		oscarSettings->setFirstPort( configGroup()->readEntry( "FirstPort", 5190 ) );
660 		oscarSettings->setLastPort( configGroup()->readEntry( "LastPort", 5199 ) );
661 		oscarSettings->setTimeout( configGroup()->readEntry( "Timeout", 10 ) );
662 
663 		Oscar::DWORD status = protocol()->statusManager()->oscarStatusOf( pres );
664 		updateVersionUpdaterStamp();
665 
666 		engine()->start( server, port, accountId(), password.left(16) );
667 		engine()->setStatus( status, mInitialStatusMessage );
668 		engine()->connectToServer( server, port, false, QString() );
669 
670 		mInitialStatusMessage.clear();
671 	}
672 }
673 
setPrivacySettings(int privacy)674 void AIMAccount::setPrivacySettings( int privacy )
675 {
676 	using namespace AIM::PrivacySettings;
677 
678 	Oscar::BYTE privacyByte = 0x01;
679 	Oscar::DWORD userClasses = 0xFFFFFFFF;
680 
681 	switch ( privacy )
682 	{
683 		case AllowAll:
684 			privacyByte = 0x01;
685 			break;
686 		case BlockAll:
687 			privacyByte = 0x02;
688 			break;
689 		case AllowPremitList:
690 			privacyByte = 0x03;
691 			break;
692 		case BlockDenyList:
693 			privacyByte = 0x04;
694 			break;
695 		case AllowMyContacts:
696 			privacyByte = 0x05;
697 			break;
698 		case BlockAIM:
699 			privacyByte = 0x01;
700 			userClasses = 0x00000004;
701 			break;
702 	}
703 
704 	engine()->setPrivacyTLVs( privacyByte, userClasses );
705 }
706 
707