/***************************************************************************
jabberaccount . cpp - core Jabber account class
- - - - - - - - - - - - - - - - - - -
begin : Sat M ? ? ? 8 2003
copyright : ( C ) 2003 by Till Gerken < till @ tantalo . net >
Based on JabberProtocol by Daniel Stone < dstone @ kde . org >
and Till Gerken < till @ tantalo . net > .
copyright : ( C ) 2006 by Olivier Goffart < ogoffart at kde . org >
Kopete ( C ) 2001 - 2003 Kopete developers
< kopete - devel @ kde . org > .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/***************************************************************************
* *
* 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 "im.h"
# include "filetransfer.h"
# include "xmpp.h"
# include "xmpp_tasks.h"
# include "qca.h"
# include "bsocket.h"
# include "jabberaccount.h"
# include "jabberbookmarks.h"
# include <time.h>
# include <tqstring.h>
# include <tqregexp.h>
# include <tqtimer.h>
# include <kconfig.h>
# include <kdebug.h>
# include <kmessagebox.h>
# include <klocale.h>
# include <kapplication.h>
# include <kaboutdata.h>
# include <tdesocketbase.h>
# include <kpassdlg.h>
# include <kinputdialog.h>
# include "kopetepassword.h"
# include "kopeteawayaction.h"
# include "kopetemetacontact.h"
# include "kopeteuiglobal.h"
# include "kopetegroup.h"
# include "kopetecontactlist.h"
# include "kopeteaccountmanager.h"
# include "contactaddednotifydialog.h"
# include "jabberconnector.h"
# include "jabberclient.h"
# include "jabberprotocol.h"
# include "jabberresourcepool.h"
# include "jabbercontactpool.h"
# include "jabberfiletransfer.h"
# include "jabbercontact.h"
# include "jabbergroupcontact.h"
# include "jabbercapabilitiesmanager.h"
# include "jabbertransport.h"
# include "dlgjabbersendraw.h"
# include "dlgjabberservices.h"
# include "dlgjabberchatjoin.h"
# include <sys/utsname.h>
# ifdef SUPPORT_JINGLE
# include "voicecaller.h"
# include "jinglevoicecaller.h"
// NOTE: Disabled for 0.12, will develop them futher in KDE4
// #include "jinglesessionmanager.h"
// #include "jinglesession.h"
// #include "jinglevoicesession.h"
# include "jinglevoicesessiondialog.h"
# endif
# define KOPETE_CAPS_NODE "http: //kopete.kde.org/jabber/caps"
JabberAccount : : JabberAccount ( JabberProtocol * parent , const TQString & accountId , const char * name )
: Kopete : : PasswordedAccount ( parent , accountId , 0 , name )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Instantiating new account " < < accountId < < endl ;
m_protocol = parent ;
m_jabberClient = 0L ;
m_resourcePool = 0L ;
m_contactPool = 0L ;
# ifdef SUPPORT_JINGLE
m_voiceCaller = 0L ;
//m_jingleSessionManager = 0L; // NOTE: Disabled for 0.12
# endif
m_bookmarks = new JabberBookmarks ( this ) ;
m_removing = false ;
m_notifiedUserCannotBindTransferPort = false ;
// add our own contact to the pool
JabberContact * myContact = contactPool ( ) - > addContact ( XMPP : : RosterItem ( accountId ) , Kopete : : ContactList : : self ( ) - > myself ( ) , false ) ;
setMyself ( myContact ) ;
TQObject : : connect ( Kopete : : ContactList : : self ( ) , TQT_SIGNAL ( globalIdentityChanged ( const TQString & , const TQVariant & ) ) , TQT_SLOT ( slotGlobalIdentityChanged ( const TQString & , const TQVariant & ) ) ) ;
m_initialPresence = XMPP : : Status ( " " , " " , 5 , true ) ;
}
JabberAccount : : ~ JabberAccount ( )
{
disconnect ( Kopete : : Account : : Manual ) ;
// Remove this account from Capabilities manager.
protocol ( ) - > capabilitiesManager ( ) - > removeAccount ( this ) ;
cleanup ( ) ;
TQMap < TQString , JabberTransport * > tranposrts_copy = m_transports ;
TQMap < TQString , JabberTransport * > : : Iterator it ;
for ( it = tranposrts_copy . begin ( ) ; it ! = tranposrts_copy . end ( ) ; + + it )
delete it . data ( ) ;
}
void JabberAccount : : cleanup ( )
{
delete m_jabberClient ;
m_jabberClient = 0L ;
delete m_resourcePool ;
m_resourcePool = 0L ;
delete m_contactPool ;
m_contactPool = 0L ;
# ifdef SUPPORT_JINGLE
delete m_voiceCaller ;
m_voiceCaller = 0L ;
// delete m_jingleSessionManager;
// m_jingleSessionManager = 0L;
# endif
}
void JabberAccount : : setS5BServerPort ( int port )
{
if ( ! m_jabberClient )
{
return ;
}
if ( ! m_jabberClient - > setS5BServerPort ( port ) & & ! m_notifiedUserCannotBindTransferPort )
{
KMessageBox : : queuedMessageBox ( Kopete : : UI : : Global : : mainWidget ( ) , KMessageBox : : Sorry ,
i18n ( " Could not bind Jabber file transfer manager to local port. Please check if the file transfer port is already in use or choose another port in the account settings. " ) ,
i18n ( " Failed to start Jabber File Transfer Manager " ) ) ;
m_notifiedUserCannotBindTransferPort = true ;
}
}
KActionMenu * JabberAccount : : actionMenu ( )
{
KActionMenu * m_actionMenu = Kopete : : Account : : actionMenu ( ) ;
m_actionMenu - > popupMenu ( ) - > insertSeparator ( ) ;
KAction * action ;
action = new KAction ( i18n ( " Join Groupchat... " ) , " jabber_group " , 0 , this , TQT_SLOT ( slotJoinNewChat ( ) ) , this , " actionJoinChat " ) ;
m_actionMenu - > insert ( action ) ;
action - > setEnabled ( isConnected ( ) ) ;
action = m_bookmarks - > bookmarksAction ( m_bookmarks ) ;
m_actionMenu - > insert ( action ) ;
action - > setEnabled ( isConnected ( ) ) ;
m_actionMenu - > popupMenu ( ) - > insertSeparator ( ) ;
action = new KAction ( i18n ( " Services... " ) , " jabber_serv_on " , 0 ,
this , TQT_SLOT ( slotGetServices ( ) ) , this , " actionJabberServices " ) ;
action - > setEnabled ( isConnected ( ) ) ;
m_actionMenu - > insert ( action ) ;
action = new KAction ( i18n ( " Send Raw Packet to Server... " ) , " mail_new " , 0 ,
this , TQT_SLOT ( slotSendRaw ( ) ) , this , " actionJabberSendRaw " ) ;
action - > setEnabled ( isConnected ( ) ) ;
m_actionMenu - > insert ( action ) ;
action = new KAction ( i18n ( " Edit User Info... " ) , " identity " , 0 ,
this , TQT_SLOT ( slotEditVCard ( ) ) , this , " actionEditVCard " ) ;
action - > setEnabled ( isConnected ( ) ) ;
m_actionMenu - > insert ( action ) ;
return m_actionMenu ;
}
JabberResourcePool * JabberAccount : : resourcePool ( )
{
if ( ! m_resourcePool )
m_resourcePool = new JabberResourcePool ( this ) ;
return m_resourcePool ;
}
JabberContactPool * JabberAccount : : contactPool ( )
{
if ( ! m_contactPool )
m_contactPool = new JabberContactPool ( this ) ;
return m_contactPool ;
}
bool JabberAccount : : createContact ( const TQString & contactId , Kopete : : MetaContact * metaContact )
{
// collect all group names
TQStringList groupNames ;
Kopete : : GroupList groupList = metaContact - > groups ( ) ;
for ( Kopete : : Group * group = groupList . first ( ) ; group ; group = groupList . next ( ) )
groupNames + = group - > displayName ( ) ;
XMPP : : Jid jid ( contactId ) ;
XMPP : : RosterItem item ( jid ) ;
item . setName ( metaContact - > displayName ( ) ) ;
item . setGroups ( groupNames ) ;
// this contact will be created with the "dirty" flag set
// (it will get reset if the contact appears in the roster during connect)
JabberContact * contact = contactPool ( ) - > addContact ( item , metaContact , true ) ;
return ( contact ! = 0 ) ;
}
void JabberAccount : : errorConnectFirst ( )
{
KMessageBox : : queuedMessageBox ( Kopete : : UI : : Global : : mainWidget ( ) ,
KMessageBox : : Error ,
i18n ( " Please connect first. " ) , i18n ( " Jabber Error " ) ) ;
}
void JabberAccount : : errorConnectionLost ( )
{
disconnected ( Kopete : : Account : : ConnectionReset ) ;
}
bool JabberAccount : : isConnecting ( )
{
XMPP : : Jid jid ( myself ( ) - > contactId ( ) ) ;
// see if we are currently trying to connect
return resourcePool ( ) - > bestResource ( jid ) . status ( ) . show ( ) = = TQString ( " connecting " ) ;
}
void JabberAccount : : connectWithPassword ( const TQString & password )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " called " < < endl ;
/* Cancel connection process if no password has been supplied. */
if ( password . isEmpty ( ) )
{
disconnect ( Kopete : : Account : : Manual ) ;
return ;
}
/* Don't do anything if we are already connected. */
if ( isConnected ( ) )
return ;
// instantiate new client backend or clean up old one
if ( ! m_jabberClient )
{
m_jabberClient = new JabberClient ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( csDisconnected ( ) ) , this , TQT_SLOT ( slotCSDisconnected ( ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( csError ( int ) ) , this , TQT_SLOT ( slotCSError ( int ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( tlsWarning ( int ) ) , this , TQT_SLOT ( slotHandleTLSWarning ( int ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( connected ( ) ) , this , TQT_SLOT ( slotConnected ( ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( error ( JabberClient : : ErrorCode ) ) , this , TQT_SLOT ( slotClientError ( JabberClient : : ErrorCode ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( subscription ( const XMPP : : Jid & , const TQString & ) ) ,
this , TQT_SLOT ( slotSubscription ( const XMPP : : Jid & , const TQString & ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( rosterRequestFinished ( bool ) ) ,
this , TQT_SLOT ( slotRosterRequestFinished ( bool ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( newContact ( const XMPP : : RosterItem & ) ) ,
this , TQT_SLOT ( slotContactUpdated ( const XMPP : : RosterItem & ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( contactUpdated ( const XMPP : : RosterItem & ) ) ,
this , TQT_SLOT ( slotContactUpdated ( const XMPP : : RosterItem & ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( contactDeleted ( const XMPP : : RosterItem & ) ) ,
this , TQT_SLOT ( slotContactDeleted ( const XMPP : : RosterItem & ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( resourceAvailable ( const XMPP : : Jid & , const XMPP : : Resource & ) ) ,
this , TQT_SLOT ( slotResourceAvailable ( const XMPP : : Jid & , const XMPP : : Resource & ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( resourceUnavailable ( const XMPP : : Jid & , const XMPP : : Resource & ) ) ,
this , TQT_SLOT ( slotResourceUnavailable ( const XMPP : : Jid & , const XMPP : : Resource & ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( messageReceived ( const XMPP : : Message & ) ) ,
this , TQT_SLOT ( slotReceivedMessage ( const XMPP : : Message & ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( incomingFileTransfer ( ) ) ,
this , TQT_SLOT ( slotIncomingFileTransfer ( ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( groupChatJoined ( const XMPP : : Jid & ) ) ,
this , TQT_SLOT ( slotGroupChatJoined ( const XMPP : : Jid & ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( groupChatLeft ( const XMPP : : Jid & ) ) ,
this , TQT_SLOT ( slotGroupChatLeft ( const XMPP : : Jid & ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( groupChatPresence ( const XMPP : : Jid & , const XMPP : : Status & ) ) ,
this , TQT_SLOT ( slotGroupChatPresence ( const XMPP : : Jid & , const XMPP : : Status & ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( groupChatError ( const XMPP : : Jid & , int , const TQString & ) ) ,
this , TQT_SLOT ( slotGroupChatError ( const XMPP : : Jid & , int , const TQString & ) ) ) ;
TQObject : : connect ( m_jabberClient , TQT_SIGNAL ( debugMessage ( const TQString & ) ) ,
this , TQT_SLOT ( slotClientDebugMessage ( const TQString & ) ) ) ;
}
else
{
m_jabberClient - > disconnect ( ) ;
}
// we need to use the old protocol for now
m_jabberClient - > setUseXMPP09 ( true ) ;
// set SSL flag (this should be converted to forceTLS when using the new protocol)
m_jabberClient - > setUseSSL ( configGroup ( ) - > readBoolEntry ( " UseSSL " , false ) ) ;
// override server and port (this should be dropped when using the new protocol and no direct SSL)
m_jabberClient - > setOverrideHost ( true , server ( ) , port ( ) ) ;
// allow plaintext password authentication or not?
m_jabberClient - > setAllowPlainTextPassword ( configGroup ( ) - > readBoolEntry ( " AllowPlainTextPassword " , false ) ) ;
// enable file transfer (if empty, IP will be set after connection has been established)
KGlobal : : config ( ) - > setGroup ( " Jabber " ) ;
m_jabberClient - > setFileTransfersEnabled ( true , KGlobal : : config ( ) - > readEntry ( " LocalIP " ) ) ;
setS5BServerPort ( KGlobal : : config ( ) - > readNumEntry ( " LocalPort " , 8010 ) ) ;
//
// Determine system name
//
if ( ! configGroup ( ) - > readBoolEntry ( " HideSystemInfo " , false ) )
{
struct utsname utsBuf ;
uname ( & utsBuf ) ;
m_jabberClient - > setClientName ( " Kopete " ) ;
m_jabberClient - > setClientVersion ( kapp - > aboutData ( ) - > version ( ) ) ;
m_jabberClient - > setOSName ( TQString ( " %1 %2 " ) . arg ( utsBuf . sysname , 1 ) . arg ( utsBuf . release , 2 ) ) ;
}
// Set caps node information
m_jabberClient - > setCapsNode ( KOPETE_CAPS_NODE ) ;
m_jabberClient - > setCapsVersion ( kapp - > aboutData ( ) - > version ( ) ) ;
// Set Disco Identity information
DiscoItem : : Identity identity ;
identity . category = " client " ;
identity . type = " pc " ;
identity . name = " Kopete " ;
m_jabberClient - > setDiscoIdentity ( identity ) ;
//BEGIN TIMEZONE INFORMATION
//
// Set timezone information (code from Psi)
// Copyright (C) 2001-2003 Justin Karneges
//
time_t x ;
time ( & x ) ;
char str [ 256 ] ;
char fmt [ 32 ] ;
int timezoneOffset ;
TQString timezoneString ;
strcpy ( fmt , " %z " ) ;
strftime ( str , 256 , fmt , localtime ( & x ) ) ;
if ( strcmp ( fmt , str ) )
{
TQString s = str ;
if ( s . at ( 0 ) = = ' + ' )
s . remove ( 0 , 1 ) ;
s . truncate ( s . length ( ) - 2 ) ;
timezoneOffset = s . toInt ( ) ;
}
strcpy ( fmt , " %Z " ) ;
strftime ( str , 256 , fmt , localtime ( & x ) ) ;
if ( strcmp ( fmt , str ) )
timezoneString = str ;
//END of timezone code
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Determined timezone " < < timezoneString < < " with UTC offset " < < timezoneOffset < < " hours. " < < endl ;
m_jabberClient - > setTimeZone ( timezoneString , timezoneOffset ) ;
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Connecting to Jabber server " < < server ( ) < < " : " < < port ( ) < < endl ;
setPresence ( XMPP : : Status ( " connecting " , " " , 0 , true ) ) ;
switch ( m_jabberClient - > connect ( XMPP : : Jid ( accountId ( ) + TQString ( " / " ) + resource ( ) ) , password ) )
{
case JabberClient : : NoTLS :
// no SSL support, at the connecting stage this means the problem is client-side
KMessageBox : : queuedMessageBox ( Kopete : : UI : : Global : : mainWidget ( ) , KMessageBox : : Error ,
i18n ( " SSL support could not be initialized for account %1. This is most likely because the QCA TLS plugin is not installed on your system. " ) .
arg ( myself ( ) - > contactId ( ) ) ,
i18n ( " Jabber SSL Error " ) ) ;
break ;
case JabberClient : : Ok :
default :
// everything alright!
break ;
}
}
void JabberAccount : : slotClientDebugMessage ( const TQString & msg )
{
kdDebug ( JABBER_DEBUG_PROTOCOL ) < < k_funcinfo < < msg < < endl ;
}
bool JabberAccount : : handleTLSWarning ( JabberClient * jabberClient , int warning )
{
TQString validityString , code ;
TQString server = jabberClient - > jid ( ) . domain ( ) ;
TQString accountId = jabberClient - > jid ( ) . bare ( ) ;
switch ( warning )
{
case QCA : : TLS : : NoCert :
validityString = i18n ( " No certificate was presented. " ) ;
code = " NoCert " ;
break ;
case QCA : : TLS : : HostMismatch :
validityString = i18n ( " The host name does not match the one in the certificate. " ) ;
code = " HostMismatch " ;
break ;
case QCA : : TLS : : Rejected :
validityString = i18n ( " The Certificate Authority rejected the certificate. " ) ;
code = " Rejected " ;
break ;
case QCA : : TLS : : Untrusted :
// FIXME: write better error message here
validityString = i18n ( " The certificate is untrusted. " ) ;
code = " Untrusted " ;
break ;
case QCA : : TLS : : SignatureFailed :
validityString = i18n ( " The signature is invalid. " ) ;
code = " SignatureFailed " ;
break ;
case QCA : : TLS : : InvalidCA :
validityString = i18n ( " The Certificate Authority is invalid. " ) ;
code = " InvalidCA " ;
break ;
case QCA : : TLS : : InvalidPurpose :
// FIXME: write better error message here
validityString = i18n ( " Invalid certificate purpose. " ) ;
code = " InvalidPurpose " ;
break ;
case QCA : : TLS : : SelfSigned :
validityString = i18n ( " The certificate is self-signed. " ) ;
code = " SelfSigned " ;
break ;
case QCA : : TLS : : Revoked :
validityString = i18n ( " The certificate has been revoked. " ) ;
code = " Revoked " ;
break ;
case QCA : : TLS : : PathLengthExceeded :
validityString = i18n ( " Maximum certificate chain length was exceeded. " ) ;
code = " PathLengthExceeded " ;
break ;
case QCA : : TLS : : Expired :
validityString = i18n ( " The certificate has expired. " ) ;
code = " Expired " ;
break ;
case QCA : : TLS : : Unknown :
default :
validityString = i18n ( " An unknown error occurred trying to validate the certificate. " ) ;
code = " Unknown " ;
break ;
}
return ( KMessageBox : : warningContinueCancel ( Kopete : : UI : : Global : : mainWidget ( ) ,
i18n ( " <qt><p>The certificate of server %1 could not be validated for account %2: %3</p><p>Do you want to continue?</p></qt> " ) .
arg ( server , accountId , validityString ) ,
i18n ( " Jabber Connection Certificate Problem " ) ,
KStdGuiItem : : cont ( ) ,
TQString ( " KopeteTLSWarning " ) + server + code ) = = KMessageBox : : Continue ) ;
}
void JabberAccount : : slotHandleTLSWarning ( int validityResult )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Handling TLS warning... " < < endl ;
if ( handleTLSWarning ( m_jabberClient , validityResult ) )
{
// resume stream
m_jabberClient - > continueAfterTLSWarning ( ) ;
}
else
{
// disconnect stream
disconnect ( Kopete : : Account : : Manual ) ;
}
}
void JabberAccount : : slotClientError ( JabberClient : : ErrorCode errorCode )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Handling client error... " < < endl ;
switch ( errorCode )
{
case JabberClient : : NoTLS :
default :
KMessageBox : : queuedMessageBox ( Kopete : : UI : : Global : : mainWidget ( ) , KMessageBox : : Error ,
i18n ( " An encrypted connection with the Jabber server could not be established. " ) ,
i18n ( " Jabber Connection Error " ) ) ;
disconnect ( Kopete : : Account : : Manual ) ;
break ;
}
}
void JabberAccount : : slotConnected ( )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Connected to Jabber server. " < < endl ;
# ifdef SUPPORT_JINGLE
if ( ! m_voiceCaller )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Creating Jingle Voice caller... " < < endl ;
m_voiceCaller = new JingleVoiceCaller ( this ) ;
TQObject : : connect ( m_voiceCaller , TQT_SIGNAL ( incoming ( const Jid & ) ) , this , TQT_SLOT ( slotIncomingVoiceCall ( const Jid & ) ) ) ;
m_voiceCaller - > initialize ( ) ;
}
#if 0
if ( ! m_jingleSessionManager )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Creating Jingle Session Manager... " < < endl ;
m_jingleSessionManager = new JingleSessionManager ( this ) ;
TQObject : : connect ( m_jingleSessionManager , TQT_SIGNAL ( incomingSession ( const TQString & , JingleSession * ) ) , this , TQT_SLOT ( slotIncomingJingleSession ( const TQString & , JingleSession * ) ) ) ;
}
# endif
// Set caps extensions
m_jabberClient - > client ( ) - > addExtension ( " voice-v1 " , Features ( TQString ( " http://www.google.com/xmpp/protocol/voice/v1 " ) ) ) ;
# endif
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Requesting roster... " < < endl ;
m_jabberClient - > requestRoster ( ) ;
}
void JabberAccount : : slotRosterRequestFinished ( bool success )
{
if ( success )
{
// the roster was imported successfully, clear
// all "dirty" items from the contact list
contactPool ( ) - > cleanUp ( ) ;
}
/* Since we are online now, set initial presence. Don't do this
* before the roster request or we will receive presence
* information before we have updated our roster with actual
* contacts from the server ! ( Iris won ' t forward presence
* information in that case either ) . */
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Setting initial presence... " < < endl ;
setPresence ( m_initialPresence ) ;
}
void JabberAccount : : slotIncomingFileTransfer ( )
{
// delegate the work to a file transfer object
new JabberFileTransfer ( this , client ( ) - > fileTransferManager ( ) - > takeIncoming ( ) ) ;
}
void JabberAccount : : setOnlineStatus ( const Kopete : : OnlineStatus & status , const TQString & reason )
{
XMPP : : Status xmppStatus = m_protocol - > kosToStatus ( status , reason ) ;
if ( status . status ( ) = = Kopete : : OnlineStatus : : Offline )
{
xmppStatus . setIsAvailable ( false ) ;
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " CROSS YOUR FINGERS! THIS IS GONNA BE WILD " < < endl ;
disconnect ( Manual , xmppStatus ) ;
return ;
}
if ( isConnecting ( ) )
{
return ;
}
if ( ! isConnected ( ) )
{
// we are not connected yet, so connect now
m_initialPresence = xmppStatus ;
connect ( status ) ;
}
else
{
setPresence ( xmppStatus ) ;
}
}
void JabberAccount : : disconnect ( Kopete : : Account : : DisconnectReason reason )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " disconnect() called " < < endl ;
if ( isConnected ( ) )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Still connected, closing connection... " < < endl ;
/* Tell backend class to disconnect. */
m_jabberClient - > disconnect ( ) ;
}
// make sure that the connection animation gets stopped if we're still
// in the process of connecting
setPresence ( XMPP : : Status ( " " , " " , 0 , false ) ) ;
m_initialPresence = XMPP : : Status ( " " , " " , 5 , true ) ;
/* FIXME:
* We should delete the JabberClient instance here ,
* but active timers in Iris prevent us from doing so .
* ( in a failed connection attempt , these timers will
* try to access an already deleted object ) .
* Instead , the instance will lurk until the next
* connection attempt .
*/
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Disconnected. " < < endl ;
disconnected ( reason ) ;
}
void JabberAccount : : disconnect ( Kopete : : Account : : DisconnectReason reason , XMPP : : Status & status )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " disconnect( reason, status ) called " < < endl ;
if ( isConnected ( ) )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Still connected, closing connection... " < < endl ;
/* Tell backend class to disconnect. */
m_jabberClient - > disconnect ( status ) ;
}
// make sure that the connection animation gets stopped if we're still
// in the process of connecting
setPresence ( status ) ;
/* FIXME:
* We should delete the JabberClient instance here ,
* but active timers in Iris prevent us from doing so .
* ( in a failed connection attempt , these timers will
* try to access an already deleted object ) .
* Instead , the instance will lurk until the next
* connection attempt .
*/
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Disconnected. " < < endl ;
Kopete : : Account : : disconnected ( reason ) ;
}
void JabberAccount : : disconnect ( )
{
disconnect ( Manual ) ;
}
void JabberAccount : : slotConnect ( )
{
connect ( ) ;
}
void JabberAccount : : slotDisconnect ( )
{
disconnect ( Kopete : : Account : : Manual ) ;
}
void JabberAccount : : slotCSDisconnected ( )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Disconnected from Jabber server. " < < endl ;
/*
* We should delete the JabberClient instance here ,
* but timers etc prevent us from doing so . Iris does
* not like to be deleted from a slot .
*/
/* It seems that we don't get offline notifications when going offline
* with the protocol , so clear all resources manually . */
resourcePool ( ) - > clear ( ) ;
}
void JabberAccount : : handleStreamError ( int streamError , int streamCondition , int connectorCode , const TQString & server , Kopete : : Account : : DisconnectReason & errorClass )
{
TQString errorText ;
TQString errorCondition ;
errorClass = Kopete : : Account : : InvalidHost ;
/*
* Display error to user .
* FIXME : for unknown errors , maybe add error codes ?
*/
switch ( streamError )
{
case XMPP : : Stream : : ErrParse :
errorClass = Kopete : : Account : : Unknown ;
errorText = i18n ( " Malformed packet received. " ) ;
break ;
case XMPP : : Stream : : ErrProtocol :
errorClass = Kopete : : Account : : Unknown ;
errorText = i18n ( " There was an unrecoverable error in the protocol. " ) ;
break ;
case XMPP : : Stream : : ErrStream :
switch ( streamCondition )
{
case XMPP : : Stream : : GenericStreamError :
errorCondition = i18n ( " Generic stream error (sorry, I do not have a more-detailed reason) " ) ;
break ;
case XMPP : : Stream : : Conflict :
// FIXME: need a better error message here
errorCondition = i18n ( " There was a conflict in the information received. " ) ;
break ;
case XMPP : : Stream : : ConnectionTimeout :
errorCondition = i18n ( " The stream timed out. " ) ;
break ;
case XMPP : : Stream : : InternalServerError :
errorCondition = i18n ( " Internal server error. " ) ;
break ;
case XMPP : : Stream : : InvalidFrom :
errorCondition = i18n ( " Stream packet received from an invalid address. " ) ;
break ;
case XMPP : : Stream : : InvalidXml :
errorCondition = i18n ( " Malformed stream packet received. " ) ;
break ;
case XMPP : : Stream : : PolicyViolation :
// FIXME: need a better error message here
errorCondition = i18n ( " Policy violation in the protocol stream. " ) ;
break ;
case XMPP : : Stream : : ResourceConstraint :
// FIXME: need a better error message here
errorCondition = i18n ( " Resource constraint. " ) ;
break ;
case XMPP : : Stream : : SystemShutdown :
// FIXME: need a better error message here
errorCondition = i18n ( " System shutdown. " ) ;
break ;
default :
errorCondition = i18n ( " Unknown reason. " ) ;
break ;
}
errorText = i18n ( " There was an error in the protocol stream: %1 " ) . arg ( errorCondition ) ;
break ;
case XMPP : : ClientStream : : ErrConnection :
switch ( connectorCode )
{
case KNetwork : : KSocketBase : : LookupFailure :
errorClass = Kopete : : Account : : InvalidHost ;
errorCondition = i18n ( " Host not found. " ) ;
break ;
case KNetwork : : KSocketBase : : AddressInUse :
errorCondition = i18n ( " Address is already in use. " ) ;
break ;
case KNetwork : : KSocketBase : : AlreadyCreated :
errorCondition = i18n ( " Cannot recreate the socket. " ) ;
break ;
case KNetwork : : KSocketBase : : AlreadyBound :
errorCondition = i18n ( " Cannot bind the socket again. " ) ;
break ;
case KNetwork : : KSocketBase : : AlreadyConnected :
errorCondition = i18n ( " Socket is already connected. " ) ;
break ;
case KNetwork : : KSocketBase : : NotConnected :
errorCondition = i18n ( " Socket is not connected. " ) ;
break ;
case KNetwork : : KSocketBase : : NotBound :
errorCondition = i18n ( " Socket is not bound. " ) ;
break ;
case KNetwork : : KSocketBase : : NotCreated :
errorCondition = i18n ( " Socket has not been created. " ) ;
break ;
case KNetwork : : KSocketBase : : WouldBlock :
errorCondition = i18n ( " Socket operation would block. You should not see this error, please use \" Report Bug \" from the Help menu. " ) ;
break ;
case KNetwork : : KSocketBase : : ConnectionRefused :
errorCondition = i18n ( " Connection refused. " ) ;
break ;
case KNetwork : : KSocketBase : : ConnectionTimedOut :
errorCondition = i18n ( " Connection timed out. " ) ;
break ;
case KNetwork : : KSocketBase : : InProgress :
errorCondition = i18n ( " Connection attempt already in progress. " ) ;
break ;
case KNetwork : : KSocketBase : : NetFailure :
errorCondition = i18n ( " Network failure. " ) ;
break ;
case KNetwork : : KSocketBase : : NotSupported :
errorCondition = i18n ( " Operation is not supported. " ) ;
break ;
case KNetwork : : KSocketBase : : Timeout :
errorCondition = i18n ( " Socket timed out. " ) ;
break ;
default :
errorClass = Kopete : : Account : : ConnectionReset ;
//errorCondition = i18n("Sorry, something unexpected happened that I do not know more about.");
break ;
}
if ( ! errorCondition . isEmpty ( ) )
errorText = i18n ( " There was a connection error: %1 " ) . arg ( errorCondition ) ;
break ;
case XMPP : : ClientStream : : ErrNeg :
switch ( streamCondition )
{
case XMPP : : ClientStream : : HostUnknown :
// FIXME: need a better error message here
errorCondition = i18n ( " Unknown host. " ) ;
break ;
case XMPP : : ClientStream : : RemoteConnectionFailed :
// FIXME: need a better error message here
errorCondition = i18n ( " Could not connect to a required remote resource. " ) ;
break ;
case XMPP : : ClientStream : : SeeOtherHost :
errorCondition = i18n ( " It appears we have been redirected to another server; I do not know how to handle this. " ) ;
break ;
case XMPP : : ClientStream : : UnsupportedVersion :
errorCondition = i18n ( " Unsupported protocol version. " ) ;
break ;
default :
errorCondition = i18n ( " Unknown error. " ) ;
break ;
}
errorText = i18n ( " There was a negotiation error: %1 " ) . arg ( errorCondition ) ;
break ;
case XMPP : : ClientStream : : ErrTLS :
switch ( streamCondition )
{
case XMPP : : ClientStream : : TLSStart :
errorCondition = i18n ( " Server rejected our request to start the TLS handshake. " ) ;
break ;
case XMPP : : ClientStream : : TLSFail :
errorCondition = i18n ( " Failed to establish a secure connection. " ) ;
break ;
default :
errorCondition = i18n ( " Unknown error. " ) ;
break ;
}
errorText = i18n ( " There was a Transport Layer Security (TLS) error: %1 " ) . arg ( errorCondition ) ;
break ;
case XMPP : : ClientStream : : ErrAuth :
switch ( streamCondition )
{
case XMPP : : ClientStream : : GenericAuthError :
errorCondition = i18n ( " Login failed with unknown reason. " ) ;
break ;
case XMPP : : ClientStream : : NoMech :
errorCondition = i18n ( " No appropriate authentication mechanism available. " ) ;
break ;
case XMPP : : ClientStream : : BadProto :
errorCondition = i18n ( " Bad SASL authentication protocol. " ) ;
break ;
case XMPP : : ClientStream : : BadServ :
errorCondition = i18n ( " Server failed mutual authentication. " ) ;
break ;
case XMPP : : ClientStream : : EncryptionRequired :
errorCondition = i18n ( " Encryption is required but not present. " ) ;
break ;
case XMPP : : ClientStream : : InvalidAuthzid :
errorCondition = i18n ( " Invalid user ID. " ) ;
break ;
case XMPP : : ClientStream : : InvalidMech :
errorCondition = i18n ( " Invalid mechanism. " ) ;
break ;
case XMPP : : ClientStream : : InvalidRealm :
errorCondition = i18n ( " Invalid realm. " ) ;
break ;
case XMPP : : ClientStream : : MechTooWeak :
errorCondition = i18n ( " Mechanism too weak. " ) ;
break ;
case XMPP : : ClientStream : : NotAuthorized :
errorCondition = i18n ( " Wrong credentials supplied. (check your user ID and password) " ) ;
break ;
case XMPP : : ClientStream : : TemporaryAuthFailure :
errorCondition = i18n ( " Temporary failure, please try again later. " ) ;
break ;
default :
errorCondition = i18n ( " Unknown error. " ) ;
break ;
}
errorText = i18n ( " There was an error authenticating with the server: %1 " ) . arg ( errorCondition ) ;
break ;
case XMPP : : ClientStream : : ErrSecurityLayer :
switch ( streamCondition )
{
case XMPP : : ClientStream : : LayerTLS :
errorCondition = i18n ( " Transport Layer Security (TLS) problem. " ) ;
break ;
case XMPP : : ClientStream : : LayerSASL :
errorCondition = i18n ( " Simple Authentication and Security Layer (SASL) problem. " ) ;
break ;
default :
errorCondition = i18n ( " Unknown error. " ) ;
break ;
}
errorText = i18n ( " There was an error in the security layer: %1 " ) . arg ( errorCondition ) ;
break ;
case XMPP : : ClientStream : : ErrBind :
switch ( streamCondition )
{
case XMPP : : ClientStream : : BindNotAllowed :
errorCondition = i18n ( " No permission to bind the resource. " ) ;
break ;
case XMPP : : ClientStream : : BindConflict :
errorCondition = i18n ( " The resource is already in use. " ) ;
break ;
default :
errorCondition = i18n ( " Unknown error. " ) ;
break ;
}
errorText = i18n ( " Could not bind a resource: %1 " ) . arg ( errorCondition ) ;
break ;
default :
errorText = i18n ( " Unknown error. " ) ;
break ;
}
/*
* This mustn ' t be queued as otherwise the reconnection
* API will attempt to reconnect , queueing another
* error until memory is exhausted .
*/
if ( ! errorText . isEmpty ( ) )
KMessageBox : : error ( Kopete : : UI : : Global : : mainWidget ( ) ,
errorText ,
i18n ( " Connection problem with Jabber server %1 " ) . arg ( server ) ) ;
}
void JabberAccount : : slotCSError ( int error )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Error in stream signalled. " < < endl ;
if ( ( error = = XMPP : : ClientStream : : ErrAuth )
& & ( client ( ) - > clientStream ( ) - > errorCondition ( ) = = XMPP : : ClientStream : : NotAuthorized ) )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Incorrect password, retrying. " < < endl ;
disconnect ( Kopete : : Account : : BadPassword ) ;
}
else
{
Kopete : : Account : : DisconnectReason errorClass = Kopete : : Account : : Unknown ;
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Disconnecting. " < < endl ;
// display message to user
if ( ! m_removing ) //when removing the account, connection errors are normal.
handleStreamError ( error , client ( ) - > clientStream ( ) - > errorCondition ( ) , client ( ) - > clientConnector ( ) - > errorCode ( ) , server ( ) , errorClass ) ;
disconnect ( errorClass ) ;
/* slotCSDisconnected will not be called*/
resourcePool ( ) - > clear ( ) ;
}
}
/* Set presence (usually called by dialog widget). */
void JabberAccount : : setPresence ( const XMPP : : Status & status )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Status: " < < status . show ( ) < < " , Reason: " < < status . status ( ) < < endl ;
// fetch input status
XMPP : : Status newStatus = status ;
// TODO: Check if Caps is enabled
// Send entity capabilities
if ( client ( ) )
{
newStatus . setCapsNode ( client ( ) - > capsNode ( ) ) ;
newStatus . setCapsVersion ( client ( ) - > capsVersion ( ) ) ;
newStatus . setCapsExt ( client ( ) - > capsExt ( ) ) ;
}
// make sure the status gets the correct priority
newStatus . setPriority ( configGroup ( ) - > readNumEntry ( " Priority " , 5 ) ) ;
XMPP : : Jid jid ( myself ( ) - > contactId ( ) ) ;
XMPP : : Resource newResource ( resource ( ) , newStatus ) ;
// update our resource in the resource pool
resourcePool ( ) - > addResource ( jid , newResource ) ;
// make sure that we only consider our own resource locally
resourcePool ( ) - > lockToResource ( jid , newResource ) ;
/*
* Unless we are in the connecting status , send a presence packet to the server
*/
if ( status . show ( ) ! = TQString ( " connecting " ) )
{
/*
* Make sure we are actually connected before sending out a packet .
*/
if ( isConnected ( ) )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Sending new presence to the server. " < < endl ;
XMPP : : JT_Presence * task = new XMPP : : JT_Presence ( client ( ) - > rootTask ( ) ) ;
task - > pres ( newStatus ) ;
task - > go ( true ) ;
}
else
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " We were not connected, presence update aborted. " < < endl ;
}
}
}
void JabberAccount : : slotSendRaw ( )
{
/* Check if we're connected. */
if ( ! isConnected ( ) )
{
errorConnectFirst ( ) ;
return ;
}
new dlgJabberSendRaw ( client ( ) , Kopete : : UI : : Global : : mainWidget ( ) ) ;
}
void JabberAccount : : slotSubscription ( const XMPP : : Jid & jid , const TQString & type )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < jid . full ( ) < < " , " < < type < < endl ;
if ( type = = " subscribe " )
{
/*
* A user wants to subscribe to our presence .
*/
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < jid . full ( ) < < " is asking for authorization to subscribe. " < < endl ;
// Is the user already in our contact list?
JabberBaseContact * contact = contactPool ( ) - > findExactMatch ( jid ) ;
Kopete : : MetaContact * metaContact = 0L ;
if ( contact )
metaContact = contact - > metaContact ( ) ;
int hideFlags = Kopete : : UI : : ContactAddedNotifyDialog : : InfoButton ;
if ( metaContact & & ! metaContact - > isTemporary ( ) )
hideFlags | = Kopete : : UI : : ContactAddedNotifyDialog : : AddCheckBox | Kopete : : UI : : ContactAddedNotifyDialog : : AddGroupBox ;
Kopete : : UI : : ContactAddedNotifyDialog * dialog =
new Kopete : : UI : : ContactAddedNotifyDialog ( jid . full ( ) , TQString ( ) , this , hideFlags ) ;
TQObject : : connect ( dialog , TQT_SIGNAL ( applyClicked ( const TQString & ) ) ,
this , TQT_SLOT ( slotContactAddedNotifyDialogClosed ( const TQString & ) ) ) ;
dialog - > show ( ) ;
}
else if ( type = = " unsubscribed " )
{
/*
* Someone else removed our authorization to see them .
*/
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < jid . full ( ) < < " revoked our presence authorization " < < endl ;
XMPP : : JT_Roster * task ;
switch ( KMessageBox : : warningYesNo ( Kopete : : UI : : Global : : mainWidget ( ) ,
i18n
( " The Jabber user %1 removed %2's subscription to them. "
" This account will no longer be able to view their online/offline status. "
" Do you want to delete the contact? " ) .
arg ( jid . full ( ) , 1 ) . arg ( accountId ( ) , 2 ) , i18n ( " Notification " ) , KStdGuiItem : : del ( ) , i18n ( " Keep " ) ) )
{
case KMessageBox : : Yes :
/*
* Delete this contact from our roster .
*/
task = new XMPP : : JT_Roster ( client ( ) - > rootTask ( ) ) ;
task - > remove ( jid ) ;
task - > go ( true ) ;
break ;
default :
/*
* We want to leave the contact in our contact list .
* In this case , we need to delete all the resources
* we have for it , as the Jabber server won ' t signal us
* that the contact is offline now .
*/
resourcePool ( ) - > removeAllResources ( jid ) ;
break ;
}
}
}
void JabberAccount : : slotContactAddedNotifyDialogClosed ( const TQString & contactid )
{ // the dialog that asked the authorisation is closed. (it was shown in slotSubscrition)
XMPP : : JT_Presence * task ;
XMPP : : Jid jid ( contactid ) ;
const Kopete : : UI : : ContactAddedNotifyDialog * dialog =
dynamic_cast < const Kopete : : UI : : ContactAddedNotifyDialog * > ( sender ( ) ) ;
if ( ! dialog | | ! isConnected ( ) )
return ;
if ( dialog - > authorized ( ) )
{
/*
* Authorize user .
*/
task = new XMPP : : JT_Presence ( client ( ) - > rootTask ( ) ) ;
task - > sub ( jid , " subscribed " ) ;
task - > go ( true ) ;
}
else
{
/*
* Reject subscription .
*/
task = new XMPP : : JT_Presence ( client ( ) - > rootTask ( ) ) ;
task - > sub ( jid , " unsubscribed " ) ;
task - > go ( true ) ;
}
if ( dialog - > added ( ) )
{
Kopete : : MetaContact * parentContact = dialog - > addContact ( ) ;
if ( parentContact )
{
TQStringList groupNames ;
Kopete : : GroupList groupList = parentContact - > groups ( ) ;
for ( Kopete : : Group * group = groupList . first ( ) ; group ; group = groupList . next ( ) )
groupNames + = group - > displayName ( ) ;
XMPP : : RosterItem item ;
// XMPP::Jid jid ( contactId );
item . setJid ( jid ) ;
item . setName ( parentContact - > displayName ( ) ) ;
item . setGroups ( groupNames ) ;
// add the new contact to our roster.
XMPP : : JT_Roster * rosterTask = new XMPP : : JT_Roster ( client ( ) - > rootTask ( ) ) ;
rosterTask - > set ( item . jid ( ) , item . name ( ) , item . groups ( ) ) ;
rosterTask - > go ( true ) ;
// send a subscription request.
XMPP : : JT_Presence * presenceTask = new XMPP : : JT_Presence ( client ( ) - > rootTask ( ) ) ;
presenceTask - > sub ( jid , " subscribe " ) ;
presenceTask - > go ( true ) ;
}
}
}
void JabberAccount : : slotContactUpdated ( const XMPP : : RosterItem & item )
{
/**
* Subscription types are : Both , From , To , Remove , None .
* Both : Both sides have authed each other , each side
* can see each other ' s presence
* From : The other side can see us .
* To : We can see the other side . ( implies we are
* authed )
* Remove : Other side revoked our subscription request .
* Not to be handled here .
* None : No subscription .
*
* Regardless of the subscription type , we have to add
* a roster item here .
*/
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " New roster item " < < item . jid ( ) . full ( ) < < " (Subscription: " < < item . subscription ( ) . toString ( ) < < " ) " < < endl ;
/*
* See if the contact need to be added , according to the criterias of
* JEP - 0162 : Best Practices for Roster and Subscription Management
* http : //www.jabber.org/jeps/jep-0162.html#contacts
*/
bool need_to_add = false ;
if ( item . subscription ( ) . type ( ) = = XMPP : : Subscription : : Both | | item . subscription ( ) . type ( ) = = XMPP : : Subscription : : To )
need_to_add = true ;
else if ( ! item . ask ( ) . isEmpty ( ) )
need_to_add = true ;
else if ( ! item . name ( ) . isEmpty ( ) | | ! item . groups ( ) . isEmpty ( ) )
need_to_add = true ;
/*
* See if the contact is already on our contact list
*/
Kopete : : Contact * c = contactPool ( ) - > findExactMatch ( item . jid ( ) ) ;
if ( c & & c = = c - > Kopete : : Contact : : account ( ) - > myself ( ) ) //don't use JabberBaseContact::account() which return alwaus the JabberAccount, and not the transport
{
// don't let remove the gateway contact, eh!
need_to_add = true ;
}
if ( need_to_add )
{
Kopete : : MetaContact * metaContact = 0L ;
if ( ! c )
{
/*
* No metacontact has been found which contains a contact with this ID ,
* so add a new metacontact to the list .
*/
metaContact = new Kopete : : MetaContact ( ) ;
TQStringList groups = item . groups ( ) ;
// add this metacontact to all groups the contact is a member of
for ( TQStringList : : Iterator it = groups . begin ( ) ; it ! = groups . end ( ) ; + + it )
metaContact - > addToGroup ( Kopete : : ContactList : : self ( ) - > findGroup ( * it ) ) ;
// put it onto contact list
Kopete : : ContactList : : self ( ) - > addMetaContact ( metaContact ) ;
}
else
{
metaContact = c - > metaContact ( ) ;
//TODO: syncronize groups
}
/*
* Add / update the contact in our pool . In case the contact is already there ,
* it will be updated . In case the contact is not in the meta contact yet , it
* will be added to it .
* The " dirty " flag is false here , because we just received the contact from
* the server ' s roster . As such , it is now a synchronized entry .
*/
JabberContact * contact = contactPool ( ) - > addContact ( item , metaContact , false ) ;
/*
* Set authorization property
*/
if ( ! item . ask ( ) . isEmpty ( ) )
{
contact - > setProperty ( protocol ( ) - > propAuthorizationStatus , i18n ( " Waiting for authorization " ) ) ;
}
else
{
contact - > removeProperty ( protocol ( ) - > propAuthorizationStatus ) ;
}
}
else if ( c ) //we don't need to add it, and it is in the contactlist
{
Kopete : : MetaContact * metaContact = c - > metaContact ( ) ;
if ( metaContact - > isTemporary ( ) )
return ;
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < c - > contactId ( ) < <
" is on the contactlist while it shouldn't. we are removing it. - " < < c < < endl ;
delete c ;
if ( metaContact - > contacts ( ) . isEmpty ( ) )
Kopete : : ContactList : : self ( ) - > removeMetaContact ( metaContact ) ;
}
}
void JabberAccount : : slotContactDeleted ( const XMPP : : RosterItem & item )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Deleting contact " < < item . jid ( ) . full ( ) < < endl ;
// since the contact instance will get deleted here, the GUI should be updated
contactPool ( ) - > removeContact ( item . jid ( ) ) ;
}
void JabberAccount : : slotReceivedMessage ( const XMPP : : Message & message )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " New message from " < < message . from ( ) . full ( ) < < endl ;
JabberBaseContact * contactFrom ;
if ( message . type ( ) = = " groupchat " )
{
// this is a group chat message, forward it to the group contact
// (the one without resource name)
XMPP : : Jid jid ( message . from ( ) . userHost ( ) ) ;
// try to locate an exact match in our pool first
contactFrom = contactPool ( ) - > findExactMatch ( jid ) ;
/**
* If there was no exact match , something is really messed up .
* We can ' t receive group chat messages from rooms that we are
* not a member of and if the room contact vanished somehow ,
* we ' re in deep trouble .
*/
if ( ! contactFrom )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " WARNING: Received a groupchat message but couldn't find room contact. Ignoring message. " < < endl ;
return ;
}
}
else
{
// try to locate an exact match in our pool first
contactFrom = contactPool ( ) - > findExactMatch ( message . from ( ) ) ;
if ( ! contactFrom )
{
// we have no exact match, try a broader search
contactFrom = contactPool ( ) - > findRelevantRecipient ( message . from ( ) ) ;
}
// see if we found the contact in our pool
if ( ! contactFrom )
{
// eliminate the resource from this contact,
// otherwise we will add the contact with the
// resource to our list
// NOTE: This is a stupid way to do it, but
// message.from().setResource("") had no
// effect. Iris bug?
XMPP : : Jid jid ( message . from ( ) . userHost ( ) ) ;
// the contact is not in our pool, add it as a temporary contact
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < jid . full ( ) < < " is unknown to us, creating temporary contact. " < < endl ;
Kopete : : MetaContact * metaContact = new Kopete : : MetaContact ( ) ;
metaContact - > setTemporary ( true ) ;
contactFrom = contactPool ( ) - > addContact ( XMPP : : RosterItem ( jid ) , metaContact , false ) ;
Kopete : : ContactList : : self ( ) - > addMetaContact ( metaContact ) ;
}
}
// pass the message on to the contact
contactFrom - > handleIncomingMessage ( message ) ;
}
void JabberAccount : : slotJoinNewChat ( )
{
if ( ! isConnected ( ) )
{
errorConnectFirst ( ) ;
return ;
}
dlgJabberChatJoin * joinDialog = new dlgJabberChatJoin ( this , Kopete : : UI : : Global : : mainWidget ( ) ) ;
joinDialog - > show ( ) ;
}
void JabberAccount : : slotGroupChatJoined ( const XMPP : : Jid & jid )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Joined group chat " < < jid . full ( ) < < endl ;
// Create new meta contact that holds the group chat contact.
Kopete : : MetaContact * metaContact = new Kopete : : MetaContact ( ) ;
metaContact - > setTemporary ( true ) ;
// Create a groupchat contact for this room
JabberGroupContact * groupContact = dynamic_cast < JabberGroupContact * > ( contactPool ( ) - > addGroupContact ( XMPP : : RosterItem ( jid ) , true , metaContact , false ) ) ;
if ( groupContact )
{
// Add the groupchat contact to the meta contact.
//metaContact->addContact ( groupContact );
Kopete : : ContactList : : self ( ) - > addMetaContact ( metaContact ) ;
}
else
delete metaContact ;
/**
* Add an initial resource for this contact to the pool . We need
* to do this to be able to lock the group status to our own presence .
* Our own presence will be updated right after this method returned
* by slotGroupChatPresence ( ) , since the server will signal our own
* presence back to us .
*/
resourcePool ( ) - > addResource ( XMPP : : Jid ( jid . userHost ( ) ) , XMPP : : Resource ( jid . resource ( ) ) ) ;
// lock the room to our own status
resourcePool ( ) - > lockToResource ( XMPP : : Jid ( jid . userHost ( ) ) , jid . resource ( ) ) ;
m_bookmarks - > insertGroupChat ( jid ) ;
}
void JabberAccount : : slotGroupChatLeft ( const XMPP : : Jid & jid )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo " Left groupchat " < < jid . full ( ) < < endl ;
// remove group contact from list
Kopete : : Contact * contact =
Kopete : : ContactList : : self ( ) - > findContact ( protocol ( ) - > pluginId ( ) , accountId ( ) , jid . userHost ( ) ) ;
if ( contact )
{
Kopete : : MetaContact * metaContact = contact - > metaContact ( ) ;
if ( metaContact & & metaContact - > isTemporary ( ) )
Kopete : : ContactList : : self ( ) - > removeMetaContact ( metaContact ) ;
else
contact - > deleteLater ( ) ;
}
// now remove it from our pool, which should clean up all subcontacts as well
contactPool ( ) - > removeContact ( XMPP : : Jid ( jid . userHost ( ) ) ) ;
}
void JabberAccount : : slotGroupChatPresence ( const XMPP : : Jid & jid , const XMPP : : Status & status )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Received groupchat presence for room " < < jid . full ( ) < < endl ;
// fetch room contact (the one without resource)
JabberGroupContact * groupContact = dynamic_cast < JabberGroupContact * > ( contactPool ( ) - > findExactMatch ( XMPP : : Jid ( jid . userHost ( ) ) ) ) ;
if ( ! groupContact )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " WARNING: Groupchat presence signalled, but we don't have a room contact? " < < endl ;
return ;
}
if ( ! status . isAvailable ( ) )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < jid . full ( ) < < " has become unavailable, removing from room " < < endl ;
// remove the resource from the pool
resourcePool ( ) - > removeResource ( jid , XMPP : : Resource ( jid . resource ( ) , status ) ) ;
// the person has become unavailable, remove it
groupContact - > removeSubContact ( XMPP : : RosterItem ( jid ) ) ;
}
else
{
// add a resource for this contact to the pool (existing resources will be updated)
resourcePool ( ) - > addResource ( jid , XMPP : : Resource ( jid . resource ( ) , status ) ) ;
// make sure the contact exists in the room (if it exists already, it won't be added twice)
groupContact - > addSubContact ( XMPP : : RosterItem ( jid ) ) ;
}
}
void JabberAccount : : slotGroupChatError ( const XMPP : : Jid & jid , int error , const TQString & reason )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Group chat error - room " < < jid . full ( ) < < " had error " < < error < < " ( " < < reason < < " ) " < < endl ;
switch ( error )
{
case JabberClient : : InvalidPasswordForMUC :
{
TQCString password ;
int result = KPasswordDialog : : getPassword ( password , i18n ( " A password is required to join the room %1. " ) . arg ( jid . node ( ) ) ) ;
if ( result = = KPasswordDialog : : Accepted )
m_jabberClient - > joinGroupChat ( jid . domain ( ) , jid . node ( ) , jid . resource ( ) , password ) ;
}
break ;
case JabberClient : : NicknameConflict :
{
bool ok ;
TQString nickname = KInputDialog : : getText ( i18n ( " Error trying to join %1 : nickname %2 is already in use " ) . arg ( jid . node ( ) , jid . resource ( ) ) ,
i18n ( " Give your nickname " ) ,
TQString ( ) ,
& ok ) ;
if ( ok )
{
m_jabberClient - > joinGroupChat ( jid . domain ( ) , jid . node ( ) , nickname ) ;
}
}
break ;
case JabberClient : : BannedFromThisMUC :
KMessageBox : : queuedMessageBox ( Kopete : : UI : : Global : : mainWidget ( ) ,
KMessageBox : : Error ,
i18n ( " You can't join the room %1 because you were banned " ) . arg ( jid . node ( ) ) ,
i18n ( " Jabber Group Chat " ) ) ;
break ;
case JabberClient : : MaxUsersReachedForThisMuc :
KMessageBox : : queuedMessageBox ( Kopete : : UI : : Global : : mainWidget ( ) ,
KMessageBox : : Error ,
i18n ( " You can't join the room %1 because the maximum users has been reached " ) . arg ( jid . node ( ) ) ,
i18n ( " Jabber Group Chat " ) ) ;
break ;
default :
{
TQString detailedReason = reason . isEmpty ( ) ? i18n ( " No reason given by the server " ) : reason ;
KMessageBox : : queuedMessageBox ( Kopete : : UI : : Global : : mainWidget ( ) ,
KMessageBox : : Error ,
i18n ( " There was an error processing your request for group chat %1. (Reason: %2, Code %3) " ) . arg ( jid . full ( ) , detailedReason , TQString : : number ( error ) ) ,
i18n ( " Jabber Group Chat " ) ) ;
}
}
}
void JabberAccount : : slotResourceAvailable ( const XMPP : : Jid & jid , const XMPP : : Resource & resource )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " New resource available for " < < jid . full ( ) < < endl ;
resourcePool ( ) - > addResource ( jid , resource ) ;
}
void JabberAccount : : slotResourceUnavailable ( const XMPP : : Jid & jid , const XMPP : : Resource & resource )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Resource now unavailable for " < < jid . full ( ) < < endl ;
resourcePool ( ) - > removeResource ( jid , resource ) ;
}
void JabberAccount : : slotEditVCard ( )
{
static_cast < JabberContact * > ( myself ( ) ) - > slotUserInfo ( ) ;
}
void JabberAccount : : slotGlobalIdentityChanged ( const TQString & key , const TQVariant & value )
{
// Check if this account is excluded from Global Identity.
if ( ! configGroup ( ) - > readBoolEntry ( " ExcludeGlobalIdentity " , false ) )
{
JabberContact * jabberMyself = static_cast < JabberContact * > ( myself ( ) ) ;
if ( key = = Kopete : : Global : : Properties : : self ( ) - > nickName ( ) . key ( ) )
{
TQString oldNick = jabberMyself - > property ( protocol ( ) - > propNickName ) . value ( ) . toString ( ) ;
TQString newNick = value . toString ( ) ;
if ( newNick ! = oldNick & & isConnected ( ) )
{
jabberMyself - > setProperty ( protocol ( ) - > propNickName , newNick ) ;
jabberMyself - > slotSendVCard ( ) ;
}
}
if ( key = = Kopete : : Global : : Properties : : self ( ) - > photo ( ) . key ( ) )
{
if ( isConnected ( ) )
{
jabberMyself - > setPhoto ( value . toString ( ) ) ;
jabberMyself - > slotSendVCard ( ) ;
}
}
}
}
const TQString JabberAccount : : resource ( ) const
{
return configGroup ( ) - > readEntry ( " Resource " , " Kopete " ) ;
}
const TQString JabberAccount : : server ( ) const
{
return configGroup ( ) - > readEntry ( " Server " ) ;
}
const int JabberAccount : : port ( ) const
{
return configGroup ( ) - > readNumEntry ( " Port " , 5222 ) ;
}
void JabberAccount : : slotGetServices ( )
{
dlgJabberServices * dialog = new dlgJabberServices ( this ) ;
dialog - > show ( ) ;
dialog - > raise ( ) ;
}
void JabberAccount : : slotIncomingVoiceCall ( const Jid & jid )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < endl ;
# ifdef SUPPORT_JINGLE
if ( voiceCaller ( ) )
{
kdDebug ( JABBER_DEBUG_GLOBAL ) < < k_funcinfo < < " Showing voice dialog. " < < endl ;
JingleVoiceSessionDialog * voiceDialog = new JingleVoiceSessionDialog ( jid , voiceCaller ( ) ) ;
voiceDialog - > show ( ) ;
}
# else
Q_UNUSED ( jid ) ;
# endif
}
// void JabberAccount::slotIncomingJingleSession( const TQString &sessionType, JingleSession *session )
// {
// #ifdef SUPPORT_JINGLE
// if(sessionType == "http://www.google.com/session/phone")
// {
// TQString from = ((XMPP::Jid)session->peers().first()).full();
// //KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Information, TQString("Received a voice session invitation from %1.").arg(from) );
// JingleVoiceSessionDialog *voiceDialog = new JingleVoiceSessionDialog( static_cast<JingleVoiceSession*>(session) );
// voiceDialog->show();
// }
// #else
// Q_UNUSED( sessionType );
// Q_UNUSED( session );
// #endif
// }
void JabberAccount : : addTransport ( JabberTransport * tr , const TQString & jid )
{
m_transports . insert ( jid , tr ) ;
}
void JabberAccount : : removeTransport ( const TQString & jid )
{
m_transports . remove ( jid ) ;
}
bool JabberAccount : : removeAccount ( )
{
if ( ! m_removing )
{
int result = KMessageBox : : warningYesNoCancel ( Kopete : : UI : : Global : : mainWidget ( ) ,
i18n ( " Do you want to also unregister \" %1 \" from the Jabber server ? \n "
" If you unregister, all your contact list may be removed on the server, "
" And you will never be able to connect to this account with any client " ) . arg ( accountLabel ( ) ) ,
i18n ( " Unregister " ) ,
KGuiItem ( i18n ( " Remove and Unregister " ) , " editdelete " ) ,
KGuiItem ( i18n ( " Remove from kopete only " ) , " edittrash " ) ,
TQString ( ) , KMessageBox : : Notify | KMessageBox : : Dangerous ) ;
if ( result = = KMessageBox : : Cancel )
{
return false ;
}
else if ( result = = KMessageBox : : Yes )
{
if ( ! isConnected ( ) )
{
errorConnectFirst ( ) ;
return false ;
}
XMPP : : JT_Register * task = new XMPP : : JT_Register ( client ( ) - > rootTask ( ) ) ;
TQObject : : connect ( task , TQT_SIGNAL ( finished ( ) ) , this , TQT_SLOT ( slotUnregisterFinished ) ) ;
task - > unreg ( ) ;
task - > go ( true ) ;
m_removing = true ;
// from my experiment, not all server reply us with a response. it simply dosconnect
// so after one seconde, we will force to remove the account
TQTimer : : singleShot ( 1111 , this , TQT_SLOT ( slotUnregisterFinished ( ) ) ) ;
return false ; //the account will be removed when the task will be finished
}
}
//remove transports from config file.
TQMap < TQString , JabberTransport * > tranposrts_copy = m_transports ;
TQMap < TQString , JabberTransport * > : : Iterator it ;
for ( it = tranposrts_copy . begin ( ) ; it ! = tranposrts_copy . end ( ) ; + + it )
{
( * it ) - > jabberAccountRemoved ( ) ;
}
return true ;
}
void JabberAccount : : slotUnregisterFinished ( )
{
const XMPP : : JT_Register * task = dynamic_cast < const XMPP : : JT_Register * > ( sender ( ) ) ;
if ( task & & ! task - > success ( ) )
{
KMessageBox : : queuedMessageBox ( 0L , KMessageBox : : Error ,
i18n ( " An error occured when trying to remove the account: \n %1 " ) . arg ( task - > statusString ( ) ) ,
i18n ( " Jabber Account Unregistration " ) ) ;
m_removing = false ;
return ;
}
if ( m_removing ) //it may be because this is now the timer.
Kopete : : AccountManager : : self ( ) - > removeAccount ( this ) ; //this will delete this
}
# include "jabberaccount.moc"
// vim: set noet ts=4 sts=4 sw=4: