/[winpt]/trunk/Src/wptFileManager.cpp
ViewVC logotype

Annotation of /trunk/Src/wptFileManager.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 167 - (hide annotations)
Thu Jan 26 10:17:17 2006 UTC (19 years, 1 month ago) by twoaday
File size: 48073 byte(s)
2006-01-25  Timo Schulz  <ts@g10code.com>
 
        * wptRegistry.cpp (get_reg_entry_gpg): Return NULL if
        the key exist with no value.
        * wptMDSumDlg.cpp (mdsum_dlg_proc): Translate string.
        * wptKeysignDlg.cpp (do_fill_seclist): Select the
        default key if possible.
        * wptFirstRunDlg.cpp (firstrun_dlg_proc): Directly
        return the choice.
        * wptKeylist.cpp (get_key_desc): New.
        (keylist_upd_key): Free memory.
        * wptKeyCache.cpp (gpg_keycache_get_default_key): New.
        (gpg_keycache_set_default_key): New.
        * WinPT.cpp (gpg_prefs_ok): New.
        (WinMain): Only start gpg prefs if needed.
         


1 werner 36 /* wptFileManager.cpp - File Manager routines
2     * Copyright (C) 2001-2005 Timo Schulz
3     * Copyright (C) 2005 g10 Code GmbH
4     *
5     * This file is part of WinPT.
6     *
7     * WinPT is free software; you can redistribute it and/or
8     * modify it under the terms of the GNU General Public License
9     * as published by the Free Software Foundation; either version 2
10     * of the License, or (at your option) any later version.
11     *
12     * WinPT 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 General Public License
18     * along with WinPT; if not, write to the Free Software Foundation,
19     * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20     */
21     /* TODO:
22     * check_armor_type: we should check the whole file and not only the first line!
23     */
24 twoaday 129
25 werner 42 #ifdef HAVE_CONFIG_H
26     #include <config.h>
27     #endif
28    
29 werner 36 #include <sys/types.h>
30     #include <windows.h>
31     #include <commdlg.h>
32     #include <io.h>
33 twoaday 41 #include <stdio.h>
34 werner 36
35 werner 47 #include "resource.h"
36 werner 36 #include "wptTypes.h"
37     #include "wptGPG.h"
38     #include "wptAgent.h"
39     #include "wptCommonCtl.h"
40     #include "wptContext.h"
41     #include "wptErrors.h"
42     #include "wptKeylist.h"
43     #include "wptFileManager.h"
44     #include "wptNLS.h"
45     #include "wptW32API.h"
46     #include "wptVersion.h"
47     #include "wptDlgs.h"
48     #include "wptZIP.h"
49     #include "wptUTF8.h"
50     #include "wptRegistry.h"
51     #include "wptImport.h"
52 werner 48 #include "wptCrypto.h"
53 twoaday 129 #include "wptKeyManager.h"
54 werner 36 #include "openpgp.h"
55    
56     #define op_begin() SetCursor (LoadCursor (NULL, IDC_WAIT))
57     #define op_end() SetCursor (LoadCursor (NULL, IDC_ARROW))
58    
59     void progress_cleanup (progress_filter_s *pfx);
60     BOOL CALLBACK file_secdel_confirm_dlg_proc (HWND dlg, UINT msg,
61     WPARAM wparam, LPARAM lparam);
62     char* gpg_keylist_to_pattern (gpgme_key_t *rset, int n);
63     gpgme_error_t sym_passphrase_cb (void *hook, const char *hint, const char *pass_inf,
64     int prev_was_bad, int fd);
65    
66     /*-- wptFileVerifyDlg.cpp --*/
67     void file_verify_add_state (file_sig_ctx_t c);
68     void file_verify_use_event (void);
69     void file_verify_wait (void);
70    
71     static const char * mm_files[] = {".mov", ".avi", ".mpg", ".mpeg",
72     ".mp3", ".wav", ".mid", ".wma",
73     ".gif", ".jpg", ".png", ".jpeg", ".dib", 0};
74    
75    
76     /* Check if the drive given by @fname is a floppy disc.
77 twoaday 41 Return value: -1 for success. */
78 werner 36 static int
79 twoaday 41 is_floppy_disc (const char *fname)
80 werner 36 {
81 twoaday 41 char drv[32] = {0};
82     int max = sizeof (drv)-1;
83 werner 36 int i=0;
84    
85     if (!strstr (fname, ":\\"))
86     return 0;
87    
88 twoaday 41 while (fname && *fname && *fname != '\\' && i < max)
89 werner 36 drv[i++] = *fname++;
90     drv[i++] = '\\';
91     drv[i++] = '\0';
92     i = GetDriveType (drv);
93     if (i == DRIVE_REMOVABLE)
94     return -1;
95     return 0;
96     }
97    
98    
99     /* Ask the user to overwrite file @fname.
100     Return value: 0 for cancel. */
101 twoaday 41 static int
102 werner 36 overwrite_file (const char *fname)
103     {
104     int id;
105    
106     if (file_exist_check (fname))
107     return -1;
108     id = log_box (_("File Manager"), MB_YESNO,
109     _("\"%s\" already exists.\n"
110     "Replace existing file?"), fname);
111     return id == IDNO ? 0 : -1;
112     }
113    
114    
115     /* Removes 'critical' attributes from the file @fname.
116     If @force is 1, the user is not asked for permission. */
117     static void
118     remove_crit_file_attrs (const char *fname, int force)
119     {
120 twoaday 41 u32 fattr;
121     int id = 0;
122 werner 36
123     if (file_exist_check (fname))
124     return; /* Does not exist */
125    
126 twoaday 41 fattr = GetFileAttributes (fname);
127     if ((fattr & FILE_ATTRIBUTE_READONLY) && force)
128     id = IDYES;
129     else if (fattr & FILE_ATTRIBUTE_READONLY)
130 werner 36 id = log_box (_("File Manager"), MB_YESNO,
131     _("\"%s\" has read-only attribute.\n"
132     "Set attribute to normal?"), fname);
133 twoaday 41 if (id == IDYES) {
134     if (!SetFileAttributes (fname, FILE_ATTRIBUTE_NORMAL))
135     msg_box (NULL, _("Could not reset file attribute to normal."),
136     _("File Manager"), MB_ERR);
137 werner 36 }
138     }
139    
140    
141 twoaday 41 /* Return 1 if the given path @fname is a directory, 0 otherwise. */
142     static int
143 werner 36 is_directory (const char *fname)
144 twoaday 41 {
145 werner 36 return GetFileAttributes (fname) & FILE_ATTRIBUTE_DIRECTORY? 1 : 0;
146     }
147    
148    
149 twoaday 41 /* Return -1 if the given name @name is a valid PGP extension. */
150     static int
151 werner 36 is_openpgp_ext (const char *name)
152     {
153 twoaday 41 if (stristr (name, ".gpg") || stristr (name, ".asc")
154     || stristr (name, ".sig") || stristr (name, ".pgp"))
155 werner 36 return -1;
156     return 0;
157     }
158    
159    
160     static int
161     is_multi_media (const char * name)
162     {
163     const char * val;
164     char * p;
165     int i;
166     int ans=0;
167    
168     i = get_reg_winpt_single (CFG_NOZIP_MMEDIA);
169     if (i == -1)
170     {
171     ans = msg_box (NULL, _("Multi-Media files are already compressed, GPG would compress\n"
172     "them anyway and this costs a lot of time.\n"
173     "It is possible to disable compression for these files.\n"
174     "Do you want to disable it?"),
175     _("File Manager"), MB_YESNO|MB_INFO);
176     set_reg_winpt_single (CFG_NOZIP_MMEDIA, ans == IDYES? 1 : 0);
177     if (ans == IDNO)
178     return 0;
179     }
180     else if (i == 0)
181     return 0;
182    
183     p = strrchr (name, '.');
184     if (!p)
185     return 0;
186 twoaday 41 for (i=0; (val = mm_files[i]); i++) {
187 werner 36 if (!stricmp (p, val))
188     return -1;
189     }
190     return 0;
191     }
192    
193    
194 twoaday 41 /* Return a GPG file extension which depends on the operation
195     mode in @ctx and the sig mode @sigmode. */
196 werner 36 const char*
197     file_get_extension (gpgme_ctx_t ctx, gpgme_sig_mode_t sigmode)
198     {
199     int use_armor = gpgme_get_armor (ctx);
200    
201     if (use_armor || sigmode == GPGME_SIG_MODE_CLEAR)
202     return ".asc";
203     if (!use_armor && sigmode == GPGME_SIG_MODE_DETACH)
204     return ".sig";
205     return ".gpg";
206     }
207    
208    
209 twoaday 41 /* Quote a file to avoid shell problems with spaces in the files. */
210 werner 36 char*
211     fm_quote_file (const char * name)
212     {
213     char * p;
214     size_t len = strlen (name) + 8;
215    
216     if (*name == '"')
217     return m_strdup (name); /* avoid double quotes */
218     p = new char[len + 1];
219     if (!p)
220     BUG (0);
221     _snprintf (p, len, "\"%s\"", name);
222    
223     return p;
224 twoaday 41 }
225 werner 36
226    
227    
228     /* Check the armor type of the file @fname and return
229     a string representation of it. */
230     static const char *
231     fm_check_armor_type (const char *fname, int *r_type)
232     {
233     FILE * fp;
234     char header[768], * p;
235    
236     if (r_type)
237     *r_type = PGP_NONE;
238     fp = fopen (fname, "rb");
239     if (!fp)
240     return "UNKNOWN";
241     p = fgets (header, sizeof (header) - 1, fp);
242     fclose (fp);
243     if (!p)
244     return "UNKNOWN";
245    
246     if (strncmp (header, "-----", 5))
247     goto leave;
248     if (strstr( header, "BEGIN PGP PUBLIC KEY" )) {
249     if (r_type) *r_type = PGP_PUBKEY;
250     return "PUBKEY";
251     }
252     else if (strstr (header, "BEGIN PGP PRIVATE KEY") ||
253     strstr (header, "BEGIN PGP SECRET KEY")) {
254     if (r_type) *r_type = PGP_SECKEY;
255     return "SECKEY";
256     }
257     else if (strstr (header, "BEGIN PGP MESSAGE")) {
258     if (r_type) *r_type = PGP_MESSAGE;
259     return "ENCRYPTED";
260     }
261     else if (strstr( header, "BEGIN PGP SIGNED MESSAGE")) {
262     if (r_type) *r_type = PGP_CLEARSIG;
263     return "SIGNED-CLEAR";
264     }
265     else if (strstr(header, "BEGIN PGP SIGNATURE")) {
266     if (r_type) *r_type = PGP_SIG;
267     return "SIGNED-DETACH";
268     }
269    
270     leave:
271     return "UNKNOWN";
272     }
273    
274    
275     /* Extract file type from @fname. If @r_type is valid,
276     it contains the PGP type on success. */
277 twoaday 105 static const char*
278 werner 36 fm_get_file_type (const char *fname, int *r_type)
279     {
280     gpg_iobuf_t inp;
281     armor_filter_context_t afx;
282 twoaday 41 PACKET *pkt;
283     const char *s = NULL;
284 twoaday 105 size_t count = 0, compr = 0;
285 twoaday 77 int rc = 0;
286 werner 36
287     if (r_type)
288     *r_type = PGP_NONE;
289     if (!fname) {
290 twoaday 41 log_debug ("fm_get_file_type: !fname\r\n");
291 werner 36 return NULL;
292     }
293    
294     if (is_floppy_disc (fname))
295     return fm_check_armor_type (fname, r_type);
296    
297     inp = gpg_iobuf_open (fname);
298     if (!inp) {
299 twoaday 77 const char *err = winpt_strerror (WPTERR_FILE_OPEN);
300     log_box (_("File Manager"), MB_ERR, "\"%s\": %s", fname, err);
301 werner 36 return NULL;
302     }
303     gpg_iobuf_ioctl (inp, 3, 1, NULL); /* disable cache */
304     if (gpg_iobuf_get_filelength (inp) > 32000000 /* 32MB */
305     && !is_openpgp_ext (fname)) {
306     gpg_iobuf_close (inp);
307     return "UNKNOWN";
308     }
309    
310     if (gpg_use_armor_filter(inp)) {
311     memset (&afx, 0, sizeof (afx));
312     gpg_iobuf_push_filter (inp, gpg_armor_filter, &afx);
313     }
314 twoaday 41 pkt = (PACKET *)calloc (1, sizeof *pkt);
315     if (!pkt)
316     BUG (NULL);
317 werner 36 gpg_init_packet (pkt);
318     while (!(rc = gpg_parse_packet (inp, pkt))) {
319     switch (pkt->pkttype) {
320     case PKT_PUBKEY_ENC:
321     s = "ENCRYPTED";rc = -2;
322     if (r_type) *r_type = PGP_MESSAGE;
323     break;
324     case PKT_SYMKEY_ENC:
325     case PKT_ENCRYPTED:
326     s = "SYMKEYENC";rc = -2;
327     if (r_type) *r_type = PGP_MESSAGE;
328     break;
329     case PKT_SIGNATURE:
330     case PKT_ONEPASS_SIG:
331     s = "SIGNED"; rc = -2;
332     if (r_type) *r_type = PGP_SIG;
333     break;
334     case PKT_PUBLIC_KEY:
335     s = "PUBKEY"; rc = -2;
336     if (r_type) *r_type = PGP_PUBKEY;
337     break;
338     case PKT_SECRET_KEY:
339     s = "SECKEY"; rc = -2;
340     if (r_type) *r_type = PGP_SECKEY;
341     break;
342 twoaday 88
343 twoaday 105 case PKT_COMPRESSED:
344     /* If we only find 1 packet and it is compressed,
345     we assume a compress one-pass signature. */
346     if (count != 0)
347     break;
348     s = "SIGNED"; rc = -2;
349     compr = 1;
350     break;
351    
352 twoaday 69 default:
353     break;
354 werner 36 }
355 twoaday 105 count++;
356 werner 36 gpg_free_packet (pkt);
357     gpg_init_packet (pkt);
358     if (rc == -2)
359     break; /* found */
360     }
361     safe_free (pkt);
362     gpg_iobuf_close (inp);
363     if (!s)
364     s = fm_check_armor_type (fname, r_type);
365     if (!s)
366     s = "UNKNOWN";
367 twoaday 105 if (!strcmp (s, "SIGNED") && !compr
368 werner 36 && strcmp (fm_check_armor_type (fname, r_type), "SIGNED-CLEAR ")) {
369     if (r_type) *r_type = PGP_SIG;
370     s = "SIGNED-DETACH";
371     }
372     return s;
373     }
374    
375    
376 twoaday 105 /* Build the File Manager list view control. */
377 werner 36 int
378     fm_build (listview_ctrl_t *lv, HWND ctrl)
379     {
380     int i, rc = 0;
381     listview_ctrl_t c;
382 twoaday 105 struct listview_column_s col[] = {
383 werner 36 {0, 80, (char *)_("Status") },
384     {1, 256, (char *)_("Name") },
385     {2, 128, (char *)_("Operation") },
386     {0, 0, NULL }
387     };
388    
389 twoaday 105 rc = listview_new (&c);
390     if (rc)
391     BUG (NULL);
392 werner 36 c->ctrl = ctrl;
393 twoaday 105 for (i = 0; col[i].width; i++)
394     listview_add_column (c, &col[i]);
395     listview_set_ext_style (c);
396     if (lv)
397 werner 36 *lv = c;
398     return 0;
399 twoaday 105 }
400 werner 36
401    
402 twoaday 105 /* Release the File Manager listview control. */
403 werner 36 void
404 twoaday 105 fm_delete (listview_ctrl_t lv)
405 werner 36 {
406 twoaday 105 if (lv) {
407     listview_release(lv);
408 werner 36 }
409 twoaday 105 }
410 werner 36
411    
412     int
413     fm_state_new (fm_state_t * ctx)
414     {
415     gpgme_error_t rc;
416     fm_state_s * c;
417    
418     c = new fm_state_s;
419     if (!c)
420     BUG (0);
421 twoaday 41 memset (c, 0, sizeof *c);
422 werner 36 rc = gpgme_new (&c->ctx);
423     if (rc)
424     BUG (0);
425     /* XXX rc = gpgme_recipients_new (&c->recp);*/
426     /* XXX gpgme_set_comment (c->ctx, "Generated by WinPT "PGM_VERSION); */
427     *ctx = c;
428     return 0;
429     } /* fm_state_new */
430    
431    
432     /* Release the FM state handle @c. */
433     void
434     fm_state_release (fm_state_t c)
435     {
436     if (!c)
437     return;
438     if (c->ctx) {
439     gpgme_release (c->ctx);
440     c->ctx = NULL;
441     }
442 twoaday 105 safe_free (c->recp);
443 werner 36 free_if_alloc (c->opaque);
444     free_if_alloc (c->output);
445     delete c; c = NULL;
446     }
447    
448     static int
449     fm_check_for_entry( listview_ctrl_t lv, const char *file )
450     {
451     char name[512];
452     int i;
453    
454     memset (name, 0, sizeof (name));
455 twoaday 41 for (i = 0; i < listview_count_items( lv, 0 ); i++) {
456 werner 36 listview_get_item_text( lv, i, 1, name, sizeof (name) - 1 );
457     if( !strcmp( name, file ) )
458     return 1; /* found */
459     }
460    
461     return 0;
462     } /* fm_check_for_entry */
463    
464    
465     static int
466     fm_set_ftype (listview_ctrl_t lv, const char *name)
467     {
468     const char *type;
469     int rc;
470    
471     rc = fm_check_for_entry (lv, name);
472     if (rc)
473     return 0;
474     type = fm_get_file_type (name, NULL);
475     if (!type || !strcmp (type, "UNKNOWN"))
476     type = gnupg_check_file_ext (name, NULL);
477     rc = listview_add_item (lv, " ");
478     if (rc)
479     return -1;
480     listview_add_sub_item (lv, 0, 0, type);
481     listview_add_sub_item (lv, 0, 1, name);
482     return 0;
483     }
484    
485    
486     static int
487     fm_add_dir_files (listview_ctrl_t lv, char *path)
488     {
489     struct _finddata_t fd;
490     char * p;
491     long hd;
492    
493     strcat (path, "\\*");
494     hd = _findfirst (path, &fd);
495     do {
496     p = new char [(strlen (path) + strlen (fd.name))+1];
497     if (!p)
498     BUG (0);
499     memcpy (p, path, strlen (path)-1);
500     p[strlen (path)-1] = 0;
501     strcat (p, fd.name);
502     if (!is_directory (p))
503     fm_set_ftype (lv, p);
504     free_if_alloc (p);
505     } while (_findnext (hd, &fd) == 0);
506     _findclose (hd);
507     return 0;
508     }
509    
510    
511     /* Add the drag & drop files from @dd_files to the
512     list view control @lv. */
513     int
514     fm_add_dropped_files (listview_ctrl_t lv, HDROP dd_files)
515     {
516     char name[384+4];
517 twoaday 77 int nfiles;
518     int rc = 0;
519     int i;
520 werner 36
521     memset (name, 0, sizeof (name));
522     nfiles = DragQueryFile (dd_files, 0xFFFFFFFF, NULL, 0);
523     for (i = 0; i < nfiles; i++) {
524     DragQueryFile (dd_files, i, name, sizeof (name) -1);
525     if (is_directory (name))
526     rc = fm_add_dir_files (lv, name);
527     else
528     rc = fm_set_ftype (lv, name);
529     if (rc == -1)
530 twoaday 41 break; /* XXX: fixme? */
531 werner 36 }
532 twoaday 167 DragFinish (dd_files);
533 werner 36 return rc;
534     }
535    
536    
537 twoaday 41 /* Add a single file @name to the list view and before
538     figure out the type of it.
539     Return value: 0 on success. */
540     static int
541     add_single_file (listview_ctrl_t lv, const char *name)
542     {
543     const char *type;
544     int rc = 0;
545    
546     type = fm_get_file_type (name, NULL);
547     if (!type)
548     return WPTERR_FILE_OPEN;
549     if (!strcmp (type, "UNKNOWN"))
550     type = gnupg_check_file_ext (name, NULL);
551     rc = listview_add_item (lv, "");
552     if (!rc) {
553     listview_add_sub_item (lv, 0, 0, type);
554     listview_add_sub_item (lv, 0, 1, name);
555     }
556     return rc;
557     }
558    
559    
560     /* Use the common Open-File-Dialog to allow the user to
561     add one ore more selected files to the listview @lv. */
562 werner 36 int
563     fm_add_opened_files (listview_ctrl_t lv, HWND dlg)
564     {
565     OPENFILENAME open;
566 twoaday 41 char file[512], name[MAX_PATH+1];
567     char *path = NULL;
568     const char *s;
569     int i, len=0, n=0;
570     int rc=0;
571    
572 werner 36 memset (&open, 0, sizeof (open));
573     open.lStructSize = sizeof (OPENFILENAME);
574     open.hInstance = glob_hinst;
575     open.lpstrTitle = _("File Open");
576 twoaday 167 open.lpstrFilter = "All Files (*.*)\0*.*\0\0";
577 werner 36 open.hwndOwner = dlg;
578     open.lpstrFile = file;
579     open.nMaxFile = sizeof (file) - 1;
580 twoaday 41 open.Flags = OFN_ALLOWMULTISELECT|OFN_EXPLORER ;
581 werner 36
582 twoaday 41 memset (file, 0, sizeof file);
583     if (!GetOpenFileName (&open))
584     return 0;
585    
586     s = file;
587     len = sizeof (file)-1;
588     for (;;) {
589     if (len < 2 || (*s == '\0' && *(s+1) == '\0'))
590     break;
591     memset (name, 0, sizeof (name));
592     for (i=0; len > 0; len--, i++) {
593     if (*s == '\0') {
594     name[i] = *s++;
595     break;
596     }
597     name[i] = *s++;
598     }
599     if (n == 0)
600     path = strdup (name);
601     else {
602     char *p = make_filename (path, name, NULL);
603     rc = add_single_file (lv, p);
604     free (p);
605     }
606     n++;
607 werner 36 }
608 twoaday 41 if (n == 1) /* single file selected. */
609     rc = add_single_file (lv, path);
610     if (path)
611     free (path);
612 werner 36 return rc;
613     }
614    
615    
616     int
617     fm_assume_onepass_sig (const char * fname)
618     {
619     gpgme_data_t dat;
620     armor_filter_context_t afx;
621     gpg_iobuf_t fp;
622     PACKET * pkt = (PACKET *)calloc (1, sizeof *pkt);
623     int check = 0;
624    
625     if (!fname) {
626     gpg_data_new_from_clipboard (&dat, 0);
627     gpg_data_release_and_set_file (dat, "gpgme.tmp");
628    
629     fp = gpg_iobuf_open ("gpgme.tmp");
630     if (!fp)
631     return 0;
632     gpg_iobuf_ioctl (fp, 3, 1, NULL);
633     if (gpg_use_armor_filter(fp)) {
634     memset (&afx, 0, sizeof (afx));
635     gpg_iobuf_push_filter (fp, gpg_armor_filter, &afx);
636     }
637     gpg_init_packet (pkt);
638     if (!gpg_parse_packet (fp, pkt)
639     && pkt->pkttype == PKT_COMPRESSED)
640     check = 1;
641     gpg_free_packet (pkt);
642     safe_free (pkt);
643     gpg_iobuf_close (fp);
644 twoaday 77 remove ("gpgme.tmp");
645 werner 36 }
646     /* XXX: implement it for real files */
647     return check;
648     }
649    
650    
651     int
652     fm_get_current_pos (listview_ctrl_t lv)
653     {
654     int i = 0, items;
655    
656     items = listview_count_items (lv, 0);
657     if (!items)
658     return -1;
659     else if (items == 1)
660     {
661     listview_select_one (lv, 0);
662     return 0;
663     }
664     else if (items > 1)
665     {
666     i = listview_get_curr_pos (lv);
667     if (i == -1)
668     {
669     msg_box (lv->ctrl, _("Please select a file."), _("File Manager"), MB_ERR);
670     return -1;
671     }
672     return i;
673     }
674    
675     return -1;
676     } /* fm_get_current_pos */
677    
678    
679     static int
680     fm_check_detached_sig( listview_ctrl_t lv, int pos )
681     {
682     char type[128];
683    
684     listview_get_item_text( lv, pos, 0, type, 127 );
685     return !strcmp( type, "SIGNED-DETACH" )? 1 : 0;
686     } /* fm_check_detached_sig */
687    
688    
689     int
690     fm_check_file_type (listview_ctrl_t lv, int pos, int fm_cmd)
691     {
692     char status[128];
693     int rc = 0;
694    
695     listview_get_item_text (lv, pos, 0, status, sizeof (status) - 1);
696    
697     switch (fm_cmd) {
698     case FM_ENCRYPT:
699     case FM_ENCRYPT_DIR:
700     case FM_SIGNENCRYPT:
701     if (strcmp (status, "ENCRYPTED")
702     && strcmp (status, "SYMKEYENC"))
703     rc = 1;
704     break;
705    
706     case FM_DECRYPT:
707     if (!strcmp (status, "DATA")
708     || !strcmp (status, "ENCRYPTED")
709     || !strcmp (status, "SYMKEYENC")
710     || !strcmp (status, "ARMORED"))
711     rc = 1;
712     break;
713    
714     case FM_SIGN:
715     if( strncmp( status, "SIGNED", 6 ) )
716     rc = 1;
717     break;
718    
719     case FM_VERIFY:
720     if( !strncmp( status, "SIGNED", 6 )
721     || !strcmp( status, "COMPRESSED" ) )
722     rc = 1;
723     break;
724    
725     case FM_SYMENC:
726     if( strcmp( status, "SYMKEYENC" ) )
727     rc = 1;
728     break;
729    
730     case FM_IMPORT:
731     if( !strcmp( status, "PUBKEY" )
732     || !strcmp( status, "SECKEY" ) )
733     rc = 1;
734     break;
735    
736     case FM_WIPE:
737     case FM_LIST:
738     rc = 1;
739     break;
740     }
741    
742     return rc;
743     } /* fm_check_file_type */
744    
745    
746 twoaday 88 /* Set the file status of the given command @fm_cmd.
747     @success is 0 on success. */
748 werner 36 static void
749 twoaday 88 fm_set_status (listview_ctrl_t lv, int pos, int fm_cmd,
750     gpgme_sig_mode_t sigmode, int success, const char *output)
751 werner 36 {
752     char status[128], operat[128];
753     int update = 1;
754     const char *s;
755    
756 twoaday 88 if (fm_cmd == FM_LIST)
757 werner 36 return;
758     success ? s = "SUCCESS" : s = "FAILED";
759 twoaday 88 strcpy (operat, s);
760 werner 36
761     switch (fm_cmd) {
762     case FM_ENCRYPT:
763     case FM_ENCRYPT_DIR:
764 twoaday 88 case FM_SIGNENCRYPT: strcpy (status, "ENCRYPTED"); break;
765     case FM_DECRYPT: strcpy (status, "UNKNOWN"); break;
766     case FM_SIGN:
767     if (sigmode == GPGME_SIG_MODE_DETACH)
768     strcpy (status, "SIGNED-DETACH");
769 twoaday 119 else if (sigmode == GPGME_SIG_MODE_CLEAR)
770     strcpy (status, "SIGNED-CLEAR");
771 twoaday 88 else
772     strcpy (status, "SIGNED");
773     break;
774 werner 36 case FM_VERIFY: update = 0; break;
775 twoaday 88 case FM_SYMENC: strcpy (status, "SYMKEYENC"); break;
776 werner 36 case FM_IMPORT: update = 0; break;
777 twoaday 88 case FM_WIPE: strcpy (status, "WIPED"); break;
778     default: strcpy (status, "UNKNOWN"); break;
779 werner 36 }
780    
781 twoaday 119 if (success && update) {
782     listview_add_sub_item (lv, pos, 0, status);
783     listview_add_sub_item (lv, pos, 1, output);
784 werner 36 }
785     listview_add_sub_item( lv, pos, 2, operat );
786 twoaday 88 }
787 werner 36
788    
789     int
790     fm_clearsign_8bit (listview_ctrl_t lv, fm_state_s *ctx)
791     {
792     FILE *f;
793     byte buf[32];
794     char name[256];
795     int i, n, cnt=0;
796    
797     if (ctx->sigmode != GPGME_SIG_MODE_CLEAR)
798     return 0;
799     listview_get_item_text (lv, -1, 1, name, sizeof (name)-1);
800     if (stristr (name, ".TXT"))
801     return 0;
802     f = fopen (name, "rb");
803     if (!f)
804     return -1; /* should never happen */
805     n = fread (buf, 1, 32, f);
806     for (i = 0; i < n; i++) {
807     if (buf[i] == 0x00 || buf[i] > 170)
808     cnt++;
809     }
810     fclose (f);
811     if (!cnt)
812     return 0;
813     n = -1;
814     i = log_box (_("File Manager"), MB_WARN|MB_YESNO,
815     _("\"%s\" does not seems to be a text file.\n"
816     "Do you really want to clearsign it?"), name);
817     if (i == IDYES)
818     n = 0;
819     return n;
820     }
821    
822     int
823     fm_parse_files (listview_ctrl_t lv, HWND dlg, int cmd)
824     {
825     struct secdel_confirm_s confirm = {0};
826     struct progress_filter_s pfx, pfx2;
827     fm_state_s * ctx;
828     int fm_cmd, sig_detached = 0;
829     int rc = 0, i, n, ndel = 0;
830     char fname[512], status[128];
831    
832     switch (cmd) {
833     case ID_FILEMISC_ENCRYPT: fm_cmd = FM_ENCRYPT; break;
834 twoaday 77 case ID_FILEMISC_ENCRYPT_ZIP:fm_cmd = FM_ENCRYPT_ZIP; break;
835 werner 36 case ID_FILEMISC_DECRYPT: fm_cmd = FM_DECRYPT; break;
836     case ID_FILEMISC_SYMENC: fm_cmd = FM_SYMENC; break;
837     case ID_FILEMISC_SIGN: fm_cmd = FM_SIGN; break;
838     case ID_FILEMISC_VERIFY: fm_cmd = FM_VERIFY; break;
839     case ID_FILEMISC_IMPORT: fm_cmd = FM_IMPORT; break;
840     case ID_FILEMISC_WIPE: fm_cmd = FM_WIPE; break;
841     case ID_FILEMISC_LIST: fm_cmd = FM_LIST; break;
842     case ID_FILEMISC_SIGNENC: fm_cmd = FM_SIGNENCRYPT; break;
843     default: return 1; /* unknown command */
844     }
845    
846     if (fm_get_current_pos (lv) == -1)
847 twoaday 77 return WPTERR_GENERAL;
848 werner 36 rc = fm_state_new (&ctx);
849     if (rc)
850     BUG (0);
851     ctx->dlg = dlg;
852    
853     // XXX: for file operations the progress dialog will be
854     // reloaded somewhere and thus a 'dummy' dialog remains
855    
856     /* we use it here to make sure that pfx_cleanup will not use
857     any weird values. */
858     memset (&pfx, 0, sizeof (pfx));
859     ctx->prog_cb = NULL;
860     if (cmd != FM_VERIFY && cmd != FM_SIGN /*&& reg_prefs.fm.progress > 0*/) {
861     pfx.hwnd = dlg;
862     /*gpgme_set_progress_cb (ctx->ctx, progress_callback, &pfx);*/
863     /*ctx->prog_cb = &pfx;*/
864     }
865    
866     /* Commands we need before we can perform the main command */
867     switch (fm_cmd) {
868 twoaday 77 case FM_ENCRYPT_ZIP:
869 werner 36 case FM_ENCRYPT:
870     case FM_SIGNENCRYPT:
871     if (fm_cmd == FM_SIGNENCRYPT)
872     ctx->req_signer = 1;
873 twoaday 105 DialogBoxParam (glob_hinst, (LPCTSTR)IDD_WINPT_FILE_ENCRYPT,
874     ctx->dlg, file_encrypt_dlg_proc, (LPARAM)ctx);
875 werner 36 if (ctx->cancel == 1) {
876 twoaday 105 rc = WPTERR_GENERAL;
877 werner 36 goto leave;
878     }
879     break;
880    
881     case FM_SIGN:
882     DialogBoxParam (glob_hinst, (LPCSTR)IDD_WINPT_FILE_SIGN, dlg,
883     file_sign_dlg_proc, (LPARAM) ctx);
884     if (ctx->cancel == 1 || fm_clearsign_8bit (lv, ctx)) {
885     rc = WPTERR_GENERAL;
886     goto leave;
887     }
888     break;
889    
890     case FM_WIPE:
891     memset (&pfx2, 0, sizeof (pfx2));
892     secure_unlink_set_cb (progress_callback, &pfx2);
893     break;
894     }
895    
896     for( i = 0, n = 0; i < listview_count_items( lv, 0 ); i++ ) {
897     if( !listview_get_item_state( lv, i ) )
898     continue;
899     listview_get_item_text( lv, i, 0, status, sizeof (status) -1 );
900     if (!strcmp( status, "ENCRYPTED" ) && fm_cmd == FM_DECRYPT)
901     n++;
902     if (!strcmp( status, "UNKNOWN" ) && fm_cmd == FM_SIGN)
903     n++;
904     if (fm_cmd == FM_WIPE)
905     ndel++;
906     }
907    
908     if (n > 1 && fm_cmd != FM_SYMENC)
909     ctx->cache_cb = 1;
910    
911     if (fm_cmd == FM_WIPE && ndel > 0) {
912     memset (&confirm, 0, sizeof confirm);
913     confirm.lv_files = lv;
914     DialogBoxParam (glob_hinst, (LPCTSTR)IDD_WINPT_FILES_SECDEL, ctx->dlg,
915 twoaday 77 file_secdel_confirm_dlg_proc, (LPARAM)&confirm);
916 werner 36 if (!confirm.yes)
917     goto leave;
918     }
919 twoaday 119
920 twoaday 77 if (fm_cmd == FM_ENCRYPT_ZIP)
921     fm_encrypt_into_zip (ctx, lv);
922    
923     for (i = 0; i < listview_count_items (lv, 0); i++) {
924 werner 36 if( !listview_get_item_state( lv, i ) )
925     continue;
926 twoaday 119 listview_get_item_text(lv, i, 1, fname, sizeof (fname) - 1);
927     if( file_exist_check (fname) && !is_directory (fname)) {
928     log_box (_("File Manager"), MB_ERR,
929     _("\"%s\" does not exist"), fname);
930 werner 36 continue;
931     }
932 twoaday 119 if (is_directory (fname))
933 werner 36 fm_cmd = FM_ENCRYPT_DIR;
934 twoaday 119 if (!fm_check_file_type (lv, i, fm_cmd))
935 werner 36 continue;
936 twoaday 119 sig_detached = fm_check_detached_sig (lv, i);
937 twoaday 77 switch (fm_cmd) {
938 werner 36 case FM_LIST: rc = fm_list( fname, dlg ); break;
939     case FM_WIPE: rc = fm_wipe( fname ); break;
940     case FM_ENCRYPT: rc = fm_encrypt( ctx, fname, 0 ); break;
941     case FM_ENCRYPT_DIR: rc = fm_encrypt_directory( ctx, fname ); break;
942     case FM_SIGNENCRYPT: rc = fm_encrypt( ctx, fname, 1 ); break;
943     case FM_DECRYPT: rc = fm_decrypt( ctx, fname ); break;
944     case FM_SIGN: rc = fm_sign( ctx, fname ); break;
945     case FM_SYMENC: rc = fm_sym_encrypt( ctx, fname );break;
946     case FM_VERIFY: rc = fm_verify (ctx, sig_detached, fname);break;
947     case FM_IMPORT:
948     free_if_alloc (ctx->opaque);
949     ctx->opaque = m_strdup (fname);
950     if (!ctx->opaque)
951     BUG (0);
952     DialogBoxParam( glob_hinst, (LPCSTR)IDD_WINPT_IMPORT, dlg,
953     file_import_dlg_proc, (LPARAM)ctx );
954     if (ctx->cancel == 1)
955     continue;
956     rc = fm_import (ctx, fname);
957     break;
958     }
959 twoaday 88 fm_set_status (lv, i, fm_cmd, ctx->sigmode, !rc, ctx->output);
960 werner 36 free_if_alloc (ctx->output);
961     progress_cleanup (&pfx);
962     }
963 twoaday 77
964 werner 36 if (fm_cmd == FM_WIPE) {
965     secure_unlink_set_cb (NULL, NULL);
966     progress_cleanup (&pfx2);
967     }
968     if (ctx->cache_cb) {
969     release_gpg_passphrase_cb (&ctx->pass_cb);
970     ctx->cache_cb = 0; /* make sure it's only used for this session! */
971     }
972    
973     /* remove wipe files from the list */
974     n = listview_count_items (lv, 0);
975     while (n--) {
976     listview_get_item_text (lv, n, 0, status, sizeof (status) - 1);
977 twoaday 77 if (!strcmp (status, "WIPED"))
978 werner 36 listview_del_item (lv, n);
979     }
980    
981     leave:
982     if (!rc)
983     fm_state_release (ctx);
984     progress_cleanup (&pfx);
985     return rc;
986     } /* fm_parse_files */
987    
988    
989     /* Wipe the given file @name with the delete mode
990     from the configuration.
991     Return value: 0 on success. */
992     int
993     fm_wipe (const char *name)
994     {
995     int rc;
996    
997     SetCursor (LoadCursor (NULL, IDC_WAIT));
998     remove_crit_file_attrs (name, 1);
999     rc = secure_unlink (name, reg_prefs.wipe_mode);
1000     SetCursor (LoadCursor (NULL, IDC_ARROW));
1001     return rc;
1002     }
1003    
1004    
1005     /* Dump out the given PGP packets from file @name in a dialog. */
1006     int
1007     fm_list (const char *name, HWND dlg)
1008     {
1009     dialog_box_param( glob_hinst, (LPCTSTR)IDD_WINPT_FILE_STAT, dlg,
1010     file_stat_dlg_proc, (LPARAM)name, _("File Status"),
1011     IDS_WINPT_FILE_STAT );
1012     return 0;
1013     }
1014    
1015    
1016     static int
1017     ask_filename (fm_state_t c, const char *msg, char **dst)
1018     {
1019     const char * s;
1020    
1021 twoaday 77 s = get_filesave_dlg (c->dlg, msg, NULL, NULL);
1022 werner 36 if (!s)
1023     return WPTERR_GENERAL;
1024    
1025     if (dst != NULL)
1026     free_if_alloc (*dst);
1027     free_if_alloc (c->output);
1028     c->output = m_strdup (s);
1029     if (!c->output)
1030     BUG (0);
1031     if (dst)
1032     *dst = fm_quote_file (s);
1033     return 0;
1034     }
1035    
1036    
1037     int
1038     fm_encrypt (fm_state_t c, const char *name, int sign)
1039     {
1040     gpgme_error_t err;
1041     gpgme_key_t key = NULL;
1042     gpgme_ctx_t ctx = c->ctx;
1043     file_data_t in=NULL, out=NULL;
1044     char *keyid = NULL, ext[5];
1045 twoaday 69 /*int no_compr = 0;*/
1046 werner 36 int rc = 0;
1047    
1048     c->output = new char[strlen (name) + 5 + 1];
1049     if (!c->output)
1050     BUG (0);
1051     strcpy (ext, file_get_extension (ctx, c->sigmode));
1052     strcpy (c->output, name);
1053 twoaday 105 strcat (c->output, ext);
1054 werner 36
1055     if (!overwrite_file (c->output)) {
1056     rc = ask_filename (c, _("Enter filename for encrypted file"), NULL);
1057     if (rc)
1058     goto leave;
1059     }
1060    
1061 twoaday 105 err = gpg_file_data_new (name, F_DATA_READ, &in);
1062 werner 36 if (err)
1063     goto leave;
1064 twoaday 105 remove_crit_file_attrs (c->output, 0);
1065     err = gpg_file_data_new (c->output, F_DATA_WRITE, &out);
1066 werner 36 if (err)
1067     goto leave;
1068    
1069     /*
1070     if (c->prog_cb) {
1071     c->prog_cb->what = name;
1072     gpg_file_data_set_cb (in, c->prog_cb);
1073     }
1074     */
1075    
1076 twoaday 105 /* XXX: disable compression for multi-media files.
1077 werner 36 no_compr = is_multi_media (name);
1078     gpgme_control (ctx, GPGME_CTRL_NO_COMPR, no_compr);
1079     */
1080    
1081     if (sign) {
1082     if (gpgme_signers_enum (ctx, 0) == NULL) {
1083     keyid = get_gnupg_default_key ();
1084     if (!keyid) {
1085     msg_box (c->dlg, _("Could not get default secret key."),
1086     _("Signing"), MB_ERR);
1087     rc = WPTERR_GENERAL;
1088     goto leave;
1089     }
1090     if (get_seckey (keyid, &key))
1091     BUG (0);
1092     gpgme_signers_add (ctx, key);
1093     }
1094     else {
1095 twoaday 77 gpgme_key_t sigkey = gpgme_signers_enum (ctx, 0);
1096     if (sigkey && sigkey->subkeys) {
1097     keyid = m_strdup (sigkey->subkeys->keyid);
1098 werner 36 if (!keyid)
1099     BUG (NULL);
1100     }
1101     }
1102     if (!c->init_cb || !c->cache_cb) {
1103     set_gpg_passphrase_cb (&c->pass_cb, c->ctx, GPG_CMD_SIGN,
1104     c->dlg, _("Signing"));
1105     c->init_cb = 1;
1106     }
1107     op_begin ();
1108     err = gpgme_op_encrypt_sign (ctx, c->recp, GPGME_ENCRYPT_ALWAYS_TRUST,
1109     in->dat, out->dat);
1110     op_end ();
1111     if (!c->cache_cb)
1112     release_gpg_passphrase_cb (&c->pass_cb);
1113     if (c->pass_cb.cancel) {
1114     rc = WPTERR_GENERAL;
1115     goto leave;
1116     }
1117     if (err) {
1118     msg_box (c->dlg, gpgme_strerror (err), _("Sign"), MB_ERR);
1119     if (gpgme_err_code (err) == GPG_ERR_BAD_PASSPHRASE)
1120     agent_del_cache (keyid);
1121     rc = WPTERR_GENERAL;
1122     goto leave;
1123     }
1124     }
1125     else {
1126     op_begin ();
1127     err = gpgme_op_encrypt (ctx, c->recp, GPGME_ENCRYPT_ALWAYS_TRUST,
1128     in->dat, out->dat);
1129     op_end ();
1130     if (err) {
1131     msg_box (c->dlg, gpgme_strerror (err), _("Encrypt"), MB_ERR);
1132     rc = WPTERR_GENERAL;
1133     goto leave;
1134     }
1135     }
1136    
1137     leave:
1138     if (in)
1139     gpg_file_data_release (in);
1140     if (out)
1141     gpg_file_data_release (out);
1142 twoaday 77 free_if_alloc (keyid);
1143 twoaday 76 if (!rc && c->wipe)
1144     secure_unlink (name, WIPE_MODE_SIMPLE);
1145 werner 36 return rc;
1146     }
1147    
1148    
1149     int
1150     fm_sym_encrypt (fm_state_t c, const char * name)
1151     {
1152     gpgme_ctx_t ctx = c->ctx;
1153     gpgme_error_t err;
1154     file_data_t in=NULL, out=NULL;
1155     int rc = 0, cancel = 0;
1156     char ext[5], * pass;
1157    
1158     pass = request_passphrase2 (_("Symmetric"), 0, &cancel);
1159     if (cancel)
1160     return 0;
1161    
1162     /* XXX gpgme_control (ctx, GPGME_CTRL_CIPHER, -1);*/
1163     c->output = new char[strlen (name) + 5 + 1];
1164     if (!c->output)
1165     BUG (0);
1166     strcpy (ext, file_get_extension (ctx, c->sigmode));
1167     strcpy (c->output, name);
1168     strcat (c->output, ext);
1169    
1170     if (overwrite_file (c->output) == 0) {
1171     rc = WPTERR_GENERAL;
1172     goto leave;
1173     }
1174    
1175     gpgme_set_passphrase_cb (ctx, sym_passphrase_cb, pass);
1176    
1177     err = gpg_file_data_new (name, 1, &in);
1178     if (err)
1179     goto leave;
1180     err = gpg_file_data_new (c->output, 0, &out);
1181     if (err)
1182     goto leave;
1183    
1184     op_begin ();
1185     err = gpgme_op_encrypt (ctx, NULL, GPGME_ENCRYPT_ALWAYS_TRUST,
1186     in->dat, out->dat);
1187     op_end ();
1188     if (err) {
1189     msg_box (c->dlg, gpgme_strerror (err), _("Symmetric"), MB_ERR);
1190     rc = WPTERR_GENERAL;
1191     goto leave;
1192     }
1193     if (file_exist_check (c->output)) {
1194     msg_box (c->dlg, _("Encryption failed."), _("Symmetric"), MB_ERR);
1195     rc = WPTERR_GENERAL;
1196     }
1197    
1198     leave:
1199     if (in)
1200     gpg_file_data_release (in);
1201     if (out)
1202     gpg_file_data_release (out);
1203     sfree_if_alloc (pass);
1204     return rc;
1205 twoaday 105 }
1206 werner 36
1207    
1208     /* Show the human readable verify result from @sigres. */
1209     static void
1210     show_verify_result (gpgme_verify_result_t sigres)
1211     {
1212     gpgme_key_t key=NULL;
1213     gpgme_signature_t sig=sigres->signatures;
1214     const char *s, *keyid;
1215     int sigok = 0;
1216     int type;
1217     char buf[384];
1218    
1219     sig = sigres->signatures;
1220     sigok = sig->summary & GPGME_SIGSUM_GREEN;
1221     s = sigok? _("Good signature") : _("BAD signature");
1222     type = sigok? MB_OK: MB_ICONWARNING|MB_OK;
1223 twoaday 105 keyid = sig->fpr;
1224 werner 36 if (!keyid)
1225     return;
1226    
1227     keyid = strlen (sig->fpr) == 40? sig->fpr+32 : sig->fpr + 24;
1228     get_pubkey (sig->fpr, &key);
1229 twoaday 105 _snprintf (buf, sizeof (buf)-1, _("Signature made %s using %s key ID %s\n"
1230     "%s from \"%s\""),
1231     strtimestamp (sig->timestamp),
1232     get_key_pubalgo (sig->pubkey_algo),
1233 werner 36 keyid, s, key? key->uids->uid : _("user ID not found"));
1234     msg_box (NULL, buf, _("Decrypt Verify"), type);
1235     }
1236    
1237    
1238     /* Check the recipients if we have at least one secret key. */
1239     bool
1240     secret_key_available (gpgme_recipient_t rset)
1241     {
1242     gpgme_recipient_t r;
1243     gpgme_key_t key;
1244    
1245 twoaday 105 for (r=rset; r; r = r->next) {
1246 werner 36 if (gpgme_err_code (r->status) == GPG_ERR_NO_SECKEY)
1247     continue;
1248     else {
1249     /* extra check to make sure the key is available right now. */
1250     if (!get_seckey (r->keyid, &key))
1251     return true;
1252     }
1253     }
1254     return false;
1255     }
1256    
1257    
1258     /* Decrypt the file @name. */
1259     int
1260     fm_decrypt (fm_state_t c, const char *name)
1261     {
1262     gpgme_error_t err;
1263     gpgme_ctx_t ctx = c->ctx;
1264     gpgme_decrypt_result_t res;
1265     gpgme_verify_result_t sigres;
1266     file_data_t in =NULL, out=NULL;
1267     int rc = 0;
1268    
1269     if (!c->init_cb || !c->cache_cb) {
1270 twoaday 119 set_gpg_passphrase_cb (&c->pass_cb, c->ctx, GPG_CMD_DECRYPT,
1271 werner 36 c->dlg, _("Decryption"));
1272 twoaday 119 c->init_cb = 1;
1273     }
1274 werner 36
1275     c->output = m_strdup (name);
1276     if (!c->output)
1277     BUG (0);
1278     if (is_openpgp_ext (c->output))
1279     c->output[strlen (c->output)-4] = '\0';
1280     else {
1281 twoaday 41 const char *s;
1282     s = get_filesave_dlg (c->dlg, _("Choose Filename for Output"),
1283     NULL, NULL);
1284 werner 36 if (s) {
1285     free_if_alloc (c->output);
1286     c->output = m_strdup (s);
1287     if (!c->output)
1288     BUG (NULL);
1289     }
1290     }
1291    
1292     if (overwrite_file (c->output) == 0) {
1293     rc = ask_filename (c, _("Please enter filename for plaintext file"), NULL);
1294     if (rc)
1295     goto leave;
1296 twoaday 105 }
1297 werner 36
1298 twoaday 105 err = gpg_file_data_new (name, F_DATA_READ, &in);
1299 werner 36 if (err)
1300     goto leave;
1301 twoaday 105 remove_crit_file_attrs (c->output, 0);
1302     err = gpg_file_data_new (c->output, F_DATA_WRITE, &out);
1303 werner 36 if (err)
1304     goto leave;
1305    
1306     op_begin ();
1307     err = gpgme_op_decrypt_verify (ctx, in->dat, out->dat);
1308     op_end ();
1309     if (!c->cache_cb)
1310     release_gpg_passphrase_cb (&c->pass_cb);
1311     if (c->pass_cb.cancel) {
1312     rc = WPTERR_GENERAL;
1313     goto leave;
1314     }
1315    
1316     res = gpgme_op_decrypt_result (ctx);
1317     if (res && res->recipients && !secret_key_available (res->recipients)) {
1318     const char *keyid = res->recipients->keyid;
1319     char *p = get_key_userid (keyid+8);
1320     gpgme_pubkey_algo_t pkalgo = res->recipients->pubkey_algo;
1321    
1322     log_box( _("Decryption"), MB_ERR,
1323     _("Encrypted with %s key, ID %s.%s\n"
1324     "Decryption failed: secret key not available."),
1325     get_key_pubalgo (pkalgo), keyid+8, p);
1326     rc = WPTERR_GENERAL;
1327     free_if_alloc (p);
1328     goto leave;
1329     }
1330     else if (err) {
1331     msg_box (c->dlg, gpgme_strerror (err), _("Decrypt"), MB_ERR);
1332     rc = WPTERR_GENERAL;
1333     goto leave;
1334     }
1335     if (file_exist_check (c->output)) {
1336 twoaday 105 log_box ("Decrypt", MB_ERR,
1337     _("Decryption failed.\n%s: does not exist."), c->output);
1338 werner 36 rc = WPTERR_GENERAL;
1339     }
1340    
1341     sigres = gpgme_op_verify_result (ctx);
1342     if (sigres && sigres->signatures)
1343     show_verify_result (sigres);
1344    
1345     leave:
1346     if (in)
1347     gpg_file_data_release (in);
1348     if (out)
1349     gpg_file_data_release (out);
1350 twoaday 119
1351 werner 36 return rc;
1352     }
1353    
1354    
1355     int
1356     fm_sign (fm_state_t c, const char * name)
1357 twoaday 105 {
1358 werner 36 gpgme_ctx_t ctx = c->ctx;
1359     gpgme_error_t err;
1360     file_data_t in=NULL, out=NULL;
1361     char ext[5];
1362 twoaday 105 int rc = 0;
1363 werner 36
1364     if (!c->init_cb || !c->cache_cb) {
1365 twoaday 119 set_gpg_passphrase_cb (&c->pass_cb, c->ctx,
1366     GPG_CMD_SIGN, c->dlg, _("Signing"));
1367 werner 36 c->init_cb = 1;
1368     }
1369    
1370     free_if_alloc (c->output);
1371     c->output = new char[strlen (name) + 5 + 1];
1372 twoaday 105 if (!c->output)
1373     BUG (NULL);
1374 werner 36 strcpy (ext, file_get_extension (ctx, c->sigmode));
1375     strcpy (c->output, name);
1376     strcat (c->output, ext);
1377    
1378     if (!overwrite_file (c->output)) {
1379     rc = ask_filename (c, _("Enter filename for signed file"), NULL);
1380     if (rc)
1381     goto leave;
1382     }
1383 twoaday 105
1384     err = gpg_file_data_new (name, F_DATA_READ, &in);
1385 werner 36 if (err)
1386     goto leave;
1387 twoaday 105 remove_crit_file_attrs (c->output, 0);
1388     err = gpg_file_data_new (c->output, F_DATA_WRITE, &out);
1389 werner 36 if (err)
1390     goto leave;
1391    
1392     op_begin ();
1393     err = gpgme_op_sign (ctx, in->dat, out->dat, c->sigmode);
1394     op_end ();
1395 twoaday 105 if (!c->cache_cb)
1396 werner 36 release_gpg_passphrase_cb (&c->pass_cb);
1397 twoaday 105 if (c->pass_cb.cancel) {
1398 werner 36 rc = WPTERR_GENERAL;
1399     goto leave;
1400     }
1401 twoaday 105 if (err) {
1402     msg_box (c->dlg, gpgme_strerror (err), _("Sign"), MB_ERR);
1403 werner 36 rc = WPTERR_GENERAL;
1404     goto leave;
1405     }
1406    
1407     leave:
1408     if (in)
1409     gpg_file_data_release (in);
1410     if (out)
1411     gpg_file_data_release (out);
1412     return rc;
1413     }
1414    
1415    
1416 twoaday 105 static void
1417 werner 36 fm_add_sig_stat (file_sig_ctx_t log)
1418     {
1419     gpgme_key_t key;
1420     const char *kid;
1421    
1422     kid = log->sig->fpr;
1423     if (!kid)
1424     BUG (NULL);
1425     if (strlen (kid) == 40)
1426     kid += 32;
1427     else if (strlen (kid) == 32)
1428     kid += 24;
1429     if (get_pubkey (kid, &key))
1430     log->use_uid = 0;
1431     else {
1432     log->user_id = key->uids->uid;
1433     log->use_uid = 1;
1434     }
1435     file_verify_add_state (log);
1436     }
1437    
1438    
1439 twoaday 105 /* Verify a detached signature from the clipboard. */
1440 werner 36 static int
1441 twoaday 105 verify_pasted (listview_ctrl_t lv, fm_state_t ctx,
1442     const char *dat, int pos, HWND dlg)
1443 werner 36 {
1444 twoaday 105 FILE *fp;
1445 werner 36 char stat[32];
1446 twoaday 105 char file[256], *fname = NULL;
1447     int del_end = 0;
1448 werner 36
1449 twoaday 105 listview_get_item_text (lv, pos, 0, stat, sizeof (stat)-1);
1450     listview_get_item_text (lv, pos, 1, file, sizeof (file)-1);
1451 werner 36 if (strcmp (stat, "UNKNOWN"))
1452     return 0;
1453     fname = make_filename (NULL, file, "asc");
1454     if (file_exist_check (fname) != 0) {
1455     fp = fopen (fname, "wb");
1456     if (fp == NULL) {
1457     log_box (_("File Manager"), MB_ERR, "could not create '%s'", fname);
1458     free_if_alloc (fname);
1459 twoaday 105 return WPTERR_GENERAL;
1460     }
1461 werner 36 fwrite (dat, 1, strlen (dat), fp);
1462     fclose (fp);
1463     del_end = 1;
1464     }
1465     fm_verify (ctx, 1, fname);
1466     if (del_end)
1467 twoaday 77 remove (fname);
1468 werner 36 free_if_alloc (fname);
1469     return 0;
1470     }
1471    
1472    
1473 twoaday 105 /* Figure out if the clipboard contains a detached signature. */
1474 werner 36 int
1475     fm_verify_pasted_detsig (listview_ctrl_t lv, HWND dlg)
1476     {
1477     fm_state_t ctx = NULL;
1478     char * dat=NULL;
1479     int i, fnd = 0;
1480    
1481     dat = get_clip_text (NULL);
1482     if (!dat || !strstr (dat, "BEGIN PGP SIGNATURE")) {
1483     msg_box (dlg, _("Could not find detached signature in the clipboard."),
1484     _("File Manager"), MB_ERR);
1485     free_if_alloc (dat);
1486     return WPTERR_GENERAL;
1487     }
1488     /* XXX find a way to filter out bad signatures or just ignore all in
1489     this case */
1490     fm_state_new (&ctx);
1491 twoaday 105 i = listview_get_curr_pos (lv);
1492     if (i= -1) {
1493 werner 36 verify_pasted (lv, ctx, dat, i, dlg);
1494     fnd = 1;
1495     }
1496     else {
1497     for (i=0; i < listview_count_items (lv, 0); i++) {
1498     verify_pasted (lv, ctx, dat, i, dlg);
1499     fnd = 1;
1500     }
1501     }
1502     if (!fnd)
1503     msg_box (dlg, _("No files to check."), _("File Manager"), MB_INFO);
1504     free_if_alloc (dat);
1505     fm_state_release (ctx);
1506     return 0;
1507     }
1508    
1509    
1510     /* Extract automatically the output file name from @name.
1511     If @detached is 1, a detached sig is assumed. */
1512     static int
1513     get_output_file (fm_state_t c, const char *name, int detached)
1514     {
1515     const char *file = NULL;
1516     const char *title;
1517     char fname[384];
1518    
1519     if (detached)
1520     title = _("Select Data File");
1521     else
1522     title = _("Selected Output File");
1523    
1524 twoaday 105 if (strstr (name, ".sig")
1525     || strstr (name, ".asc")
1526     || strstr (name, ".gpg")) {
1527 werner 36 _snprintf (fname, sizeof (fname) - 1, "%s", name);
1528     fname[strlen (fname) - 4] = '\0';
1529     if (file_exist_check (fname) == 0 && detached)
1530     file = fname;
1531     else if (!detached) {
1532     /* If the signature is clear or normal, make sure we do not
1533     overwrite the original file if it exists. */
1534     if (file_exist_check (fname) == 0 && !overwrite_file (fname)) {
1535     file = get_filesave_dlg (c->dlg, title, NULL, NULL);
1536     if (!file)
1537     return WPTERR_GENERAL;
1538     }
1539     else
1540     file = fname;
1541     }
1542     }
1543     if (!file)
1544     file = get_fileopen_dlg (c->dlg, title, NULL, NULL);
1545     if (file) {
1546     free_if_alloc (c->output);
1547     c->output = m_strdup (file);
1548     if (!c->output)
1549     BUG (NULL);
1550     }
1551     else {
1552     msg_box (c->dlg, _("Invalid file name. Exit"), _("Verify"), MB_ERR);
1553     return WPTERR_GENERAL;
1554     }
1555     if (detached)
1556     c->sigmode = GPGME_SIG_MODE_DETACH;
1557     else {
1558     if (strstr (name, ".asc"))
1559     c->sigmode = GPGME_SIG_MODE_CLEAR;
1560     else
1561     c->sigmode = GPGME_SIG_MODE_NORMAL;
1562     }
1563     return 0;
1564     }
1565    
1566    
1567     /* Verify the signature from the file @name. If @detached 1,
1568     it is assumed that a detached signature should be checked. */
1569     int
1570     fm_verify (fm_state_t c, int detached, const char *name)
1571     {
1572     gpgme_ctx_t ctx = c->ctx;
1573     gpgme_error_t err;
1574     gpgme_signature_t s;
1575     gpgme_verify_result_t res;
1576     struct file_sig_ctx_s log;
1577     file_data_t in=NULL, out=NULL;
1578     int rc = 0;
1579    
1580     if (strstr (name, ".sig"))
1581     detached = 1;
1582    
1583     if (get_output_file (c, name, detached))
1584     return WPTERR_GENERAL;
1585    
1586     memset (&log, 0, sizeof (log));
1587     log.file = m_strdup (name);
1588     if (!log.file)
1589     BUG (NULL);
1590     file_verify_create_dlg ();
1591    
1592 twoaday 105 err = gpg_file_data_new (name, F_DATA_READ, &in);
1593 werner 36 if (err)
1594     goto leave;
1595 twoaday 105 err = gpg_file_data_new (c->output,
1596     detached? F_DATA_READ : F_DATA_WRITE, &out);
1597 werner 36 if (err)
1598     goto leave;
1599    
1600     op_begin ();
1601     if (c->sigmode == GPGME_SIG_MODE_DETACH)
1602     err = gpgme_op_verify (ctx, in->dat, out->dat, NULL);
1603     else
1604     err = gpgme_op_verify (ctx, in->dat, in->dat, out->dat);
1605     op_end ();
1606     if (err) {
1607     msg_box (c->dlg, gpgme_strerror (err), _("Verify"), MB_ERR);
1608     rc = WPTERR_GENERAL;
1609     goto leave;
1610     }
1611    
1612     res = gpgme_op_verify_result (ctx);
1613     for (s=res->signatures; s; s=s->next) {
1614     log.sig = s;
1615     fm_add_sig_stat (&log);
1616     }
1617     if (!c->output)
1618     c->output = m_strdup (name); /* for later use */
1619    
1620     leave:
1621     if (in)
1622     gpg_file_data_release (in);
1623     if (out)
1624     gpg_file_data_release (out);
1625 twoaday 105 free_if_alloc (log.file);
1626 werner 36 return rc;
1627     }
1628    
1629    
1630 twoaday 105 /* Import the keys from the file @name.
1631     Return value: 0 on success. */
1632 werner 36 int
1633     fm_import (fm_state_t c, const char *name)
1634     {
1635     gpgme_ctx_t ctx = c->ctx;
1636     gpgme_error_t err;
1637     gpgme_import_result_t res;
1638     file_data_t keydata = NULL;
1639     int rc = 0;
1640    
1641     free_if_alloc (c->output);
1642     c->output = m_strdup (name);
1643     if (!c->output)
1644     BUG (NULL);
1645    
1646 twoaday 105 err = gpg_file_data_new (name, F_DATA_READ, &keydata);
1647 werner 36 if (err)
1648     goto leave;
1649    
1650     op_begin ();
1651     err = gpgme_op_import (ctx, keydata->dat);
1652     op_end ();
1653     if (err) {
1654     msg_box (c->dlg, gpgme_strerror (err), _("Import"), MB_ERR);
1655     rc = WPTERR_GENERAL;
1656     goto leave;
1657     }
1658    
1659     res = gpgme_op_import_result (ctx);
1660     print_import_status (res);
1661     if (res->no_user_id > 0) {
1662 twoaday 105 msg_box (c->dlg, _("Key without a self signature was dectected!\n"
1663 werner 36 "(This key is NOT usable for encryption, etc)\n"
1664     "\n"
1665     "Cannot import these key(s)!"), _("Import"), MB_INFO);
1666     }
1667    
1668     leave:
1669     if (keydata)
1670     gpg_file_data_release (keydata);
1671     return rc;
1672 twoaday 105 }
1673 werner 36
1674    
1675     /* Export the selected keys from the File Manager to a file. */
1676     int
1677     fm_export (fm_state_t c)
1678     {
1679 twoaday 69 int rc = 0;
1680 werner 36 gpgme_ctx_t ctx = c->ctx;
1681     gpgme_error_t err;
1682     gpgme_key_t *rset = c->recp;
1683     file_data_t keydata = NULL;
1684 twoaday 94 const char *name;
1685 werner 36 char *p = NULL, *patt = NULL;
1686    
1687     if (!rset || !rset[0]) {
1688     msg_box (c->dlg, _("No key was selected for export."), _("Export"), MB_ERR);
1689     rc = WPTERR_GENERAL;
1690     goto leave;
1691     }
1692    
1693 twoaday 105 if (rset[1] == NULL) /* count == 1*/
1694 twoaday 129 p = km_gen_export_filename (rset[0]->subkeys->keyid+8, 0);
1695 werner 36
1696 twoaday 77 name = get_filesave_dlg (c->dlg, _("Choose Name for Key File"),
1697 werner 36 NULL, p? p : NULL);
1698     if (!name)
1699     name = "keys.gpg";
1700    
1701     patt = gpg_keylist_to_pattern (rset, c->n_recp);
1702    
1703 twoaday 105 err = gpg_file_data_new (name, F_DATA_WRITE, &keydata);
1704 werner 36 if (err)
1705     goto leave;
1706    
1707     op_begin ();
1708     err = gpgme_op_export (ctx, patt, 0, keydata->dat);
1709     op_end ();
1710     if (err) {
1711     msg_box (c->dlg, gpgme_strerror (err), _("Export"), MB_ERR);
1712     rc = WPTERR_GENERAL;
1713     goto leave;
1714     }
1715     log_box (_("GnuPG status"), MB_OK, _("Finished (Output: %s)"), name);
1716    
1717     leave:
1718     if (keydata)
1719     gpg_file_data_release (keydata);
1720     if (patt)
1721     free (patt);
1722     free_if_alloc (p);
1723     return rc;
1724     }
1725    
1726    
1727     /* Parse the command line and process the given file. */
1728     int
1729     fm_parse_command_line (char *cmdl)
1730     {
1731     fm_state_t ctx;
1732     const char *s;
1733     char *p, *fn = NULL;
1734     int count = 0, detached = 0;
1735     int type;
1736    
1737     if (!cmdl || !*cmdl)
1738     return 0;
1739    
1740     fm_state_new (&ctx);
1741     ctx->dlg = GetActiveWindow ();
1742     ctx->cache_cb = 1;
1743    
1744     p = cmdl;
1745     if (p && *p > 32 && !stristr (p, "winpt.exe")
1746     && !strstr (p, "--" )) {
1747     count++;
1748     if (*p == '"') { /* need to remove quotes */
1749     fn = new char[strlen (p)];
1750     if (!fn)
1751     BUG (NULL);
1752     memcpy (fn, p+1, strlen (p) - 2);
1753     fn[strlen (p) -2] = '\0';
1754     }
1755     else
1756     fn = m_strdup (p);
1757     s = fm_get_file_type (fn, &type);
1758     if (!s || !strcmp (s, "UNKNOWN"))
1759     s = gnupg_check_file_ext (fn, &type);
1760     if (type == PGP_NONE) {
1761     log_box (_("File Manager"), MB_ERR,
1762     _("%s: no valid OpenPGP data found."), p);
1763     free_if_alloc (fn);
1764     return count;
1765     }
1766     switch (type) {
1767     case PGP_MESSAGE:
1768     fm_decrypt (ctx, fn);
1769     break;
1770    
1771     case PGP_PUBKEY:
1772     case PGP_SECKEY:
1773     fm_import (ctx, fn);
1774     break;
1775    
1776     case PGP_SIG:
1777     case PGP_CLEARSIG:
1778     file_verify_use_event ();
1779     if (type == PGP_SIG)
1780     detached = 1;
1781     fm_verify (ctx, detached, fn);
1782     file_verify_wait ();
1783 twoaday 77 break;
1784 twoaday 88
1785 twoaday 77 default:
1786     break;
1787 werner 36 }
1788     }
1789    
1790     wipememory (&ctx->pass_cb, sizeof (ctx->pass_cb));
1791     free_if_alloc (fn);
1792     fm_state_release (ctx);
1793     return count;
1794     }
1795    
1796    
1797 twoaday 105 /* Extract the last folder name from @name. */
1798 werner 36 const char*
1799     default_dirname (const char *name)
1800     {
1801 twoaday 105 char *p = strrchr (name, '\\');
1802     if (!p)
1803 werner 36 return NULL;
1804     return p+1;
1805 twoaday 105 }
1806 werner 36
1807    
1808 twoaday 77 /* Store all selected files from @lv in a zip archive
1809     and encrypt the zip archive then.
1810     Return value: 0 on success. */
1811 werner 36 int
1812 twoaday 77 fm_encrypt_into_zip (fm_state_t ctx, listview_ctrl_t lv)
1813     {
1814     PK_FILE_LIST list=NULL;
1815     const char *outfile;
1816     char *out_enc;
1817     int nitems = listview_count_items (lv, 0);
1818     int i, idx = -1;
1819     int rc;
1820    
1821     if (!nitems) {
1822     msg_box (NULL, _("Encrypting into a ZIP archive makes sense with multiple files"),
1823     _("File Manager"), MB_ERR);
1824 twoaday 105 return WPTERR_GENERAL;
1825 twoaday 77 }
1826    
1827 twoaday 105 outfile = get_filesave_dlg (ctx->dlg, _("Choose File Name for Output"),
1828     NULL, "Encrypted_Files.zip");
1829 twoaday 77 if (!outfile)
1830 twoaday 105 return WPTERR_GENERAL;
1831 twoaday 77
1832     for (i=0; i < nitems; i++) {
1833     char name[300];
1834     if (!listview_get_item_state (lv, i))
1835     continue;
1836     if (idx == -1)
1837     idx = i;
1838     listview_get_item_text (lv, i, 1, name, sizeof (name)-1);
1839     pk_list_add (&list, name);
1840     }
1841    
1842     pk_archiv_create (list, outfile);
1843     pk_list_free (list);
1844    
1845     rc = fm_encrypt (ctx, outfile, 0);
1846     DeleteFile (outfile);
1847     if (rc)
1848     return rc;
1849    
1850     out_enc = make_filename (NULL, outfile, "gpg");
1851 twoaday 88 fm_set_status (lv, idx, FM_ENCRYPT, (gpgme_sig_mode_t)0, 1, out_enc);
1852 twoaday 77 free_if_alloc (out_enc);
1853    
1854     for (i=0; i < nitems; i++) {
1855     if (i != idx && listview_get_item_state (lv, i))
1856     listview_del_item (lv, i);
1857     }
1858     return 0;
1859     }
1860    
1861    
1862     int
1863 twoaday 105 fm_encrypt_directory (fm_state_t c, const char *name)
1864 werner 36 {
1865     PK_FILE_LIST list = NULL;
1866     WIN32_FIND_DATA findbuf;
1867     HANDLE hd;
1868     const char * s;
1869     char * patt = NULL, * p;
1870     int rc = 0;
1871    
1872 twoaday 105 if (!is_directory (name))
1873 werner 36 return -1;
1874 twoaday 105 patt = new char[strlen (name) + 4];
1875     if (!patt)
1876     BUG (NULL);
1877     strcpy (patt, name);
1878     strcat (patt, "\\*");
1879     hd = FindFirstFile (patt, &findbuf);
1880     if (!hd) {
1881     free_if_alloc (patt);
1882     return WPTERR_GENERAL;
1883 werner 36 }
1884     if( strcmp( findbuf.cFileName, "." ) && strcmp( findbuf.cFileName, ".." ) ) {
1885     p = make_filename( name, findbuf.cFileName, NULL );
1886     pk_list_add( &list, p );
1887     free_if_alloc( p );
1888     }
1889     while( FindNextFile( hd, &findbuf ) ) {
1890     if( strcmp( findbuf.cFileName, "." ) && strcmp( findbuf.cFileName, ".." ) ) {
1891     p = make_filename( name, findbuf.cFileName, NULL );
1892     pk_list_add( &list, p );
1893     free_if_alloc( p );
1894     }
1895     }
1896 twoaday 105 s = get_filesave_dlg (c->dlg, _("Choose a Name for the Archive"),
1897     NULL, default_dirname (name));
1898 werner 36 if( !s ) {
1899     msg_box( c->dlg, _("Invalid archive name. Exit."), _("Encrypt Directory"), MB_ERR );
1900     rc = -1;
1901     goto leave;
1902     }
1903    
1904     rc = pk_archiv_create( list, s );
1905     if( rc )
1906     msg_box( c->dlg, _("Could not create zip archive."), _("Encrypt Directory"), MB_ERR );
1907     else {
1908     fm_encrypt( c, s, 0 );
1909 twoaday 77 remove( s );
1910 werner 36 }
1911     leave:
1912     FindClose (hd);
1913     pk_list_free( list );
1914     free_if_alloc( patt );
1915     return rc;
1916 twoaday 105 }
1917 werner 36
1918    
1919     static int CALLBACK
1920 twoaday 105 fm_cmp_cb (LPARAM first, LPARAM second, LPARAM sortby)
1921 werner 36 {
1922 twoaday 105 const char *a = 0;
1923     const char *b = 0;
1924 werner 36
1925     switch( (int)sortby ) {
1926     case FM_SORT_STAT:
1927     break;
1928     case FM_SORT_NAME:
1929     break;
1930     case FM_SORT_OP:
1931     break;
1932     }
1933 twoaday 105 return stricmp (a, b);
1934     }
1935 werner 36
1936    
1937 twoaday 105 /* Sort the list items from @lv with the mode given by @sortby. */
1938 werner 36 int
1939 twoaday 105 fm_sort (listview_ctrl_t lv, int sortby)
1940 werner 36 {
1941     return listview_sort_items( lv, sortby, fm_cmp_cb );
1942 twoaday 105 }
1943 werner 36
1944    
1945 twoaday 105 /* Start the 'print md' dialog. Pass over the listview control
1946     @lv and the digest algo @mdalgo. */
1947 werner 36 void
1948 twoaday 105 fm_print_md (listview_ctrl_t lv, HWND dlg, int mdalgo)
1949 werner 36 {
1950     struct md_file_s mdctx;
1951    
1952 twoaday 105 if (listview_count_items (lv, 0) == 0)
1953 werner 36 return;
1954     memset (&mdctx, 0, sizeof (mdctx));
1955     mdctx.lv = lv;
1956     mdctx.mdalgo = mdalgo;
1957 twoaday 105 DialogBoxParam (glob_hinst, (LPCTSTR)IDD_WINPT_FILE_MDSUM, dlg,
1958     mdsum_dlg_proc, (LPARAM)&mdctx);
1959     }
1960 werner 36
1961    
1962 twoaday 77 /* Send the selected file in @lv via MAPI to a mail recipient. */
1963 werner 36 int
1964     fm_send_file (listview_ctrl_t lv)
1965     {
1966     char buf[128];
1967     int rc;
1968    
1969     rc = listview_get_item_text (lv, -1, 1, buf, sizeof (buf)-1);
1970 twoaday 77 if (rc != -1)
1971     mapi_send_ascfile (buf);
1972 werner 36 return 0;
1973     }

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26