summaryrefslogtreecommitdiffstats
path: root/dcoprss
diff options
context:
space:
mode:
Diffstat (limited to 'dcoprss')
-rw-r--r--dcoprss/Makefile.am28
-rw-r--r--dcoprss/article.cpp49
-rw-r--r--dcoprss/cache.cpp129
-rw-r--r--dcoprss/cache.h86
-rw-r--r--dcoprss/client.cpp75
-rw-r--r--dcoprss/document.cpp307
-rw-r--r--dcoprss/feedbrowser.cpp144
-rw-r--r--dcoprss/feedbrowser.h66
-rw-r--r--dcoprss/main.cpp39
-rw-r--r--dcoprss/query.cpp271
-rw-r--r--dcoprss/query.h120
-rw-r--r--dcoprss/rssnewsfeed.h115
-rw-r--r--dcoprss/rssservice.desktop88
-rw-r--r--dcoprss/service.cpp105
-rw-r--r--dcoprss/service.h290
-rw-r--r--dcoprss/test.sh24
-rw-r--r--dcoprss/xmlrpciface.cpp401
-rw-r--r--dcoprss/xmlrpciface.h185
18 files changed, 2522 insertions, 0 deletions
diff --git a/dcoprss/Makefile.am b/dcoprss/Makefile.am
new file mode 100644
index 00000000..2bc93d67
--- /dev/null
+++ b/dcoprss/Makefile.am
@@ -0,0 +1,28 @@
+bin_PROGRAMS = rssservice rssclient feedbrowser
+INCLUDES = -I$(top_srcdir) $(all_includes)
+
+rssservice_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+rssservice_LDADD = $(LIB_KIO) ../librss/librss.la
+rssservice_SOURCES = main.cpp service.cpp query.cpp document.cpp article.cpp query.skel service.skel xmlrpciface.cpp cache.cpp
+
+
+# client stuff
+rssclient_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+rssclient_LDADD = $(LIB_KDECORE)
+rssclient_SOURCES = client.cpp
+
+feedbrowser_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+feedbrowser_LDADD = $(LIB_KDEUI)
+feedbrowser_SOURCES = feedbrowser.skel feedbrowser.cpp
+
+noinst_HEADERS = service.h query.h xmlrpciface.h cache.h
+
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/dcoprss.pot
+
+
+service_DATA = rssservice.desktop
+servicedir = $(kde_servicesdir)
+
diff --git a/dcoprss/article.cpp b/dcoprss/article.cpp
new file mode 100644
index 00000000..d191bd74
--- /dev/null
+++ b/dcoprss/article.cpp
@@ -0,0 +1,49 @@
+/* $Id$ */
+/***************************************************************************
+ article.cpp - A DCOP Service to provide RSS data
+ -------------------
+ begin : Saturday 15 February 2003
+ copyright : (C) 2003 by Ian Reinhart Geiser
+ email : geiseri@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 <kdebug.h>
+#include <kurl.h>
+#include "service.h"
+
+RSSArticle::RSSArticle(Article *art) :
+ DCOPObject(), m_Art(art)
+{
+ kdDebug() << "New article..." << endl;
+ kdDebug() << m_Art->link().prettyURL() << endl;
+}
+
+RSSArticle::~RSSArticle()
+{
+ kdDebug() << "Article going away..." << endl;
+ delete m_Art;
+}
+
+QString RSSArticle::title()
+{
+ //kdDebug() << "Get title " << m_Art->title() << endl;
+ return m_Art->title();
+}
+
+QString RSSArticle::description()
+{
+ return m_Art->description();
+}
+
+QString RSSArticle::link()
+{
+ return m_Art->link().prettyURL();
+}
diff --git a/dcoprss/cache.cpp b/dcoprss/cache.cpp
new file mode 100644
index 00000000..9c80a9a3
--- /dev/null
+++ b/dcoprss/cache.cpp
@@ -0,0 +1,129 @@
+/*
+ * cache.cpp - (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "cache.h"
+#include "xmlrpciface.h"
+
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include <qdatastream.h>
+#include <qfile.h>
+
+bool CacheEntry::isValid() const
+{
+ // Cache entries get invalid after on hour. One shouldn't hardcode this
+ // but for now it'll do.
+ return m_timeStamp.secsTo( QDateTime::currentDateTime() ) < 3600;
+}
+
+Cache *Cache::m_instance = 0;
+
+Cache &Cache::self()
+{
+ if ( !m_instance )
+ m_instance = new Cache;
+ return *m_instance;
+}
+
+QString Cache::getCacheKey( const QString &server, const QString &method,
+ const QValueList<QVariant> &args )
+{
+ QString key;
+ key = server + QString::fromLatin1( "__" );
+ key += method + QString::fromLatin1( "__" );
+ QValueList<QVariant>::ConstIterator it = args.begin();
+ QValueList<QVariant>::ConstIterator end = args.end();
+ for ( ; it != end; ++it )
+ key += KXMLRPC::Query::marshal( *it );
+
+ return key;
+}
+
+Cache::Cache()
+{
+ load();
+}
+
+Cache::~Cache()
+{
+ save();
+}
+
+void Cache::load()
+{
+ QFile file( cacheFileName() );
+ if ( !file.open( IO_ReadOnly ) ) {
+ kdDebug() << "Failed to open cache file " << cacheFileName() << endl;
+ return;
+ }
+
+ QDataStream stream( &file );
+ while ( !stream.atEnd() ) {
+ QString key;
+ stream >> key;
+
+ CacheEntry *entry = new CacheEntry;
+ stream >> *entry;
+
+ QDict<CacheEntry>::insert( key, entry );
+ }
+}
+
+void Cache::save()
+{
+ QFile file( cacheFileName() );
+ if ( !file.open( IO_WriteOnly ) ) {
+ kdDebug() << "Failed to open cache file " << cacheFileName() << endl;
+ return;
+ }
+
+ QDataStream stream( &file );
+
+ QDictIterator<CacheEntry> it( *this );
+ for ( ; it.current() != 0; ++it )
+ stream << it.currentKey() << *it.current();
+}
+
+void Cache::touch( const QString &key )
+{
+ CacheEntry *entry = find( key );
+ if ( !entry )
+ return;
+ entry->m_timeStamp = QDateTime::currentDateTime();
+}
+
+void Cache::insert( const QString &key, const KXMLRPC::Query::Result &result )
+{
+ CacheEntry *entry = new CacheEntry;
+ entry->m_timeStamp = QDateTime::currentDateTime();
+ entry->m_result = result;
+ QDict<CacheEntry>::insert( key, entry );
+}
+
+QString Cache::cacheFileName() const
+{
+ return locateLocal( "appdata", "cache/dcoprss.cache" );
+}
+
diff --git a/dcoprss/cache.h b/dcoprss/cache.h
new file mode 100644
index 00000000..8248b609
--- /dev/null
+++ b/dcoprss/cache.h
@@ -0,0 +1,86 @@
+/*
+ * cache.h - (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef CACHE_H
+#define CACHE_H
+
+#include <qcstring.h>
+#include <qdatetime.h>
+#include <qdict.h>
+
+#include <xmlrpciface.h>
+
+class CacheEntry
+{
+ friend class Cache;
+ friend QDataStream &operator>>( QDataStream &s, CacheEntry &e );
+ public:
+ const QDateTime &timeStamp() const { return m_timeStamp; }
+ const KXMLRPC::Query::Result result() const { return m_result; }
+ bool isValid() const;
+
+ private:
+ QDateTime m_timeStamp;
+ KXMLRPC::Query::Result m_result;
+};
+
+class Cache : public QDict<CacheEntry>
+{
+ public:
+ static Cache &self();
+
+ static QString getCacheKey( const QString &server,
+ const QString &method,
+ const QValueList<QVariant> &args );
+
+ void load();
+ void save();
+
+ void touch( const QString &key );
+
+ void insert( const QString &key, const KXMLRPC::Query::Result &result );
+
+ private:
+ Cache();
+ Cache( const Cache &rhs ); // disabled
+ Cache &operator=( const Cache &rhs ); // disabled
+ ~Cache();
+
+ QString cacheFileName() const;
+
+ static Cache *m_instance;
+};
+
+inline QDataStream &operator<<( QDataStream &s, const CacheEntry &e )
+{
+ return s << e.timeStamp() << e.result();
+}
+
+inline QDataStream &operator>>( QDataStream &s, CacheEntry &e )
+{
+ return s >> e.m_timeStamp >> e.m_result;
+}
+
+#endif // CACHE_H
+// vim:ts=4:sw=4:noet
diff --git a/dcoprss/client.cpp b/dcoprss/client.cpp
new file mode 100644
index 00000000..b74894de
--- /dev/null
+++ b/dcoprss/client.cpp
@@ -0,0 +1,75 @@
+/* $Id$ */
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <dcopref.h>
+#include <qdatastream.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qcstring.h>
+#include <kdebug.h>
+#include <stdlib.h>
+/*
+class rssIface : virtual public DCOPObject
+{
+ K_DCOP
+public:
+
+ rssIface( KApplication *app)
+ {
+ // get our DCOP client and attach so that we may use it
+ DCOPClient *client = app->dcopClient();
+ client->attach();
+ QString error;
+ QCString appID;
+ kdDebug() << "Looking for rss service..." << endl;
+ if (!client->isApplicationRegistered("rssservice"))
+ {
+ kdDebug() << "Could not find service so I am starting it..." << endl;
+ if(KApplication::startServiceByName("rssservice",QStringList(), &error, &appID ))
+ {
+ kdDebug() << "Starting rssservice failed with message: " << error << endl;
+ exit(0);
+ }
+ }
+ kdDebug ()<< "Accessing rssservice..." << endl;
+
+ if (!connectDCOPSignal(0,0, "documentUpdated(DCOPRef)",
+ "refresh(DCOPRef)",false))
+ kdDebug() << "Could not attach signal..." << endl;
+ else
+ kdDebug() << "attached dcop signals..." << endl;
+
+ QString url("http://freshmeat.net/backend/fm.rdf");
+ DCOPRef m_rssservice("rssservice","RSSService");
+ m_rssservice.call("load(QString)", url);
+ QStringList returnList = m_rssservice.call("list()");
+ DCOPRef doc = m_rssservice.call("document(QString)", returnList[0]);
+ QString title = doc.call("title()");
+ QString link = doc.call("link()");
+ QString description = doc.call("description()");
+ kdDebug() << title << endl;
+ kdDebug() << link << endl;
+ kdDebug() << description << endl;
+ }
+
+ k_dcop:
+ virtual void refresh(DCOPRef doc)
+ {
+ QString title = doc.call("title()");
+ QString link = doc.call("link()");
+ QString description = doc.call("description()");
+ kdDebug() << title << endl;
+ kdDebug() << link << endl;
+ kdDebug() << description << endl;
+ }
+
+ private:
+
+};
+*/
+int main(int argc, char **argv)
+{
+ KApplication *app = new KApplication(argc, argv, "client", false);
+
+ app->exec();
+}
diff --git a/dcoprss/document.cpp b/dcoprss/document.cpp
new file mode 100644
index 00000000..b43f600e
--- /dev/null
+++ b/dcoprss/document.cpp
@@ -0,0 +1,307 @@
+/* $Id$ */
+/***************************************************************************
+ document.cpp - A DCOP Service to provide RSS data
+ -------------------
+ begin : Saturday 15 February 2003
+ copyright : (C) 2003 by Ian Reinhart Geiser
+ email : geiseri@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 <kdebug.h>
+#include <qdatetime.h>
+#include <kurl.h>
+#include "service.h"
+
+RSSDocument::RSSDocument(const QString& url) :
+ QObject(), DCOPObject(), m_Url(url)
+{
+
+ m_list.setAutoDelete( true );
+ m_Doc = 0L;
+ m_pix = QPixmap();
+ m_isLoading = false;
+ m_maxAge = 60;
+ m_Timeout = QDateTime::currentDateTime();
+ m_state.clear();
+}
+
+RSSDocument::~RSSDocument()
+{
+ kdDebug() << "Document going away..." << endl;
+
+ delete m_Doc;
+}
+
+void RSSDocument::loadingComplete(Loader *ldr, Document doc, Status stat)
+{
+
+
+ if( m_Doc != 0L)
+ {
+ delete m_Doc;
+
+ }
+
+ if (stat != RSS::Success)
+ {
+ kdDebug() << "Document error! Loader:" << ldr->errorCode() << " Parser:" << stat << endl;
+
+ m_isLoading = false;
+ m_Doc = 0L;
+ if( stat == RSS::ParseError )
+ documentUpdateError(DCOPRef(this), 1);
+ else if( stat == RSS::RetrieveError )
+ documentUpdateError(DCOPRef(this), 2);
+ else
+ documentUpdateError(DCOPRef(this), 3);
+ }
+ else
+ {
+ kdDebug() << "New Document is done..." << endl;
+ m_Doc = new Document(doc);
+ m_list.clear();
+ Article::List list = doc.articles();
+ for(Article::List::ConstIterator it = list.begin(); it != list.end(); ++it)
+ {
+ int state = m_state[(*it).title()];
+ if( state == 0 ) m_state[(*it).title()] = 1; // new
+ else if( state == 1 ) m_state[(*it).title()] = 2; // old message now
+ m_list.append( new RSSArticle( new Article(*it)));
+ }
+ Image *img = m_Doc->image();
+ if ( img )
+ {
+ connect(img, SIGNAL(gotPixmap(const QPixmap &)),
+ SLOT(pixmapLoaded(const QPixmap &)));
+ img->getPixmap();
+ pixmapUpdating(DCOPRef(this));
+ }
+ m_isLoading = false;
+ documentUpdated(DCOPRef(this));
+
+ kdDebug() << "Old Mod time " << m_Timeout.toString() << endl;
+ m_Timeout = m_Timeout.addSecs(m_maxAge * 60 );
+ kdDebug() << "New Mod time " << m_Timeout.toString() << endl;
+
+ }
+}
+
+void RSSDocument::pixmapLoaded(const QPixmap &pix )
+{
+ m_pix = pix;
+ pixmapUpdated(DCOPRef(this));
+}
+
+QString RSSDocument::webMaster()
+{
+ if( m_Doc != 0L)
+ return m_Doc->webMaster();
+ else
+ return "";
+}
+
+QString RSSDocument::managingEditor()
+{
+ if( m_Doc != 0L)
+ return m_Doc->managingEditor();
+ else
+ return "";
+}
+
+QString RSSDocument::rating()
+{
+ if( m_Doc != 0L)
+ return m_Doc->rating();
+ else
+ return "";
+}
+
+QDateTime RSSDocument::lastBuildDate()
+{
+ if( m_Doc != 0L)
+ return m_Doc->lastBuildDate();
+ else
+ return QDateTime::currentDateTime();
+}
+
+QDateTime RSSDocument::pubDate()
+{
+ if( m_Doc != 0L)
+ return m_Doc->pubDate();
+ else
+ return QDateTime::currentDateTime();
+}
+
+QString RSSDocument::copyright()
+{
+ if( m_Doc != 0L)
+ return m_Doc->copyright();
+ else
+ return "";
+}
+
+QStringList RSSDocument::articles()
+{
+ if( m_Doc != 0L)
+ {
+ kdDebug() << "Document giving articles..." << endl;
+ Article::List list = m_Doc->articles();
+ QStringList stringList;
+
+ for(Article::List::ConstIterator it = list.begin(); it != list.end(); ++it)
+ stringList.append((*it).title());
+ return stringList;
+ }
+ else
+ return QStringList();
+}
+
+DCOPRef RSSDocument::article(int idx)
+{
+ if(m_list.at(idx))
+ return DCOPRef(m_list.at(idx));
+ else
+ return DCOPRef();
+}
+
+int RSSDocument::count()
+{
+ if( m_Doc != 0L)
+ return m_Doc->articles().count();
+ return 0;
+}
+
+QString RSSDocument::link()
+{
+ if( m_Doc != 0L)
+ return m_Doc->link().prettyURL();
+ else
+ return "";
+}
+
+QString RSSDocument::description()
+{
+ if( m_Doc != 0L)
+ return m_Doc->description();
+ else
+ return "";
+}
+
+QString RSSDocument::title()
+{
+ if( m_Doc != 0L)
+ return m_Doc->title();
+ else
+ return "";
+}
+
+QString RSSDocument::verbVersion()
+{
+ if( m_Doc != 0L)
+ return m_Doc->verbVersion();
+ else
+ return "";
+}
+
+QString RSSDocument::pixmapURL()
+{
+ if( m_Doc != 0L)
+ if( m_Doc->image() )
+ return m_Doc->image()->url().prettyURL();
+ else
+ return "";
+ else
+ return "";
+}
+
+QPixmap RSSDocument::pixmap()
+{
+ return m_pix;
+}
+
+bool RSSDocument::documentValid()
+{
+ if (m_Doc != 0L)
+ return true;
+ else
+ return false;
+}
+
+bool RSSDocument::pixmapValid()
+{
+ return !m_pix.isNull();
+}
+
+void RSSDocument::refresh()
+{
+ kdDebug() << "Mod time " << m_Timeout.toString() << endl;
+ kdDebug() << "Current time " << QDateTime::currentDateTime().toString() << endl;
+
+ if(!m_isLoading && (QDateTime::currentDateTime() >= m_Timeout))
+ {
+ kdDebug() << "Document going to refresh" << endl;
+ m_isLoading = true;
+ Loader *loader = Loader::create(this,
+ SLOT(loadingComplete(Loader *, Document, Status)));
+ loader->loadFrom(KURL( m_Url ), new FileRetriever());
+ documentUpdating(DCOPRef(this));
+ }
+ else
+ {
+ documentUpdated(DCOPRef(this));
+ if(pixmapValid())
+ pixmapUpdated(DCOPRef(this));
+ /*
+ else
+ {
+ // Refactor this!
+ Image *img = m_Doc->image();
+ if ( img )
+ {
+ connect(img, SIGNAL(gotPixmap(const QPixmap &)),
+ SLOT(pixmapLoaded(const QPixmap &)));
+ img->getPixmap();
+ pixmapUpdating(DCOPRef(this));
+ }
+ }
+ */
+ }
+
+}
+
+int RSSDocument::maxAge()
+{
+ return m_maxAge;
+}
+
+void RSSDocument::setMaxAge(int _min)
+{
+ m_Timeout.addSecs(-m_maxAge);
+ m_maxAge = _min;
+ m_Timeout.addSecs(m_maxAge);
+}
+
+int RSSDocument::state( const QString &title) const
+{
+ return m_state[title];
+}
+
+void RSSDocument::setState( const QString &title, int s )
+{
+ m_state[title] = s;
+}
+
+void RSSDocument::read( const QString &title)
+{
+ m_state[title] = 3;
+}
+
+#include "service.moc"
diff --git a/dcoprss/feedbrowser.cpp b/dcoprss/feedbrowser.cpp
new file mode 100644
index 00000000..59a55040
--- /dev/null
+++ b/dcoprss/feedbrowser.cpp
@@ -0,0 +1,144 @@
+#include "feedbrowser.h"
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <dcopclient.h>
+
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qvbox.h>
+
+CategoryItem::CategoryItem( KListView *parent, const QString &category )
+ : KListViewItem( parent ),
+ m_category( category )
+{
+ init();
+}
+
+CategoryItem::CategoryItem( KListViewItem *parent, const QString &category )
+ : KListViewItem( parent ),
+ m_category( category )
+{
+ init();
+}
+
+void CategoryItem::init()
+{
+ m_populated = false;
+ m_dcopIface = 0;
+
+ setText( 0, m_category.mid( m_category.findRev( '/' ) + 1 ).replace( '_', ' ' ) );
+}
+
+void CategoryItem::setOpen( bool open )
+{
+ if ( open && !m_populated ) {
+ populate();
+ m_populated = true;
+ }
+ KListViewItem::setOpen( open );
+}
+
+void CategoryItem::populate()
+{
+ m_dcopIface = new DCOPRSSIface( this, "m_dcopIface" );
+ connect( m_dcopIface, SIGNAL( gotCategories( const QStringList & ) ),
+ this, SLOT( gotCategories( const QStringList & ) ) );
+ m_dcopIface->getCategories( m_category );
+}
+
+void CategoryItem::gotCategories( const QStringList &categories )
+{
+ delete m_dcopIface;
+ m_dcopIface = 0;
+
+ QStringList::ConstIterator it = categories.begin();
+ QStringList::ConstIterator end = categories.end();
+ for ( ; it != end; ++it )
+ new CategoryItem( this, *it );
+
+ if ( !categories.isEmpty() )
+ KListViewItem::setOpen( true );
+}
+
+DCOPRSSIface::DCOPRSSIface( QObject *parent, const char *name ) :
+ QObject( parent, name ), DCOPObject( "FeedBrowser" )
+{
+ connectDCOPSignal( "rssservice", "RSSQuery", "gotCategories(QStringList)",
+ "slotGotCategories(QStringList)", false );
+}
+
+void DCOPRSSIface::getCategories( const QString &cat )
+{
+ QByteArray data;
+ QDataStream stream( data, IO_WriteOnly );
+ stream << cat;
+ kapp->dcopClient()->send( "rssservice", "RSSQuery",
+ "getCategories(QString)", data );
+}
+
+void DCOPRSSIface::slotGotCategories( const QStringList &categories )
+{
+ emit gotCategories( categories );
+}
+
+FeedBrowserDlg::FeedBrowserDlg( QWidget *parent, const char *name )
+ : KDialogBase( parent, name, true, i18n( "DCOPRSS Feed Browser" ),
+ Close, Close, true )
+{
+ m_dcopIface = new DCOPRSSIface( this, "m_dcopIface" );
+ connect( m_dcopIface, SIGNAL( gotCategories( const QStringList & ) ),
+ this, SLOT( gotTopCategories( const QStringList & ) ) );
+
+ QVBox *mainWidget = makeVBoxMainWidget();
+
+ m_feedList = new KListView( mainWidget, "m_feedList" );
+ m_feedList->setAllColumnsShowFocus( true );
+ m_feedList->setRootIsDecorated( true );
+ m_feedList->addColumn( i18n( "Name" ) );
+ connect( m_feedList, SIGNAL( executed( QListViewItem * ) ),
+ this, SLOT( itemSelected( QListViewItem * ) ) );
+ connect( m_feedList, SIGNAL( returnPressed( QListViewItem * ) ),
+ this, SLOT( itemSelected( QListViewItem * ) ) );
+
+ resize( 500, 400 );
+
+ getTopCategories();
+}
+
+void FeedBrowserDlg::getTopCategories()
+{
+ m_dcopIface->getCategories( "Top" );
+}
+
+void FeedBrowserDlg::gotTopCategories( const QStringList &categories )
+{
+ QStringList::ConstIterator it = categories.begin();
+ QStringList::ConstIterator end = categories.end();
+ for ( ; it != end; ++it )
+ new CategoryItem( m_feedList, *it );
+}
+
+void FeedBrowserDlg::itemSelected( QListViewItem *item )
+{
+ item->setOpen( !item->isOpen() );
+}
+
+int main( int argc, char **argv )
+{
+ KGlobal::locale()->setMainCatalogue( "dcoprss" );
+ KAboutData aboutData( "feedbrowser", I18N_NOOP( "Feed Browser" ), "0.1" );
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KApplication app;
+ FeedBrowserDlg *dlg = new FeedBrowserDlg( 0 );
+ app.setMainWidget( dlg );
+ dlg->show();
+ return app.exec();
+}
+
+#include "feedbrowser.moc"
diff --git a/dcoprss/feedbrowser.h b/dcoprss/feedbrowser.h
new file mode 100644
index 00000000..829ecd5e
--- /dev/null
+++ b/dcoprss/feedbrowser.h
@@ -0,0 +1,66 @@
+#ifndef FEEDBROWSER_H
+#define FEEDBROWSER_H
+
+#include <qobject.h>
+#include <dcopobject.h>
+#include <kdialogbase.h>
+#include <klistview.h>
+
+class DCOPRSSIface : public QObject, public DCOPObject
+{
+ K_DCOP
+ Q_OBJECT
+ public:
+ DCOPRSSIface( QObject *parent, const char *name = 0 );
+
+ k_dcop:
+ void slotGotCategories( const QStringList &categories );
+
+ public slots:
+ void getCategories( const QString &cat = "Top" );
+
+ signals:
+ void gotCategories( const QStringList &categories );
+};
+
+class CategoryItem : public QObject, public KListViewItem
+{
+ Q_OBJECT
+ public:
+ CategoryItem( KListView *parent, const QString &category );
+ CategoryItem( KListViewItem *parent, const QString &category );
+
+ virtual void setOpen( bool open );
+
+ private slots:
+ void gotCategories( const QStringList &categories );
+
+ private:
+ void populate();
+ void init();
+
+ QString m_category;
+ bool m_populated;
+ DCOPRSSIface *m_dcopIface;
+};
+
+class FeedBrowserDlg : public KDialogBase
+{
+ Q_OBJECT
+ friend class CategoryItem;
+ public:
+ FeedBrowserDlg( QWidget *parent, const char *name = 0 );
+
+ private slots:
+ void itemSelected( QListViewItem *item );
+ void gotTopCategories( const QStringList &categories );
+
+ private:
+ void getTopCategories();
+
+ DCOPRSSIface *m_dcopIface;
+ KListView *m_feedList;
+};
+
+#endif // FEEDBROWSER_H
+// vim:ts=4:sw=4:noet
diff --git a/dcoprss/main.cpp b/dcoprss/main.cpp
new file mode 100644
index 00000000..fcebf8c0
--- /dev/null
+++ b/dcoprss/main.cpp
@@ -0,0 +1,39 @@
+/* $Id$ */
+
+#include <kuniqueapplication.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <dcopclient.h>
+#include "service.h"
+#include "query.h"
+
+int main (int argc, char *argv[])
+{
+ KLocale::setMainCatalogue("dcoprss");
+ KAboutData aboutdata("rssservice", I18N_NOOP("KDE RSS Service"),
+ "0.8", I18N_NOOP("A RSS data service."),
+ KAboutData::License_GPL, "(C) 2003, Ian Reinhart Geiser");
+ aboutdata.addAuthor("Ian Reinhart Geiser",I18N_NOOP("Developer"),"geiseri@kde.org");
+
+ KCmdLineArgs::init( argc, argv, &aboutdata );
+ // KCmdLineArgs::addCmdLineOptions( options );
+ KUniqueApplication::addCmdLineOptions();
+
+ if (!KUniqueApplication::start())
+ {
+ kdDebug() << "rssservice is already running!" << endl;
+ return (0);
+ }
+
+ KUniqueApplication app;
+ kdDebug() << "starting rssservice " << endl;
+ // This app is started automatically, no need for session management
+ app.disableSessionManagement();
+ RSSService *service = new RSSService;
+ QueryService *query = new QueryService(service);
+
+ return app.exec();
+}
diff --git a/dcoprss/query.cpp b/dcoprss/query.cpp
new file mode 100644
index 00000000..b2c29fdf
--- /dev/null
+++ b/dcoprss/query.cpp
@@ -0,0 +1,271 @@
+/* $Id$ */
+
+/***************************************************************************
+ query.cpp - A query interface to select RSS feeds.
+ -------------------
+ begin : Saturday 15 February 2003
+ copyright : (C) 2003 by Ian Reinhart Geiser
+ email : geiseri@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 "cache.h"
+#include "query.h"
+
+#include <kdebug.h>
+#include <krfcdate.h>
+
+#include "service.h"
+#include "xmlrpciface.h"
+
+using KXMLRPC::Server;
+
+void SlotCaller::call( QObject *object, const char *slot,
+ const KXMLRPC::Query::Result &result )
+{
+ SlotCaller caller;
+ connect( &caller, SIGNAL( signal( const KXMLRPC::Query::Result &) ),
+ object, slot );
+ emit caller.signal( result );
+}
+
+QueryService::QueryService( RSSService *service ) : QObject(), DCOPObject( "RSSQuery" ),
+ m_service( service )
+{
+ m_xmlrpcServer = new KXMLRPC::Server( KURL( "http://www.syndic8.com/xmlrpc.php"), this );
+}
+
+QStringList QueryService::listActive()
+{
+ if ( !m_service )
+ return QStringList();
+ return m_service->list();
+}
+
+void QueryService::cachedCall( const QString &method,
+ const QValueList<QVariant> &args,
+ const char *slot )
+{
+ kdDebug() << "Calling " << method << endl;
+
+ const QString cacheKey = Cache::getCacheKey( m_xmlrpcServer->url().url(),
+ method, args );
+
+ CacheEntry *cacheEntry = Cache::self().find( cacheKey );
+ if ( cacheEntry != 0 && cacheEntry->isValid() ) {
+ kdDebug() << "Using cached result." << endl;
+ SlotCaller::call( this, slot, cacheEntry->result() );
+ } else {
+ kdDebug() << "No cached result found, querying server." << endl;
+ m_xmlrpcServer->call( method, args, this, slot );
+ }
+}
+
+void QueryService::updateCache( const KXMLRPC::Query::Result &result )
+{
+ const QString cacheKey = Cache::getCacheKey( result.server(),
+ result.method(),
+ result.args() );
+
+ CacheEntry *cacheEntry = Cache::self().find( cacheKey );
+ if ( cacheEntry == 0 ) {
+ kdDebug() << "Inserting returned result into cache." << endl;
+ Cache::self().insert( cacheKey, result );
+ }
+}
+
+void QueryService::findFeeds( const QString &query )
+{
+ kdDebug() << "QueryService::findFeeds()" << endl;
+
+ QStringList args;
+ args << query << "headlines_rank";
+
+ cachedCall( "syndic8.FindFeeds", Server::toVariantList( args ),
+ SLOT( slotFoundFeeds( const KXMLRPC::Query::Result & ) ) );
+}
+
+void QueryService::findSites( const QString& query )
+{
+ kdDebug() << "QueryService::findSites()" << endl;
+
+ cachedCall( "syndic8.FindSites", Server::toVariantList( query ),
+ SLOT( slotFoundFeeds( const KXMLRPC::Query::Result & ) ) );
+}
+
+void QueryService::getFeedInfo( const QVariant& ids )
+{
+ kdDebug() << "QueryService::getFeedInfo()" << endl;
+
+ cachedCall( "syndic8.GetFeedInfo", Server::toVariantList( ids ),
+ SLOT( slotGotFeedInfo( const KXMLRPC::Query::Result & ) ) );
+}
+
+void QueryService::getCategories( const QString &category )
+{
+ kdDebug() << "QueryService::getCategories()" << endl;
+
+ if ( category == "Top" ) {
+ cachedCall( "syndic8.GetCategoryRoots", Server::toVariantList( QString::fromLatin1( "DMOZ" ) ),
+ SLOT( slotGotCategories( const KXMLRPC::Query::Result & ) ) );
+ } else {
+ QStringList args;
+ args << "DMOZ" << category;
+ cachedCall( "syndic8.GetCategoryChildren", Server::toVariantList( args ),
+ SLOT( slotGotCategories( const KXMLRPC::Query::Result & ) ) );
+ }
+}
+
+void QueryService::getFeedsInCategory( const QString &category )
+{
+ kdDebug() << "QueryService::getFeedsInCategory()" << endl;
+
+ QStringList args;
+ args << "DMOZ" << category;
+
+ cachedCall( "syndic8.GetFeedsInCategory", Server::toVariantList( args ),
+ SLOT( slotGotFeedsInCategory( const KXMLRPC::Query::Result & ) ) );
+}
+
+void QueryService::slotFoundFeeds( const KXMLRPC::Query::Result &result )
+{
+ kdDebug() << "QueryService::slotFoundFeeds()" << endl;
+ if ( !result.success() ) {
+ kdWarning() << "Failed to query for feeds: " << result.errorString() << endl;
+ return;
+ }
+
+ updateCache( result );
+
+ QValueList<int> ids;
+
+ const QValueList<QVariant> values = result.data()[ 0 ].toList();
+ QValueList<QVariant>::ConstIterator it = values.begin();
+ QValueList<QVariant>::ConstIterator end = values.end();
+ for ( ; it != end; ++it ) {
+ ids << ( *it ).toInt();
+ kdDebug() << "Found feed #" << ( *it ).toInt() << endl;
+ }
+ feedIds( ids );
+}
+
+void QueryService::slotGotFeedInfo( const KXMLRPC::Query::Result &result )
+{
+ kdDebug() << "QueryService::slotGotFeedInfo()" << endl;
+ if ( !result.success() ) {
+ kdWarning() << "Failed to get feed info: " << result.errorString() << endl;
+ return;
+ }
+
+ updateCache( result );
+
+ QMap<QString, QString> links;
+ QValueList<RSSNewsFeed> feeds;
+
+ const QValueList<QVariant> feedInfos = result.data();
+ QValueList<QVariant>::ConstIterator it = feedInfos.begin();
+ QValueList<QVariant>::ConstIterator end = feedInfos.end();
+ for ( ; it != end; ++it ) {
+ const QMap<QString, QVariant> feedInfo = ( *it ).toMap();
+
+ const QString name = feedInfo[ "sitename" ].toString();
+ const QString link = feedInfo[ "dataurl" ].toString();
+ links[ name ] = link;
+
+ RSSNewsFeed feed;
+ feed.m_id = feedInfo[ "feedid" ].toUInt();
+ feed.m_name = feedInfo[ "sitename" ].toString();
+ feed.m_homePage = feedInfo[ "siteurl" ].toString();
+ feed.m_sourceFile = feedInfo[ "dataurl" ].toString();
+ feed.m_imageUrl = feedInfo[ "imageurl" ].toString();
+ feed.m_webmaster = feedInfo[ "webmaster" ].toString();
+ feed.m_editor = feedInfo[ "editor" ].toString();
+ feed.m_publisher = feedInfo[ "publisher" ].toString();
+ feed.m_creator = feedInfo[ "creator" ].toString();
+ QDateTime dateTime;
+ dateTime.setTime_t( KRFCDate::parseDate( feedInfo[ "date_created" ].toString() ) );
+ feed.m_dateCreated = dateTime;
+ dateTime.setTime_t( KRFCDate::parseDate( feedInfo[ "date_approved" ].toString() ) );
+ feed.m_dateApproved = dateTime;
+ dateTime.setTime_t( KRFCDate::parseDate( feedInfo[ "date_xml_changed" ].toString() ) );
+ feed.m_dateXmlChanged = dateTime;
+ feed.m_fetchable = feedInfo[ "fetchable" ].toBool();
+ feed.m_description = feedInfo[ "description" ].toString();
+ feed.m_origin = feedInfo[ "origin" ].toString();
+ feed.m_languageCode = feedInfo[ "lang_code" ].toString();
+ feed.m_status = feedInfo[ "status" ].toString();
+ feed.m_version = feedInfo[ "rss_version" ].toString();
+ feed.m_views = feedInfo[ "views" ].toUInt();
+ feed.m_headlinesPerDay = feedInfo[ "headlines_per_day" ].toUInt();
+ feed.m_headlinesRank = feedInfo[ "headlines_rank" ].toUInt();
+ feed.m_toolkit = feedInfo[ "toolkit" ].toString();
+ feed.m_toolkitVersion = feedInfo[ "toolkit_version" ].toString();
+ feed.m_pollingInterval = feedInfo[ "cur_polling_interval" ].toUInt();
+ dateTime.setTime_t( feedInfo[ "last_poll_time" ].toUInt() );
+ feed.m_lastPoll = dateTime;
+ // ### feed.m_categories missing here!
+
+ feeds << feed;
+
+ kdDebug() << "Retrieved data for newsfeed '" << name << "' <" << link << ">" << endl;
+ }
+
+ feedInfo( links );
+ feedInfo( feeds );
+}
+
+void QueryService::slotGotCategories( const KXMLRPC::Query::Result &result )
+{
+ kdDebug() << "QueryService::slotGotCategories()" << endl;
+ if ( !result.success() ) {
+ kdWarning() << "Failed to get the list of categories: " << result.errorString() << endl;
+ return;
+ }
+
+ updateCache( result );
+
+ QStringList categories;
+
+ const QValueList<QVariant> cats = result.data()[ 0 ].toList();
+ QValueList<QVariant>::ConstIterator it = cats.begin();
+ QValueList<QVariant>::ConstIterator end = cats.end();
+ for ( ; it != end; ++it )
+ categories << ( *it ).toString();
+
+ kdDebug() << "Got categories: " << categories.join( ", " ) << endl;
+ gotCategories( categories );
+
+}
+
+void QueryService::slotGotFeedsInCategory( const KXMLRPC::Query::Result &result )
+{
+ kdDebug() << "QueryService::slotGotFeedsInCategory()" << endl;
+ if ( !result.success() ) {
+ kdWarning() << "Failed to get the feeds in the given category: " << result.errorString() << endl;
+ return;
+ }
+
+ updateCache( result );
+
+ QValueList<int> ids;
+
+ const QValueList<QVariant> values = result.data()[ 0 ].toList();
+ QValueList<QVariant>::ConstIterator it = values.begin();
+ QValueList<QVariant>::ConstIterator end = values.end();
+ for ( ; it != end; ++it ) {
+ ids << ( *it ).toInt();
+ kdDebug() << "Got feed in category: #" << ( *it ).toInt() << endl;
+ }
+
+ gotFeedsInCategory( ids );
+}
+
+#include "query.moc"
+// vim:ts=4:sw=4:noet
diff --git a/dcoprss/query.h b/dcoprss/query.h
new file mode 100644
index 00000000..d87e0505
--- /dev/null
+++ b/dcoprss/query.h
@@ -0,0 +1,120 @@
+/* $Id$ */
+#ifndef _QUERY_SERVICE
+#define _QUERY_SERVICE
+
+/***************************************************************************
+ query.h - A query interface to select RSS feeds.
+ -------------------
+ begin : Saturday 15 February 2003
+ copyright : (C) 2003 by Ian Reinhart Geiser
+ email : geiseri@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 "rssnewsfeed.h"
+#include "xmlrpciface.h"
+
+#include <dcopobject.h>
+
+#include <qmap.h>
+#include <qobject.h>
+#include <qvaluelist.h>
+#include <qvariant.h>
+
+class RSSService;
+
+/**
+ * Helper class which just calls the slot given it's QObject/const char*
+ * representation.
+ */
+class SlotCaller : public QObject
+{
+ Q_OBJECT
+ public:
+ static void call( QObject *object, const char *slot,
+ const KXMLRPC::Query::Result &value );
+
+ signals:
+ void signal( const KXMLRPC::Query::Result &value );
+
+ private:
+ SlotCaller() { }
+};
+
+class QueryService : public QObject, public DCOPObject
+{
+ K_DCOP
+ Q_OBJECT
+ public:
+ QueryService( RSSService *service );
+
+ k_dcop_signals:
+ void feedIds( QValueList<int> ids );
+ void feedInfo(QMap<QString, QString> links);
+ void feedInfo(QValueList<RSSNewsFeed> feeds);
+ void gotCategories( const QStringList &categories );
+ void gotFeedsInCategory( const QValueList<int> &ids );
+
+ k_dcop:
+ /**
+ * Lists the active feeds in use...
+ **/
+ QStringList listActive(); // just for testing...
+
+ /**
+ * Query the www.syndic8.com XML-RPC interface for the
+ * string. The RSS ids are treturned in a integer list.
+ **/
+ void findFeeds( const QString& query );
+
+ /**
+ * Query the www.syndic8.com XML-RPC interface for the
+ * string and matches it against the SiteURL feed of
+ * each feed in the feed list. The RSS ids are treturned
+ * in a integer list.
+ **/
+ void findSites( const QString& query );
+
+ /**
+ * Query the www.syndic8.com XML-RPC interface for the
+ * requested RSS feed(s). Returned is a QMap with the format
+ * of Name (Site URL), RSS URL
+ **/
+ void getFeedInfo( const QVariant& ids );
+
+ /**
+ * Returns the list of subcategories in the specified category.
+ * If no "Top" is specified, the root categories are returned.
+ */
+ void getCategories( const QString &category );
+
+ /**
+ * Queries the database for the list of needsfeed ID's which are
+ * associated with the given category.
+ */
+ void getFeedsInCategory( const QString &category );
+
+
+ private slots:
+ void slotFoundFeeds( const KXMLRPC::Query::Result &result );
+ void slotGotFeedInfo( const KXMLRPC::Query::Result &result );
+ void slotGotCategories( const KXMLRPC::Query::Result &result );
+ void slotGotFeedsInCategory( const KXMLRPC::Query::Result &result );
+
+ private:
+ void cachedCall( const QString &method, const QValueList<QVariant> &args,
+ const char *slot );
+ void updateCache( const KXMLRPC::Query::Result &result );
+
+ RSSService *m_service;
+ KXMLRPC::Server *m_xmlrpcServer;
+};
+#endif
diff --git a/dcoprss/rssnewsfeed.h b/dcoprss/rssnewsfeed.h
new file mode 100644
index 00000000..3c2e04b5
--- /dev/null
+++ b/dcoprss/rssnewsfeed.h
@@ -0,0 +1,115 @@
+/*
+ * rssnewsfeed.h
+ *
+ * Copyright (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef RSSNEWSFEED_H
+#define RSSNEWSFEED_H
+
+#include <qdatetime.h>
+#include <qstringlist.h>
+#include <qvariant.h>
+#include <kdatastream.h>
+
+class QueryService;
+
+class RSSNewsFeed
+{
+ friend QDataStream &operator>>( QDataStream &stream, RSSNewsFeed &feed );
+ friend QDataStream &operator<<( QDataStream &stream, const RSSNewsFeed &feed );
+ friend class QueryService;
+ public:
+ unsigned int id() const { return m_id; }
+ QString name() const { return m_name; }
+ QString description() const { return m_description; }
+ QString origin() const { return m_origin; }
+ QString languageCode() const { return m_languageCode; }
+ QString status() const { return m_status; }
+ QString version() const { return m_version; }
+ QString homePage() const { return m_homePage; }
+ QString sourceFile() const { return m_sourceFile; }
+ QString imageUrl() const { return m_imageUrl; }
+ QString webmaster() const { return m_webmaster; }
+ QString editor() const { return m_editor; }
+ QString publisher() const { return m_publisher; }
+ QString creator() const { return m_creator; }
+ const QDateTime &dateCreated() const { return m_dateCreated; }
+ const QDateTime &dateApproved() const { return m_dateApproved; }
+ const QDateTime dateXmlChanged() const { return m_dateXmlChanged; }
+ bool fetchable() const { return m_fetchable; }
+ unsigned int views() const { return m_views; }
+ unsigned int headlinesPerDay() const { return m_headlinesPerDay; }
+ unsigned int headlinesRank() const { return m_headlinesRank; }
+ QString toolkit() const { return m_toolkit; }
+ QString toolkitVersion() const { return m_toolkitVersion; }
+ unsigned int pollingInterval() const { return m_pollingInterval; }
+ const QDateTime &lastPoll() const { return m_lastPoll; }
+ QStringList categories() const { return m_categories; }
+
+ private:
+ unsigned int m_id;
+ QString m_name;
+ QString m_description;
+ QString m_origin;
+ QString m_languageCode;
+ QString m_status;
+ QString m_version;
+ QString m_homePage;
+ QString m_sourceFile;
+ QString m_imageUrl;
+ QString m_webmaster;
+ QString m_editor;
+ QString m_publisher;
+ QString m_creator;
+ QDateTime m_dateCreated;
+ QDateTime m_dateApproved;
+ QDateTime m_dateXmlChanged;
+ bool m_fetchable;
+ unsigned int m_views;
+ unsigned int m_headlinesPerDay;
+ unsigned int m_headlinesRank;
+ QString m_toolkit;
+ QString m_toolkitVersion;
+ unsigned int m_pollingInterval;
+ QDateTime m_lastPoll;
+ QStringList m_categories;
+};
+
+inline QDataStream &operator<<( QDataStream &stream, const RSSNewsFeed &feed )
+{
+ return stream << feed.m_id << feed.m_name << feed.m_description
+ << feed.m_origin << feed.m_languageCode << feed.m_status
+ << feed.m_version << feed.m_homePage << feed.m_sourceFile
+ << feed.m_imageUrl << feed.m_webmaster << feed.m_publisher
+ << feed.m_creator << feed.m_dateCreated << feed.m_dateApproved
+ << feed.m_dateXmlChanged << feed.m_fetchable << feed.m_views
+ << feed.m_headlinesPerDay << feed.m_headlinesRank
+ << feed.m_toolkit << feed.m_toolkitVersion
+ << feed.m_pollingInterval << feed.m_lastPoll
+ << feed.m_categories;
+}
+
+inline QDataStream &operator>>( QDataStream &stream, RSSNewsFeed &feed )
+{
+ int i;
+ stream >> feed.m_id >> feed.m_name >> feed.m_description
+ >> feed.m_origin >> feed.m_languageCode >> feed.m_status
+ >> feed.m_version >> feed.m_homePage >> feed.m_sourceFile
+ >> feed.m_imageUrl >> feed.m_webmaster >> feed.m_publisher
+ >> feed.m_creator >> feed.m_dateCreated >> feed.m_dateApproved
+ >> feed.m_dateXmlChanged >> i >> feed.m_views
+ >> feed.m_headlinesPerDay >> feed.m_headlinesRank
+ >> feed.m_toolkit >> feed.m_toolkitVersion
+ >> feed.m_pollingInterval >> feed.m_lastPoll
+ >> feed.m_categories;
+ feed.m_fetchable = i != 0;
+ return stream;
+}
+
+#endif
+// vim:ts=4:sw=4:noet
diff --git a/dcoprss/rssservice.desktop b/dcoprss/rssservice.desktop
new file mode 100644
index 00000000..1692b267
--- /dev/null
+++ b/dcoprss/rssservice.desktop
@@ -0,0 +1,88 @@
+[Desktop Entry]
+Type=Service
+Name=rssservice
+Name[bn]=আর-এস-এস সার্ভিস
+Name[ca]=Servei RSS
+Name[cs]=RSS služba
+Name[de]=RSS-Dienst
+Name[el]=υπηρεσία rss
+Name[he]=שרות RSS
+Name[hi]=आरएसएस-सर्विस
+Name[hu]=RSS szolgáltatás
+Name[it]=Servizio RSS
+Name[ja]=rssサービス
+Name[lt]=rss tarnyba
+Name[nb]=rss-tjeneste
+Name[nds]=RSS-Deenst
+Name[nl]=RSS-dienst
+Name[nn]=rss-teneste
+Name[pl]=Usługa RSS
+Name[pt_BR]=serviço rss
+Name[ro]=Serviciu RSS
+Name[sv]=RSS-tjänst
+Name[ta]=rssசேவை
+Name[th]=บริการ rss
+Name[tr]=rssservisi
+Exec=rssservice
+X-DCOP-ServiceType=Unique
+X-KDE-StartupNotify=false
+Comment=RSS DCOP services
+Comment[ar]= خدمات RSS DCOP
+Comment[be]=Сервісы RSS для DCOP
+Comment[bn]=আর-এস-এস ডিকপ সার্ভিস
+Comment[br]=Servijoù DCOP RSS
+Comment[bs]=RSS DCOP servisi
+Comment[ca]=Serveis RSS de DCOP
+Comment[cs]=RSS DCOP služby
+Comment[cy]=Gwasanaethau DCOP RSS
+Comment[da]=RSS DCOP-tjenester
+Comment[de]=RSS DCOP-Dienste
+Comment[el]=Υπηρεσίες RSS DCOP
+Comment[eo]=RSS-DCOP-servoj
+Comment[es]=Servicios RSS de DCOP
+Comment[et]=RSS DCOP-teenused
+Comment[eu]=RSS DCOP zerbitzuak
+Comment[fa]=خدمات RSS DCOP
+Comment[fi]=RSS-DCOP-palvelut
+Comment[fr]=Services DCOP RSS
+Comment[ga]=Seirbhísí DCOP RSS
+Comment[gl]=Servicios RSS de DCOP
+Comment[he]=שרותי RSS DCOP
+Comment[hi]=आरएसएस डीकॉप सेवाएँ
+Comment[hr]=RSS DCOP servisi
+Comment[hu]=RSS DCOP-szolgáltatás
+Comment[is]=RSS DCOP þjónustur
+Comment[it]=Servizi DCOP RSS
+Comment[ja]=RSS DCOP サービス
+Comment[ka]=RSS DCOP სერვისები
+Comment[kk]=RSS DCOP қызметтері
+Comment[km]=សេវា RSS DCOP
+Comment[lt]=RSS DCOP tarnyba
+Comment[mk]=Сервиси за RSS DCOP
+Comment[nb]=RSS DCOP -tjenester
+Comment[nds]=RSS-DCOP-Deenst
+Comment[ne]=RSS DCOP सेवा
+Comment[nl]=RSS DCOP-diensten
+Comment[nn]=RSS DCOP-tenester
+Comment[pa]=RSS DCOP ਸੇਵਾਵਾਂ
+Comment[pl]=Usługa DCOP dla RSS
+Comment[pt]=Serviços DCOP de RSS
+Comment[pt_BR]=Serviços DCOP RSS
+Comment[ro]=Serviciu DCOP RSS
+Comment[ru]=Службы RSS DCOP
+Comment[se]=RSS DCOP-bálvalusat
+Comment[sk]=RSS DCOP služby
+Comment[sl]=Storitve RSS DCOP
+Comment[sr]=RSS DCOP сервиси
+Comment[sr@Latn]=RSS DCOP servisi
+Comment[sv]=RSS DCOP-tjänster
+Comment[ta]=RSS DCOP சேவைகள்
+Comment[tg]=Хадамотҳои RSS DCOP
+Comment[th]=บริการ RSS DCOP
+Comment[tr]=RSS DCOP hizmetleri
+Comment[uk]=Служби RSS DCOP
+Comment[uz]=RSS DCOP xizmatlari
+Comment[uz@cyrillic]=RSS DCOP хизматлари
+Comment[zh_CN]=RSS DCOP 服务
+Comment[zh_HK]=RSS DCOP 服務
+Comment[zh_TW]=RSS DCOP 服務
diff --git a/dcoprss/service.cpp b/dcoprss/service.cpp
new file mode 100644
index 00000000..74544dc1
--- /dev/null
+++ b/dcoprss/service.cpp
@@ -0,0 +1,105 @@
+/* $Id$ */
+/***************************************************************************
+ service.cpp - A DCOP Service to provide RSS data
+ -------------------
+ begin : Saturday 15 February 2003
+ copyright : (C) 2003 by Ian Reinhart Geiser
+ email : geiseri@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 <kdebug.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include "service.h"
+#include "cache.h"
+
+RSSService::RSSService() :
+ DCOPObject("RSSService")
+{
+ m_list.setAutoDelete( true );
+
+ loadLinks();
+}
+
+RSSService::~RSSService()
+{
+}
+
+
+QStringList RSSService::list()
+{
+ QStringList lst;
+ QDictIterator<RSSDocument> itr(m_list);
+ for(; itr.current(); ++itr)
+ lst.append(itr.currentKey());
+ return lst;
+}
+
+DCOPRef RSSService::add(QString id)
+{
+ if(m_list.find(id) == 0L) { // add a new one only if we need to
+ m_list.insert(id, new RSSDocument(id));
+ added(id);
+ saveLinks();
+ }
+ return document(id);
+}
+
+void RSSService::remove(QString id)
+{
+ m_list.remove(id);
+ removed(id);
+ saveLinks();
+}
+
+DCOPRef RSSService::document(QString id)
+{
+ if( m_list[id] )
+ return DCOPRef(m_list[id]);
+ else
+ return DCOPRef();
+}
+
+void RSSService::exit()
+{
+ //Save all current RSS links.
+ saveLinks();
+ Cache::self().save();
+ kapp->quit();
+}
+
+
+void RSSService::loadLinks()
+{
+ KConfig *conf = kapp->config();
+ conf->setGroup("RSS Links");
+ const QStringList links = conf->readListEntry ("links");
+ QStringList::ConstIterator it = links.begin();
+ QStringList::ConstIterator end = links.end();
+ for ( ; it != end; ++it )
+ add( *it );
+}
+
+void RSSService::saveLinks()
+{
+ KConfig *conf = kapp->config();
+ conf->setGroup("RSS Links");
+ QStringList lst;
+ QDictIterator<RSSDocument> itr(m_list);
+ for(; itr.current(); ++itr)
+ lst.append(itr.currentKey());
+
+ conf->writeEntry("links", lst);
+ conf->sync();
+}
+
+
+
diff --git a/dcoprss/service.h b/dcoprss/service.h
new file mode 100644
index 00000000..35cf229e
--- /dev/null
+++ b/dcoprss/service.h
@@ -0,0 +1,290 @@
+/* $Id$ */
+#ifndef _RSS_SERVICE
+#define _RSS_SERVICE
+
+/***************************************************************************
+ service.h - A DCOP Service to provide RSS data
+ -------------------
+ begin : Saturday 15 February 2003
+ copyright : (C) 2003 by Ian Reinhart Geiser
+ email : geiseri@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 <dcopobject.h>
+#include <dcopref.h>
+#include <qdict.h>
+#include <qptrlist.h>
+#include <qstringlist.h>
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qpixmap.h>
+#include <librss/global.h>
+#include <librss/loader.h>
+#include <librss/document.h>
+#include <librss/article.h>
+#include <librss/image.h>
+/**
+* This is a DCOP Service do not include this header in anything
+*
+**/
+using namespace RSS;
+
+class RSSDocument;
+class RSSArticle;
+
+class RSSService : public DCOPObject
+{
+ K_DCOP
+
+ private:
+
+ QDict<RSSDocument> m_list;
+
+ public:
+ RSSService();
+ ~RSSService();
+ void saveLinks();
+ void loadLinks();
+
+
+ k_dcop_signals:
+ /**
+ * Emmitted when a new document has been added. You can then
+ * use document(QString) to get the dcop ref for the object.
+ * Note: this document may or may not be valid at this
+ * point so you should connect your dcop signals and then
+ * do a documentValid() on the dcop ref to make sure of its
+ * state.
+ **/
+
+ void added(QString);
+ /**
+ * Emmitted when the document has been removed.
+ * note at this point the DCOPRef for this object is
+ * invalid and you will cannot access it any longer.
+ * When in doubt call a refresh on it, since if its in the
+ * process of loading the document call will be safely ignored
+ * and you will be notified of the updates.
+ **/
+ void removed(QString);
+ k_dcop:
+ /**
+ * Add a new rdf file resource. This will return a dcop reference to the resource. If its a new
+ * one it will be added otherwise an existing resource reference will be returned.
+ * once this reference has been returned you may connect dcop signals and then call
+ * refresh on the RSSDocument. The document will not be updated until refresh is called.
+ **/
+ DCOPRef add(QString url);
+ /**
+ * Return a list of current rss documents
+ **/
+ QStringList list();
+ /**
+ * Remove an rss document resource. NOTE: Be aware that others may be using this
+ * resource and if you remove it they may break. Likewise be aware that someone may
+ * decide to remove your resource on you so you should always check to see if the resource
+ * is valid before you access it.
+ **/
+ void remove(QString url);
+ /**
+ * Return the reference to a requested resource. If this resource is not present a null dcopref is
+ * returned.
+ **/
+ DCOPRef document(QString url);
+ /**
+ * Exit the RSSService. This will clean everything up and exit.
+ **/
+ void exit();
+};
+
+class RSSDocument : public QObject, public DCOPObject
+{
+ Q_OBJECT
+ K_DCOP
+
+ private:
+ bool m_isLoading;
+ QString m_Url;
+ Document *m_Doc;
+ QPixmap m_pix;
+ QPtrList<RSSArticle> m_list;
+ QMap<QString,int> m_state;
+ QDateTime m_Timeout;
+ int m_maxAge;
+
+ private slots:
+ void pixmapLoaded(const QPixmap&);
+ void loadingComplete(Loader *, Document, Status);
+
+ public:
+ RSSDocument(const QString& url);
+ ~RSSDocument();
+
+ k_dcop_signals:
+ /**
+ * The pixmap is currently loading
+ **/
+ void pixmapUpdating(DCOPRef);
+ /**
+ * The pixmap is ready for viewing
+ * you can then use dcopref->call("pixmap()"); to return it.
+ *
+ **/
+ void pixmapUpdated(DCOPRef);
+ /**
+ * The document is currently updating
+ **/
+ void documentUpdating(DCOPRef);
+ /**
+ * The document is ready for viewing
+ * you can then use dcopref->call() to access its data
+ **/
+ void documentUpdated(DCOPRef);
+ /**
+ * The document failed to update, with and error...
+ * 1 - RSS Parse Error
+ * 2 - Could not access file
+ * 3 - Unknown error.
+ **/
+ void documentUpdateError(DCOPRef, int);
+
+ k_dcop:
+ /**
+ * Return the webmaster information from the RSS::Document
+ **/
+ QString webMaster();
+ /**
+ * Return the manageing editor from the RSS::Document
+ **/
+ QString managingEditor();
+ /**
+ * Returns the rating of the RSS::Document
+ **/
+ QString rating();
+ /**
+ * Returns the last build date from the RSS::Document
+ **/
+ QDateTime lastBuildDate();
+ /**
+ * Returns the publication date from the RSS::Document
+ **/
+ QDateTime pubDate();
+ /**
+ * Returns the copyright information from the RSS::Document
+ **/
+ QString copyright();
+ /**
+ * Returns a list of article titles
+ **/
+ QStringList articles();
+ /**
+ * Returns the number of articles
+ **/
+ int count();
+ /**
+ * Returns a dcop reference to the article from the index
+ **/
+ DCOPRef article(int idx);
+ /**
+ * Returns the link from the RSS::Document
+ **/
+ QString link();
+ /**
+ * Returns the description from the RSS::Document
+ **/
+ QString description();
+ /**
+ * Returns the title from the RSS::Document
+ **/
+ QString title();
+ /**
+ * Returns the text version from the RSS::Document
+ **/
+ QString verbVersion();
+ /**
+ * Returns the url for the pixmap from the RSS::Document
+ **/
+ QString pixmapURL();
+ /**
+ * Returns the actual pixmap from the RSS::Document's RSS::Image
+ **/
+ QPixmap pixmap();
+ /**
+ * Returns if the RSSDocument contains a valid RSS::Document yet.
+ **/
+ bool documentValid();
+ /**
+ * Returns if the RSSDocument contains a valid RSS::Image
+ **/
+ bool pixmapValid();
+ /**
+ * Refresh the current RSS::Document.
+ * This must be called before the document is valid.
+ **/
+ void refresh();
+
+ /**
+ * Return the maximum age of the RSS document (Default is 60 minutes)
+ **/
+ int maxAge();
+
+ /**
+ * Set the maximum age of the RSS document.
+ **/
+ void setMaxAge(int minutes);
+
+ /**
+ * Returns the state of the article
+ * 0 - not present (deleted from the rss service)
+ * 1 - new
+ * 2 - unread
+ * 3 - read
+ */
+ int state( const QString &title) const;
+
+ /**
+ * Set the article state
+ */
+ void setState( const QString &title, int s );
+
+ /**
+ * Convience method that will set a title to read.
+ */
+ void read( const QString &title);
+};
+
+class RSSArticle : public DCOPObject
+{
+ K_DCOP
+
+ private:
+ Article *m_Art;
+
+ public:
+ RSSArticle(Article *art);
+ ~RSSArticle();
+
+ k_dcop:
+ /**
+ * Return the articles title
+ **/
+ QString title();
+ /**
+ * Return the articles description
+ **/
+ QString description();
+ /**
+ * Return the link to the article
+ **/
+ QString link();
+};
+#endif
diff --git a/dcoprss/test.sh b/dcoprss/test.sh
new file mode 100644
index 00000000..d5d3e773
--- /dev/null
+++ b/dcoprss/test.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+ID=`dcopstart rssservice`
+#dcop $ID RSSService add "http://www.kde.org/dotkdeorg.rdf"
+#dcop $ID RSSService add "http://freshmeat.net/backend/fm.rdf"
+
+#dcop $REF1 refresh
+#dcop $REF2 refresh
+echo "Articles:"
+DOCS=`dcop rssservice RSSService list`
+for DOC in $DOCS
+do
+ DOCREF=`dcop rssservice RSSService document "$DOC"`
+ TITLE=`dcop $DOCREF title`
+ CNT=`dcop $DOCREF count`
+ echo $TITLE - $CNT
+ echo "------------------------------------"
+ while let "CNT >0"
+ do
+ let "CNT=CNT-1"
+ ART=`dcop $DOCREF article $CNT`
+ TEXT=`dcop $ART title`
+ echo "$CNT $TEXT"
+ done
+done
diff --git a/dcoprss/xmlrpciface.cpp b/dcoprss/xmlrpciface.cpp
new file mode 100644
index 00000000..e86639eb
--- /dev/null
+++ b/dcoprss/xmlrpciface.cpp
@@ -0,0 +1,401 @@
+/*
+ * kxmlrpcclient.cpp - (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "xmlrpciface.h"
+
+#include <kdebug.h>
+#include <kio/job.h>
+#include <klocale.h>
+#include <kmdcodec.h>
+
+#include <qdom.h>
+
+using namespace KXMLRPC;
+
+Query *Query::create( QObject *parent, const char *name )
+{
+ return new Query( parent, name );
+}
+
+void Query::call( const QString &server, const QString &method,
+ const QValueList<QVariant> &args, const QString &userAgent )
+{
+ m_buffer.open( IO_ReadWrite );
+ m_server = server;
+ m_method = method;
+ m_args = args;
+
+ const QString xmlMarkup = markupCall( method, args );
+
+ QByteArray postData;
+ QDataStream stream( postData, IO_WriteOnly );
+ stream.writeRawBytes( xmlMarkup.utf8(), xmlMarkup.length() );
+
+ KIO::TransferJob *job = KIO::http_post( KURL( server ), postData, false );
+ job->addMetaData( "UserAgent", userAgent );
+ job->addMetaData( "content-type", "Content-Type: text/xml; charset=utf-8" );
+ connect( job, SIGNAL( infoMessage( KIO::Job *, const QString & ) ),
+ this, SLOT( slotInfoMessage( KIO::Job *, const QString & ) ) );
+ connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
+ this, SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ this, SLOT( slotResult( KIO::Job * ) ) );
+}
+
+void Query::slotInfoMessage( KIO::Job *, const QString &msg )
+{
+ emit infoMessage( msg );
+}
+
+void Query::slotData( KIO::Job *, const QByteArray &data )
+{
+ m_buffer.writeBlock( data );
+}
+
+void Query::slotResult( KIO::Job *job )
+{
+ Result response;
+ response.m_server = m_server;
+ response.m_method = m_method;
+ response.m_args = m_args;
+
+ response.m_success = false;
+
+ if ( job->error() != 0 ) {
+ response.m_errorCode = job->error();
+ response.m_errorString = job->errorString();
+ emit finished( response );
+ delete this;
+ return;
+ }
+
+ QDomDocument doc;
+ if ( !doc.setContent( m_buffer.buffer() ) ) {
+ response.m_errorCode = -1;
+ response.m_errorString = i18n( "Received invalid XML markup" );
+ emit finished( response );
+ delete this;
+ return;
+ }
+
+ m_buffer.close();
+
+ if ( isMessageResponse( doc ) )
+ response = parseMessageResponse( doc );
+ else if ( isFaultResponse( doc ) )
+ response = parseFaultResponse( doc );
+ else {
+ response.m_errorCode = 1;
+ response.m_errorString = i18n( "Unknown type of XML markup received" );
+ }
+
+ // parserMessageResponse and parseFaultResponse overwrite these fields.
+ response.m_server = m_server;
+ response.m_method = m_method;
+ response.m_args = m_args;
+
+ emit finished( response );
+ delete this;
+}
+
+bool Query::isMessageResponse( const QDomDocument &doc ) const
+{
+ return doc.documentElement().firstChild().toElement().tagName().lower() == "params";
+}
+
+Query::Result Query::parseMessageResponse( const QDomDocument &doc ) const
+{
+ Result response;
+ response.m_success = true;
+
+ QDomNode paramNode = doc.documentElement().firstChild().firstChild();
+ while ( !paramNode.isNull() ) {
+ response.m_data << demarshal( paramNode.firstChild().toElement() );
+ paramNode = paramNode.nextSibling();
+ }
+
+ return response;
+}
+
+bool Query::isFaultResponse( const QDomDocument &doc ) const
+{
+ return doc.documentElement().firstChild().toElement().tagName().lower() == "fault";
+}
+
+Query::Result Query::parseFaultResponse( const QDomDocument &doc ) const
+{
+ Result response;
+ response.m_success = false;
+
+ QDomNode errorNode = doc.documentElement().firstChild().firstChild();
+ const QVariant errorVariant = demarshal( errorNode.toElement() );
+ response.m_errorCode = errorVariant.toMap()[ "faultCode" ].toInt();
+ response.m_errorString = errorVariant.toMap()[ "faultString" ].toString();
+
+ return response;
+}
+
+QString Query::markupCall( const QString &cmd,
+ const QValueList<QVariant> &args ) const
+{
+ QString markup = "<?xml version='1.0' ?><methodCall>";
+
+ markup += "<methodName>" + cmd + "</methodName>";
+
+ if ( !args.isEmpty() ) {
+ markup += "<params>";
+ QValueList<QVariant>::ConstIterator it = args.begin();
+ QValueList<QVariant>::ConstIterator end = args.end();
+ for ( ; it != end; ++it )
+ markup += "<param>" + marshal( *it ) + "</param>";
+ markup += "</params>";
+ }
+
+ markup += "</methodCall>";
+
+ return markup;
+}
+
+QString Query::marshal( const QVariant &arg )
+{
+ QString s = "<value>";
+ switch ( arg.type() ) {
+ case QVariant::String:
+ case QVariant::CString:
+ s += "<string>" + arg.toString() + "</string>";
+ break;
+ case QVariant::Int:
+ s += "<int>" + QString::number( arg.toInt() ) + "</int>";
+ break;
+ case QVariant::Double:
+ s += "<double>" + QString::number( arg.toDouble() ) + "</double>";
+ break;
+ case QVariant::Bool:
+ s += "<boolean>";
+ s += arg.toBool() ? "true" : "false";
+ s += "</boolean>";
+ break;
+ case QVariant::ByteArray:
+ s += "<base64>" + KCodecs::base64Encode( arg.toByteArray() ) + "</base64>";
+ break;
+ case QVariant::DateTime:
+ s += "<datetime.iso8601>" + arg.toDateTime().toString( Qt::ISODate ) + "</datetime.iso8601>";
+ break;
+ case QVariant::List: {
+ s += "<array><data>";
+ const QValueList<QVariant> args = arg.toList();
+ QValueList<QVariant>::ConstIterator it = args.begin();
+ QValueList<QVariant>::ConstIterator end = args.end();
+ for ( ; it != end; ++it )
+ s += marshal( *it );
+ s += "</data></array>";
+ break;
+ }
+ case QVariant::Map: {
+ s += "<struct>";
+ QMap<QString, QVariant> map = arg.toMap();
+ QMap<QString, QVariant>::ConstIterator it = map.begin();
+ QMap<QString, QVariant>::ConstIterator end = map.end();
+ for ( ; it != end; ++it ) {
+ s += "<member>";
+ s += "<name>" + it.key() + "</name>";
+ s += marshal( it.data() );
+ s += "</member>";
+ }
+ s += "</struct>";
+ break;
+ }
+ default:
+ kdWarning() << "Failed to marshal unknown variant type: " << arg.type() << endl;
+ return "<value/>";
+ };
+ return s + "</value>";
+}
+
+QVariant Query::demarshal( const QDomElement &elem )
+{
+ Q_ASSERT( elem.tagName().lower() == "value" );
+
+ if ( !elem.firstChild().isElement() )
+ return QVariant( elem.text() );
+
+ const QDomElement typeElement = elem.firstChild().toElement();
+ const QString typeName = typeElement.tagName().lower();
+
+ if ( typeName == "string" )
+ return QVariant( typeElement.text() );
+ else if ( typeName == "i4" || typeName == "int" )
+ return QVariant( typeElement.text().toInt() );
+ else if ( typeName == "double" )
+ return QVariant( typeElement.text().toDouble() );
+ else if ( typeName == "boolean" ) {
+ if ( typeElement.text().lower() == "true" || typeElement.text() == "1" )
+ return QVariant( true );
+ else
+ return QVariant( false );
+ } else if ( typeName == "base64" )
+ return QVariant( KCodecs::base64Decode( typeElement.text().latin1() ) );
+ else if ( typeName == "datetime" || typeName == "datetime.iso8601" )
+ return QVariant( QDateTime::fromString( typeElement.text(), Qt::ISODate ) );
+ else if ( typeName == "array" ) {
+ QValueList<QVariant> values;
+ QDomNode valueNode = typeElement.firstChild().firstChild();
+ while ( !valueNode.isNull() ) {
+ values << demarshal( valueNode.toElement() );
+ valueNode = valueNode.nextSibling();
+ }
+ return QVariant( values );
+ } else if ( typeName == "struct" ) {
+ QMap<QString, QVariant> map;
+ QDomNode memberNode = typeElement.firstChild();
+ while ( !memberNode.isNull() ) {
+ const QString key = memberNode.toElement().elementsByTagName( "name" ).item( 0 ).toElement().text();
+ const QVariant data = demarshal( memberNode.toElement().elementsByTagName( "value" ).item( 0 ).toElement() );
+ map[ key ] = data;
+ memberNode = memberNode.nextSibling();
+ }
+ return QVariant( map );
+ } else
+ kdWarning() << "Cannot demarshal unknown type " << typeName << endl;
+
+ return QVariant();
+}
+
+Query::Query( QObject *parent, const char *name ) : QObject( parent, name )
+{
+}
+
+QValueList<QVariant> Server::toVariantList( const QVariant &arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( int arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( bool arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( double arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( const QString &arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( const QCString &arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( const QByteArray &arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( const QDateTime &arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( const QStringList &arg )
+{
+ QValueList<QVariant> args;
+ QStringList::ConstIterator it = arg.begin();
+ QStringList::ConstIterator end = arg.end();
+ for ( ; it != end; ++it )
+ args << QVariant( *it );
+ return args;
+}
+
+Server::Server( const KURL &url, QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ if ( url.isValid() )
+ m_url = url;
+}
+
+void Server::setUrl( const KURL &url )
+{
+ m_url = url.isValid() ? url : KURL();
+}
+
+void Server::call( const QString &method, const QValueList<QVariant> &args,
+ QObject *receiver, const char *slot )
+{
+ if ( m_url.isEmpty() ) {
+ kdWarning() << "Cannot execute call to " << method << ": empty server URL" << endl;
+ return;
+ }
+
+ Query *query = Query::create( this );
+ connect( query, SIGNAL( infoMessage( const QString & ) ),
+ this, SIGNAL( infoMessage( const QString & ) ) );
+ connect( query, SIGNAL( finished( const KXMLRPC::Query::Result & ) ),
+ receiver, slot );
+ query->call( m_url.url(), method, args, m_userAgent );
+}
+
+void Server::call( const QString &method, const QValueList<QVariant> &args,
+ QObject *receiver, const char *slot,
+ QObject *infoObject, const char *infoSlot )
+{
+ if ( m_url.isEmpty() ) {
+ kdWarning() << "Cannot execute call to " << method << ": empty server URL" << endl;
+ return;
+ }
+
+ Query *query = Query::create( this );
+ connect( query, SIGNAL( infoMessage( const QString &msg ) ),
+ infoObject, infoSlot );
+ connect( query, SIGNAL( finished( const KXMLRPC::Query::Result & ) ),
+ receiver, slot );
+ query->call( m_url.url(), method, args, m_userAgent );
+}
+
+#include "xmlrpciface.moc"
+// vim:ts=4:sw=4:noet
diff --git a/dcoprss/xmlrpciface.h b/dcoprss/xmlrpciface.h
new file mode 100644
index 00000000..f7897e10
--- /dev/null
+++ b/dcoprss/xmlrpciface.h
@@ -0,0 +1,185 @@
+/*
+ * kxmlrpcclient.h - (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef KXMLRPCCLIENT_H
+#define KXMLRPCCLIENT_H
+
+#include <kurl.h>
+
+#include <qbuffer.h>
+#include <qdatastream.h>
+#include <qobject.h>
+#include <qvariant.h>
+#include <qvaluelist.h>
+
+class QDomDocument;
+class QDomElement;
+
+namespace KIO
+{
+ class Job;
+}
+
+namespace KXMLRPC
+{
+ class Query;
+ class QueryResult;
+ class Server;
+
+ class Query : public QObject
+ {
+ Q_OBJECT
+ public:
+ class Result
+ {
+ friend class Query;
+ friend QDataStream &operator>>( QDataStream &s, Query::Result &r );
+ public:
+ Result() { }
+
+ bool success() const { return m_success; }
+ int errorCode() const { return m_errorCode; }
+ QString errorString() const { return m_errorString; }
+ QValueList<QVariant> data() const { return m_data; }
+ QString server() const { return m_server; }
+ QString method() const { return m_method; }
+ QValueList<QVariant> args() const { return m_args; }
+
+ private:
+ bool m_success;
+ int m_errorCode;
+ QString m_errorString;
+ QValueList<QVariant> m_data;
+ QString m_server;
+ QString m_method;
+ QValueList<QVariant> m_args;
+ };
+
+ static Query *create( QObject *parent = 0, const char *name = 0 );
+ static QString marshal( const QVariant &v );
+ static QVariant demarshal( const QDomElement &e );
+
+ public slots:
+ void call( const QString &server, const QString &method,
+ const QValueList<QVariant> &args = QValueList<QVariant>(),
+ const QString &userAgent = "KDE-XMLRPC" );
+
+ signals:
+ void infoMessage( const QString &msg );
+ void finished( const KXMLRPC::Query::Result &result );
+
+ private slots:
+ void slotInfoMessage( KIO::Job *job, const QString &msg );
+ void slotData( KIO::Job *job, const QByteArray &data );
+ void slotResult( KIO::Job *job );
+
+ private:
+ bool isMessageResponse( const QDomDocument &doc ) const;
+ bool isFaultResponse( const QDomDocument &doc ) const;
+
+ Result parseMessageResponse( const QDomDocument &doc ) const;
+ Result parseFaultResponse( const QDomDocument &doc ) const;
+
+ QString markupCall( const QString &method,
+ const QValueList<QVariant> &args ) const;
+
+ Query( QObject *parent = 0, const char *name = 0 );
+
+ QBuffer m_buffer;
+ QString m_server;
+ QString m_method;
+ QValueList<QVariant> m_args;
+ };
+
+ class Server : public QObject
+ {
+ Q_OBJECT
+ public:
+ Server( const KURL &url = KURL(),
+ QObject *parent = 0, const char *name = 0 );
+
+ const KURL &url() const { return m_url; }
+ void setUrl( const KURL &url );
+
+ QString userAgent() const { return m_userAgent; }
+ void setUserAgent( const QString &userAgent) { m_userAgent = userAgent; }
+
+ template <typename T>
+ void call( const QString &method, const QValueList<T> &arg,
+ QObject *object, const char *slot );
+
+ static QValueList<QVariant> toVariantList( const QVariant &arg );
+ static QValueList<QVariant> toVariantList( int arg );
+ static QValueList<QVariant> toVariantList( bool arg );
+ static QValueList<QVariant> toVariantList( double arg );
+ static QValueList<QVariant> toVariantList( const QString &arg );
+ static QValueList<QVariant> toVariantList( const QCString &arg );
+ static QValueList<QVariant> toVariantList( const QByteArray &arg );
+ static QValueList<QVariant> toVariantList( const QDateTime &arg );
+ static QValueList<QVariant> toVariantList( const QStringList &arg );
+
+ signals:
+ void infoMessage( const QString &msg );
+
+ public slots:
+ void call( const QString &method, const QValueList<QVariant> &args,
+ QObject *object, const char *slot );
+ void call( const QString &method, const QValueList<QVariant> &args,
+ QObject *object, const char *slot,
+ QObject *infoObject, const char *infoSlot );
+
+ private:
+ KURL m_url;
+ QString m_userAgent;
+ };
+
+ inline QDataStream &operator>>( QDataStream &s, Query::Result &r )
+ {
+ return s >> r.m_errorCode >> r.m_errorString >> r.m_data
+ >> r.m_server >> r.m_method >> r.m_args;
+ }
+}
+
+template <typename T>
+void KXMLRPC::Server::call( const QString &method, const QValueList<T> &arg,
+ QObject *object, const char *slot )
+{
+ QValueList<QVariant> args;
+
+ typename QValueList<T>::ConstIterator it = arg.begin();
+ typename QValueList<T>::ConstIterator end = arg.end();
+ for ( ; it != end; ++it )
+ args << QVariant( *it );
+
+ call( method, args, object, slot );
+}
+
+inline QDataStream &operator<<( QDataStream &s, const KXMLRPC::Query::Result &r )
+{
+ return s << r.errorCode() << r.errorString() << r.data()
+ << r.server() << r.method() << r.args();
+}
+
+#endif // KXMLRPCCLIENT_H
+// vim:ts=4:sw=4:noet