aboutsummaryrefslogtreecommitdiffstats
path: root/gpgcontext.cpp
diff options
context:
space:
mode:
authornils <nils@34ebc366-c3a9-4b3c-9f84-69acf7962910>2011-10-29 22:29:36 +0000
committernils <nils@34ebc366-c3a9-4b3c-9f84-69acf7962910>2011-10-29 22:29:36 +0000
commit25db5c0f40379dadd4224cfbd87aad92a49d90ae (patch)
treee940feff28027f9b80397e2cc1b7e82b58df8e41 /gpgcontext.cpp
parentbaeutified icons for sign and verify (diff)
downloadgpg4usb-25db5c0f40379dadd4224cfbd87aad92a49d90ae.tar.gz
gpg4usb-25db5c0f40379dadd4224cfbd87aad92a49d90ae.zip
refactored gpgwin -> mainwindow and context -> gpgcontext
git-svn-id: http://cpunk.de/svn/src/gpg4usb/trunk@588 34ebc366-c3a9-4b3c-9f84-69acf7962910
Diffstat (limited to 'gpgcontext.cpp')
-rw-r--r--gpgcontext.cpp671
1 files changed, 671 insertions, 0 deletions
diff --git a/gpgcontext.cpp b/gpgcontext.cpp
new file mode 100644
index 0000000..77856f9
--- /dev/null
+++ b/gpgcontext.cpp
@@ -0,0 +1,671 @@
+/*
+ * context.cpp
+ *
+ * Copyright 2008 gpg4usb-team <[email protected]>
+ *
+ * This file is part of gpg4usb.
+ *
+ * Gpg4usb 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Gpg4usb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gpg4usb. If not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "gpgcontext.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <unistd.h> /* contains read/write */
+#endif
+
+
+namespace GpgME
+{
+
+/** Constructor
+ * Set up gpgme-context, set paths to app-run path
+ */
+GpgContext::GpgContext()
+{
+
+ /** get application path */
+ QString appPath = qApp->applicationDirPath();
+
+ /** The function `gpgme_check_version' must be called before any other
+ * function in the library, because it initializes the thread support
+ * subsystem in GPGME. (from the info page) */
+ gpgme_check_version(NULL);
+
+ // TODO: Set gpgme_language to config
+ // http://lavica.fesb.hr/cgi-bin/info2html?%28gpgme%29Locale
+ setlocale(LC_ALL, "");
+ /** set locale, because tests do also */
+ gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
+ //qDebug() << "Locale set to" << LC_CTYPE << " - " << setlocale(LC_CTYPE, NULL);
+#ifndef _WIN32
+ gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL));
+#endif
+
+ err = gpgme_new(&mCtx);
+ checkErr(err);
+ /** here come the settings, instead of /usr/bin/gpg
+ * a executable in the same path as app is used.
+ * also lin/win must be checked, for calling gpg.exe if needed
+ */
+#ifdef _WIN32
+ QString gpgBin = appPath + "/bin/gpg.exe";
+#else
+ QString gpgBin = appPath + "/bin/gpg";
+#endif
+ QString gpgKeys = appPath + "/keydb";
+ /* err = gpgme_ctx_set_engine_info(mCtx, GPGME_PROTOCOL_OpenPGP,
+ gpgBin.toUtf8().constData(),
+ gpgKeys.toUtf8().constData());*/
+ err = gpgme_ctx_set_engine_info(mCtx, GPGME_PROTOCOL_OpenPGP,
+ gpgBin.toLocal8Bit().constData(),
+ gpgKeys.toLocal8Bit().constData());
+ checkErr(err);
+
+
+ /** Setting the output type must be done at the beginning */
+ /** think this means ascii-armor --> ? */
+ gpgme_set_armor(mCtx, 1);
+ /** passphrase-callback */
+ gpgme_set_passphrase_cb(mCtx, passphraseCb, this);
+
+ /** check if app is called with -d from command line */
+ if (qApp->arguments().contains("-d")) {
+ qDebug() << "gpgme_data_t debug on";
+ debug = true;
+ } else {
+ debug = false;
+ }
+
+}
+
+/** Destructor
+ * Release gpgme-context
+ */
+GpgContext::~GpgContext()
+{
+ if (mCtx) gpgme_release(mCtx);
+ mCtx = 0;
+}
+
+/** Import Key from QByteArray
+ *
+ */
+void GpgContext::importKey(QByteArray inBuffer)
+{
+ err = gpgme_data_new_from_mem(&in, inBuffer.data(), inBuffer.size(), 1);
+ checkErr(err);
+ err = gpgme_op_import(mCtx, in);
+ checkErr(err);
+ gpgme_data_release(in);
+ emit keyDBChanged();
+}
+
+/** Generate New Key with values params
+ *
+ */
+void GpgContext::generateKey(QString *params)
+{
+ err = gpgme_op_genkey(mCtx, params->toAscii().data(), NULL, NULL);
+ checkErr(err);
+ emit keyDBChanged();
+}
+
+/** Export Key to QByteArray
+ *
+ */
+bool GpgContext::exportKeys(QStringList *uidList, QByteArray *outBuffer)
+{
+ size_t read_bytes;
+ gpgme_data_t out = 0;
+ outBuffer->resize(0);
+
+ if (uidList->count() == 0) {
+ QMessageBox::critical(0, "Export Keys Error", "No Keys Selected");
+ return false;
+ }
+
+ for (int i = 0; i < uidList->count(); i++) {
+ err = gpgme_data_new(&out);
+ checkErr(err);
+
+ err = gpgme_op_export(mCtx, uidList->at(i).toAscii().constData(), 0, out);
+ checkErr(err);
+
+ read_bytes = gpgme_data_seek(out, 0, SEEK_END);
+
+ err = readToBuffer(out, outBuffer);
+ checkErr(err);
+ gpgme_data_release(out);
+ }
+ return true;
+}
+
+gpgme_key_t GpgContext::getKeyDetails(QString uid)
+{
+ gpgme_key_t key;
+
+ // try secret
+ gpgme_get_key(mCtx, uid.toAscii().constData(), &key, 1);
+ // ok, its a public key
+ if (!key) {
+ gpgme_get_key(mCtx, uid.toAscii().constData(), &key, 0);
+ }
+ return key;
+}
+
+/** List all availabe Keys (VERY much like kgpgme)
+ */
+GpgKeyList GpgContext::listKeys()
+{
+ gpgme_error_t err;
+ gpgme_key_t key;
+
+ GpgKeyList keys;
+ //TODO dont run the loop more often than necessary
+ // list all keys ( the 0 is for all )
+ err = gpgme_op_keylist_start(mCtx, NULL, 0);
+ checkErr(err);
+ while (!(err = gpgme_op_keylist_next(mCtx, &key))) {
+ GpgKey gpgkey;
+
+ if (!key->subkeys)
+ continue;
+
+ gpgkey.id = key->subkeys->keyid;
+ gpgkey.fpr = key->subkeys->fpr;
+ gpgkey.expired = (key->expired != 0);
+
+ if (key->uids) {
+ gpgkey.name = key->uids->name;
+ gpgkey.email = key->uids->email;
+ }
+ keys.append(gpgkey);
+ gpgme_key_unref(key);
+ }
+ gpgme_op_keylist_end(mCtx);
+
+ // list only private keys ( the 1 does )
+ gpgme_op_keylist_start(mCtx, NULL, 1);
+ while (!(err = gpgme_op_keylist_next(mCtx, &key))) {
+ if (!key->subkeys)
+ continue;
+ // iterate keys, mark privates
+ GpgKeyList::iterator it = keys.begin();
+ while (it != keys.end()) {
+ if (key->subkeys->keyid == it->id.toStdString())
+ it->privkey = true;
+ it++;
+ }
+
+ gpgme_key_unref(key);
+ }
+ gpgme_op_keylist_end(mCtx);
+
+ return keys;
+}
+
+/** Delete keys
+ */
+
+void GpgContext::deleteKeys(QStringList *uidList)
+{
+ QString tmp;
+ gpgme_key_t key;
+
+ foreach(tmp, *uidList) {
+ gpgme_op_keylist_start(mCtx, tmp.toAscii().constData(), 0);
+ gpgme_op_keylist_next(mCtx, &key);
+ gpgme_op_keylist_end(mCtx);
+ gpgme_op_delete(mCtx, key, 1);
+ }
+ emit keyDBChanged();
+}
+
+/** Encrypt inBuffer for reciepients-uids, write
+ * result to outBuffer
+ */
+bool GpgContext::encrypt(QStringList *uidList, const QByteArray &inBuffer, QByteArray *outBuffer)
+{
+
+ gpgme_data_t in = 0, out = 0;
+ outBuffer->resize(0);
+
+ if (uidList->count() == 0) {
+ QMessageBox::critical(0, tr("No Key Selected"), tr("No Key Selected"));
+ return false;
+ }
+
+ //gpgme_encrypt_result_t e_result;
+ gpgme_key_t recipients[uidList->count()+1];
+
+ /* get key for user */
+ for (int i = 0; i < uidList->count(); i++) {
+ // the last 0 is for public keys, 1 would return private keys
+ gpgme_op_keylist_start(mCtx, uidList->at(i).toAscii().constData(), 0);
+ gpgme_op_keylist_next(mCtx, &recipients[i]);
+ gpgme_op_keylist_end(mCtx);
+ }
+ //Last entry in array has to be NULL
+ recipients[uidList->count()] = NULL;
+
+ //If the last parameter isnt 0, a private copy of data is made
+ if (mCtx) {
+ err = gpgme_data_new_from_mem(&in, inBuffer.data(), inBuffer.size(), 1);
+ checkErr(err);
+ if (!err) {
+ err = gpgme_data_new(&out);
+ checkErr(err);
+ if (!err) {
+ err = gpgme_op_encrypt(mCtx, recipients, GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
+ checkErr(err);
+ if (!err) {
+ err = readToBuffer(out, outBuffer);
+ checkErr(err);
+ }
+ }
+ }
+ }
+ /* unref all keys */
+ for (int i = 0; i <= uidList->count(); i++) {
+ gpgme_key_unref(recipients[i]);
+ }
+ if (in) {
+ gpgme_data_release(in);
+ }
+ if (out) {
+ gpgme_data_release(out);
+ }
+ return (err == GPG_ERR_NO_ERROR);
+}
+
+/** Decrypt QByteAarray, return QByteArray
+ * mainly from http://basket.kde.org/ (kgpgme.cpp)
+ */
+bool GpgContext::decrypt(const QByteArray &inBuffer, QByteArray *outBuffer)
+{
+ gpgme_data_t in = 0, out = 0;
+ gpgme_decrypt_result_t result = 0;
+
+ outBuffer->resize(0);
+ if (mCtx) {
+ err = gpgme_data_new_from_mem(&in, inBuffer.data(), inBuffer.size(), 1);
+ checkErr(err);
+ if (!err) {
+ err = gpgme_data_new(&out);
+ checkErr(err);
+ if (!err) {
+ err = gpgme_op_decrypt(mCtx, in, out);
+ checkErr(err);
+ if (!err) {
+ result = gpgme_op_decrypt_result(mCtx);
+ if (result->unsupported_algorithm) {
+ QMessageBox::critical(0, tr("Unsupported algorithm"), result->unsupported_algorithm);
+ } else {
+ err = readToBuffer(out, outBuffer);
+ checkErr(err);
+ }
+ }
+ }
+ }
+ }
+ if (err != GPG_ERR_NO_ERROR && err != GPG_ERR_CANCELED) {
+ QMessageBox::critical(0, tr("Error decrypting:"), gpgme_strerror(err));
+ return false;
+ }
+
+ if (! settings.value("general/rememberPassword").toBool()) {
+ clearPasswordCache();
+ }
+
+ if (in) {
+ gpgme_data_release(in);
+ }
+ if (out) {
+ gpgme_data_release(out);
+ }
+ return (err == GPG_ERR_NO_ERROR);
+}
+
+/** Read gpgme-Data to QByteArray
+ * mainly from http://basket.kde.org/ (kgpgme.cpp)
+ */
+#define BUF_SIZE (32 * 1024)
+gpgme_error_t GpgContext::readToBuffer(gpgme_data_t in, QByteArray *outBuffer)
+{
+ int ret;
+ gpgme_error_t err = GPG_ERR_NO_ERROR;
+
+ ret = gpgme_data_seek(in, 0, SEEK_SET);
+ if (ret) {
+ err = gpgme_err_code_from_errno(errno);
+ checkErr(err, "failed dataseek in readToBuffer");
+ } else {
+ char *buf = new char[BUF_SIZE + 2];
+
+ if (buf) {
+ while ((ret = gpgme_data_read(in, buf, BUF_SIZE)) > 0) {
+ uint size = outBuffer->size();
+ outBuffer->resize(size + ret);
+ memcpy(outBuffer->data() + size, buf, ret);
+ }
+ if (ret < 0) {
+ err = gpgme_err_code_from_errno(errno);
+ checkErr(err, "failed data_read in readToBuffer");
+ }
+ delete[] buf;
+ }
+ }
+ return err;
+}
+
+/** The Passphrase window, if not provided by env-Var GPG_AGENT_INFO
+ * originally copied from http://basket.kde.org/ (kgpgme.cpp), but modified
+ */
+gpgme_error_t GpgContext::passphraseCb(void *hook, const char *uid_hint,
+ const char *passphrase_info,
+ int last_was_bad, int fd)
+{
+ GpgContext *gpg = static_cast<GpgContext*>(hook);
+ return gpg->passphrase(uid_hint, passphrase_info, last_was_bad, fd);
+}
+
+gpgme_error_t GpgContext::passphrase(const char *uid_hint,
+ const char * /*passphrase_info*/,
+ int last_was_bad, int fd)
+{
+ gpgme_error_t returnValue = GPG_ERR_CANCELED;
+ QString passwordDialogMessage;
+ QString gpgHint = uid_hint;
+ bool result;
+
+ if (last_was_bad) {
+ passwordDialogMessage += "<i>"+tr("Wrong password")+".</i><br><br>\n\n";
+ clearPasswordCache();
+ }
+
+ /** if uid provided */
+ if (!gpgHint.isEmpty()) {
+ // remove UID, leave only username & email
+ gpgHint.remove(0, gpgHint.indexOf(" "));
+ passwordDialogMessage += "<b>Enter Password for</b><br>\n" + gpgHint + "\n";
+ }
+
+ if (mPasswordCache.isEmpty()) {
+ QString password = QInputDialog::getText(0, tr("Enter Password"),
+ passwordDialogMessage, QLineEdit::Password,
+ "", &result, Qt::Window);
+
+ if (result) mPasswordCache = password.toAscii();
+ } else {
+ result = true;
+ }
+
+ if (result) {
+
+#ifndef _WIN32
+ if (write(fd, mPasswordCache.data(), mPasswordCache.length()) == -1) {
+ qDebug() << "something is terribly broken";
+ }
+#else
+ DWORD written;
+ WriteFile((HANDLE) fd, mPasswordCache.data(), mPasswordCache.length(), &written, 0);
+#endif
+
+ returnValue = 0;
+ }
+
+#ifndef _WIN32
+ if (write(fd, "\n", 1) == -1) {
+ qDebug() << "something is terribly broken";
+ }
+#else
+ DWORD written;
+ WriteFile((HANDLE) fd, "\n", 1, &written, 0);
+#endif
+
+ return returnValue;
+}
+
+/** also from kgpgme.cpp, seems to clear password from mem */
+void GpgContext::clearPasswordCache()
+{
+ if (mPasswordCache.size() > 0) {
+ mPasswordCache.fill('\0');
+ mPasswordCache.truncate(0);
+ }
+}
+
+// error-handling
+int GpgContext::checkErr(gpgme_error_t err, QString comment) const
+{
+ //if (err != GPG_ERR_NO_ERROR && err != GPG_ERR_CANCELED) {
+ if (err != GPG_ERR_NO_ERROR) {
+ qDebug() << "[Error " << comment << "] Source: " << gpgme_strsource(err) << " String: " << gpgme_strerror(err);
+ }
+ return err;
+}
+
+int GpgContext::checkErr(gpgme_error_t err) const
+{
+ //if (err != GPG_ERR_NO_ERROR && err != GPG_ERR_CANCELED) {
+ if (err != GPG_ERR_NO_ERROR) {
+ qDebug() << "[Error] Source: " << gpgme_strsource(err) << " String: " << gpgme_strerror(err);
+ }
+ return err;
+}
+
+
+/** export private key, TODO errohandling, e.g. like in seahorse (seahorse-gpg-op.c) **/
+
+void GpgContext::exportSecretKey(QString uid, QByteArray *outBuffer)
+{
+ // export private key to outBuffer
+ QStringList arguments;
+ arguments << "--armor" << "--export-secret-key" << uid;
+ QByteArray *err = new QByteArray();
+ executeGpgCommand(arguments, outBuffer, err);
+
+ // append public key to outBuffer
+ QByteArray *pubKey = new QByteArray();
+ QStringList keyList;
+ keyList.append(uid);
+ exportKeys(&keyList,pubKey);
+ outBuffer->append(*pubKey);
+}
+
+/** return type should be gpgme_error_t*/
+void GpgContext::executeGpgCommand(QStringList arguments, QByteArray *stdOut, QByteArray *stdErr)
+{
+ gpgme_engine_info_t engine = gpgme_ctx_get_engine_info(mCtx);
+
+ QStringList args;
+ args << "--homedir" << engine->home_dir << "--batch" << arguments;
+
+ QProcess gpg;
+ gpg.start(engine->file_name, args);
+ gpg.waitForFinished();
+
+ *stdOut = gpg.readAllStandardOutput();
+ *stdErr = gpg.readAllStandardError();
+}
+
+/***
+ * TODO: return type should contain:
+ * -> list of sigs
+ * -> valid
+ * -> errors
+ */
+gpgme_signature_t GpgContext::verify(QByteArray inBuffer) {
+
+ int error=0;
+ gpgme_data_t in;
+ gpgme_error_t err;
+ gpgme_signature_t sign;
+ gpgme_verify_result_t result;
+
+ err = gpgme_data_new_from_mem(&in, inBuffer.data(), inBuffer.size(), 1);
+ checkErr(err);
+
+ err = gpgme_op_verify (mCtx, in, NULL, in);
+ error = checkErr(err);
+
+ if (error != 0) {
+ return NULL;
+ }
+
+ result = gpgme_op_verify_result (mCtx);
+ sign = result->signatures;
+ return sign;
+}
+
+/***
+ * return type should contain:
+ * -> list of sigs
+ * -> valid
+ * -> decrypted message
+ */
+//void GpgContext::decryptVerify(QByteArray in) {
+
+/* gpgme_error_t err;
+ gpgme_data_t in, out;
+
+ gpgme_decrypt_result_t decrypt_result;
+ gpgme_verify_result_t verify_result;
+
+ err = gpgme_op_decrypt_verify (mCtx, in, out);
+ decrypt_result = gpgme_op_decrypt_result (mCtx);
+
+ verify_result = gpgme_op_verify_result (mCtx);
+ */
+//}
+
+bool GpgContext::sign(QStringList *uidList, const QByteArray &inBuffer, QByteArray *outBuffer ) {
+
+ gpgme_error_t err;
+ gpgme_data_t in, out;
+ gpgme_sign_result_t result;
+
+ if (uidList->count() == 0) {
+ QMessageBox::critical(0, tr("Key Selection"), tr("No Private Key Selected"));
+ return false;
+ }
+
+ // at start or end?
+ gpgme_signers_clear(mCtx);
+
+ //gpgme_encrypt_result_t e_result;
+ gpgme_key_t signers[uidList->count()+1];
+
+
+ // TODO: do we really need array? adding one key in loop should be ok
+ for (int i = 0; i < uidList->count(); i++) {
+ // the last 0 is for public keys, 1 would return private keys
+ gpgme_op_keylist_start(mCtx, uidList->at(i).toAscii().constData(), 0);
+ gpgme_op_keylist_next(mCtx, &signers[i]);
+ gpgme_op_keylist_end(mCtx);
+
+ err = gpgme_signers_add (mCtx, signers[i]);
+ checkErr(err);
+ }
+
+ err = gpgme_data_new_from_mem(&in, inBuffer.data(), inBuffer.size(), 1);
+ checkErr(err);
+ err = gpgme_data_new (&out);
+ checkErr(err);
+
+ /*
+ `GPGME_SIG_MODE_NORMAL'
+ A normal signature is made, the output includes the plaintext
+ and the signature.
+
+ `GPGME_SIG_MODE_DETACH'
+ A detached signature is made.
+
+ `GPGME_SIG_MODE_CLEAR'
+ A clear text signature is made. The ASCII armor and text
+ mode settings of the context are ignored.
+ */
+
+ err = gpgme_op_sign (mCtx, in, out, GPGME_SIG_MODE_CLEAR);
+ checkErr (err);
+
+ if (err == GPG_ERR_CANCELED) {
+ return false;
+ }
+
+ if (err != GPG_ERR_NO_ERROR) {
+ QMessageBox::critical(0, tr("Error signing:"), gpgme_strerror(err));
+ return false;
+ }
+
+ result = gpgme_op_sign_result (mCtx);
+ err = readToBuffer(out, outBuffer);
+ checkErr (err);
+
+ gpgme_data_release(in);
+ gpgme_data_release(out);
+
+ return (err == GPG_ERR_NO_ERROR);
+}
+
+/*
+ * if there is no '\n' before the PGP-Begin-Block, but for example a whitespace,
+ * GPGME doesn't recognise the Message as encrypted. This function adds '\n'
+ * before the PGP-Begin-Block, if missing.
+ */
+void GpgContext::preventNoDataErr(QByteArray *in)
+{
+ int block_start = in->indexOf("-----BEGIN PGP MESSAGE-----");
+ if (block_start > 0 && in->at(block_start - 1) != '\n') {
+ in->insert(block_start, '\n');
+ }
+ block_start = in->indexOf("-----BEGIN PGP SIGNED MESSAGE-----");
+ if (block_start > 0 && in->at(block_start - 1) != '\n') {
+ in->insert(block_start, '\n');
+ }
+}
+
+/*
+ * isSigned returns:
+ * - 0, if text isn't signed at all
+ * - 1, if text is partially signed
+ * - 2, if text is completly signed
+ */
+int GpgContext::textIsSigned(const QByteArray &text) {
+ if (text.trimmed().startsWith("-----BEGIN PGP SIGNED MESSAGE-----") && text.trimmed().endsWith("-----END PGP SIGNATURE-----")) {
+ return 2;
+ }
+ if (text.contains("-----BEGIN PGP SIGNED MESSAGE-----") && text.contains("-----END PGP SIGNATURE-----")) {
+ return 1;
+ }
+ return 0;
+}
+
+QString GpgContext::beautifyFingerprint(QString fingerprint)
+{
+ uint len = fingerprint.length();
+ if ((len > 0) && (len % 4 == 0))
+ for (uint n = 0; 4 *(n + 1) < len; ++n)
+ fingerprint.insert(5 * n + 4, ' ');
+ return fingerprint;
+}
+
+}
+
+
+
+
+