summaryrefslogtreecommitdiffstats
path: root/kopete/plugins/webpresence/webpresenceplugin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/plugins/webpresence/webpresenceplugin.cpp')
-rw-r--r--kopete/plugins/webpresence/webpresenceplugin.cpp473
1 files changed, 473 insertions, 0 deletions
diff --git a/kopete/plugins/webpresence/webpresenceplugin.cpp b/kopete/plugins/webpresence/webpresenceplugin.cpp
new file mode 100644
index 00000000..1856d94c
--- /dev/null
+++ b/kopete/plugins/webpresence/webpresenceplugin.cpp
@@ -0,0 +1,473 @@
+/*
+ webpresenceplugin.cpp
+
+ Kopete Web Presence plugin
+
+ Copyright (c) 2005 by Tommi Rantala <[email protected]>
+ Copyright (c) 2002,2003 by Will Stephenson <[email protected]>
+
+ Kopete (c) 2002-2005 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <qdom.h>
+#include <qtimer.h>
+#include <qfile.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( QObject *parent, const char *name, const QStringList& /*args*/ )
+ : Kopete::Plugin( WebPresencePluginFactory::instance(), parent, name ),
+ shuttingDown( false ), resultFormatting( WEB_HTML )
+{
+ m_writeScheduler = new QTimer( this );
+ connect ( m_writeScheduler, SIGNAL( timeout() ), this, SLOT( slotWriteFile() ) );
+ connect( Kopete::AccountManager::self(), SIGNAL(accountRegistered(Kopete::Account*)),
+ this, SLOT( listenToAllAccounts() ) );
+ connect( Kopete::AccountManager::self(), SIGNAL(accountUnregistered(Kopete::Account*)),
+ this, SLOT( listenToAllAccounts() ) );
+
+ connect(this, SIGNAL(settingsChanged()), this, 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 )
+ {
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( *it );
+ QDictIterator<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
+ QObject::disconnect( account->myself(),
+ SIGNAL(onlineStatusChanged( Kopete::Contact *,
+ const Kopete::OnlineStatus &,
+ const Kopete::OnlineStatus & ) ),
+ this,
+ SLOT( slotWaitMoreStatusChanges() ) ) ;
+ QObject::connect( account->myself(),
+ SIGNAL(onlineStatusChanged( Kopete::Contact *,
+ const Kopete::OnlineStatus &,
+ const Kopete::OnlineStatus & ) ),
+ this,
+ 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, SIGNAL( result( KIO::Job * ) ),
+ 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;
+ QString notKnown = i18n( "Not yet known" );
+
+ QDomDocument doc;
+
+ doc.appendChild( doc.createProcessingInstruction( "xml",
+ "version=\"1.0\" encoding=\"UTF-8\"" ) );
+
+ QDomElement root = doc.createElement( "webpresence" );
+ doc.appendChild( root );
+
+ // insert the current date/time
+ QDomElement date = doc.createElement( "listdate" );
+ QDomText t = doc.createTextNode(
+ KGlobal::locale()->formatDateTime( QDateTime::currentDateTime() ) );
+ date.appendChild( t );
+ root.appendChild( date );
+
+ // insert the user's name
+ QDomElement name = doc.createElement( "name" );
+ QDomText 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
+ QDomElement accounts = doc.createElement( "accounts" );
+ root.appendChild( accounts );
+
+ QPtrList<Kopete::Account> list = Kopete::AccountManager::self()->accounts();
+ // If no accounts, stop here
+ if ( !list.isEmpty() )
+ {
+ for( QPtrListIterator<Kopete::Account> it( list );
+ Kopete::Account *account=it.current();
+ ++it )
+ {
+ QDomElement acc = doc.createElement( "account" );
+ //output += h.openTag( "account" );
+
+ QDomElement protoName = doc.createElement( "protocol" );
+ QDomText protoNameText = doc.createTextNode(
+ account->protocol()->pluginId() );
+ protoName.appendChild( protoNameText );
+ acc.appendChild( protoName );
+
+ Kopete::Contact* me = account->myself();
+ QString displayName = me->property( Kopete::Global::Properties::self()->nickName() ).value().toString();
+ QDomElement accName = doc.createElement( "accountname" );
+ QDomText accNameText = doc.createTextNode( ( me )
+ ? displayName
+ : notKnown );
+ accName.appendChild( accNameText );
+ acc.appendChild( accName );
+
+ QDomElement accStatus = doc.createElement( "accountstatus" );
+ QDomText statusText = doc.createTextNode( ( me )
+ ? statusAsString( me->onlineStatus() )
+ : notKnown ) ;
+ accStatus.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->onlineStatus().status() == Kopete::OnlineStatus::Away &&
+ !me->property("awayMessage").value().toString().isEmpty() ) {
+ accStatus.setAttribute( "awayreason",
+ me->property("awayMessage").value().toString() );
+ }
+
+ // Add the online status description as an attribute, if one exits.
+ if ( !me->onlineStatus().description().isEmpty() ) {
+ accStatus.setAttribute( "statusdescription",
+ me->onlineStatus().description() );
+ }
+ }
+ acc.appendChild( accStatus );
+
+ if ( showAddresses )
+ {
+ QDomElement accAddress = doc.createElement( "accountaddress" );
+ QDomText 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();
+ QTextStream *stream = file->textStream();
+ stream->setEncoding( QTextStream::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;
+
+ QFile 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( QFile::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;
+}
+
+QString WebPresencePlugin::statusAsString( const Kopete::OnlineStatus &newStatus )
+{
+ if (shuttingDown)
+ return "OFFLINE";
+
+ QString status;
+ switch ( newStatus.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"