1 /*
2   icqontact.cpp  -  Oscar Protocol Plugin
3 
4   Copyright (c) 2003      by Stefan Gehn  <metz@gehn.net>
5   Copyright (c) 2003      by Olivier Goffart <oggoffart@kde.org>
6   Copyright (c) 2006,2007 by Roman Jarosz <kedgedev@centrum.cz>
7 
8   Kopete    (c) 2003-2007 by the Kopete developers  <kopete-devel@kde.org>
9 
10   *************************************************************************
11   *                                                                       *
12   * This program is free software; you can redistribute it and/or modify  *
13   * it under the terms of the GNU General Public License as published by  *
14   * the Free Software Foundation; either version 2 of the License, or     *
15   * (at your option) any later version.                                   *
16   *                                                                       *
17   *************************************************************************
18 */
19 
20 #include "icqcontact.h"
21 
22 #include <QPointer>
23 #include <qtimer.h>
24 #include <KActionCollection>
25 #include <KLocalizedString>
26 #include <knotification.h>
27 #include <qinputdialog.h>
28 #include <krandom.h>
29 #include <ktoggleaction.h>
30 #include <QIcon>
31 
32 #include "kopeteuiglobal.h"
33 #include "kopetemetacontact.h"
34 
35 #include "icqprotocol.h"
36 #include "icqaccount.h"
37 #include "icquserinfowidget.h"
38 #include "icqauthreplydialog.h"
39 
40 #include "oscarutils.h"
41 #include "contactmanager.h"
42 #include "oscarstatusmanager.h"
43 
ICQContact(Kopete::Account * account,const QString & name,Kopete::MetaContact * parent,const QString & icon)44 ICQContact::ICQContact( Kopete::Account* account, const QString &name, Kopete::MetaContact *parent,
45 						const QString& icon )
46 : ICQContactBase( account, name, parent, icon )
47 {
48 	m_requestingInfo = InfoNone;
49 	mProtocol = static_cast<ICQProtocol *>(protocol());
50 	m_infoWidget = nullptr;
51 
52 	setPresenceTarget( Oscar::Presence( Oscar::Presence::Offline ) );
53 
54 	QObject::connect( mAccount->engine(), SIGNAL(loggedIn()), this, SLOT(loggedIn()) );
55 	//QObject::connect( mAccount->engine(), SIGNAL(userIsOnline(QString)), this, SLOT(userOnline(QString,UserDetails)) );
56 	QObject::connect( mAccount->engine(), SIGNAL(userIsOffline(QString)), this, SLOT(userOffline(QString)) );
57 	QObject::connect( mAccount->engine(), SIGNAL(authReplyReceived(QString,QString,bool)),
58 	                  this, SLOT(slotGotAuthReply(QString,QString,bool)) );
59 	QObject::connect( mAccount->engine(), SIGNAL(receivedIcqShortInfo(QString)),
60 	                  this, SLOT(receivedShortInfo(QString)) );
61 	QObject::connect( mAccount->engine(), SIGNAL(receivedIcqLongInfo(QString)),
62 	                  this, SLOT(receivedLongInfo(QString)) );
63 	QObject::connect( mAccount->engine(), SIGNAL(receivedUserInfo(QString,UserDetails)),
64 	                  this, SLOT(userInfoUpdated(QString,UserDetails)) );
65 	QObject::connect( mAccount->engine(), SIGNAL(receivedIcqTlvInfo(QString)),
66 	                  this, SLOT(receivedTlvInfo(QString)) );
67 }
68 
~ICQContact()69 ICQContact::~ICQContact()
70 {
71 	delete m_infoWidget;
72 }
73 
setSSIItem(const OContact & ssiItem)74 void ICQContact::setSSIItem( const OContact& ssiItem )
75 {
76 	if ( ssiItem.waitingAuth() )
77 		setOnlineStatus( mProtocol->statusManager()->waitingForAuth() );
78 
79 	if ( ssiItem.type() != 0xFFFF && ssiItem.waitingAuth() == false &&
80 	     onlineStatus().status() == Kopete::OnlineStatus::Unknown )
81 	{
82 		//make sure they're offline
83 		setPresenceTarget( Oscar::Presence( Oscar::Presence::Offline ) );
84 	}
85 
86 	if ( mAccount->engine()->isActive() && m_ssiItem.metaInfoId() != ssiItem.metaInfoId() )
87 	{
88 		// User info has changed, check nickname or status description if needed.
89 		// If mAccount->isConnected() is false then the metaInfoId has changed while
90 		// we were offline and we don't know how many users changed its info, so better
91 		// delay the request.
92 		if ( mAccount->isConnected() )
93 			QTimer::singleShot( 0, this, SLOT(requestMediumTlvInfo()) );
94 		else
95 			requestMediumTlvInfoDelayed();
96 	}
97 
98 	ICQContactBase::setSSIItem( ssiItem );
99 }
100 
setEncoding(int mib)101 void ICQContact::setEncoding( int mib )
102 {
103 	ICQContactBase::setEncoding( mib );
104 	QTimer::singleShot( 0, this, SLOT(requestShortInfo()) );
105 }
106 
userInfoUpdated(const QString & contact,const UserDetails & details)107 void ICQContact::userInfoUpdated( const QString& contact, const UserDetails& details )
108 {
109 	if ( Oscar::normalize( contact  ) != Oscar::normalize( contactId() ) )
110 		return;
111 
112 	// invalidate old away message if user was offline
113 	if ( !isOnline() )
114 	{
115 		removeProperty( mProtocol->statusTitle );
116 		removeProperty( mProtocol->statusMessage );
117 	}
118 
119 	kDebug( OSCAR_ICQ_DEBUG ) << "extendedStatus is " << details.extendedStatus();
120 	Oscar::Presence presence = mProtocol->statusManager()->presenceOf( details.extendedStatus(), details.userClass() );
121 
122 	if ( details.dcOutsideSpecified() )
123 		setProperty( mProtocol->ipAddress, details.dcExternalIp().toString() );
124 
125 	if ( details.capabilitiesSpecified() )
126 		setProperty( mProtocol->clientFeatures, details.clientName() );
127 
128 	OscarContact::userInfoUpdated( contact, details );
129 
130 	refreshStatus( m_details, presence );
131 }
132 
refreshStatus(const UserDetails & details,Oscar::Presence presence)133 void ICQContact::refreshStatus( const UserDetails& details, Oscar::Presence presence )
134 {
135 	// Filter our XStatus and ExtStatus
136 	presence.setFlags( presence.flags() & ~Oscar::Presence::StatusTypeMask );
137 
138 	if ( details.statusMood() != -1 )
139 	{
140 		presence.setFlags( presence.flags() | Oscar::Presence::ExtStatus2 );
141 		presence.setMood( details.statusMood() );
142 
143 		Kopete::StatusMessage msg;
144 		msg.setTitle( details.personalMessage() );
145 		setStatusMessage( msg );
146 	}
147 	// XStatus don't support offline status so don't show it (xtrazStatusSpecified can be true if contact was online)
148 	else if ( details.xtrazStatus() != -1 && presence.type() != Oscar::Presence::Offline )
149 	{
150 		presence.setFlags( presence.flags() | Oscar::Presence::XStatus );
151 		presence.setXtrazStatus( details.xtrazStatus() );
152 
153 		Kopete::StatusMessage msg;
154 		msg.setTitle( details.personalMessage() );
155 		setStatusMessage( msg );
156 	}
157 	else if ( !details.personalMessage().isEmpty() )
158 	{
159 		presence.setFlags( presence.flags() | Oscar::Presence::ExtStatus );
160 
161 		Kopete::StatusMessage msg;
162 		msg.setTitle( details.personalMessage() );
163 		setStatusMessage( msg );
164 	}
165 	else
166 	{
167 		Kopete::StatusMessage msg;
168 		setStatusMessage( msg ); // set an empty status message
169 	}
170 
171 	setPresenceTarget( presence );
172 
173 	Oscar::Presence selfPres( mProtocol->statusManager()->presenceOf( account()->myself()->onlineStatus() ) );
174 	bool selfVisible = !(selfPres.flags() & Oscar::Presence::Invisible);
175 
176 	if ( selfVisible && isReachable() && presence.type() != Oscar::Presence::Offline )
177 	{
178 		Client::ICQStatus contactStatus = Client::ICQOnline;
179 		if ( details.xtrazStatus() != -1 )
180 		{
181 			contactStatus = Client::ICQXStatus;
182 		}
183 		else
184 		{
185 			switch ( presence.type() )
186 			{
187 			case Oscar::Presence::Online:
188 				contactStatus = Client::ICQOnline;
189 				break;
190 			case Oscar::Presence::Away:
191 				contactStatus = Client::ICQAway;
192 				break;
193 			case Oscar::Presence::NotAvailable:
194 				contactStatus = Client::ICQNotAvailable;
195 				break;
196 			case Oscar::Presence::Occupied:
197 				contactStatus = Client::ICQOccupied;
198 				break;
199 			case Oscar::Presence::DoNotDisturb:
200 				contactStatus = Client::ICQDoNotDisturb;
201 				break;
202 			case Oscar::Presence::FreeForChat:
203 				contactStatus = Client::ICQFreeForChat;
204 				break;
205 			default:
206 				break;
207 			}
208 		}
209 
210 		// FIXME: How can we check if client supports status plugin messages?
211 		if ( details.onlineStatusMsgSupport() )
212 			contactStatus |= Client::ICQPluginStatus;
213 
214 		// If contact is online and doesn't support status plugin messages than
215 		// this contact can't have online status message.
216 		if ( contactStatus == Client::ICQOnline && !details.onlineStatusMsgSupport() )
217 		{
218 			mAccount->engine()->removeICQAwayMessageRequest( contactId() );
219 			removeProperty( mProtocol->statusMessage );
220 		}
221 		else
222 		{
223 			mAccount->engine()->addICQAwayMessageRequest( contactId(), contactStatus );
224 		}
225 	}
226 	else
227 	{
228 		mAccount->engine()->removeICQAwayMessageRequest( contactId() );
229 	}
230 }
231 
userOnline(const QString & userId)232 void ICQContact::userOnline( const QString& userId )
233 {
234 	if ( Oscar::normalize( userId ) != Oscar::normalize( contactId() ) )
235 		return;
236 
237 	kDebug(OSCAR_ICQ_DEBUG) << "Setting " << userId << " online";
238 	setPresenceTarget( Oscar::Presence( Oscar::Presence::Online ) );
239 }
240 
userOffline(const QString & userId)241 void ICQContact::userOffline( const QString& userId )
242 {
243 	if ( Oscar::normalize( userId ) != Oscar::normalize( contactId() ) )
244 		return;
245 
246 	m_details.clear();
247 
248 	kDebug(OSCAR_ICQ_DEBUG) << "Setting " << userId << " offline";
249 	if ( m_ssiItem.waitingAuth() )
250 		setOnlineStatus( mProtocol->statusManager()->waitingForAuth() );
251 	else
252 		refreshStatus( m_details, Oscar::Presence( Oscar::Presence::Offline ) );
253 
254 	removeProperty( mProtocol->statusTitle );
255 	removeProperty( mProtocol->statusMessage );
256 }
257 
loggedIn()258 void ICQContact::loggedIn()
259 {
260 	if ( metaContact()->isTemporary() )
261 		return;
262 
263 	if ( m_ssiItem.waitingAuth() )
264 		setOnlineStatus( mProtocol->statusManager()->waitingForAuth() );
265 
266 	requestShortInfoDelayed();
267 }
268 
slotRequestAuth()269 void ICQContact::slotRequestAuth()
270 {
271     QString reason = QInputDialog::getText( nullptr, i18n("Request Authorization"),
272                                             i18n("Reason for requesting authorization:"), QLineEdit::Normal,
273 	                                        i18n("Please authorize me so I can add you to my contact list") );
274 	if ( !reason.isNull() )
275 		mAccount->engine()->requestAuth( contactId(), reason );
276 }
277 
slotSendAuth()278 void ICQContact::slotSendAuth()
279 {
280 	kDebug(OSCAR_ICQ_DEBUG) << "Sending auth reply";
281 	QPointer <ICQAuthReplyDialog> replyDialog = new ICQAuthReplyDialog( 0, false );
282 
283 	replyDialog->setUser( displayName() );
284 	if ( replyDialog->exec() && replyDialog )
285 		mAccount->engine()->sendAuth( contactId(), replyDialog->reason(), replyDialog->grantAuth() );
286 	delete replyDialog;
287 }
288 
slotGotAuthReply(const QString & contact,const QString & reason,bool granted)289 void ICQContact::slotGotAuthReply( const QString& contact, const QString& reason, bool granted )
290 {
291 	if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
292 		return;
293 
294 	if ( account()->isBusy() )
295 		return;
296 
297 	kDebug(OSCAR_ICQ_DEBUG) ;
298 	QString message;
299 	if( granted )
300 	{
301 		message = i18n( "User %1 has granted your authorization request.\nReason: %2" ,
302 			  displayName() ,
303 			  reason );
304 
305 		// remove the unknown status
306 		setPresenceTarget( Oscar::Presence( Oscar::Presence::Offline ) );
307 	}
308 	else
309 	{
310 		message = i18n( "User %1 has rejected the authorization request.\nReason: %2" ,
311 			  displayName() ,
312 			  reason );
313 	}
314 	KNotification::event( QString::fromLatin1("icq_authorization"), message );
315 }
316 
requestShortInfo()317 void ICQContact::requestShortInfo()
318 {
319 	kDebug(OSCAR_ICQ_DEBUG) << "requesting short info for " << contactId();
320 	if ( mAccount->engine()->isActive() )
321 		mAccount->engine()->requestShortInfo( contactId() );
322 
323 	// Don't clear m_requestingInfo if info with higher contents was requested
324 	if ( m_requestingInfo <= InfoShort )
325 		m_requestingInfo = InfoNone;
326 }
327 
receivedShortInfo(const QString & contact)328 void ICQContact::receivedShortInfo( const QString& contact )
329 {
330 	if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
331 		return;
332 
333 	QTextCodec* codec = contactCodec();
334 
335 	ICQShortInfo shortInfo = mAccount->engine()->getShortInfo( contact );
336 
337 	setProperty( mProtocol->firstName, codec->toUnicode( shortInfo.firstName ) );
338 	setProperty( mProtocol->lastName, codec->toUnicode( shortInfo.lastName ) );
339 	setNickName( codec->toUnicode( shortInfo.nickname ) );
340 }
341 
receivedLongInfo(const QString & contact)342 void ICQContact::receivedLongInfo( const QString& contact )
343 {
344 	if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
345 	{
346 		if ( m_infoWidget )
347 			m_infoWidget->deleteLater();
348 		return;
349 	}
350 
351 	QTextCodec* codec = contactCodec();
352 
353 	kDebug(OSCAR_ICQ_DEBUG) << "received long info from engine";
354 
355 	ICQGeneralUserInfo genInfo = mAccount->engine()->getGeneralInfo( contact );
356 
357 	setProperty( mProtocol->firstName, codec->toUnicode( genInfo.firstName.get() ) );
358 	setProperty( mProtocol->lastName, codec->toUnicode( genInfo.lastName.get() ) );
359 	setNickName( codec->toUnicode( genInfo.nickName.get() ) );
360 
361 	emit haveBasicInfo( genInfo );
362 
363 	ICQWorkUserInfo workInfo = mAccount->engine()->getWorkInfo( contact );
364 	emit haveWorkInfo( workInfo );
365 
366 	ICQEmailInfo emailInfo = mAccount->engine()->getEmailInfo( contact );
367 	emit haveEmailInfo( emailInfo );
368 
369 	ICQNotesInfo notesInfo = mAccount->engine()->getNotesInfo( contact );
370 	emit haveNotesInfo( notesInfo );
371 
372 	ICQMoreUserInfo moreInfo = mAccount->engine()->getMoreInfo( contact );
373 	emit haveMoreInfo( moreInfo );
374 
375 	ICQInterestInfo interestInfo = mAccount->engine()->getInterestInfo( contact );
376 	emit haveInterestInfo( interestInfo );
377 
378 	ICQOrgAffInfo orgAffInfo = mAccount->engine()->getOrgAffInfo( contact );
379 	emit haveOrgAffInfo( orgAffInfo );
380 }
381 
requestMediumTlvInfo()382 void ICQContact::requestMediumTlvInfo()
383 {
384 	kDebug(OSCAR_ICQ_DEBUG) << "requesting medium tlv info for " << contactId();
385 	if ( mAccount->engine()->isActive() && !m_ssiItem.metaInfoId().isEmpty() )
386 		mAccount->engine()->requestMediumTlvInfo( contactId(), m_ssiItem.metaInfoId() );
387 
388 	// Don't clear m_requestingInfo if info with higher contents was requested
389 	if ( m_requestingInfo <= InfoMediumTlv )
390 		m_requestingInfo = InfoNone;
391 }
392 
receivedTlvInfo(const QString & contact)393 void ICQContact::receivedTlvInfo( const QString& contact )
394 {
395 	if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
396 		return;
397 
398 	ICQFullInfo info = mAccount->engine()->getFullInfo( contact );
399 
400 	setProperty( mProtocol->firstName, QString::fromUtf8( info.firstName.get() ) );
401 	setProperty( mProtocol->lastName, QString::fromUtf8( info.lastName.get() ) );
402 	setNickName( QString::fromUtf8( info.nickName.get() ) );
403 }
404 
requestShortInfoDelayed(int minDelay)405 void ICQContact::requestShortInfoDelayed( int minDelay )
406 {
407 	if ( mAccount->engine()->isActive() && m_requestingInfo < InfoShort )
408 	{
409 		m_requestingInfo = InfoShort;
410 		int time = ( KRandom::random() % 20 ) * 1000 + minDelay;
411 		kDebug(OSCAR_ICQ_DEBUG) << "requesting info in " << time/1000 << " seconds";
412 		QTimer::singleShot( time, this, SLOT(infoDelayTimeout()) );
413 	}
414 }
415 
requestMediumTlvInfoDelayed(int minDelay)416 void ICQContact::requestMediumTlvInfoDelayed( int minDelay )
417 {
418 	if ( mAccount->engine()->isActive() && m_requestingInfo < InfoMediumTlv )
419 	{
420 		m_requestingInfo = InfoMediumTlv;
421 		int time = ( KRandom::random() % 20 ) * 1000 + minDelay;
422 		kDebug(OSCAR_ICQ_DEBUG) << "requesting info in " << time/1000 << " seconds";
423 		QTimer::singleShot( time, this, SLOT(infoDelayTimeout()) );
424 	}
425 }
426 
infoDelayTimeout()427 void ICQContact::infoDelayTimeout()
428 {
429 	if ( m_requestingInfo == InfoMediumTlv )
430 		requestMediumTlvInfo();
431 	else if ( m_requestingInfo == InfoShort )
432 		requestShortInfo();
433 }
434 
435 #if 0
436 void ICQContact::slotContactChanged(const UserInfo &u)
437 {
438 	if (u.sn != contactName())
439 		return;
440 
441 	// update mInfo and general stuff from OscarContact
442 	slotParseUserInfo(u);
443 
444 	/*kDebug(14190) << "Called for '"
445 		<< displayName() << "', contactName()=" << contactName() << endl;*/
446 	QStringList capList;
447 	// Append client name and version in case we found one
448 	if (!mInfo.clientName.isEmpty())
449 	{
450 		if (!mInfo.clientVersion.isEmpty())
451 		{
452 			capList << i18nc("Translators: client-name client-version",
453 				"%1 %2", mInfo.clientName, mInfo.clientVersion);
454 		}
455 		else
456 		{
457 			capList << mInfo.clientName;
458 		}
459 	}
460 	// and now for some general informative capabilities
461 	if (hasCap(CAP_UTF8))
462 		capList << i18n("UTF-8");
463 	if (hasCap(CAP_RTFMSGS))
464 		capList << i18n("RTF-Messages");
465 	if (hasCap(CAP_IMIMAGE))
466 		capList << i18n("DirectIM/IMImage");
467 	if (hasCap(CAP_CHAT))
468 		capList << i18n("Groupchat");
469 
470 	if (capList.count() > 0)
471 		setProperty(mProtocol->clientFeatures, capList.join(", "));
472 	else
473 		removeProperty(mProtocol->clientFeatures);
474 
475 	unsigned int newStatus = 0;
476 	mInvisible = (mInfo.icqextstatus & ICQ_STATUS_IS_INVIS);
477 
478 	if (mInfo.icqextstatus & ICQ_STATUS_IS_FFC)
479 		newStatus = OSCAR_FFC;
480 	else if (mInfo.icqextstatus & ICQ_STATUS_IS_DND)
481 		newStatus = OSCAR_DND;
482 	else if (mInfo.icqextstatus & ICQ_STATUS_IS_OCC)
483 		newStatus = OSCAR_OCC;
484 	else if (mInfo.icqextstatus & ICQ_STATUS_IS_NA)
485 		newStatus = OSCAR_NA;
486 	else if (mInfo.icqextstatus & ICQ_STATUS_IS_AWAY)
487 		newStatus = OSCAR_AWAY;
488 	else
489 		newStatus = OSCAR_ONLINE;
490 
491 	if (this != account()->myself())
492 	{
493 		if(newStatus != onlineStatus().internalStatus())
494 		{
495 			if(newStatus != OSCAR_ONLINE) // if user changed to some state other than online
496 			{
497 				mAccount->engine()->requestAwayMessage(this);
498 			}
499 			else // user changed to "Online" status and has no away message anymore
500 			{
501 				removeProperty(mProtocol->statusMessage);
502 			}
503 		}
504 	}
505 
506 	setStatus(newStatus);
507 }
508 
509 void ICQContact::slotOffgoingBuddy(QString sender)
510 {
511 	if(sender != contactName())
512 		return;
513 
514 	removeProperty(mProtocol->clientFeatures);
515 	removeProperty(mProtocol->statusMessage);
516 	setOnlineStatus(mProtocol->statusOffline);
517 }
518 
519 void ICQContact::gotIM(OscarSocket::OscarMessageType /*type*/, const QString &message)
520 {
521 	// Build a Kopete::Message and set the body as Rich Text
522 	Kopete::ContactPtrList tmpList;
523 	tmpList.append(account()->myself());
524 	Kopete::Message msg(this, tmpList, message, Kopete::Message::Inbound,
525 		Kopete::Message::RichText);
526 	manager(true)->appendMessage(msg);
527 }
528 
529 void ICQContact::slotSendMsg(Kopete::Message& message, Kopete::ChatSession *)
530 {
531 	if (message.plainBody().isEmpty()) // no text, do nothing
532 		return;
533 
534 	// Check to see if we're even online
535 	if(!account()->isConnected())
536 	{
537 		KMessageBox::sorry(Kopete::UI::Global::mainWidget(),
538 			i18n("<qt>You must be logged on to ICQ before you can "
539 				"send a message to a user.</qt>"),
540 			i18n("Not Signed On"));
541 		return;
542 	}
543 
544 	// FIXME: We don't do HTML in ICQ
545 	// we might be able to do that in AIM and we might also convert
546 	// HTML to RTF for ICQ type-2 messages  [mETz]
547 	static_cast<OscarAccount*>(account())->engine()->sendIM(
548 		message.plainBody(), this, false);
549 
550 	// Show the message we just sent in the chat window
551 	manager(Kopete::Contact::CanCreate)->appendMessage(message);
552 	manager(Kopete::Contact::CanCreate)->messageSucceeded();
553 }
554 
555 #endif
556 
isReachable()557 bool ICQContact::isReachable()
558 {
559 	return account()->isConnected();
560 }
561 
customContextMenuActions()562 QList<QAction*> *ICQContact::customContextMenuActions()
563 {
564 	QList<QAction*> *actions = new QList<QAction*>();
565 
566 	actionRequestAuth = new QAction( i18n("&Request Authorization"), this );
567         //, "actionRequestAuth");
568     actionRequestAuth->setIcon( QIcon::fromTheme(QStringLiteral("mail-reply-sender")) );
569 	QObject::connect(actionRequestAuth, &QAction::triggered, this, &ICQContact::slotRequestAuth);
570 
571 	actionSendAuth = new QAction( i18n("&Grant Authorization"), this );
572         //, "actionSendAuth");
573     actionSendAuth->setIcon( QIcon::fromTheme(QStringLiteral("mail-forward")) );
574 	QObject::connect(actionSendAuth, &QAction::triggered, this, &ICQContact::slotSendAuth);
575 
576 	m_actionIgnore = new KToggleAction(i18n("&Ignore"), this );
577         //, "actionIgnore");
578 	QObject::connect(m_actionIgnore, &KToggleAction::triggered, this, &ICQContact::slotIgnore);
579 
580 	m_actionVisibleTo = new KToggleAction(i18n("Always &Visible To"), this );
581         //, "actionVisibleTo");
582 	QObject::connect(m_actionVisibleTo, &KToggleAction::triggered, this, &ICQContact::slotVisibleTo);
583 
584 	m_actionInvisibleTo = new KToggleAction(i18n("Always &Invisible To"), this );
585         //, "actionInvisibleTo");
586 	QObject::connect(m_actionInvisibleTo, &KToggleAction::triggered, this, &ICQContact::slotInvisibleTo);
587 
588 	m_selectEncoding = new QAction( i18n( "Select Encoding..." ), this );
589         //, "changeEncoding" );
590     m_selectEncoding->setIcon( QIcon::fromTheme(QStringLiteral("character-set")) );
591 	QObject::connect(m_selectEncoding, &QAction::triggered, this, &ICQContact::changeContactEncoding);
592 
593 	bool on = account()->isConnected();
594 	if ( m_ssiItem.waitingAuth() )
595 		actionRequestAuth->setEnabled(on);
596 	else
597 		actionRequestAuth->setEnabled(false);
598 
599 	actionSendAuth->setEnabled(on);
600 	m_actionIgnore->setEnabled(on);
601 	m_actionVisibleTo->setEnabled(on);
602 	m_actionInvisibleTo->setEnabled(on);
603 
604 	ContactManager* ssi = account()->engine()->ssiManager();
605 	m_actionIgnore->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_IGNORE ));
606 	m_actionVisibleTo->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_VISIBLE ));
607 	m_actionInvisibleTo->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_INVISIBLE ));
608 
609 	actions->append(actionRequestAuth);
610 	actions->append(actionSendAuth);
611     actions->append( m_selectEncoding );
612 
613 	actions->append(m_actionIgnore);
614 	actions->append(m_actionVisibleTo);
615 	actions->append(m_actionInvisibleTo);
616 
617 	// temporary action collection, used to apply Kiosk policy to the actions
618 	KActionCollection tempCollection((QObject*)0);
619 	tempCollection.addAction(QLatin1String("contactRequestAuth"), actionRequestAuth);
620 	tempCollection.addAction(QLatin1String("contactSendAuth"), actionSendAuth);
621 	tempCollection.addAction(QLatin1String("contactSelectEncoding"), m_selectEncoding);
622 	tempCollection.addAction(QLatin1String("contactIgnore"), m_actionIgnore);
623 	tempCollection.addAction(QLatin1String("oscarContactAlwaysVisibleTo"), m_actionVisibleTo);
624 	tempCollection.addAction(QLatin1String("oscarContactAlwaysInvisibleTo"), m_actionInvisibleTo);
625 	return actions;
626 }
627 
slotUserInfo()628 void ICQContact::slotUserInfo()
629 {
630 	if ( m_infoWidget )
631 	{
632 		m_infoWidget->showNormal();
633 		m_infoWidget->activateWindow();
634 		return;
635 	}
636 
637 	m_infoWidget = new ICQUserInfoWidget( this, Kopete::UI::Global::mainWidget() );
638 	QObject::connect(m_infoWidget, &ICQUserInfoWidget::finished, this, &ICQContact::closeUserInfoDialog);
639 	// QObject::connect(m_infoWidget, &ICQUserInfoWidget::okClicked, this, &ICQContact::storeUserInfoDialog);
640 	m_infoWidget->show();
641 }
642 
storeUserInfoDialog()643 void ICQContact::storeUserInfoDialog()
644 {
645 	QString alias = m_infoWidget->getAlias();
646 	if ( alias.isEmpty() )
647 		requestShortInfoDelayed( 5000 );
648 	else
649 		mAccount->engine()->changeContactAlias( contactId(), alias );
650 }
651 
closeUserInfoDialog()652 void ICQContact::closeUserInfoDialog()
653 {
654 	QObject::disconnect( this, 0, m_infoWidget, 0 );
655 	m_infoWidget->deleteLater();
656 	m_infoWidget = nullptr;
657 }
658 
slotIgnore()659 void ICQContact::slotIgnore()
660 {
661 	account()->engine()->setIgnore( contactId(), m_actionIgnore->isChecked() );
662 }
663 
slotVisibleTo()664 void ICQContact::slotVisibleTo()
665 {
666 	account()->engine()->setVisibleTo( contactId(), m_actionVisibleTo->isChecked() );
667 }
668 
slotInvisibleTo()669 void ICQContact::slotInvisibleTo()
670 {
671 	account()->engine()->setInvisibleTo( contactId(), m_actionInvisibleTo->isChecked() );
672 }
673 
674 #if 0
675 
676 void ICQContact::slotReadAwayMessage()
677 {
678 	kDebug(14153) << "account='" << account()->accountId() <<
679 		"', contact='" << displayName() << "'" << endl;
680 
681 	if (!awayMessageDialog)
682 	{
683 		awayMessageDialog = new ICQReadAway(this, 0L, "awayMessageDialog");
684 		if(!awayMessageDialog)
685 			return;
686 		QObject::connect(awayMessageDialog, &ICQReadAway::closing, this, &ICQContact::slotCloseAwayMessageDialog);
687 		awayMessageDialog->show();
688 	}
689 	else
690 	{
691 		awayMessageDialog->raise();
692 	}
693 }
694 
695 void ICQContact::slotCloseAwayMessageDialog()
696 {
697 	awayMessageDialog->deleteLater();
698 	awayMessageDialog = nullptr;
699 }
700 
701 const QString ICQContact::awayMessage()
702 {
703 	kDebug(14150) <<  property(mProtocol->statusMessage).value().toString();
704 	return property(mProtocol->statusMessage).value().toString();
705 }
706 
707 void ICQContact::setAwayMessage(const QString &message)
708 {
709 	/*kDebug(14150) <<
710 		"Called for '" << displayName() << "', away msg='" << message << "'" << endl;*/
711 	setProperty(mProtocol->statusMessage, message);
712 	emit awayMessageChanged();
713 }
714 
715 void ICQContact::slotUpdGeneralInfo(const int seq, const ICQGeneralUserInfo &inf)
716 {
717 	// compare reply's sequence with the one we sent with our last request
718 	if(seq != userinfoRequestSequence)
719 		return;
720 	generalInfo = inf;
721 
722 	if(!generalInfo.firstName.isEmpty())
723 		setProperty(mProtocol->firstName, generalInfo.firstName);
724 	else
725 		removeProperty(mProtocol->firstName);
726 
727 	if(!generalInfo.lastName.isEmpty())
728 		setProperty(mProtocol->lastName, generalInfo.lastName);
729 	else
730 		removeProperty(mProtocol->lastName);
731 
732 	if(!generalInfo.eMail.isEmpty())
733 		setProperty(mProtocol->emailAddress, generalInfo.eMail);
734 	else
735 		removeProperty(mProtocol->emailAddress);
736 	/*
737 	if(!generalInfo.phoneNumber.isEmpty())
738 		setProperty("privPhoneNum", generalInfo.phoneNumber);
739 	else
740 		removeProperty("privPhoneNum");
741 
742 	if(!generalInfo.faxNumber.isEmpty())
743 		setProperty("privFaxNum", generalInfo.faxNumber);
744 	else
745 		removeProperty("privFaxNum");
746 
747 	if(!generalInfo.cellularNumber.isEmpty())
748 		setProperty("privMobileNum", generalInfo.cellularNumber);
749 	else
750 		removeProperty("privMobileNum");
751 	*/
752 
753 	setDisplayName(generalInfo.nickName);
754 
755 	incUserInfoCounter();
756 }
757 
758 void ICQContact::slotSnacFailed(WORD snacID)
759 {
760 	if (userinfoRequestSequence != 0)
761 		kDebug(14153) << "snacID = " << snacID << " seq = " << userinfoRequestSequence;
762 
763 	//TODO: ugly interaction between snacID and request sequence, see OscarSocket::sendCLI_TOICQSRV
764 	if (snacID == (0x0000 << 16) | userinfoRequestSequence)
765 	{
766 		userinfoRequestSequence = 0;
767 		emit userInfoRequestFailed();
768 	}
769 }
770 
771 void ICQContact::slotIgnore()
772 {
773 	kDebug(14150) <<
774 		"Called; ignore = " << actionIgnore->isChecked() << endl;
775 	setIgnore(actionIgnore->isChecked(), true);
776 }
777 
778 void ICQContact::slotVisibleTo()
779 {
780 	kDebug(14150) <<
781 		"Called; visible = " << actionVisibleTo->isChecked() << endl;
782 	setVisibleTo(actionVisibleTo->isChecked(), true);
783 }
784 #endif
785