1 /***************************************************************************
2 * Copyright (C) 2006 by Petri Damsten *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
25 #include <kapplication.h>
26 #include <kmessagebox.h>
28 #include <kiconloader.h>
29 #include <klistview.h>
31 #include <qcheckbox.h>
37 #include <Q3VBoxLayout>
44 // KGpgSelKey class based on class in KGpg with the same name
46 class KGpgSelKey
: public KDialogBase
49 KListView
* keysListpr
;
53 KGpgSelKey(QWidget
*parent
, const char *name
, QString preselected
,
55 KDialogBase( parent
, name
, true,i18n("Private Key List"),Ok
| Cancel
) {
58 QWidget
* page
= new QWidget(this);
60 KIconLoader
* loader
= KGlobal::iconLoader();
61 QPixmap keyPair
= loader
->loadIcon("kgpg_key2", KIcon::Small
, 20);
63 setMinimumSize(350,100);
64 keysListpr
= new KListView(page
);
65 keysListpr
->setRootIsDecorated(true);
66 keysListpr
->addColumn(i18n("Name"));
67 keysListpr
->addColumn(i18n("Email"));
68 keysListpr
->addColumn(i18n("ID"));
69 keysListpr
->setShowSortIndicator(true);
70 keysListpr
->setFullWidth(true);
71 keysListpr
->setAllColumnsShowFocus(true);
73 labeltxt
= new QLabel(i18n("Choose a secret key:"),page
);
74 vbox
= new Q3VBoxLayout(page
);
76 KGpgKeyList list
= gpg
.keys(true);
78 for(KGpgKeyList::iterator it
= list
.begin(); it
!= list
.end(); ++it
) {
79 QString name
= gpg
.checkForUtf8((*it
).name
);
80 KListViewItem
*item
= new
81 KListViewItem(keysListpr
, name
, (*it
).email
, (*it
).id
);
82 item
->setPixmap(0,keyPair
);
83 if(preselected
== (*it
).id
) {
84 keysListpr
->setSelected(item
, true);
85 keysListpr
->setCurrentItem(item
);
88 if(!keysListpr
->selectedItem()) {
89 keysListpr
->setSelected(keysListpr
->firstChild(), true);
90 keysListpr
->setCurrentItem(keysListpr
->firstChild());
92 vbox
->addWidget(labeltxt
);
93 vbox
->addWidget(keysListpr
);
98 Q3ListViewItem
* item
= keysListpr
->selectedItem();
101 return item
->text(2);
106 KGpgMe::KGpgMe() : m_ctx(0), m_useGnuPGAgent(true)
108 init(GPGME_PROTOCOL_OpenPGP
);
109 if(gpgme_new(&m_ctx
)) {
113 gpgme_set_armor(m_ctx
, 1);
121 gpgme_release(m_ctx
);
125 void KGpgMe::clearCache()
127 if(m_cache
.size() > 0)
134 void KGpgMe::init(gpgme_protocol_t proto
)
138 gpgme_check_version(NULL
);
139 setlocale(LC_ALL
, "");
140 gpgme_set_locale(NULL
, LC_CTYPE
, setlocale(LC_CTYPE
, NULL
));
141 gpgme_set_locale(NULL
, LC_MESSAGES
, setlocale(LC_MESSAGES
, NULL
));
143 err
= gpgme_engine_check_version(proto
);
145 KMessageBox::error(kapp
->activeWindow(), QString("%1: %2")
146 .arg(gpgme_strsource(err
)).arg(gpgme_strerror(err
)));
150 QString
KGpgMe::checkForUtf8(QString txt
)
152 // code borrowed from KGpg which borrowed it from gpa
155 // Make sure the encoding is UTF-8.
156 // Test structure suggested by Werner Koch
158 return QString::null
;
160 for(s
= txt
.ascii(); *s
&& !(*s
& 0x80); s
++)
162 if (*s
&& !strchr (txt
.ascii(), 0xc3) && (txt
.find("\\x")==-1))
165 // The string is not in UTF-8
166 //if (strchr (txt.ascii(), 0xc3)) return (txt+" +++");
167 if (txt
.find("\\x")==-1)
168 return QString::fromUtf8(txt
.ascii());
169 // if (!strchr (txt.ascii(), 0xc3) || (txt.find("\\x")!=-1)) {
170 for(int idx
= 0 ; (idx
= txt
.find( "\\x", idx
)) >= 0 ; ++idx
) {
172 str
[0] = (char)QString(txt
.mid(idx
+ 2, 2)).toShort(0, 16);
173 txt
.replace(idx
, 4, str
);
175 if (!strchr (txt
.ascii(), 0xc3))
176 return QString::fromUtf8(txt
.ascii());
178 return QString::fromUtf8(QString::fromUtf8(txt
.ascii()).ascii());
179 // perform Utf8 twice, or some keys display badly
183 QString
KGpgMe::selectKey(QString previous
)
185 KGpgSelKey
dlg(kapp
->activeWindow(), "", previous
, *this);
192 // Rest of the code is mainly based in gpgme examples
194 KGpgKeyList
KGpgMe::keys(bool privateKeys
/* = false */) const
197 gpgme_error_t err
= 0, err2
= 0;
199 gpgme_keylist_result_t result
= 0;
202 err
= gpgme_op_keylist_start(m_ctx
, NULL
, privateKeys
);
204 while(!(err
= gpgme_op_keylist_next(m_ctx
, &key
))) {
209 gpgkey
.id
= key
->subkeys
->keyid
;
211 gpgkey
.name
= key
->uids
->name
;
212 gpgkey
.email
= key
->uids
->email
;
215 gpgme_key_unref(key
);
218 if (gpg_err_code (err
) == GPG_ERR_EOF
)
220 err2
= gpgme_op_keylist_end(m_ctx
);
227 KMessageBox::error(kapp
->activeWindow(), QString("%1: %2")
228 .arg(gpgme_strsource(err
)).arg(gpgme_strerror(err
)));
231 result
= gpgme_op_keylist_result(m_ctx
);
232 if (result
->truncated
) {
233 KMessageBox::error(kapp
->activeWindow(),
234 i18n("Key listing unexpectedly truncated."));
240 bool KGpgMe::encrypt(const QByteArray
& inBuffer
, Q_ULONG length
,
241 QByteArray
* outBuffer
, QString keyid
/* = QString::null */)
243 gpgme_error_t err
= 0;
244 gpgme_data_t in
= 0, out
= 0;
245 gpgme_key_t keys
[2] = { NULL
, NULL
};
246 gpgme_key_t
* key
= NULL
;
247 gpgme_encrypt_result_t result
= 0;
249 outBuffer
->resize(0);
251 err
= gpgme_data_new_from_mem(&in
, inBuffer
.data(), length
, 1);
253 err
= gpgme_data_new(&out
);
259 err
= gpgme_get_key(m_ctx
, keyid
.ascii(), &keys
[0], 0);
264 err
= gpgme_op_encrypt(m_ctx
, key
, GPGME_ENCRYPT_ALWAYS_TRUST
,
267 result
= gpgme_op_encrypt_result(m_ctx
);
268 if (result
->invalid_recipients
) {
269 KMessageBox::error(kapp
->activeWindow(), QString("%1: %2")
270 .arg(i18n("That public key is not meant for encryption"))
271 .arg(result
->invalid_recipients
->fpr
));
274 err
= readToBuffer(out
, outBuffer
);
281 if(err
!= GPG_ERR_NO_ERROR
&& err
!= GPG_ERR_CANCELED
) {
282 KMessageBox::error(kapp
->activeWindow(), QString("%1: %2")
283 .arg(gpgme_strsource(err
)).arg(gpgme_strerror(err
)));
285 if(err
!= GPG_ERR_NO_ERROR
)
288 gpgme_key_unref(keys
[0]);
290 gpgme_data_release(in
);
292 gpgme_data_release(out
);
293 return (err
== GPG_ERR_NO_ERROR
);
296 bool KGpgMe::decrypt(const QByteArray
& inBuffer
, QByteArray
* outBuffer
)
298 gpgme_error_t err
= 0;
299 gpgme_data_t in
= 0, out
= 0;
300 gpgme_decrypt_result_t result
= 0;
302 outBuffer
->resize(0);
304 err
= gpgme_data_new_from_mem(&in
, inBuffer
.data(), inBuffer
.size(), 1);
306 err
= gpgme_data_new(&out
);
308 err
= gpgme_op_decrypt(m_ctx
, in
, out
);
310 result
= gpgme_op_decrypt_result(m_ctx
);
311 if(result
->unsupported_algorithm
) {
312 KMessageBox::error(kapp
->activeWindow(), QString("%1: %2")
313 .arg(i18n("Unsupported algorithm"))
314 .arg(result
->unsupported_algorithm
));
317 err
= readToBuffer(out
, outBuffer
);
323 if(err
!= GPG_ERR_NO_ERROR
&& err
!= GPG_ERR_CANCELED
) {
324 KMessageBox::error(kapp
->activeWindow(), QString("%1: %2")
325 .arg(gpgme_strsource(err
)).arg(gpgme_strerror(err
)));
327 if(err
!= GPG_ERR_NO_ERROR
)
330 gpgme_data_release(in
);
332 gpgme_data_release(out
);
333 return (err
== GPG_ERR_NO_ERROR
);
336 #define BUF_SIZE (32 * 1024)
338 gpgme_error_t
KGpgMe::readToBuffer(gpgme_data_t in
, QByteArray
* outBuffer
) const
341 gpgme_error_t err
= GPG_ERR_NO_ERROR
;
343 ret
= gpgme_data_seek(in
, 0, SEEK_SET
);
345 err
= gpgme_err_code_from_errno(errno
);
348 char* buf
= new char[BUF_SIZE
+ 2];
351 while((ret
= gpgme_data_read(in
, buf
, BUF_SIZE
)) > 0) {
352 uint size
= outBuffer
->size();
353 if(outBuffer
->resize(size
+ ret
))
354 memcpy(outBuffer
->data() + size
, buf
, ret
);
357 err
= gpgme_err_code_from_errno(errno
);
364 bool KGpgMe::isGnuPGAgentAvailable()
366 QString agent_info
= getenv("GPG_AGENT_INFO");
368 if (agent_info
.find(':') > 0)
373 void KGpgMe::setPassphraseCb()
378 agent_info
= getenv("GPG_AGENT_INFO");
382 if (agent_info
.find(':'))
384 if(agent_info
.startsWith("disable:"))
385 setenv("GPG_AGENT_INFO", agent_info
.mid(8), 1);
389 if(!agent_info
.startsWith("disable:"))
390 setenv("GPG_AGENT_INFO", "disable:" + agent_info
, 1);
393 gpgme_set_passphrase_cb(m_ctx
, 0, 0);
395 gpgme_set_passphrase_cb(m_ctx
, passphraseCb
, this);
398 gpgme_error_t
KGpgMe::passphraseCb(void* hook
, const char* uid_hint
,
399 const char* passphrase_info
,
400 int last_was_bad
, int fd
)
402 KGpgMe
* gpg
= static_cast<KGpgMe
*>(hook
);
403 return gpg
->passphrase(uid_hint
, passphrase_info
, last_was_bad
, fd
);
406 gpgme_error_t
KGpgMe::passphrase(const char* uid_hint
,
407 const char* /*passphrase_info*/,
408 int last_was_bad
, int fd
)
410 gpgme_error_t res
= GPG_ERR_CANCELED
;
412 QString gpg_hint
= checkForUtf8(uid_hint
);
416 s
+= "<b>" + i18n("Wrong password.") + "</b><br><br>\n\n";
420 if(!m_text
.isEmpty())
421 s
+= m_text
+ "<br>";
423 if(!gpg_hint
.isEmpty())
426 if(m_cache
.isEmpty()){
430 result
= KPasswordDialog::getNewPassword(password
, s
);
432 result
= KPasswordDialog::getPassword(password
, s
);
434 if(result
== KPasswordDialog::Accepted
)
438 result
= KPasswordDialog::Accepted
;
440 if(result
== KPasswordDialog::Accepted
) {
441 write(fd
, m_cache
.data(), m_cache
.length());
447 #endif // HAVE_LIBGPGME