summaryrefslogtreecommitdiffstats
path: root/kbugbuster/backend
diff options
context:
space:
mode:
Diffstat (limited to 'kbugbuster/backend')
-rw-r--r--kbugbuster/backend/Makefile.am16
-rw-r--r--kbugbuster/backend/bug.cpp240
-rw-r--r--kbugbuster/backend/bug.h92
-rw-r--r--kbugbuster/backend/bugcache.cpp289
-rw-r--r--kbugbuster/backend/bugcache.h47
-rw-r--r--kbugbuster/backend/bugcommand.cpp317
-rw-r--r--kbugbuster/backend/bugcommand.h217
-rw-r--r--kbugbuster/backend/bugdetails.cpp268
-rw-r--r--kbugbuster/backend/bugdetails.h55
-rw-r--r--kbugbuster/backend/bugdetailsimpl.h40
-rw-r--r--kbugbuster/backend/bugdetailsjob.cpp50
-rw-r--r--kbugbuster/backend/bugdetailsjob.h32
-rw-r--r--kbugbuster/backend/bugdetailspart.h21
-rw-r--r--kbugbuster/backend/bugimpl.h36
-rw-r--r--kbugbuster/backend/bugjob.cpp97
-rw-r--r--kbugbuster/backend/bugjob.h45
-rw-r--r--kbugbuster/backend/buglistjob.cpp75
-rw-r--r--kbugbuster/backend/buglistjob.h58
-rw-r--r--kbugbuster/backend/bugmybugsjob.cpp78
-rw-r--r--kbugbuster/backend/bugmybugsjob.h52
-rw-r--r--kbugbuster/backend/bugserver.cpp412
-rw-r--r--kbugbuster/backend/bugserver.h152
-rw-r--r--kbugbuster/backend/bugserverconfig.cpp150
-rw-r--r--kbugbuster/backend/bugserverconfig.h91
-rw-r--r--kbugbuster/backend/bugsystem.cpp436
-rw-r--r--kbugbuster/backend/bugsystem.h146
-rw-r--r--kbugbuster/backend/domprocessor.cpp407
-rw-r--r--kbugbuster/backend/domprocessor.h69
-rw-r--r--kbugbuster/backend/error.h43
-rw-r--r--kbugbuster/backend/htmlparser.cpp294
-rw-r--r--kbugbuster/backend/htmlparser.h116
-rw-r--r--kbugbuster/backend/kbbprefs.cpp170
-rw-r--r--kbugbuster/backend/kbbprefs.h82
-rw-r--r--kbugbuster/backend/mailsender.cpp212
-rw-r--r--kbugbuster/backend/mailsender.h50
-rw-r--r--kbugbuster/backend/package.cpp82
-rw-r--r--kbugbuster/backend/package.h43
-rw-r--r--kbugbuster/backend/packageimpl.h31
-rw-r--r--kbugbuster/backend/packagelistjob.cpp68
-rw-r--r--kbugbuster/backend/packagelistjob.h55
-rw-r--r--kbugbuster/backend/person.cpp74
-rw-r--r--kbugbuster/backend/person.h26
-rw-r--r--kbugbuster/backend/processor.cpp78
-rw-r--r--kbugbuster/backend/processor.h63
-rw-r--r--kbugbuster/backend/rdfprocessor.cpp107
-rw-r--r--kbugbuster/backend/rdfprocessor.h43
-rw-r--r--kbugbuster/backend/smtp.cpp181
-rw-r--r--kbugbuster/backend/smtp.h67
48 files changed, 5873 insertions, 0 deletions
diff --git a/kbugbuster/backend/Makefile.am b/kbugbuster/backend/Makefile.am
new file mode 100644
index 00000000..e1bfd266
--- /dev/null
+++ b/kbugbuster/backend/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES= -I$(srcdir)/.. $(all_includes)
+
+noinst_LTLIBRARIES = libkbbbackend.la
+
+libkbbbackend_la_SOURCES = packagelistjob.cpp bugjob.cpp \
+ package.cpp bugsystem.cpp bug.cpp bugdetails.cpp \
+ bugcommand.cpp buglistjob.cpp bugmybugsjob.cpp \
+ mailsender.cpp bugcache.cpp bugdetailsjob.cpp \
+ person.cpp smtp.cpp bugserver.cpp \
+ bugserverconfig.cpp \
+ processor.cpp \
+ domprocessor.cpp rdfprocessor.cpp htmlparser.cpp \
+ kbbprefs.cpp
+
+METASOURCES = AUTO
+
diff --git a/kbugbuster/backend/bug.cpp b/kbugbuster/backend/bug.cpp
new file mode 100644
index 00000000..4523652e
--- /dev/null
+++ b/kbugbuster/backend/bug.cpp
@@ -0,0 +1,240 @@
+
+#include "bug.h"
+
+#include "bugimpl.h"
+
+#include <assert.h>
+#include <kdebug.h>
+
+Bug::Bug()
+: m_impl( NULL )
+{
+}
+
+Bug::Bug( BugImpl *impl )
+: m_impl( impl )
+{
+}
+
+Bug::Bug( const Bug &other )
+{
+ (*this) = other;
+}
+
+Bug Bug::fromNumber( const QString &bugNumber )
+{
+ return new BugImpl( QString::null, Person(), bugNumber, 0xFFFFFFFF, Normal, Person(),
+ Unconfirmed, Bug::BugMergeList() );
+}
+
+Bug &Bug::operator=( const Bug &rhs )
+{
+ m_impl = rhs.m_impl;
+ return *this;
+}
+
+Bug::~Bug()
+{
+}
+
+QString Bug::severityLabel( Bug::Severity s )
+{
+ switch ( s )
+ {
+ case Critical: return i18n("Critical");
+ case Grave: return i18n("Grave");
+ case Major: return i18n("Major");
+ case Crash: return i18n("Crash");
+ case Normal: return i18n("Normal");
+ case Minor: return i18n("Minor");
+ case Wishlist: return i18n("Wishlist");
+ default:
+ case SeverityUndefined: return i18n("Undefined");
+ }
+}
+
+QString Bug::severityToString( Bug::Severity s )
+{
+ switch ( s ) {
+ case Critical: return QString::fromLatin1( "critical" );
+ case Grave: return QString::fromLatin1( "grave" );
+ case Major: return QString::fromLatin1( "major" );
+ case Crash: return QString::fromLatin1( "crash" );
+ case Normal: return QString::fromLatin1( "normal" );
+ case Minor: return QString::fromLatin1( "minor" );
+ case Wishlist: return QString::fromLatin1( "wishlist" );
+ default:
+ kdWarning() << "Bug::severityToString invalid severity " << s << endl;
+ return QString::fromLatin1( "<invalid>" );
+ }
+}
+
+Bug::Severity Bug::stringToSeverity( const QString &s, bool *ok )
+{
+ if ( ok )
+ *ok = true;
+
+ if ( s == "critical" ) return Critical;
+ else if ( s == "grave" ) return Grave;
+ else if ( s == "major" ) return Major;
+ else if ( s == "crash" || s == "drkonqi" ) return Crash;
+ else if ( s == "normal" ) return Normal;
+ else if ( s == "minor" ) return Minor;
+ else if ( s == "wishlist" ) return Wishlist;
+
+ kdWarning() << "Bug::stringToSeverity: invalid severity: " << s << endl;
+ if ( ok )
+ *ok = false;
+ return SeverityUndefined;
+}
+
+QValueList<Bug::Severity> Bug::severities()
+{
+ QValueList<Severity> s;
+ s << Critical << Grave << Major << Crash << Normal << Minor << Wishlist;
+ return s;
+}
+
+QString Bug::statusLabel( Bug::Status s )
+{
+ switch ( s )
+ {
+ case Unconfirmed: return i18n("Unconfirmed");
+ case New: return i18n("New");
+ case Assigned: return i18n("Assigned");
+ case Reopened: return i18n("Reopened");
+ case Closed: return i18n("Closed");
+ default:
+ case StatusUndefined: return i18n("Undefined");
+ }
+}
+
+QString Bug::statusToString( Bug::Status s )
+{
+ switch ( s ) {
+ case Unconfirmed: return QString::fromLatin1( "unconfirmed" );
+ case New: return QString::fromLatin1( "new" );
+ case Assigned: return QString::fromLatin1( "assigned" );
+ case Reopened: return QString::fromLatin1( "reopened" );
+ case Closed: return QString::fromLatin1( "closed" );
+ default:
+ kdWarning() << "Bug::statusToString invalid status " << s << endl;
+ return QString::fromLatin1( "<invalid>" );
+ }
+}
+
+Bug::Status Bug::stringToStatus( const QString &s, bool *ok )
+{
+ if ( ok )
+ *ok = true;
+
+ if ( s == "unconfirmed" ) return Unconfirmed;
+ else if ( s == "new" ) return New;
+ else if ( s == "assigned" ) return Assigned;
+ else if ( s == "reopened" ) return Reopened;
+ else if ( s == "closed" ) return Closed;
+
+ kdWarning() << "Bug::stringToStatus: invalid status: " << s << endl;
+ if ( ok )
+ *ok = false;
+ return StatusUndefined;
+}
+
+QString Bug::title() const
+{
+ if ( !m_impl )
+ return QString::null;
+
+ return m_impl->title;
+}
+
+void Bug::setTitle( QString title)
+{
+ if ( m_impl )
+ m_impl->title = title;
+}
+
+uint Bug::age() const
+{
+ if ( !m_impl )
+ return 0;
+
+ return m_impl->age;
+}
+
+void Bug::setAge( uint age )
+{
+ if ( m_impl )
+ m_impl->age = age;
+}
+
+struct Person Bug::submitter() const
+{
+ if ( !m_impl )
+ return Person( QString::null, QString::null );
+
+ return m_impl->submitter;
+}
+
+QString Bug::number() const
+{
+ if ( !m_impl )
+ return QString::null;
+
+ return m_impl->number;
+}
+
+Bug::Severity Bug::severity() const
+{
+ if ( !m_impl )
+ return Normal;
+
+ return m_impl->severity;
+}
+
+void Bug::setSeverity( Bug::Severity severity )
+{
+ if ( m_impl )
+ m_impl->severity = severity;
+}
+
+Bug::BugMergeList Bug::mergedWith() const
+{
+ if ( !m_impl )
+ return Bug::BugMergeList();
+
+ return m_impl->mergedWith;
+}
+
+
+Bug::Status Bug::status() const
+{
+ if ( !m_impl )
+ return StatusUndefined;
+
+ return m_impl->status;
+}
+
+QString Bug::severityAsString() const
+{
+ return severityToString( severity() );
+}
+
+Person Bug::developerTODO() const
+{
+ return (m_impl == NULL) ? Person( QString::null, QString::null ) :
+ m_impl->developerTODO;
+}
+
+bool Bug::operator==( const Bug &rhs )
+{
+ return m_impl == rhs.m_impl;
+}
+
+bool Bug::operator<( const Bug &rhs ) const
+{
+ return m_impl < rhs.m_impl;
+}
+
+/* vim: set ts=4 sw=4 et softtabstop=4: */
+
diff --git a/kbugbuster/backend/bug.h b/kbugbuster/backend/bug.h
new file mode 100644
index 00000000..9a5ae8b6
--- /dev/null
+++ b/kbugbuster/backend/bug.h
@@ -0,0 +1,92 @@
+#ifndef __bug_h__
+#define __bug_h__
+
+#include "person.h"
+
+#include <qvaluelist.h>
+
+#include <ksharedptr.h>
+
+class BugImpl;
+
+class Bug
+{
+public:
+ typedef QValueList<Bug> List;
+ typedef QValueList<int> BugMergeList;
+
+ enum Severity { SeverityUndefined, Critical, Grave, Major, Crash, Normal,
+ Minor, Wishlist };
+ enum Status { StatusUndefined, Unconfirmed, New, Assigned, Reopened,
+ Closed };
+
+ Bug();
+ Bug( BugImpl *impl );
+ Bug( const Bug &other );
+ Bug &operator=( const Bug &rhs );
+ ~Bug();
+
+ static QString severityLabel( Severity s );
+ /**
+ Return string representation of severity. This function is symmetric to
+ stringToSeverity().
+ */
+ static QString severityToString( Severity s );
+ /**
+ Return severity code of string representation. This function is symmetric
+ to severityToString().
+ */
+ static Severity stringToSeverity( const QString &, bool *ok = 0 );
+
+ static QValueList<Severity> severities();
+
+ uint age() const;
+ void setAge( uint days );
+
+ QString title() const;
+ void setTitle( QString title );
+ Person submitter() const;
+ QString number() const;
+ Severity severity() const;
+ void setSeverity( Severity severity );
+ QString severityAsString() const;
+ Person developerTODO() const;
+
+ BugMergeList mergedWith() const;
+
+ /**
+ * Status of a bug. Currently open or closed.
+ * TODO: Should we add a status 'deleted' here ?
+ */
+ Status status() const;
+ void setStatus( Status newStatus );
+
+ static QString statusLabel( Status s );
+ /**
+ Return string representation of status. This function is symmetric to
+ stringToStatus().
+ */
+ static QString statusToString( Status s );
+ /**
+ Return status code of string representation. This function is symmetric
+ to statusToString().
+ */
+ static Status stringToStatus( const QString &, bool *ok = 0 );
+
+ bool operator==( const Bug &rhs );
+ bool operator<( const Bug &rhs ) const;
+
+ bool isNull() const { return m_impl == 0; }
+
+ static Bug fromNumber( const QString &bugNumber );
+
+private:
+ BugImpl *impl() const { return m_impl; }
+
+ KSharedPtr<BugImpl> m_impl;
+};
+
+#endif
+
+/* vim: set sw=4 ts=4 et softtabstop=4: */
+
diff --git a/kbugbuster/backend/bugcache.cpp b/kbugbuster/backend/bugcache.cpp
new file mode 100644
index 00000000..0eb44c8c
--- /dev/null
+++ b/kbugbuster/backend/bugcache.cpp
@@ -0,0 +1,289 @@
+// (C) 2001, Cornelius Schumacher
+
+#include <qstringlist.h>
+#include <qfile.h>
+
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kurl.h>
+#include <kdebug.h>
+
+#include "packageimpl.h"
+#include "bugimpl.h"
+#include "bugdetailsimpl.h"
+
+#include "bugcache.h"
+
+BugCache::BugCache( const QString &id )
+{
+ mId = id;
+
+ init();
+}
+
+BugCache::~BugCache()
+{
+ m_cachePackages->sync();
+ m_cacheBugs->sync();
+
+ delete m_cachePackages;
+ delete m_cacheBugs;
+}
+
+void BugCache::init()
+{
+ mCachePackagesFileName = locateLocal( "appdata", mId + "-packages.cache" );
+ mCacheBugsFileName = locateLocal( "appdata", mId + "-bugs.cache" );
+
+ m_cachePackages = new KSimpleConfig( mCachePackagesFileName );
+ m_cacheBugs = new KSimpleConfig( mCacheBugsFileName );
+}
+
+void BugCache::savePackageList( const Package::List &pkgs )
+{
+ Package::List::ConstIterator it;
+ for (it = pkgs.begin(); it != pkgs.end(); ++it) {
+ m_cachePackages->setGroup((*it).name());
+ m_cachePackages->writeEntry("description",(*it).description());
+ m_cachePackages->writeEntry("numberOfBugs",(*it).numberOfBugs());
+ m_cachePackages->writeEntry("components",(*it).components());
+ writePerson(m_cachePackages,"Maintainer",(*it).maintainer());
+ }
+}
+
+Package::List BugCache::loadPackageList()
+{
+ Package::List pkgs;
+
+ QStringList packages = m_cachePackages->groupList();
+ QStringList::ConstIterator it;
+ for( it = packages.begin(); it != packages.end(); ++it ) {
+ if ((*it) == "<default>") continue;
+ if ((*it).contains("/")) continue;
+
+ m_cachePackages->setGroup(*it);
+
+ QString description = m_cachePackages->readEntry("description");
+ int numberOfBugs = m_cachePackages->readNumEntry("numberOfBugs");
+ Person maintainer = readPerson( m_cachePackages, "Maintainer");
+ QStringList components = m_cachePackages->readListEntry("components");
+
+ pkgs.append( Package( new PackageImpl( (*it), description, numberOfBugs,
+ maintainer, components ) ) );
+ }
+
+ return pkgs;
+}
+
+void BugCache::invalidatePackageList()
+{
+ // Completely wipe out packages.cache
+ QStringList packages = m_cachePackages->groupList();
+ QStringList::ConstIterator it;
+ for( it = packages.begin(); it != packages.end(); ++it ) {
+ if ((*it) == "<default>") continue;
+ m_cachePackages->deleteGroup(*it, true);
+ }
+}
+
+void BugCache::saveBugList( const Package &pkg, const QString &component, const Bug::List &bugs )
+{
+ QStringList bugList;
+
+ Bug::List::ConstIterator it;
+ for( it = bugs.begin(); it != bugs.end(); ++it ) {
+ QString number = (*it).number();
+ bugList.append( number );
+ m_cacheBugs->setGroup( number );
+ m_cacheBugs->writeEntry( "Title", (*it).title() );
+ m_cacheBugs->writeEntry( "Severity", Bug::severityToString((*it).severity()) );
+ m_cacheBugs->writeEntry( "Status", Bug::statusToString((*it).status()) );
+ m_cacheBugs->writeEntry( "MergedWith" , (*it).mergedWith() );
+ m_cacheBugs->writeEntry( "Age", ( *it ).age() );
+ writePerson( m_cacheBugs, "Submitter", (*it).submitter() );
+ writePerson( m_cacheBugs, "TODO", (*it).developerTODO() );
+ }
+
+ if ( component.isEmpty() )
+ m_cachePackages->setGroup( pkg.name() );
+ else {
+ m_cachePackages->setGroup( pkg.name() + "/" + component );
+ }
+
+ m_cachePackages->writeEntry( "bugList", bugList );
+}
+
+Bug::List BugCache::loadBugList( const Package &pkg, const QString &component, bool disconnected )
+{
+// kdDebug() << "Loading bug list for " << pkg.name() << endl;
+
+ Bug::List bugList;
+
+ if ( component.isEmpty() )
+ m_cachePackages->setGroup( pkg.name() );
+ else
+ m_cachePackages->setGroup( pkg.name() + "/" + component );
+
+ QStringList bugs = m_cachePackages->readListEntry( "bugList" );
+
+// kdDebug() << " Bugs: " << (bugs.join(",")) << endl;
+
+ QStringList::ConstIterator it;
+ for( it = bugs.begin(); it != bugs.end(); ++it ) {
+ if ( m_cacheBugs->hasGroup(*it) )
+ {
+ m_cacheBugs->setGroup(*it);
+ QString title = m_cacheBugs->readEntry("Title");
+ if ( !title.isEmpty() ) // dunno how I ended up with an all empty bug in the cache
+ {
+ Person submitter = readPerson( m_cacheBugs, "Submitter" );
+ Bug::Status status = Bug::stringToStatus( m_cacheBugs->readEntry("Status") );
+ Bug::Severity severity = Bug::stringToSeverity( m_cacheBugs->readEntry("Severity") );
+ Person developerTODO = readPerson( m_cacheBugs, "TODO" );
+ Bug::BugMergeList mergedWith = m_cacheBugs->readIntListEntry( "MergedWith" );
+ uint age = m_cacheBugs->readUnsignedNumEntry( "Age", 0xFFFFFFFF );
+ bugList.append( Bug( new BugImpl( title, submitter, ( *it ), age,
+ severity, developerTODO,
+ status, mergedWith ) ) );
+ }
+ } else {
+ // This bug is in the package cache's buglist but not in the bugs cache
+ // Probably a new bug, we need to fetch it - if we're not in disconnected mode
+ kdWarning() << "Bug " << *it << " not in bug cache" << endl;
+ if ( !disconnected )
+ return Bug::List(); // returning an empty list will trigger a reload of the buglist
+ }
+ }
+
+ return bugList;
+}
+
+void BugCache::invalidateBugList( const Package& pkg, const QString &component )
+{
+ kdDebug() << "BugCache::invalidateBugList " << pkg.name()
+ << " (" << component << ")" << endl;
+
+ // Erase bug list for this package
+ if ( component.isEmpty() ) {
+ m_cachePackages->setGroup( pkg.name() );
+ } else {
+ QString key = pkg.name() + "/" + component;
+ m_cachePackages->setGroup( key );
+ m_cachePackages->setGroup( pkg.name() + "/" + component );
+ }
+
+ m_cachePackages->writeEntry("bugList",QString::null);
+}
+
+void BugCache::saveBugDetails( const Bug &bug, const BugDetails &details )
+{
+ m_cacheBugs->setGroup( bug.number() );
+
+ m_cacheBugs->writeEntry( "Version", details.version() );
+ m_cacheBugs->writeEntry( "Source", details.source() );
+ m_cacheBugs->writeEntry( "Compiler", details.compiler() );
+ m_cacheBugs->writeEntry( "OS", details.os() );
+
+ QStringList senders;
+ QStringList texts;
+ QStringList dates;
+
+ BugDetailsPart::List parts = details.parts();
+ BugDetailsPart::List::ConstIterator it;
+ for ( it = parts.begin(); it != parts.end(); ++it ) {
+ senders.append( (*it).sender.fullName() );
+ texts.append( (*it).text );
+ dates.append( (*it).date.toString( Qt::ISODate ) );
+ }
+
+ m_cacheBugs->writeEntry( "Details", texts );
+ m_cacheBugs->writeEntry( "Senders", senders );
+ m_cacheBugs->writeEntry( "Dates", dates );
+}
+
+bool BugCache::hasBugDetails( const Bug& bug ) const
+{
+ if ( !m_cacheBugs->hasGroup( bug.number() ) )
+ return false;
+
+ m_cacheBugs->setGroup( bug.number() );
+ return m_cacheBugs->hasKey( "Details" );
+}
+
+BugDetails BugCache::loadBugDetails( const Bug &bug )
+{
+ if ( !m_cacheBugs->hasGroup( bug.number() ) ) {
+ return BugDetails();
+ }
+
+ m_cacheBugs->setGroup( bug.number() );
+
+ BugDetailsPart::List parts;
+
+ QStringList texts = m_cacheBugs->readListEntry( "Details" );
+ QStringList senders = m_cacheBugs->readListEntry( "Senders" );
+ QStringList dates = m_cacheBugs->readListEntry( "Dates" );
+
+ QStringList::ConstIterator itTexts = texts.begin();
+ QStringList::ConstIterator itSenders = senders.begin();
+ QStringList::ConstIterator itDates = dates.begin();
+ while( itTexts != texts.end() ) {
+ QDateTime date = QDateTime::fromString( *itDates, Qt::ISODate );
+ parts.append( BugDetailsPart( Person(*itSenders), date, *itTexts ) );
+
+ ++itTexts;
+ ++itSenders;
+ ++itDates;
+ }
+
+ if ( parts.count() == 0 ) {
+ return BugDetails();
+ }
+
+ QString version = m_cacheBugs->readEntry( "Version" );
+ QString source = m_cacheBugs->readEntry( "Source" );
+ QString compiler = m_cacheBugs->readEntry( "Compiler" );
+ QString os = m_cacheBugs->readEntry( "OS" );
+
+ return BugDetails( new BugDetailsImpl( version, source, compiler, os,
+ parts ) );
+}
+
+void BugCache::invalidateBugDetails( const Bug& bug )
+{
+ m_cacheBugs->deleteGroup( bug.number(), true );
+}
+
+void BugCache::clear()
+{
+ delete m_cachePackages;
+ delete m_cacheBugs;
+
+ QFile f1( mCachePackagesFileName );
+ f1.remove();
+
+ QFile f2( mCacheBugsFileName );
+ f2.remove();
+
+ init();
+}
+
+void BugCache::writePerson( KSimpleConfig *file, const QString &key,
+ const Person &p )
+{
+ QStringList values;
+ values.append(p.name);
+ values.append(p.email);
+ file->writeEntry( key, values );
+}
+
+struct Person BugCache::readPerson( KSimpleConfig *file, const QString &key )
+{
+ struct Person p;
+ QStringList values = file->readListEntry(key);
+ if ( values.count() > 0 )
+ p.name = values[0];
+ if ( values.count() > 1 )
+ p.email = values[1];
+ return p;
+}
diff --git a/kbugbuster/backend/bugcache.h b/kbugbuster/backend/bugcache.h
new file mode 100644
index 00000000..af1aed11
--- /dev/null
+++ b/kbugbuster/backend/bugcache.h
@@ -0,0 +1,47 @@
+#ifndef BUGCACHE_H
+#define BUGCACHE_H
+
+class KSimpleConfig;
+
+#include "package.h"
+#include "bug.h"
+#include "bugdetails.h"
+
+class BugCache
+{
+ public:
+ BugCache( const QString &id );
+ ~BugCache();
+
+ void savePackageList( const Package::List &pkgs );
+ Package::List loadPackageList();
+ void invalidatePackageList();
+
+ void saveBugList( const Package &pkg, const QString &component, const Bug::List & );
+ Bug::List loadBugList( const Package &pkg, const QString &component, bool disconnected );
+ void invalidateBugList( const Package &pkg, const QString &component );
+
+ void saveBugDetails( const Bug &bug, const BugDetails & );
+ BugDetails loadBugDetails( const Bug &bug );
+ void invalidateBugDetails( const Bug &bug );
+ bool hasBugDetails( const Bug& bug ) const;
+
+ void clear();
+
+ private:
+ void init();
+
+ void writePerson( KSimpleConfig *file, const QString &key,
+ const Person &p );
+ struct Person readPerson (KSimpleConfig *file, const QString &key );
+
+ QString mId;
+
+ KSimpleConfig *m_cachePackages;
+ KSimpleConfig *m_cacheBugs;
+
+ QString mCachePackagesFileName;
+ QString mCacheBugsFileName;
+};
+
+#endif
diff --git a/kbugbuster/backend/bugcommand.cpp b/kbugbuster/backend/bugcommand.cpp
new file mode 100644
index 00000000..332c937d
--- /dev/null
+++ b/kbugbuster/backend/bugcommand.cpp
@@ -0,0 +1,317 @@
+#include <kdebug.h>
+#include <kconfig.h>
+#include <klocale.h>
+
+#include "bugcommand.h"
+
+QString BugCommand::name()
+{
+ return i18n("Unknown");
+}
+
+QString BugCommand::details()
+{
+ return QString::null;
+}
+
+BugCommand *BugCommand::load( KConfig *config, const QString &type )
+{
+ QString bugNumber = config->group();
+ // ### this sucks. we better let Bug implement proper persistance,
+ // because this way of instantiating a bug object doesn't bring back
+ // properties like title, package, etc. (Simon)
+ Bug bug = Bug::fromNumber( bugNumber );
+ Package pkg = Package(); // hack
+
+ if ( type == "Close" ) {
+ return new BugCommandClose( bug, config->readEntry( type ), pkg );
+ } else if ( type == "Reopen" ) {
+ return new BugCommandReopen( bug, pkg );
+ } else if ( type == "Merge" ) {
+ return new BugCommandMerge( config->readListEntry( type ), pkg );
+ } else if ( type == "Unmerge" ) {
+ return new BugCommandUnmerge( bug, pkg );
+ } else if ( type == "Reassign" ) {
+ return new BugCommandReassign( bug, config->readEntry( type ), pkg );
+ } else if ( type == "Retitle" ) {
+ return new BugCommandRetitle( bug, config->readEntry( type ), pkg );
+ } else if ( type == "Severity" ) {
+ return new BugCommandSeverity( bug, config->readEntry( type ), pkg );
+ } else if ( type == "Reply" ) {
+ return new BugCommandReply( bug, config->readEntry( type ), config->readNumEntry("Recipient",Normal) );
+ } else if ( type == "ReplyPrivate" ) {
+ QStringList args = config->readListEntry( type );
+ if ( args.count() != 2 ) return 0;
+ return new BugCommandReplyPrivate( bug, *(args.at(0)), *(args.at(1)) );
+ } else {
+ kdDebug() << "Warning! Unknown bug command '" << type << "'" << endl;
+ return 0;
+ }
+}
+
+///////////////////// Close /////////////////////
+
+QString BugCommandClose::controlString() const
+{
+ if (m_message.isEmpty()) {
+ return "close " + m_bug.number();
+ } else {
+ return QString::null;
+ }
+}
+
+QString BugCommandClose::mailAddress() const
+{
+ kdDebug() << "BugCommandClose::mailAddress(): number: " << m_bug.number() << endl;
+
+ if (m_message.isEmpty()) {
+ return QString::null;
+ } else {
+ return m_bug.number() + "-done@bugs.kde.org";
+ }
+}
+
+QString BugCommandClose::mailText() const
+{
+ if (m_message.isEmpty()) {
+ return QString::null;
+ } else {
+ return m_message;
+ }
+}
+
+QString BugCommandClose::name()
+{
+ return i18n("Close");
+}
+
+QString BugCommandClose::details() const
+{
+ return m_message;
+}
+
+void BugCommandClose::save( KConfig *config )
+{
+ config->writeEntry( "Close",m_message );
+}
+
+///////////////////// Close Silently /////////////////////
+
+QString BugCommandCloseSilently::controlString() const
+{
+ return "done " + m_bug.number();
+}
+
+QString BugCommandCloseSilently::name()
+{
+ return i18n("Close Silently");
+}
+
+void BugCommandCloseSilently::save( KConfig *config )
+{
+ config->writeEntry( "CloseSilently", true );
+}
+
+///////////////////// Reopen /////////////////////
+
+QString BugCommandReopen::controlString() const
+{
+ return "reopen " + m_bug.number();
+}
+
+QString BugCommandReopen::name()
+{
+ return i18n("Reopen");
+}
+
+void BugCommandReopen::save( KConfig *config )
+{
+ config->writeEntry( "Reopen", true );
+}
+
+///////////////////// Retitle /////////////////////
+
+QString BugCommandRetitle::controlString() const
+{
+ return "retitle " + m_bug.number() + " " + m_title;
+}
+
+QString BugCommandRetitle::name()
+{
+ return i18n("Retitle");
+}
+
+QString BugCommandRetitle::details() const
+{
+ return m_title;
+}
+
+void BugCommandRetitle::save( KConfig *config )
+{
+ config->writeEntry( "Retitle", m_title );
+}
+
+///////////////////// Merge /////////////////////
+
+QString BugCommandMerge::controlString() const
+{
+ return "merge " + m_bugNumbers.join(" ");
+}
+
+QString BugCommandMerge::name()
+{
+ return i18n("Merge");
+}
+
+QString BugCommandMerge::details() const
+{
+ return m_bugNumbers.join(", ");
+}
+
+void BugCommandMerge::save( KConfig *config )
+{
+ config->writeEntry( "Merge", m_bugNumbers );
+}
+
+///////////////////// Unmerge /////////////////////
+
+QString BugCommandUnmerge::controlString() const
+{
+ return "unmerge " + m_bug.number();
+}
+
+QString BugCommandUnmerge::name()
+{
+ return i18n("Unmerge");
+}
+
+void BugCommandUnmerge::save( KConfig *config )
+{
+ config->writeEntry( "Unmerge", true );
+}
+
+///////////////////// Reply /////////////////////
+
+QString BugCommandReply::mailAddress() const
+{
+ return m_bug.number() + "@bugs.kde.org";
+#if 0
+ switch ( m_recipient ) {
+ case Normal:
+ return m_bug.number() + "@bugs.kde.org";
+ case Maintonly:
+ return m_bug.number() + "-maintonly@bugs.kde.org";
+ case Quiet:
+ return m_bug.number() + "-quiet@bugs.kde.org";
+ }
+ return QString::null;
+#endif
+}
+
+QString BugCommandReply::mailText() const
+{
+ return m_message;
+}
+
+QString BugCommandReply::name()
+{
+ return i18n("Reply");
+#if 0
+ switch ( m_recipient ) {
+ case Normal:
+ return i18n("Reply");
+ case Maintonly:
+ return i18n("Reply (Maintonly)");
+ case Quiet:
+ return i18n("Reply (Quiet)");
+ }
+ return QString::null;
+#endif
+}
+
+QString BugCommandReply::details() const
+{
+ return m_message;
+}
+
+void BugCommandReply::save( KConfig *config )
+{
+ config->writeEntry( "Reply", m_message );
+#if 0
+ config->writeEntry( "Recipient", m_recipient );
+#endif
+}
+
+///////////////////// Reply Private /////////////////////
+
+QString BugCommandReplyPrivate::mailAddress() const
+{
+ return m_address;
+}
+
+QString BugCommandReplyPrivate::mailText() const
+{
+ return m_message;
+}
+
+QString BugCommandReplyPrivate::name()
+{
+ return i18n("Private Reply");
+}
+
+QString BugCommandReplyPrivate::details() const
+{
+ return m_message;
+}
+
+void BugCommandReplyPrivate::save( KConfig *config )
+{
+ QStringList args;
+ args << m_address;
+ args << m_message;
+ config->writeEntry( "ReplyPrivate", args );
+}
+
+///////////////////// Severity /////////////////////
+
+QString BugCommandSeverity::controlString() const
+{
+ return "severity " + m_bug.number() + " " + m_severity.lower();
+}
+
+QString BugCommandSeverity::name()
+{
+ return i18n("Severity");
+}
+
+QString BugCommandSeverity::details() const
+{
+ return m_severity;
+}
+
+void BugCommandSeverity::save( KConfig *config )
+{
+ config->writeEntry( "Severity", m_severity );
+}
+
+///////////////////// Reassign /////////////////////
+
+QString BugCommandReassign::controlString() const
+{
+ return "reassign " + m_bug.number() + " " + m_package;
+}
+
+QString BugCommandReassign::name()
+{
+ return i18n("Reassign");
+}
+
+QString BugCommandReassign::details() const
+{
+ return m_package;
+}
+
+void BugCommandReassign::save( KConfig *config )
+{
+ config->writeEntry( "Reassign", m_package );
+}
diff --git a/kbugbuster/backend/bugcommand.h b/kbugbuster/backend/bugcommand.h
new file mode 100644
index 00000000..96b9c85c
--- /dev/null
+++ b/kbugbuster/backend/bugcommand.h
@@ -0,0 +1,217 @@
+#ifndef BUGCOMMAND_H
+#define BUGCOMMAND_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+
+#include "bug.h"
+#include "package.h"
+
+class KConfig;
+
+class BugCommand {
+ public:
+ enum Mode { Normal, Maintonly, Quiet };
+ enum State { None, Queued, Sent };
+
+ BugCommand( const Bug &bug ) : m_bug( bug ) {}
+ BugCommand( const Bug &bug, const Package &pkg ) : m_bug( bug ), m_package( pkg ) {}
+ virtual ~BugCommand() {}
+
+ virtual QString controlString() const { return QString::null; }
+
+ virtual QString mailAddress() const { return QString::null; }
+ virtual QString mailText() const { return QString::null; }
+
+ Bug bug() const { return m_bug; }
+ Package package() const { return m_package; }
+
+ virtual QString name();
+ virtual QString details();
+
+ virtual QString type() const { return QString::null; }
+
+ virtual void save( KConfig * ) = 0;
+ static BugCommand *load( KConfig *, const QString &type );
+
+ protected:
+ Bug m_bug;
+ Package m_package;
+};
+
+class BugCommandClose : public BugCommand {
+ public:
+ BugCommandClose( const Bug &bug, const QString &message, const Package &pkg ) :
+ BugCommand( bug, pkg ), m_message( message ) {}
+
+ QString controlString() const;
+ QString mailAddress() const;
+ QString mailText() const;
+
+ QString name();
+ QString details() const;
+
+ QString type() const { return QString::fromLatin1("Close"); }
+
+ void save( KConfig * );
+
+ private:
+ QString m_message;
+};
+
+class BugCommandCloseSilently : public BugCommand {
+ public:
+ BugCommandCloseSilently( const Bug &bug, const Package &pkg ) :
+ BugCommand( bug, pkg ) {}
+
+ QString controlString() const;
+
+ QString name();
+
+ QString type() const { return QString::fromLatin1("CloseSilently"); }
+
+ void save( KConfig * );
+};
+
+class BugCommandReopen : public BugCommand {
+ public:
+ BugCommandReopen( const Bug &bug, const Package &pkg ) :
+ BugCommand( bug, pkg ) {}
+
+ QString controlString() const;
+
+ QString name();
+
+ QString type() const { return QString::fromLatin1("Reopen"); }
+
+ void save( KConfig * );
+};
+
+class BugCommandRetitle : public BugCommand {
+ public:
+ BugCommandRetitle( const Bug &bug, const QString &title, const Package &pkg ) :
+ BugCommand( bug, pkg ), m_title( title ) {}
+
+ QString controlString() const;
+
+ QString name();
+ QString details() const;
+
+ QString type() const { return QString::fromLatin1("Retitle"); }
+
+ void save( KConfig * );
+
+ private:
+ QString m_title;
+};
+
+class BugCommandMerge : public BugCommand {
+ public:
+ BugCommandMerge( const QStringList &bugNumbers, const Package &pkg ) :
+ BugCommand( Bug(), pkg ), m_bugNumbers( bugNumbers ) {}
+
+ QString controlString() const;
+
+ QString name();
+ QString details() const;
+
+ QString type() const { return QString::fromLatin1("Merge"); }
+
+ void save( KConfig * );
+
+ private:
+ QStringList m_bugNumbers;
+};
+
+class BugCommandUnmerge : public BugCommand {
+ public:
+ BugCommandUnmerge( const Bug &bug, const Package &pkg ) :
+ BugCommand( bug, pkg ) {}
+
+ QString name();
+
+ QString type() const { return QString::fromLatin1("Unmerge"); }
+
+ void save( KConfig * );
+
+ QString controlString() const;
+};
+
+class BugCommandReply : public BugCommand {
+ public:
+ BugCommandReply( const Bug &bug, const QString &message, const int &recipient) :
+ BugCommand( bug ), m_message( message ), m_recipient( recipient ) {}
+
+ QString mailAddress() const;
+ QString mailText() const;
+
+ QString name();
+ QString details() const;
+
+ QString type() const { return QString::fromLatin1("Reply"); }
+
+ void save( KConfig * );
+
+ private:
+ QString m_message;
+ int m_recipient;
+};
+
+class BugCommandReplyPrivate : public BugCommand {
+ public:
+ BugCommandReplyPrivate( const Bug &bug, const QString &address,
+ const QString &message ) :
+ BugCommand( bug ), m_address( address ), m_message( message ) {}
+
+ QString mailAddress() const;
+ QString mailText() const;
+
+ QString name();
+ QString details() const;
+
+ QString type() const { return QString::fromLatin1("ReplyPrivate"); }
+
+ void save( KConfig * );
+
+ private:
+ QString m_address;
+ QString m_message;
+};
+
+class BugCommandSeverity : public BugCommand {
+ public:
+ BugCommandSeverity( const Bug &bug, const QString &severity, const Package &pkg ) :
+ BugCommand( bug, pkg ), m_severity( severity ) {}
+
+ QString name();
+ QString details() const;
+
+ QString type() const { return QString::fromLatin1("Severity"); }
+
+ QString controlString() const;
+
+ void save( KConfig * );
+
+ private:
+ QString m_severity;
+};
+
+class BugCommandReassign : public BugCommand {
+ public:
+ BugCommandReassign( const Bug &bug, const QString &package, const Package &pkg ) :
+ BugCommand( bug, pkg ), m_package( package ) {}
+
+ QString name();
+ QString details() const;
+
+ QString type() const { return QString::fromLatin1("Reassign"); }
+
+ QString controlString() const;
+
+ void save( KConfig * );
+
+ private:
+ QString m_package;
+};
+
+#endif
diff --git a/kbugbuster/backend/bugdetails.cpp b/kbugbuster/backend/bugdetails.cpp
new file mode 100644
index 00000000..ec512d79
--- /dev/null
+++ b/kbugbuster/backend/bugdetails.cpp
@@ -0,0 +1,268 @@
+
+#include "bugdetails.h"
+
+#include "bugdetailsimpl.h"
+#include <qstringlist.h>
+#include <kdebug.h>
+#include <kmdcodec.h>
+#include <kmessagebox.h>
+#include <qregexp.h>
+
+BugDetails::BugDetails()
+{
+}
+
+BugDetails::BugDetails( BugDetailsImpl *impl ) :
+ m_impl( impl )
+{
+}
+
+BugDetails::BugDetails( const BugDetails &other )
+{
+ (*this) = other;
+}
+
+BugDetails &BugDetails::operator=( const BugDetails &rhs )
+{
+ m_impl = rhs.m_impl;
+ return *this;
+}
+
+BugDetails::~BugDetails()
+{
+}
+
+QString BugDetails::version() const
+{
+ if ( !m_impl )
+ return QString::null;
+
+ return m_impl->version;
+}
+
+QString BugDetails::source() const
+{
+ if ( !m_impl )
+ return QString::null;
+
+ return m_impl->source;
+}
+
+QString BugDetails::compiler() const
+{
+ if ( !m_impl )
+ return QString::null;
+
+ return m_impl->compiler;
+}
+
+QString BugDetails::os() const
+{
+ if ( !m_impl )
+ return QString::null;
+
+ return m_impl->os;
+}
+
+QDateTime BugDetails::submissionDate() const
+{
+ if ( !m_impl ) return QDateTime();
+
+ if ( m_impl->parts.count() > 0 ) {
+ return m_impl->parts.last().date;
+ }
+ return QDateTime();
+}
+
+int BugDetails::age() const
+{
+ if ( !m_impl )
+ return 0;
+
+ return submissionDate().daysTo( QDateTime::currentDateTime() );
+}
+
+BugDetailsPart::List BugDetails::parts() const
+{
+ if ( !m_impl )
+ return BugDetailsPart::List();
+
+ return m_impl->parts;
+}
+
+QValueList<BugDetailsImpl::AttachmentDetails> BugDetails::attachmentDetails() const
+{
+ if ( m_impl )
+ return m_impl->attachments;
+ else
+ return QValueList<BugDetailsImpl::AttachmentDetails>();
+}
+
+void BugDetails::addAttachmentDetails( const QValueList<BugDetailsImpl::AttachmentDetails>& attch )
+{
+ if ( m_impl )
+ m_impl->attachments = attch;
+}
+
+QValueList<BugDetails::Attachment> BugDetails::extractAttachments() const
+{
+ QValueList<BugDetails::Attachment> lst;
+ if ( !m_impl )
+ return lst;
+ BugDetailsPart::List parts = m_impl->parts;
+ for ( BugDetailsPart::List::ConstIterator it = parts.begin(); it != parts.end(); ++it ) {
+ lst += extractAttachments( (*it).text );
+ }
+ return lst;
+}
+
+//#define DEBUG_EXTRACT
+
+QValueList<BugDetails::Attachment> BugDetails::extractAttachments( const QString& text )
+{
+ QValueList<BugDetails::Attachment> lst;
+ QStringList lines = QStringList::split( '\n', text );
+#ifdef DEBUG_EXTRACT
+ kdDebug() << k_funcinfo << lines.count() << " lines." << endl;
+#endif
+ QString boundary;
+ for ( QStringList::Iterator it = lines.begin() ; it != lines.end() ; ++it )
+ {
+#ifdef DEBUG_EXTRACT
+ kdDebug() << "Line: " << *it << endl;
+#endif
+ if ( (*it).startsWith( " Content-Type" ) ) // ## leading space comes from khtml
+ {
+#ifdef DEBUG_EXTRACT
+ //kdDebug() << "BugDetails::extractAttachments going back, looking for empty or boundary=" << boundary << endl;
+#endif
+ // Rewind until last empty line
+ QStringList::Iterator rit = it;
+ for ( ; rit != lines.begin() ; --rit ) {
+ QString line = *rit;
+ if ( line.endsWith( "<br />" ) )
+ line = line.left( line.length() - 6 );
+ while ( !line.isEmpty() && line[0] == ' ' )
+ line.remove( 0, 1 );
+#ifdef DEBUG_EXTRACT
+ kdDebug() << "Back: '" << line << "'" << endl;
+#endif
+ if ( line.isEmpty() ) {
+ ++rit; // boundary is next line
+ boundary = *rit;
+ if ( boundary.endsWith( "<br />" ) )
+ boundary = boundary.left( boundary.length() - 6 );
+ if ( boundary[0] == ' ' )
+ boundary.remove( 0, 1 );
+ kdDebug() << "BugDetails::extractAttachments boundary=" << boundary << endl;
+ break;
+ }
+ if ( line == boundary )
+ break;
+ }
+ // Forward until next empty line (end of headers) - and parse filename
+ QString filename;
+ QString encoding;
+ rit = it;
+ for ( ; rit != lines.end() ; ++rit ) {
+ QString header = *rit;
+ if ( header.endsWith( "<br />" ) )
+ header = header.left( header.length() - 6 );
+ if ( header[0] == ' ' )
+ header.remove( 0, 1 );
+#ifdef DEBUG_EXTRACT
+ kdDebug() << "Header: '" << header << "'" << endl;
+#endif
+ if ( header.isEmpty() )
+ break;
+ if ( header.startsWith( "Content-Disposition:" ) )
+ {
+#ifdef DEBUG_EXTRACT
+ kdDebug() << "BugDetails::extractAttachments found header " << *rit << endl;
+#endif
+ // Taken from libkdenetwork/kmime_headers.cpp
+ int pos=header.find("filename=", 0, false);
+ QString fn;
+ if(pos>-1) {
+ pos+=9;
+ fn=header.mid(pos, header.length()-pos);
+ if ( fn.startsWith( "\"" ) && fn.endsWith( "\"" ) )
+ fn = fn.mid( 1, fn.length() - 2 );
+ //filename=decodeRFC2047String(fn, &e_ncCS, defaultCS(), forceCS());
+ filename = fn; // hack
+ kdDebug() << "BugDetails::extractAttachments filename=" << filename << endl;
+ }
+
+ }
+ else if ( header.startsWith( "Content-Transfer-Encoding:" ) )
+ {
+ encoding = header.mid( 26 ).lower();
+ while ( encoding[0] == ' ' )
+ encoding.remove( 0, 1 );
+ kdDebug() << "BugDetails::extractAttachments encoding=" << encoding << endl;
+ }
+ } //for
+ if ( rit == lines.end() )
+ break;
+
+ it = rit;
+ if ( rit != lines.end() && !filename.isEmpty() )
+ {
+ ++it;
+ if ( it == lines.end() )
+ break;
+ // Read encoded contents
+ QString contents;
+ for ( ; it != lines.end() ; ++it )
+ {
+ QString line = *it;
+ if ( line.endsWith( "</tt>" ) )
+ line = line.left( line.length() - 5 );
+ if ( line.endsWith( "<br />" ) ) // necessary for the boundary check
+ line = line.left( line.length() - 6 );
+ while ( !line.isEmpty() && line[0] == ' ' )
+ line.remove( 0, 1 );
+ if ( line.isEmpty() || line == boundary ) // end of attachment
+ break;
+ if ( line == boundary+"--" ) // end of last attachment
+ {
+ boundary = QString::null;
+ break;
+ }
+ contents += line; // no newline, because of linebreaking between <br and />
+ }
+ contents = contents.replace( QRegExp("<br */>"), QString::null );
+#ifdef DEBUG_EXTRACT
+ kdDebug() << "BugDetails::extractAttachments contents=***\n" << contents << "\n***" << endl;
+#endif
+ kdDebug() << "BugDetails::extractAttachments creating attachment " << filename << endl;
+ Attachment a;
+ if ( encoding == "base64" )
+ KCodecs::base64Decode( contents.local8Bit(), a.contents /*out*/ );
+ else
+ //KCodecs::uudecode( contents.local8Bit(), a.contents /*out*/ );
+ KMessageBox::information( 0, i18n("Attachment %1 could not be decoded.\nEncoding: %2").arg(filename).arg(encoding) );
+#ifdef DEBUG_EXTRACT
+ kdDebug() << "Result: ***\n" << QCString( a.contents.data(), a.contents.size()+1 ) << "\n*+*" << endl;
+#endif
+ a.filename = filename;
+ lst.append(a);
+ if ( it == lines.end() )
+ break;
+ }
+ }
+ }
+#ifdef DEBUG_EXTRACT
+ kdDebug() << "BugDetails::extractAttachments returning " << lst.count() << " attachments for this part." << endl;
+#endif
+ return lst;
+}
+
+bool BugDetails::operator==( const BugDetails &rhs )
+{
+ return m_impl == rhs.m_impl;
+}
+
+/**
+ * vim:ts=4:sw=4:et
+ */
diff --git a/kbugbuster/backend/bugdetails.h b/kbugbuster/backend/bugdetails.h
new file mode 100644
index 00000000..08d20777
--- /dev/null
+++ b/kbugbuster/backend/bugdetails.h
@@ -0,0 +1,55 @@
+#ifndef __bugdetails_h__
+#define __bugdetails_h__
+
+#include "person.h"
+#include "bugdetailspart.h"
+#include "bugdetailsimpl.h"
+
+#include <qvaluelist.h>
+
+#include <ksharedptr.h>
+
+class BugDetailsImpl;
+
+class BugDetails
+{
+public:
+ typedef QValueList<BugDetails> List;
+ struct Attachment {
+ QByteArray contents;
+ QString filename;
+ };
+
+ BugDetails();
+ BugDetails( BugDetailsImpl *impl );
+ BugDetails( const BugDetails &other );
+ BugDetails &operator=( const BugDetails &rhs );
+ ~BugDetails();
+
+ QString version() const;
+ QString source() const;
+ QString compiler() const;
+ QString os() const;
+ BugDetailsPart::List parts() const;
+ void addAttachmentDetails( const QValueList<BugDetailsImpl::AttachmentDetails>& attch );
+ QValueList<BugDetailsImpl::AttachmentDetails> attachmentDetails() const;
+ QValueList<BugDetails::Attachment> extractAttachments() const;
+ static QValueList<BugDetails::Attachment> extractAttachments( const QString& text );
+
+ QDateTime submissionDate() const;
+ int age() const;
+
+ bool operator==( const BugDetails &rhs );
+
+ bool isNull() const { return m_impl == 0; }
+
+private:
+ BugDetailsImpl *impl() const { return m_impl; }
+
+ KSharedPtr<BugDetailsImpl> m_impl;
+};
+
+#endif
+
+/* vim: set sw=4 ts=4 et softtabstop=4: */
+
diff --git a/kbugbuster/backend/bugdetailsimpl.h b/kbugbuster/backend/bugdetailsimpl.h
new file mode 100644
index 00000000..df3e5d6d
--- /dev/null
+++ b/kbugbuster/backend/bugdetailsimpl.h
@@ -0,0 +1,40 @@
+#ifndef __bugdetails_impl_h__
+#define __bugdetails_impl_h__
+
+#include <ksharedptr.h>
+
+#include "bugdetailspart.h"
+
+struct BugDetailsImpl : public KShared
+{
+public:
+ BugDetailsImpl( const QString &_version, const QString &_source,
+ const QString &_compiler, const QString &_os,
+ const BugDetailsPart::List &_parts )
+ : version( _version ), source( _source ), compiler( _compiler ),
+ os( _os ), parts( _parts ) {}
+
+ struct AttachmentDetails {
+ AttachmentDetails() { }
+ AttachmentDetails( const QString& descr, const QString& dt,
+ const QString& idf ) : description( descr ),
+ date( dt ),
+ id( idf ) { }
+ QString description;
+ QString date;
+ QString id;
+ };
+
+ QString version;
+ QString source;
+ QString compiler;
+ QString os;
+ BugDetailsPart::List parts;
+ QValueList<BugDetailsImpl::AttachmentDetails> attachments;
+};
+
+#endif
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/bugdetailsjob.cpp b/kbugbuster/backend/bugdetailsjob.cpp
new file mode 100644
index 00000000..83599c1d
--- /dev/null
+++ b/kbugbuster/backend/bugdetailsjob.cpp
@@ -0,0 +1,50 @@
+
+#include "bugdetailsjob.h"
+#include "bug.h"
+#include "bugdetails.h"
+#include "bugdetailsimpl.h"
+#include "packageimpl.h"
+#include "bugserver.h"
+#include "processor.h"
+
+#include <kdebug.h>
+#include <assert.h>
+
+BugDetailsJob::BugDetailsJob( BugServer *server )
+ : BugJob( server )
+{
+}
+
+BugDetailsJob::~BugDetailsJob()
+{
+}
+
+void BugDetailsJob::start( const Bug &bug )
+{
+ m_bug = bug;
+
+ KURL bugUrl = server()->bugDetailsUrl( bug );
+
+ kdDebug() << "BugDetailsJob::start(): " << bugUrl.url() << endl;
+ BugJob::start( bugUrl );
+}
+
+void BugDetailsJob::process( const QByteArray &data )
+{
+ BugDetails bugDetails;
+
+ KBB::Error err = server()->processor()->parseBugDetails( data, bugDetails );
+
+ if ( err ) {
+ emit error( i18n("Bug %1: %2").arg( m_bug.number() )
+ .arg( err.message() ) );
+ } else {
+ emit bugDetailsAvailable( m_bug, bugDetails );
+ }
+}
+
+#include "bugdetailsjob.moc"
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/bugdetailsjob.h b/kbugbuster/backend/bugdetailsjob.h
new file mode 100644
index 00000000..e07ad3a2
--- /dev/null
+++ b/kbugbuster/backend/bugdetailsjob.h
@@ -0,0 +1,32 @@
+#ifndef __bugdetailsjob_h__
+#define __bugdetailsjob_h__
+
+#include "bugjob.h"
+#include "bug.h"
+#include "bugdetails.h"
+#include "bugdetailspart.h"
+
+class BugDetailsJob : public BugJob
+{
+ Q_OBJECT
+ public:
+ BugDetailsJob( BugServer * );
+ virtual ~BugDetailsJob();
+
+ void start( const Bug &bug );
+
+ signals:
+ void bugDetailsAvailable( const Bug &bug, const BugDetails &details );
+
+ protected:
+ virtual void process( const QByteArray &data );
+
+ private:
+ Bug m_bug;
+};
+
+#endif
+
+/*
+ * vim:ts=4:sw=4:et
+ */
diff --git a/kbugbuster/backend/bugdetailspart.h b/kbugbuster/backend/bugdetailspart.h
new file mode 100644
index 00000000..483057c8
--- /dev/null
+++ b/kbugbuster/backend/bugdetailspart.h
@@ -0,0 +1,21 @@
+#ifndef BUGDETAILSPART_H
+#define BUGDETAILSPART_H
+
+#include <qvaluelist.h>
+#include <qdatetime.h>
+
+struct BugDetailsPart
+{
+ typedef QValueList<BugDetailsPart> List;
+
+ BugDetailsPart () {}
+ BugDetailsPart( const Person &_sender, const QDateTime &_date,
+ const QString &_text )
+ : sender( _sender ), date( _date ), text( _text ) {}
+
+ Person sender;
+ QDateTime date;
+ QString text;
+};
+
+#endif
diff --git a/kbugbuster/backend/bugimpl.h b/kbugbuster/backend/bugimpl.h
new file mode 100644
index 00000000..630105a1
--- /dev/null
+++ b/kbugbuster/backend/bugimpl.h
@@ -0,0 +1,36 @@
+#ifndef __bug_impl_h__
+#define __bug_impl_h__
+
+#include "person.h"
+#include "bug.h"
+
+#include <kurl.h>
+#include <ksharedptr.h>
+
+struct BugImpl : public KShared
+{
+public:
+ BugImpl( const QString &_title, const Person &_submitter, QString _number,
+ uint _age, Bug::Severity _severity, Person _developerTODO,
+ Bug::Status _status, const Bug::BugMergeList& _mergedWith )
+ : age( _age ), title( _title ), submitter( _submitter ), number( _number ),
+ severity( _severity ), developerTODO( _developerTODO ),
+ status( _status ), mergedWith( _mergedWith )
+ {
+ }
+
+ uint age;
+ QString title;
+ Person submitter;
+ QString number;
+ Bug::Severity severity;
+ Person developerTODO;
+ Bug::Status status;
+
+ Bug::BugMergeList mergedWith;
+};
+
+#endif
+
+// vim: set sw=4 ts=4 sts=4 et:
+
diff --git a/kbugbuster/backend/bugjob.cpp b/kbugbuster/backend/bugjob.cpp
new file mode 100644
index 00000000..6f32fb97
--- /dev/null
+++ b/kbugbuster/backend/bugjob.cpp
@@ -0,0 +1,97 @@
+
+#include "bugjob.h"
+
+#include "kbbprefs.h"
+
+#include <kio/job.h>
+
+#include <string.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+BugJob::BugJob( BugServer *server )
+ : Job( false ), mServer( server )
+{
+}
+
+BugJob::~BugJob()
+{
+}
+
+void BugJob::start( const KURL &url )
+{
+ kdDebug() << "BugJob::start(): " << url.url() << endl;
+
+ if ( KBBPrefs::instance()->mDebugMode ) {
+ BugSystem::saveQuery( url );
+ }
+
+ // ### obey post, if necessary
+
+ KIO::Job *job = KIO::get( url, true /*always 'reload=true', we have our own cache*/, false );
+
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ this, SLOT( ioResult( KIO::Job * ) ) );
+ connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
+ this, SLOT( ioData( KIO::Job *, const QByteArray & ) ) );
+ connect( job, SIGNAL( infoMessage( KIO::Job *, const QString & ) ),
+ this, SLOT( ioInfoMessage( KIO::Job *, const QString & ) ) );
+ connect( job, SIGNAL( percent( KIO::Job *, unsigned long ) ),
+ this, SLOT( ioInfoPercent( KIO::Job *, unsigned long ) ) );
+}
+
+void BugJob::ioResult( KIO::Job *job )
+{
+ m_error = job->error();
+ m_errorText = job->errorText();
+
+ if ( job->error() )
+ {
+ emit error( m_errorText );
+ BugSystem::self()->unregisterJob(this);
+ this->kill();
+ return;
+ }
+
+ infoMessage( i18n( "Parsing..." ) );
+
+#if 0
+ kdDebug() << "--START:" << m_data << ":END--" << endl;
+#endif
+
+ if ( KBBPrefs::instance()->mDebugMode ) {
+ BugSystem::saveResponse( m_data );
+ }
+
+ process( m_data );
+ infoMessage( i18n( "Ready." ) );
+
+ emit jobEnded( this );
+
+ delete this;
+}
+
+void BugJob::ioData( KIO::Job *, const QByteArray &data )
+{
+ unsigned int start = m_data.size();
+
+ m_data.resize( m_data.size() + data.size() );
+ memcpy( m_data.data() + start, data.data(), data.size() );
+}
+
+void BugJob::ioInfoMessage( KIO::Job *, const QString &_text )
+{
+ QString text = _text;
+ emit infoMessage( text );
+}
+
+void BugJob::ioInfoPercent( KIO::Job *, unsigned long percent )
+{
+ emit infoPercent( percent );
+}
+
+#include "bugjob.moc"
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/bugjob.h b/kbugbuster/backend/bugjob.h
new file mode 100644
index 00000000..2c439810
--- /dev/null
+++ b/kbugbuster/backend/bugjob.h
@@ -0,0 +1,45 @@
+#ifndef KBB_BUGJOB_H
+#define KBB_BUGJOB_H
+
+#include <kio/jobclasses.h>
+
+#include "bugserver.h"
+
+class BugJob : public KIO::Job
+{
+ Q_OBJECT
+ public:
+ BugJob( BugServer * );
+ virtual ~BugJob();
+
+ BugServer *server() const { return mServer; }
+
+ signals:
+ void infoMessage( const QString &text );
+ void infoPercent( unsigned long percent );
+ void error( const QString &text );
+ void jobEnded( BugJob * );
+
+ protected:
+ void start( const KURL &url /*, const KParts::URLArgs &args = KParts::URLArgs()*/ );
+
+ virtual void process( const QByteArray &data ) = 0;
+
+ private slots:
+ void ioResult( KIO::Job *job );
+
+ void ioData( KIO::Job *job, const QByteArray &data );
+
+ void ioInfoMessage( KIO::Job *job, const QString &text );
+
+ void ioInfoPercent( KIO::Job *job, unsigned long percent );
+
+ private:
+ QByteArray m_data;
+ BugServer *mServer;
+};
+
+#endif
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/buglistjob.cpp b/kbugbuster/backend/buglistjob.cpp
new file mode 100644
index 00000000..e1dea2f4
--- /dev/null
+++ b/kbugbuster/backend/buglistjob.cpp
@@ -0,0 +1,75 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+
+#include "buglistjob.h"
+#include "bug.h"
+#include "bugimpl.h"
+#include "packageimpl.h"
+#include "bugserver.h"
+#include "domprocessor.h"
+#include "htmlparser.h"
+
+#include <kdebug.h>
+
+#include <qbuffer.h>
+#include <qregexp.h>
+
+BugListJob::BugListJob( BugServer *server )
+ : BugJob( server )
+{
+}
+
+BugListJob::~BugListJob()
+{
+}
+
+void BugListJob::start( const Package &pkg, const QString &component )
+{
+ m_package = pkg;
+ m_component = component;
+
+ BugJob::start( server()->bugListUrl( pkg, component ) );
+}
+
+
+void BugListJob::process( const QByteArray &data )
+{
+ Bug::List bugs;
+
+ KBB::Error err = server()->processor()->parseBugList( data, bugs );
+
+ if ( err ) {
+ emit error( i18n("Package %1: %2").arg( m_package.name() )
+ .arg( err.message() ) );
+ } else {
+ emit bugListAvailable( m_package, m_component, bugs );
+ }
+}
+
+
+#include "buglistjob.moc"
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/buglistjob.h b/kbugbuster/backend/buglistjob.h
new file mode 100644
index 00000000..0260d3a0
--- /dev/null
+++ b/kbugbuster/backend/buglistjob.h
@@ -0,0 +1,58 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+#ifndef BUGLISTJOB_H
+#define BUGLISTJOB_H
+
+#include "bugjob.h"
+#include "package.h"
+#include "bug.h"
+
+#include <qdom.h>
+
+class BugListJob : public BugJob
+{
+ Q_OBJECT
+ public:
+ BugListJob( BugServer * );
+ virtual ~BugListJob();
+
+ void start( const Package &pkg, const QString &component );
+
+ protected:
+ void process( const QByteArray &data );
+
+ signals:
+ void bugListAvailable( const Package &pkg, const QString &component, const Bug::List &bugs );
+
+ protected:
+ Package m_package;
+ QString m_component;
+};
+
+
+#endif
+
+/*
+ * vim:ts=4:sw=4:et
+ */
diff --git a/kbugbuster/backend/bugmybugsjob.cpp b/kbugbuster/backend/bugmybugsjob.cpp
new file mode 100644
index 00000000..92aa4448
--- /dev/null
+++ b/kbugbuster/backend/bugmybugsjob.cpp
@@ -0,0 +1,78 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2002-2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2004 Martijn Klingens <klingens@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+
+#include "bugmybugsjob.h"
+#include "bug.h"
+#include "bugimpl.h"
+#include "packageimpl.h"
+#include "bugserver.h"
+#include "domprocessor.h"
+#include "htmlparser.h"
+
+#include <kdebug.h>
+
+#include <qbuffer.h>
+#include <qregexp.h>
+
+BugMyBugsJob::BugMyBugsJob( BugServer *server )
+: BugJob( server )
+{
+}
+
+BugMyBugsJob::~BugMyBugsJob()
+{
+}
+
+void BugMyBugsJob::start()
+{
+ KURL url = server()->serverConfig().baseUrl();
+ url.setFileName( "buglist.cgi" );
+ url.setQuery( "bug_status=NEW&bug_status=ASSIGNED&bug_status=UNCONFIRMED&bug_status=REOPENED" );
+ url.addQueryItem( "email1", server()->serverConfig().user() );
+ url.addQueryItem( "emailtype1", "exact" );
+ url.addQueryItem( "emailassigned_to1", "1" );
+ url.addQueryItem( "emailreporter1", "1" );
+ url.addQueryItem( "format", "rdf" );
+ BugJob::start( url );
+}
+
+void BugMyBugsJob::process( const QByteArray &data )
+{
+ Bug::List bugs;
+
+ Processor *processor = new RdfProcessor( server() );
+ KBB::Error err = processor->parseBugList( data, bugs );
+ delete processor;
+
+ if ( err )
+ emit error( i18n( "My Bugs: %2" ).arg( err.message() ) );
+ else
+ emit bugListAvailable( i18n( "My Bugs" ), bugs );
+}
+
+#include "bugmybugsjob.moc"
+
+// vim: set sw=4 ts=4 et:
+
diff --git a/kbugbuster/backend/bugmybugsjob.h b/kbugbuster/backend/bugmybugsjob.h
new file mode 100644
index 00000000..f51ee229
--- /dev/null
+++ b/kbugbuster/backend/bugmybugsjob.h
@@ -0,0 +1,52 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2002-2003 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2004 Martijn Klingens <klingens@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+
+#ifndef BUGMYBUGSJOB_H
+#define BUGMYBUGSJOB_H
+
+#include "bugjob.h"
+#include "bug.h"
+
+class BugMyBugsJob : public BugJob
+{
+ Q_OBJECT
+
+public:
+ BugMyBugsJob( BugServer * );
+ virtual ~BugMyBugsJob();
+
+ void start();
+
+protected:
+ void process( const QByteArray &data );
+
+signals:
+ void bugListAvailable( const QString &label, const Bug::List &bugs );
+};
+
+#endif
+
+// vim: set ts=4 sw=4 et:
+
diff --git a/kbugbuster/backend/bugserver.cpp b/kbugbuster/backend/bugserver.cpp
new file mode 100644
index 00000000..0e6a70e6
--- /dev/null
+++ b/kbugbuster/backend/bugserver.cpp
@@ -0,0 +1,412 @@
+/*
+ This file is part of KBugBuster.
+ Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+
+#include "bugserver.h"
+#include "kbbprefs.h"
+#include "rdfprocessor.h"
+#include "bugcache.h"
+#include "bugcommand.h"
+#include "mailsender.h"
+#include "bugserverconfig.h"
+#include "htmlparser.h"
+
+#include <kstandarddirs.h>
+#include <ksimpleconfig.h>
+#include <kdebug.h>
+
+BugServer::BugServer()
+{
+ init();
+}
+
+BugServer::BugServer( const BugServerConfig &cfg )
+ : mServerConfig( cfg )
+{
+ init();
+}
+
+void BugServer::init()
+{
+ mCache = new BugCache( identifier() );
+
+ QString commandsFile = locateLocal( "appdata", identifier() + "commands" );
+ mCommandsFile = new KSimpleConfig( commandsFile );
+
+ QString bugzilla = mServerConfig.bugzillaVersion();
+
+ if ( bugzilla == "KDE" ) mProcessor = new DomProcessor( this );
+ else if ( bugzilla == "2.10" ) mProcessor = new HtmlParser_2_10( this );
+ else if ( bugzilla == "2.14.2" ) mProcessor = new HtmlParser_2_14_2( this );
+ else if ( bugzilla == "2.17.1" ) mProcessor = new HtmlParser_2_17_1( this );
+ else mProcessor = new HtmlParser( this );
+
+ loadCommands();
+}
+
+BugServer::~BugServer()
+{
+ saveCommands();
+
+ delete mProcessor;
+ delete mCommandsFile;
+ delete mCache;
+}
+
+void BugServer::setServerConfig( const BugServerConfig &cfg )
+{
+ mServerConfig = cfg;
+}
+
+BugServerConfig &BugServer::serverConfig()
+{
+ return mServerConfig;
+}
+
+QString BugServer::identifier()
+{
+ QString id = mServerConfig.baseUrl().host();
+ return id;
+}
+
+Processor *BugServer::processor() const
+{
+ return mProcessor;
+}
+
+KURL BugServer::packageListUrl()
+{
+ KURL url = mServerConfig.baseUrl();
+
+ mProcessor->setPackageListQuery( url );
+
+ return url;
+}
+
+KURL BugServer::bugListUrl( const Package &product, const QString &component )
+{
+ KURL url = mServerConfig.baseUrl();
+
+ mProcessor->setBugListQuery( url, product, component );
+
+ return url;
+}
+
+KURL BugServer::bugDetailsUrl( const Bug &bug )
+{
+ KURL url = mServerConfig.baseUrl();
+
+ mProcessor->setBugDetailsQuery( url, bug );
+
+ return url;
+}
+
+KURL BugServer::bugLink( const Bug &bug )
+{
+ KURL url = mServerConfig.baseUrl();
+
+ url.setFileName( "show_bug.cgi" );
+ url.setQuery( "id=" + bug.number() );
+
+ kdDebug() << "URL: " << url.url() << endl;
+
+ return url;
+}
+
+KURL BugServer::attachmentViewLink( const QString &id )
+{
+ KURL url = mServerConfig.baseUrl();
+
+ url.setFileName( "attachment.cgi" );
+ url.setQuery( "id=" + id + "&action=view" );
+
+ return url;
+}
+
+KURL BugServer::attachmentEditLink( const QString &id )
+{
+ KURL url = mServerConfig.baseUrl();
+
+ url.setFileName( "attachment.cgi" );
+ url.setQuery( "id=" + id + "&action=edit" );
+
+ return url;
+}
+
+Bug::Status BugServer::bugStatus( const QString &str )
+{
+ if ( str == "UNCONFIRMED" ) {
+ return Bug::Unconfirmed;
+ } else if ( str == "NEW" ) {
+ return Bug::New;
+ } else if ( str == "ASSIGNED" ) {
+ return Bug::Assigned;
+ } else if ( str == "REOPENED" ) {
+ return Bug::Reopened;
+ } else if ( str == "RESOLVED" ) {
+ return Bug::Closed;
+ } else if ( str == "VERIFIED" ) {
+ return Bug::Closed;
+ } else if ( str == "CLOSED" ) {
+ return Bug::Closed;
+ } else {
+ return Bug::StatusUndefined;
+ }
+}
+
+Bug::Severity BugServer::bugSeverity( const QString &str )
+{
+ if ( str == "critical" ) {
+ return Bug::Critical;
+ } else if ( str == "grave" ) {
+ return Bug::Grave;
+ } else if ( str == "major" ) {
+ return Bug::Major;
+ } else if ( str == "crash" ) {
+ return Bug::Crash;
+ } else if ( str == "normal" ) {
+ return Bug::Normal;
+ } else if ( str == "minor" ) {
+ return Bug::Minor;
+ } else if ( str == "wishlist" ) {
+ return Bug::Wishlist;
+ } else {
+ return Bug::SeverityUndefined;
+ }
+}
+
+void BugServer::readConfig( KConfig * /*config*/ )
+{
+}
+
+void BugServer::writeConfig( KConfig * /*config*/ )
+{
+}
+
+bool BugServer::queueCommand( BugCommand *cmd )
+{
+ // mCommands[bug] is a QPtrList. Get or create, set to autodelete, then append command.
+ mCommands[cmd->bug().number()].setAutoDelete( true );
+ QPtrListIterator<BugCommand> cmdIt( mCommands[cmd->bug().number()] );
+ for ( ; cmdIt.current(); ++cmdIt )
+ if ( cmdIt.current()->type() == cmd->type() )
+ return false;
+ mCommands[cmd->bug().number()].append( cmd );
+ return true;
+}
+
+QPtrList<BugCommand> BugServer::queryCommands( const Bug &bug ) const
+{
+ CommandsMap::ConstIterator it = mCommands.find( bug.number() );
+ if (it == mCommands.end()) return QPtrList<BugCommand>();
+ else return *it;
+}
+
+bool BugServer::hasCommandsFor( const Bug &bug ) const
+{
+ CommandsMap::ConstIterator it = mCommands.find( bug.number() );
+ return it != mCommands.end();
+}
+
+void BugServer::sendCommands( MailSender *mailer, const QString &senderName,
+ const QString &senderEmail, bool sendBCC,
+ const QString &recipient )
+{
+ // Disable mail commands for non-KDE servers
+ if ( mServerConfig.baseUrl() != KURL( "http://bugs.kde.org" ) ) return;
+
+ QString controlText;
+
+ // For each bug that has commands.....
+ CommandsMap::ConstIterator it;
+ for(it = mCommands.begin(); it != mCommands.end(); ++it ) {
+ Bug bug;
+ Package pkg;
+ // And for each command....
+ QPtrListIterator<BugCommand> cmdIt( *it );
+ for ( ; cmdIt.current() ; ++cmdIt ) {
+ BugCommand* cmd = cmdIt.current();
+ bug = cmd->bug();
+ if (!cmd->package().isNull())
+ pkg = cmd->package();
+ if (!cmd->controlString().isNull()) {
+ kdDebug() << "control@bugs.kde.org: " << cmd->controlString() << endl;
+ controlText += cmd->controlString() + "\n";
+ } else {
+ kdDebug() << cmd->mailAddress() << ": " << cmd->mailText() << endl;
+ // ### hm, should probably re-use the existing mailer instance and
+ // implement message queueing for smtp
+ MailSender *directMailer = mailer->clone();
+#if 0
+ connect( directMailer, SIGNAL( status( const QString & ) ),
+ this, SIGNAL( infoMessage( const QString & ) ) );
+#endif
+ if (!directMailer->send( senderName, senderEmail, cmd->mailAddress(),
+ cmd->bug().title().prepend( "Re: " ),
+ cmd->mailText(), sendBCC, recipient )) {
+ delete mailer;
+ return;
+ }
+ }
+ }
+ if (!bug.isNull()) {
+ mCommandsFile->deleteGroup( bug.number(), true ); // done, remove command
+ mCache->invalidateBugDetails( bug );
+ if ( !pkg.isNull() ) {
+ mCache->invalidateBugList( pkg, QString::null ); // the status of the bug comes from the buglist...
+
+ QStringList::ConstIterator it2;
+ for (it2 = pkg.components().begin();it2 != pkg.components().end();++it2) {
+ mCache->invalidateBugList( pkg, (*it2) ); // the status of the bug comes from the buglist...
+ }
+ }
+ }
+ }
+
+ if (!controlText.isEmpty()) {
+ kdDebug() << "control@bugs.kde.org doesn't work anymore" << endl;
+#if 0
+ if ( !mailer->send( senderName, senderEmail, "control@bugs.kde.org",
+ i18n("Mail generated by KBugBuster"), controlText,
+ sendBCC, recipient ))
+ return;
+#endif
+ } else {
+ delete mailer;
+ }
+
+ mCommands.clear();
+}
+
+void BugServer::clearCommands( const QString &bug )
+{
+ mCommands.remove( bug );
+ mCommandsFile->deleteGroup( bug, true );
+}
+
+bool BugServer::commandsPending() const
+{
+ if ( mCommands.count() > 0 ) return true;
+ else return false;
+}
+
+QStringList BugServer::listCommands() const
+{
+ QStringList result;
+ CommandsMap::ConstIterator it;
+ for(it = mCommands.begin(); it != mCommands.end(); ++it ) {
+ QPtrListIterator<BugCommand> cmdIt( *it );
+ for ( ; cmdIt.current() ; ++cmdIt ) {
+ BugCommand* cmd = cmdIt.current();
+ if (!cmd->controlString().isNull())
+ result.append( i18n("Control command: %1").arg(cmd->controlString()) );
+ else
+ result.append( i18n("Mail to %1").arg(cmd->mailAddress()) );
+ }
+ }
+ return result;
+}
+
+QStringList BugServer::bugsWithCommands() const
+{
+ QStringList bugs;
+
+ CommandsMap::ConstIterator it;
+ for(it = mCommands.begin(); it != mCommands.end(); ++it ) {
+ bugs.append( it.key() );
+ }
+
+ return bugs;
+}
+
+void BugServer::saveCommands() const
+{
+ CommandsMap::ConstIterator it;
+ for(it = mCommands.begin(); it != mCommands.end(); ++it ) {
+ mCommandsFile->setGroup( it.key() );
+ QPtrListIterator<BugCommand> cmdIt( *it );
+ for ( ; cmdIt.current() ; ++cmdIt ) {
+ BugCommand* cmd = cmdIt.current();
+ cmd->save( mCommandsFile );
+ }
+ }
+
+ mCommandsFile->sync();
+}
+
+void BugServer::loadCommands()
+{
+ mCommands.clear();
+
+ QStringList bugs = mCommandsFile->groupList();
+ QStringList::ConstIterator it;
+ for( it = bugs.begin(); it != bugs.end(); ++it ) {
+ mCommandsFile->setGroup( *it );
+ QMap<QString, QString> entries = mCommandsFile->entryMap ( *it );
+ QMap<QString, QString>::ConstIterator it;
+ for( it = entries.begin(); it != entries.end(); ++it ) {
+ QString type = it.key();
+ BugCommand *cmd = BugCommand::load( mCommandsFile, type );
+ if ( cmd ) {
+ mCommands[cmd->bug().number()].setAutoDelete(true);
+ mCommands[cmd->bug().number()].append(cmd);
+ }
+ }
+ }
+}
+
+void BugServer::setPackages( const Package::List &packages )
+{
+ mPackages = packages;
+}
+
+const Package::List &BugServer::packages() const
+{
+ return mPackages;
+}
+
+void BugServer::setBugs( const Package &pkg, const QString &component,
+ const Bug::List &bugs )
+{
+ QPair<Package, QString> pkg_key = QPair<Package, QString>(pkg, component);
+ mBugs[ pkg_key ] = bugs;
+}
+
+const Bug::List &BugServer::bugs( const Package &pkg, const QString &component )
+{
+ QPair<Package, QString> pkg_key = QPair<Package, QString>(pkg, component);
+ return mBugs[ pkg_key ];
+}
+
+void BugServer::setBugDetails( const Bug &bug, const BugDetails &details )
+{
+ mBugDetails[ bug ] = details;
+}
+
+const BugDetails &BugServer::bugDetails( const Bug &bug )
+{
+ return mBugDetails[ bug ];
+}
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/bugserver.h b/kbugbuster/backend/bugserver.h
new file mode 100644
index 00000000..3b534fa0
--- /dev/null
+++ b/kbugbuster/backend/bugserver.h
@@ -0,0 +1,152 @@
+/*
+ This file is part of KBugBuster.
+ Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+#ifndef BUGSERVER_H
+#define BUGSERVER_H
+
+#include <qstring.h>
+
+#include <kurl.h>
+
+#include "bug.h"
+#include "package.h"
+#include "bugsystem.h"
+#include "bugserverconfig.h"
+
+class Processor;
+class BugCache;
+class MailSender;
+class BugServerConfig;
+
+class BugServer
+{
+ public:
+ BugServer();
+ BugServer( const BugServerConfig & );
+ ~BugServer();
+
+ /**
+ BugServer takes ownership of the BugServerConfig object.
+ */
+ void setServerConfig( const BugServerConfig & );
+ BugServerConfig &serverConfig();
+
+ QString identifier();
+
+ BugCache *cache() const { return mCache; }
+
+ KURL packageListUrl();
+
+ KURL bugListUrl( const Package &, const QString &component );
+
+ KURL bugDetailsUrl( const Bug & );
+
+ KURL bugLink( const Bug & );
+ KURL attachmentViewLink( const QString &id );
+ KURL attachmentEditLink( const QString &id );
+
+ Bug::Status bugStatus( const QString & );
+
+ Bug::Severity bugSeverity( const QString & );
+
+ Processor *processor() const;
+
+ void readConfig( KConfig * );
+
+ void writeConfig( KConfig * );
+
+ /**
+ Queue a new command.
+ */
+ bool queueCommand( BugCommand * );
+ /**
+ Return all the commands for a given bug.
+ */
+ QPtrList<BugCommand> queryCommands( const Bug & ) const;
+ /**
+ Return true if we have a least one command for this bug.
+ */
+ bool hasCommandsFor( const Bug &bug ) const;
+ /**
+ Send all commands (generate the mails).
+ */
+ void sendCommands( MailSender *, const QString &senderName,
+ const QString &senderEmail, bool sendBCC,
+ const QString &recipient );
+ /**
+ Forget all commands for a given bug.
+ */
+ void clearCommands( const QString &bug );
+ /**
+ Return true if any command has been created.
+ */
+ bool commandsPending() const;
+ /**
+ List all pending commands.
+ */
+ QStringList listCommands() const;
+ /**
+ Return numbers of all bugs having at least one command queued.
+ */
+ QStringList bugsWithCommands() const;
+
+ void saveCommands() const;
+ void loadCommands();
+
+ void setPackages( const Package::List & );
+ const Package::List &packages() const;
+
+ void setBugs( const Package &, const QString &component,
+ const Bug::List & );
+ const Bug::List &bugs( const Package &, const QString &component );
+
+ void setBugDetails( const Bug &, const BugDetails & );
+ const BugDetails &bugDetails( const Bug & );
+
+ private:
+ void init();
+
+ BugServerConfig mServerConfig;
+
+ Processor *mProcessor;
+
+ BugCache *mCache;
+
+ Package::List mPackages;
+ // Map package -> list of bugs
+ typedef QMap< QPair<Package, QString>, Bug::List > BugListMap;
+ BugListMap mBugs;
+ // Map bug -> bug details (i.e. contents of the report)
+ typedef QMap< Bug, BugDetails > BugDetailsMap;
+ BugDetailsMap mBugDetails;
+ // Map bug-number -> list of commands
+ typedef QMap< QString, QPtrList<BugCommand> > CommandsMap;
+ CommandsMap mCommands;
+
+ KSimpleConfig *mCommandsFile;
+};
+
+#endif
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/bugserverconfig.cpp b/kbugbuster/backend/bugserverconfig.cpp
new file mode 100644
index 00000000..0669c0ab
--- /dev/null
+++ b/kbugbuster/backend/bugserverconfig.cpp
@@ -0,0 +1,150 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+
+#include "bugserverconfig.h"
+
+#include "kbbprefs.h"
+
+#include <kdebug.h>
+#include <kconfig.h>
+
+BugServerConfig::BugServerConfig()
+{
+ mName = "KDE";
+ mBaseUrl = "http://bugs.kde.org";
+ mUser = "bugzilla@kde.org";
+ mBugzillaVersion = "KDE";
+}
+
+BugServerConfig::BugServerConfig( const QString &name, const KURL &baseUrl )
+ : mName( name ), mBaseUrl( baseUrl ), mBugzillaVersion( "KDE" )
+{
+}
+
+BugServerConfig::~BugServerConfig()
+{
+}
+
+void BugServerConfig::setName( const QString &name )
+{
+ mName = name;
+}
+
+QString BugServerConfig::name() const
+{
+ return mName;
+}
+
+void BugServerConfig::setBaseUrl( const KURL &baseUrl )
+{
+ mBaseUrl = baseUrl;
+}
+
+KURL BugServerConfig::baseUrl() const
+{
+ return mBaseUrl;
+}
+
+void BugServerConfig::setUser( const QString &user )
+{
+ mUser = user;
+}
+
+QString BugServerConfig::user() const
+{
+ return mUser;
+}
+
+void BugServerConfig::setPassword( const QString &password )
+{
+ mPassword = password;
+}
+
+QString BugServerConfig::password() const
+{
+ return mPassword;
+}
+
+void BugServerConfig::setBugzillaVersion( const QString &s )
+{
+ mBugzillaVersion = s;
+}
+
+QString BugServerConfig::bugzillaVersion() const
+{
+ return mBugzillaVersion;
+}
+
+QStringList BugServerConfig::bugzillaVersions()
+{
+ QStringList v;
+
+ v << "2.10";
+ v << "2.14.2";
+ v << "2.16.2";
+ v << "2.17.1";
+ v << "KDE";
+ v << "Bugworld";
+
+ return v;
+}
+
+void BugServerConfig::readConfig( KConfig *cfg, const QString &name )
+{
+ mName = name;
+
+ cfg->setGroup( "BugServer " + name );
+
+ mBaseUrl = cfg->readEntry( "BaseUrl" );
+ mUser = cfg->readEntry( "User" );
+ mPassword = cfg->readEntry( "Password" );
+
+ mBugzillaVersion = cfg->readEntry( "BugzillaVersion", "KDE" );
+
+ mRecentPackages = cfg->readListEntry( "RecentPackages" );
+ mCurrentPackage = cfg->readEntry( "CurrentPackage" );
+ mCurrentComponent = cfg->readEntry( "CurrentComponent" );
+ mCurrentBug = cfg->readEntry( "CurrentBug" );
+}
+
+void BugServerConfig::writeConfig( KConfig *cfg )
+{
+ cfg->setGroup( "BugServer " + mName );
+
+ cfg->writeEntry( "BaseUrl", mBaseUrl.url() );
+ cfg->writeEntry( "User", mUser );
+ cfg->writeEntry( "Password", mPassword );
+
+ cfg->writeEntry( "BugzillaVersion", mBugzillaVersion );
+
+ cfg->writeEntry( "RecentPackages", mRecentPackages );
+ cfg->writeEntry( "CurrentPackage", mCurrentPackage );
+ cfg->writeEntry( "CurrentComponent", mCurrentComponent );
+ cfg->writeEntry( "CurrentBug", mCurrentBug );
+}
+
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/bugserverconfig.h b/kbugbuster/backend/bugserverconfig.h
new file mode 100644
index 00000000..2c9be828
--- /dev/null
+++ b/kbugbuster/backend/bugserverconfig.h
@@ -0,0 +1,91 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+#ifndef BUGSERVERCONFIG_H
+#define BUGSERVERCONFIG_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+
+#include <kurl.h>
+
+class KConfig;
+
+class BugServerConfig
+{
+ public:
+ BugServerConfig();
+ BugServerConfig( const QString &name, const KURL &baseUrl );
+ ~BugServerConfig();
+
+ void setName( const QString &name );
+ QString name() const;
+
+ void setBaseUrl( const KURL &url );
+ KURL baseUrl() const;
+
+ void setUser( const QString &user );
+ QString user() const;
+
+ void setPassword( const QString &password );
+ QString password() const;
+
+ void readConfig( KConfig *, const QString &name );
+ void writeConfig( KConfig * );
+
+ static QStringList bugzillaVersions();
+
+ void setBugzillaVersion( const QString & );
+ QString bugzillaVersion() const;
+
+ void setRecentPackages( const QStringList &v ) { mRecentPackages = v; }
+ QStringList recentPackages() const { return mRecentPackages; }
+
+ void setCurrentPackage( const QString &v ) { mCurrentPackage = v; }
+ QString currentPackage() const { return mCurrentPackage; }
+
+ void setCurrentComponent( const QString &v ) { mCurrentComponent = v; }
+ QString currentComponent() const { return mCurrentComponent; }
+
+ void setCurrentBug( const QString &v ) { mCurrentBug = v; }
+ QString currentBug() const { return mCurrentBug; }
+
+ private:
+ QString mName;
+ KURL mBaseUrl;
+ QString mUser;
+ QString mPassword;
+
+ QString mBugzillaVersion;
+
+ QStringList mRecentPackages;
+ QString mCurrentPackage;
+ QString mCurrentComponent;
+ QString mCurrentBug;
+};
+
+#endif
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/bugsystem.cpp b/kbugbuster/backend/bugsystem.cpp
new file mode 100644
index 00000000..26432e08
--- /dev/null
+++ b/kbugbuster/backend/bugsystem.cpp
@@ -0,0 +1,436 @@
+
+#include "bugsystem.h"
+#include "packagelistjob.h"
+#include "buglistjob.h"
+#include "bugmybugsjob.h"
+#include "bugdetailsjob.h"
+#include "bugcommand.h"
+
+#include <kstaticdeleter.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kemailsettings.h>
+#include <kstandarddirs.h>
+#include <ksimpleconfig.h>
+#include <kconfig.h>
+
+#include "packageimpl.h"
+#include "bugimpl.h"
+#include "bugdetailsimpl.h"
+#include "mailsender.h"
+#include "kbbprefs.h"
+#include "bugserver.h"
+#include "bugserverconfig.h"
+#include "bugcache.h"
+
+KStaticDeleter<BugSystem> bssd;
+
+BugSystem *BugSystem::s_self = 0;
+
+QString BugSystem::mLastResponse;
+
+BugSystem *BugSystem::self()
+{
+ if ( !s_self )
+ s_self = bssd.setObject( s_self, new BugSystem );
+
+ return s_self;
+}
+
+BugSystem::BugSystem()
+ : m_disconnected( false )
+{
+ mServer = 0;
+}
+
+BugSystem::~BugSystem()
+{
+ QValueList<BugServer *>::ConstIterator it;
+ for( it = mServerList.begin(); it != mServerList.end(); ++it ) {
+ delete *it;
+ }
+}
+
+BugCache *BugSystem::cache()const
+{
+ return mServer->cache();
+}
+
+void BugSystem::setDisconnected( bool disconnected )
+{
+ m_disconnected = disconnected;
+}
+
+bool BugSystem::disconnected() const
+{
+ return m_disconnected;
+}
+
+void BugSystem::retrievePackageList()
+{
+ mServer->setPackages( mServer->cache()->loadPackageList() );
+
+ if( !mServer->packages().isEmpty() ) {
+ emit packageListAvailable( mServer->packages() );
+ } else {
+ emit packageListCacheMiss();
+
+ if ( !m_disconnected )
+ {
+ emit packageListLoading();
+
+ PackageListJob *job = new PackageListJob( mServer );
+ connect( job, SIGNAL( packageListAvailable( const Package::List & ) ),
+ this, SIGNAL( packageListAvailable( const Package::List & ) ) );
+ connect( job, SIGNAL( packageListAvailable( const Package::List & ) ),
+ this, SLOT( setPackageList( const Package::List & ) ) );
+ connect( job, SIGNAL( error( const QString & ) ),
+ this, SIGNAL( loadingError( const QString & ) ) );
+ connectJob( job );
+
+ registerJob( job );
+
+ job->start();
+ }
+ }
+}
+
+void BugSystem::retrieveBugList( const Package &pkg, const QString &component )
+{
+ kdDebug() << "BugSystem::retrieveBugList(): " << pkg.name() << endl;
+
+ if ( pkg.isNull() )
+ return;
+
+ mServer->setBugs( pkg, component,
+ mServer->cache()->loadBugList( pkg, component,
+ m_disconnected ) );
+
+ // Since the GUI stops showing the splash widget after this signal,
+ // we should not emit anything on a cache miss...
+ if( !mServer->bugs( pkg, component ).isEmpty() )
+ emit bugListAvailable( pkg, component, mServer->bugs( pkg, component ) );
+ else
+ {
+ emit bugListCacheMiss( pkg );
+
+ if ( !m_disconnected )
+ {
+ kdDebug() << "BugSystem::retrieveBugList() starting job" << endl;
+ emit bugListLoading( pkg, component );
+
+ BugListJob *job = new BugListJob( mServer );
+ connect( job, SIGNAL( bugListAvailable( const Package &, const QString &, const Bug::List & ) ),
+ this, SIGNAL( bugListAvailable( const Package &, const QString &, const Bug::List & ) ) );
+ connect( job, SIGNAL( bugListAvailable( const Package &, const QString &, const Bug::List & ) ),
+ this, SLOT( setBugList( const Package &, const QString &, const Bug::List & ) ) );
+ connect( job, SIGNAL( error( const QString & ) ),
+ this, SIGNAL( loadingError( const QString & ) ) );
+ connectJob( job );
+
+ registerJob( job );
+
+ job->start( pkg, component );
+ }
+ }
+}
+
+void BugSystem::retrieveMyBugsList()
+{
+ kdDebug() << k_funcinfo << endl;
+
+ if ( m_disconnected )
+ {
+ // This function is not cached for now
+ emit bugListCacheMiss( i18n( "My Bugs" ) );
+ }
+ else
+ {
+ kdDebug() << k_funcinfo << "Starting job" << endl;
+
+ emit bugListLoading( i18n( "Retrieving My Bugs list..." ) );
+
+ BugMyBugsJob *job = new BugMyBugsJob( mServer );
+
+ connect( job, SIGNAL( bugListAvailable( const QString &, const Bug::List & ) ),
+ this, SIGNAL( bugListAvailable( const QString &, const Bug::List & ) ) );
+ connect( job, SIGNAL( error( const QString & ) ),
+ this, SIGNAL( loadingError( const QString & ) ) );
+ connectJob( job );
+
+ registerJob( job );
+
+ job->start();
+ }
+}
+
+void BugSystem::retrieveBugDetails( const Bug &bug )
+{
+ if ( bug.isNull() )
+ return;
+
+ kdDebug() << "BugSystem::retrieveBugDetails(): " << bug.number() << endl;
+
+ mServer->setBugDetails( bug, mServer->cache()->loadBugDetails( bug ) );
+
+ if ( !mServer->bugDetails( bug ).isNull() ) {
+// kdDebug() << "Found in cache." << endl;
+ emit bugDetailsAvailable( bug, mServer->bugDetails( bug ) );
+ } else {
+// kdDebug() << "Not found in cache." << endl;
+ emit bugDetailsCacheMiss( bug );
+
+ if ( !m_disconnected ) {
+ emit bugDetailsLoading( bug );
+
+ BugDetailsJob *job = new BugDetailsJob( mServer );
+ connect( job, SIGNAL( bugDetailsAvailable( const Bug &, const BugDetails & ) ),
+ this, SIGNAL( bugDetailsAvailable( const Bug &, const BugDetails & ) ) );
+ connect( job, SIGNAL( bugDetailsAvailable( const Bug &, const BugDetails & ) ),
+ this, SLOT( setBugDetails( const Bug &, const BugDetails & ) ) );
+ connect( job, SIGNAL( error( const QString & ) ),
+ this, SIGNAL( bugDetailsLoadingError() ) );
+ connectJob( job );
+
+ registerJob( job );
+
+ job->start( bug );
+ }
+ }
+}
+
+void BugSystem::connectJob( BugJob *job )
+{
+ connect( job, SIGNAL( infoMessage( const QString & ) ),
+ this, SIGNAL( infoMessage( const QString & ) ) );
+ connect( job, SIGNAL( infoPercent( unsigned long ) ),
+ this, SIGNAL( infoPercent( unsigned long ) ) );
+ connect( job, SIGNAL( jobEnded( BugJob * ) ),
+ SLOT( unregisterJob( BugJob * ) ) );
+}
+
+void BugSystem::setPackageList( const Package::List &pkgs )
+{
+ mServer->setPackages( pkgs );
+
+ mServer->cache()->savePackageList( pkgs );
+}
+
+void BugSystem::setBugList( const Package &pkg, const QString &component, const Bug::List &bugs )
+{
+ mServer->setBugs( pkg, component, bugs );
+ mServer->cache()->saveBugList( pkg, component, bugs );
+}
+
+void BugSystem::setBugDetails( const Bug &bug, const BugDetails &details )
+{
+ mServer->setBugDetails( bug , details );
+
+ mServer->cache()->saveBugDetails( bug, details );
+}
+
+Package::List BugSystem::packageList() const
+{
+ return mServer->packages();
+}
+
+Package BugSystem::package( const QString &pkgname ) const
+{
+ Package::List::ConstIterator it;
+ for( it = mServer->packages().begin(); it != mServer->packages().end(); ++it ) {
+ if( pkgname == (*it).name() ) return (*it);
+ }
+ return Package();
+}
+
+Bug BugSystem::bug( const Package &pkg, const QString &component, const QString &number ) const
+{
+ Bug::List bugs = mServer->bugs( pkg, component );
+
+ Bug::List::ConstIterator it;
+ for( it = bugs.begin(); it != bugs.end(); ++it ) {
+ if( number == (*it).number() ) return (*it);
+ }
+ return Bug();
+}
+
+void BugSystem::queueCommand( BugCommand *cmd )
+{
+ if ( mServer->queueCommand( cmd ) ) emit commandQueued( cmd );
+}
+
+void BugSystem::clearCommands( const QString &bug )
+{
+ mServer->clearCommands( bug );
+
+ emit commandCanceled( bug );
+}
+
+void BugSystem::clearCommands()
+{
+ QStringList bugs = mServer->bugsWithCommands();
+
+ QStringList::ConstIterator it;
+ for( it = bugs.begin(); it != bugs.end(); ++it ) {
+ clearCommands( *it );
+ }
+}
+
+void BugSystem::sendCommands()
+{
+ QString recipient = KBBPrefs::instance()->mOverrideRecipient;
+ bool sendBCC = KBBPrefs::instance()->mSendBCC;
+
+ KEMailSettings emailSettings;
+ QString senderName = emailSettings.getSetting( KEMailSettings::RealName );
+ QString senderEmail = emailSettings.getSetting( KEMailSettings::EmailAddress );
+ QString smtpServer = emailSettings.getSetting( KEMailSettings::OutServer );
+
+ MailSender::MailClient client = (MailSender::MailClient)KBBPrefs::instance()->mMailClient;
+
+ // ### connect to signals
+ MailSender *mailer = new MailSender( client, smtpServer );
+ connect( mailer, SIGNAL( status( const QString & ) ),
+ SIGNAL( infoMessage( const QString & ) ) );
+
+ mServer->sendCommands( mailer, senderName, senderEmail, sendBCC, recipient );
+}
+
+void BugSystem::setServerList( const QValueList<BugServerConfig> &servers )
+{
+ if ( servers.isEmpty() ) return;
+
+ QString currentServer;
+ if ( mServer ) currentServer = mServer->serverConfig().name();
+ else currentServer = KBBPrefs::instance()->mCurrentServer;
+
+ killAllJobs();
+
+ QValueList<BugServer *>::ConstIterator serverIt;
+ for( serverIt = mServerList.begin(); serverIt != mServerList.end();
+ ++serverIt ) {
+ delete *serverIt;
+ }
+ mServerList.clear();
+
+ QValueList<BugServerConfig>::ConstIterator cfgIt;
+ for( cfgIt = servers.begin(); cfgIt != servers.end(); ++cfgIt ) {
+ mServerList.append( new BugServer( *cfgIt ) );
+ }
+
+ setCurrentServer( currentServer );
+}
+
+QValueList<BugServer *> BugSystem::serverList()
+{
+ return mServerList;
+}
+
+void BugSystem::setCurrentServer( const QString &name )
+{
+ killAllJobs();
+
+ BugServer *server = findServer( name );
+ if ( server ) {
+ mServer = server;
+ } else {
+ kdError() << "Server '" << name << "' not known." << endl;
+ if ( mServerList.isEmpty() ) {
+ kdError() << "Fatal error: server list empty." << endl;
+ } else {
+ mServer = mServerList.first();
+ }
+ }
+
+ if ( mServer ) {
+ KBBPrefs::instance()->mCurrentServer = mServer->serverConfig().name();
+ }
+}
+
+BugServer *BugSystem::findServer( const QString &name )
+{
+ QValueList<BugServer *>::ConstIterator serverIt;
+ for( serverIt = mServerList.begin(); serverIt != mServerList.end();
+ ++serverIt ) {
+ if ( (*serverIt)->serverConfig().name() == name ) return *serverIt;
+ }
+ return 0;
+}
+
+void BugSystem::saveQuery( const KURL &url )
+{
+ mLastResponse = "Query: " + url.url();
+ mLastResponse += "\n\n";
+}
+
+void BugSystem::saveResponse( const QByteArray &response )
+{
+ mLastResponse += response;
+}
+
+QString BugSystem::lastResponse()
+{
+ return mLastResponse;
+}
+
+void BugSystem::readConfig( KConfig *config )
+{
+ config->setGroup("Servers");
+ QStringList servers = config->readListEntry( "Servers" );
+
+ QValueList<BugServerConfig> serverList;
+
+ if ( servers.isEmpty() ) {
+ serverList.append( BugServerConfig() );
+ } else {
+ QStringList::ConstIterator it;
+ for( it = servers.begin(); it != servers.end(); ++it ) {
+ BugServerConfig cfg;
+ cfg.readConfig( config, *it );
+ serverList.append( cfg );
+ }
+ }
+
+ setServerList( serverList );
+}
+
+void BugSystem::writeConfig( KConfig *config )
+{
+ QValueList<BugServer *>::ConstIterator itServer;
+ QStringList servers;
+ QValueList<BugServer *> serverList = BugSystem::self()->serverList();
+ for( itServer = serverList.begin(); itServer != serverList.end();
+ ++itServer ) {
+ BugServerConfig serverConfig = (*itServer)->serverConfig();
+ servers.append( serverConfig.name() );
+ serverConfig.writeConfig( config );
+ }
+
+ config->setGroup("Servers");
+ config->writeEntry( "Servers", servers );
+}
+
+void BugSystem::registerJob( BugJob *job )
+{
+ mJobs.append( job );
+}
+
+void BugSystem::unregisterJob( BugJob *job )
+{
+ mJobs.removeRef( job );
+}
+
+void BugSystem::killAllJobs()
+{
+ BugJob *job;
+ for( job = mJobs.first(); job; job = mJobs.next() ) {
+ job->kill();
+ unregisterJob( job );
+ }
+}
+
+#include "bugsystem.moc"
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/bugsystem.h b/kbugbuster/backend/bugsystem.h
new file mode 100644
index 00000000..c573698b
--- /dev/null
+++ b/kbugbuster/backend/bugsystem.h
@@ -0,0 +1,146 @@
+#ifndef __bugsystem_h__
+#define __bugsystem_h__
+
+#include "package.h"
+#include "bug.h"
+#include "bugdetails.h"
+#include "bugcache.h"
+
+#include <kurl.h>
+
+#include <qobject.h>
+#include <qptrlist.h>
+#include <qmap.h>
+#include <qpair.h>
+
+class KConfig;
+
+class BugCommand;
+class BugServer;
+class BugServerConfig;
+class BugJob;
+
+class BugSystem : public QObject
+{
+ Q_OBJECT
+ friend class BugJob;
+ public:
+ BugSystem();
+ virtual ~BugSystem();
+
+ static BugSystem *self();
+
+ BugCache *cache()const;
+ BugServer *server() const { return mServer; }
+
+ /**
+ BugSystem takes ownership of the BugServerConfig objects.
+ */
+ void setServerList( const QValueList<BugServerConfig> &servers );
+ QValueList<BugServer *> serverList();
+
+ void setCurrentServer( const QString & );
+
+ void retrievePackageList();
+ void retrieveBugList( const Package &, const QString &component );
+ void retrieveBugDetails( const Bug & );
+
+ /**
+ * Load the bugs the user reported himself, or for which he is the assigned to person
+ */
+ void retrieveMyBugsList();
+
+ /**
+ Queue a new command.
+ */
+ void queueCommand( BugCommand * );
+ /**
+ Forget all commands for a given bug.
+ */
+ void clearCommands( const QString &bug );
+ /**
+ Forget all commands for all bugs.
+ */
+ void clearCommands();
+ /**
+ Send all commands (generate the mails).
+ */
+ void sendCommands();
+
+ void setDisconnected( bool );
+ bool disconnected() const;
+
+ Package::List packageList() const;
+
+ Package package( const QString &pkgname ) const;
+ Bug bug( const Package &pkg, const QString &component, const QString &number ) const;
+
+ static void saveQuery( const KURL &url );
+ static void saveResponse( const QByteArray &d );
+ static QString lastResponse();
+
+ void readConfig( KConfig * );
+ void writeConfig( KConfig * );
+
+ signals:
+ void packageListAvailable( const Package::List &pkgs );
+ void bugListAvailable( const Package &pkg, const QString &component, const Bug::List & );
+ void bugListAvailable( const QString &label, const Bug::List & );
+ void bugDetailsAvailable( const Bug &, const BugDetails & );
+
+ void packageListLoading();
+ void bugListLoading( const Package &, const QString &component );
+ void bugListLoading( const QString &label );
+ void bugDetailsLoading( const Bug & );
+
+ void packageListCacheMiss();
+ void bugListCacheMiss( const Package &package );
+ void bugListCacheMiss( const QString &label );
+ void bugDetailsCacheMiss( const Bug & );
+
+ void bugDetailsLoadingError();
+
+ void infoMessage( const QString &message );
+ void infoPercent( unsigned long percent );
+
+ void commandQueued( BugCommand * );
+ void commandCanceled( const QString & );
+
+ void loadingError( const QString &text );
+
+ protected:
+ BugServer *findServer( const QString &name );
+
+ void registerJob( BugJob * );
+
+ void connectJob( BugJob * );
+
+ void killAllJobs();
+
+ protected slots:
+ void unregisterJob( BugJob * );
+
+ private slots:
+ void setPackageList( const Package::List &pkgs );
+ void setBugList( const Package &pkg, const QString &component, const Bug::List &bugs );
+ void setBugDetails( const Bug &bug, const BugDetails &details );
+
+ private:
+ bool m_disconnected;
+
+ BugServer *mServer;
+
+ QValueList<BugServer *> mServerList;
+
+ QPtrList<BugJob> mJobs;
+
+ static BugSystem *s_self;
+
+ static QString mLastResponse;
+};
+
+#endif
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/domprocessor.cpp b/kbugbuster/backend/domprocessor.cpp
new file mode 100644
index 00000000..7643ca11
--- /dev/null
+++ b/kbugbuster/backend/domprocessor.cpp
@@ -0,0 +1,407 @@
+/*
+ This file is part of KBugBuster.
+ Copyright (c) 2002 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+
+#include "domprocessor.h"
+
+#include <qregexp.h>
+#include <qstylesheet.h>
+
+#include <kdebug.h>
+#include <kmdcodec.h>
+
+#include "bugserver.h"
+#include "packageimpl.h"
+#include "bugimpl.h"
+#include "bugdetailsimpl.h"
+#include "kbbprefs.h"
+
+DomProcessor::DomProcessor( BugServer *server )
+ : Processor( server )
+{
+}
+
+DomProcessor::~DomProcessor()
+{
+}
+
+KBB::Error DomProcessor::parsePackageList( const QByteArray &data,
+ Package::List &packages )
+{
+ QDomDocument doc;
+ if ( !doc.setContent( data ) ) {
+ return KBB::Error( "Error parsing xml response for package list request." );
+ }
+
+ QDomElement bugzilla = doc.documentElement();
+
+ if ( bugzilla.isNull() ) {
+ return KBB::Error( "No document in xml response." );
+ }
+
+ KBB::Error err = parseDomPackageList( bugzilla, packages );
+
+ return err;
+}
+
+KBB::Error DomProcessor::parseBugList( const QByteArray &data, Bug::List &bugs )
+{
+ QDomDocument doc;
+ if ( !doc.setContent( data ) ) {
+ return KBB::Error( "Error parsing xml response for bug list request" );
+ }
+
+ QDomElement bugzilla = doc.documentElement();
+
+ if ( bugzilla.isNull() ) {
+ return KBB::Error( "No document in xml response." );
+ }
+
+ KBB::Error err = parseDomBugList( bugzilla, bugs );
+
+ return err;
+}
+
+KBB::Error DomProcessor::parseBugDetails( const QByteArray &data,
+ BugDetails &bugDetails )
+{
+ QDomDocument doc;
+ if ( !doc.setContent( data ) ) {
+ return KBB::Error( "Error parsing xml response for bug details request." );
+ }
+
+ QDomElement bugzilla = doc.documentElement();
+
+ if ( bugzilla.isNull() ) {
+ return KBB::Error( "No document in xml response." );
+ }
+
+ QDomNode p;
+ for ( p = bugzilla.firstChild(); !p.isNull(); p = p.nextSibling() ) {
+ QDomElement bug = p.toElement();
+ if ( bug.tagName() != "bug" ) continue;
+
+ KBB::Error err = parseDomBugDetails( bug, bugDetails );
+
+ if ( err ) return err;
+ }
+
+ return KBB::Error();
+}
+
+
+KBB::Error DomProcessor::parseDomPackageList( const QDomElement &element,
+ Package::List &packages )
+{
+ QDomNode p;
+ for ( p = element.firstChild(); !p.isNull(); p = p.nextSibling() ) {
+ QDomElement bug = p.toElement();
+
+ if ( bug.tagName() != "product" ) continue;
+
+ QString pkgName = bug.attribute( "name" );
+ uint bugCount = 999;
+ Person maintainer;
+ QString description;
+ QStringList components;
+
+ QDomNode n;
+ for( n = bug.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ QDomElement e = n.toElement();
+ if ( e.tagName() == "descr" ) description= e.text().stripWhiteSpace();
+ if ( e.tagName() == "component" ) components += e.text().stripWhiteSpace();
+ }
+
+ Package pkg( new PackageImpl( pkgName, description, bugCount, maintainer, components ) );
+
+ if ( !pkg.isNull() ) {
+ packages.append( pkg );
+ }
+ }
+
+ return KBB::Error();
+}
+
+KBB::Error DomProcessor::parseDomBugList( const QDomElement &topElement,
+ Bug::List &bugs )
+{
+ QDomElement element;
+
+ if ( topElement.tagName() != "querybugids" ) {
+ QDomNode buglist = topElement.namedItem( "querybugids" );
+ element = buglist.toElement();
+ if ( element.isNull() ) {
+ return KBB::Error( "No querybugids element found." );
+ }
+ } else {
+ element = topElement;
+ }
+
+ QDomNode p;
+ for ( p = element.firstChild(); !p.isNull(); p = p.nextSibling() ) {
+ QDomElement hit = p.toElement();
+
+ kdDebug() << "DomProcessor::parseDomBugList(): tag: " << hit.tagName() << endl;
+
+ if ( hit.tagName() == "error" ) {
+ return KBB::Error( "Error: " + hit.text() );
+ } else if ( hit.tagName() != "hit" ) continue;
+
+ QString title;
+ QString submitterName;
+ QString submitterEmail;
+ QString bugNr;
+ Bug::Status status = Bug::StatusUndefined;
+ Bug::Severity severity = Bug::SeverityUndefined;
+ Person developerTodo;
+ Bug::BugMergeList mergedList;
+ uint age = 0xFFFFFFFF;
+
+ QDomNode n;
+ for ( n = hit.firstChild(); !n.isNull(); n = n.nextSibling() )
+ {
+ QDomElement e = n.toElement();
+
+ if ( e.tagName() == "bugid" )
+ bugNr = e.text();
+ else if ( e.tagName() == "status" )
+ status = server()->bugStatus( e.text() );
+ else if ( e.tagName() == "descr" )
+ title = e.text();
+ else if ( e.tagName() == "reporter" )
+ submitterEmail = e.text();
+ else if ( e.tagName() == "reporterName" )
+ submitterName = e.text();
+ else if ( e.tagName() == "severity" )
+ severity = Bug::stringToSeverity( e.text() );
+ else if ( e.tagName() == "creationdate" )
+ age = ( QDateTime::fromString( e.text(), Qt::ISODate ) ).daysTo( QDateTime::currentDateTime() );
+ }
+
+ Person submitter( submitterName, submitterEmail );
+
+ Bug bug( new BugImpl( title, submitter, bugNr, age, severity,
+ developerTodo, status, mergedList ) );
+
+ if ( !bug.isNull() ) {
+ bugs.append( bug );
+ }
+ }
+
+ return KBB::Error();
+}
+
+KBB::Error DomProcessor::parseDomBugDetails( const QDomElement &element,
+ BugDetails &bugDetails )
+{
+ if ( element.tagName() != "bug" ) return KBB::Error( "No <bug> tag found" );
+
+ BugDetailsPart::List parts;
+ QValueList<BugDetailsImpl::AttachmentDetails> attachments;
+
+ QString versionXml;
+ QString osXml;
+
+ QString version;
+ QString source;
+ QString compiler;
+ QString os;
+
+ QDomNode n;
+ for( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ QDomElement e = n.toElement();
+ if ( e.tagName() == "version" ) versionXml = e.text().stripWhiteSpace();
+ if ( e.tagName() == "op_sys" ) osXml = e.text().stripWhiteSpace();
+
+ if ( e.tagName() == "long_desc" ) {
+
+ QString encoding = e.attribute( "encoding" );
+
+ Person sender;
+ QDateTime date;
+ QString text;
+
+ QDomNode n2;
+ for( n2 = e.firstChild(); !n2.isNull(); n2 = n2.nextSibling() ) {
+ QDomElement e2 = n2.toElement();
+ if ( e2.tagName() == "who" ) {
+ sender = Person::parseFromString( e2.text() );
+ } else if ( e2.tagName() == "bug_when" ) {
+ date = parseDate( e2.text().stripWhiteSpace() );
+ } else if ( e2.tagName() == "thetext" ) {
+ QString in;
+ if ( encoding == "base64" ) {
+ in = KCodecs::base64Decode( e2.text().latin1() );
+ } else {
+ in = e2.text();
+ }
+
+ QString raw = QStyleSheet::escape( in );
+
+ if ( parts.isEmpty() )
+ {
+ QTextStream ts( &raw, IO_ReadOnly );
+ QString line;
+ while( !( line = ts.readLine() ).isNull() ) {
+ if ( parseAttributeLine( line, "Version", version ) ) continue;
+ if ( parseAttributeLine( line, "Installed from", source ) ) continue;
+ if ( parseAttributeLine( line, "Compiler", compiler ) ) continue;
+ if ( parseAttributeLine( line, "OS", os ) ) continue;
+
+ text += line + "\n";
+ }
+ } else {
+ text += raw;
+ }
+ QString bugBaseURL = server()->serverConfig().baseUrl().htmlURL();
+ text = "<pre>" + wrapLines( text ).replace( QRegExp( "(Created an attachment \\(id=([0-9]+)\\))" ),
+ "<a href=\"" + bugBaseURL + "/attachment.cgi?id=\\2&action=view\">\\1</a>" ) + "\n</pre>";
+ }
+ }
+
+ parts.prepend( BugDetailsPart( sender, date, text ) );
+ }
+
+ if ( e.tagName() == "attachment" ) {
+ QString attachid, date, desc;
+ for( QDomNode node = e.firstChild(); !node.isNull(); node = node.nextSibling() ) {
+ QDomElement e2 = node.toElement();
+ if ( e2.tagName() == "attachid" ) {
+ attachid = e2.text();
+ } else if ( e2.tagName() == "date" ) {
+ date = e2.text().stripWhiteSpace();
+ } else if ( e2.tagName() == "desc" ) {
+ desc = "<pre>" + wrapLines( QStyleSheet::escape(e2.text()) ) + "\n</pre>";
+ }
+ }
+ attachments.append( BugDetailsImpl::AttachmentDetails( desc, date, attachid ) );
+ }
+ }
+
+ if ( version.isEmpty() ) version = versionXml;
+ if ( os.isEmpty() ) os = osXml;
+
+ bugDetails = BugDetails( new BugDetailsImpl( version, source, compiler, os,
+ parts ) );
+ bugDetails.addAttachmentDetails( attachments );
+
+ return KBB::Error();
+}
+
+void DomProcessor::setPackageListQuery( KURL &url )
+{
+ url.setFileName( "xml.cgi" );
+ url.setQuery( "?data=versiontable" );
+}
+
+void DomProcessor::setBugListQuery( KURL &url, const Package &product, const QString &component )
+{
+ if ( server()->serverConfig().bugzillaVersion() == "Bugworld" ) {
+ url.setFileName( "bugworld.cgi" );
+ } else {
+ url.setFileName( "xmlquery.cgi" );
+ }
+
+ QString user = server()->serverConfig().user();
+
+ if ( component.isEmpty() )
+ url.setQuery( "?user=" + user + "&product=" + product.name() );
+ else
+ url.setQuery( "?user=" + user + "&product=" + product.name() + "&component=" + component );
+
+ if ( KBBPrefs::instance()->mShowClosedBugs )
+ url.addQueryItem( "addClosed", "1" );
+}
+
+void DomProcessor::setBugDetailsQuery( KURL &url, const Bug &bug )
+{
+ url.setFileName( "xml.cgi" );
+ url.setQuery( "?id=" + bug.number() );
+}
+
+QString DomProcessor::wrapLines( const QString &text )
+{
+ int wrap = KBBPrefs::instance()->mWrapColumn;
+
+ QStringList lines = QStringList::split( '\n', text, true );
+ //kdDebug() << lines.count() << " lines." << endl;
+
+ QString out;
+ bool removeBlankLines = true;
+ for ( QStringList::Iterator it = lines.begin() ; it != lines.end() ; ++it )
+ {
+ QString line = *it;
+
+ if ( removeBlankLines ) {
+ if ( line.isEmpty() ) continue;
+ else removeBlankLines = false;
+ }
+
+ //kdDebug() << "BugDetailsJob::processNode IN line='" << line << "'" << endl;
+
+ QString wrappedLine;
+ while ( line.length() > uint( wrap ) )
+ {
+ int breakPoint = line.findRev( ' ', wrap );
+ //kdDebug() << "Breaking at " << breakPoint << endl;
+ if( breakPoint == -1 ) {
+ wrappedLine += line.left( wrap ) + '\n';
+ line = line.mid( wrap );
+ } else {
+ wrappedLine += line.left( breakPoint ) + '\n';
+ line = line.mid( breakPoint + 1 );
+ }
+ }
+ wrappedLine += line; // the remainder
+ //kdDebug() << "BugDetailsJob::processNode OUT wrappedLine='" << wrappedLine << "'" << endl;
+
+ out += wrappedLine + "\n";
+ }
+
+ return out;
+}
+
+bool DomProcessor::parseAttributeLine( const QString &line, const QString &key,
+ QString &result )
+{
+ if ( !result.isEmpty() ) return false;
+
+ if ( !line.startsWith( key + ":" ) ) return false;
+
+ QString value = line.mid( key.length() + 1 );
+ value = value.stripWhiteSpace();
+
+ result = value;
+
+ return true;
+}
+
+QDateTime DomProcessor::parseDate( const QString &dateStr )
+{
+ QDateTime date = QDateTime::fromString( dateStr, Qt::ISODate );
+
+ return date;
+}
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/domprocessor.h b/kbugbuster/backend/domprocessor.h
new file mode 100644
index 00000000..1553ae4a
--- /dev/null
+++ b/kbugbuster/backend/domprocessor.h
@@ -0,0 +1,69 @@
+/*
+ This file is part of KBugBuster.
+ Copyright (c) 2002 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+#ifndef DOMPROCESSOR_H
+#define DOMPROCESSOR_H
+
+#include "bug.h"
+#include "bugdetails.h"
+#include "package.h"
+#include "error.h"
+#include "processor.h"
+
+#include <kurl.h>
+
+#include <qdom.h>
+
+class BugServer;
+
+class DomProcessor : public Processor
+{
+ public:
+ DomProcessor( BugServer * );
+ virtual ~DomProcessor();
+
+ KBB::Error parsePackageList( const QByteArray &data,
+ Package::List &packages );
+ KBB::Error parseBugList( const QByteArray &data, Bug::List &bugs );
+ KBB::Error parseBugDetails( const QByteArray &, BugDetails & );
+
+ void setPackageListQuery( KURL & );
+ void setBugListQuery( KURL &, const Package &, const QString &component );
+ void setBugDetailsQuery( KURL &, const Bug & );
+
+ protected:
+ virtual KBB::Error parseDomPackageList( const QDomElement &,
+ Package::List & );
+ virtual KBB::Error parseDomBugList( const QDomElement &, Bug::List & );
+ virtual KBB::Error parseDomBugDetails( const QDomElement &, BugDetails & );
+
+ QString wrapLines( const QString & );
+ bool parseAttributeLine( const QString &line, const QString &key,
+ QString &result );
+ QDateTime parseDate( const QString & );
+};
+
+#endif
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/error.h b/kbugbuster/backend/error.h
new file mode 100644
index 00000000..e12bcf61
--- /dev/null
+++ b/kbugbuster/backend/error.h
@@ -0,0 +1,43 @@
+/*
+ This file is part of KBugBuster.
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+#ifndef KBB_ERROR_H
+#define KBB_ERROR_H
+
+namespace KBB {
+
+class Error
+{
+ public:
+ Error( const QString &msg = QString::null ) : mMsg( msg ) {}
+
+ operator bool() { return !mMsg.isEmpty(); }
+
+ QString message() const { return mMsg; }
+
+ private:
+ QString mMsg;
+};
+
+}
+
+#endif
diff --git a/kbugbuster/backend/htmlparser.cpp b/kbugbuster/backend/htmlparser.cpp
new file mode 100644
index 00000000..7e53c1bd
--- /dev/null
+++ b/kbugbuster/backend/htmlparser.cpp
@@ -0,0 +1,294 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+
+#include "htmlparser.h"
+#include "bugimpl.h"
+#include "packageimpl.h"
+
+#include <kdebug.h>
+
+#include <qbuffer.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+
+KBB::Error HtmlParser::parseBugList( const QByteArray &data, Bug::List &bugs )
+{
+ QBuffer buffer( data );
+ if ( !buffer.open( IO_ReadOnly ) ) {
+ return KBB::Error( "Can't open buffer" );
+ }
+
+ QTextStream ts( &buffer );
+
+ mState = Idle;
+
+ QString line;
+ while ( !( line = ts.readLine() ).isNull() ) {
+ KBB::Error err = parseLine( line, bugs );
+ if ( err ) return err;
+ }
+
+ return KBB::Error();
+}
+
+KBB::Error HtmlParser::parsePackageList( const QByteArray &data,
+ Package::List &packages )
+{
+ init();
+
+ QBuffer buffer( data );
+ if ( !buffer.open( IO_ReadOnly ) ) {
+ return KBB::Error( "Can't open buffer" );
+ }
+
+ QTextStream ts( &buffer );
+
+ QString line;
+ while ( !( line = ts.readLine() ).isNull() ) {
+ KBB::Error err = parseLine( line, packages );
+ if ( err ) return err;
+ }
+
+ processResult( packages );
+
+ return KBB::Error();
+}
+
+void HtmlParser::init()
+{
+}
+
+void HtmlParser::setPackageListQuery( KURL &url )
+{
+ url.setFileName( "query.cgi" );
+}
+
+KBB::Error HtmlParser::parseLine( const QString &, Bug::List & )
+{
+ return KBB::Error();
+}
+
+KBB::Error HtmlParser::parseLine( const QString &, Package::List & )
+{
+ return KBB::Error();
+}
+
+void HtmlParser::processResult( Package::List & )
+{
+}
+
+QString HtmlParser::getAttribute( const QString &line, const QString &name )
+{
+ int pos1 = line.find( name + "=\"" );
+ if ( pos1 < 1 ) return QString::null;
+ pos1 += name.length() + 2;
+ int pos2 = line.find( "\"", pos1 );
+ if ( pos2 < 1 ) return QString::null;
+ return line.mid( pos1, pos2 - pos1 );
+}
+
+bool HtmlParser::getCpts( const QString &line, QString &key,
+ QStringList &values )
+{
+ if ( !line.contains( QRegExp( "\\s*cpts" ) ) ) return false;
+
+// kdDebug() << "LINE: " << line << endl;
+ int pos1 = line.find( "[" );
+ if ( pos1 < 0 ) return false;
+ int pos2 = line.find( "]", ++pos1 );
+ if ( pos2 < 0 ) return false;
+
+ key = line.mid( pos1, pos2 - pos1 );
+ int pos3 = key.find( "'" );
+ if ( pos3 >= 0 ) {
+ int pos4 = key.find( "'", ++pos3 );
+ if ( pos4 >= 0 ) key = key.mid( pos3, pos4 - pos3 );
+ }
+// kdDebug() << " KEY: " << key << endl;
+
+ pos1 = line.find( "'", ++pos2 );
+ if ( pos1 >= 0 ) pos2 = line.find( "'", ++pos1 );
+
+ while ( pos1 >= 0 && pos2 >= 0 ) {
+ QString value = line.mid( pos1, pos2 - pos1 );
+// kdDebug() << " VALUE: " << value << endl;
+
+ values.append( value );
+
+ pos1 = line.find( "'", ++pos2 );
+ if ( pos1 >= 0 ) pos2 = line.find( "'", ++pos1 );
+ }
+
+ return true;
+}
+
+KBB::Error HtmlParser_2_10::parseLine( const QString &line, Bug::List &bugs )
+{
+ if ( line.startsWith( "<TR VALIGN" ) ) {
+// kdDebug() << "LINE: " << line << endl;
+ QRegExp re( "show_bug\\.cgi\\?id=(\\d+)" );
+ re.search( line );
+ QString number = re.cap( 1 );
+// kdDebug() << " NUMBER: " << number << endl;
+
+ QString summary;
+ int pos = line.findRev( "summary>" );
+ if ( pos >= 0 ) summary = line.mid( pos + 8 );
+
+ Bug bug( new BugImpl( summary, Person(), number, 0xFFFFFFFF, Bug::SeverityUndefined,
+ Person(), Bug::StatusUndefined,
+ Bug::BugMergeList() ) );
+
+ if ( !bug.isNull() ) {
+ bugs.append( bug );
+ }
+ }
+
+ return KBB::Error();
+}
+
+KBB::Error HtmlParser_2_10::parseLine( const QString &line,
+ Package::List &packages )
+{
+ QString package;
+ QStringList components;
+
+ if ( getCpts( line, package, components ) ) {
+ packages.append( Package( new PackageImpl( package, "", 0, Person(),
+ components ) ) );
+ }
+
+ return KBB::Error();
+}
+
+
+void HtmlParser_2_14_2::init()
+{
+ mComponentsMap.clear();
+
+ mState = Idle;
+}
+
+KBB::Error HtmlParser_2_14_2::parseLine( const QString &line,
+ Package::List & )
+{
+ switch ( mState ) {
+ case Idle:
+ if ( line.startsWith( "tms[" ) ) mState = Components;
+ break;
+ case Components: {
+ if ( line.startsWith( "function" ) ) mState = Finished;
+ QString key;
+ QStringList values;
+ if ( getCpts( line, key, values ) ) {
+// kdDebug() << "KEY: " << key << " VALUES: " << values.join(",") << endl;
+ if ( values.count() == 2 ) {
+ mComponentsMap[ values.last() ].append( key );
+ }
+ }
+ }
+ default:
+ break;
+ }
+
+ return KBB::Error();
+}
+
+void HtmlParser_2_14_2::processResult( Package::List &packages )
+{
+ QMap<QString,QStringList>::ConstIterator it;
+ for ( it = mComponentsMap.begin(); it != mComponentsMap.end(); ++it ) {
+ packages.append( Package( new PackageImpl( it.key(), "", 0, Person(),
+ it.data() ) ) );
+ }
+}
+
+
+void HtmlParser_2_17_1::init()
+{
+ mProducts.clear();
+ mComponents.clear();
+
+ mState = Idle;
+}
+
+KBB::Error HtmlParser_2_17_1::parseBugList( const QByteArray &data, Bug::List &bugs )
+{
+ return RdfProcessor::parseBugList( data, bugs );
+}
+
+KBB::Error HtmlParser_2_17_1::parseLine( const QString & /*line*/, Bug::List &/*bugs*/ )
+{
+ return KBB::Error( "Not implemented" );
+}
+
+KBB::Error HtmlParser_2_17_1::parseLine( const QString &line, Package::List & )
+{
+ switch ( mState ) {
+ case Idle:
+ case SearchComponents:
+ if ( line.contains( "var cpts" ) ) mState = Components;
+ break;
+ case SearchProducts:
+ if ( line.contains( "onchange=\"selectProduct" ) ) mState = Products;
+ break;
+ case Components: {
+ if ( line.contains( QRegExp( "\\s*function" ) ) ) {
+ mState = SearchProducts;
+ }
+ QString key;
+ QStringList components;
+ if ( getCpts( line, key, components ) ) {
+ mComponents.append( components );
+ }
+ }
+ case Products: {
+ if ( line.contains( "</select>" ) ) mState = Finished;
+ QString product = getAttribute( line, "value" );
+ if ( !product.isEmpty() ) {
+ kdDebug() << "PRODUCT: " << product << endl;
+ mProducts.append( product );
+ }
+ break;
+ }
+ case Finished:
+ default:
+ break;
+ }
+
+ return KBB::Error();
+}
+
+void HtmlParser_2_17_1::processResult( Package::List &packages )
+{
+ QStringList::ConstIterator itProduct = mProducts.begin();
+ QValueList<QStringList>::ConstIterator itComponents = mComponents.begin();
+
+ while( itProduct != mProducts.end() && itComponents != mComponents.end() ) {
+ packages.append( Package( new PackageImpl( *itProduct, "", 0, Person(),
+ *itComponents ) ) );
+ ++itProduct;
+ ++itComponents;
+ }
+}
diff --git a/kbugbuster/backend/htmlparser.h b/kbugbuster/backend/htmlparser.h
new file mode 100644
index 00000000..ffb0a22a
--- /dev/null
+++ b/kbugbuster/backend/htmlparser.h
@@ -0,0 +1,116 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+#ifndef HTMLPARSER_H
+#define HTMLPARSER_H
+
+#include "package.h"
+#include "bug.h"
+#include "error.h"
+#include "rdfprocessor.h"
+
+#include <qstringlist.h>
+#include <qvaluelist.h>
+#include <qmap.h>
+
+class HtmlParser : public RdfProcessor
+{
+ protected:
+ enum State { Idle, SearchComponents, SearchProducts, Components, Products,
+ Finished };
+ State mState;
+
+ public:
+ HtmlParser( BugServer *s ) : RdfProcessor( s ), mState( Idle ) {}
+ virtual ~HtmlParser() {}
+
+ KBB::Error parseBugList( const QByteArray &data, Bug::List &bugs );
+ KBB::Error parsePackageList( const QByteArray &data,
+ Package::List &packages );
+
+ void setPackageListQuery( KURL & );
+
+ protected:
+ virtual void init();
+
+ virtual KBB::Error parseLine( const QString &line, Bug::List &bugs );
+ virtual KBB::Error parseLine( const QString &line,
+ Package::List &packages );
+
+ virtual void processResult( Package::List &packages );
+
+ QString getAttribute( const QString &line, const QString &name );
+ bool getCpts( const QString &line, QString &key, QStringList &values );
+};
+
+
+class HtmlParser_2_10 : public HtmlParser
+{
+ public:
+ HtmlParser_2_10( BugServer *s ) : HtmlParser( s ) {}
+
+ protected:
+ KBB::Error parseLine( const QString &line, Bug::List &bugs );
+ KBB::Error parseLine( const QString &line, Package::List &packages );
+};
+
+
+class HtmlParser_2_14_2 : public HtmlParser_2_10
+{
+ public:
+ HtmlParser_2_14_2( BugServer *s ) : HtmlParser_2_10( s ) {}
+
+ protected:
+ void init();
+
+ KBB::Error parseLine( const QString &line, Package::List &packages );
+
+ void processResult( Package::List &packages );
+
+ private:
+ QMap<QString, QStringList> mComponentsMap;
+};
+
+
+
+class HtmlParser_2_17_1 : public HtmlParser
+{
+ public:
+ HtmlParser_2_17_1( BugServer *s ) : HtmlParser( s ) {}
+
+ KBB::Error parseBugList( const QByteArray &data, Bug::List &bugs );
+
+ protected:
+ void init();
+
+ KBB::Error parseLine( const QString &line, Bug::List &bugs );
+ KBB::Error parseLine( const QString &line, Package::List &packages );
+
+ void processResult( Package::List &packages );
+
+ private:
+ QStringList mProducts;
+ QValueList<QStringList> mComponents;
+};
+
+#endif
diff --git a/kbugbuster/backend/kbbprefs.cpp b/kbugbuster/backend/kbbprefs.cpp
new file mode 100644
index 00000000..30f337ab
--- /dev/null
+++ b/kbugbuster/backend/kbbprefs.cpp
@@ -0,0 +1,170 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2001,2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+
+#include <qstring.h>
+#include <qstringlist.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kconfig.h>
+
+#include "bugsystem.h"
+#include "bugserver.h"
+#include "bugserverconfig.h"
+
+#include "kbbprefs.h"
+
+KBBPrefs *KBBPrefs::mInstance = 0;
+
+KBBPrefs::KBBPrefs() : KConfigSkeleton()
+{
+ setCurrentGroup("History");
+
+ addItemInt("RecentPackagesCount",mRecentPackagesCount,7);
+ addItemIntList("Splitter1",mSplitter1);
+ addItemIntList("Splitter2",mSplitter2);
+
+
+ setCurrentGroup("Personal Settings");
+
+ addItemInt("MailClient",mMailClient,MailSender::KMail,"Mail Client");
+ addItemBool("ShowClosedBugs",mShowClosedBugs,false);
+ addItemBool("ShowWishes",mShowWishes,true);
+ addItemBool("ShowVotes", mShowVoted, false);
+ addItemInt("MinimumVotes", mMinVotes, 0);
+ addItemBool("SendBCC",mSendBCC,false);
+ addItemString("OverrideRecipient",mOverrideRecipient,QString::null);
+ addItemInt("WrapColumn",mWrapColumn,90);
+
+
+ setCurrentGroup("MsgInputDlg");
+
+ addItemInt("MsgDialogWidth",mMsgDlgWidth);
+ addItemInt("MsgDialogHeight",mMsgDlgHeight);
+ addItemIntList("MsgDialogSplitter",mMsgDlgSplitter);
+
+
+ setCurrentGroup( "Debug" );
+
+ addItemBool( "DebugMode", mDebugMode, false );
+
+
+ setCurrentGroup( "Servers" );
+
+ addItemString("CurrentServer",mCurrentServer);
+}
+
+
+KBBPrefs::~KBBPrefs()
+{
+ delete mInstance;
+ mInstance = 0;
+}
+
+
+KBBPrefs *KBBPrefs::instance()
+{
+ if (!mInstance) {
+ mInstance = new KBBPrefs();
+ mInstance->readConfig();
+ }
+
+ return mInstance;
+}
+
+void KBBPrefs::usrSetDefaults()
+{
+ setMessageButtonsDefault();
+}
+
+void KBBPrefs::usrReadConfig()
+{
+ mMessageButtons.clear();
+
+ config()->setGroup("MessageButtons");
+ QStringList buttonList = config()->readListEntry("ButtonList");
+ if (buttonList.isEmpty()) {
+ setMessageButtonsDefault();
+ } else {
+ QStringList::ConstIterator it;
+ for(it = buttonList.begin(); it != buttonList.end(); ++it) {
+ QString text = config()->readEntry(*it);
+ mMessageButtons.insert(*it,text);
+ }
+ }
+
+ BugSystem::self()->readConfig( config() );
+}
+
+void KBBPrefs::usrWriteConfig()
+{
+ config()->setGroup("MessageButtons");
+ QStringList buttonList;
+ QMap<QString,QString>::ConstIterator it;
+ for(it = mMessageButtons.begin();it != mMessageButtons.end();++it) {
+ buttonList.append(it.key());
+ config()->writeEntry(it.key(),it.data());
+ }
+ config()->writeEntry("ButtonList",buttonList);
+
+ BugSystem::self()->writeConfig( config() );
+}
+
+void KBBPrefs::setMessageButtonsDefault()
+{
+ mMessageButtons.clear();
+ mMessageButtons.insert(i18n("Bug Fixed in CVS"),"Thank you for your bug report.\n"
+ "The bug that you reported has been identified and has been fixed in the\n"
+ "latest development (CVS) version of KDE. The bug report will be closed.\n");
+ mMessageButtons.insert(i18n("Duplicate Report"),"Thank you for your bug report.\n"
+ "This bug/feature request has already been reported and this report will\n"
+ "be marked as a duplicate.\n");
+ mMessageButtons.insert(i18n("Packaging Bug"),"Thank you for your bug report.\n"
+ "The bug that you reported appears to be a packaging bug, due to a\n"
+ "problem in the way in which your distribution/vendor has packaged\n"
+ "KDE for distribution.\n"
+ "The bug report will be closed since it is not a KDE problem.\n"
+ "Please send the bug report to your distribution/vendor instead.\n");
+ mMessageButtons.insert(i18n("Feature Implemented in CVS"),"Thank you for your bug report.\n"
+ "The feature that you requested has been implemented in the latest\n"
+ "development (CVS) version of KDE. The feature request will be closed.\n");
+ mMessageButtons.insert(i18n("More Information Required"),"Thank you for your bug report.\n"
+ "You have not provided enough information for us to be able to reproduce\n"
+ "the bug. Please provide a detailed account of the steps required to\n"
+ "trigger and reproduce the bug. Without this information, we will not be\n"
+ "able to reproduce, identify and fix the bug.\n");
+ mMessageButtons.insert(i18n("No Longer Applicable"),"Thank you for your bug report.\n"
+ "The bug that your reported no longer applies to the latest development\n"
+ "(CVS) version of KDE. This is most probably because it has been fixed,\n"
+ "the application has been substantially modified or the application no\n"
+ "longer exists. The bug report will be closed.\n");
+ mMessageButtons.insert(i18n("Won't Fix Bug"),"Thank you for your bug report/feature request.\n"
+ "Unfortunately, this bug will never be fixed or the feature never\n"
+ "implemented. The bug report/feature request will be closed.\n");
+ mMessageButtons.insert(i18n("Cannot Reproduce Bug"),"Thank you for your bug report.\n"
+ "This bug can not be reproduced using the current development (CVS)\n"
+ "version of KDE. This suggests that the bug has already been fixed.\n"
+ "The bug report will be closed.\n");
+}
+
diff --git a/kbugbuster/backend/kbbprefs.h b/kbugbuster/backend/kbbprefs.h
new file mode 100644
index 00000000..64d7f20d
--- /dev/null
+++ b/kbugbuster/backend/kbbprefs.h
@@ -0,0 +1,82 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2001,2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+#ifndef KBBPREFS_H
+#define KBBPREFS_H
+
+#include <qmap.h>
+
+#include <kconfigskeleton.h>
+
+#include "mailsender.h"
+
+class QStringList;
+
+class KBBPrefs : public KConfigSkeleton
+{
+ public:
+ virtual ~KBBPrefs();
+
+ static KBBPrefs *instance();
+
+ protected:
+ void usrSetDefaults();
+ void usrReadConfig();
+ void usrWriteConfig();
+
+ void setMessageButtonsDefault();
+
+ private:
+ KBBPrefs();
+
+ static KBBPrefs *mInstance;
+
+ public:
+ int mRecentPackagesCount;
+
+ QValueList<int> mSplitter1;
+ QValueList<int> mSplitter2;
+
+ int mMailClient;
+ bool mShowClosedBugs;
+ bool mShowWishes;
+ bool mSendBCC;
+ QString mOverrideRecipient;
+
+ bool mShowVoted;
+ int mMinVotes;
+
+ int mWrapColumn;
+
+ QMap<QString,QString> mMessageButtons;
+
+ int mMsgDlgWidth;
+ int mMsgDlgHeight;
+ QValueList<int> mMsgDlgSplitter;
+
+ bool mDebugMode;
+
+ QString mCurrentServer;
+};
+
+#endif
diff --git a/kbugbuster/backend/mailsender.cpp b/kbugbuster/backend/mailsender.cpp
new file mode 100644
index 00000000..ec32405d
--- /dev/null
+++ b/kbugbuster/backend/mailsender.cpp
@@ -0,0 +1,212 @@
+#ifndef QT_NO_ASCII_CAST
+#define QT_NO_ASCII_CAST
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+
+#include <qtimer.h>
+
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kurl.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kprocess.h>
+
+#include "mailsender.h"
+#include "smtp.h"
+
+MailSender::MailSender(MailClient client,const QString &smtpServer) :
+ m_client( client ), m_smtpServer( smtpServer )
+{
+}
+
+MailSender::~MailSender()
+{
+}
+
+MailSender *MailSender::clone() const
+{
+ return new MailSender(m_client,m_smtpServer);
+}
+
+bool MailSender::send(const QString &fromName,const QString &fromEmail,const QString &to,
+ const QString &subject,const QString &body,bool bcc,
+ const QString &recipient)
+{
+ QString from( fromName );
+ if ( !fromEmail.isEmpty() )
+ from += QString::fromLatin1( " <%2>" ).arg( fromEmail );
+ kdDebug() << "MailSender::sendMail():\nFrom: " << from << "\nTo: " << to
+ << "\nbccflag:" << bcc
+ << "\nRecipient:" << recipient
+ << "\nSubject: " << subject << "\nBody: \n" << body << endl;
+
+ // ### FIXME: bcc is not supported in direct mode and recipient is not
+ // supported in sendmail and kmail mode
+
+ if (m_client == Sendmail) {
+ kdDebug() << "Sending per Sendmail" << endl;
+
+ bool needHeaders = true;
+
+ QString command = KStandardDirs::findExe(QString::fromLatin1("sendmail"),
+ QString::fromLatin1("/sbin:/usr/sbin:/usr/lib"));
+ if (!command.isNull()) command += QString::fromLatin1(" -oi -t");
+ else {
+ command = KStandardDirs::findExe(QString::fromLatin1("mail"));
+ if (command.isNull()) return false; // give up
+
+ command.append(QString::fromLatin1(" -s "));
+ command.append(KProcess::quote(subject));
+
+ if (bcc) {
+ command.append(QString::fromLatin1(" -b "));
+ command.append(KProcess::quote(from));
+ }
+
+ command.append(" ");
+ command.append(KProcess::quote(to));
+
+ needHeaders = false;
+ }
+
+ FILE * fd = popen(command.local8Bit(),"w");
+ if (!fd)
+ {
+ kdError() << "Unable to open a pipe to " << command << endl;
+ QTimer::singleShot( 0, this, SLOT( deleteLater() ) );
+ return false;
+ }
+
+ QString textComplete;
+ if (needHeaders)
+ {
+ textComplete += QString::fromLatin1("From: ") + from + '\n';
+ textComplete += QString::fromLatin1("To: ") + to + '\n';
+ if (bcc) textComplete += QString::fromLatin1("Bcc: ") + from + '\n';
+ textComplete += QString::fromLatin1("Subject: ") + subject + '\n';
+ textComplete += QString::fromLatin1("X-Mailer: KBugBuster") + '\n';
+ }
+ textComplete += '\n'; // end of headers
+ textComplete += body;
+
+ emit status( i18n( "Sending through sendmail..." ) );
+ fwrite(textComplete.local8Bit(),textComplete.length(),1,fd);
+
+ pclose(fd);
+ } else if ( m_client == KMail ) {
+ kdDebug() << "Sending per KMail" << endl;
+
+ if (!kapp->dcopClient()->isApplicationRegistered("kmail")) {
+ KMessageBox::error(0,i18n("No running instance of KMail found."));
+ QTimer::singleShot( 0, this, SLOT( deleteLater() ) );
+ return false;
+ }
+
+ emit status( i18n( "Passing mail to KDE email program..." ) );
+ if (!kMailOpenComposer(to,"", (bcc ? from : ""), subject,body,0,KURL())) {
+ QTimer::singleShot( 0, this, SLOT( deleteLater() ) );
+ return false;
+ }
+ } else if ( m_client == Direct ) {
+ kdDebug() << "Sending Direct" << endl;
+
+ QStringList recipients;
+ if ( !recipient.isEmpty() )
+ recipients << recipient;
+ else
+ recipients << to;
+
+ QString message = QString::fromLatin1( "From: " ) + from +
+ QString::fromLatin1( "\nTo: " ) + to +
+ QString::fromLatin1( "\nSubject: " ) + subject +
+ QString::fromLatin1( "\nX-Mailer: KBugBuster" ) +
+ QString::fromLatin1( "\n\n" ) + body;
+
+ Smtp *smtp = new Smtp( fromEmail, recipients, message, m_smtpServer );
+ connect( smtp, SIGNAL( status( const QString & ) ),
+ this, SIGNAL( status( const QString & ) ) );
+ connect( smtp, SIGNAL( success() ),
+ this, SLOT( smtpSuccess() ) );
+ connect( smtp, SIGNAL( error( const QString &, const QString & ) ),
+ this, SLOT( smtpError( const QString &, const QString & ) ) );
+
+ smtp->insertChild( this ); // die when smtp dies
+ } else {
+ kdDebug() << "Invalid mail client setting." << endl;
+ }
+
+ if (m_client != Direct)
+ {
+ emit finished();
+ QTimer::singleShot( 0, this, SLOT( deleteLater() ) );
+ }
+
+ return true;
+}
+
+void MailSender::smtpSuccess()
+{
+ if ( parent() != sender() || !parent()->inherits( "Smtp" ) )
+ return;
+
+ static_cast<Smtp *>( parent() )->quit();
+ emit finished();
+}
+
+void MailSender::smtpError(const QString &_command, const QString &_response)
+{
+ if ( parent() != sender() || !parent()->inherits( "Smtp" ) )
+ return;
+
+ QString command = _command;
+ QString response = _response;
+
+ Smtp *smtp = static_cast<Smtp *>( parent() );
+ smtp->removeChild( this );
+ delete smtp;
+
+ KMessageBox::error( qApp->activeWindow(),
+ i18n( "Error during SMTP transfer.\n"
+ "command: %1\n"
+ "response: %2" ).arg( command ).arg( response ) );
+
+ emit finished();
+ QTimer::singleShot( 0, this, SLOT( deleteLater() ) );
+}
+
+int MailSender::kMailOpenComposer(const QString& arg0,const QString& arg1,
+ const QString& arg2,const QString& arg3,const QString& arg4,int arg5,
+ const KURL& arg6)
+{
+ int result = 0;
+
+ QByteArray data, replyData;
+ QCString replyType;
+ QDataStream arg( data, IO_WriteOnly );
+ arg << arg0;
+ arg << arg1;
+ arg << arg2;
+ arg << arg3;
+ arg << arg4;
+ arg << arg5;
+ arg << arg6;
+ if (kapp->dcopClient()->call("kmail","KMailIface","openComposer(QString,QString,QString,QString,QString,int,KURL)", data, replyType, replyData ) ) {
+ if ( replyType == "int" ) {
+ QDataStream _reply_stream( replyData, IO_ReadOnly );
+ _reply_stream >> result;
+ } else {
+ kdDebug() << "kMailOpenComposer() call failed." << endl;
+ }
+ } else {
+ kdDebug() << "kMailOpenComposer() call failed." << endl;
+ }
+ return result;
+}
+
+#include "mailsender.moc"
+
diff --git a/kbugbuster/backend/mailsender.h b/kbugbuster/backend/mailsender.h
new file mode 100644
index 00000000..06517f9c
--- /dev/null
+++ b/kbugbuster/backend/mailsender.h
@@ -0,0 +1,50 @@
+#ifndef MAILSENDER_H
+#define MAILSENDER_H
+
+#include <qstring.h>
+#include <qobject.h>
+
+class KURL;
+class Smtp;
+
+class MailSender : public QObject
+{
+ Q_OBJECT
+ public:
+ enum MailClient { Sendmail = 0, KMail = 1, Direct = 2 };
+
+ MailSender(MailClient,const QString &smtpServer=QString::null);
+ virtual ~MailSender();
+
+ MailSender *clone() const;
+
+ /**
+ Send mail with specified from, to and subject field and body as text. If
+ bcc is set, send a blind carbon copy to the sender from.
+ If recipient is specified the mail is sent to the specified address
+ instead of 'to' . (this currently only works in for direct mail
+ sending through SMTP.
+ */
+ bool send(const QString &fromName, const QString &fromEmail,
+ const QString &to,const QString &subject,
+ const QString &body,bool bcc=false,
+ const QString &recipient = QString::null);
+
+ signals:
+ void status( const QString &message );
+ void finished();
+
+ private slots:
+ void smtpSuccess();
+ void smtpError(const QString &command, const QString &response);
+
+ private:
+ int kMailOpenComposer(const QString& arg0,const QString& arg1,
+ const QString& arg2,const QString& arg3,
+ const QString& arg4,int arg5,const KURL& arg6);
+
+ MailClient m_client;
+ QString m_smtpServer;
+};
+
+#endif
diff --git a/kbugbuster/backend/package.cpp b/kbugbuster/backend/package.cpp
new file mode 100644
index 00000000..ae009397
--- /dev/null
+++ b/kbugbuster/backend/package.cpp
@@ -0,0 +1,82 @@
+
+#include "package.h"
+
+#include "packageimpl.h"
+
+Package::Package()
+{
+}
+
+Package::Package( PackageImpl *impl )
+ : m_impl( impl )
+{
+}
+
+Package::Package( const Package &other )
+{
+ (*this) = other;
+}
+
+Package &Package::operator=( const Package &rhs )
+{
+ m_impl = rhs.m_impl;
+ return *this;
+}
+
+Package::~Package()
+{
+}
+
+QString Package::name() const
+{
+ if ( !m_impl )
+ return QString::null;
+
+ return m_impl->name;
+}
+
+QString Package::description() const
+{
+ if ( !m_impl )
+ return QString::null;
+
+ return m_impl->description;
+}
+
+uint Package::numberOfBugs() const
+{
+ if ( !m_impl )
+ return 0;
+
+ return m_impl->numberOfBugs;
+}
+
+Person Package::maintainer() const
+{
+ if ( !m_impl )
+ return Person();
+
+ return m_impl->maintainer;
+}
+
+const QStringList Package::components() const
+{
+ if ( !m_impl )
+ return QStringList();
+
+ return m_impl->components;
+}
+
+bool Package::operator==( const Package &rhs )
+{
+ return m_impl == rhs.m_impl;
+}
+
+bool Package::operator<( const Package &rhs ) const
+{
+ return m_impl < rhs.m_impl;
+}
+
+/**
+ * vim:ts=4:sw=4:et
+ */
diff --git a/kbugbuster/backend/package.h b/kbugbuster/backend/package.h
new file mode 100644
index 00000000..7b5c69a1
--- /dev/null
+++ b/kbugbuster/backend/package.h
@@ -0,0 +1,43 @@
+#ifndef __package_h__
+#define __package_h__
+
+#include "person.h"
+
+#include <qvaluelist.h>
+
+#include <ksharedptr.h>
+
+class PackageImpl;
+
+class Package
+{
+public:
+ typedef QValueList<Package> List;
+
+ Package();
+ Package( PackageImpl *impl );
+ Package( const Package &other );
+ Package &operator=( const Package &rhs );
+ ~Package();
+
+ QString name() const;
+ QString description() const;
+ uint numberOfBugs() const;
+ Person maintainer() const;
+ const QStringList components() const;
+
+ bool isNull() const { return m_impl == 0; }
+
+ PackageImpl *impl() const { return m_impl; }
+
+ bool operator==( const Package &rhs );
+ bool operator<( const Package &rhs ) const;
+
+private:
+ KSharedPtr<PackageImpl> m_impl;
+};
+
+#endif
+
+/* vim: set sw=4 ts=4 et softtabstop=4: */
+
diff --git a/kbugbuster/backend/packageimpl.h b/kbugbuster/backend/packageimpl.h
new file mode 100644
index 00000000..c60a1079
--- /dev/null
+++ b/kbugbuster/backend/packageimpl.h
@@ -0,0 +1,31 @@
+#ifndef __packageimpl_h__
+#define __packageimpl_h__
+
+#include "person.h"
+
+#include <qstringlist.h>
+#include <kurl.h>
+#include <ksharedptr.h>
+
+struct PackageImpl : public KShared
+{
+public:
+ PackageImpl( const QString &_name, const QString &_description,
+ uint _numberOfBugs, const Person &_maintainer,
+ const QStringList &_components )
+ : name( _name ), description( _description ),numberOfBugs( _numberOfBugs ),
+ maintainer( _maintainer ), components(_components)
+ {}
+
+ QString name;
+ QString description;
+ uint numberOfBugs;
+ Person maintainer;
+ QStringList components;
+};
+
+#endif
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/packagelistjob.cpp b/kbugbuster/backend/packagelistjob.cpp
new file mode 100644
index 00000000..6b05badf
--- /dev/null
+++ b/kbugbuster/backend/packagelistjob.cpp
@@ -0,0 +1,68 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+
+#include "packagelistjob.h"
+#include "package.h"
+#include "packageimpl.h"
+#include "bugserver.h"
+#include "domprocessor.h"
+#include "htmlparser.h"
+
+#include <kdebug.h>
+#include <assert.h>
+
+#include <qdom.h>
+#include <qbuffer.h>
+
+PackageListJob::PackageListJob( BugServer *server )
+ : BugJob( server )
+{
+}
+
+PackageListJob::~PackageListJob()
+{
+}
+
+void PackageListJob::start()
+{
+ BugJob::start( server()->packageListUrl() );
+}
+
+void PackageListJob::process( const QByteArray &data )
+{
+ Package::List packages;
+ KBB::Error err = server()->processor()->parsePackageList( data, packages );
+ if ( err ) {
+ emit error( err.message() );
+ } else {
+ emit packageListAvailable( packages );
+ }
+}
+
+
+#include "packagelistjob.moc"
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/packagelistjob.h b/kbugbuster/backend/packagelistjob.h
new file mode 100644
index 00000000..0e1bca99
--- /dev/null
+++ b/kbugbuster/backend/packagelistjob.h
@@ -0,0 +1,55 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+
+#ifndef __packagelistjob_h__
+#define __packagelistjob_h__
+
+#include "bugjob.h"
+
+#include "package.h"
+
+#include <qdom.h>
+
+class PackageListJob : public BugJob
+{
+ Q_OBJECT
+ public:
+ PackageListJob( BugServer * );
+ virtual ~PackageListJob();
+
+ void start();
+
+ protected:
+ void process( const QByteArray &data );
+
+ signals:
+ void packageListAvailable( const Package::List &pkgs );
+};
+
+
+#endif
+
+/*
+ * vim:ts=4:sw=4:et
+ */
diff --git a/kbugbuster/backend/person.cpp b/kbugbuster/backend/person.cpp
new file mode 100644
index 00000000..a9f63be0
--- /dev/null
+++ b/kbugbuster/backend/person.cpp
@@ -0,0 +1,74 @@
+#include <kdebug.h>
+
+#include "person.h"
+
+Person::Person( const QString &fullName )
+{
+ int emailPos = fullName.find( '<' );
+ if ( emailPos < 0 ) {
+ email = fullName;
+ } else {
+ email = fullName.mid( emailPos + 1, fullName.length() - 1 );
+ name = fullName.left( emailPos - 1 );
+ }
+}
+
+QString Person::fullName(bool html) const
+{
+ if( name.isEmpty() )
+ {
+ if( email.isEmpty() )
+ return i18n( "Unknown" );
+ else
+ return email;
+ }
+ else
+ {
+ if( email.isEmpty() )
+ return name;
+ else
+ if ( html ) {
+ return name + " &lt;" + email + "&gt;";
+ } else {
+ return name + " <" + email + ">";
+ }
+ }
+}
+
+Person Person::parseFromString( const QString &_str )
+{
+ Person res;
+
+ QString str = _str;
+
+ int ltPos = str.find( '<' );
+ if ( ltPos != -1 )
+ {
+ int gtPos = str.find( '>', ltPos );
+ if ( gtPos != -1 )
+ {
+ res.name = str.left( ltPos - 1 );
+ str = str.mid( ltPos + 1, gtPos - ( ltPos + 1 ) );
+ }
+ }
+
+ int atPos = str.find( '@' );
+ int spacedAtPos = str.find( QString::fromLatin1( " at " ) );
+ if ( atPos == -1 && spacedAtPos != -1 )
+ str.replace( spacedAtPos, 4, QString::fromLatin1( "@" ) );
+
+ int spacePos = str.find( ' ' );
+ while ( spacePos != -1 )
+ {
+ str[ spacePos ] = '.';
+ spacePos = str.find( ' ', spacePos );
+ }
+
+ res.email = str;
+
+ return res;
+}
+
+/**
+ * vim:et:ts=4:sw=4
+ */
diff --git a/kbugbuster/backend/person.h b/kbugbuster/backend/person.h
new file mode 100644
index 00000000..f29074f9
--- /dev/null
+++ b/kbugbuster/backend/person.h
@@ -0,0 +1,26 @@
+#ifndef __person_h__
+#define __person_h__
+
+#include <qstring.h>
+#include <klocale.h>
+
+struct Person
+{
+ Person() {}
+ Person( const QString &fullName );
+ Person( const QString &_name, const QString &_email )
+ : name( _name ), email( _email ) {}
+
+ QString name;
+ QString email;
+
+ QString fullName( bool html = false ) const;
+
+ static Person parseFromString( const QString &str );
+};
+
+#endif
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/processor.cpp b/kbugbuster/backend/processor.cpp
new file mode 100644
index 00000000..332b4418
--- /dev/null
+++ b/kbugbuster/backend/processor.cpp
@@ -0,0 +1,78 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+
+#include <qstylesheet.h>
+
+#include <kdebug.h>
+#include <kmdcodec.h>
+
+#include "processor.h"
+
+#include "bugserver.h"
+#include "packageimpl.h"
+#include "bugimpl.h"
+#include "bugdetailsimpl.h"
+#include "kbbprefs.h"
+
+Processor::Processor( BugServer *server )
+ : mServer( server )
+{
+}
+
+Processor::~Processor()
+{
+}
+
+void Processor::setPackageListQuery( KURL &url )
+{
+ url.setFileName( "xml.cgi" );
+ url.setQuery( "?data=versiontable" );
+}
+
+void Processor::setBugListQuery( KURL &url, const Package &product, const QString &component )
+{
+ if ( mServer->serverConfig().bugzillaVersion() == "Bugworld" ) {
+ url.setFileName( "bugworld.cgi" );
+ } else {
+ url.setFileName( "xmlquery.cgi" );
+ }
+
+ QString user = mServer->serverConfig().user();
+
+ if ( component.isEmpty() )
+ url.setQuery( "?user=" + user + "&product=" + product.name() );
+ else
+ url.setQuery( "?user=" + user + "&product=" + product.name() + "&component=" + component );
+}
+
+void Processor::setBugDetailsQuery( KURL &url, const Bug &bug )
+{
+ url.setFileName( "xml.cgi" );
+ url.setQuery( "?id=" + bug.number() );
+}
+
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/processor.h b/kbugbuster/backend/processor.h
new file mode 100644
index 00000000..cadaa407
--- /dev/null
+++ b/kbugbuster/backend/processor.h
@@ -0,0 +1,63 @@
+/*
+ This file is part of KBugBuster.
+
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+#ifndef KBB_PROCESSOR_H
+#define KBB_PROCESSOR_H
+
+#include "bug.h"
+#include "bugdetails.h"
+#include "package.h"
+#include "error.h"
+
+#include <kurl.h>
+
+class BugServer;
+
+class Processor
+{
+ public:
+ Processor( BugServer * );
+ virtual ~Processor();
+
+ BugServer *server() const { return mServer; }
+
+ virtual KBB::Error parseBugList( const QByteArray &data,
+ Bug::List &bugs ) = 0;
+ virtual KBB::Error parsePackageList( const QByteArray &data,
+ Package::List &packages ) = 0;
+ virtual KBB::Error parseBugDetails( const QByteArray &, BugDetails & ) = 0;
+
+ virtual void setPackageListQuery( KURL & ) = 0;
+ virtual void setBugListQuery( KURL &, const Package &,
+ const QString &component ) = 0;
+ virtual void setBugDetailsQuery( KURL &, const Bug & ) = 0;
+
+ private:
+ BugServer *mServer;
+};
+
+#endif
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/rdfprocessor.cpp b/kbugbuster/backend/rdfprocessor.cpp
new file mode 100644
index 00000000..89bb3402
--- /dev/null
+++ b/kbugbuster/backend/rdfprocessor.cpp
@@ -0,0 +1,107 @@
+/*
+ This file is part of KBugBuster.
+ Copyright (c) 2002 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+
+#include "rdfprocessor.h"
+
+#include "bugserver.h"
+#include "packageimpl.h"
+#include "bugimpl.h"
+#include "bugdetailsimpl.h"
+#include "kbbprefs.h"
+
+#include "kdebug.h"
+
+RdfProcessor::RdfProcessor( BugServer *server )
+ : DomProcessor( server )
+{
+}
+
+RdfProcessor::~RdfProcessor()
+{
+}
+
+KBB::Error RdfProcessor::parseDomBugList( const QDomElement &element,
+ Bug::List &bugs )
+{
+ if ( element.tagName() != "RDF" ) {
+ kdDebug() << "RdfProcessor::parseBugList(): no RDF element." << endl;
+ return KBB::Error( "No RDF element found" );
+ }
+
+ QDomNodeList bugNodes = element.elementsByTagName( "bz:bug" );
+
+ for( uint i = 0; i < bugNodes.count(); ++i ) {
+ QString title;
+ Person submitter;
+ QString bugNr;
+ Bug::Status status = Bug::StatusUndefined;
+ Bug::Severity severity = Bug::SeverityUndefined;
+ Person developerTodo;
+ Bug::BugMergeList mergedList;
+
+ QDomNode hit = bugNodes.item( i );
+
+ QDomNode n;
+ for( n = hit.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ QDomElement e = n.toElement();
+
+ if ( e.tagName() == "bz:id" ) {
+ bugNr = e.text();
+ } else if ( e.tagName() == "bz:status" ) {
+ status = server()->bugStatus( e.text() );
+ } else if ( e.tagName() == "bz:severity" ) {
+ severity = server()->bugSeverity( e.text() );
+ } else if ( e.tagName() == "bz:summary" ) {
+ title = e.text();
+ }
+ }
+
+ Bug bug( new BugImpl( title, submitter, bugNr, 0xFFFFFFFF, severity,
+ developerTodo, status, mergedList ) );
+
+ if ( !bug.isNull() ) {
+ bugs.append( bug );
+ }
+ }
+
+ return KBB::Error();
+}
+
+void RdfProcessor::setBugListQuery( KURL &url, const Package &product, const QString &component )
+{
+ url.setFileName( "buglist.cgi" );
+ if ( component.isEmpty() )
+ url.setQuery( "?format=rdf&product=" + product.name() );
+ else
+ url.setQuery( "?format=rdf&product=" + product.name() + "&component=" + component );
+ if ( KBBPrefs::instance()->mShowVoted ) {
+ url.addQueryItem( "field0-0-0", "votes" );
+ url.addQueryItem( "type0-0-0", "greaterthan" );
+ QString num = QString::number( KBBPrefs::instance()->mMinVotes );;
+ url.addQueryItem( "value0-0-0", num );
+ }
+}
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/rdfprocessor.h b/kbugbuster/backend/rdfprocessor.h
new file mode 100644
index 00000000..0e0bd780
--- /dev/null
+++ b/kbugbuster/backend/rdfprocessor.h
@@ -0,0 +1,43 @@
+/*
+ This file is part of KBugBuster.
+ Copyright (c) 2002 Cornelius Schumacher <schumacher@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.
+
+ 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ As a special exception, permission is given to link this program
+ with any edition of Qt, and distribute the resulting executable,
+ without including the source code for Qt in the source distribution.
+*/
+#ifndef RDFPROCESSOR_H
+#define RDFPROCESSOR_H
+
+#include "domprocessor.h"
+
+class RdfProcessor : public DomProcessor
+{
+ public:
+ RdfProcessor( BugServer * );
+ virtual ~RdfProcessor();
+
+ KBB::Error parseDomBugList( const QDomElement &, Bug::List & );
+
+ void setBugListQuery( KURL &, const Package &, const QString &component );
+};
+
+#endif
+
+/*
+ * vim:sw=4:ts=4:et
+ */
diff --git a/kbugbuster/backend/smtp.cpp b/kbugbuster/backend/smtp.cpp
new file mode 100644
index 00000000..d54cafab
--- /dev/null
+++ b/kbugbuster/backend/smtp.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** This file is a modified version of part of an example program for Qt.
+** This file may be used, distributed and modified without limitation.
+**
+** Don Sanders <sanders@kde.org>
+**
+*****************************************************************************/
+
+#include "smtp.h"
+
+#include <qtextstream.h>
+#include <qsocket.h>
+#include <qtimer.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+
+Smtp::Smtp( const QString &from, const QStringList &to,
+ const QString &aMessage,
+ const QString &server,
+ unsigned short int port )
+{
+ skipReadResponse = false;
+ mSocket = new QSocket( this );
+ connect ( mSocket, SIGNAL( readyRead() ),
+ this, SLOT( readyRead() ) );
+ connect ( mSocket, SIGNAL( connected() ),
+ this, SLOT( connected() ) );
+ connect ( mSocket, SIGNAL( error(int) ),
+ this, SLOT( socketError(int) ) );
+
+ message = aMessage;
+
+ this->from = from;
+ rcpt = to;
+ state = smtpInit;
+ command = "";
+
+ emit status( i18n( "Connecting to %1" ).arg( server ) );
+
+ mSocket->connectToHost( server, port );
+ t = new QTextStream( mSocket );
+ t->setEncoding(QTextStream::Latin1);
+}
+
+
+Smtp::~Smtp()
+{
+ if (t)
+ delete t;
+ if (mSocket)
+ delete mSocket;
+}
+
+
+void Smtp::send( const QString &from, const QStringList &to,
+ const QString &aMessage )
+{
+ skipReadResponse = true;
+ message = aMessage;
+ this->from = from;
+ rcpt = to;
+
+ state = smtpMail;
+ command = "";
+ readyRead();
+}
+
+
+void Smtp::quit()
+{
+ skipReadResponse = true;
+ state = smtpQuit;
+ command = "";
+ readyRead();
+}
+
+
+void Smtp::connected()
+{
+ emit status( i18n( "Connected to %1" ).arg( mSocket->peerName() ) );
+}
+
+void Smtp::socketError(int errorCode)
+{
+ command = "CONNECT";
+ switch ( errorCode ) {
+ case QSocket::ErrConnectionRefused:
+ responseLine = i18n( "Connection refused." );
+ break;
+ case QSocket::ErrHostNotFound:
+ responseLine = i18n( "Host Not Found." );
+ break;
+ case QSocket::ErrSocketRead:
+ responseLine = i18n( "Error reading socket." );
+ break;
+ default:
+ responseLine = i18n( "Internal error, unrecognized error." );
+ }
+ QTimer::singleShot( 0, this, SLOT(emitError()) );
+}
+
+void Smtp::emitError() {
+ error( command, responseLine );
+}
+
+void Smtp::readyRead()
+{
+ if (!skipReadResponse) {
+ // SMTP is line-oriented
+ if ( !mSocket->canReadLine() )
+ return;
+
+ do {
+ responseLine = mSocket->readLine();
+ response += responseLine;
+ } while( mSocket->canReadLine() && responseLine[3] != ' ' );
+ }
+ skipReadResponse = false;
+
+ if ( state == smtpInit && responseLine[0] == '2' ) {
+ // banner was okay, let's go on
+ command = "HELO there";
+ *t << "HELO there\r\n";
+ state = smtpMail;
+ } else if ( state == smtpMail && responseLine[0] == '2' ) {
+ // HELO response was okay (well, it has to be)
+ command = "MAIL";
+ *t << "MAIL FROM: <" << from << ">\r\n";
+ state = smtpRcpt;
+ } else if ( state == smtpRcpt && responseLine[0] == '2' && (rcpt.begin() != rcpt.end())) {
+ command = "RCPT";
+ *t << "RCPT TO: <" << *(rcpt.begin()) << ">\r\n";
+ rcpt.remove( rcpt.begin() );
+ if (rcpt.begin() == rcpt.end())
+ state = smtpData;
+ } else if ( state == smtpData && responseLine[0] == '2' ) {
+ command = "DATA";
+ *t << "DATA\r\n";
+ state = smtpBody;
+ } else if ( state == smtpBody && responseLine[0] == '3' ) {
+ command = "DATA";
+ QString seperator = "";
+ if (message[message.length() - 1] != '\n')
+ seperator = "\r\n";
+ *t << message << seperator << ".\r\n";
+ state = smtpSuccess;
+ } else if ( state == smtpSuccess && responseLine[0] == '2' ) {
+ QTimer::singleShot( 0, this, SIGNAL(success()) );
+ } else if ( state == smtpQuit && responseLine[0] == '2' ) {
+ command = "QUIT";
+ *t << "QUIT\r\n";
+ // here, we just close.
+ state = smtpClose;
+ emit status( i18n( "Message sent" ) );
+ } else if ( state == smtpClose ) {
+ // we ignore it
+ } else { // error occurred
+ QTimer::singleShot( 0, this, SLOT(emitError()) );
+ state = smtpClose;
+ }
+
+ response = "";
+
+ if ( state == smtpClose ) {
+ delete t;
+ t = 0;
+ delete mSocket;
+ mSocket = 0;
+ QTimer::singleShot( 0, this, SLOT(deleteMe()) );
+ }
+}
+
+
+void Smtp::deleteMe()
+{
+ delete this;
+}
+
+#include "smtp.moc"
diff --git a/kbugbuster/backend/smtp.h b/kbugbuster/backend/smtp.h
new file mode 100644
index 00000000..d800cb77
--- /dev/null
+++ b/kbugbuster/backend/smtp.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** This file is a modified version of part of an example program for Qt.
+** This file may be used, distributed and modified without limitation.
+**
+** Don Sanders <sanders@kde.org>
+**
+*****************************************************************************/
+
+#ifndef SMTP_H
+#define SMTP_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+class QSocket;
+class QTextStream;
+
+class Smtp : public QObject
+{
+ Q_OBJECT
+
+public:
+ Smtp( const QString &from, const QStringList &to, const QString &message,
+ const QString &server, unsigned short int port = 25 );
+ ~Smtp();
+ void send( const QString &, const QStringList &, const QString & );
+ void quit();
+
+
+signals:
+ void success();
+ void status( const QString & );
+ void error( const QString &command, const QString &response );
+
+private slots:
+ void readyRead();
+ void connected();
+ void deleteMe();
+ void socketError(int err);
+ void emitError();
+
+private:
+ enum State {
+ smtpInit,
+ smtpMail,
+ smtpRcpt,
+ smtpData,
+ smtpBody,
+ smtpSuccess,
+ smtpQuit,
+ smtpClose
+ };
+
+ QString message;
+ QString from;
+ QStringList rcpt;
+ QSocket *mSocket;
+ QTextStream * t;
+ int state;
+ QString response, responseLine;
+ bool skipReadResponse;
+ QString command;
+};
+
+#endif