summaryrefslogtreecommitdiffstats
path: root/cervisia/cvsservice
diff options
context:
space:
mode:
Diffstat (limited to 'cervisia/cvsservice')
-rw-r--r--cervisia/cvsservice/DESIGN108
-rw-r--r--cervisia/cvsservice/Makefile.am54
-rw-r--r--cervisia/cvsservice/TODO12
-rw-r--r--cervisia/cvsservice/cvsaskpass.cpp77
-rw-r--r--cervisia/cvsservice/cvsjob.cpp236
-rw-r--r--cervisia/cvsservice/cvsjob.h83
-rw-r--r--cervisia/cvsservice/cvsloginjob.cpp156
-rw-r--r--cervisia/cvsservice/cvsloginjob.h58
-rw-r--r--cervisia/cvsservice/cvsservice.cpp1008
-rw-r--r--cervisia/cvsservice/cvsservice.desktop73
-rw-r--r--cervisia/cvsservice/cvsservice.h375
-rw-r--r--cervisia/cvsservice/cvsserviceutils.cpp45
-rw-r--r--cervisia/cvsservice/cvsserviceutils.h40
-rw-r--r--cervisia/cvsservice/main.cpp46
-rw-r--r--cervisia/cvsservice/repository.cpp266
-rw-r--r--cervisia/cvsservice/repository.h109
-rw-r--r--cervisia/cvsservice/sshagent.cpp237
-rw-r--r--cervisia/cvsservice/sshagent.h64
18 files changed, 3047 insertions, 0 deletions
diff --git a/cervisia/cvsservice/DESIGN b/cervisia/cvsservice/DESIGN
new file mode 100644
index 00000000..cbd414da
--- /dev/null
+++ b/cervisia/cvsservice/DESIGN
@@ -0,0 +1,108 @@
+OVERVIEW
+--------
+
+The cvs DCOP service consists of the following three parts:
+
+1. CvsService - The main interface to the functionality of the cvs command line
+ client. There is one method for each cvs command, e.g. add,
+ checkout, commit, etc... The methods assemble the command line
+ arguments, create a CvsJob and return a DCOPRef object for it
+ to the caller. There is one instance of this service for each
+ application instance.
+
+2. Repository - This DCOPObject manages the configuration data of the current
+ cvs repository. The data is automatically updated when other
+ service instances change it.
+
+3. CvsJob - This class represents a cvs job. You can execute and cancel it,
+ and you can retrieve the output of the cvs client by either
+ connecting to the proper DCOP signals or by using the output()
+ method. There are two types of jobs. First the non-concurrent
+ job which has to run alone, like cvs update or import. Second
+ the jobs which can run concurrently like cvs log or annotate.
+
+USAGE
+-----
+
+How-to use this service in C++ applications:
+
+ // start DCOP service
+ QString error;
+ QCString appId;
+
+ KApplication::startServiceByDesktopName("cvsservice", QStringList(), &error,
+ &appId);
+
+ // create stub for repository
+ Repository_stub repository(appId, "CvsRepository");
+
+ // set directory of working copy
+ repository.setWorkingCopy("/home/user/kde/kdesdk/cervisia");
+
+ // create stub for service
+ CvsService_stub cvsService(appId, "CvsService");
+
+ // call "cvs log" for cervisiapart.h
+ DCOPRef job = cvsService.log("cervisiapart.h");
+
+ // connect to signals to get output
+ connectDCOPSignal(job.app(), job.obj(), "jobExited(bool, int)", [MY SLOT]);
+ connectDCOPSignal(job.app(), job.obj(), "receivedStdout(QString)",
+ [MY SLOT]);
+
+ // execute the cvs command
+ job.execute();
+
+
+How-to use this service in a shell script:
+
+ #!/bin/sh
+
+ # start DCOP service
+ APP=`dcopstart cvsservice`
+
+ # set directory of working copy
+ dcop $APP CvsRepository setWorkingCopy /home/user/kde/kdesdk/cervisia
+
+ # call "cvs log" for cervisiapart.h
+ JOB=`dcop $APP CvsService log cervisiapart.h`
+
+ # execute the cvs command
+ dcop $JOB execute
+
+ # print the output on stdout
+ dcop $JOB output
+
+ # stop DCOP service
+ dcop $APP CvsService quit
+
+
+How-to use this service in a javascript:
+
+ #!/usr/bin/env kjscmd
+
+ var client = new DCOPClient(this);
+ if ( client.attach() )
+ {
+ // start DCOP service
+ var appID = client.dcopStart("cvsservice");
+
+ // set directory of working copy
+ client.send(appID, "CvsRepository", "setWorkingCopy(QString)", "/home/user/kde/kdesdk/cervisia");
+
+ // call "cvs log" for cervisiapart.h
+ var job = client.call(appID, "CvsService", "log(QString)", "cervisiapart.h");
+
+ // execute the cvs command
+ job.call("execute()");
+
+ // wait for job to finish
+ while( job.call("isRunning()") );
+
+ // print the output on stdout
+ var output = job.call("output()");
+ println(output);
+
+ // stop DCOP service
+ client.send(appID, "CvsService", "quit()");
+ }
diff --git a/cervisia/cvsservice/Makefile.am b/cervisia/cvsservice/Makefile.am
new file mode 100644
index 00000000..7b036036
--- /dev/null
+++ b/cervisia/cvsservice/Makefile.am
@@ -0,0 +1,54 @@
+#
+# CvsService Makefile.am
+#
+# Copyright (c) 2002-2003 Christian Loose <christian.loose@hamburg.de>
+#
+#
+
+INCLUDES = $(all_includes)
+
+# cvs DCOP service
+bin_PROGRAMS =
+kdeinit_LTLIBRARIES = cvsservice.la cvsaskpass.la
+
+cvsservice_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+cvsservice_la_LIBADD = $(LIB_KIO)
+cvsservice_la_SOURCES = main.cpp cvsservice.cpp cvsjob.cpp \
+ cvsservice.skel cvsservice.stub cvsjob.skel cvsjob.stub \
+ repository.cpp repository.skel repository.stub sshagent.cpp \
+ cvsserviceutils.cpp cvsloginjob.cpp cvsloginjob.skel cvsloginjob.stub
+
+cvsaskpass_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+cvsaskpass_la_LIBADD = $(LIB_KDEUI)
+cvsaskpass_la_SOURCES = cvsaskpass.cpp
+
+include_HEADERS = cvsservice_stub.h cvsjob_stub.h repository_stub.h
+noinst_HEADERS = cvsservice.h cvsjob.h repository.h cvsserviceutils.h \
+ sshagent.h
+
+# cvs DCOP service stub library
+lib_LTLIBRARIES = libcvsservice.la
+
+libcvsservice_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 0:1
+libcvsservice_la_LIBADD = $(LIB_KDECORE)
+libcvsservice_la_SOURCES = cvsservice.stub cvsjob.stub repository.stub dummy.cpp
+
+dummy.cpp:
+ echo > dummy.cpp
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+# install .desktop file
+service_DATA = cvsservice.desktop
+servicedir = $(kde_servicesdir)
+
+# i18n
+messages:
+ $(XGETTEXT) *.cpp *.h -o $(podir)/cvsservice.pot
+
+# API documentation
+# Not activated because KDE_INIT_DOXYGEN is missing in
+# kdesdk's configure.in.in
+#DOXYGEN_REFERENCES = dcop kdecore kdeui
+#include ../../admin/Doxyfile.am
diff --git a/cervisia/cvsservice/TODO b/cervisia/cvsservice/TODO
new file mode 100644
index 00000000..d46d2f9a
--- /dev/null
+++ b/cervisia/cvsservice/TODO
@@ -0,0 +1,12 @@
+TODO
+----
+
+* Move handling of recent commit messages from CervisiaPart to
+ DCOP service
+
+* Add special job classes derived from CvsJob that take over
+ the parsing of cvs' output from Cervisia
+
+* Missing cvs functionality needed for Cervisia:
+ - cvs watch (add/remove)
+ - cvs diff (make patch)
diff --git a/cervisia/cvsservice/cvsaskpass.cpp b/cervisia/cvsservice/cvsaskpass.cpp
new file mode 100644
index 00000000..c5410dee
--- /dev/null
+++ b/cervisia/cvsservice/cvsaskpass.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2003 Christian Loose <christian.loose@hamburg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <qregexp.h>
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kpassdlg.h>
+
+#include <iostream>
+
+
+static KCmdLineOptions options[] =
+{
+ { "+[prompt]", I18N_NOOP("prompt"), 0 },
+ KCmdLineLastOption
+};
+
+
+extern "C" KDE_EXPORT int kdemain(int argc, char** argv)
+{
+ KAboutData about("cvsaskpass", I18N_NOOP("cvsaskpass"), "0.1",
+ I18N_NOOP("ssh-askpass for the CVS DCOP Service"),
+ KAboutData::License_LGPL,
+ I18N_NOOP("Copyright (c) 2003 Christian Loose"));
+
+ KCmdLineArgs::init(argc, argv, &about);
+ KCmdLineArgs::addCmdLineOptions(options);
+
+ // no need to register with the dcop server
+ KApplication::disableAutoDcopRegistration();
+ KApplication app;
+
+ // no need for session management
+ app.disableSessionManagement();
+
+ if( !KCmdLineArgs::parsedArgs()->count() )
+ return 1;
+
+ // parse repository name from the passed argument
+ QString prompt = KCmdLineArgs::parsedArgs()->arg(0);
+ QRegExp rx("(.*@.*)'s password:");
+ int pos = rx.search(prompt);
+
+ KPasswordDialog dlg(KPasswordDialog::Password, false, 0);
+ dlg.setPrompt(i18n("Please type in your password below."));
+
+ if( pos > -1 )
+ dlg.addLine(i18n("Repository:"), rx.cap(1));
+
+ int res = dlg.exec();
+ if( res == KPasswordDialog::Accepted )
+ {
+ std::cout << dlg.password() << std::endl;
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/cervisia/cvsservice/cvsjob.cpp b/cervisia/cvsservice/cvsjob.cpp
new file mode 100644
index 00000000..1178de96
--- /dev/null
+++ b/cervisia/cvsservice/cvsjob.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2002-2003 Christian Loose <christian.loose@hamburg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "cvsjob.h"
+
+#include <qfile.h>
+#include <kdebug.h>
+#include <kprocess.h>
+
+#include "sshagent.h"
+
+
+struct CvsJob::Private
+{
+ Private() : isRunning(false)
+ {
+ childproc = new KProcess;
+ childproc->setUseShell(true, "/bin/sh");
+ }
+ ~Private() { delete childproc; }
+
+ KProcess* childproc;
+ QString server;
+ QString rsh;
+ QString directory;
+ bool isRunning;
+ QStringList outputLines;
+};
+
+
+CvsJob::CvsJob(unsigned jobNum)
+ : QObject()
+ , DCOPObject()
+ , d(new Private)
+{
+ QString objId("CvsJob" + QString::number(jobNum));
+ setObjId(objId.local8Bit());
+}
+
+
+CvsJob::CvsJob(const QString& objId)
+ : QObject()
+ , DCOPObject()
+ , d(new Private)
+{
+ setObjId(objId.local8Bit());
+}
+
+
+CvsJob::~CvsJob()
+{
+ delete d;
+}
+
+
+void CvsJob::clearCvsCommand()
+{
+ d->childproc->clearArguments();
+}
+
+
+void CvsJob::setRSH(const QString& rsh)
+{
+ d->rsh = rsh;
+}
+
+
+void CvsJob::setServer(const QString& server)
+{
+ d->server = server;
+}
+
+
+void CvsJob::setDirectory(const QString& directory)
+{
+ d->directory = directory;
+}
+
+
+bool CvsJob::isRunning() const
+{
+ return d->isRunning;
+}
+
+
+CvsJob& CvsJob::operator<<(const QString& arg)
+{
+ *d->childproc << arg;
+ return *this;
+}
+
+
+CvsJob& CvsJob::operator<<(const char* arg)
+{
+ *d->childproc << arg;
+ return *this;
+}
+
+
+CvsJob& CvsJob::operator<<(const QCString& arg)
+{
+ *d->childproc << arg;
+ return *this;
+}
+
+
+CvsJob& CvsJob::operator<<(const QStringList& args)
+{
+ *d->childproc << args;
+ return *this;
+}
+
+
+QString CvsJob::cvsCommand() const
+{
+ QString command;
+
+ const QValueList<QCString>& args(d->childproc->args());
+ for (QValueList<QCString>::const_iterator it = args.begin(), itEnd = args.end();
+ it != itEnd; ++it)
+ {
+ if (!command.isEmpty())
+ command += ' ';
+
+ command += QFile::decodeName(*it);
+ }
+
+ return command;
+}
+
+
+QStringList CvsJob::output() const
+{
+ return d->outputLines;
+}
+
+
+bool CvsJob::execute()
+{
+ // setup job environment to use the ssh-agent (if it is running)
+ SshAgent ssh;
+ if( !ssh.pid().isEmpty() )
+ {
+ // kdDebug(8051) << "PID = " << ssh.pid() << endl;
+ // kdDebug(8051) << "SOCK = " << ssh.authSock() << endl;
+
+ d->childproc->setEnvironment("SSH_AGENT_PID", ssh.pid());
+ d->childproc->setEnvironment("SSH_AUTH_SOCK", ssh.authSock());
+ }
+
+ d->childproc->setEnvironment("SSH_ASKPASS", "cvsaskpass");
+
+ if( !d->rsh.isEmpty() )
+ d->childproc->setEnvironment("CVS_RSH", d->rsh);
+
+ if( !d->server.isEmpty() )
+ d->childproc->setEnvironment("CVS_SERVER", d->server);
+
+ if( !d->directory.isEmpty() )
+ d->childproc->setWorkingDirectory(d->directory);
+
+ connect(d->childproc, SIGNAL(processExited(KProcess*)),
+ SLOT(slotProcessExited()));
+ connect(d->childproc, SIGNAL(receivedStdout(KProcess*, char*, int)),
+ SLOT(slotReceivedStdout(KProcess*, char*, int)));
+ connect(d->childproc, SIGNAL(receivedStderr(KProcess*, char*, int)),
+ SLOT(slotReceivedStderr(KProcess*, char*, int)) );
+
+ kdDebug(8051) << "Execute cvs command: " << cvsCommand() << endl;
+
+ d->isRunning = true;
+ return d->childproc->start(KProcess::NotifyOnExit, KProcess::AllOutput);
+}
+
+
+void CvsJob::cancel()
+{
+ d->childproc->kill();
+}
+
+
+void CvsJob::slotProcessExited()
+{
+ // disconnect all connections to childproc's signals
+ d->childproc->disconnect();
+ d->childproc->clearArguments();
+
+ d->isRunning = false;
+
+ emit jobExited(d->childproc->normalExit(), d->childproc->exitStatus());
+}
+
+
+void CvsJob::slotReceivedStdout(KProcess* proc, char* buffer, int buflen)
+{
+ Q_UNUSED(proc);
+
+ QString output = QString::fromLocal8Bit(buffer, buflen);
+
+ // accumulate output
+ d->outputLines += QStringList::split("\n", output);
+
+ emit receivedStdout(output);
+}
+
+
+void CvsJob::slotReceivedStderr(KProcess* proc, char* buffer, int buflen)
+{
+ Q_UNUSED(proc);
+
+ QString output = QString::fromLocal8Bit(buffer, buflen);
+
+ // accumulate output
+ d->outputLines += QStringList::split("\n", output);
+
+ emit receivedStderr(output);
+}
+
+#include "cvsjob.moc"
diff --git a/cervisia/cvsservice/cvsjob.h b/cervisia/cvsservice/cvsjob.h
new file mode 100644
index 00000000..14194499
--- /dev/null
+++ b/cervisia/cvsservice/cvsjob.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2002-2003 Christian Loose <christian.loose@hamburg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef CVSJOB_H
+#define CVSJOB_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <dcopobject.h>
+
+class KProcess;
+
+
+class KDE_EXPORT CvsJob : public QObject, public DCOPObject
+{
+ Q_OBJECT
+ K_DCOP
+
+public:
+ explicit CvsJob(unsigned jobNum);
+ explicit CvsJob(const QString& objId);
+ virtual ~CvsJob();
+
+ void clearCvsCommand();
+ void setRSH(const QString& rsh);
+ void setServer(const QString& server);
+ void setDirectory(const QString& directory);
+
+ CvsJob& operator<<(const QString& arg);
+ CvsJob& operator<<(const char* arg);
+ CvsJob& operator<<(const QCString& arg);
+ CvsJob& operator<<(const QStringList& args);
+
+k_dcop:
+ bool execute();
+ void cancel();
+
+ bool isRunning() const;
+
+ /**
+ * Current cvs command.
+ *
+ * @return The current cvs command. Can be null if not set.
+ */
+ QString cvsCommand() const;
+
+ QStringList output() const;
+
+k_dcop_signals:
+ void jobExited(bool normalExit, int status);
+ void receivedStdout(const QString& buffer);
+ void receivedStderr(const QString& buffer);
+
+private slots:
+ void slotProcessExited();
+ void slotReceivedStdout(KProcess* proc, char* buffer, int buflen);
+ void slotReceivedStderr(KProcess* proc, char* buffer, int buflen);
+
+private:
+ struct Private;
+ Private* d;
+};
+
+
+#endif
diff --git a/cervisia/cvsservice/cvsloginjob.cpp b/cervisia/cvsservice/cvsloginjob.cpp
new file mode 100644
index 00000000..79a9fa08
--- /dev/null
+++ b/cervisia/cvsservice/cvsloginjob.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2003 Christian Loose <christian.loose@hamburg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "cvsloginjob.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpassdlg.h>
+
+#include <sys/types.h>
+#include <signal.h>
+
+static const char LOGIN_PHRASE[] = "Logging in to";
+static const char FAILURE_PHRASE[] = "authorization failed:";
+static const char PASS_PHRASE[] = "CVS password: ";
+
+
+CvsLoginJob::CvsLoginJob(unsigned jobNum)
+ : DCOPObject()
+ , m_Proc(0)
+{
+ QString objId("CvsLoginJob" + QString::number(jobNum));
+ setObjId(objId.local8Bit());
+
+ m_Proc = new PtyProcess;
+}
+
+
+CvsLoginJob::~CvsLoginJob()
+{
+ delete m_Proc;
+}
+
+
+void CvsLoginJob::setServer(const QString& server)
+{
+ m_Server = server;
+}
+
+
+void CvsLoginJob::setCvsClient(const QCString& cvsClient)
+{
+ m_CvsClient = cvsClient;
+
+ m_Arguments.clear();
+ m_Arguments += "-f";
+}
+
+
+void CvsLoginJob::setRepository(const QCString& repository)
+{
+ m_Arguments += "-d";
+ m_Arguments += repository;
+ m_Arguments += "login";
+}
+
+
+bool CvsLoginJob::execute()
+{
+ static QCString repository;
+
+ int res = m_Proc->exec(m_CvsClient, m_Arguments);
+ if( res < 0 )
+ {
+ kdDebug(8051) << "Couldn't start 'cvs login' process!" << endl;
+ return false;
+ }
+
+ bool result = false;
+ while( true )
+ {
+ QCString line = m_Proc->readLine();
+ if( line.isNull() )
+ {
+ return result;
+ }
+
+ // add line to output list
+ m_output << line;
+ kdDebug(8051) << "process output = " << line << endl;
+
+ // retrieve repository from 'Logging in to'-line
+ if( line.contains(LOGIN_PHRASE) )
+ {
+ repository = line.remove(0, line.find(":pserver:"));
+ continue;
+ }
+
+ // process asks for the password
+ // search case insensitive as cvs and cvsnt use different capitalization
+ if( line.contains(PASS_PHRASE, false) )
+ {
+ kdDebug(8051) << "process waits for the password." << endl;
+
+ // show password dialog
+ // TODO: We really should display the repository name. Unfortunately
+ // the dialog doesn't show part of the repository name, because
+ // it's too long. :-(
+ QCString password;
+ int res = KPasswordDialog::getPassword(password, i18n("Please type "
+ "in your password for the repository below."));
+ if( res == KPasswordDialog::Accepted )
+ {
+ // send password to process
+ m_Proc->WaitSlave();
+ m_Proc->writeLine(password);
+
+ // wait for the result
+ while( !line.contains(FAILURE_PHRASE) )
+ {
+ line = m_Proc->readLine();
+ if( line.isNull() )
+ return true;
+
+ // add line to output list
+ m_output << line;
+ kdDebug(8051) << "process output = " << line << endl;
+ }
+
+ result = false;
+ }
+ else
+ {
+ // user pressed cancel so kill the process
+ kill(m_Proc->pid(), SIGKILL);
+ m_Proc->waitForChild();
+ result = false;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+QStringList CvsLoginJob::output()
+{
+ return m_output;
+}
diff --git a/cervisia/cvsservice/cvsloginjob.h b/cervisia/cvsservice/cvsloginjob.h
new file mode 100644
index 00000000..0754074c
--- /dev/null
+++ b/cervisia/cvsservice/cvsloginjob.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2003 Christian Loose <christian.loose@hamburg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef CVSLOGINJOB_H
+#define CVSLOGINJOB_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <dcopobject.h>
+
+#include <kdesu/process.h>
+
+
+class CvsLoginJob : public DCOPObject
+{
+ K_DCOP
+
+public:
+ explicit CvsLoginJob(unsigned jobNum);
+ virtual ~CvsLoginJob();
+
+ void setServer(const QString& server);
+
+ void setCvsClient(const QCString& cvsClient);
+ void setRepository(const QCString& repository);
+
+k_dcop:
+ bool execute();
+ QStringList output();
+
+private:
+ PtyProcess* m_Proc;
+ QString m_Server;
+ QString m_Rsh;
+ QCString m_CvsClient;
+ QCStringList m_Arguments;
+ QStringList m_output;
+};
+
+
+#endif
diff --git a/cervisia/cvsservice/cvsservice.cpp b/cervisia/cvsservice/cvsservice.cpp
new file mode 100644
index 00000000..a5f02e20
--- /dev/null
+++ b/cervisia/cvsservice/cvsservice.cpp
@@ -0,0 +1,1008 @@
+/*
+ * Copyright (c) 2002-2004 Christian Loose <christian.loose@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "cvsservice.h"
+
+#include <qintdict.h>
+#include <qstring.h>
+
+#include <dcopref.h>
+#include <dcopclient.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+
+#include "cvsjob.h"
+#include "cvsloginjob.h"
+#include "cvsserviceutils.h"
+#include "repository.h"
+#include "sshagent.h"
+
+
+static const char SINGLE_JOB_ID[] = "NonConcurrentJob";
+static const char REDIRECT_STDERR[] = "2>&1";
+
+enum WatchEvents { None=0, All=1, Commits=2, Edits=4, Unedits=8 };
+
+struct CvsService::Private
+{
+ Private() : singleCvsJob(0), lastJobId(0), repository(0) {}
+ ~Private()
+ {
+ delete repository;
+ delete singleCvsJob;
+ }
+
+ CvsJob* singleCvsJob; // non-concurrent cvs job, like update or commit
+ DCOPRef singleJobRef; // DCOP reference to non-concurrent cvs job
+ QIntDict<CvsJob> cvsJobs; // concurrent cvs jobs, like diff or annotate
+ QIntDict<CvsLoginJob> loginJobs;
+ unsigned lastJobId;
+
+ QCString appId; // cache the DCOP clients app id
+
+ Repository* repository;
+
+ CvsJob* createCvsJob();
+ DCOPRef setupNonConcurrentJob(Repository* repo = 0);
+
+ bool hasWorkingCopy();
+ bool hasRunningJob();
+};
+
+
+CvsService::CvsService()
+ : DCOPObject("CvsService")
+ , d(new Private)
+{
+ d->appId = kapp->dcopClient()->appId();
+
+ // create non-concurrent cvs job
+ d->singleCvsJob = new CvsJob(SINGLE_JOB_ID);
+ d->singleJobRef.setRef(d->appId, d->singleCvsJob->objId());
+
+ // create repository manager
+ d->repository = new Repository();
+
+ d->cvsJobs.setAutoDelete(true);
+ d->loginJobs.setAutoDelete(true);
+
+ KConfig* config = kapp->config();
+ KConfigGroupSaver cs(config, "General");
+ if( config->readBoolEntry("UseSshAgent", false) )
+ {
+ // use the existing or start a new ssh-agent
+ SshAgent ssh;
+ // TODO CL do we need the return value?
+ //bool res = ssh.querySshAgent();
+ ssh.querySshAgent();
+ }
+}
+
+
+CvsService::~CvsService()
+{
+ // kill the ssh-agent (when we started it)
+ SshAgent ssh;
+ ssh.killSshAgent();
+
+ d->cvsJobs.clear();
+ d->loginJobs.clear();
+ delete d;
+}
+
+
+DCOPRef CvsService::add(const QStringList& files, bool isBinary)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs add [-kb] [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "add";
+
+ if( isBinary )
+ *d->singleCvsJob << "-kb";
+
+ *d->singleCvsJob << CvsServiceUtils::joinFileList(files) << REDIRECT_STDERR;
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::addWatch(const QStringList& files, int events)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "watch add";
+
+ if( events != All )
+ {
+ if( events & Commits )
+ *d->singleCvsJob << "-a commit";
+ if( events & Edits )
+ *d->singleCvsJob << "-a edit";
+ if( events & Unedits )
+ *d->singleCvsJob << "-a unedit";
+ }
+
+ *d->singleCvsJob << CvsServiceUtils::joinFileList(files);
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::annotate(const QString& fileName, const QString& revision)
+{
+ if( !d->hasWorkingCopy() )
+ return DCOPRef();
+
+ // create a cvs job
+ CvsJob* job = d->createCvsJob();
+
+ // assemble the command line
+ // (cvs log [FILE] && cvs annotate [-r rev] [FILE])
+ QString quotedName = KProcess::quote(fileName);
+ QString cvsClient = d->repository->cvsClient();
+
+ *job << "(" << cvsClient << "log" << quotedName << "&&"
+ << cvsClient << "annotate";
+
+ if( !revision.isEmpty() )
+ *job << "-r" << revision;
+
+ // *Hack*
+ // because the string "Annotations for blabla" is
+ // printed to stderr even with option -Q.
+ *job << quotedName << ")" << REDIRECT_STDERR;
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::checkout(const QString& workingDir, const QString& repository,
+ const QString& module, const QString& tag,
+ bool pruneDirs)
+{
+ if( d->hasRunningJob() )
+ return DCOPRef();
+
+ Repository repo(repository);
+
+ // assemble the command line
+ // cd [DIRECTORY] && cvs -d [REPOSITORY] checkout [-r tag] [-P] [MODULE]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << "cd" << KProcess::quote(workingDir) << "&&"
+ << repo.cvsClient()
+ << "-d" << repository
+ << "checkout";
+
+ if( !tag.isEmpty() )
+ *d->singleCvsJob << "-r" << tag;
+
+ if( pruneDirs )
+ *d->singleCvsJob << "-P";
+
+ *d->singleCvsJob << module;
+
+ return d->setupNonConcurrentJob(&repo);
+}
+
+
+DCOPRef CvsService::checkout(const QString& workingDir, const QString& repository,
+ const QString& module, const QString& tag,
+ bool pruneDirs, const QString& alias, bool exportOnly)
+{
+ if( d->hasRunningJob() )
+ return DCOPRef();
+
+ Repository repo(repository);
+
+ // assemble the command line
+ // cd [DIRECTORY] && cvs -d [REPOSITORY] co [-r tag] [-P] [-d alias] [MODULE]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << "cd" << KProcess::quote(workingDir) << "&&"
+ << repo.cvsClient()
+ << "-d" << repository;
+ if( exportOnly)
+ *d->singleCvsJob << "export";
+ else
+ *d->singleCvsJob << "checkout";
+
+ if( !tag.isEmpty() )
+ *d->singleCvsJob << "-r" << tag;
+
+ if( pruneDirs && !exportOnly )
+ *d->singleCvsJob << "-P";
+
+ if( !alias.isEmpty() )
+ *d->singleCvsJob << "-d" << alias;
+
+ *d->singleCvsJob << module;
+
+ return d->setupNonConcurrentJob(&repo);
+}
+
+DCOPRef CvsService::checkout(const QString& workingDir, const QString& repository,
+ const QString& module, const QString& tag,
+ bool pruneDirs, const QString& alias, bool exportOnly,
+ bool recursive)
+{
+ if( d->hasRunningJob() )
+ return DCOPRef();
+
+ Repository repo(repository);
+
+ // assemble the command line
+ // cd [DIRECTORY] && cvs -d [REPOSITORY] co [-r tag] [-P] [-d alias] [MODULE]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << "cd" << KProcess::quote(workingDir) << "&&"
+ << repo.cvsClient()
+ << "-d" << repository;
+ if( exportOnly)
+ *d->singleCvsJob << "export";
+ else
+ *d->singleCvsJob << "checkout";
+
+ if( !tag.isEmpty() )
+ *d->singleCvsJob << "-r" << tag;
+
+ if( pruneDirs && !exportOnly )
+ *d->singleCvsJob << "-P";
+
+ if( !alias.isEmpty() )
+ *d->singleCvsJob << "-d" << alias;
+
+ if( ! recursive )
+ *d->singleCvsJob << "-l";
+
+ *d->singleCvsJob << module;
+
+ return d->setupNonConcurrentJob(&repo);
+}
+
+DCOPRef CvsService::commit(const QStringList& files, const QString& commitMessage,
+ bool recursive)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs commit [-l] [-m MESSAGE] [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "commit";
+
+ if( !recursive )
+ *d->singleCvsJob << "-l";
+
+ *d->singleCvsJob << "-m" << KProcess::quote(commitMessage)
+ << CvsServiceUtils::joinFileList(files) << REDIRECT_STDERR;
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::createRepository(const QString& repository)
+{
+ if( d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs -d [REPOSITORY] init
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << "mkdir -p" << KProcess::quote(repository) << "&&"
+ << d->repository->cvsClient()
+ << "-d" << KProcess::quote(repository)
+ << "init";
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::createTag(const QStringList& files, const QString& tag,
+ bool branch, bool force)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs tag [-b] [-F] [TAG] [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "tag";
+
+ if( branch )
+ *d->singleCvsJob << "-b";
+
+ if( force )
+ *d->singleCvsJob << "-F";
+
+ *d->singleCvsJob << KProcess::quote(tag)
+ << CvsServiceUtils::joinFileList(files);
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::deleteTag(const QStringList& files, const QString& tag,
+ bool branch, bool force)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs tag -d [-b] [-F] [TAG] [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "tag" << "-d";
+
+ if( branch )
+ *d->singleCvsJob << "-b";
+
+ if( force )
+ *d->singleCvsJob << "-F";
+
+ *d->singleCvsJob << KProcess::quote(tag)
+ << CvsServiceUtils::joinFileList(files);
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::downloadCvsIgnoreFile(const QString& repository,
+ const QString& outputFile)
+{
+ Repository repo(repository);
+
+ // create a cvs job
+ CvsJob* job = d->createCvsJob();
+
+ // assemble the command line
+ // cvs -d [REPOSITORY] -q checkout -p CVSROOT/cvsignore > [OUTPUTFILE]
+ *job << repo.cvsClient() << "-d" << repository
+ << "-q checkout -p CVSROOT/cvsignore >"
+ << KProcess::quote(outputFile);
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::downloadRevision(const QString& fileName,
+ const QString& revision,
+ const QString& outputFile)
+{
+ if( !d->hasWorkingCopy() )
+ return DCOPRef();
+
+ // create a cvs job
+ CvsJob* job = d->createCvsJob();
+
+ // assemble the command line
+ // cvs update -p -r [REV] [FILE] > [OUTPUTFILE]
+ *job << d->repository->cvsClient() << "update -p";
+
+ if( !revision.isEmpty() )
+ *job << "-r" << KProcess::quote(revision);
+
+ *job << KProcess::quote(fileName) << ">" << KProcess::quote(outputFile);
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::downloadRevision(const QString& fileName,
+ const QString& revA,
+ const QString& outputFileA,
+ const QString& revB,
+ const QString& outputFileB)
+{
+ if( !d->hasWorkingCopy() )
+ return DCOPRef();
+
+ // create a cvs job
+ CvsJob* job = d->createCvsJob();
+
+ // assemble the command line
+ // cvs update -p -r [REVA] [FILE] > [OUTPUTFILEA] ;
+ // cvs update -p -r [REVB] [FILE] > [OUTPUTFILEB]
+ *job << d->repository->cvsClient() << "update -p"
+ << "-r" << KProcess::quote(revA)
+ << KProcess::quote(fileName) << ">" << KProcess::quote(outputFileA)
+ << ";" << d->repository->cvsClient() << "update -p"
+ << "-r" << KProcess::quote(revB)
+ << KProcess::quote(fileName) << ">" << KProcess::quote(outputFileB);
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::diff(const QString& fileName, const QString& revA,
+ const QString& revB, const QString& diffOptions,
+ unsigned contextLines)
+{
+ // cvs diff [DIFFOPTIONS] -U CONTEXTLINES [-r REVA] {-r REVB] [FILE]
+ QString format = "-U" + QString::number(contextLines);
+ return diff(fileName, revA, revB, diffOptions, format);
+}
+
+
+DCOPRef CvsService::diff(const QString& fileName, const QString& revA,
+ const QString& revB, const QString& diffOptions,
+ const QString& format)
+{
+ if( !d->hasWorkingCopy() )
+ return DCOPRef();
+
+ // create a cvs job
+ CvsJob* job = d->createCvsJob();
+
+ // assemble the command line
+ // cvs diff [DIFFOPTIONS] [FORMAT] [-r REVA] {-r REVB] [FILE]
+ *job << d->repository->cvsClient() << "diff" << diffOptions
+ << format;
+
+ if( !revA.isEmpty() )
+ *job << "-r" << KProcess::quote(revA);
+
+ if( !revB.isEmpty() )
+ *job << "-r" << KProcess::quote(revB);
+
+ *job << KProcess::quote(fileName);
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::edit(const QStringList& files)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs edit [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "edit"
+ << CvsServiceUtils::joinFileList(files);
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::editors(const QStringList& files)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs editors [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "editors"
+ << CvsServiceUtils::joinFileList(files);
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::history()
+{
+ if( !d->hasWorkingCopy() )
+ return DCOPRef();
+
+ // create a cvs job
+ CvsJob* job = d->createCvsJob();
+
+ // assemble the command line
+ // cvs history -e -a
+ *job << d->repository->cvsClient() << "history -e -a";
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::import(const QString& workingDir, const QString& repository,
+ const QString& module, const QString& ignoreList,
+ const QString& comment, const QString& vendorTag,
+ const QString& releaseTag, bool importAsBinary)
+{
+ if( d->hasRunningJob() )
+ return DCOPRef();
+
+ Repository repo(repository);
+
+ // assemble the command line
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << "cd" << KProcess::quote(workingDir) << "&&"
+ << repo.cvsClient()
+ << "-d" << repository
+ << "import";
+
+ if( importAsBinary )
+ *d->singleCvsJob << "-kb";
+
+ const QString ignore = ignoreList.stripWhiteSpace();
+ if( !ignore.isEmpty() )
+ *d->singleCvsJob << "-I" << KProcess::quote(ignore);
+
+ QString logMessage = comment.stripWhiteSpace();
+ logMessage.prepend("\"");
+ logMessage.append("\"");
+ *d->singleCvsJob << "-m" << logMessage;
+
+ *d->singleCvsJob << module << vendorTag << releaseTag;
+
+ return d->setupNonConcurrentJob(&repo);
+}
+
+
+DCOPRef CvsService::import(const QString& workingDir, const QString& repository,
+ const QString& module, const QString& ignoreList,
+ const QString& comment, const QString& vendorTag,
+ const QString& releaseTag, bool importAsBinary,
+ bool useModificationTime)
+{
+ if( d->hasRunningJob() )
+ return DCOPRef();
+
+ Repository repo(repository);
+
+ // assemble the command line
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << "cd" << KProcess::quote(workingDir) << "&&"
+ << repo.cvsClient()
+ << "-d" << repository
+ << "import";
+
+ if( importAsBinary )
+ *d->singleCvsJob << "-kb";
+
+ if( useModificationTime )
+ *d->singleCvsJob << "-d";
+
+ const QString ignore = ignoreList.stripWhiteSpace();
+ if( !ignore.isEmpty() )
+ *d->singleCvsJob << "-I" << KProcess::quote(ignore);
+
+ QString logMessage = comment.stripWhiteSpace();
+ logMessage.prepend("\"");
+ logMessage.append("\"");
+ *d->singleCvsJob << "-m" << logMessage;
+
+ *d->singleCvsJob << module << vendorTag << releaseTag;
+
+ return d->setupNonConcurrentJob(&repo);
+}
+
+
+DCOPRef CvsService::lock(const QStringList& files)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs admin -l [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "admin -l"
+ << CvsServiceUtils::joinFileList(files);
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::log(const QString& fileName)
+{
+ if( !d->hasWorkingCopy() )
+ return DCOPRef();
+
+ // create a cvs job
+ CvsJob* job = d->createCvsJob();
+
+ // assemble the command line
+ // cvs log [FILE]
+ *job << d->repository->cvsClient() << "log" << KProcess::quote(fileName);
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::login(const QString& repository)
+{
+ if( repository.isEmpty() )
+ return DCOPRef();
+
+ Repository repo(repository);
+
+ // create a cvs job
+ ++(d->lastJobId);
+
+ CvsLoginJob* job = new CvsLoginJob(d->lastJobId);
+ d->loginJobs.insert(d->lastJobId, job);
+
+ // TODO: CVS_SERVER doesn't work ATM
+// job->setServer(repo.server());
+
+ // assemble the command line
+ // cvs -d [REPOSITORY] login
+ job->setCvsClient(repo.clientOnly().local8Bit());
+ job->setRepository(repository.local8Bit());
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::logout(const QString& repository)
+{
+ if( repository.isEmpty() )
+ return DCOPRef();
+
+ Repository repo(repository);
+
+ // create a cvs job
+ ++(d->lastJobId);
+
+ CvsJob* job = new CvsJob(d->lastJobId);
+ d->cvsJobs.insert(d->lastJobId, job);
+
+ job->setRSH(repo.rsh());
+ job->setServer(repo.server());
+ job->setDirectory(repo.workingCopy());
+
+ // assemble the command line
+ // cvs -d [REPOSITORY] logout
+ *job << repo.cvsClient() << "-d" << repository << "logout";
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::makePatch()
+{
+ return makePatch("", "-u");
+}
+
+
+DCOPRef CvsService::makePatch(const QString& diffOptions, const QString& format)
+{
+ if( !d->hasWorkingCopy() )
+ return DCOPRef();
+
+ // create a cvs job
+ CvsJob* job = d->createCvsJob();
+
+ // assemble the command line
+ // cvs diff [DIFFOPTIONS] [FORMAT] -R 2>/dev/null
+ *job << d->repository->cvsClient() << "diff" << diffOptions << format << "-R"
+ << "2>/dev/null";
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::moduleList(const QString& repository)
+{
+ Repository repo(repository);
+
+ // create a cvs job
+ ++(d->lastJobId);
+
+ CvsJob* job = new CvsJob(d->lastJobId);
+ d->cvsJobs.insert(d->lastJobId, job);
+
+ job->setRSH(repo.rsh());
+ job->setServer(repo.server());
+ job->setDirectory(repo.workingCopy());
+
+ // assemble the command line
+ // cvs -d [REPOSITORY] checkout -c
+ *job << repo.cvsClient() << "-d" << repository << "checkout -c";
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::remove(const QStringList& files, bool recursive)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs remove -f [-l] [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "remove -f";
+
+ if( !recursive )
+ *d->singleCvsJob << "-l";
+
+ *d->singleCvsJob << CvsServiceUtils::joinFileList(files) << REDIRECT_STDERR;
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::removeWatch(const QStringList& files, int events)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "watch remove";
+
+ if( events != All )
+ {
+ if( events & Commits )
+ *d->singleCvsJob << "-a commit";
+ if( events & Edits )
+ *d->singleCvsJob << "-a edit";
+ if( events & Unedits )
+ *d->singleCvsJob << "-a unedit";
+ }
+
+ *d->singleCvsJob << CvsServiceUtils::joinFileList(files);
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::rlog(const QString& repository, const QString& module,
+ bool recursive)
+{
+ Repository repo(repository);
+
+ // create a cvs job
+ ++(d->lastJobId);
+
+ CvsJob* job = new CvsJob(d->lastJobId);
+ d->cvsJobs.insert(d->lastJobId, job);
+
+ job->setRSH(repo.rsh());
+ job->setServer(repo.server());
+
+ // assemble the command line
+ // cvs -d [REPOSITORY] rlog [-l] [MODULE]
+ *job << repo.cvsClient() << "-d" << repository << "rlog";
+
+ if( !recursive )
+ *job << "-l";
+
+ *job << module;
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::simulateUpdate(const QStringList& files, bool recursive,
+ bool createDirs, bool pruneDirs)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs -n update [-l] [-d] [-P] [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "-n -q update";
+
+ if( !recursive )
+ *d->singleCvsJob << "-l";
+
+ if( createDirs )
+ *d->singleCvsJob << "-d";
+
+ if( pruneDirs )
+ *d->singleCvsJob << "-P";
+
+ *d->singleCvsJob << CvsServiceUtils::joinFileList(files) << REDIRECT_STDERR;
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::status(const QStringList& files, bool recursive, bool tagInfo)
+{
+ if( !d->hasWorkingCopy() )
+ return DCOPRef();
+
+ // create a cvs job
+ CvsJob* job = d->createCvsJob();
+
+ // assemble the command line
+ // cvs status [-l] [-v] [FILES]
+ *job << d->repository->cvsClient() << "status";
+
+ if( !recursive )
+ *job << "-l";
+
+ if( tagInfo )
+ *job << "-v";
+
+ *job << CvsServiceUtils::joinFileList(files);
+
+ // return a DCOP reference to the cvs job
+ return DCOPRef(d->appId, job->objId());
+}
+
+
+DCOPRef CvsService::unedit(const QStringList& files)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // echo y | cvs unedit [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << "echo y |"
+ << d->repository->cvsClient() << "unedit"
+ << CvsServiceUtils::joinFileList(files);
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::unlock(const QStringList& files)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs admin -u [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "admin -u"
+ << CvsServiceUtils::joinFileList(files);
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::update(const QStringList& files, bool recursive,
+ bool createDirs, bool pruneDirs, const QString& extraOpt)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs update [-l] [-d] [-P] [EXTRAOPTIONS] [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "-q update";
+
+ if( !recursive )
+ *d->singleCvsJob << "-l";
+
+ if( createDirs )
+ *d->singleCvsJob << "-d";
+
+ if( pruneDirs )
+ *d->singleCvsJob << "-P";
+
+ *d->singleCvsJob << extraOpt << CvsServiceUtils::joinFileList(files)
+ << REDIRECT_STDERR;
+
+ return d->setupNonConcurrentJob();
+}
+
+
+DCOPRef CvsService::watchers(const QStringList& files)
+{
+ if( !d->hasWorkingCopy() || d->hasRunningJob() )
+ return DCOPRef();
+
+ // assemble the command line
+ // cvs watchers [FILES]
+ d->singleCvsJob->clearCvsCommand();
+
+ *d->singleCvsJob << d->repository->cvsClient() << "watchers"
+ << CvsServiceUtils::joinFileList(files);
+
+ return d->setupNonConcurrentJob();
+}
+
+
+void CvsService::quit()
+{
+ kapp->quit();
+}
+
+
+CvsJob* CvsService::Private::createCvsJob()
+{
+ ++lastJobId;
+
+ // create a cvs job
+ CvsJob* job = new CvsJob(lastJobId);
+ cvsJobs.insert(lastJobId, job);
+
+ job->setRSH(repository->rsh());
+ job->setServer(repository->server());
+ job->setDirectory(repository->workingCopy());
+
+ return job;
+}
+
+
+DCOPRef CvsService::Private::setupNonConcurrentJob(Repository* repo)
+{
+ // no explicit repository provided?
+ if( !repo )
+ repo = repository;
+
+ singleCvsJob->setRSH(repo->rsh());
+ singleCvsJob->setServer(repo->server());
+ singleCvsJob->setDirectory(repo->workingCopy());
+
+ return singleJobRef;
+}
+
+
+bool CvsService::Private::hasWorkingCopy()
+{
+ if( repository->workingCopy().isEmpty() )
+ {
+ KMessageBox::sorry(0, i18n("You have to set a local working copy "
+ "directory before you can use this function!"));
+ return false;
+ }
+
+ return true;
+}
+
+
+bool CvsService::Private::hasRunningJob()
+{
+ bool result = singleCvsJob->isRunning();
+
+ if( result )
+ KMessageBox::sorry(0, i18n("There is already a job running"));
+
+ return result;
+}
diff --git a/cervisia/cvsservice/cvsservice.desktop b/cervisia/cvsservice/cvsservice.desktop
new file mode 100644
index 00000000..31cd6dad
--- /dev/null
+++ b/cervisia/cvsservice/cvsservice.desktop
@@ -0,0 +1,73 @@
+[Desktop Entry]
+Type=Service
+Name=CvsService
+Name[br]=Servij Cvs
+Name[bs]=CvsServis
+Name[ca]=Servei de CVS
+Name[cs]=CVS služba
+Name[cy]=GwasanaethCVS
+Name[de]=CVS-Dienst
+Name[el]=Cvs υπηρεσία
+Name[eo]=CvsServo
+Name[es]=Servicio CVS
+Name[et]=CVS teenus
+Name[fi]=Cvs-palvelu
+Name[gl]=Servizo CVS
+Name[hi]=सीवीएस-सर्विस
+Name[hu]=CVS szolgáltatás
+Name[it]=Servizio CVS
+Name[ja]=CVS サービス
+Name[lt]=CvsTarnyba
+Name[nds]=CVS-Deenst
+Name[pt_BR]=ServiçoCVS
+Name[sv]=CVS-tjänst
+Name[ta]=Cvsசேவை
+Name[tg]=ҲизматиCvs
+Name[zh_TW]=CVS 服務
+Exec=cvsservice
+X-DCOP-ServiceType=Multi
+X-KDE-StartupNotify=false
+Comment=A DCOP service that provides an interface to cvs
+Comment[bg]=Услуга на DCOP, която предлага интерфейс към CVS
+Comment[bs]=DCOP servis koji pruža interfejs na CVS
+Comment[ca]=Un servei DCOP que proporciona una interfície per al cvs
+Comment[cs]=DCOP služba poskytující rozhraní k CVS
+Comment[cy]=Gwasanaeth DCOP sy'n darparu rhyngwyneb i cvs
+Comment[da]=En DCOP-tjeneste der sørger for en CVS-grænseflade
+Comment[de]=Ein DCOP-Dienst, der eine Schnittstelle zu CVS bereitstellt
+Comment[el]=Μια υπηρεσία DCOP που προσφέρει ένα περιβάλλον χρήσης για το cvs
+Comment[es]=Un servicio DCOP que proporciona una interfaz para cvs
+Comment[et]=CVSi DCOP liidese teenus
+Comment[eu]=cvs-rako interfazea eskeintzen duen DCOP zerbitzua
+Comment[fa]=خدمت DCOP که واسطی را برای cvs فراهم می‌کند
+Comment[fi]=DCOP-palvelu, joka tarjoaa rajapinnan cvs:lle
+Comment[fr]=Un service DCOP qui fournit une interface à CVS
+Comment[gl]=Un servizo de DCOP que fornece unha interface para CVS
+Comment[hi]=एक डीकॉप सर्विस जो सीवीएस को इंटरफेस प्रदान करता है
+Comment[hu]=DCOP-alapú szolgáltatás a CVS eléréséhez
+Comment[is]=DCOP þjónusta sem veitir viðmót á cvs
+Comment[it]=Un servizio DCOP che fornisce un'interfaccia a cvs
+Comment[ja]=CVS インターフェースを提供する DCOP サービス
+Comment[ka]=DCOP სერვისი, რომელიც cvs-ს ინტერფეისს შეიცავს
+Comment[kk]=CVS интерфейсін қамтамасыз ететін DCOP қызметі
+Comment[lt]=DCOP tarnyba, pateikianti cvs sąsają
+Comment[nb]=En DCOP-tjeneste som tilbyr et grensesnitt mot cvs
+Comment[nds]=En DCOP-Deenst, wat en Koppelsteed na CVS praatstellt
+Comment[ne]=cvs मा इन्टरफेस उपलब्ध गर्ने एउटा डीसीओपी कार्य
+Comment[nl]=Een DCOP-dienst die een interface naar cvs biedt
+Comment[nn]=Ei DCOP-teneste som tilbyr eit grensesnitt mot CVS
+Comment[pl]=Usługa DCOP pozwalająca na dostęp do CVS
+Comment[pt]=Um serviço de DCOP que oferece uma interface para o CVS
+Comment[pt_BR]=Um serviço DCOP que provê uma interface para o cvs
+Comment[ru]=Сервис DCOP для интерфейса с cvs
+Comment[sk]=Služba DCOP pre prístup k CVS
+Comment[sl]=Storitev DCOP, ki omogoča vmesnik do CVS
+Comment[sr]=DCOP сервис који пружа интерфејс за CVS
+Comment[sr@Latn]=DCOP servis koji pruža interfejs za CVS
+Comment[sv]=DCOP-tjänst som tillhandahåller ett gränssnitt till CVS
+Comment[ta]=ஒரு cvsக்கு ஒரு இடைமுகத்தினை அளிக்கக்கூடிய DCOPசேவை
+Comment[tg]=Хизмати DCOP барои интерфейс бо cvs
+Comment[tr]= CVS'ye arayüz sağlayan bir DCOP Servisi
+Comment[uk]=Служба DCOP, яка надає інтерфейс до cvs
+Comment[zh_CN]=提供 CVS 接口的 DCOP 服务
+Comment[zh_TW]=提供 cvs 介面的 DCOP 服務
diff --git a/cervisia/cvsservice/cvsservice.h b/cervisia/cvsservice/cvsservice.h
new file mode 100644
index 00000000..0cc43e66
--- /dev/null
+++ b/cervisia/cvsservice/cvsservice.h
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2002-2004 Christian Loose <christian.loose@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef CVSSERVICE_H
+#define CVSSERVICE_H
+
+#include <qstringlist.h>
+#include <dcopref.h>
+#include <dcopobject.h>
+
+class QString;
+
+
+class KDE_EXPORT CvsService : public DCOPObject
+{
+ K_DCOP
+
+public:
+ CvsService();
+ ~CvsService();
+
+k_dcop:
+ /**
+ * Adds new files to an existing project. The files don't actually
+ * appear in the repository until a subsequent commit is performed.
+ *
+ * @param files A list of files that should be added to the repository.
+ * @param isBinary Set to true to treat the files as binary files (-kb)
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef add(const QStringList& files, bool isBinary);
+
+ /**
+ */
+ DCOPRef addWatch(const QStringList& files, int events);
+
+ /**
+ * Shows information on who last modified each line of a file and when.
+ *
+ * @param fileName the name of the file to show annotations for
+ * @param revision show annotations for this revision (number or tag)
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef annotate(const QString& fileName, const QString& revision);
+
+ /**
+ * Checks out a module from the repository into a working copy.
+ *
+ * @param workingDir path to a local working copy directory
+ * @param repository
+ * @param module the name of the module
+ * @param tag
+ * @param pruneDirs remove empty directories from the working copy.
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef checkout(const QString& workingDir, const QString& repository,
+ const QString& module, const QString& tag, bool pruneDirs);
+
+ /**
+ * Checks out a module from the repository into a working copy.
+ *
+ * @param workingDir path to a local working copy directory
+ * @param repository
+ * @param module the name of the module
+ * @param tag
+ * @param pruneDirs remove empty directories from the working copy.
+ * @param alias alternative directory to check out to
+ * @param exportOnly flag to show we want a cvs export rather than a checkout
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ //### KDE4: merge with above checkout() method
+ DCOPRef checkout(const QString& workingDir, const QString& repository,
+ const QString& module, const QString& tag, bool pruneDirs,
+ const QString& alias, bool exportOnly);
+
+ /**
+ * Checks out a module from the repository into a working copy.
+ *
+ * @param workingDir path to a local working copy directory
+ * @param repository
+ * @param module the name of the module
+ * @param tag
+ * @param pruneDirs remove empty directories from the working copy.
+ * @param alias alternative directory to check out to
+ * @param exportOnly flag to show we want a cvs export rather than a checkout
+ * @param recursive check out dirs recursively
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef checkout(const QString& workingDir, const QString& repository,
+ const QString& module, const QString& tag, bool pruneDirs,
+ const QString& alias, bool exportOnly, bool recursive);
+
+ /**
+ *
+ * @param files A list of files with changes that should be committed to
+ * the repository.
+ * @param commitMessage log message describing the changes
+ * @param recursive
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef commit(const QStringList& files, const QString& commitMessage,
+ bool recursive);
+
+ /**
+ * Creates a new root repository.
+ *
+ * @param repository
+ */
+ DCOPRef createRepository(const QString& repository);
+
+ /**
+ */
+ DCOPRef createTag(const QStringList& files, const QString& tag,
+ bool branch, bool force);
+
+ /**
+ */
+ DCOPRef deleteTag(const QStringList& files, const QString& tag,
+ bool branch, bool force);
+
+ /**
+ */
+ DCOPRef downloadCvsIgnoreFile(const QString& repository,
+ const QString& outputFile);
+
+ /**
+ */
+ DCOPRef downloadRevision(const QString& fileName, const QString& revision,
+ const QString& outputFile);
+
+ /**
+ */
+ DCOPRef downloadRevision(const QString& fileName, const QString& revA,
+ const QString& outputFileA, const QString& revB,
+ const QString& outputFileB);
+
+ /**
+ *
+ * @param fileName
+ * @param revA
+ * @param revB
+ * @param diffOptions
+ * @param contextLines
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef diff(const QString& fileName, const QString& revA,
+ const QString& revB, const QString& diffOptions,
+ unsigned contextLines);
+
+ /**
+ *
+ * @param fileName
+ * @param revA
+ * @param revB
+ * @param diffOptions
+ * @param format
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef diff(const QString& fileName, const QString& revA,
+ const QString& revB, const QString& diffOptions,
+ const QString& format);
+
+ /**
+ * @param files
+ */
+ DCOPRef edit(const QStringList& files);
+
+ /**
+ * @param files
+ */
+ DCOPRef editors(const QStringList& files);
+
+ /**
+ * Shows a history of activity (like checkouts, commits, etc) in the
+ * repository for all users and all record types.
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef history();
+
+ /**
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef import(const QString& workingDir, const QString& repository,
+ const QString& module, const QString& ignoreList,
+ const QString& comment, const QString& vendorTag,
+ const QString& releaseTag, bool importAsBinary);
+
+ /**
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ //### KDE4: merge with above import() method
+ DCOPRef import(const QString& workingDir, const QString& repository,
+ const QString& module, const QString& ignoreList,
+ const QString& comment, const QString& vendorTag,
+ const QString& releaseTag, bool importAsBinary,
+ bool useModificationTime);
+
+ /**
+ * @param files
+ */
+ DCOPRef lock(const QStringList& files);
+
+ /**
+ * Shows log messages for a file.
+ *
+ * @param fileName the name of the file to show log messages for
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef log(const QString& fileName);
+
+ /**
+ * @param repository
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef login(const QString& repository);
+
+ /**
+ * @param repository
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef logout(const QString& repository);
+
+ /**
+ */
+ DCOPRef makePatch();
+
+ /**
+ */
+ //### KDE4: merge with above makePatch() method
+ DCOPRef makePatch(const QString& diffOptions, const QString& format);
+
+ /**
+ * @param repository
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef moduleList(const QString& repository);
+
+ /**
+ * Deletes files from the local working copy and schedules them to be
+ * removed from the repository. The files don't actually disappear from
+ * the repository until a subsequent commit is performed.
+ *
+ * @param files A list of files that should be removed from the repository.
+ * @param recursive descend into subdirectories.
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef remove(const QStringList& files, bool recursive);
+
+ /**
+ */
+ DCOPRef removeWatch(const QStringList& files, int events);
+
+ /**
+ */
+ DCOPRef rlog(const QString& repository, const QString& module,
+ bool recursive);
+
+ /**
+ * Shows a summary of what's been done locally, without changing the
+ * working copy. (cvs -n update)
+ *
+ * @param files
+ * @param recursive descend into subdirectories.
+ * @param createDirs
+ * @param pruneDirs
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef simulateUpdate(const QStringList& files, bool recursive,
+ bool createDirs, bool pruneDirs);
+
+ /**
+ * Shows the status of the files in the working copy.
+ *
+ * @param files
+ * @param recursive descend into subdirectories.
+ * @param tagInfo show tag information for the file.
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef status(const QStringList& files, bool recursive, bool tagInfo);
+
+ /**
+ * @param files
+ */
+ DCOPRef unedit(const QStringList& files);
+
+ /**
+ * @param files
+ */
+ DCOPRef unlock(const QStringList& files);
+
+ /**
+ * Merges changes from the repository into the files of the
+ * working copy.
+ *
+ * @param files A list of files that should be updated.
+ * @param recursive descend into subdirectories.
+ * @param createDirs create directories that exist in the repository
+ * but not yet in the working copy.
+ * @param pruneDirs remove empty directories from the working copy.
+ * @param extraOpt
+ *
+ * @return A DCOP reference to the cvs job or in case of failure a
+ * null reference.
+ */
+ DCOPRef update(const QStringList& files, bool recursive, bool createDirs,
+ bool pruneDirs, const QString& extraOpt);
+
+ /**
+ * @param files
+ */
+ DCOPRef watchers(const QStringList& files);
+
+ /**
+ * Quits the DCOP service.
+ */
+ void quit();
+
+private:
+ struct Private;
+ Private* d;
+};
+
+
+#endif
diff --git a/cervisia/cvsservice/cvsserviceutils.cpp b/cervisia/cvsservice/cvsserviceutils.cpp
new file mode 100644
index 00000000..73bfc531
--- /dev/null
+++ b/cervisia/cvsservice/cvsserviceutils.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2003 Christian Loose <christian.loose@hamburg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "cvsserviceutils.h"
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <kprocess.h>
+
+
+QString CvsServiceUtils::joinFileList(const QStringList& files)
+{
+ QString result;
+
+ QStringList::ConstIterator it = files.begin();
+ QStringList::ConstIterator end = files.end();
+
+ for( ; it != end; ++it )
+ {
+ result += KProcess::quote(*it);
+ result += " ";
+ }
+
+ if( result.length() > 0 )
+ result.truncate(result.length()-1);
+
+ return result;
+}
diff --git a/cervisia/cvsservice/cvsserviceutils.h b/cervisia/cvsservice/cvsserviceutils.h
new file mode 100644
index 00000000..8fb7290f
--- /dev/null
+++ b/cervisia/cvsservice/cvsserviceutils.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2003 Christian Loose <christian.loose@hamburg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef CVSSERVICE_UTILS_H
+#define CVSSERVICE_UTILS_H
+
+class QString;
+class QStringList;
+
+
+namespace CvsServiceUtils
+{
+
+/**
+ * Joins a list of file names to one QString and quotes
+ * each name properly for usage with KProcess.
+ */
+QString joinFileList(const QStringList& files);
+
+}
+
+
+#endif
diff --git a/cervisia/cvsservice/main.cpp b/cervisia/cvsservice/main.cpp
new file mode 100644
index 00000000..4f6c748d
--- /dev/null
+++ b/cervisia/cvsservice/main.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2002 Christian Loose <christian.loose@hamburg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include "cvsservice.h"
+
+
+extern "C" KDE_EXPORT int kdemain(int argc, char** argv)
+{
+ KAboutData about("cvsservice", I18N_NOOP("CVS DCOP service"), "0.1",
+ I18N_NOOP("DCOP service for CVS"), KAboutData::License_LGPL,
+ "Copyright (c) 2002-2003 Christian Loose");
+ about.addAuthor("Christian Loose", I18N_NOOP("Developer"),
+ "christian.loose@hamburg.de");
+
+ KCmdLineArgs::init(argc, argv, &about);
+
+ KApplication app;
+
+ // This app is started automatically, no need for session management
+ app.disableSessionManagement();
+
+ CvsService service;
+
+ return app.exec();
+}
diff --git a/cervisia/cvsservice/repository.cpp b/cervisia/cvsservice/repository.cpp
new file mode 100644
index 00000000..d2cff113
--- /dev/null
+++ b/cervisia/cvsservice/repository.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2002-2004 Christian Loose <christian.loose@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "repository.h"
+
+#include <qdir.h>
+#include <qfile.h>
+#include <qstring.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdirwatch.h>
+#include <kstandarddirs.h>
+
+#include "sshagent.h"
+
+
+struct Repository::Private
+{
+ Private() : compressionLevel(0) {}
+
+ QString configFileName;
+
+ QString workingCopy;
+ QString location;
+
+ QString client;
+ QString rsh;
+ QString server;
+ int compressionLevel;
+ bool retrieveCvsignoreFile;
+
+ void readConfig();
+ void readGeneralConfig();
+};
+
+
+
+Repository::Repository()
+ : QObject()
+ , DCOPObject("CvsRepository")
+ , d(new Private)
+{
+ d->readGeneralConfig();
+
+ // other cvsservice instances might change the configuration file
+ // so we watch it for changes
+ d->configFileName = locate("config", "cvsservicerc");
+ KDirWatch* fileWatcher = new KDirWatch(this);
+ connect(fileWatcher, SIGNAL(dirty(const QString&)),
+ this, SLOT(slotConfigDirty(const QString&)));
+ fileWatcher->addFile(d->configFileName);
+}
+
+
+Repository::Repository(const QString& repository)
+ : QObject()
+ , DCOPObject()
+ , d(new Private)
+{
+ d->location = repository;
+ d->readGeneralConfig();
+ d->readConfig();
+
+ // other cvsservice instances might change the configuration file
+ // so we watch it for changes
+ d->configFileName = locate("config", "cvsservicerc");
+ KDirWatch* fileWatcher = new KDirWatch(this);
+ connect(fileWatcher, SIGNAL(dirty(const QString&)),
+ this, SLOT(slotConfigDirty(const QString&)));
+ fileWatcher->addFile(d->configFileName);
+}
+
+
+Repository::~Repository()
+{
+ delete d;
+}
+
+
+QString Repository::cvsClient() const
+{
+ QString client(d->client);
+
+ // suppress reading of the '.cvsrc' file
+ client += " -f";
+
+ // we don't need the command line option if there is no compression level set
+ if( d->compressionLevel > 0 )
+ {
+ client += " -z" + QString::number(d->compressionLevel) + " ";
+ }
+
+ return client;
+}
+
+
+QString Repository::clientOnly() const
+{
+ return d->client;
+}
+
+
+QString Repository::rsh() const
+{
+ return d->rsh;
+}
+
+
+QString Repository::server() const
+{
+ return d->server;
+}
+
+
+bool Repository::setWorkingCopy(const QString& dirName)
+{
+ const QFileInfo fi(dirName);
+ const QString path = fi.absFilePath();
+
+ // is this really a cvs-controlled directory?
+ const QFileInfo cvsDirInfo(path + "/CVS");
+ if( !cvsDirInfo.exists() || !cvsDirInfo.isDir() ||
+ !QFile::exists( cvsDirInfo.filePath() + "/Entries" ) ||
+ !QFile::exists( cvsDirInfo.filePath() + "/Repository" ) ||
+ !QFile::exists( cvsDirInfo.filePath() + "/Root" ) )
+ return false;
+
+ d->workingCopy = path;
+ d->location = QString::null;
+
+ // determine path to the repository
+ QFile rootFile(path + "/CVS/Root");
+ if( rootFile.open(IO_ReadOnly) )
+ {
+ QTextStream stream(&rootFile);
+ d->location = stream.readLine();
+ }
+ rootFile.close();
+
+ // add identities (ssh-add) to ssh-agent
+ // TODO CL make sure this is called only once
+ if( d->location.contains(":ext:", false) > 0 )
+ {
+ SshAgent ssh;
+ ssh.addSshIdentities();
+ }
+
+ QDir::setCurrent(path);
+ d->readConfig();
+
+ return true;
+}
+
+
+QString Repository::workingCopy() const
+{
+ return d->workingCopy;
+}
+
+
+QString Repository::location() const
+{
+ return d->location;
+}
+
+
+bool Repository::retrieveCvsignoreFile() const
+{
+ return d->retrieveCvsignoreFile;
+}
+
+
+void Repository::slotConfigDirty(const QString& fileName)
+{
+ if( fileName == d->configFileName )
+ {
+ // reread the configuration data from disk
+ kapp->config()->reparseConfiguration();
+ d->readConfig();
+ }
+}
+
+
+void Repository::Private::readGeneralConfig()
+{
+ KConfig* config = kapp->config();
+
+ // get path to cvs client programm
+ config->setGroup("General");
+ client = config->readPathEntry("CVSPath", "cvs");
+}
+
+
+void Repository::Private::readConfig()
+{
+ KConfig* config = kapp->config();
+
+ // Sometimes the location can be unequal to the entry in the CVS/Root.
+ //
+ // This can happen when the checkout was done with a repository name
+ // like :pserver:user@cvs.kde.org:/home/kde. When cvs then saves the
+ // name into the .cvspass file, it adds the default cvs port to it like
+ // this :pserver:user@cvs.kde.org:2401/home/kde. This name is then also
+ // used for the configuration group.
+ //
+ // In order to be able to read this group, we then have to manually add
+ // the port number to it.
+ QString repositoryGroup = QString::fromLatin1("Repository-") + location;
+ if( !config->hasGroup(repositoryGroup) )
+ {
+ // find the position of the first path separator
+ const int insertPos = repositoryGroup.find('/');
+ if( insertPos > 0 )
+ {
+ // add port to location
+ // (1) :pserver:user@hostname.com:/path
+ if( repositoryGroup.at(insertPos - 1) == ':' )
+ repositoryGroup.insert(insertPos, "2401");
+ // (2) :pserver:user@hostname.com/path
+ else
+ repositoryGroup.insert(insertPos, ":2401");
+ }
+ }
+
+ config->setGroup(repositoryGroup);
+
+ // should we retrieve the CVSROOT/cvsignore file from the cvs server?
+ retrieveCvsignoreFile = config->readBoolEntry("RetrieveCvsignore", false);
+
+ // see if there is a specific compression level set for this repository
+ compressionLevel = config->readNumEntry("Compression", -1);
+
+ // use default global compression level instead?
+ if( compressionLevel < 0 )
+ {
+ KConfigGroupSaver cs(config, "General");
+ compressionLevel = config->readNumEntry("Compression", 0);
+ }
+
+ // get remote shell client to access the remote repository
+ rsh = config->readPathEntry("rsh");
+
+ // get program to start on the server side
+ server = config->readEntry("cvs_server");
+}
+
+
+#include "repository.moc"
diff --git a/cervisia/cvsservice/repository.h b/cervisia/cvsservice/repository.h
new file mode 100644
index 00000000..c90277bc
--- /dev/null
+++ b/cervisia/cvsservice/repository.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2002-2003 Christian Loose <christian.loose@hamburg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef REPOSITORY_H
+#define REPOSITORY_H
+
+#include <qobject.h>
+#include <dcopobject.h>
+
+class QString;
+
+
+/**
+ * Represents a local or remote cvs repository with
+ * its repository-specific configuration data.
+ */
+class KDE_EXPORT Repository : public QObject, public DCOPObject
+{
+ K_DCOP
+ Q_OBJECT
+
+public:
+ Repository();
+ explicit Repository(const QString& repository);
+ ~Repository();
+
+ /**
+ * cvs command (including the user-specified path) with the options
+ * for this repository.
+ *
+ * @return A cvs command (including path).
+ */
+ QString cvsClient() const;
+
+ /**
+ */
+ QString clientOnly() const;
+
+ /**
+ * Remote shell command line client which should be used to
+ * access the remote cvs repository, when :ext: access method
+ * is specified. ($CVS_RSH)
+ *
+ * @return The remote shell client. Can be null if not set.
+ */
+ QString rsh() const;
+
+ /**
+ * Program to start on the server side when accessing a remote
+ * repository using :ext: access method. ($CVS_SERVER)
+ *
+ * @return The server program. Can be null if not set.
+ */
+ QString server() const;
+
+k_dcop:
+ /**
+ * Changes the working copy and the corresponding cvs repository.
+ *
+ * @param dirName path to the local working copy directory.
+ */
+ bool setWorkingCopy(const QString& dirName);
+
+ /**
+ * Path to the current working copy.
+ *
+ * @return The working copy directory. Can be null if not set.
+ */
+ QString workingCopy() const;
+
+ /**
+ * Path and method to access the current cvs repository.
+ * i.e. :pserver:user@cvs.project.org:/home/project
+ *
+ * @return The path and method to access the cvs repository.
+ */
+ QString location() const;
+
+ /**
+ */
+ bool retrieveCvsignoreFile() const;
+
+private slots:
+ void slotConfigDirty(const QString& fileName);
+
+private:
+ struct Private;
+ Private* d;
+};
+
+
+#endif
diff --git a/cervisia/cvsservice/sshagent.cpp b/cervisia/cvsservice/sshagent.cpp
new file mode 100644
index 00000000..48bc5eef
--- /dev/null
+++ b/cervisia/cvsservice/sshagent.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2003 Christian Loose <christian.loose@hamburg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "sshagent.h"
+
+#include <qregexp.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <kprocess.h>
+
+#include <stdlib.h>
+
+
+// initialize static member variables
+bool SshAgent::m_isRunning = false;
+bool SshAgent::m_isOurAgent = false;
+QString SshAgent::m_authSock = QString::null;
+QString SshAgent::m_pid = QString::null;
+
+
+SshAgent::SshAgent(QObject* parent, const char* name)
+ : QObject(parent, name)
+{
+}
+
+
+SshAgent::~SshAgent()
+{
+}
+
+
+bool SshAgent::querySshAgent()
+{
+ kdDebug(8051) << "SshAgent::querySshAgent(): ENTER" << endl;
+
+ if( m_isRunning )
+ return true;
+
+ // Did the user already start a ssh-agent process?
+ char* pid;
+ if( (pid = ::getenv("SSH_AGENT_PID")) != 0 )
+ {
+ kdDebug(8051) << "SshAgent::querySshAgent(): ssh-agent already exists"
+ << endl;
+
+ m_pid = QString::fromLocal8Bit(pid);
+
+ char* sock = ::getenv("SSH_AUTH_SOCK");
+ if( sock )
+ m_authSock = QString::fromLocal8Bit(sock);
+
+ m_isOurAgent = false;
+ m_isRunning = true;
+ }
+ // We have to start a new ssh-agent process
+ else
+ {
+ kdDebug(8051) << "SshAgent::querySshAgent(): start ssh-agent" << endl;
+
+ m_isOurAgent = true;
+ m_isRunning = startSshAgent();
+ }
+
+ return m_isRunning;
+}
+
+
+bool SshAgent::addSshIdentities()
+{
+ kdDebug(8051) << "SshAgent::addSshIdentities(): ENTER" << endl;
+
+ if( !m_isRunning || !m_isOurAgent )
+ return false;
+
+ // add identities to ssh-agent
+ KProcess proc;
+
+ proc.setEnvironment("SSH_AGENT_PID", m_pid);
+ proc.setEnvironment("SSH_AUTH_SOCK", m_authSock);
+ proc.setEnvironment("SSH_ASKPASS", "cvsaskpass");
+
+ proc << "ssh-add";
+
+ connect(&proc, SIGNAL(receivedStdout(KProcess*, char*, int)),
+ SLOT(slotReceivedStdout(KProcess*, char*, int)));
+ connect(&proc, SIGNAL(receivedStderr(KProcess*, char*, int)),
+ SLOT(slotReceivedStderr(KProcess*, char*, int)));
+
+ proc.start(KProcess::DontCare, KProcess::AllOutput);
+
+ // wait for process to finish
+ // TODO CL use timeout?
+ proc.wait();
+
+ kdDebug(8051) << "SshAgent::slotProcessExited(): added identities" << endl;
+
+ return (proc.normalExit() && proc.exitStatus() == 0);
+}
+
+
+void SshAgent::killSshAgent()
+{
+ kdDebug(8051) << "SshAgent::killSshAgent(): ENTER" << endl;
+
+ if( !m_isRunning || !m_isOurAgent )
+ return;
+
+ KProcess proc;
+
+ proc << "kill" << m_pid;
+
+ proc.start(KProcess::DontCare, KProcess::NoCommunication);
+
+ kdDebug(8051) << "SshAgent::killSshAgent(): killed pid = " << m_pid << endl;
+}
+
+
+void SshAgent::slotProcessExited(KProcess*)
+{
+ kdDebug(8051) << "SshAgent::slotProcessExited(): ENTER" << endl;
+
+ QRegExp cshPidRx("setenv SSH_AGENT_PID (\\d*);");
+ QRegExp cshSockRx("setenv SSH_AUTH_SOCK (.*);");
+
+ QRegExp bashPidRx("SSH_AGENT_PID=(\\d*).*");
+ QRegExp bashSockRx("SSH_AUTH_SOCK=(.*\\.\\d*);.*");
+
+ QStringList::Iterator it = m_outputLines.begin();
+ QStringList::Iterator end = m_outputLines.end();
+ for( ; it != end; ++it )
+ {
+ if( m_pid.isEmpty() )
+ {
+ int pos = cshPidRx.search(*it);
+ if( pos > -1 )
+ {
+ m_pid = cshPidRx.cap(1);
+ continue;
+ }
+
+ pos = bashPidRx.search(*it);
+ if( pos > -1 )
+ {
+ m_pid = bashPidRx.cap(1);
+ continue;
+ }
+ }
+
+ if( m_authSock.isEmpty() )
+ {
+ int pos = cshSockRx.search(*it);
+ if( pos > -1 )
+ {
+ m_authSock = cshSockRx.cap(1);
+ continue;
+ }
+
+ pos = bashSockRx.search(*it);
+ if( pos > -1 )
+ {
+ m_authSock = bashSockRx.cap(1);
+ continue;
+ }
+ }
+ }
+
+ kdDebug(8051) << "SshAgent::slotProcessExited(): pid = " << m_pid
+ << ", socket = " << m_authSock << endl;
+}
+
+
+void SshAgent::slotReceivedStdout(KProcess* proc, char* buffer, int buflen)
+{
+ Q_UNUSED(proc);
+
+ QString output = QString::fromLocal8Bit(buffer, buflen);
+ m_outputLines += QStringList::split("\n", output);
+
+ kdDebug(8051) << "SshAgent::slotReceivedStdout(): output = " << output << endl;
+}
+
+
+void SshAgent::slotReceivedStderr(KProcess* proc, char* buffer, int buflen)
+{
+ Q_UNUSED(proc);
+
+ QString output = QString::fromLocal8Bit(buffer, buflen);
+ m_outputLines += QStringList::split("\n", output);
+
+ kdDebug(8051) << "SshAgent::slotReceivedStderr(): output = " << output << endl;
+}
+
+
+bool SshAgent::startSshAgent()
+{
+ kdDebug(8051) << "SshAgent::startSshAgent(): ENTER" << endl;
+
+ KProcess proc;
+
+ proc << "ssh-agent";
+
+ connect(&proc, SIGNAL(processExited(KProcess*)),
+ SLOT(slotProcessExited(KProcess*)));
+ connect(&proc, SIGNAL(receivedStdout(KProcess*, char*, int)),
+ SLOT(slotReceivedStdout(KProcess*, char*, int)));
+ connect(&proc, SIGNAL(receivedStderr(KProcess*, char*, int)),
+ SLOT(slotReceivedStderr(KProcess*, char*, int)) );
+
+ proc.start(KProcess::NotifyOnExit, KProcess::All);
+
+ // wait for process to finish
+ // TODO CL use timeout?
+ proc.wait();
+
+ return (proc.normalExit() && proc.exitStatus() == 0);
+}
+
+
+#include "sshagent.moc"
diff --git a/cervisia/cvsservice/sshagent.h b/cervisia/cvsservice/sshagent.h
new file mode 100644
index 00000000..894a9026
--- /dev/null
+++ b/cervisia/cvsservice/sshagent.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2003 Christian Loose <christian.loose@hamburg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef SSHAGENT_H
+#define SSHAGENT_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+class KProcess;
+
+
+class SshAgent : public QObject
+{
+ Q_OBJECT
+
+public:
+ SshAgent(QObject* parent = 0, const char* name = 0);
+ ~SshAgent();
+
+ bool querySshAgent();
+ bool addSshIdentities();
+ void killSshAgent();
+
+ bool isRunning() const { return m_isRunning; }
+ QString pid() const { return m_pid; }
+ QString authSock() const { return m_authSock; }
+
+private slots:
+ void slotProcessExited(KProcess*);
+ void slotReceivedStdout(KProcess* proc, char* buffer, int buflen);
+ void slotReceivedStderr(KProcess* proc, char* buffer, int buflen);
+
+private:
+ bool startSshAgent();
+
+ QStringList m_outputLines;
+
+ static bool m_isRunning;
+ static bool m_isOurAgent;
+ static QString m_authSock;
+ static QString m_pid;
+};
+
+
+#endif