aboutsummaryrefslogtreecommitdiffstats
path: root/kgpg/gpgproc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kgpg/gpgproc.cpp')
-rw-r--r--kgpg/gpgproc.cpp370
1 files changed, 370 insertions, 0 deletions
diff --git a/kgpg/gpgproc.cpp b/kgpg/gpgproc.cpp
new file mode 100644
index 0000000..10f2845
--- /dev/null
+++ b/kgpg/gpgproc.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2007,2008,2009,2010,2011,2012 Rolf Eike Beer <[email protected]>
+ */
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "gpgproc.h"
+
+//#include "kgpgsettings.h"
+
+//#include <KDebug>
+#include <QDebug>
+//#include <KProcess>
+#include "kprocess.h"
+//#include <KStandardDirs>
+#include <QDir>
+#include <QTextCodec>
+
+class GnupgBinary {
+public:
+ GnupgBinary();
+
+ const QString &binary() const;
+ void setBinary(const QString &executable);
+ const QStringList &standardArguments() const;
+ unsigned int version() const;
+ bool supportsDebugLevel() const;
+ //static GnupgBinary* instance();
+
+private:
+ QString m_binary;
+ QStringList m_standardArguments;
+ unsigned int m_version;
+ bool m_useDebugLevel;
+};
+
+GnupgBinary::GnupgBinary()
+ : m_useDebugLevel(false)
+{
+}
+
+const QString &GnupgBinary::binary() const
+{
+ return m_binary;
+}
+
+/**
+ * @brief check if GnuPG returns an error for this arguments
+ * @param executable the GnuPG executable to call
+ * @param arguments the arguments to pass to executable
+ *
+ * The arguments will be used together with "--version", so they should not
+ * be any commands.
+ */
+static bool checkGnupgArguments(const QString &executable, const QStringList &arguments)
+{
+ KProcess gpg;
+
+ // We ignore the output anyway, just make sure it doesn't clutter the output of
+ // the parent process. Simplify the handling by putting all trash in one can.
+ gpg.setOutputChannelMode(KProcess::MergedChannels);
+
+ QStringList allArguments = arguments;
+ allArguments << QLatin1String("--version");
+ gpg.setProgram(executable, allArguments);
+
+ return (gpg.execute() == 0);
+}
+
+static QString getGpgProcessHome(const QString &binary)
+{
+ GPGProc process(0, binary);
+ process << QLatin1String( "--version" );
+ process.start();
+ process.waitForFinished(-1);
+
+ if (process.exitCode() == 255) {
+ return QString();
+ }
+
+ QString line;
+ while (process.readln(line) != -1) {
+ if (line.startsWith(QLatin1String("Home: "))) {
+ line.remove(0, 6);
+ return line;
+ }
+ }
+
+ return QString();
+}
+
+
+void GnupgBinary::setBinary(const QString &executable)
+{
+ qDebug() << "checking version of GnuPG executable" << executable;
+ // must be set first as gpgVersionString() uses GPGProc to parse the output
+ m_binary = executable;
+ const QString verstr = GPGProc::gpgVersionString(executable);
+ m_version = GPGProc::gpgVersion(verstr);
+ qDebug() << "version is" << verstr << m_version;
+
+ m_useDebugLevel = (m_version > 0x20000);
+
+ const QString gpgConfigFile = "";//KGpgSettings::gpgConfigPath();
+
+ m_standardArguments.clear();
+ m_standardArguments << QLatin1String( "--no-secmem-warning" )
+ << QLatin1String( "--no-tty" )
+ << QLatin1String("--no-greeting")
+ << QLatin1String("--homedir") << GPGProc::getGpgHome("");
+
+ if (!gpgConfigFile.isEmpty())
+ m_standardArguments << QLatin1String("--options")
+ << gpgConfigFile;
+
+ QStringList debugLevelArguments(QLatin1String("--debug-level"));
+ debugLevelArguments << QLatin1String("none");
+ if (checkGnupgArguments(executable, debugLevelArguments))
+ m_standardArguments << debugLevelArguments;
+}
+
+const QStringList& GnupgBinary::standardArguments() const
+{
+ return m_standardArguments;
+}
+
+unsigned int GnupgBinary::version() const
+{
+ return m_version;
+}
+
+bool GnupgBinary::supportsDebugLevel() const
+{
+ return m_useDebugLevel;
+}
+
+Q_GLOBAL_STATIC(GnupgBinary, lastBinary)
+
+GPGProc::GPGProc(QObject *parent, const QString &binary)
+ : KLineBufferedProcess(parent)
+{
+ resetProcess(binary);
+}
+
+GPGProc::~GPGProc()
+{
+}
+
+void
+GPGProc::resetProcess(const QString &binary)
+{
+ GnupgBinary *bin = lastBinary();
+ QString executable;
+
+ //qDebug() << "bin:" << binary;
+
+ if (binary.isEmpty()) {
+ //executable = KGpgSettings::gpgBinaryPath();
+ QString appPath = qApp->applicationDirPath();
+ QString gpgBin;
+ #ifdef Q_WS_WIN
+ gpgBin = appPath + "/bin/gpg.exe";
+ #endif
+ #ifdef Q_WS_MAC
+ gpgBin = appPath + "/gpg-mac";
+ #endif
+ #ifdef Q_WS_X11
+ gpgBin = appPath + "/bin/gpg";
+ #endif
+ executable = gpgBin;
+ } else
+ executable = binary;
+
+ if (bin->binary() != executable)
+ bin->setBinary(executable);
+ //qDebug() << "ex: " << executable;
+ setProgram(executable, bin->standardArguments());
+
+ setOutputChannelMode(OnlyStdoutChannel);
+
+ disconnect(SIGNAL(finished(int,QProcess::ExitStatus)));
+ disconnect(SIGNAL(lineReadyStandardOutput()));
+}
+
+void GPGProc::start()
+{
+ // make sure there is exactly one connection from us to that signal
+ connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(finished()), Qt::UniqueConnection);
+ connect(this, SIGNAL(lineReadyStandardOutput()), this, SLOT(received()), Qt::UniqueConnection);
+ KProcess::start();
+}
+
+void GPGProc::received()
+{
+ emit readReady();
+}
+
+void GPGProc::finished()
+{
+ emit processExited();
+}
+
+int GPGProc::readln(QString &line, const bool colons)
+{
+ QByteArray a;
+ if (!readLineStandardOutput(&a))
+ return -1;
+
+ line = recode(a, colons);
+
+ return line.length();
+}
+
+int GPGProc::readln(QStringList &l)
+{
+ QString s;
+
+ int len = readln(s);
+ if (len < 0)
+ return len;
+
+ l = s.split(QLatin1Char( ':' ));
+
+ for (int i = 0; i < l.count(); ++i)
+ {
+ int j = 0;
+ while ((j = l[i].indexOf(QLatin1String( "\\x3a" ), j, Qt::CaseInsensitive)) >= 0)
+ {
+ l[i].replace(j, 4, QLatin1Char( ':' ));
+ j++;
+ }
+ }
+
+ return l.count();
+}
+
+QString
+GPGProc::recode(QByteArray a, const bool colons)
+{
+ int pos = 0;
+
+ while ((pos = a.indexOf("\\x", pos)) >= 0) {
+ if (pos > a.length() - 4)
+ break;
+
+ const QByteArray pattern(a.mid(pos, 4));
+ const QByteArray hexnum(pattern.right(2));
+ bool ok;
+ char n[2];
+ n[0] = hexnum.toUShort(&ok, 16);
+ n[1] = '\0'; // to use n as a 0-terminated string
+ if (!ok)
+ continue;
+
+ // QLatin1Char( ':' ) must be skipped, it is used as column delimiter
+ // since it is pure ascii it can be replaced in QString.
+ if (!colons && (n[0] == ':' )) {
+ pos += 3;
+ continue;
+ }
+
+ // it is likely to find the same byte sequence more than once
+ int npos = pos;
+ do {
+ a.replace(npos, 4, n);
+ } while ((npos = a.indexOf(pattern, npos)) >= 0);
+ }
+
+ return QTextCodec::codecForName("utf8")->toUnicode(a);
+}
+
+int GPGProc::gpgVersion(const QString &vstr)
+{
+ if (vstr.isEmpty())
+ return -1;
+
+ QStringList values(vstr.split(QLatin1Char( '.' )));
+ if (values.count() < 3)
+ return -2;
+
+ return (0x10000 * values[0].toInt() + 0x100 * values[1].toInt() + values[2].toInt());
+}
+
+QString GPGProc::gpgVersionString(const QString &binary)
+{
+ GPGProc process(0, binary);
+ process << QLatin1String( "--version" );
+ process.start();
+ process.waitForFinished(-1);
+
+ if (process.exitCode() == 255) {
+ qDebug() << "exit255";
+ return QString();
+ }
+
+ QString line;
+ if (process.readln(line) != -1) {
+ qDebug() << line;
+ return line.simplified().section(QLatin1Char( ' ' ), -1);
+ } else {
+ qDebug() << "no readln";
+ return QString();
+ }
+}
+
+QString GPGProc::getGpgStartupError(const QString &binary)
+{
+ GPGProc process(0, binary);
+ process << QLatin1String( "--version" );
+ process.start();
+ process.waitForFinished(-1);
+
+ QString result;
+
+ while (process.hasLineStandardError()) {
+ QByteArray tmp;
+ process.readLineStandardError(&tmp);
+ tmp += '\n';
+ result += QString::fromUtf8(tmp);
+ }
+
+ return result;
+}
+
+QString GPGProc::getGpgHome(const QString &binary)
+{
+ // First try: if environment is set GnuPG will use that directory
+ // We can use this directly without starting a new process
+/* QByteArray env(qgetenv("GNUPGHOME"));
+ QString gpgHome;
+ if (!env.isEmpty()) {
+ gpgHome = QLatin1String( env );
+ } else if (!binary.isEmpty()) {
+ // Second try: start GnuPG and ask what it is
+ gpgHome = getGpgProcessHome(binary);
+ }
+
+ // Third try: guess what it is.
+ if (gpgHome.isEmpty()) {
+#ifdef Q_OS_WIN32 //krazy:exclude=cpp
+ gpgHome = qgetenv("APPDATA") + QLatin1String( "/gnupg/" );
+ gpgHome.replace(QLatin1Char( '\\' ), QLatin1Char( '/' ));
+#else
+ gpgHome = QDir::homePath() + QLatin1String( "/.gnupg/" );
+#endif
+ }
+
+ gpgHome.replace(QLatin1String( "//" ), QLatin1String( "/" ));
+
+ if (!gpgHome.endsWith(QLatin1Char( '/' )))
+ gpgHome.append(QLatin1Char( '/' ));
+
+ if (gpgHome.startsWith(QLatin1Char( '~' )))
+ gpgHome.replace(0, 1, QDir::homePath());
+
+ //KStandardDirs::makeDir(gpgHome, 0700);*/
+ QString appPath = qApp->applicationDirPath();
+ QString gpgHome = appPath + "/keydb";
+
+ return gpgHome;
+}
+
+//#include "gpgproc.moc"