You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
474 lines
13 KiB
474 lines
13 KiB
/*
|
|
webpresenceplugin.cpp
|
|
|
|
Kopete Web Presence plugin
|
|
|
|
Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
|
|
Copyright (c) 2002,2003 by Will Stephenson <will@stevello.free-online.co.uk>
|
|
|
|
Kopete (c) 2002-2005 by the 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 "config.h"
|
|
|
|
#include <tqdom.h>
|
|
#include <tqtimer.h>
|
|
#include <tqfile.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <kconfig.h>
|
|
#include <kgenericfactory.h>
|
|
#include <kmessagebox.h>
|
|
#include <ktempfile.h>
|
|
#include <kstandarddirs.h>
|
|
|
|
#ifdef HAVE_XSLT
|
|
#include <libxml/parser.h>
|
|
#include <libxml/tree.h>
|
|
|
|
#include <libxslt/xsltconfig.h>
|
|
#include <libxslt/xsltInternals.h>
|
|
#include <libxslt/transform.h>
|
|
#include <libxslt/xsltutils.h>
|
|
#endif
|
|
|
|
#include "kopetepluginmanager.h"
|
|
#include "kopeteprotocol.h"
|
|
#include "kopeteaccountmanager.h"
|
|
#include "kopeteaccount.h"
|
|
|
|
#include "webpresenceplugin.h"
|
|
|
|
typedef KGenericFactory<WebPresencePlugin> WebPresencePluginFactory;
|
|
K_EXPORT_COMPONENT_FACTORY( kopete_webpresence, WebPresencePluginFactory( "kopete_webpresence" ) )
|
|
|
|
WebPresencePlugin::WebPresencePlugin( TQObject *parent, const char *name, const TQStringList& /*args*/ )
|
|
: Kopete::Plugin( WebPresencePluginFactory::instance(), parent, name ),
|
|
shuttingDown( false ), resultFormatting( WEB_HTML )
|
|
{
|
|
m_writeScheduler = new TQTimer( this );
|
|
connect ( m_writeScheduler, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotWriteFile() ) );
|
|
connect( Kopete::AccountManager::self(), TQT_SIGNAL(accountRegistered(Kopete::Account*)),
|
|
this, TQT_SLOT( listenToAllAccounts() ) );
|
|
connect( Kopete::AccountManager::self(), TQT_SIGNAL(accountUnregistered(Kopete::Account*)),
|
|
this, TQT_SLOT( listenToAllAccounts() ) );
|
|
|
|
connect(this, TQT_SIGNAL(settingsChanged()), this, TQT_SLOT( loadSettings() ) );
|
|
loadSettings();
|
|
listenToAllAccounts();
|
|
}
|
|
|
|
WebPresencePlugin::~WebPresencePlugin()
|
|
{
|
|
}
|
|
|
|
void WebPresencePlugin::loadSettings()
|
|
{
|
|
KConfig *kconfig = KGlobal::config();
|
|
kconfig->setGroup( "Web Presence Plugin" );
|
|
|
|
frequency = kconfig->readNumEntry("UploadFrequency", 15);
|
|
resultURL = kconfig->readPathEntry("uploadURL");
|
|
|
|
resultFormatting = WEB_UNDEFINED;
|
|
|
|
if ( kconfig->readBoolEntry( "formatHTML", false ) ) {
|
|
resultFormatting = WEB_HTML;
|
|
} else if ( kconfig->readBoolEntry( "formatXHTML", false ) ) {
|
|
resultFormatting = WEB_XHTML;
|
|
} else if ( kconfig->readBoolEntry( "formatXML", false ) ) {
|
|
resultFormatting = WEB_XML;
|
|
} else if ( kconfig->readBoolEntry( "formatStylesheet", false ) ) {
|
|
resultFormatting = WEB_CUSTOM;
|
|
userStyleSheet = kconfig->readEntry("formatStylesheetURL");
|
|
}
|
|
|
|
// Default to HTML if we dont get anything useful from config file.
|
|
if ( resultFormatting == WEB_UNDEFINED )
|
|
resultFormatting = WEB_HTML;
|
|
|
|
useImagesInHTML = kconfig->readBoolEntry( "useImagesHTML", false );
|
|
useImName = kconfig->readBoolEntry("showName", true);
|
|
userName = kconfig->readEntry("showThisName");
|
|
showAddresses = kconfig->readBoolEntry("includeIMAddress", false);
|
|
|
|
// Update file when settings are changed.
|
|
slotWriteFile();
|
|
}
|
|
|
|
void WebPresencePlugin::listenToAllAccounts()
|
|
{
|
|
// connect to signals notifying of all accounts' status changes
|
|
ProtocolList protocols = allProtocols();
|
|
|
|
for ( ProtocolList::Iterator it = protocols.begin();
|
|
it != protocols.end(); ++it )
|
|
{
|
|
TQDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( *it );
|
|
TQDictIterator<Kopete::Account> acIt( accounts );
|
|
|
|
for( ; Kopete::Account *account = acIt.current(); ++acIt )
|
|
{
|
|
listenToAccount( account );
|
|
}
|
|
}
|
|
slotWaitMoreStatusChanges();
|
|
}
|
|
|
|
void WebPresencePlugin::listenToAccount( Kopete::Account* account )
|
|
{
|
|
if(account && account->myself())
|
|
{
|
|
// Connect to the account's status changed signal
|
|
// because we can't know if the account has already connected
|
|
TQObject::disconnect( account->myself(),
|
|
TQT_SIGNAL(onlineStatusChanged( Kopete::Contact *,
|
|
const Kopete::OnlineStatus &,
|
|
const Kopete::OnlineStatus & ) ),
|
|
this,
|
|
TQT_SLOT( slotWaitMoreStatusChanges() ) ) ;
|
|
TQObject::connect( account->myself(),
|
|
TQT_SIGNAL(onlineStatusChanged( Kopete::Contact *,
|
|
const Kopete::OnlineStatus &,
|
|
const Kopete::OnlineStatus & ) ),
|
|
this,
|
|
TQT_SLOT( slotWaitMoreStatusChanges() ) );
|
|
}
|
|
}
|
|
|
|
void WebPresencePlugin::slotWaitMoreStatusChanges()
|
|
{
|
|
if ( !m_writeScheduler->isActive() )
|
|
m_writeScheduler->start( frequency * 1000 );
|
|
}
|
|
|
|
void WebPresencePlugin::slotWriteFile()
|
|
{
|
|
m_writeScheduler->stop();
|
|
|
|
// generate the (temporary) XML file representing the current contactlist
|
|
KURL dest( resultURL );
|
|
if ( resultURL.isEmpty() || !dest.isValid() )
|
|
{
|
|
kdDebug(14309) << "url is empty or not valid. NOT UPDATING!" << endl;
|
|
return;
|
|
}
|
|
|
|
KTempFile* xml = generateFile();
|
|
xml->setAutoDelete( true );
|
|
kdDebug(14309) << k_funcinfo << " " << xml->name() << endl;
|
|
|
|
switch( resultFormatting ) {
|
|
case WEB_XML:
|
|
m_output = xml;
|
|
xml = 0L;
|
|
break;
|
|
case WEB_HTML:
|
|
case WEB_XHTML:
|
|
case WEB_CUSTOM:
|
|
m_output = new KTempFile();
|
|
m_output->setAutoDelete( true );
|
|
|
|
if ( !transform( xml, m_output ) )
|
|
{
|
|
//TODO: give some error to user, even better if shown only once
|
|
delete m_output;
|
|
m_output = 0L;
|
|
|
|
delete xml;
|
|
return;
|
|
}
|
|
|
|
delete xml; // might make debugging harder!
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
// upload it to the specified URL
|
|
KURL src( m_output->name() );
|
|
KIO::FileCopyJob *job = KIO::file_move( src, dest, -1, true, false, false );
|
|
connect( job, TQT_SIGNAL( result( KIO::Job * ) ),
|
|
TQT_SLOT( slotUploadJobResult( KIO::Job * ) ) );
|
|
}
|
|
|
|
void WebPresencePlugin::slotUploadJobResult( KIO::Job *job )
|
|
{
|
|
if ( job->error() ) {
|
|
kdDebug(14309) << "Error uploading presence info." << endl;
|
|
KMessageBox::queuedDetailedError( 0, i18n("An error occurred when uploading your presence page.\nCheck the path and write permissions of the destination."), 0, displayName() );
|
|
delete m_output;
|
|
m_output = 0L;
|
|
}
|
|
}
|
|
|
|
KTempFile* WebPresencePlugin::generateFile()
|
|
{
|
|
// generate the (temporary) XML file representing the current contactlist
|
|
kdDebug( 14309 ) << k_funcinfo << endl;
|
|
TQString notKnown = i18n( "Not yet known" );
|
|
|
|
TQDomDocument doc;
|
|
|
|
doc.appendChild( doc.createProcessingInstruction( "xml",
|
|
"version=\"1.0\" encoding=\"UTF-8\"" ) );
|
|
|
|
TQDomElement root = doc.createElement( "webpresence" );
|
|
doc.appendChild( root );
|
|
|
|
// insert the current date/time
|
|
TQDomElement date = doc.createElement( "listdate" );
|
|
TQDomText t = doc.createTextNode(
|
|
KGlobal::locale()->formatDateTime( TQDateTime::tqcurrentDateTime() ) );
|
|
date.appendChild( t );
|
|
root.appendChild( date );
|
|
|
|
// insert the user's name
|
|
TQDomElement name = doc.createElement( "name" );
|
|
TQDomText nameText;
|
|
if ( !useImName && !userName.isEmpty() )
|
|
nameText = doc.createTextNode( userName );
|
|
else
|
|
nameText = doc.createTextNode( notKnown );
|
|
name.appendChild( nameText );
|
|
root.appendChild( name );
|
|
|
|
// insert the list of the user's accounts
|
|
TQDomElement accounts = doc.createElement( "accounts" );
|
|
root.appendChild( accounts );
|
|
|
|
TQPtrList<Kopete::Account> list = Kopete::AccountManager::self()->accounts();
|
|
// If no accounts, stop here
|
|
if ( !list.isEmpty() )
|
|
{
|
|
for( TQPtrListIterator<Kopete::Account> it( list );
|
|
Kopete::Account *account=it.current();
|
|
++it )
|
|
{
|
|
TQDomElement acc = doc.createElement( "account" );
|
|
//output += h.openTag( "account" );
|
|
|
|
TQDomElement protoName = doc.createElement( "protocol" );
|
|
TQDomText protoNameText = doc.createTextNode(
|
|
account->protocol()->pluginId() );
|
|
protoName.appendChild( protoNameText );
|
|
acc.appendChild( protoName );
|
|
|
|
Kopete::Contact* me = account->myself();
|
|
TQString displayName = me->property( Kopete::Global::Properties::self()->nickName() ).value().toString();
|
|
TQDomElement accName = doc.createElement( "accountname" );
|
|
TQDomText accNameText = doc.createTextNode( ( me )
|
|
? displayName
|
|
: notKnown );
|
|
accName.appendChild( accNameText );
|
|
acc.appendChild( accName );
|
|
|
|
TQDomElement acctqStatus = doc.createElement( "accountstatus" );
|
|
TQDomText statusText = doc.createTextNode( ( me )
|
|
? statusAsString( me->onlinetqStatus() )
|
|
: notKnown ) ;
|
|
acctqStatus.appendChild( statusText );
|
|
|
|
// Dont add these if we're shutting down, because the result
|
|
// would be quite weird.
|
|
if ( !shuttingDown ) {
|
|
|
|
// Add away message as an attribute, if one exists.
|
|
if ( me->onlinetqStatus().status() == Kopete::OnlineStatus::Away &&
|
|
!me->property("awayMessage").value().toString().isEmpty() ) {
|
|
acctqStatus.setAttribute( "awayreason",
|
|
me->property("awayMessage").value().toString() );
|
|
}
|
|
|
|
// Add the online status description as an attribute, if one exits.
|
|
if ( !me->onlinetqStatus().description().isEmpty() ) {
|
|
acctqStatus.setAttribute( "statusdescription",
|
|
me->onlinetqStatus().description() );
|
|
}
|
|
}
|
|
acc.appendChild( acctqStatus );
|
|
|
|
if ( showAddresses )
|
|
{
|
|
TQDomElement accAddress = doc.createElement( "accountaddress" );
|
|
TQDomText addressText = doc.createTextNode( ( me )
|
|
? me->contactId()
|
|
: notKnown );
|
|
accAddress.appendChild( addressText );
|
|
acc.appendChild( accAddress );
|
|
}
|
|
|
|
accounts.appendChild( acc );
|
|
}
|
|
}
|
|
|
|
// write the XML to a temporary file
|
|
KTempFile* file = new KTempFile();
|
|
TQTextStream *stream = file->textStream();
|
|
stream->setEncoding( TQTextStream::UnicodeUTF8 );
|
|
doc.save( *stream, 4 );
|
|
file->close();
|
|
return file;
|
|
}
|
|
|
|
bool WebPresencePlugin::transform( KTempFile * src, KTempFile * dest )
|
|
{
|
|
#ifdef HAVE_XSLT
|
|
bool retval = true;
|
|
xmlSubstituteEntitiesDefault( 1 );
|
|
xmlLoadExtDtdDefaultValue = 1;
|
|
|
|
TQFile sheet;
|
|
|
|
switch ( resultFormatting ) {
|
|
case WEB_XML:
|
|
// Oops! We tried to call transform() but XML was requested.
|
|
return false;
|
|
case WEB_HTML:
|
|
if ( useImagesInHTML ) {
|
|
sheet.setName( locate( "appdata", "webpresence/webpresence_html_images.xsl" ) );
|
|
} else {
|
|
sheet.setName( locate( "appdata", "webpresence/webpresence_html.xsl" ) );
|
|
}
|
|
break;
|
|
case WEB_XHTML:
|
|
if ( useImagesInHTML ) {
|
|
sheet.setName( locate( "appdata", "webpresence/webpresence_xhtml_images.xsl" ) );
|
|
} else {
|
|
sheet.setName( locate( "appdata", "webpresence/webpresence_xhtml.xsl" ) );
|
|
}
|
|
break;
|
|
case WEB_CUSTOM:
|
|
sheet.setName( userStyleSheet );
|
|
break;
|
|
default:
|
|
// Shouldn't ever reach here.
|
|
return false;
|
|
}
|
|
|
|
// TODO: auto / smart pointers would be useful here
|
|
xsltStylesheetPtr cur = 0;
|
|
xmlDocPtr doc = 0;
|
|
xmlDocPtr res = 0;
|
|
|
|
if ( !sheet.exists() ) {
|
|
kdDebug(14309) << k_funcinfo << "ERROR: Style sheet not found" << endl;
|
|
retval = false;
|
|
goto end;
|
|
}
|
|
|
|
// is the cast safe?
|
|
cur = xsltParseStylesheetFile( (const xmlChar *) sheet.name().latin1() );
|
|
if ( !cur ) {
|
|
kdDebug(14309) << k_funcinfo << "ERROR: Style sheet parsing failed" << endl;
|
|
retval = false;
|
|
goto end;
|
|
}
|
|
|
|
doc = xmlParseFile( TQFile::encodeName( src->name() ) );
|
|
if ( !doc ) {
|
|
kdDebug(14309) << k_funcinfo << "ERROR: XML parsing failed" << endl;
|
|
retval = false;
|
|
goto end;
|
|
}
|
|
|
|
res = xsltApplyStylesheet( cur, doc, 0 );
|
|
if ( !res ) {
|
|
kdDebug(14309) << k_funcinfo << "ERROR: Style sheet apply failed" << endl;
|
|
retval = false;
|
|
goto end;
|
|
}
|
|
|
|
if ( xsltSaveResultToFile(dest->fstream(), res, cur) == -1 ) {
|
|
kdDebug(14309) << k_funcinfo << "ERROR: Style sheet apply failed" << endl;
|
|
retval = false;
|
|
goto end;
|
|
}
|
|
|
|
// then it all worked!
|
|
dest->close();
|
|
|
|
end:
|
|
xsltCleanupGlobals();
|
|
xmlCleanupParser();
|
|
if (doc) xmlFreeDoc(doc);
|
|
if (res) xmlFreeDoc(res);
|
|
if (cur) xsltFreeStylesheet(cur);
|
|
|
|
return retval;
|
|
|
|
#else
|
|
Q_UNUSED( src );
|
|
Q_UNUSED( dest );
|
|
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
ProtocolList WebPresencePlugin::allProtocols()
|
|
{
|
|
kdDebug( 14309 ) << k_funcinfo << endl;
|
|
|
|
Kopete::PluginList plugins = Kopete::PluginManager::self()->loadedPlugins( "Protocols" );
|
|
Kopete::PluginList::ConstIterator it;
|
|
|
|
ProtocolList result;
|
|
|
|
for ( it = plugins.begin(); it != plugins.end(); ++it ) {
|
|
result.append( static_cast<Kopete::Protocol *>( *it ) );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
TQString WebPresencePlugin::statusAsString( const Kopete::OnlineStatus &newtqStatus )
|
|
{
|
|
if (shuttingDown)
|
|
return "OFFLINE";
|
|
|
|
TQString status;
|
|
switch ( newtqStatus.status() )
|
|
{
|
|
case Kopete::OnlineStatus::Online:
|
|
status = "ONLINE";
|
|
break;
|
|
case Kopete::OnlineStatus::Away:
|
|
status = "AWAY";
|
|
break;
|
|
case Kopete::OnlineStatus::Offline:
|
|
case Kopete::OnlineStatus::Invisible:
|
|
status = "OFFLINE";
|
|
break;
|
|
default:
|
|
status = "UNKNOWN";
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
void WebPresencePlugin::aboutToUnload()
|
|
{
|
|
// Stop timer. Dont need it anymore.
|
|
m_writeScheduler->stop();
|
|
|
|
// Force statusAsString() report all accounts as OFFLINE.
|
|
shuttingDown = true;
|
|
|
|
// Do final update of webpresence file.
|
|
slotWriteFile();
|
|
|
|
emit readyForUnload();
|
|
}
|
|
|
|
// vim: set noet ts=4 sts=4 sw=4:
|
|
#include "webpresenceplugin.moc"
|