~ubuntu-branches/ubuntu/saucy/kopete/saucy-proposed

« back to all changes in this revision

Viewing changes to plugins/webpresence/webpresenceplugin.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-06-21 02:22:39 UTC
  • Revision ID: package-import@ubuntu.com-20130621022239-63l3zc8p0nf26pt6
Tags: upstream-4.10.80
ImportĀ upstreamĀ versionĀ 4.10.80

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
   webpresenceplugin.cpp
 
3
 
 
4
   Kopete Web Presence plugin
 
5
 
 
6
   Copyright     2005-2006 by Tommi Rantala   <tommi.rantala@cs.helsinki.fi>
 
7
   Copyright (c) 2002,2003 by Will Stephenson <will@stevello.free-online.co.uk>
 
8
 
 
9
   Kopete    (c) 2002-2006 by the Kopete developers  <kopete-devel@kde.org>
 
10
 
 
11
 *************************************************************************
 
12
 *                                                                       *
 
13
 * This program is free software; you can redistribute it and/or modify  *
 
14
 * it under the terms of the GNU General Public License as published by  *
 
15
 * the Free Software Foundation; either version 2 of the License, or     *
 
16
 * (at your option) any later version.                                   *
 
17
 *                                                                       *
 
18
 *************************************************************************
 
19
 */
 
20
 
 
21
#include <config-kopete.h> // for HAVE_XSLT
 
22
 
 
23
#include "webpresenceplugin.h"
 
24
 
 
25
#include <QtCore/QTimer>
 
26
#include <QtCore/QFile>
 
27
#include <QtCore/QTextCodec>
 
28
#include <QtCore/QTextStream>
 
29
#include <QtCore/QList>
 
30
#include <QtCore/QDateTime>
 
31
#include <QtXml/QDomDocument>
 
32
 
 
33
#include <kdebug.h>
 
34
#include <kgenericfactory.h>
 
35
#include <kio/job.h>
 
36
#include <kmessagebox.h>
 
37
#include <ktemporaryfile.h>
 
38
#include <kstandarddirs.h>
 
39
 
 
40
#ifdef HAVE_XSLT
 
41
#include <libxml/parser.h>
 
42
#include <libxml/tree.h>
 
43
 
 
44
#include <libxslt/xsltconfig.h>
 
45
#include <libxslt/xsltInternals.h>
 
46
#include <libxslt/transform.h>
 
47
#include <libxslt/xsltutils.h>
 
48
#endif
 
49
 
 
50
#include "kopetepluginmanager.h"
 
51
#include "kopeteprotocol.h"
 
52
#include "kopeteaccountmanager.h"
 
53
#include "kopeteaccount.h"
 
54
#include "kopetecontact.h"
 
55
 
 
56
#include "webpresenceconfig.h"
 
57
 
 
58
K_PLUGIN_FACTORY(WebPresencePluginFactory, registerPlugin<WebPresencePlugin>();)
 
59
K_EXPORT_PLUGIN(WebPresencePluginFactory( "kopete_webpresence" ))
 
60
 
 
61
 
 
62
WebPresencePlugin::WebPresencePlugin( QObject *parent, const QVariantList& /*args*/ )
 
63
        : Kopete::Plugin( WebPresencePluginFactory::componentData(), parent ),
 
64
        shuttingDown( false ), resultFormatting( WEB_HTML ), m_output(0)
 
65
{
 
66
        m_writeScheduler = new QTimer( this );
 
67
        connect ( m_writeScheduler, SIGNAL(timeout()), this, SLOT(slotWriteFile()) );
 
68
        connect( Kopete::AccountManager::self(), SIGNAL(accountRegistered(Kopete::Account*)),
 
69
                                this, SLOT(listenToAllAccounts()) );
 
70
        connect( Kopete::AccountManager::self(), SIGNAL(accountUnregistered(const Kopete::Account*)),
 
71
                                this, SLOT(listenToAllAccounts()) );
 
72
 
 
73
        connect(this, SIGNAL(settingsChanged()), this, SLOT(slotSettingsChanged()) );
 
74
        slotSettingsChanged();
 
75
        listenToAllAccounts();
 
76
}
 
77
 
 
78
WebPresencePlugin::~WebPresencePlugin()
 
79
{
 
80
        delete m_output;
 
81
}
 
82
 
 
83
void WebPresencePlugin::slotSettingsChanged()
 
84
{
 
85
        // Force reading config
 
86
        WebPresenceConfig::self()->readConfig();
 
87
        
 
88
        resultFormatting = WEB_UNDEFINED;
 
89
 
 
90
        if ( WebPresenceConfig::self()->formatHTML() ) {
 
91
                resultFormatting = WEB_HTML;
 
92
        } else if ( WebPresenceConfig::self()->formatXHTML() ) {
 
93
                resultFormatting = WEB_XHTML;
 
94
        } else if ( WebPresenceConfig::self()->formatXML() ) {
 
95
                resultFormatting = WEB_XML;
 
96
        } else if ( WebPresenceConfig::self()->formatStylesheet() ) {
 
97
                resultFormatting = WEB_CUSTOM;
 
98
                userStyleSheet = WebPresenceConfig::self()->formatStylesheetURL();
 
99
        }
 
100
 
 
101
        // Default to HTML, if we don't get anything useful from config file.
 
102
        if ( resultFormatting == WEB_UNDEFINED )
 
103
                resultFormatting = WEB_HTML;
 
104
 
 
105
        // Update file
 
106
        slotWriteFile();
 
107
}
 
108
 
 
109
void WebPresencePlugin::listenToAllAccounts()
 
110
{
 
111
        // Connect to signals notifying of all accounts' status changes.
 
112
        ProtocolList protocols = allProtocols();
 
113
 
 
114
        for ( ProtocolList::Iterator it = protocols.begin();
 
115
                        it != protocols.end(); ++it )
 
116
        {
 
117
                QList<Kopete::Account*> accounts = Kopete::AccountManager::self()->accounts( *it );
 
118
                foreach(Kopete::Account *account, accounts)
 
119
                {
 
120
                        listenToAccount( account );
 
121
                }
 
122
        }
 
123
        slotWaitMoreStatusChanges();
 
124
}
 
125
 
 
126
void WebPresencePlugin::listenToAccount( Kopete::Account* account )
 
127
{
 
128
        if(account && account->myself())
 
129
        {
 
130
                // Connect to the account's status changed signal
 
131
                // because we can't know if the account has already connected
 
132
                QObject::disconnect( account->myself(),
 
133
                                                SIGNAL(onlineStatusChanged( Kopete::Contact *,
 
134
                                                                const Kopete::OnlineStatus &,
 
135
                                                                const Kopete::OnlineStatus & ) ),
 
136
                                                this,
 
137
                                                SLOT(slotWaitMoreStatusChanges()) ) ;
 
138
                QObject::connect( account->myself(),
 
139
                                                SIGNAL(onlineStatusChanged( Kopete::Contact *,
 
140
                                                                const Kopete::OnlineStatus &,
 
141
                                                                const Kopete::OnlineStatus & ) ),
 
142
                                                this,
 
143
                                                SLOT(slotWaitMoreStatusChanges()) );
 
144
        }
 
145
}
 
146
 
 
147
void WebPresencePlugin::slotWaitMoreStatusChanges()
 
148
{
 
149
        if ( !m_writeScheduler->isActive() )
 
150
                m_writeScheduler->start( WebPresenceConfig::self()->uploadFrequency() * 1000 );
 
151
}
 
152
 
 
153
void WebPresencePlugin::slotWriteFile()
 
154
{
 
155
        m_writeScheduler->stop();
 
156
 
 
157
        // generate the (temporary) XML file representing the current contact list
 
158
        const KUrl dest = WebPresenceConfig::self()->uploadURL();
 
159
        if ( dest.isEmpty() || !dest.isValid() )
 
160
        {
 
161
                kDebug(14309) << "url is empty or not valid. NOT UPDATING!";
 
162
                return;
 
163
        }
 
164
 
 
165
        KTemporaryFile* xml = generateFile();
 
166
        xml->setAutoRemove( true );
 
167
 
 
168
        switch( resultFormatting ) {
 
169
        case WEB_XML:
 
170
                m_output = xml;
 
171
                xml = 0L;
 
172
                break;
 
173
        case WEB_HTML:
 
174
        case WEB_XHTML:
 
175
        case WEB_CUSTOM:
 
176
                m_output = new KTemporaryFile();
 
177
                m_output->open();
 
178
 
 
179
                if ( !transform( xml, m_output ) )
 
180
                {
 
181
                        //TODO: give some error to user, even better if shown only once
 
182
                        delete m_output;
 
183
                        m_output = 0L;
 
184
 
 
185
                        delete xml;
 
186
                        return;
 
187
                }
 
188
 
 
189
                delete xml; // might make debugging harder!
 
190
                break;
 
191
        default:
 
192
                return;
 
193
        }
 
194
 
 
195
        // upload it to the specified URL
 
196
        KUrl src( m_output->fileName() );
 
197
        KIO::FileCopyJob *job = KIO::file_move( src, dest, -1, KIO::Overwrite | KIO::HideProgressInfo );
 
198
        connect( job, SIGNAL(result(KJob*)),
 
199
                        SLOT(slotUploadJobResult(KJob*)) );
 
200
}
 
201
 
 
202
void WebPresencePlugin::slotUploadJobResult( KJob *job )
 
203
{
 
204
        if ( job->error() ) {
 
205
                kDebug(14309) << "Error uploading presence info.";
 
206
                KMessageBox::queuedDetailedError( 0, i18n("An error occurred when uploading your presence page.\nCheck the path and write permissions of the destination."), 0, displayName() );
 
207
                delete m_output;
 
208
                m_output = 0L;
 
209
        }
 
210
}
 
211
 
 
212
KTemporaryFile* WebPresencePlugin::generateFile()
 
213
{
 
214
        // generate the (temporary) XML file representing the current contact list
 
215
        kDebug( 14309 );
 
216
        QString notKnown = i18n( "Not yet known" );
 
217
 
 
218
        QDomDocument doc;
 
219
 
 
220
        doc.appendChild( doc.createProcessingInstruction( "xml",
 
221
                                "version=\"1.0\" encoding=\"UTF-8\"" ) );
 
222
 
 
223
        QDomElement root = doc.createElement( "webpresence" );
 
224
        doc.appendChild( root );
 
225
 
 
226
        // insert the current date/time
 
227
        QDomElement date = doc.createElement( "listdate" );
 
228
        QDomText t = doc.createTextNode(
 
229
                        KGlobal::locale()->formatDateTime( QDateTime::currentDateTime() ) );
 
230
        date.appendChild( t );
 
231
        root.appendChild( date );
 
232
 
 
233
        // insert the user's name
 
234
        QDomElement name = doc.createElement( "name" );
 
235
        QDomText nameText;
 
236
        const QString userName = WebPresenceConfig::self()->showThisName();
 
237
        if ( !WebPresenceConfig::self()->showName() && !userName.isEmpty() )
 
238
                nameText = doc.createTextNode( userName );
 
239
        else
 
240
                nameText = doc.createTextNode( notKnown );
 
241
        name.appendChild( nameText );
 
242
        root.appendChild( name );
 
243
 
 
244
        // insert the list of the user's accounts
 
245
        QDomElement accounts = doc.createElement( "accounts" );
 
246
        root.appendChild( accounts );
 
247
 
 
248
        QList<Kopete::Account*> list = Kopete::AccountManager::self()->accounts();
 
249
        // If no accounts, stop here
 
250
        if ( !list.isEmpty() )
 
251
        {
 
252
                foreach(Kopete::Account *account, list)
 
253
                {
 
254
                        QDomElement acc = doc.createElement( "account" );
 
255
                        //output += h.openTag( "account" );
 
256
 
 
257
                        QDomElement protoName = doc.createElement( "protocol" );
 
258
                        QDomText protoNameText = doc.createTextNode(
 
259
                                        account->protocol()->pluginId() );
 
260
                        protoName.appendChild( protoNameText );
 
261
                        acc.appendChild( protoName );
 
262
 
 
263
                        Kopete::Contact* me = account->myself();
 
264
                        QString displayName = me->property( Kopete::Global::Properties::self()->nickName() ).value().toString();
 
265
                        QDomElement accName = doc.createElement( "accountname" );
 
266
                        QDomText accNameText = doc.createTextNode( ( me )
 
267
                                        ? displayName
 
268
                                        : notKnown );
 
269
                        accName.appendChild( accNameText );
 
270
                        acc.appendChild( accName );
 
271
 
 
272
                        QDomElement accStatus = doc.createElement( "accountstatus" );
 
273
                        QDomText statusText = doc.createTextNode( ( me )
 
274
                                        ? statusAsString( me->onlineStatus() )
 
275
                                        : notKnown ) ;
 
276
                        accStatus.appendChild( statusText );
 
277
 
 
278
                        // Do not add these if we're shutting down, because the result
 
279
                        // would be quite weird.
 
280
                        if ( !shuttingDown ) {
 
281
 
 
282
                                // Add away message as an attribute, if one exists.
 
283
                                if ( (me->onlineStatus().status() == Kopete::OnlineStatus::Away ||
 
284
                                                me->onlineStatus().status() == Kopete::OnlineStatus::Busy) &&
 
285
                                                !me->property("awayMessage").value().toString().isEmpty() ) {
 
286
                                        accStatus.setAttribute( "awayreason",
 
287
                                                        me->property("awayMessage").value().toString() );
 
288
                                }
 
289
 
 
290
                                // Add the online status description as an attribute, if one exits.
 
291
                                if ( !me->onlineStatus().description().isEmpty() ) {
 
292
                                        accStatus.setAttribute( "statusdescription",
 
293
                                                        me->onlineStatus().description() );
 
294
                                }
 
295
                        }
 
296
                        acc.appendChild( accStatus );
 
297
 
 
298
                        if ( WebPresenceConfig::self()->includeIMAddress() )
 
299
                        {
 
300
                                QDomElement accAddress = doc.createElement( "accountaddress" );
 
301
                                QDomText addressText = doc.createTextNode( ( me )
 
302
                                                ? me->contactId()
 
303
                                                : notKnown );
 
304
                                accAddress.appendChild( addressText );
 
305
                                acc.appendChild( accAddress );
 
306
                        }
 
307
 
 
308
                        accounts.appendChild( acc );
 
309
                }
 
310
        }
 
311
 
 
312
        // write the XML to a temporary file
 
313
        KTemporaryFile* file = new KTemporaryFile();
 
314
        file->setAutoRemove(false);
 
315
        file->open();
 
316
        QTextStream stream( file );
 
317
        stream.setCodec(QTextCodec::codecForName("UTF-8"));
 
318
        doc.save( stream, 4 );
 
319
        stream.flush();
 
320
        return file;
 
321
}
 
322
 
 
323
bool WebPresencePlugin::transform( KTemporaryFile * src, KTemporaryFile * dest )
 
324
{
 
325
#ifdef HAVE_XSLT
 
326
        bool retval = true;
 
327
        xmlSubstituteEntitiesDefault( 1 );
 
328
        xmlLoadExtDtdDefaultValue = 1;
 
329
 
 
330
        QFile sheet;
 
331
 
 
332
        switch ( resultFormatting ) {
 
333
        case WEB_XML:
 
334
                // Oops! We tried to call transform() but XML was requested.
 
335
                return false;
 
336
        case WEB_HTML:
 
337
                if ( WebPresenceConfig::self()->useImagesHTML() ) {
 
338
                        sheet.setFileName( KStandardDirs::locate( "appdata", "webpresence/webpresence_html_images.xsl" ) );
 
339
                } else {
 
340
                        sheet.setFileName( KStandardDirs::locate( "appdata", "webpresence/webpresence_html.xsl" ) );
 
341
                }
 
342
                break;
 
343
        case WEB_XHTML:
 
344
                if ( WebPresenceConfig::self()->useImagesHTML() ) {
 
345
                        sheet.setFileName( KStandardDirs::locate( "appdata", "webpresence/webpresence_xhtml_images.xsl" ) );
 
346
                } else {
 
347
                        sheet.setFileName( KStandardDirs::locate( "appdata", "webpresence/webpresence_xhtml.xsl" ) );
 
348
                }
 
349
                break;
 
350
        case WEB_CUSTOM:
 
351
                sheet.setFileName( userStyleSheet.path() );
 
352
                break;
 
353
        default:
 
354
                // Shouldn't ever reach here.
 
355
                return false;
 
356
        }
 
357
 
 
358
        // TODO: auto / smart pointers would be useful here
 
359
        xsltStylesheetPtr cur = 0;
 
360
        xmlDocPtr doc = 0;
 
361
        xmlDocPtr res = 0;
 
362
 
 
363
        if ( !sheet.exists() ) {
 
364
                kDebug(14309) << "ERROR: Style sheet not found";
 
365
                retval = false;
 
366
                goto end;
 
367
        }
 
368
 
 
369
        // is the cast safe?
 
370
        cur = xsltParseStylesheetFile( (const xmlChar *) sheet.fileName().toLatin1().data() );
 
371
        if ( !cur ) {
 
372
                kDebug(14309) << "ERROR: Style sheet parsing failed";
 
373
                retval = false;
 
374
                goto end;
 
375
        }
 
376
 
 
377
        doc = xmlParseFile( QFile::encodeName( src->fileName() ) );
 
378
        if ( !doc ) {
 
379
                kDebug(14309) << "ERROR: XML parsing failed";
 
380
                retval = false;
 
381
                goto end;
 
382
        }
 
383
 
 
384
        res = xsltApplyStylesheet( cur, doc, 0 );
 
385
        if ( !res ) {
 
386
                kDebug(14309) << "ERROR: Style sheet apply failed";
 
387
                retval = false;
 
388
                goto end;
 
389
        }
 
390
 
 
391
 
 
392
        if ( xsltSaveResultToFd(dest->handle(), res, cur) == -1 ) {
 
393
                kDebug(14309) << "ERROR: Style sheet apply failed";
 
394
                retval = false;
 
395
                goto end;
 
396
        }
 
397
 
 
398
        // then it all worked!
 
399
 
 
400
end:
 
401
        xsltCleanupGlobals();
 
402
        xmlCleanupParser();
 
403
        if (doc) xmlFreeDoc(doc);
 
404
        if (res) xmlFreeDoc(res);
 
405
        if (cur) xsltFreeStylesheet(cur);
 
406
 
 
407
        return retval;
 
408
 
 
409
#else
 
410
        Q_UNUSED( src );
 
411
        Q_UNUSED( dest );
 
412
 
 
413
        return false;
 
414
#endif
 
415
}
 
416
 
 
417
ProtocolList WebPresencePlugin::allProtocols()
 
418
{
 
419
        kDebug( 14309 ) ;
 
420
 
 
421
        Kopete::PluginList plugins = Kopete::PluginManager::self()->loadedPlugins( "Protocols" );
 
422
        Kopete::PluginList::ConstIterator it;
 
423
 
 
424
        ProtocolList result;
 
425
 
 
426
        for ( it = plugins.constBegin(); it != plugins.constEnd(); ++it ) {
 
427
                result.append( static_cast<Kopete::Protocol *>( *it ) );
 
428
        }
 
429
 
 
430
        return result;
 
431
}
 
432
 
 
433
QString WebPresencePlugin::statusAsString( const Kopete::OnlineStatus &newStatus )
 
434
{
 
435
        if (shuttingDown)
 
436
                return "OFFLINE";
 
437
 
 
438
        QString status;
 
439
        switch ( newStatus.status() )
 
440
        {
 
441
        case Kopete::OnlineStatus::Online:
 
442
                status = "ONLINE";
 
443
                break;
 
444
        case Kopete::OnlineStatus::Away:
 
445
                status = "AWAY";
 
446
                break;
 
447
        case Kopete::OnlineStatus::Busy:
 
448
                status = "BUSY";
 
449
                break;
 
450
        case Kopete::OnlineStatus::Offline:
 
451
        case Kopete::OnlineStatus::Invisible:
 
452
                status = "OFFLINE";
 
453
                break;
 
454
        default:
 
455
                status = "UNKNOWN";
 
456
        }
 
457
 
 
458
        return status;
 
459
}
 
460
 
 
461
void WebPresencePlugin::aboutToUnload()
 
462
{
 
463
        // Stop timer. Do not need it anymore.
 
464
        m_writeScheduler->stop();
 
465
 
 
466
        // Force statusAsString() report all accounts as OFFLINE.
 
467
        shuttingDown = true;
 
468
 
 
469
        // Do final update of webpresence file.
 
470
        slotWriteFile();
 
471
 
 
472
        emit readyForUnload();
 
473
}
 
474
 
 
475
// vim: set noet ts=4 sts=4 sw=4:
 
476
#include "webpresenceplugin.moc"