
/***************************************************************************
                   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 <tqca.h>
#include "bsocket.h"

#include "jabberaccount.h"
#include "jabberbookmarks.h"

#include <time.h>

#include <tqstring.h>
#include <tqregexp.h>
#include <tqtimer.h>

#include <tdeconfig.h>
#include <kdebug.h>
#include <tdemessagebox.h>
#include <tdelocale.h>
#include <tdeapplication.h>
#include <tdeaboutdata.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(), TQ_SIGNAL( globalIdentityChanged(const TQString&, const TQVariant& ) ), TQ_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;
	}

}

TDEActionMenu *JabberAccount::actionMenu ()
{
	TDEActionMenu *m_actionMenu = Kopete::Account::actionMenu();

	m_actionMenu->popupMenu()->insertSeparator();

	TDEAction *action;
	
	action = new TDEAction (i18n ("Join Groupchat..."), "jabber_group", 0, this, TQ_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 TDEAction ( i18n ("Services..."), "jabber_serv_on", 0,
							this, TQ_SLOT ( slotGetServices () ), this, "actionJabberServices");
	action->setEnabled( isConnected() );
	m_actionMenu->insert ( action );

	action = new TDEAction ( i18n ("Send Raw Packet to Server..."), "mail-message-new", 0,
										 this, TQ_SLOT ( slotSendRaw () ), this, "actionJabberSendRaw") ;
	action->setEnabled( isConnected() );
	m_actionMenu->insert ( action );

	action = new TDEAction ( i18n ("Edit User Info..."), "identity", 0,
										 this, TQ_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, TQ_SIGNAL ( csDisconnected () ), this, TQ_SLOT ( slotCSDisconnected () ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( csError ( int ) ), this, TQ_SLOT ( slotCSError ( int ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( tlsWarning ( int ) ), this, TQ_SLOT ( slotHandleTLSWarning ( int ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( connected () ), this, TQ_SLOT ( slotConnected () ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( error ( JabberClient::ErrorCode ) ), this, TQ_SLOT ( slotClientError ( JabberClient::ErrorCode ) ) );

		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( subscription ( const XMPP::Jid &, const TQString & ) ),
				   this, TQ_SLOT ( slotSubscription ( const XMPP::Jid &, const TQString & ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( rosterRequestFinished ( bool ) ),
				   this, TQ_SLOT ( slotRosterRequestFinished ( bool ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( newContact ( const XMPP::RosterItem & ) ),
				   this, TQ_SLOT ( slotContactUpdated ( const XMPP::RosterItem & ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( contactUpdated ( const XMPP::RosterItem & ) ),
				   this, TQ_SLOT ( slotContactUpdated ( const XMPP::RosterItem & ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( contactDeleted ( const XMPP::RosterItem & ) ),
				   this, TQ_SLOT ( slotContactDeleted ( const XMPP::RosterItem & ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( resourceAvailable ( const XMPP::Jid &, const XMPP::Resource & ) ),
				   this, TQ_SLOT ( slotResourceAvailable ( const XMPP::Jid &, const XMPP::Resource & ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( resourceUnavailable ( const XMPP::Jid &, const XMPP::Resource & ) ),
				   this, TQ_SLOT ( slotResourceUnavailable ( const XMPP::Jid &, const XMPP::Resource & ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( messageReceived ( const XMPP::Message & ) ),
				   this, TQ_SLOT ( slotReceivedMessage ( const XMPP::Message & ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( incomingFileTransfer () ),
				   this, TQ_SLOT ( slotIncomingFileTransfer () ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( groupChatJoined ( const XMPP::Jid & ) ),
				   this, TQ_SLOT ( slotGroupChatJoined ( const XMPP::Jid & ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( groupChatLeft ( const XMPP::Jid & ) ),
				   this, TQ_SLOT ( slotGroupChatLeft ( const XMPP::Jid & ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( groupChatPresence ( const XMPP::Jid &, const XMPP::Status & ) ),
				   this, TQ_SLOT ( slotGroupChatPresence ( const XMPP::Jid &, const XMPP::Status & ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( groupChatError ( const XMPP::Jid &, int, const TQString & ) ),
				   this, TQ_SLOT ( slotGroupChatError ( const XMPP::Jid &, int, const TQString & ) ) );
		TQObject::connect ( m_jabberClient, TQ_SIGNAL ( debugMessage ( const TQString & ) ),
				   this, TQ_SLOT ( slotClientDebugMessage ( const TQString & ) ) );
	}
	else
	{
		m_jabberClient->disconnect ();
	}

	// 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)
	if( configGroup()->readBoolEntry ( "CustomServer", false ) ) {
		m_jabberClient->setUseXMPP09 ( true );
		m_jabberClient->setOverrideHost ( true, server (), port () );
	}
	else {
		m_jabberClient->setUseXMPP09 ( false );
		m_jabberClient->setOverrideHost ( false );
	}

	// 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)
	TDEGlobal::config()->setGroup ( "Jabber" );
	m_jabberClient->setFileTransfersEnabled ( true, TDEGlobal::config()->readEntry ( "LocalIP" ) );
	setS5BServerPort ( TDEGlobal::config()->readNumEntry ( "LocalPort", 8010 ) );

	//
	// Determine system name
	//
	if ( !configGroup()->readBoolEntry ( "HideSystemInfo", false ) )
	{
		struct utsname utsBuf;

		uname (&utsBuf);

		m_jabberClient->setClientName ("Kopete");
		m_jabberClient->setClientVersion (tdeApp->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(tdeApp->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 TQCA 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 TQCA::TLS::NoCert:
			validityString = i18n("No certificate was presented.");
			code = "NoCert";
			break;
		case TQCA::TLS::HostMismatch:
			validityString = i18n("The host name does not match the one in the certificate.");
			code = "HostMismatch";
			break;
		case TQCA::TLS::Rejected:
			validityString = i18n("The Certificate Authority rejected the certificate.");
			code = "Rejected";
			break;
		case TQCA::TLS::Untrusted:
			// FIXME: write better error message here
			validityString = i18n("The certificate is untrusted.");
			code = "Untrusted";
			break;
		case TQCA::TLS::SignatureFailed:
			validityString = i18n("The signature is invalid.");
			code = "SignatureFailed";
			break;
		case TQCA::TLS::InvalidCA:
			validityString = i18n("The Certificate Authority is invalid.");
			code = "InvalidCA";
			break;
		case TQCA::TLS::InvalidPurpose:
			// FIXME: write better error  message here
			validityString = i18n("Invalid certificate purpose.");
			code = "InvalidPurpose";
			break;
		case TQCA::TLS::SelfSigned:
			validityString = i18n("The certificate is self-signed.");
			code = "SelfSigned";
			break;
		case TQCA::TLS::Revoked:
			validityString = i18n("The certificate has been revoked.");
			code = "Revoked";
			break;
		case TQCA::TLS::PathLengthExceeded:
			validityString = i18n("Maximum certificate chain length was exceeded.");
			code = "PathLengthExceeded";
			break;
		case TQCA::TLS::Expired:
			validityString = i18n("The certificate has expired.");
			code = "Expired";
			break;
		case TQCA::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,TQ_SIGNAL(incoming(const Jid&)),this,TQ_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, TQ_SIGNAL(incomingSession(const TQString &, JingleSession *)), this, TQ_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::TDESocketBase::LookupFailure:
					errorClass = Kopete::Account::InvalidHost;
					errorCondition = i18n("Host not found.");
					break;
				case KNetwork::TDESocketBase::AddressInUse:
					errorCondition = i18n("Address is already in use.");
					break;
				case KNetwork::TDESocketBase::AlreadyCreated:
					errorCondition = i18n("Cannot recreate the socket.");
					break;
				case KNetwork::TDESocketBase::AlreadyBound:
					errorCondition = i18n("Cannot bind the socket again.");
					break;
				case KNetwork::TDESocketBase::AlreadyConnected:
					errorCondition = i18n("Socket is already connected.");
					break;
				case KNetwork::TDESocketBase::NotConnected:
					errorCondition = i18n("Socket is not connected.");
					break;
				case KNetwork::TDESocketBase::NotBound:
					errorCondition = i18n("Socket is not bound.");
					break;
				case KNetwork::TDESocketBase::NotCreated:
					errorCondition = i18n("Socket has not been created.");
					break;
				case KNetwork::TDESocketBase::WouldBlock:
					errorCondition = i18n("Socket operation would block. You should not see this error, please use \"Report Bug\" from the Help menu.");
					break;
				case KNetwork::TDESocketBase::ConnectionRefused:
					errorCondition = i18n("Connection refused.");
					break;
				case KNetwork::TDESocketBase::ConnectionTimedOut:
					errorCondition = i18n("Connection timed out.");
					break;
				case KNetwork::TDESocketBase::InProgress:
					errorCondition = i18n("Connection attempt already in progress.");
					break;
				case KNetwork::TDESocketBase::NetFailure:
					errorCondition = i18n("Network failure.");
					break;
				case KNetwork::TDESocketBase::NotSupported:
					errorCondition = i18n("Operation is not supported.");
					break;
				case KNetwork::TDESocketBase::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,TQ_SIGNAL(applyClicked(const TQString&)),
						this,TQ_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:
		{
			TQString 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" ), "edit-delete"),
					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, TQ_SIGNAL ( finished () ), this, TQ_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, TQ_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"
