Renamed to olgpgmain and olgpgcore. Added fucntion to retrieve the
[gpgol.git] / src / passphrase-dialog.c
1 /* passphrase-dialog.c
2  *      Copyright (C) 2004 Timo Schulz
3  *      Copyright (C) 2005 g10 Code GmbH
4  *
5  * This file is part of GPGME Dialogs.
6  *
7  * GPGME Dialogs is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation; either version 2.1 
10  * of the License, or (at your option) any later version.
11  *  
12  * GPGME Dialogs is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with GPGME Dialogs; if not, write to the Free Software Foundation, 
19  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
20  */
21 #include <windows.h>
22 #include <time.h>
23
24 #include "olgpgcoredlgs.h"
25 #include "gpgme.h"
26 #include "keycache.h"
27 #include "intern.h"
28 #include "usermap.h"
29
30 static void
31 add_string_list (HWND hbox, const char **list, int start_idx)
32 {
33     const char * s;
34     int i;
35
36     for (i=0; (s=list[i]); i++)
37         SendMessage (hbox, CB_ADDSTRING, 0, (LPARAM)(const char *)s);
38     SendMessage (hbox, CB_SETCURSEL, (WPARAM) start_idx, 0);
39 }
40
41
42 static void
43 set_key_hint (struct decrypt_key_s * dec, HWND dlg, int ctrlid)
44 {
45     const char *s = dec->user_id;
46     char *key_hint;
47     char stop_char=0;
48     size_t i=0;
49
50     if (dec->user_id != NULL) {
51         key_hint = (char *)xmalloc (17 + strlen (dec->user_id) + 32);
52         if (strchr (s, '<') && strchr (s, '>'))
53             stop_char = '<';
54         else if (strchr (s, '(') && strchr (s, ')'))
55             stop_char = '(';
56         while (s && *s != stop_char)
57             key_hint[i++] = *s++;
58         key_hint[i++] = ' ';
59         sprintf (key_hint+i, "(0x%s)", dec->keyid+8);
60     }
61     else
62         key_hint = xstrdup ("No key hint given.");
63     SendDlgItemMessage (dlg, ctrlid, CB_ADDSTRING, 0, 
64                         (LPARAM)(const char *)key_hint);
65     SendDlgItemMessage (dlg, ctrlid, CB_SETCURSEL, 0, 0);
66     xfree (key_hint);
67 }
68
69
70 static void
71 load_recipbox (HWND dlg, int ctlid, gpgme_ctx_t ctx)
72 {       
73     gpgme_decrypt_result_t res;
74     gpgme_recipient_t r;
75     void *usermap ;
76
77     if (ctx == NULL)
78         return;
79     res = gpgme_op_decrypt_result (ctx);
80     if (res == NULL || res->recipients == NULL)
81         return;
82     usermap = new_usermap (res->recipients);
83     for (r = res->recipients; r; r = r->next) {
84         char *userid = HashTable_get (usermap, r->keyid);
85         SendDlgItemMessage (dlg, ctlid, LB_ADDSTRING, 0, 
86                             (LPARAM)(const char*)userid);
87     }
88     free_usermap (usermap);
89 }
90
91
92 static void
93 load_secbox (HWND dlg, int ctlid)
94 {
95     gpgme_key_t sk;
96     size_t n=0, doloop=1;
97     void *ctx=NULL;
98
99     enum_gpg_seckeys (NULL, &ctx);
100     while (doloop) {
101         const char *name, *email, *keyid, *algo;
102         char *p;
103
104         if (enum_gpg_seckeys (&sk, &ctx))
105             doloop = 0;
106
107         if (gpgme_key_get_ulong_attr (sk, GPGME_ATTR_KEY_REVOKED, NULL, 0) ||
108             gpgme_key_get_ulong_attr (sk, GPGME_ATTR_KEY_EXPIRED, NULL, 0) ||
109             gpgme_key_get_ulong_attr (sk, GPGME_ATTR_KEY_INVALID, NULL, 0))
110             continue;
111         
112         name = gpgme_key_get_string_attr (sk, GPGME_ATTR_NAME, NULL, 0);
113         email = gpgme_key_get_string_attr (sk, GPGME_ATTR_EMAIL, NULL, 0);
114         keyid = gpgme_key_get_string_attr (sk, GPGME_ATTR_KEYID, NULL, 0);
115         algo = gpgme_key_get_string_attr (sk, GPGME_ATTR_ALGO, NULL, 0);
116         if (!email)
117             email = "";
118         p = (char *)xcalloc (1, strlen (name) + strlen (email) + 17 + 32);
119         if (email && strlen (email))
120             sprintf (p, "%s <%s> (0x%s, %s)", name, email, keyid+8, algo);
121         else
122             sprintf (p, "%s (0x%s, %s)", name, keyid+8, algo);
123         SendDlgItemMessage (dlg, ctlid, CB_ADDSTRING, 0, 
124                             (LPARAM)(const char *) p);
125         xfree (p);
126     }
127     
128     ctx = NULL;
129     reset_gpg_seckeys (&ctx);
130     doloop = 1;
131     n = 0;
132     while (doloop) {
133         if (enum_gpg_seckeys (&sk, &ctx))
134             doloop = 0;
135         if (gpgme_key_get_ulong_attr (sk, GPGME_ATTR_KEY_REVOKED, NULL, 0) ||
136             gpgme_key_get_ulong_attr (sk, GPGME_ATTR_KEY_EXPIRED, NULL, 0) ||
137             gpgme_key_get_ulong_attr (sk, GPGME_ATTR_KEY_INVALID, NULL, 0))
138             continue;
139         SendDlgItemMessage (dlg, ctlid, CB_SETITEMDATA, n, (LPARAM)(DWORD)sk);
140         n++;
141     }
142     SendDlgItemMessage (dlg, ctlid, CB_SETCURSEL, 0, 0);
143     reset_gpg_seckeys (&ctx);
144 }
145
146
147 static BOOL CALLBACK
148 decrypt_key_dlg_proc (HWND dlg, UINT msg, WPARAM wparam, LPARAM lparam)
149 {
150     static struct decrypt_key_s * dec;
151     static int hide_state = 1;
152     size_t n;
153
154     switch (msg) {
155     case WM_INITDIALOG:
156         log_debug ("decrypt_key_dlg_proc: WM_INITDIALOG\n");
157         dec = (struct decrypt_key_s *)lparam;
158         if (dec && dec->use_as_cb) {
159             dec->opts = 0;
160             dec->pass = NULL;
161             set_key_hint (dec, dlg, IDC_DEC_KEYLIST);
162             EnableWindow (GetDlgItem (dlg, IDC_DEC_KEYLIST), FALSE);
163         }
164         if (dec && dec->last_was_bad)
165             SetDlgItemText (dlg, IDC_DEC_HINT, "Invalid passphrase; please try again...");
166         else
167             SetDlgItemText (dlg, IDC_DEC_HINT, "");
168         if (dec && !dec->use_as_cb)
169             load_secbox (dlg, IDC_DEC_KEYLIST);
170         CheckDlgButton (dlg, IDC_DEC_HIDE, BST_CHECKED);
171         center_window (dlg, NULL);
172         if (dec->hide_pwd) {
173             ShowWindow (GetDlgItem (dlg, IDC_DEC_HIDE), SW_HIDE);
174             ShowWindow (GetDlgItem (dlg, IDC_DEC_PASS), SW_HIDE);
175             ShowWindow (GetDlgItem (dlg, IDC_DEC_PASSINF), SW_HIDE);
176             /* XXX: make the dialog window smaller */
177         }
178         else
179             SetFocus (GetDlgItem (dlg, IDC_DEC_PASS));
180         SetForegroundWindow (dlg);
181         return FALSE;
182
183     case WM_DESTROY:
184         log_debug ("decrypt_key_dlg_proc: WM_DESTROY\n");
185         hide_state = 1;
186         break;
187
188     case WM_SYSCOMMAND:
189         log_debug ("decrypt_key_dlg_proc: WM_SYSCOMMAND\n");
190         if (wparam == SC_CLOSE)
191             EndDialog (dlg, TRUE);
192         break;
193
194     case WM_COMMAND:
195         log_debug ("decrypt_key_dlg_proc: WM_COMMAND\n");
196         switch (HIWORD (wparam)) {
197         case BN_CLICKED:
198             if ((int)LOWORD (wparam) == IDC_DEC_HIDE) {
199                 HWND hwnd;
200
201                 hide_state ^= 1;
202                 hwnd = GetDlgItem (dlg, IDC_DEC_PASS);
203                 SendMessage (hwnd, EM_SETPASSWORDCHAR, hide_state? '*' : 0, 0);
204                 SetFocus (hwnd);
205             }
206             break;
207         }
208         switch (LOWORD (wparam)) {
209         case IDOK:
210             n = SendDlgItemMessage (dlg, IDC_DEC_PASS, WM_GETTEXTLENGTH, 0, 0);
211             if (n) {
212                 dec->pass = (char *)xcalloc (1, n+2);
213                 GetDlgItemText (dlg, IDC_DEC_PASS, dec->pass, n+1);
214             }
215             if (!dec->use_as_cb) {
216                 int idx = SendDlgItemMessage (dlg, IDC_DEC_KEYLIST, 
217                                               CB_GETCURSEL, 0, 0);
218                 dec->signer = (gpgme_key_t)SendDlgItemMessage (dlg, IDC_DEC_KEYLIST,
219                                                                CB_GETITEMDATA, idx, 0);
220                 gpgme_key_ref (dec->signer);
221             }
222             EndDialog (dlg, TRUE);
223             break;
224
225         case IDCANCEL:
226             if (dec && dec->use_as_cb && (dec->flags & 0x01)) {
227                 const char *warn = "If you cancel this dialog, the message will be sent without signing.\n\n"
228                                    "Do you really want to cancel?";
229                 n = MessageBox (dlg, warn, "Secret Key Dialog", MB_ICONWARNING|MB_YESNO);
230                 if (n == IDNO)
231                     return FALSE;
232             }
233             dec->opts = OPT_FLAG_CANCEL;
234             dec->pass = NULL;
235             EndDialog (dlg, FALSE);
236             break;
237         }
238         break;
239     }
240     return FALSE;
241 }
242
243
244 static BOOL CALLBACK
245 decrypt_key_ext_dlg_proc (HWND dlg, UINT msg, WPARAM wparam, LPARAM lparam)
246 {
247     static struct decrypt_key_s * dec;
248     static int hide_state = 1;
249     size_t n;
250
251     switch (msg) {
252     case WM_INITDIALOG:
253         dec = (struct decrypt_key_s *)lparam;
254         if (dec != NULL) {
255             dec->opts = 0;
256             dec->pass = NULL;
257             set_key_hint (dec, dlg, IDC_DECEXT_KEYLIST);
258             EnableWindow (GetDlgItem (dlg, IDC_DECEXT_KEYLIST), FALSE);
259         }
260         if (dec && dec->last_was_bad)
261             SetDlgItemText (dlg, IDC_DECEXT_HINT, "Invalid passphrase; please try again...");
262         else
263             SetDlgItemText (dlg, IDC_DECEXT_HINT, "");
264         if (dec != NULL)
265             load_recipbox (dlg, IDC_DECEXT_RSET, (gpgme_ctx_t)dec->ctx);
266         CheckDlgButton (dlg, IDC_DECEXT_HIDE, BST_CHECKED);
267         center_window (dlg, NULL);
268         SetFocus (GetDlgItem (dlg, IDC_DECEXT_PASS));
269         SetForegroundWindow (dlg);
270         return FALSE;
271
272     case WM_DESTROY:
273         hide_state = 1;
274         break;
275
276     case WM_SYSCOMMAND:
277         if (wparam == SC_CLOSE)
278             EndDialog (dlg, TRUE);
279         break;
280
281     case WM_COMMAND:
282         switch (HIWORD (wparam)) {
283         case BN_CLICKED:
284             if ((int)LOWORD (wparam) == IDC_DECEXT_HIDE) {
285                 HWND hwnd;
286
287                 hide_state ^= 1;
288                 hwnd = GetDlgItem (dlg, IDC_DECEXT_PASS);
289                 SendMessage (hwnd, EM_SETPASSWORDCHAR, hide_state? '*' : 0, 0);
290                 SetFocus (hwnd);
291             }
292             break;
293         }
294         switch (LOWORD (wparam)) {
295         case IDOK:
296             n = SendDlgItemMessage (dlg, IDC_DECEXT_PASS, WM_GETTEXTLENGTH, 0, 0);
297             if (n) {
298                 dec->pass = (char *)xcalloc( 1, n+2 );
299                 GetDlgItemText( dlg, IDC_DECEXT_PASS, dec->pass, n+1 );
300             }
301             EndDialog (dlg, TRUE);
302             break;
303
304         case IDCANCEL:
305             if (dec && dec->use_as_cb && (dec->flags & 0x01)) {
306                 const char *warn = "If you cancel this dialog, the message will be sent without signing.\n"
307                                    "Do you really want to cancel?";
308                 n = MessageBox (dlg, warn, "Secret Key Dialog", MB_ICONWARNING|MB_YESNO);
309                 if (n == IDNO)
310                     return FALSE;
311             }
312             dec->opts = OPT_FLAG_CANCEL;
313             dec->pass = NULL;
314             EndDialog (dlg, FALSE);
315             break;
316         }
317         break;
318     }
319     return FALSE;
320 }
321
322 /* Display a signer dialog which contains all secret keys, useable
323    for signing data. The key is returned in r_key. The password in
324    r_passwd. */
325 int 
326 signer_dialog_box (gpgme_key_t *r_key, char **r_passwd)
327 {
328     struct decrypt_key_s hd;
329     int rc = 0;
330
331     memset(&hd, 0, sizeof (hd));
332     hd.hide_pwd = 1;
333     DialogBoxParam (glob_hinst, (LPCTSTR)IDD_DEC, GetDesktopWindow (),
334                     decrypt_key_dlg_proc, (LPARAM)&hd);
335     if (hd.signer) {
336         if (r_passwd)
337             *r_passwd = xstrdup (hd.pass);
338         else {      
339             xfree (hd.pass);
340             hd.pass = NULL;
341         }
342         *r_key = hd.signer;
343     }
344     if (hd.opts & OPT_FLAG_CANCEL)
345         rc = -1;
346     memset (&hd, 0, sizeof (hd));    
347     return rc;
348 }
349
350
351 /* GPGME passphrase callback function. It starts the decryption dialog
352    to request the passphrase from the user. */
353 gpgme_error_t
354 passphrase_callback_box (void *opaque, const char *uid_hint, 
355                          const char *pass_info,
356                          int prev_was_bad, int fd)
357 {
358     struct decrypt_key_s *hd = (struct decrypt_key_s *)opaque;
359     DWORD nwritten = 0;
360
361     log_debug ("passphrase_callback_box: enter (uh=`%s',pi=`%s')\n", 
362                uid_hint?uid_hint:"(null)", pass_info?pass_info:"(null)");
363     if (hd->opts & OPT_FLAG_CANCEL) {
364         WriteFile ((HANDLE)fd, "\n", 1, &nwritten, NULL);
365         CloseHandle ((HANDLE)fd);
366         log_debug ("passphrase_callback_box: leave (due to cancel flag)\n");
367         return -1;
368     }
369     if (prev_was_bad) {
370         log_debug ("passphrase_callback_box: last passphrase was bad\n");
371         xfree (hd->pass);
372         hd->pass = NULL;
373     }
374
375     if (hd && uid_hint && !hd->pass) {
376         const char * s = uid_hint;
377         size_t i=0;
378         int rc;
379         
380         while (s && *s != ' ')
381             hd->keyid[i++] = *s++;
382         hd->keyid[i] = '\0'; s++;
383         if (hd->user_id) {
384             xfree (hd->user_id);
385             hd->user_id = NULL;
386         }
387         hd->user_id = (char *)xcalloc (1, strlen (s) + 2);
388         strcpy (hd->user_id, s);
389
390         hd->last_was_bad = prev_was_bad? 1: 0;
391         hd->use_as_cb = 1;
392         if (hd->flags & 0x01)
393             rc = DialogBoxParam (glob_hinst, (LPCSTR)IDD_DEC, GetDesktopWindow (),
394                             decrypt_key_dlg_proc, (LPARAM)hd);
395         else
396             rc = DialogBoxParam (glob_hinst, (LPCTSTR)IDD_DEC_EXT, GetDesktopWindow (),
397                             decrypt_key_ext_dlg_proc, 
398                             (LPARAM)hd);
399         if (rc <= 0) {
400             char buf[256];
401     
402             FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (), 
403                            MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), 
404                            buf, sizeof (buf)-1, NULL);
405             
406             log_debug ("passphrase_callback_box: dialog failed rc=%d (%s)\n",
407                        rc, buf);
408         }
409     }
410     else 
411         log_debug ("passphrase_callback_box: hd=%p hd->pass=`%s'\n",
412                    hd, hd && hd->pass? hd->pass: "(null)");
413         
414     if (hd->pass != NULL) {
415         log_debug ("passphrase_callback_box: sending passphrase ...\n");
416         WriteFile ((HANDLE)fd, hd->pass, strlen (hd->pass), &nwritten, NULL);
417         WriteFile ((HANDLE)fd, "\n", 1, &nwritten, NULL);
418     }
419     else
420         WriteFile((HANDLE)fd, "\n", 1, &nwritten, NULL);
421     log_debug ("passphrase_callback_box: leave\n");
422     return 0;
423 }
424
425
426 /* Release the context which was used in the passphrase callback. */
427 void
428 free_decrypt_key (struct decrypt_key_s * ctx)
429 {
430     if (!ctx)
431         return;
432     if (ctx->pass) {
433         xfree (ctx->pass);
434         ctx->pass = NULL;
435     }
436     if (ctx->user_id) {
437         xfree (ctx->user_id);
438         ctx->user_id = NULL;
439     }
440     xfree(ctx);
441 }