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

Annotation of /trunk/Src/wptFileManager.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 129 - (hide annotations)
Fri Dec 30 13:56:10 2005 UTC (19 years, 2 months ago) by twoaday
File size: 48049 byte(s)
2005-12-27  Timo Schulz  <ts@g10code.com>
                                                                                
        * wptListView.cpp (listview_set_view): New.
        (listview_del_column): New.
        * wptW32API.cpp (get_locale_date): New.
        (get_menu_state): New.
        (force_foreground_window): New.
        * wptVerifyList.cpp (strtimestamp): Support for
        locale date formats.
        * wptGPGUtil.cpp (gpg_revoke_cert): Handle bad
        passphrases.
        * wptKeyEditCB.cpp (editkey_command_handler): Immediately
        return when a bad passphrase was submitted.
        * wptKeyRevokersDlg.cpp (keyrevokers_dlg_proc): Change
        column order.
        * wptKeylist.cpp (keylist_upd_col): New.
        * wptKeyManagerDlg.cpp (update_ui_items): Deactivate
        'Revocation' for public keys.
        (translate_menu_strings): s/Revoke/Revoke Cert.
        (modify_listview_columns): New.


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     return rc;
533     }
534    
535    
536 twoaday 41 /* Add a single file @name to the list view and before
537     figure out the type of it.
538     Return value: 0 on success. */
539     static int
540     add_single_file (listview_ctrl_t lv, const char *name)
541     {
542     const char *type;
543     int rc = 0;
544    
545     type = fm_get_file_type (name, NULL);
546     if (!type)
547     return WPTERR_FILE_OPEN;
548     if (!strcmp (type, "UNKNOWN"))
549     type = gnupg_check_file_ext (name, NULL);
550     rc = listview_add_item (lv, "");
551     if (!rc) {
552     listview_add_sub_item (lv, 0, 0, type);
553     listview_add_sub_item (lv, 0, 1, name);
554     }
555     return rc;
556     }
557    
558    
559     /* Use the common Open-File-Dialog to allow the user to
560     add one ore more selected files to the listview @lv. */
561 werner 36 int
562     fm_add_opened_files (listview_ctrl_t lv, HWND dlg)
563     {
564     OPENFILENAME open;
565 twoaday 41 char file[512], name[MAX_PATH+1];
566     char *path = NULL;
567     const char *s;
568     int i, len=0, n=0;
569     int rc=0;
570    
571 werner 36 memset (&open, 0, sizeof (open));
572     open.lStructSize = sizeof (OPENFILENAME);
573     open.hInstance = glob_hinst;
574     open.lpstrTitle = _("File Open");
575     open.lpstrFilter = _("All Files (*.*)\0*.*\0\0");
576     open.hwndOwner = dlg;
577     open.lpstrFile = file;
578     open.nMaxFile = sizeof (file) - 1;
579 twoaday 41 open.Flags = OFN_ALLOWMULTISELECT|OFN_EXPLORER ;
580 werner 36
581 twoaday 41 memset (file, 0, sizeof file);
582     if (!GetOpenFileName (&open))
583     return 0;
584    
585     s = file;
586     len = sizeof (file)-1;
587     for (;;) {
588     if (len < 2 || (*s == '\0' && *(s+1) == '\0'))
589     break;
590     memset (name, 0, sizeof (name));
591     for (i=0; len > 0; len--, i++) {
592     if (*s == '\0') {
593     name[i] = *s++;
594     break;
595     }
596     name[i] = *s++;
597     }
598     if (n == 0)
599     path = strdup (name);
600     else {
601     char *p = make_filename (path, name, NULL);
602     rc = add_single_file (lv, p);
603     free (p);
604     }
605     n++;
606 werner 36 }
607 twoaday 41 if (n == 1) /* single file selected. */
608     rc = add_single_file (lv, path);
609     if (path)
610     free (path);
611 werner 36 return rc;
612     }
613    
614    
615     int
616     fm_assume_onepass_sig (const char * fname)
617     {
618     gpgme_data_t dat;
619     armor_filter_context_t afx;
620     gpg_iobuf_t fp;
621     PACKET * pkt = (PACKET *)calloc (1, sizeof *pkt);
622     int check = 0;
623    
624     if (!fname) {
625     gpg_data_new_from_clipboard (&dat, 0);
626     gpg_data_release_and_set_file (dat, "gpgme.tmp");
627    
628     fp = gpg_iobuf_open ("gpgme.tmp");
629     if (!fp)
630     return 0;
631     gpg_iobuf_ioctl (fp, 3, 1, NULL);
632     if (gpg_use_armor_filter(fp)) {
633     memset (&afx, 0, sizeof (afx));
634     gpg_iobuf_push_filter (fp, gpg_armor_filter, &afx);
635     }
636     gpg_init_packet (pkt);
637     if (!gpg_parse_packet (fp, pkt)
638     && pkt->pkttype == PKT_COMPRESSED)
639     check = 1;
640     gpg_free_packet (pkt);
641     safe_free (pkt);
642     gpg_iobuf_close (fp);
643 twoaday 77 remove ("gpgme.tmp");
644 werner 36 }
645     /* XXX: implement it for real files */
646     return check;
647     }
648    
649    
650     int
651     fm_get_current_pos (listview_ctrl_t lv)
652     {
653     int i = 0, items;
654    
655     items = listview_count_items (lv, 0);
656     if (!items)
657     return -1;
658     else if (items == 1)
659     {
660     listview_select_one (lv, 0);
661     return 0;
662     }
663     else if (items > 1)
664     {
665     i = listview_get_curr_pos (lv);
666     if (i == -1)
667     {
668     msg_box (lv->ctrl, _("Please select a file."), _("File Manager"), MB_ERR);
669     return -1;
670     }
671     return i;
672     }
673    
674     return -1;
675     } /* fm_get_current_pos */
676    
677    
678     static int
679     fm_check_detached_sig( listview_ctrl_t lv, int pos )
680     {
681     char type[128];
682    
683     listview_get_item_text( lv, pos, 0, type, 127 );
684     return !strcmp( type, "SIGNED-DETACH" )? 1 : 0;
685     } /* fm_check_detached_sig */
686    
687    
688     int
689     fm_check_file_type (listview_ctrl_t lv, int pos, int fm_cmd)
690     {
691     char status[128];
692     int rc = 0;
693    
694     listview_get_item_text (lv, pos, 0, status, sizeof (status) - 1);
695    
696     switch (fm_cmd) {
697     case FM_ENCRYPT:
698     case FM_ENCRYPT_DIR:
699     case FM_SIGNENCRYPT:
700     if (strcmp (status, "ENCRYPTED")
701     && strcmp (status, "SYMKEYENC"))
702     rc = 1;
703     break;
704    
705     case FM_DECRYPT:
706     if (!strcmp (status, "DATA")
707     || !strcmp (status, "ENCRYPTED")
708     || !strcmp (status, "SYMKEYENC")
709     || !strcmp (status, "ARMORED"))
710     rc = 1;
711     break;
712    
713     case FM_SIGN:
714     if( strncmp( status, "SIGNED", 6 ) )
715     rc = 1;
716     break;
717    
718     case FM_VERIFY:
719     if( !strncmp( status, "SIGNED", 6 )
720     || !strcmp( status, "COMPRESSED" ) )
721     rc = 1;
722     break;
723    
724     case FM_SYMENC:
725     if( strcmp( status, "SYMKEYENC" ) )
726     rc = 1;
727     break;
728    
729     case FM_IMPORT:
730     if( !strcmp( status, "PUBKEY" )
731     || !strcmp( status, "SECKEY" ) )
732     rc = 1;
733     break;
734    
735     case FM_WIPE:
736     case FM_LIST:
737     rc = 1;
738     break;
739     }
740    
741     return rc;
742     } /* fm_check_file_type */
743    
744    
745 twoaday 88 /* Set the file status of the given command @fm_cmd.
746     @success is 0 on success. */
747 werner 36 static void
748 twoaday 88 fm_set_status (listview_ctrl_t lv, int pos, int fm_cmd,
749     gpgme_sig_mode_t sigmode, int success, const char *output)
750 werner 36 {
751     char status[128], operat[128];
752     int update = 1;
753     const char *s;
754    
755 twoaday 88 if (fm_cmd == FM_LIST)
756 werner 36 return;
757     success ? s = "SUCCESS" : s = "FAILED";
758 twoaday 88 strcpy (operat, s);
759 werner 36
760     switch (fm_cmd) {
761     case FM_ENCRYPT:
762     case FM_ENCRYPT_DIR:
763 twoaday 88 case FM_SIGNENCRYPT: strcpy (status, "ENCRYPTED"); break;
764     case FM_DECRYPT: strcpy (status, "UNKNOWN"); break;
765     case FM_SIGN:
766     if (sigmode == GPGME_SIG_MODE_DETACH)
767     strcpy (status, "SIGNED-DETACH");
768 twoaday 119 else if (sigmode == GPGME_SIG_MODE_CLEAR)
769     strcpy (status, "SIGNED-CLEAR");
770 twoaday 88 else
771     strcpy (status, "SIGNED");
772     break;
773 werner 36 case FM_VERIFY: update = 0; break;
774 twoaday 88 case FM_SYMENC: strcpy (status, "SYMKEYENC"); break;
775 werner 36 case FM_IMPORT: update = 0; break;
776 twoaday 88 case FM_WIPE: strcpy (status, "WIPED"); break;
777     default: strcpy (status, "UNKNOWN"); break;
778 werner 36 }
779    
780 twoaday 119 if (success && update) {
781     listview_add_sub_item (lv, pos, 0, status);
782     listview_add_sub_item (lv, pos, 1, output);
783 werner 36 }
784     listview_add_sub_item( lv, pos, 2, operat );
785 twoaday 88 }
786 werner 36
787    
788     int
789     fm_clearsign_8bit (listview_ctrl_t lv, fm_state_s *ctx)
790     {
791     FILE *f;
792     byte buf[32];
793     char name[256];
794     int i, n, cnt=0;
795    
796     if (ctx->sigmode != GPGME_SIG_MODE_CLEAR)
797     return 0;
798     listview_get_item_text (lv, -1, 1, name, sizeof (name)-1);
799     if (stristr (name, ".TXT"))
800     return 0;
801     f = fopen (name, "rb");
802     if (!f)
803     return -1; /* should never happen */
804     n = fread (buf, 1, 32, f);
805     for (i = 0; i < n; i++) {
806     if (buf[i] == 0x00 || buf[i] > 170)
807     cnt++;
808     }
809     fclose (f);
810     if (!cnt)
811     return 0;
812     n = -1;
813     i = log_box (_("File Manager"), MB_WARN|MB_YESNO,
814     _("\"%s\" does not seems to be a text file.\n"
815     "Do you really want to clearsign it?"), name);
816     if (i == IDYES)
817     n = 0;
818     return n;
819     }
820    
821     int
822     fm_parse_files (listview_ctrl_t lv, HWND dlg, int cmd)
823     {
824     struct secdel_confirm_s confirm = {0};
825     struct progress_filter_s pfx, pfx2;
826     fm_state_s * ctx;
827     int fm_cmd, sig_detached = 0;
828     int rc = 0, i, n, ndel = 0;
829     char fname[512], status[128];
830    
831     switch (cmd) {
832     case ID_FILEMISC_ENCRYPT: fm_cmd = FM_ENCRYPT; break;
833 twoaday 77 case ID_FILEMISC_ENCRYPT_ZIP:fm_cmd = FM_ENCRYPT_ZIP; break;
834 werner 36 case ID_FILEMISC_DECRYPT: fm_cmd = FM_DECRYPT; break;
835     case ID_FILEMISC_SYMENC: fm_cmd = FM_SYMENC; break;
836     case ID_FILEMISC_SIGN: fm_cmd = FM_SIGN; break;
837     case ID_FILEMISC_VERIFY: fm_cmd = FM_VERIFY; break;
838     case ID_FILEMISC_IMPORT: fm_cmd = FM_IMPORT; break;
839     case ID_FILEMISC_WIPE: fm_cmd = FM_WIPE; break;
840     case ID_FILEMISC_LIST: fm_cmd = FM_LIST; break;
841     case ID_FILEMISC_SIGNENC: fm_cmd = FM_SIGNENCRYPT; break;
842     default: return 1; /* unknown command */
843     }
844    
845     if (fm_get_current_pos (lv) == -1)
846 twoaday 77 return WPTERR_GENERAL;
847 werner 36 rc = fm_state_new (&ctx);
848     if (rc)
849     BUG (0);
850     ctx->dlg = dlg;
851    
852     // XXX: for file operations the progress dialog will be
853     // reloaded somewhere and thus a 'dummy' dialog remains
854    
855     /* we use it here to make sure that pfx_cleanup will not use
856     any weird values. */
857     memset (&pfx, 0, sizeof (pfx));
858     ctx->prog_cb = NULL;
859     if (cmd != FM_VERIFY && cmd != FM_SIGN /*&& reg_prefs.fm.progress > 0*/) {
860     pfx.hwnd = dlg;
861     /*gpgme_set_progress_cb (ctx->ctx, progress_callback, &pfx);*/
862     /*ctx->prog_cb = &pfx;*/
863     }
864    
865     /* Commands we need before we can perform the main command */
866     switch (fm_cmd) {
867 twoaday 77 case FM_ENCRYPT_ZIP:
868 werner 36 case FM_ENCRYPT:
869     case FM_SIGNENCRYPT:
870     if (fm_cmd == FM_SIGNENCRYPT)
871     ctx->req_signer = 1;
872 twoaday 105 DialogBoxParam (glob_hinst, (LPCTSTR)IDD_WINPT_FILE_ENCRYPT,
873     ctx->dlg, file_encrypt_dlg_proc, (LPARAM)ctx);
874 werner 36 if (ctx->cancel == 1) {
875 twoaday 105 rc = WPTERR_GENERAL;
876 werner 36 goto leave;
877     }
878     break;
879    
880     case FM_SIGN:
881     DialogBoxParam (glob_hinst, (LPCSTR)IDD_WINPT_FILE_SIGN, dlg,
882     file_sign_dlg_proc, (LPARAM) ctx);
883     if (ctx->cancel == 1 || fm_clearsign_8bit (lv, ctx)) {
884     rc = WPTERR_GENERAL;
885     goto leave;
886     }
887     break;
888    
889     case FM_WIPE:
890     memset (&pfx2, 0, sizeof (pfx2));
891     secure_unlink_set_cb (progress_callback, &pfx2);
892     break;
893     }
894    
895     for( i = 0, n = 0; i < listview_count_items( lv, 0 ); i++ ) {
896     if( !listview_get_item_state( lv, i ) )
897     continue;
898     listview_get_item_text( lv, i, 0, status, sizeof (status) -1 );
899     if (!strcmp( status, "ENCRYPTED" ) && fm_cmd == FM_DECRYPT)
900     n++;
901     if (!strcmp( status, "UNKNOWN" ) && fm_cmd == FM_SIGN)
902     n++;
903     if (fm_cmd == FM_WIPE)
904     ndel++;
905     }
906    
907     if (n > 1 && fm_cmd != FM_SYMENC)
908     ctx->cache_cb = 1;
909    
910     if (fm_cmd == FM_WIPE && ndel > 0) {
911     memset (&confirm, 0, sizeof confirm);
912     confirm.lv_files = lv;
913     DialogBoxParam (glob_hinst, (LPCTSTR)IDD_WINPT_FILES_SECDEL, ctx->dlg,
914 twoaday 77 file_secdel_confirm_dlg_proc, (LPARAM)&confirm);
915 werner 36 if (!confirm.yes)
916     goto leave;
917     }
918 twoaday 119
919 twoaday 77 if (fm_cmd == FM_ENCRYPT_ZIP)
920     fm_encrypt_into_zip (ctx, lv);
921    
922     for (i = 0; i < listview_count_items (lv, 0); i++) {
923 werner 36 if( !listview_get_item_state( lv, i ) )
924     continue;
925 twoaday 119 listview_get_item_text(lv, i, 1, fname, sizeof (fname) - 1);
926     if( file_exist_check (fname) && !is_directory (fname)) {
927     log_box (_("File Manager"), MB_ERR,
928     _("\"%s\" does not exist"), fname);
929 werner 36 continue;
930     }
931 twoaday 119 if (is_directory (fname))
932 werner 36 fm_cmd = FM_ENCRYPT_DIR;
933 twoaday 119 if (!fm_check_file_type (lv, i, fm_cmd))
934 werner 36 continue;
935 twoaday 119 sig_detached = fm_check_detached_sig (lv, i);
936 twoaday 77 switch (fm_cmd) {
937 werner 36 case FM_LIST: rc = fm_list( fname, dlg ); break;
938     case FM_WIPE: rc = fm_wipe( fname ); break;
939     case FM_ENCRYPT: rc = fm_encrypt( ctx, fname, 0 ); break;
940     case FM_ENCRYPT_DIR: rc = fm_encrypt_directory( ctx, fname ); break;
941     case FM_SIGNENCRYPT: rc = fm_encrypt( ctx, fname, 1 ); break;
942     case FM_DECRYPT: rc = fm_decrypt( ctx, fname ); break;
943     case FM_SIGN: rc = fm_sign( ctx, fname ); break;
944     case FM_SYMENC: rc = fm_sym_encrypt( ctx, fname );break;
945     case FM_VERIFY: rc = fm_verify (ctx, sig_detached, fname);break;
946     case FM_IMPORT:
947     free_if_alloc (ctx->opaque);
948     ctx->opaque = m_strdup (fname);
949     if (!ctx->opaque)
950     BUG (0);
951     DialogBoxParam( glob_hinst, (LPCSTR)IDD_WINPT_IMPORT, dlg,
952     file_import_dlg_proc, (LPARAM)ctx );
953     if (ctx->cancel == 1)
954     continue;
955     rc = fm_import (ctx, fname);
956     break;
957     }
958 twoaday 88 fm_set_status (lv, i, fm_cmd, ctx->sigmode, !rc, ctx->output);
959 werner 36 free_if_alloc (ctx->output);
960     progress_cleanup (&pfx);
961     }
962 twoaday 77
963 werner 36 if (fm_cmd == FM_WIPE) {
964     secure_unlink_set_cb (NULL, NULL);
965     progress_cleanup (&pfx2);
966     }
967     if (ctx->cache_cb) {
968     release_gpg_passphrase_cb (&ctx->pass_cb);
969     ctx->cache_cb = 0; /* make sure it's only used for this session! */
970     }
971    
972     /* remove wipe files from the list */
973     n = listview_count_items (lv, 0);
974     while (n--) {
975     listview_get_item_text (lv, n, 0, status, sizeof (status) - 1);
976 twoaday 77 if (!strcmp (status, "WIPED"))
977 werner 36 listview_del_item (lv, n);
978     }
979    
980     leave:
981     if (!rc)
982     fm_state_release (ctx);
983     progress_cleanup (&pfx);
984     return rc;
985     } /* fm_parse_files */
986    
987    
988     /* Wipe the given file @name with the delete mode
989     from the configuration.
990     Return value: 0 on success. */
991     int
992     fm_wipe (const char *name)
993     {
994     int rc;
995    
996     SetCursor (LoadCursor (NULL, IDC_WAIT));
997     remove_crit_file_attrs (name, 1);
998     rc = secure_unlink (name, reg_prefs.wipe_mode);
999     SetCursor (LoadCursor (NULL, IDC_ARROW));
1000     return rc;
1001     }
1002    
1003    
1004     /* Dump out the given PGP packets from file @name in a dialog. */
1005     int
1006     fm_list (const char *name, HWND dlg)
1007     {
1008     dialog_box_param( glob_hinst, (LPCTSTR)IDD_WINPT_FILE_STAT, dlg,
1009     file_stat_dlg_proc, (LPARAM)name, _("File Status"),
1010     IDS_WINPT_FILE_STAT );
1011     return 0;
1012     }
1013    
1014    
1015     static int
1016     ask_filename (fm_state_t c, const char *msg, char **dst)
1017     {
1018     const char * s;
1019    
1020 twoaday 77 s = get_filesave_dlg (c->dlg, msg, NULL, NULL);
1021 werner 36 if (!s)
1022     return WPTERR_GENERAL;
1023    
1024     if (dst != NULL)
1025     free_if_alloc (*dst);
1026     free_if_alloc (c->output);
1027     c->output = m_strdup (s);
1028     if (!c->output)
1029     BUG (0);
1030     if (dst)
1031     *dst = fm_quote_file (s);
1032     return 0;
1033     }
1034    
1035    
1036     int
1037     fm_encrypt (fm_state_t c, const char *name, int sign)
1038     {
1039     gpgme_error_t err;
1040     gpgme_key_t key = NULL;
1041     gpgme_ctx_t ctx = c->ctx;
1042     file_data_t in=NULL, out=NULL;
1043     char *keyid = NULL, ext[5];
1044 twoaday 69 /*int no_compr = 0;*/
1045 werner 36 int rc = 0;
1046    
1047     c->output = new char[strlen (name) + 5 + 1];
1048     if (!c->output)
1049     BUG (0);
1050     strcpy (ext, file_get_extension (ctx, c->sigmode));
1051     strcpy (c->output, name);
1052 twoaday 105 strcat (c->output, ext);
1053 werner 36
1054     if (!overwrite_file (c->output)) {
1055     rc = ask_filename (c, _("Enter filename for encrypted file"), NULL);
1056     if (rc)
1057     goto leave;
1058     }
1059    
1060 twoaday 105 err = gpg_file_data_new (name, F_DATA_READ, &in);
1061 werner 36 if (err)
1062     goto leave;
1063 twoaday 105 remove_crit_file_attrs (c->output, 0);
1064     err = gpg_file_data_new (c->output, F_DATA_WRITE, &out);
1065 werner 36 if (err)
1066     goto leave;
1067    
1068     /*
1069     if (c->prog_cb) {
1070     c->prog_cb->what = name;
1071     gpg_file_data_set_cb (in, c->prog_cb);
1072     }
1073     */
1074    
1075 twoaday 105 /* XXX: disable compression for multi-media files.
1076 werner 36 no_compr = is_multi_media (name);
1077     gpgme_control (ctx, GPGME_CTRL_NO_COMPR, no_compr);
1078     */
1079    
1080     if (sign) {
1081     if (gpgme_signers_enum (ctx, 0) == NULL) {
1082     keyid = get_gnupg_default_key ();
1083     if (!keyid) {
1084     msg_box (c->dlg, _("Could not get default secret key."),
1085     _("Signing"), MB_ERR);
1086     rc = WPTERR_GENERAL;
1087     goto leave;
1088     }
1089     if (get_seckey (keyid, &key))
1090     BUG (0);
1091     gpgme_signers_add (ctx, key);
1092     }
1093     else {
1094 twoaday 77 gpgme_key_t sigkey = gpgme_signers_enum (ctx, 0);
1095     if (sigkey && sigkey->subkeys) {
1096     keyid = m_strdup (sigkey->subkeys->keyid);
1097 werner 36 if (!keyid)
1098     BUG (NULL);
1099     }
1100     }
1101     if (!c->init_cb || !c->cache_cb) {
1102     set_gpg_passphrase_cb (&c->pass_cb, c->ctx, GPG_CMD_SIGN,
1103     c->dlg, _("Signing"));
1104     c->init_cb = 1;
1105     }
1106     op_begin ();
1107     err = gpgme_op_encrypt_sign (ctx, c->recp, GPGME_ENCRYPT_ALWAYS_TRUST,
1108     in->dat, out->dat);
1109     op_end ();
1110     if (!c->cache_cb)
1111     release_gpg_passphrase_cb (&c->pass_cb);
1112     if (c->pass_cb.cancel) {
1113     rc = WPTERR_GENERAL;
1114     goto leave;
1115     }
1116     if (err) {
1117     msg_box (c->dlg, gpgme_strerror (err), _("Sign"), MB_ERR);
1118     if (gpgme_err_code (err) == GPG_ERR_BAD_PASSPHRASE)
1119     agent_del_cache (keyid);
1120     rc = WPTERR_GENERAL;
1121     goto leave;
1122     }
1123     }
1124     else {
1125     op_begin ();
1126     err = gpgme_op_encrypt (ctx, c->recp, GPGME_ENCRYPT_ALWAYS_TRUST,
1127     in->dat, out->dat);
1128     op_end ();
1129     if (err) {
1130     msg_box (c->dlg, gpgme_strerror (err), _("Encrypt"), MB_ERR);
1131     rc = WPTERR_GENERAL;
1132     goto leave;
1133     }
1134     }
1135    
1136     leave:
1137     if (in)
1138     gpg_file_data_release (in);
1139     if (out)
1140     gpg_file_data_release (out);
1141 twoaday 77 free_if_alloc (keyid);
1142 twoaday 76 if (!rc && c->wipe)
1143     secure_unlink (name, WIPE_MODE_SIMPLE);
1144 werner 36 return rc;
1145     }
1146    
1147    
1148     int
1149     fm_sym_encrypt (fm_state_t c, const char * name)
1150     {
1151     gpgme_ctx_t ctx = c->ctx;
1152     gpgme_error_t err;
1153     file_data_t in=NULL, out=NULL;
1154     int rc = 0, cancel = 0;
1155     char ext[5], * pass;
1156    
1157     pass = request_passphrase2 (_("Symmetric"), 0, &cancel);
1158     if (cancel)
1159     return 0;
1160    
1161     /* XXX gpgme_control (ctx, GPGME_CTRL_CIPHER, -1);*/
1162     c->output = new char[strlen (name) + 5 + 1];
1163     if (!c->output)
1164     BUG (0);
1165     strcpy (ext, file_get_extension (ctx, c->sigmode));
1166     strcpy (c->output, name);
1167     strcat (c->output, ext);
1168    
1169     if (overwrite_file (c->output) == 0) {
1170     rc = WPTERR_GENERAL;
1171     goto leave;
1172     }
1173    
1174     gpgme_set_passphrase_cb (ctx, sym_passphrase_cb, pass);
1175    
1176     err = gpg_file_data_new (name, 1, &in);
1177     if (err)
1178     goto leave;
1179     err = gpg_file_data_new (c->output, 0, &out);
1180     if (err)
1181     goto leave;
1182    
1183     op_begin ();
1184     err = gpgme_op_encrypt (ctx, NULL, GPGME_ENCRYPT_ALWAYS_TRUST,
1185     in->dat, out->dat);
1186     op_end ();
1187     if (err) {
1188     msg_box (c->dlg, gpgme_strerror (err), _("Symmetric"), MB_ERR);
1189     rc = WPTERR_GENERAL;
1190     goto leave;
1191     }
1192     if (file_exist_check (c->output)) {
1193     msg_box (c->dlg, _("Encryption failed."), _("Symmetric"), MB_ERR);
1194     rc = WPTERR_GENERAL;
1195     }
1196    
1197     leave:
1198     if (in)
1199     gpg_file_data_release (in);
1200     if (out)
1201     gpg_file_data_release (out);
1202     sfree_if_alloc (pass);
1203     return rc;
1204 twoaday 105 }
1205 werner 36
1206    
1207     /* Show the human readable verify result from @sigres. */
1208     static void
1209     show_verify_result (gpgme_verify_result_t sigres)
1210     {
1211     gpgme_key_t key=NULL;
1212     gpgme_signature_t sig=sigres->signatures;
1213     const char *s, *keyid;
1214     int sigok = 0;
1215     int type;
1216     char buf[384];
1217    
1218     sig = sigres->signatures;
1219     sigok = sig->summary & GPGME_SIGSUM_GREEN;
1220     s = sigok? _("Good signature") : _("BAD signature");
1221     type = sigok? MB_OK: MB_ICONWARNING|MB_OK;
1222 twoaday 105 keyid = sig->fpr;
1223 werner 36 if (!keyid)
1224     return;
1225    
1226     keyid = strlen (sig->fpr) == 40? sig->fpr+32 : sig->fpr + 24;
1227     get_pubkey (sig->fpr, &key);
1228 twoaday 105 _snprintf (buf, sizeof (buf)-1, _("Signature made %s using %s key ID %s\n"
1229     "%s from \"%s\""),
1230     strtimestamp (sig->timestamp),
1231     get_key_pubalgo (sig->pubkey_algo),
1232 werner 36 keyid, s, key? key->uids->uid : _("user ID not found"));
1233     msg_box (NULL, buf, _("Decrypt Verify"), type);
1234     }
1235    
1236    
1237     /* Check the recipients if we have at least one secret key. */
1238     bool
1239     secret_key_available (gpgme_recipient_t rset)
1240     {
1241     gpgme_recipient_t r;
1242     gpgme_key_t key;
1243    
1244 twoaday 105 for (r=rset; r; r = r->next) {
1245 werner 36 if (gpgme_err_code (r->status) == GPG_ERR_NO_SECKEY)
1246     continue;
1247     else {
1248     /* extra check to make sure the key is available right now. */
1249     if (!get_seckey (r->keyid, &key))
1250     return true;
1251     }
1252     }
1253     return false;
1254     }
1255    
1256    
1257     /* Decrypt the file @name. */
1258     int
1259     fm_decrypt (fm_state_t c, const char *name)
1260     {
1261     gpgme_error_t err;
1262     gpgme_ctx_t ctx = c->ctx;
1263     gpgme_decrypt_result_t res;
1264     gpgme_verify_result_t sigres;
1265     file_data_t in =NULL, out=NULL;
1266     int rc = 0;
1267    
1268     if (!c->init_cb || !c->cache_cb) {
1269 twoaday 119 set_gpg_passphrase_cb (&c->pass_cb, c->ctx, GPG_CMD_DECRYPT,
1270 werner 36 c->dlg, _("Decryption"));
1271 twoaday 119 c->init_cb = 1;
1272     }
1273 werner 36
1274     c->output = m_strdup (name);
1275     if (!c->output)
1276     BUG (0);
1277     if (is_openpgp_ext (c->output))
1278     c->output[strlen (c->output)-4] = '\0';
1279     else {
1280 twoaday 41 const char *s;
1281     s = get_filesave_dlg (c->dlg, _("Choose Filename for Output"),
1282     NULL, NULL);
1283 werner 36 if (s) {
1284     free_if_alloc (c->output);
1285     c->output = m_strdup (s);
1286     if (!c->output)
1287     BUG (NULL);
1288     }
1289     }
1290    
1291     if (overwrite_file (c->output) == 0) {
1292     rc = ask_filename (c, _("Please enter filename for plaintext file"), NULL);
1293     if (rc)
1294     goto leave;
1295 twoaday 105 }
1296 werner 36
1297 twoaday 105 err = gpg_file_data_new (name, F_DATA_READ, &in);
1298 werner 36 if (err)
1299     goto leave;
1300 twoaday 105 remove_crit_file_attrs (c->output, 0);
1301     err = gpg_file_data_new (c->output, F_DATA_WRITE, &out);
1302 werner 36 if (err)
1303     goto leave;
1304    
1305     op_begin ();
1306     err = gpgme_op_decrypt_verify (ctx, in->dat, out->dat);
1307     op_end ();
1308     if (!c->cache_cb)
1309     release_gpg_passphrase_cb (&c->pass_cb);
1310     if (c->pass_cb.cancel) {
1311     rc = WPTERR_GENERAL;
1312     goto leave;
1313     }
1314    
1315     res = gpgme_op_decrypt_result (ctx);
1316     if (res && res->recipients && !secret_key_available (res->recipients)) {
1317     const char *keyid = res->recipients->keyid;
1318     char *p = get_key_userid (keyid+8);
1319     gpgme_pubkey_algo_t pkalgo = res->recipients->pubkey_algo;
1320    
1321     log_box( _("Decryption"), MB_ERR,
1322     _("Encrypted with %s key, ID %s.%s\n"
1323     "Decryption failed: secret key not available."),
1324     get_key_pubalgo (pkalgo), keyid+8, p);
1325     rc = WPTERR_GENERAL;
1326     free_if_alloc (p);
1327     goto leave;
1328     }
1329     else if (err) {
1330     msg_box (c->dlg, gpgme_strerror (err), _("Decrypt"), MB_ERR);
1331     rc = WPTERR_GENERAL;
1332     goto leave;
1333     }
1334     if (file_exist_check (c->output)) {
1335 twoaday 105 log_box ("Decrypt", MB_ERR,
1336     _("Decryption failed.\n%s: does not exist."), c->output);
1337 werner 36 rc = WPTERR_GENERAL;
1338     }
1339    
1340     sigres = gpgme_op_verify_result (ctx);
1341     if (sigres && sigres->signatures)
1342     show_verify_result (sigres);
1343    
1344     leave:
1345     if (in)
1346     gpg_file_data_release (in);
1347     if (out)
1348     gpg_file_data_release (out);
1349 twoaday 119
1350 werner 36 return rc;
1351     }
1352    
1353    
1354     int
1355     fm_sign (fm_state_t c, const char * name)
1356 twoaday 105 {
1357 werner 36 gpgme_ctx_t ctx = c->ctx;
1358     gpgme_error_t err;
1359     file_data_t in=NULL, out=NULL;
1360     char ext[5];
1361 twoaday 105 int rc = 0;
1362 werner 36
1363     if (!c->init_cb || !c->cache_cb) {
1364 twoaday 119 set_gpg_passphrase_cb (&c->pass_cb, c->ctx,
1365     GPG_CMD_SIGN, c->dlg, _("Signing"));
1366 werner 36 c->init_cb = 1;
1367     }
1368    
1369     free_if_alloc (c->output);
1370     c->output = new char[strlen (name) + 5 + 1];
1371 twoaday 105 if (!c->output)
1372     BUG (NULL);
1373 werner 36 strcpy (ext, file_get_extension (ctx, c->sigmode));
1374     strcpy (c->output, name);
1375     strcat (c->output, ext);
1376    
1377     if (!overwrite_file (c->output)) {
1378     rc = ask_filename (c, _("Enter filename for signed file"), NULL);
1379     if (rc)
1380     goto leave;
1381     }
1382 twoaday 105
1383     err = gpg_file_data_new (name, F_DATA_READ, &in);
1384 werner 36 if (err)
1385     goto leave;
1386 twoaday 105 remove_crit_file_attrs (c->output, 0);
1387     err = gpg_file_data_new (c->output, F_DATA_WRITE, &out);
1388 werner 36 if (err)
1389     goto leave;
1390    
1391     op_begin ();
1392     err = gpgme_op_sign (ctx, in->dat, out->dat, c->sigmode);
1393     op_end ();
1394 twoaday 105 if (!c->cache_cb)
1395 werner 36 release_gpg_passphrase_cb (&c->pass_cb);
1396 twoaday 105 if (c->pass_cb.cancel) {
1397 werner 36 rc = WPTERR_GENERAL;
1398     goto leave;
1399     }
1400 twoaday 105 if (err) {
1401     msg_box (c->dlg, gpgme_strerror (err), _("Sign"), MB_ERR);
1402 werner 36 rc = WPTERR_GENERAL;
1403     goto leave;
1404     }
1405    
1406     leave:
1407     if (in)
1408     gpg_file_data_release (in);
1409     if (out)
1410     gpg_file_data_release (out);
1411     return rc;
1412     }
1413    
1414    
1415 twoaday 105 static void
1416 werner 36 fm_add_sig_stat (file_sig_ctx_t log)
1417     {
1418     gpgme_key_t key;
1419     const char *kid;
1420    
1421     kid = log->sig->fpr;
1422     if (!kid)
1423     BUG (NULL);
1424     if (strlen (kid) == 40)
1425     kid += 32;
1426     else if (strlen (kid) == 32)
1427     kid += 24;
1428     if (get_pubkey (kid, &key))
1429     log->use_uid = 0;
1430     else {
1431     log->user_id = key->uids->uid;
1432     log->use_uid = 1;
1433     }
1434     file_verify_add_state (log);
1435     }
1436    
1437    
1438 twoaday 105 /* Verify a detached signature from the clipboard. */
1439 werner 36 static int
1440 twoaday 105 verify_pasted (listview_ctrl_t lv, fm_state_t ctx,
1441     const char *dat, int pos, HWND dlg)
1442 werner 36 {
1443 twoaday 105 FILE *fp;
1444 werner 36 char stat[32];
1445 twoaday 105 char file[256], *fname = NULL;
1446     int del_end = 0;
1447 werner 36
1448 twoaday 105 listview_get_item_text (lv, pos, 0, stat, sizeof (stat)-1);
1449     listview_get_item_text (lv, pos, 1, file, sizeof (file)-1);
1450 werner 36 if (strcmp (stat, "UNKNOWN"))
1451     return 0;
1452     fname = make_filename (NULL, file, "asc");
1453     if (file_exist_check (fname) != 0) {
1454     fp = fopen (fname, "wb");
1455     if (fp == NULL) {
1456     log_box (_("File Manager"), MB_ERR, "could not create '%s'", fname);
1457     free_if_alloc (fname);
1458 twoaday 105 return WPTERR_GENERAL;
1459     }
1460 werner 36 fwrite (dat, 1, strlen (dat), fp);
1461     fclose (fp);
1462     del_end = 1;
1463     }
1464     fm_verify (ctx, 1, fname);
1465     if (del_end)
1466 twoaday 77 remove (fname);
1467 werner 36 free_if_alloc (fname);
1468     return 0;
1469     }
1470    
1471    
1472 twoaday 105 /* Figure out if the clipboard contains a detached signature. */
1473 werner 36 int
1474     fm_verify_pasted_detsig (listview_ctrl_t lv, HWND dlg)
1475     {
1476     fm_state_t ctx = NULL;
1477     char * dat=NULL;
1478     int i, fnd = 0;
1479    
1480     dat = get_clip_text (NULL);
1481     if (!dat || !strstr (dat, "BEGIN PGP SIGNATURE")) {
1482     msg_box (dlg, _("Could not find detached signature in the clipboard."),
1483     _("File Manager"), MB_ERR);
1484     free_if_alloc (dat);
1485     return WPTERR_GENERAL;
1486     }
1487     /* XXX find a way to filter out bad signatures or just ignore all in
1488     this case */
1489     fm_state_new (&ctx);
1490 twoaday 105 i = listview_get_curr_pos (lv);
1491     if (i= -1) {
1492 werner 36 verify_pasted (lv, ctx, dat, i, dlg);
1493     fnd = 1;
1494     }
1495     else {
1496     for (i=0; i < listview_count_items (lv, 0); i++) {
1497     verify_pasted (lv, ctx, dat, i, dlg);
1498     fnd = 1;
1499     }
1500     }
1501     if (!fnd)
1502     msg_box (dlg, _("No files to check."), _("File Manager"), MB_INFO);
1503     free_if_alloc (dat);
1504     fm_state_release (ctx);
1505     return 0;
1506     }
1507    
1508    
1509     /* Extract automatically the output file name from @name.
1510     If @detached is 1, a detached sig is assumed. */
1511     static int
1512     get_output_file (fm_state_t c, const char *name, int detached)
1513     {
1514     const char *file = NULL;
1515     const char *title;
1516     char fname[384];
1517    
1518     if (detached)
1519     title = _("Select Data File");
1520     else
1521     title = _("Selected Output File");
1522    
1523 twoaday 105 if (strstr (name, ".sig")
1524     || strstr (name, ".asc")
1525     || strstr (name, ".gpg")) {
1526 werner 36 _snprintf (fname, sizeof (fname) - 1, "%s", name);
1527     fname[strlen (fname) - 4] = '\0';
1528     if (file_exist_check (fname) == 0 && detached)
1529     file = fname;
1530     else if (!detached) {
1531     /* If the signature is clear or normal, make sure we do not
1532     overwrite the original file if it exists. */
1533     if (file_exist_check (fname) == 0 && !overwrite_file (fname)) {
1534     file = get_filesave_dlg (c->dlg, title, NULL, NULL);
1535     if (!file)
1536     return WPTERR_GENERAL;
1537     }
1538     else
1539     file = fname;
1540     }
1541     }
1542     if (!file)
1543     file = get_fileopen_dlg (c->dlg, title, NULL, NULL);
1544     if (file) {
1545     free_if_alloc (c->output);
1546     c->output = m_strdup (file);
1547     if (!c->output)
1548     BUG (NULL);
1549     }
1550     else {
1551     msg_box (c->dlg, _("Invalid file name. Exit"), _("Verify"), MB_ERR);
1552     return WPTERR_GENERAL;
1553     }
1554     if (detached)
1555     c->sigmode = GPGME_SIG_MODE_DETACH;
1556     else {
1557     if (strstr (name, ".asc"))
1558     c->sigmode = GPGME_SIG_MODE_CLEAR;
1559     else
1560     c->sigmode = GPGME_SIG_MODE_NORMAL;
1561     }
1562     return 0;
1563     }
1564    
1565    
1566     /* Verify the signature from the file @name. If @detached 1,
1567     it is assumed that a detached signature should be checked. */
1568     int
1569     fm_verify (fm_state_t c, int detached, const char *name)
1570     {
1571     gpgme_ctx_t ctx = c->ctx;
1572     gpgme_error_t err;
1573     gpgme_signature_t s;
1574     gpgme_verify_result_t res;
1575     struct file_sig_ctx_s log;
1576     file_data_t in=NULL, out=NULL;
1577     int rc = 0;
1578    
1579     if (strstr (name, ".sig"))
1580     detached = 1;
1581    
1582     if (get_output_file (c, name, detached))
1583     return WPTERR_GENERAL;
1584    
1585     memset (&log, 0, sizeof (log));
1586     log.file = m_strdup (name);
1587     if (!log.file)
1588     BUG (NULL);
1589     file_verify_create_dlg ();
1590    
1591 twoaday 105 err = gpg_file_data_new (name, F_DATA_READ, &in);
1592 werner 36 if (err)
1593     goto leave;
1594 twoaday 105 err = gpg_file_data_new (c->output,
1595     detached? F_DATA_READ : F_DATA_WRITE, &out);
1596 werner 36 if (err)
1597     goto leave;
1598    
1599     op_begin ();
1600     if (c->sigmode == GPGME_SIG_MODE_DETACH)
1601     err = gpgme_op_verify (ctx, in->dat, out->dat, NULL);
1602     else
1603     err = gpgme_op_verify (ctx, in->dat, in->dat, out->dat);
1604     op_end ();
1605     if (err) {
1606     msg_box (c->dlg, gpgme_strerror (err), _("Verify"), MB_ERR);
1607     rc = WPTERR_GENERAL;
1608     goto leave;
1609     }
1610    
1611     res = gpgme_op_verify_result (ctx);
1612     for (s=res->signatures; s; s=s->next) {
1613     log.sig = s;
1614     fm_add_sig_stat (&log);
1615     }
1616     if (!c->output)
1617     c->output = m_strdup (name); /* for later use */
1618    
1619     leave:
1620     if (in)
1621     gpg_file_data_release (in);
1622     if (out)
1623     gpg_file_data_release (out);
1624 twoaday 105 free_if_alloc (log.file);
1625 werner 36 return rc;
1626     }
1627    
1628    
1629 twoaday 105 /* Import the keys from the file @name.
1630     Return value: 0 on success. */
1631 werner 36 int
1632     fm_import (fm_state_t c, const char *name)
1633     {
1634     gpgme_ctx_t ctx = c->ctx;
1635     gpgme_error_t err;
1636     gpgme_import_result_t res;
1637     file_data_t keydata = NULL;
1638     int rc = 0;
1639    
1640     free_if_alloc (c->output);
1641     c->output = m_strdup (name);
1642     if (!c->output)
1643     BUG (NULL);
1644    
1645 twoaday 105 err = gpg_file_data_new (name, F_DATA_READ, &keydata);
1646 werner 36 if (err)
1647     goto leave;
1648    
1649     op_begin ();
1650     err = gpgme_op_import (ctx, keydata->dat);
1651     op_end ();
1652     if (err) {
1653     msg_box (c->dlg, gpgme_strerror (err), _("Import"), MB_ERR);
1654     rc = WPTERR_GENERAL;
1655     goto leave;
1656     }
1657    
1658     res = gpgme_op_import_result (ctx);
1659     print_import_status (res);
1660     if (res->no_user_id > 0) {
1661 twoaday 105 msg_box (c->dlg, _("Key without a self signature was dectected!\n"
1662 werner 36 "(This key is NOT usable for encryption, etc)\n"
1663     "\n"
1664     "Cannot import these key(s)!"), _("Import"), MB_INFO);
1665     }
1666    
1667     leave:
1668     if (keydata)
1669     gpg_file_data_release (keydata);
1670     return rc;
1671 twoaday 105 }
1672 werner 36
1673    
1674     /* Export the selected keys from the File Manager to a file. */
1675     int
1676     fm_export (fm_state_t c)
1677     {
1678 twoaday 69 int rc = 0;
1679 werner 36 gpgme_ctx_t ctx = c->ctx;
1680     gpgme_error_t err;
1681     gpgme_key_t *rset = c->recp;
1682     file_data_t keydata = NULL;
1683 twoaday 94 const char *name;
1684 werner 36 char *p = NULL, *patt = NULL;
1685    
1686     if (!rset || !rset[0]) {
1687     msg_box (c->dlg, _("No key was selected for export."), _("Export"), MB_ERR);
1688     rc = WPTERR_GENERAL;
1689     goto leave;
1690     }
1691    
1692 twoaday 105 if (rset[1] == NULL) /* count == 1*/
1693 twoaday 129 p = km_gen_export_filename (rset[0]->subkeys->keyid+8, 0);
1694 werner 36
1695 twoaday 77 name = get_filesave_dlg (c->dlg, _("Choose Name for Key File"),
1696 werner 36 NULL, p? p : NULL);
1697     if (!name)
1698     name = "keys.gpg";
1699    
1700     patt = gpg_keylist_to_pattern (rset, c->n_recp);
1701    
1702 twoaday 105 err = gpg_file_data_new (name, F_DATA_WRITE, &keydata);
1703 werner 36 if (err)
1704     goto leave;
1705    
1706     op_begin ();
1707     err = gpgme_op_export (ctx, patt, 0, keydata->dat);
1708     op_end ();
1709     if (err) {
1710     msg_box (c->dlg, gpgme_strerror (err), _("Export"), MB_ERR);
1711     rc = WPTERR_GENERAL;
1712     goto leave;
1713     }
1714     log_box (_("GnuPG status"), MB_OK, _("Finished (Output: %s)"), name);
1715    
1716     leave:
1717     if (keydata)
1718     gpg_file_data_release (keydata);
1719     if (patt)
1720     free (patt);
1721     free_if_alloc (p);
1722     return rc;
1723     }
1724    
1725    
1726     /* Parse the command line and process the given file. */
1727     int
1728     fm_parse_command_line (char *cmdl)
1729     {
1730     fm_state_t ctx;
1731     const char *s;
1732     char *p, *fn = NULL;
1733     int count = 0, detached = 0;
1734     int type;
1735    
1736     if (!cmdl || !*cmdl)
1737     return 0;
1738    
1739     fm_state_new (&ctx);
1740     ctx->dlg = GetActiveWindow ();
1741     ctx->cache_cb = 1;
1742    
1743     p = cmdl;
1744     if (p && *p > 32 && !stristr (p, "winpt.exe")
1745     && !strstr (p, "--" )) {
1746     count++;
1747     if (*p == '"') { /* need to remove quotes */
1748     fn = new char[strlen (p)];
1749     if (!fn)
1750     BUG (NULL);
1751     memcpy (fn, p+1, strlen (p) - 2);
1752     fn[strlen (p) -2] = '\0';
1753     }
1754     else
1755     fn = m_strdup (p);
1756     s = fm_get_file_type (fn, &type);
1757     if (!s || !strcmp (s, "UNKNOWN"))
1758     s = gnupg_check_file_ext (fn, &type);
1759     if (type == PGP_NONE) {
1760     log_box (_("File Manager"), MB_ERR,
1761     _("%s: no valid OpenPGP data found."), p);
1762     free_if_alloc (fn);
1763     return count;
1764     }
1765     switch (type) {
1766     case PGP_MESSAGE:
1767     fm_decrypt (ctx, fn);
1768     break;
1769    
1770     case PGP_PUBKEY:
1771     case PGP_SECKEY:
1772     fm_import (ctx, fn);
1773     break;
1774    
1775     case PGP_SIG:
1776     case PGP_CLEARSIG:
1777     file_verify_use_event ();
1778     if (type == PGP_SIG)
1779     detached = 1;
1780     fm_verify (ctx, detached, fn);
1781     file_verify_wait ();
1782 twoaday 77 break;
1783 twoaday 88
1784 twoaday 77 default:
1785     break;
1786 werner 36 }
1787     }
1788    
1789     wipememory (&ctx->pass_cb, sizeof (ctx->pass_cb));
1790     free_if_alloc (fn);
1791     fm_state_release (ctx);
1792     return count;
1793     }
1794    
1795    
1796 twoaday 105 /* Extract the last folder name from @name. */
1797 werner 36 const char*
1798     default_dirname (const char *name)
1799     {
1800 twoaday 105 char *p = strrchr (name, '\\');
1801     if (!p)
1802 werner 36 return NULL;
1803     return p+1;
1804 twoaday 105 }
1805 werner 36
1806    
1807 twoaday 77 /* Store all selected files from @lv in a zip archive
1808     and encrypt the zip archive then.
1809     Return value: 0 on success. */
1810 werner 36 int
1811 twoaday 77 fm_encrypt_into_zip (fm_state_t ctx, listview_ctrl_t lv)
1812     {
1813     PK_FILE_LIST list=NULL;
1814     const char *outfile;
1815     char *out_enc;
1816     int nitems = listview_count_items (lv, 0);
1817     int i, idx = -1;
1818     int rc;
1819    
1820     if (!nitems) {
1821     msg_box (NULL, _("Encrypting into a ZIP archive makes sense with multiple files"),
1822     _("File Manager"), MB_ERR);
1823 twoaday 105 return WPTERR_GENERAL;
1824 twoaday 77 }
1825    
1826 twoaday 105 outfile = get_filesave_dlg (ctx->dlg, _("Choose File Name for Output"),
1827     NULL, "Encrypted_Files.zip");
1828 twoaday 77 if (!outfile)
1829 twoaday 105 return WPTERR_GENERAL;
1830 twoaday 77
1831     for (i=0; i < nitems; i++) {
1832     char name[300];
1833     if (!listview_get_item_state (lv, i))
1834     continue;
1835     if (idx == -1)
1836     idx = i;
1837     listview_get_item_text (lv, i, 1, name, sizeof (name)-1);
1838     pk_list_add (&list, name);
1839     }
1840    
1841     pk_archiv_create (list, outfile);
1842     pk_list_free (list);
1843    
1844     rc = fm_encrypt (ctx, outfile, 0);
1845     DeleteFile (outfile);
1846     if (rc)
1847     return rc;
1848    
1849     out_enc = make_filename (NULL, outfile, "gpg");
1850 twoaday 88 fm_set_status (lv, idx, FM_ENCRYPT, (gpgme_sig_mode_t)0, 1, out_enc);
1851 twoaday 77 free_if_alloc (out_enc);
1852    
1853     for (i=0; i < nitems; i++) {
1854     if (i != idx && listview_get_item_state (lv, i))
1855     listview_del_item (lv, i);
1856     }
1857     return 0;
1858     }
1859    
1860    
1861     int
1862 twoaday 105 fm_encrypt_directory (fm_state_t c, const char *name)
1863 werner 36 {
1864     PK_FILE_LIST list = NULL;
1865     WIN32_FIND_DATA findbuf;
1866     HANDLE hd;
1867     const char * s;
1868     char * patt = NULL, * p;
1869     int rc = 0;
1870    
1871 twoaday 105 if (!is_directory (name))
1872 werner 36 return -1;
1873 twoaday 105 patt = new char[strlen (name) + 4];
1874     if (!patt)
1875     BUG (NULL);
1876     strcpy (patt, name);
1877     strcat (patt, "\\*");
1878     hd = FindFirstFile (patt, &findbuf);
1879     if (!hd) {
1880     free_if_alloc (patt);
1881     return WPTERR_GENERAL;
1882 werner 36 }
1883     if( strcmp( findbuf.cFileName, "." ) && strcmp( findbuf.cFileName, ".." ) ) {
1884     p = make_filename( name, findbuf.cFileName, NULL );
1885     pk_list_add( &list, p );
1886     free_if_alloc( p );
1887     }
1888     while( FindNextFile( hd, &findbuf ) ) {
1889     if( strcmp( findbuf.cFileName, "." ) && strcmp( findbuf.cFileName, ".." ) ) {
1890     p = make_filename( name, findbuf.cFileName, NULL );
1891     pk_list_add( &list, p );
1892     free_if_alloc( p );
1893     }
1894     }
1895 twoaday 105 s = get_filesave_dlg (c->dlg, _("Choose a Name for the Archive"),
1896     NULL, default_dirname (name));
1897 werner 36 if( !s ) {
1898     msg_box( c->dlg, _("Invalid archive name. Exit."), _("Encrypt Directory"), MB_ERR );
1899     rc = -1;
1900     goto leave;
1901     }
1902    
1903     rc = pk_archiv_create( list, s );
1904     if( rc )
1905     msg_box( c->dlg, _("Could not create zip archive."), _("Encrypt Directory"), MB_ERR );
1906     else {
1907     fm_encrypt( c, s, 0 );
1908 twoaday 77 remove( s );
1909 werner 36 }
1910     leave:
1911     FindClose (hd);
1912     pk_list_free( list );
1913     free_if_alloc( patt );
1914     return rc;
1915 twoaday 105 }
1916 werner 36
1917    
1918     static int CALLBACK
1919 twoaday 105 fm_cmp_cb (LPARAM first, LPARAM second, LPARAM sortby)
1920 werner 36 {
1921 twoaday 105 const char *a = 0;
1922     const char *b = 0;
1923 werner 36
1924     switch( (int)sortby ) {
1925     case FM_SORT_STAT:
1926     break;
1927     case FM_SORT_NAME:
1928     break;
1929     case FM_SORT_OP:
1930     break;
1931     }
1932 twoaday 105 return stricmp (a, b);
1933     }
1934 werner 36
1935    
1936 twoaday 105 /* Sort the list items from @lv with the mode given by @sortby. */
1937 werner 36 int
1938 twoaday 105 fm_sort (listview_ctrl_t lv, int sortby)
1939 werner 36 {
1940     return listview_sort_items( lv, sortby, fm_cmp_cb );
1941 twoaday 105 }
1942 werner 36
1943    
1944 twoaday 105 /* Start the 'print md' dialog. Pass over the listview control
1945     @lv and the digest algo @mdalgo. */
1946 werner 36 void
1947 twoaday 105 fm_print_md (listview_ctrl_t lv, HWND dlg, int mdalgo)
1948 werner 36 {
1949     struct md_file_s mdctx;
1950    
1951 twoaday 105 if (listview_count_items (lv, 0) == 0)
1952 werner 36 return;
1953     memset (&mdctx, 0, sizeof (mdctx));
1954     mdctx.lv = lv;
1955     mdctx.mdalgo = mdalgo;
1956 twoaday 105 DialogBoxParam (glob_hinst, (LPCTSTR)IDD_WINPT_FILE_MDSUM, dlg,
1957     mdsum_dlg_proc, (LPARAM)&mdctx);
1958     }
1959 werner 36
1960    
1961 twoaday 77 /* Send the selected file in @lv via MAPI to a mail recipient. */
1962 werner 36 int
1963     fm_send_file (listview_ctrl_t lv)
1964     {
1965     char buf[128];
1966     int rc;
1967    
1968     rc = listview_get_item_text (lv, -1, 1, buf, sizeof (buf)-1);
1969 twoaday 77 if (rc != -1)
1970     mapi_send_ascfile (buf);
1971 werner 36 return 0;
1972     }

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26