summaryrefslogtreecommitdiffstats
path: root/src/kgpgme.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/kgpgme.cpp')
-rw-r--r--src/kgpgme.cpp443
1 files changed, 443 insertions, 0 deletions
diff --git a/src/kgpgme.cpp b/src/kgpgme.cpp
new file mode 100644
index 0000000..4aaeeeb
--- /dev/null
+++ b/src/kgpgme.cpp
@@ -0,0 +1,443 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Petri Damsten *
+ * damu@iki.fi *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "kgpgme.h"
+
+#ifdef HAVE_LIBGPGME
+
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <kpassdlg.h>
+#include <kiconloader.h>
+#include <klistview.h>
+#include <kdebug.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <klocale.h>
+#include <locale.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+// KGpgSelKey class based on class in KGpg with the same name
+
+class KGpgSelKey : public KDialogBase
+{
+ private:
+ KListView* keysListpr;
+
+ public:
+
+ KGpgSelKey(QWidget *parent, const char *name, QString preselected,
+ const KGpgMe& gpg):
+ KDialogBase( parent, name, true,i18n("Private Key List"),Ok | Cancel) {
+ QString keyname;
+ QVBoxLayout* vbox;
+ QWidget* page = new QWidget(this);
+ QLabel* labeltxt;
+ KIconLoader* loader = KGlobal::iconLoader();
+ QPixmap keyPair = loader->loadIcon("kgpg_key2", KIcon::Small, 20);
+
+ setMinimumSize(350,100);
+ keysListpr = new KListView(page);
+ keysListpr->setRootIsDecorated(true);
+ keysListpr->addColumn(i18n("Name"));
+ keysListpr->addColumn(i18n("Email"));
+ keysListpr->addColumn(i18n("ID"));
+ keysListpr->setShowSortIndicator(true);
+ keysListpr->setFullWidth(true);
+ keysListpr->setAllColumnsShowFocus(true);
+
+ labeltxt = new QLabel(i18n("Choose a secret key:"),page);
+ vbox = new QVBoxLayout(page);
+
+ KGpgKeyList list = gpg.keys(true);
+
+ for(KGpgKeyList::iterator it = list.begin(); it != list.end(); ++it) {
+ QString name = gpg.checkForUtf8((*it).name);
+ KListViewItem *item = new
+ KListViewItem(keysListpr, name, (*it).email, (*it).id);
+ item->setPixmap(0,keyPair);
+ if(preselected == (*it).id) {
+ keysListpr->setSelected(item, true);
+ keysListpr->setCurrentItem(item);
+ }
+ }
+ if(!keysListpr->selectedItem()) {
+ keysListpr->setSelected(keysListpr->firstChild(), true);
+ keysListpr->setCurrentItem(keysListpr->firstChild());
+ }
+ vbox->addWidget(labeltxt);
+ vbox->addWidget(keysListpr);
+ setMainWidget(page);
+ };
+
+ QString key() {
+ QListViewItem* item = keysListpr->selectedItem();
+
+ if(item)
+ return item->text(2);
+ return "";
+ }
+};
+
+KGpgMe::KGpgMe() : m_ctx(0), m_useGnuPGAgent(true)
+{
+ init(GPGME_PROTOCOL_OpenPGP);
+ if(gpgme_new(&m_ctx)) {
+ m_ctx = 0;
+ }
+ else {
+ gpgme_set_armor(m_ctx, 1);
+ setPassphraseCb();
+ }
+}
+
+KGpgMe::~KGpgMe()
+{
+ if(m_ctx)
+ gpgme_release(m_ctx);
+ clearCache();
+}
+
+void KGpgMe::clearCache()
+{
+ if(m_cache.size() > 0)
+ {
+ m_cache.fill('\0');
+ m_cache.truncate(0);
+ }
+}
+
+void KGpgMe::init(gpgme_protocol_t proto)
+{
+ gpgme_error_t err;
+
+ gpgme_check_version(NULL);
+ setlocale(LC_ALL, "");
+ gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
+ gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL));
+
+ err = gpgme_engine_check_version(proto);
+ if(err) {
+ KMessageBox::error(kapp->activeWindow(), QString("%1: %2")
+ .arg(gpgme_strsource(err)).arg(gpgme_strerror(err)));
+ }
+}
+
+QString KGpgMe::checkForUtf8(QString txt)
+{
+ // code borrowed from KGpg which borrowed it from gpa
+ const char *s;
+
+ // Make sure the encoding is UTF-8.
+ // Test structure suggested by Werner Koch
+ if(txt.isEmpty())
+ return QString::null;
+
+ for(s = txt.ascii(); *s && !(*s & 0x80); s++)
+ ;
+ if (*s && !strchr (txt.ascii(), 0xc3) && (txt.find("\\x")==-1))
+ return txt;
+
+ // The string is not in UTF-8
+ //if (strchr (txt.ascii(), 0xc3)) return (txt+" +++");
+ if (txt.find("\\x")==-1)
+ return QString::fromUtf8(txt.ascii());
+ // if (!strchr (txt.ascii(), 0xc3) || (txt.find("\\x")!=-1)) {
+ for(int idx = 0 ; (idx = txt.find( "\\x", idx )) >= 0 ; ++idx) {
+ char str[2] = "x";
+ str[0] = (char)QString(txt.mid(idx + 2, 2)).toShort(0, 16);
+ txt.replace(idx, 4, str);
+ }
+ if (!strchr (txt.ascii(), 0xc3))
+ return QString::fromUtf8(txt.ascii());
+ else
+ return QString::fromUtf8(QString::fromUtf8(txt.ascii()).ascii());
+ // perform Utf8 twice, or some keys display badly
+ return txt;
+}
+
+QString KGpgMe::selectKey(QString previous)
+{
+ KGpgSelKey dlg(kapp->activeWindow(), "", previous, *this);
+
+ if(dlg.exec())
+ return dlg.key();
+ return "";
+}
+
+// Rest of the code is mainly based in gpgme examples
+
+KGpgKeyList KGpgMe::keys(bool privateKeys /* = false */) const
+{
+ KGpgKeyList keys;
+ gpgme_error_t err = 0, err2 = 0;
+ gpgme_key_t key = 0;
+ gpgme_keylist_result_t result = 0;
+
+ if(m_ctx) {
+ err = gpgme_op_keylist_start(m_ctx, NULL, privateKeys);
+ if(!err) {
+ while(!(err = gpgme_op_keylist_next(m_ctx, &key))) {
+ KGpgKey gpgkey;
+
+ if(!key->subkeys)
+ continue;
+ gpgkey.id = key->subkeys->keyid;
+ if(key->uids) {
+ gpgkey.name = key->uids->name;
+ gpgkey.email = key->uids->email;
+ }
+ keys.append(gpgkey);
+ gpgme_key_unref(key);
+ }
+
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ err = 0;
+ err2 = gpgme_op_keylist_end(m_ctx);
+ if(!err)
+ err = err2;
+ }
+ }
+
+ if(err) {
+ KMessageBox::error(kapp->activeWindow(), QString("%1: %2")
+ .arg(gpgme_strsource(err)).arg(gpgme_strerror(err)));
+ }
+ else {
+ result = gpgme_op_keylist_result(m_ctx);
+ if (result->truncated) {
+ KMessageBox::error(kapp->activeWindow(),
+ i18n("Key listing unexpectedly truncated."));
+ }
+ }
+ return keys;
+}
+
+bool KGpgMe::encrypt(const QByteArray& inBuffer, Q_ULONG length,
+ QByteArray* outBuffer, QString keyid /* = QString::null */)
+{
+ gpgme_error_t err = 0;
+ gpgme_data_t in = 0, out = 0;
+ gpgme_key_t keys[2] = { NULL, NULL };
+ gpgme_key_t* key = NULL;
+ gpgme_encrypt_result_t result = 0;
+
+ outBuffer->resize(0);
+ if(m_ctx) {
+ err = gpgme_data_new_from_mem(&in, inBuffer.data(), length, 1);
+ if(!err) {
+ err = gpgme_data_new(&out);
+ if(!err) {
+ if(keyid.isNull()) {
+ key = NULL;
+ }
+ else {
+ err = gpgme_get_key(m_ctx, keyid.ascii(), &keys[0], 0);
+ key = keys;
+ }
+
+ if(!err) {
+ err = gpgme_op_encrypt(m_ctx, key, GPGME_ENCRYPT_ALWAYS_TRUST,
+ in, out);
+ if(!err) {
+ result = gpgme_op_encrypt_result(m_ctx);
+ if (result->invalid_recipients) {
+ KMessageBox::error(kapp->activeWindow(), QString("%1: %2")
+ .arg(i18n("That public key is not meant for encryption"))
+ .arg(result->invalid_recipients->fpr));
+ }
+ else {
+ err = readToBuffer(out, outBuffer);
+ }
+ }
+ }
+ }
+ }
+ }
+ if(err != GPG_ERR_NO_ERROR && err != GPG_ERR_CANCELED) {
+ KMessageBox::error(kapp->activeWindow(), QString("%1: %2")
+ .arg(gpgme_strsource(err)).arg(gpgme_strerror(err)));
+ }
+ if(err != GPG_ERR_NO_ERROR)
+ clearCache();
+ if(keys[0])
+ gpgme_key_unref(keys[0]);
+ if(in)
+ gpgme_data_release(in);
+ if(out)
+ gpgme_data_release(out);
+ return (err == GPG_ERR_NO_ERROR);
+}
+
+bool KGpgMe::decrypt(const QByteArray& inBuffer, QByteArray* outBuffer)
+{
+ gpgme_error_t err = 0;
+ gpgme_data_t in = 0, out = 0;
+ gpgme_decrypt_result_t result = 0;
+
+ outBuffer->resize(0);
+ if(m_ctx) {
+ err = gpgme_data_new_from_mem(&in, inBuffer.data(), inBuffer.size(), 1);
+ if(!err) {
+ err = gpgme_data_new(&out);
+ if(!err) {
+ err = gpgme_op_decrypt(m_ctx, in, out);
+ if(!err) {
+ result = gpgme_op_decrypt_result(m_ctx);
+ if(result->unsupported_algorithm) {
+ KMessageBox::error(kapp->activeWindow(), QString("%1: %2")
+ .arg(i18n("Unsupported algorithm"))
+ .arg(result->unsupported_algorithm));
+ }
+ else {
+ err = readToBuffer(out, outBuffer);
+ }
+ }
+ }
+ }
+ }
+ if(err != GPG_ERR_NO_ERROR && err != GPG_ERR_CANCELED) {
+ KMessageBox::error(kapp->activeWindow(), QString("%1: %2")
+ .arg(gpgme_strsource(err)).arg(gpgme_strerror(err)));
+ }
+ if(err != GPG_ERR_NO_ERROR)
+ clearCache();
+ if(in)
+ gpgme_data_release(in);
+ if(out)
+ gpgme_data_release(out);
+ return (err == GPG_ERR_NO_ERROR);
+}
+
+#define BUF_SIZE (32 * 1024)
+
+gpgme_error_t KGpgMe::readToBuffer(gpgme_data_t in, QByteArray* outBuffer) const
+{
+ 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);
+ }
+ else {
+ char* buf = new char[BUF_SIZE + 2];
+
+ if(buf) {
+ while((ret = gpgme_data_read(in, buf, BUF_SIZE)) > 0) {
+ uint size = outBuffer->size();
+ if(outBuffer->resize(size + ret))
+ memcpy(outBuffer->data() + size, buf, ret);
+ }
+ if(ret < 0)
+ err = gpgme_err_code_from_errno(errno);
+ delete[] buf;
+ }
+ }
+ return err;
+}
+
+bool KGpgMe::isGnuPGAgentAvailable()
+{
+ QString agent_info = getenv("GPG_AGENT_INFO");
+
+ if (agent_info.find(':') > 0)
+ return true;
+ return false;
+}
+
+void KGpgMe::setPassphraseCb()
+{
+ bool agent = false;
+ QString agent_info;
+
+ agent_info = getenv("GPG_AGENT_INFO");
+
+ if(m_useGnuPGAgent)
+ {
+ if (agent_info.find(':'))
+ agent = true;
+ if(agent_info.startsWith("disable:"))
+ setenv("GPG_AGENT_INFO", agent_info.mid(8), 1);
+ }
+ else
+ {
+ if(!agent_info.startsWith("disable:"))
+ setenv("GPG_AGENT_INFO", "disable:" + agent_info, 1);
+ }
+ if (agent)
+ gpgme_set_passphrase_cb(m_ctx, 0, 0);
+ else
+ gpgme_set_passphrase_cb(m_ctx, passphraseCb, this);
+}
+
+gpgme_error_t KGpgMe::passphraseCb(void* hook, const char* uid_hint,
+ const char* passphrase_info,
+ int last_was_bad, int fd)
+{
+ KGpgMe* gpg = static_cast<KGpgMe*>(hook);
+ return gpg->passphrase(uid_hint, passphrase_info, last_was_bad, fd);
+}
+
+gpgme_error_t KGpgMe::passphrase(const char* uid_hint,
+ const char* /*passphrase_info*/,
+ int last_was_bad, int fd)
+{
+ gpgme_error_t res = GPG_ERR_CANCELED;
+ QString s;
+ QString gpg_hint = checkForUtf8(uid_hint);
+ int result;
+
+ if(last_was_bad){
+ s += "<b>" + i18n("Wrong password.") + "</b><br><br>\n\n";
+ clearCache();
+ }
+
+ if(!m_text.isEmpty())
+ s += m_text + "<br>";
+
+ if(!gpg_hint.isEmpty())
+ s += gpg_hint;
+
+ if(m_cache.isEmpty()){
+ QCString password;
+
+ if(m_saving)
+ result = KPasswordDialog::getNewPassword(password, s);
+ else
+ result = KPasswordDialog::getPassword(password, s);
+
+ if(result == KPasswordDialog::Accepted)
+ m_cache = password;
+ }
+ else
+ result = KPasswordDialog::Accepted;
+
+ if(result == KPasswordDialog::Accepted) {
+ write(fd, m_cache.data(), m_cache.length());
+ res = 0;
+ }
+ write(fd, "\n", 1);
+ return res;
+}
+#endif // HAVE_LIBGPGME