Statistics
| Revision:

root / src / rfc2015.c @ 1

History | View | Annotate | Download (35.3 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 2001 Werner Koch (dd9jn)
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
 */
19

    
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23

    
24
#if USE_GPGME
25

    
26
#include "defs.h"
27

    
28
#include <glib.h>
29
#include <stdio.h>
30
#include <string.h>
31
#include <locale.h>
32
#include <ctype.h>
33

    
34
#include <gpgme.h>
35

    
36
#include "intl.h"
37
#include "procmime.h"
38
#include "procheader.h"
39
#include "base64.h"
40
#include "uuencode.h"
41
#include "unmime.h"
42
#include "codeconv.h"
43
#include "utils.h"
44
#include "prefs_common.h"
45
#include "passphrase.h"
46
#include "select-keys.h"
47
#include "sigstatus.h"
48
#include "rfc2015.h"
49

    
50
#define DIM(v)     (sizeof(v)/sizeof((v)[0]))
51

    
52
static char *content_names[] = {
53
    "Content-Type",
54
    "Content-Disposition",
55
    "Content-Transfer-Encoding",
56
    NULL
57
};
58

    
59
static char *mime_version_name[] = {
60
    "Mime-Version",
61
    NULL
62
};
63

    
64
#if 0
65
static void dump_mimeinfo (const char *text, MimeInfo *x)
66
{
67
    debug_print ("MimeInfo[%s] %p  level=%d\n",
68
               text, x, x? x->level:0 );
69
    if (!x)
70
        return;
71

72
    debug_print ("      enc=`%s' enc_type=%d mime_type=%d\n",
73
               x->encoding, x->encoding_type, x->mime_type );
74
    debug_print ("      cont_type=`%s' cs=`%s' name=`%s' bnd=`%s'\n",
75
               x->content_type, x->charset, x->name, x->boundary );
76
    debug_print ("      cont_disp=`%s' fname=`%s' fpos=%ld size=%u, lvl=%d\n",
77
               x->content_disposition, x->filename, x->fpos, x->size,
78
               x->level );
79
    dump_mimeinfo (".main", x->main );
80
    dump_mimeinfo (".sub", x->sub );
81
    dump_mimeinfo (".next", x->next );
82
    debug_print ("MimeInfo[.parent] %p\n", x ); 
83
    dump_mimeinfo (".children", x->children );
84
    dump_mimeinfo (".plaintext", x->plaintext );
85
}
86

87
static void dump_part ( MimeInfo *mimeinfo, FILE *fp )
88
{
89
    unsigned int size = mimeinfo->size;
90
    int c;
91

92
    if (fseek (fp, mimeinfo->fpos, SEEK_SET)) {
93
        debug_print ("dump_part: fseek error\n");
94
        return;
95
    }
96

97
    debug_print ("--- begin dump_part ----\n");
98
    while (size-- && (c = getc (fp)) != EOF) 
99
        putc (c, stderr);
100
    if (ferror (fp))
101
        debug_print ("dump_part: read error\n");
102
    debug_print ("--- end dump_part ----\n");
103
}
104
#endif
105

    
106
void
107
rfc2015_disable_all (void)
108
{
109
    /* FIXME: set a flag, so that we don't bother the user with failed
110
     * gpgme messages */
111
}
112

    
113

    
114
void
115
rfc2015_secure_remove (const char *fname)
116
{
117
    if (!fname)
118
        return;
119
    /* fixme: overwrite the file first */
120
    remove (fname);
121
}
122

    
123

    
124
static const gchar *
125
sig_status_to_string (GpgmeSigStat status)
126
{
127
    const gchar *result;
128

    
129
    switch (status) {
130
      case GPGME_SIG_STAT_NONE:
131
        result = _("Oops: Signature not verified");
132
        break;
133
      case GPGME_SIG_STAT_NOSIG:
134
        result = _("No signature found");
135
        break;
136
      case GPGME_SIG_STAT_GOOD:
137
        result = _("Good signature");
138
        break;
139
      case GPGME_SIG_STAT_BAD:
140
        result = _("BAD signature");
141
        break;
142
      case GPGME_SIG_STAT_NOKEY:
143
        result = _("No public key to verify the signature");
144
        break;
145
      case GPGME_SIG_STAT_ERROR:
146
        result = _("Error verifying the signature");
147
        break;
148
      case GPGME_SIG_STAT_DIFF:
149
        result = _("Different results for signatures");
150
        break;
151
      default:
152
        result = _("Error: Unknown status");
153
        break;
154
    }
155

    
156
    return result;
157
}
158

    
159
static const gchar *
160
sig_status_with_name (GpgmeSigStat status)
161
{
162
    const gchar *result;
163

    
164
    switch (status) {
165
      case GPGME_SIG_STAT_NONE:
166
        result = _("Oops: Signature not verified");
167
        break;
168
      case GPGME_SIG_STAT_NOSIG:
169
        result = _("No signature found");
170
        break;
171
      case GPGME_SIG_STAT_GOOD:
172
        result = _("Good signature from \"%s\"");
173
        break;
174
      case GPGME_SIG_STAT_BAD:
175
        result = _("BAD signature from \"%s\"");
176
        break;
177
      case GPGME_SIG_STAT_NOKEY:
178
        result = _("No public key to verify the signature");
179
        break;
180
      case GPGME_SIG_STAT_ERROR:
181
        result = _("Error verifying the signature");
182
        break;
183
      case GPGME_SIG_STAT_DIFF:
184
        result = _("Different results for signatures");
185
        break;
186
      default:
187
        result = _("Error: Unknown status");
188
        break;
189
    }
190

    
191
    return result;
192
}
193

    
194
static void
195
sig_status_for_key(GString *str, GpgmeCtx ctx, GpgmeSigStat status, 
196
                   GpgmeKey key, const gchar *fpr)
197
{
198
        gint idx = 0;
199
        const char *uid;
200

    
201
        uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, NULL, idx);
202
        if (uid == NULL) {
203
                g_string_sprintfa (str, "%s\n",
204
                                   sig_status_to_string (status));
205
                if ((fpr != NULL) && (*fpr != '\0'))
206
                        g_string_sprintfa (str, "Key fingerprint: %s\n", fpr);
207
                g_string_append (str, _("Cannot find user ID for this key."));
208
                return;
209
        }
210
        g_string_sprintfa (str, sig_status_with_name (status), uid);
211
        g_string_append (str, "\n");
212

    
213
        while (1) {
214
                uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID,
215
                                                 NULL, ++idx);
216
                if (uid == NULL)
217
                        break;
218
                g_string_sprintfa (str, _("                aka \"%s\"\n"),
219
                                   uid);
220
        }
221
}
222

    
223
static gchar *
224
sig_status_full (GpgmeCtx ctx)
225
{
226
        GString *str;
227
        gint sig_idx = 0;
228
        GpgmeError err;
229
        GpgmeSigStat status;
230
        GpgmeKey key;
231
        const char *fpr;
232
        time_t created;
233
        struct tm *ctime_val;
234
        char ctime_str[80];
235
        gchar *retval;
236

    
237
        str = g_string_new ("");
238

    
239
        fpr = gpgme_get_sig_status (ctx, sig_idx, &status, &created);
240
        while (fpr != NULL) {
241
                if (created != 0) {
242
                        ctime_val = localtime (&created);
243
                        strftime (ctime_str, sizeof (ctime_str), "%c", 
244
                                  ctime_val);
245
                        g_string_sprintfa (str,
246
                                           _("Signature made at %s\n"),
247
                                           ctime_str);
248
                }
249
                err = gpgme_get_sig_key (ctx, sig_idx, &key);
250
                if (err != 0) {
251
                        g_string_sprintfa (str, "%s\n",
252
                                           sig_status_to_string (status));
253
                        if ((fpr != NULL) && (*fpr != '\0'))
254
                                g_string_sprintfa (str, 
255
                                                   _("Key fingerprint: %s\n"),
256
                                                   fpr);
257
                } else {
258
                        sig_status_for_key (str, ctx, status, key, fpr);
259
                        gpgme_key_unref (key);
260
                }
261
                g_string_append (str, "\n\n");
262

    
263
                fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created);
264
        }
265

    
266
        retval = str->str;
267
        g_string_free (str, FALSE);
268
        return retval;
269
}
270

    
271
static void check_signature (MimeInfo *mimeinfo, MimeInfo *partinfo, FILE *fp)
272
{
273
    GpgmeCtx ctx = NULL;
274
    GpgmeError err;
275
    GpgmeData sig = NULL, text = NULL;
276
    GpgmeSigStat status = GPGME_SIG_STAT_NONE;
277
    GpgmegtkSigStatus statuswindow = NULL;
278
    const char *result = NULL;
279
    gchar *tmp_file;
280
    gint n_exclude_chars = 0;
281

    
282
    if (prefs_common.gpg_signature_popup)
283
        statuswindow = gpgmegtk_sig_status_create ();
284

    
285
    err = gpgme_new (&ctx);
286
    if (err) {
287
        debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
288
        goto leave;
289
    }
290

    
291
    /* don't include the last empty line.
292
       It does not belong to the signed text */
293
    if (mimeinfo->children->size > 0) {
294
        if (fseek(fp, mimeinfo->children->fpos + mimeinfo->children->size - 1,
295
                  SEEK_SET) < 0) {
296
            perror("fseek");
297
            goto leave;
298
        }
299
        if (fgetc(fp) == '\n') {
300
            n_exclude_chars++;
301
            if (mimeinfo->children->size > 1) {
302
                if (fseek(fp, mimeinfo->children->fpos + mimeinfo->children->size - 2,
303
                          SEEK_SET) < 0) {
304
                    perror("fseek");
305
                    goto leave;
306
                }
307
                if (fgetc(fp) == '\r')
308
                    n_exclude_chars++;
309
            }
310
        }
311
    }
312

    
313
    /* canonicalize the file part. */
314
    tmp_file = get_tmp_file();
315
    if (copy_file_part(fp, mimeinfo->children->fpos,
316
                       mimeinfo->children->size - n_exclude_chars,
317
                       tmp_file) < 0) {
318
        g_free(tmp_file);
319
        goto leave;
320
    }
321
    if (canonicalize_file_replace(tmp_file) < 0) {
322
        unlink(tmp_file);
323
        g_free(tmp_file);
324
        goto leave;
325
    }
326

    
327
    err = gpgme_data_new_from_file(&text, tmp_file, 1);
328

    
329
    unlink(tmp_file);
330
    g_free(tmp_file);
331

    
332
    if (!err)
333
        err = gpgme_data_new_from_filepart (&sig, NULL, fp,
334
                                            partinfo->fpos, partinfo->size);
335
    if (err) {
336
        debug_print ("gpgme_data_new_from_filepart failed: %s\n",
337
                   gpgme_strerror (err));
338
        goto leave;
339
    }
340

    
341
    err = gpgme_op_verify (ctx, sig, text, &status);
342
    if (err)  {
343
        debug_print ("gpgme_op_verify failed: %s\n", gpgme_strerror (err));
344
        goto leave;
345
    }
346

    
347
    /* FIXME: check what the heck this sig_status_full stuff is.
348
     * it should better go into sigstatus.c */
349
    g_free (partinfo->sigstatus_full);
350
    partinfo->sigstatus_full = sig_status_full (ctx);
351

    
352
leave:
353
    result = gpgmegtk_sig_status_to_string(status);
354
    debug_print("verification status: %s\n", result);
355
    if (prefs_common.gpg_signature_popup)
356
        gpgmegtk_sig_status_update (statuswindow, ctx);
357

    
358
    g_free (partinfo->sigstatus);
359
    partinfo->sigstatus = g_strdup (result);
360

    
361
    gpgme_data_release (sig);
362
    gpgme_data_release (text);
363
    gpgme_release (ctx);
364
    if (prefs_common.gpg_signature_popup)
365
        gpgmegtk_sig_status_destroy (statuswindow);
366
}
367

    
368
/*
369
 * Copy a gpgme data object to a temporary file and
370
 * return this filename 
371
 */
372
#if 0
373
static char *
374
copy_gpgmedata_to_temp (GpgmeData data, guint *length)
375
{
376
    static int id;
377
    char *tmp;
378
    FILE *fp;
379
    char buf[100];
380
    size_t nread;
381
    GpgmeError err;
382
    
383
    tmp = g_strdup_printf("%s%cgpgtmp.%08x",
384
                          get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id );
385

386
    if ((fp = fopen(tmp, "wb")) == NULL) {
387
        FILE_OP_ERROR(tmp, "fopen");
388
        g_free(tmp);
389
        return NULL;
390
    }
391

392
    err = gpgme_data_rewind ( data );
393
    if (err)
394
        debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
395

396
    while (!(err = gpgme_data_read (data, buf, 100, &nread))) {
397
        fwrite ( buf, nread, 1, fp );
398
    }
399

400
    if (err != GPGME_EOF)
401
        debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
402

403
    fclose (fp);
404
    *length = nread;
405

406
    return tmp;
407
}
408
#endif
409

    
410
static GpgmeData
411
pgp_decrypt (MimeInfo *partinfo, FILE *fp)
412
{
413
    GpgmeCtx ctx = NULL;
414
    GpgmeError err;
415
    GpgmeData cipher = NULL, plain = NULL;
416
    struct passphrase_cb_info_s info;
417

    
418
    memset (&info, 0, sizeof info);
419

    
420
    err = gpgme_new (&ctx);
421
    if (err) {
422
        debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
423
        goto leave;
424
    }
425

    
426
    err = gpgme_data_new_from_filepart (&cipher, NULL, fp,
427
                                        partinfo->fpos, partinfo->size);
428
    if (err) {
429
        debug_print ("gpgme_data_new_from_filepart failed: %s\n",
430
                     gpgme_strerror (err));
431
        goto leave;
432
    }
433

    
434
    err = gpgme_data_new (&plain);
435
    if (err) {
436
        debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
437
        goto leave;
438
    }
439

    
440
    if (!getenv("GPG_AGENT_INFO")) {
441
        info.c = ctx;
442
        gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
443
    } 
444

    
445
    err = gpgme_op_decrypt (ctx, cipher, plain);
446

    
447
leave:
448
    gpgme_data_release (cipher);
449
    if (err) {
450
        gpgmegtk_free_passphrase();
451
        debug_print ("decryption failed: %s\n", gpgme_strerror (err));
452
        gpgme_data_release (plain);
453
        plain = NULL;
454
    }
455
    else
456
        debug_print ("** decryption succeeded\n");
457

    
458
    gpgme_release (ctx);
459
    return plain;
460
}
461

    
462
MimeInfo * rfc2015_find_signature (MimeInfo *mimeinfo)
463
{
464
    MimeInfo *partinfo;
465
    int n = 0;
466

    
467
    if (!mimeinfo)
468
        return NULL;
469
    if (g_strcasecmp (mimeinfo->content_type, "multipart/signed"))
470
        return NULL;
471

    
472
    debug_print ("** multipart/signed encountered\n");
473

    
474
    /* check that we have at least 2 parts of the correct type */
475
    for (partinfo = mimeinfo->children;
476
         partinfo != NULL; partinfo = partinfo->next) {
477
        if (++n > 1  && !g_strcasecmp (partinfo->content_type,
478
                                       "application/pgp-signature"))
479
            break;
480
    }
481

    
482
    return partinfo;
483
}
484

    
485
gboolean rfc2015_has_signature (MimeInfo *mimeinfo)
486
{
487
    return rfc2015_find_signature (mimeinfo) != NULL;
488
}
489

    
490
void rfc2015_check_signature (MimeInfo *mimeinfo, FILE *fp)
491
{
492
    MimeInfo *partinfo;
493

    
494
    partinfo = rfc2015_find_signature (mimeinfo);
495
    if (!partinfo)
496
        return;
497

    
498
#if 0
499
    g_message ("** yep, it is a pgp signature");
500
    dump_mimeinfo ("gpg-signature", partinfo );
501
    dump_part (partinfo, fp );
502
    dump_mimeinfo ("signed text", mimeinfo->children );
503
    dump_part (mimeinfo->children, fp);
504
#endif
505

    
506
    check_signature (mimeinfo, partinfo, fp);
507
}
508

    
509
int rfc2015_is_encrypted (MimeInfo *mimeinfo)
510
{
511
    if (!mimeinfo || mimeinfo->mime_type != MIME_MULTIPART)
512
        return 0;
513
    if (g_strcasecmp (mimeinfo->content_type, "multipart/encrypted"))
514
        return 0;
515
    /* fixme: we should check the protocol parameter */
516
    return 1;
517
}
518

    
519
gboolean rfc2015_msg_is_encrypted (const gchar *file)
520
{
521
        FILE *fp;
522
        MimeInfo *mimeinfo;
523
        int ret;
524

    
525
        if ((fp = fopen(file, "rb")) == NULL)
526
                return FALSE;
527

    
528
        mimeinfo = procmime_scan_mime_header(fp);
529
        if(!mimeinfo) {
530
                fclose(fp);
531
                return FALSE;
532
        }
533

    
534
        ret = rfc2015_is_encrypted(mimeinfo);
535
        procmime_mimeinfo_free_all(mimeinfo);
536
        return ret != 0 ? TRUE : FALSE;
537
}
538

    
539
static int
540
name_cmp(const char *a, const char *b)
541
{
542
    for( ; *a && *b; a++, b++) {
543
        if(*a != *b
544
           && toupper(*(unsigned char *)a) != toupper(*(unsigned char *)b))
545
            return 1;
546
    }
547

    
548
    return *a != *b;
549
}
550

    
551
static int
552
headerp(char *p, char **names)
553
{
554
    int i, c;
555
    char *p2;
556

    
557
    p2 = strchr(p, ':');
558
    if(!p2 || p == p2) {
559
        return 0;
560
    }
561
    if(p2[-1] == ' ' || p2[-1] == '\t') {
562
        return 0;
563
    }
564

    
565
    if(!names[0])
566
        return 1;  
567

    
568
    c = *p2;
569
    *p2 = 0;
570
    for(i = 0 ; names[i] != NULL; i++) {
571
        if(!name_cmp (names[i], p))
572
            break;
573
    }
574
    *p2 = c;
575

    
576
    return names[i] != NULL;
577
}
578

    
579

    
580
#define DECRYPTION_ABORT() \
581
{ \
582
    procmime_mimeinfo_free_all(tmpinfo); \
583
    msginfo->decryption_failed = 1; \
584
    return; \
585
}
586

    
587
void rfc2015_decrypt_message (MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
588
{
589
    static int id;
590
    MimeInfo *tmpinfo, *partinfo;
591
    int ver_ok = 0;
592
    char *fname;
593
    GpgmeData plain;
594
    FILE *dstfp;
595
    size_t nread;
596
    char buf[BUFFSIZE];
597
    int in_cline;
598
    GpgmeError err;
599

    
600
    g_return_if_fail (msginfo != NULL);
601
    g_return_if_fail (mimeinfo != NULL);
602
    g_return_if_fail (fp != NULL);
603
    g_return_if_fail (mimeinfo->mime_type == MIME_MULTIPART);
604

    
605
    debug_print ("** decrypting multipart/encrypted message\n");
606

    
607
    /* skip headers */
608
    if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
609
        perror("fseek");
610
    tmpinfo = procmime_scan_mime_header(fp);
611
    if (!tmpinfo || tmpinfo->mime_type != MIME_MULTIPART) {
612
        DECRYPTION_ABORT();
613
    }
614

    
615
    procmime_scan_multipart_message(tmpinfo, fp);
616

    
617
    /* check that we have the 2 parts */
618
    partinfo = tmpinfo->children;
619
    if (!partinfo || !partinfo->next) {
620
        DECRYPTION_ABORT();
621
    }
622
    if (!g_strcasecmp (partinfo->content_type, "application/pgp-encrypted")) {
623
        /* Fixme: check that the version is 1 */
624
        ver_ok = 1;
625
    }
626
    partinfo = partinfo->next;
627
    if (ver_ok &&
628
        !g_strcasecmp (partinfo->content_type, "application/octet-stream")) {
629
        if (partinfo->next)
630
            g_warning ("oops: pgp_encrypted with more than 2 parts");
631
    }
632
    else {
633
        DECRYPTION_ABORT();
634
    }
635

    
636
    debug_print ("** yep, it is pgp encrypted\n");
637

    
638
    plain = pgp_decrypt (partinfo, fp);
639
    if (!plain) {
640
        DECRYPTION_ABORT();
641
    }
642

    
643
    fname = g_strdup_printf("%s%cplaintext.%08x",
644
                            get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
645

    
646
    if ((dstfp = fopen(fname, "wb")) == NULL) {
647
        FILE_OP_ERROR(fname, "fopen");
648
        g_free(fname);
649
        DECRYPTION_ABORT();
650
    }
651

    
652
    /* write the orginal header to the new file */
653
    if (fseek(fp, tmpinfo->fpos, SEEK_SET) < 0)
654
        perror("fseek");
655

    
656
    in_cline = 0;
657
    while (fgets(buf, sizeof(buf), fp)) {
658
        if (headerp (buf, content_names)) {
659
            in_cline = 1;
660
            continue;
661
        }
662
        if (in_cline) {
663
            if (buf[0] == ' ' || buf[0] == '\t')
664
                continue;
665
            in_cline = 0;
666
        }
667
        if (buf[0] == '\r' || buf[0] == '\n')
668
            break;
669
        fputs (buf, dstfp);
670
    }
671

    
672
    err = gpgme_data_rewind (plain);
673
    if (err)
674
        debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
675

    
676
    while (!(err = gpgme_data_read (plain, buf, sizeof(buf), &nread))) {
677
        fwrite (buf, nread, 1, dstfp);
678
    }
679

    
680
    if (err != GPGME_EOF) {
681
        debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
682
    }
683

    
684
    fclose (dstfp);
685
    procmime_mimeinfo_free_all(tmpinfo);
686

    
687
    msginfo->plaintext_file = fname;
688
    msginfo->decryption_failed = 0;
689
}
690

    
691
#undef DECRYPTION_ABORT
692

    
693

    
694
/*
695
 * plain contains an entire mime object.
696
 * Encrypt it and return an GpgmeData object with the encrypted version of
697
 * the file or NULL in case of error.
698
 */
699
static GpgmeData
700
pgp_encrypt ( GpgmeData plain, GpgmeRecipients rset )
701
{
702
    GpgmeCtx ctx = NULL;
703
    GpgmeError err;
704
    GpgmeData cipher = NULL;
705

    
706
    err = gpgme_new (&ctx);
707
    if (!err)
708
        err = gpgme_data_new (&cipher);
709
    if (!err) {
710
        gpgme_set_armor (ctx, 1);
711
        err = gpgme_op_encrypt (ctx, rset, plain, cipher);
712
    }
713

    
714
    if (err) {
715
        debug_print ("encryption failed: %s\n", gpgme_strerror (err));
716
        gpgme_data_release (cipher);
717
        cipher = NULL;
718
    }
719
    else {
720
        debug_print ("** encryption succeeded\n");
721
    }
722

    
723
    gpgme_release (ctx);
724
    return cipher;
725
}
726

    
727
/*
728
 * Create and return a list of keys matching a key id
729
 */
730

    
731
GSList *rfc2015_create_signers_list (const char *keyid)
732
{
733
        GSList *key_list = NULL;
734
        GpgmeCtx list_ctx = NULL;
735
        GSList *p;
736
        GpgmeError err;
737
        GpgmeKey key;
738

    
739
        err = gpgme_new (&list_ctx);
740
        if (err)
741
                goto leave;
742
        err = gpgme_op_keylist_start (list_ctx, keyid, 1);
743
        if (err)
744
                goto leave;
745
        while ( !(err = gpgme_op_keylist_next (list_ctx, &key)) ) {
746
                key_list = g_slist_append (key_list, key);
747
        }
748
        if (err != GPGME_EOF)
749
                goto leave;
750
        err = 0;
751
        if (key_list == NULL) {
752
                debug_print ("no keys found for keyid \"%s\"\n", keyid);
753
        }
754

    
755
leave:
756
        if (err) {
757
                debug_print ("rfc2015_create_signers_list failed: %s\n", gpgme_strerror (err));
758
                for (p = key_list; p != NULL; p = p->next)
759
                        gpgme_key_unref ((GpgmeKey) p->data);
760
                g_slist_free (key_list);
761
        }
762
        if (list_ctx)
763
                gpgme_release (list_ctx);
764
        return err ? NULL : key_list;
765
}
766

    
767
/*
768
 * Encrypt the file by extracting all recipients and finding the
769
 * encryption keys for all of them.  The file content is then replaced
770
 * by the encrypted one.  */
771
int
772
rfc2015_encrypt (const char *file, GSList *recp_list, gboolean ascii_armored)
773
{
774
    FILE *fp = NULL;
775
    char buf[BUFFSIZE];
776
    int i, clineidx, saved_last;
777
    char *clines[3] = {NULL};
778
    GpgmeError err;
779
    GpgmeData header = NULL;
780
    GpgmeData plain = NULL;
781
    GpgmeData cipher = NULL;
782
    GpgmeRecipients rset = NULL;
783
    size_t nread;
784
    int mime_version_seen = 0;
785
    char *boundary;
786

    
787
    boundary = generate_mime_boundary ("Encrypt");
788

    
789
    /* Create the list of recipients */
790
    rset = gpgmegtk_recipient_selection (recp_list);
791
    if (!rset) {
792
        debug_print ("error creating recipient list\n" );
793
        goto failure;
794
    }
795

    
796
    /* Open the source file */
797
    if ((fp = fopen(file, "rb")) == NULL) {
798
        FILE_OP_ERROR(file, "fopen");
799
        goto failure;
800
    }
801

    
802
    err = gpgme_data_new (&header);
803
    if (!err)
804
        err = gpgme_data_new (&plain);
805
    if (err) {
806
        debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
807
        goto failure;
808
    }
809

    
810
    /* get the content header lines from the source */
811
    clineidx = 0;
812
    saved_last = 0;
813
    while (!err && fgets(buf, sizeof(buf), fp)) {
814
        /* fixme: check for overlong lines */
815
        if (headerp (buf, content_names)) {
816
            if (clineidx >= DIM (clines)) {
817
                debug_print ("rfc2015_encrypt: too many content lines\n");
818
                goto failure;
819
            }
820
            clines[clineidx++] = g_strdup (buf);
821
            saved_last = 1;
822
            continue;
823
        }
824
        if (saved_last) {
825
            if (*buf == ' ' || *buf == '\t') {
826
                char *last = clines[clineidx - 1];
827
                clines[clineidx - 1] = g_strconcat (last, buf, NULL);
828
                g_free (last);
829
                continue;
830
            }
831
            saved_last = 0;
832
        }
833

    
834
        if (headerp (buf, mime_version_name)) 
835
            mime_version_seen = 1;
836

    
837
        if (buf[0] == '\r' || buf[0] == '\n')
838
            break;
839
        err = gpgme_data_write (header, buf, strlen (buf));
840
    }
841
    if (ferror (fp)) {
842
        FILE_OP_ERROR (file, "fgets");
843
        goto failure;
844
    }
845

    
846
    /* write them to the temp data and add the rest of the message */
847
    for (i = 0; !err && i < clineidx; i++) {
848
        debug_print ("%% %s:%d: cline=`%s'", __FILE__ ,__LINE__, clines[i]);
849
        err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
850
    }
851
    if (!err)
852
        err = gpgme_data_write (plain, "\r\n", 2);
853
    while (!err && fgets(buf, sizeof(buf), fp)) {
854
        err = gpgme_data_write (plain, buf, strlen (buf));
855
    }
856
    if (ferror (fp)) {
857
        FILE_OP_ERROR (file, "fgets");
858
        goto failure;
859
    }
860
    if (err) {
861
        debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
862
        goto failure;
863
    }
864

    
865
    cipher = pgp_encrypt (plain, rset);
866
    gpgme_data_release (plain); plain = NULL;
867
    gpgme_recipients_release (rset); rset = NULL;
868
    if (!cipher)
869
        goto failure;
870

    
871
    /* we have the encrypted message available in cipher and now we
872
     * are going to rewrite the source file. To be sure that file has
873
     * been truncated we use an approach which should work everywhere:
874
     * close the file and then reopen it for writing. It is important
875
     * that this works, otherwise it may happen that parts of the
876
     * plaintext are still in the file (The encrypted stuff is, due to
877
     * compression, usually shorter than the plaintext). 
878
     * 
879
     * Yes, there is a race condition here, but everyone, who is so
880
     * stupid to store the temp file with the plaintext in a public
881
     * directory has to live with this anyway. */
882
    if (fclose (fp)) {
883
        FILE_OP_ERROR(file, "fclose");
884
        goto failure;
885
    }
886
    if ((fp = fopen(file, "wb")) == NULL) {
887
        FILE_OP_ERROR(file, "fopen");
888
        goto failure;
889
    }
890

    
891
    /* Write the header, append new content lines, part 1 and part 2 header */
892
    err = gpgme_data_rewind (header);
893
    if (err) {
894
        debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
895
        goto failure;
896
    }
897
    while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
898
        fwrite (buf, nread, 1, fp);
899
    }
900
    if (err != GPGME_EOF) {
901
        debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
902
        goto failure;
903
    }
904
    if (ferror (fp)) {
905
        FILE_OP_ERROR (file, "fwrite");
906
        goto failure;
907
    }
908
    gpgme_data_release (header); header = NULL;
909
    
910
    if (!mime_version_seen) 
911
        fputs ("MIME-Version: 1\r\n", fp);
912

    
913
    if (ascii_armored) {
914
        fprintf(fp, 
915
            "Content-Type: text/plain; charset=US-ASCII\r\n"
916
            "Content-Transfer-Encoding: 7bit\r\n"  
917
            "\r\n");
918
    } else {
919
        fprintf (fp,
920
                "Content-Type: multipart/encrypted;"
921
                " protocol=\"application/pgp-encrypted\";\r\n"
922
                " boundary=\"%s\"\r\n"
923
                "\r\n"
924
                "--%s\r\n"
925
                "Content-Type: application/pgp-encrypted\r\n"
926
                "\r\n"
927
                "Version: 1\r\n"
928
                "\r\n"
929
                "--%s\r\n"
930
                "Content-Type: application/octet-stream\r\n"
931
                "\r\n",
932
                boundary, boundary, boundary);
933
    }
934

    
935
    /* append the encrypted stuff */
936
    err = gpgme_data_rewind (cipher);
937
    if (err) {
938
        debug_print ("** gpgme_data_rewind on cipher failed: %s\n",
939
                   gpgme_strerror (err));
940
        goto failure;
941
    }
942

    
943
    while (!(err = gpgme_data_read (cipher, buf, BUFFSIZE, &nread))) {
944
        fwrite (buf, nread, 1, fp);
945
    }
946
    if (err != GPGME_EOF) {
947
        debug_print ("** gpgme_data_read failed: %s\n", gpgme_strerror (err));
948
        goto failure;
949
    }
950

    
951
    /* and the final boundary */
952
    if (!ascii_armored) {
953
        fprintf (fp,
954
                 "\r\n"
955
                 "--%s--\r\n",
956
                 boundary);
957
    }
958
    fflush (fp);
959
    if (ferror (fp)) {
960
        FILE_OP_ERROR (file, "fwrite");
961
        goto failure;
962
    }
963
    fclose (fp);
964
    gpgme_data_release (cipher);
965
    return 0;
966

    
967
failure:
968
    if (fp) 
969
        fclose (fp);
970
    gpgme_data_release (header);
971
    gpgme_data_release (plain);
972
    gpgme_data_release (cipher);
973
    gpgme_recipients_release (rset);
974
    g_free (boundary);
975
    return -1; /* error */
976
}
977

    
978
/* 
979
 * plain contains an entire mime object.  Sign it and return an
980
 * GpgmeData object with the signature of it or NULL in case of error.
981
 * r_siginfo returns an XML object with information about the signature.
982
 */
983
static GpgmeData
984
pgp_sign (GpgmeData plain, GSList *key_list, gboolean clearsign,
985
          char **r_siginfo)
986
{
987
    GSList *p;
988
    GpgmeCtx ctx = NULL;
989
    GpgmeError err;
990
    GpgmeData sig = NULL;
991
    struct passphrase_cb_info_s info;
992

    
993
    *r_siginfo = NULL;
994
    memset (&info, 0, sizeof info);
995

    
996
    err = gpgme_new (&ctx);
997
    if (err)
998
        goto leave;
999
    err = gpgme_data_new (&sig);
1000
    if (err)
1001
        goto leave;
1002

    
1003
    if (!getenv("GPG_AGENT_INFO")) {
1004
        info.c = ctx;
1005
        gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
1006
    }
1007
    gpgme_set_textmode (ctx, 1);
1008
    gpgme_set_armor (ctx, 1);
1009
    gpgme_signers_clear (ctx);
1010
    for (p = key_list; p != NULL; p = p->next) {
1011
        err = gpgme_signers_add (ctx, (GpgmeKey) p->data);
1012
        if (err)
1013
            goto leave;
1014
    }
1015
    for (p = key_list; p != NULL; p = p->next)
1016
        gpgme_key_unref ((GpgmeKey) p->data);
1017
    g_slist_free (key_list);
1018

    
1019
    if (err)
1020
        goto leave;
1021
    err = gpgme_op_sign
1022
        (ctx, plain, sig,
1023
         clearsign ? GPGME_SIG_MODE_CLEAR : GPGME_SIG_MODE_DETACH);
1024
    if (!err)
1025
        *r_siginfo = gpgme_get_op_info (ctx, 0);
1026

    
1027
leave:
1028
    if (err) {
1029
        gpgmegtk_free_passphrase();
1030
        debug_print ("signing failed: %s\n", gpgme_strerror (err));
1031
        gpgme_data_release (sig);
1032
        sig = NULL;
1033
    }
1034
    else {
1035
        debug_print ("signing succeeded\n");
1036
    }
1037

    
1038
    gpgme_release (ctx);
1039
    return sig;
1040
}
1041

    
1042
/*
1043
 * Find TAG in XML and return a pointer into xml set just behind the
1044
 * closing angle.  Return NULL if not found. 
1045
 */
1046
static const char *
1047
find_xml_tag (const char *xml, const char *tag)
1048
{
1049
    int taglen = strlen (tag);
1050
    const char *s = xml;
1051
 
1052
    while ( (s = strchr (s, '<')) ) {
1053
        s++;
1054
        if (!strncmp (s, tag, taglen)) {
1055
            const char *s2 = s + taglen;
1056
            if (*s2 == '>' || isspace (*(const unsigned char*)s2) ) {
1057
                /* found */
1058
                while (*s2 && *s2 != '>') /* skip attributes */
1059
                    s2++;
1060
                /* fixme: do need to handle angles inside attribute vallues? */
1061
                return *s2? (s2+1):NULL;
1062
            }
1063
        }
1064
        while (*s && *s != '>') /* skip to end of tag */
1065
            s++;
1066
    }
1067
    return NULL;
1068
}
1069

    
1070

    
1071
/*
1072
 * Extract the micalg from an GnupgOperationInfo XML container.
1073
 */
1074
static char *
1075
extract_micalg (char *xml)
1076
{
1077
    const char *s;
1078

    
1079
    s = find_xml_tag (xml, "GnupgOperationInfo");
1080
    if (s) {
1081
        const char *s_end = find_xml_tag (s, "/GnupgOperationInfo");
1082
        s = find_xml_tag (s, "signature");
1083
        if (s && s_end && s < s_end) {
1084
            const char *s_end2 = find_xml_tag (s, "/signature");
1085
            if (s_end2 && s_end2 < s_end) {
1086
                s = find_xml_tag (s, "micalg");
1087
                if (s && s < s_end2) {
1088
                    s_end = strchr (s, '<');
1089
                    if (s_end) {
1090
                        char *p = g_malloc (s_end - s + 1);
1091
                        memcpy (p, s, s_end - s);
1092
                        p[s_end-s] = 0;
1093
                        return p;
1094
                    }
1095
                }
1096
            }
1097
        }
1098
    }
1099
    return NULL;
1100
}
1101

    
1102

    
1103
/*
1104
 * Sign the file and replace its content with the signed one.
1105
 */
1106
int
1107
rfc2015_sign (const char *file, GSList *key_list)
1108
{
1109
    FILE *fp = NULL;
1110
    char buf[BUFFSIZE];
1111
    int i, clineidx, saved_last;
1112
    char *clines[3] = {NULL};
1113
    GpgmeError err;
1114
    GpgmeData header = NULL;
1115
    GpgmeData plain = NULL;
1116
    GpgmeData sigdata = NULL;
1117
    size_t nread;
1118
    int mime_version_seen = 0;
1119
    char *boundary;
1120
    char *micalg = NULL;
1121
    char *siginfo;
1122

    
1123
    boundary = generate_mime_boundary ("Signature");
1124

    
1125
    /* Open the source file */
1126
    if ((fp = fopen(file, "rb")) == NULL) {
1127
        FILE_OP_ERROR(file, "fopen");
1128
        goto failure;
1129
    }
1130

    
1131
    err = gpgme_data_new (&header);
1132
    if (!err)
1133
        err = gpgme_data_new (&plain);
1134
    if (err) {
1135
        debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
1136
        goto failure;
1137
    }
1138

    
1139
    /* get the content header lines from the source */
1140
    clineidx = 0;
1141
    saved_last = 0;
1142
    while (!err && fgets(buf, sizeof(buf), fp)) {
1143
        /* fixme: check for overlong lines */
1144
        if (headerp (buf, content_names)) {
1145
            if (clineidx >= DIM (clines)) {
1146
                debug_print ("rfc2015_sign: too many content lines\n");
1147
                goto failure;
1148
            }
1149
            clines[clineidx++] = g_strdup (buf);
1150
            saved_last = 1;
1151
            continue;
1152
        }
1153
        if (saved_last) {
1154
            if (*buf == ' ' || *buf == '\t') {
1155
                char *last = clines[clineidx - 1];
1156
                clines[clineidx - 1] = g_strconcat (last, buf, NULL);
1157
                g_free (last);
1158
                continue;
1159
            }
1160
            saved_last = 0;
1161
        }
1162

    
1163
        if (headerp (buf, mime_version_name)) 
1164
            mime_version_seen = 1;
1165

    
1166
        if (buf[0] == '\r' || buf[0] == '\n')
1167
            break;
1168
        err = gpgme_data_write (header, buf, strlen (buf));
1169
    }
1170
    if (ferror (fp)) {
1171
        FILE_OP_ERROR (file, "fgets");
1172
        goto failure;
1173
    }
1174

    
1175
    /* write them to the temp data and add the rest of the message */
1176
    for (i = 0; !err && i < clineidx; i++) {
1177
        err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
1178
    }
1179
    if (!err)
1180
        err = gpgme_data_write (plain, "\r\n", 2 );
1181
    while (!err && fgets(buf, sizeof(buf), fp)) {
1182
        err = gpgme_data_write (plain, buf, strlen (buf));
1183
    }
1184
    if (ferror (fp)) {
1185
        FILE_OP_ERROR (file, "fgets");
1186
        goto failure;
1187
    }
1188
    if (err) {
1189
        debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
1190
        goto failure;
1191
    }
1192

    
1193
    sigdata = pgp_sign (plain, key_list, FALSE, &siginfo); 
1194
    if (siginfo) {
1195
        micalg = extract_micalg (siginfo);
1196
        free (siginfo);
1197
    }
1198
    if (!sigdata) 
1199
        goto failure;
1200

    
1201
    /* we have the signed message available in sigdata and now we are
1202
     * going to rewrite the original file. To be sure that file has
1203
     * been truncated we use an approach which should work everywhere:
1204
     * close the file and then reopen it for writing. */
1205
    if (fclose (fp)) {
1206
        FILE_OP_ERROR(file, "fclose");
1207
        goto failure;
1208
    }
1209
    if ((fp = fopen(file, "wb")) == NULL) {
1210
        FILE_OP_ERROR(file, "fopen");
1211
        goto failure;
1212
    }
1213

    
1214
    /* Write the rfc822 header and add new content lines */
1215
    err = gpgme_data_rewind (header);
1216
    if (err)
1217
        debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
1218
    while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
1219
        fwrite (buf, nread, 1, fp);
1220
    }
1221
    if (err != GPGME_EOF) {
1222
        debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1223
        goto failure;
1224
    }
1225
    if (ferror (fp)) {
1226
        FILE_OP_ERROR (file, "fwrite");
1227
        goto failure;
1228
    }
1229
    gpgme_data_release (header);
1230
    header = NULL;
1231

    
1232
    if (!mime_version_seen) 
1233
        fputs ("MIME-Version: 1.0\r\n", fp);
1234
    fprintf (fp, "Content-Type: multipart/signed; "
1235
             "protocol=\"application/pgp-signature\";\r\n");
1236
    if (micalg)
1237
        fprintf (fp, " micalg=\"%s\";\r\n", micalg);
1238
    fprintf (fp, " boundary=\"%s\"\r\n", boundary);
1239

    
1240
    /* Part 1: signed material */
1241
    fprintf (fp, "\r\n"
1242
                 "--%s\r\n",
1243
                 boundary);
1244
    err = gpgme_data_rewind (plain);
1245
    if (err) {
1246
        debug_print ("gpgme_data_rewind on plain failed: %s\n",
1247
                   gpgme_strerror (err));
1248
        goto failure;
1249
    }
1250
    while (!(err = gpgme_data_read (plain, buf, BUFFSIZE, &nread))) {
1251
        fwrite (buf, nread, 1, fp);   
1252
    }
1253
    if (err != GPGME_EOF) {
1254
        debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1255
        goto failure;
1256
    }
1257

    
1258
    /* Part 2: signature */
1259
    fprintf (fp, "\r\n"
1260
                 "--%s\r\n",
1261
                 boundary);
1262
    fputs ("Content-Type: application/pgp-signature\r\n"
1263
           "\r\n", fp);
1264

    
1265
    err = gpgme_data_rewind (sigdata);
1266
    if (err) {
1267
        debug_print ("gpgme_data_rewind on sigdata failed: %s\n",
1268
                   gpgme_strerror (err));
1269
        goto failure;
1270
    }
1271

    
1272
    while (!(err = gpgme_data_read (sigdata, buf, BUFFSIZE, &nread))) {
1273
        fwrite (buf, nread, 1, fp);
1274
    }
1275
    if (err != GPGME_EOF) {
1276
        debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1277
        goto failure;
1278
    }
1279

    
1280
    /* Final boundary */
1281
    fprintf (fp, "\r\n"
1282
                 "--%s--\r\n",
1283
                 boundary);
1284
    fflush (fp);
1285
    if (ferror (fp)) {
1286
        FILE_OP_ERROR (file, "fwrite");
1287
        goto failure;
1288
    }
1289
    fclose (fp);
1290
    gpgme_data_release (header);
1291
    gpgme_data_release (plain);
1292
    gpgme_data_release (sigdata);
1293
    g_free (boundary);
1294
    g_free (micalg);
1295
    return 0;
1296

    
1297
failure:
1298
    if (fp) 
1299
        fclose (fp);
1300
    gpgme_data_release (header);
1301
    gpgme_data_release (plain);
1302
    gpgme_data_release (sigdata);
1303
    g_free (boundary);
1304
    g_free (micalg);
1305
    return -1; /* error */
1306
}
1307

    
1308

    
1309
/*
1310
 * Sign the file with clear text and replace its content with the signed one.
1311
 */
1312
gint
1313
rfc2015_clearsign (const gchar *file, GSList *key_list)
1314
{
1315
    FILE *fp;
1316
    gchar buf[BUFFSIZE];
1317
    GpgmeError err;
1318
    GpgmeData text = NULL;
1319
    GpgmeData sigdata = NULL;
1320
    size_t nread;
1321
    gchar *siginfo;
1322

    
1323
    if ((fp = fopen(file, "rb")) == NULL) {
1324
        FILE_OP_ERROR(file, "fopen");
1325
        goto failure;
1326
    }
1327

    
1328
    err = gpgme_data_new(&text);
1329
    if (err) {
1330
        debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
1331
        goto failure;
1332
    }
1333

    
1334
    while (!err && fgets(buf, sizeof(buf), fp)) {
1335
        err = gpgme_data_write(text, buf, strlen(buf));
1336
    }
1337
    if (ferror(fp)) {
1338
        FILE_OP_ERROR(file, "fgets");
1339
        goto failure;
1340
    }
1341
    if (err) {
1342
        debug_print("gpgme_data_write failed: %s\n", gpgme_strerror(err));
1343
        goto failure;
1344
    }
1345

    
1346
    sigdata = pgp_sign(text, key_list, TRUE, &siginfo);
1347
    if (siginfo) {
1348
        g_free(siginfo);
1349
    }
1350
    if (!sigdata)
1351
        goto failure;
1352

    
1353
    if (fclose(fp) == EOF) {
1354
        FILE_OP_ERROR(file, "fclose");
1355
        fp = NULL;
1356
        goto failure;
1357
    }
1358
    if ((fp = fopen(file, "wb")) == NULL) {
1359
        FILE_OP_ERROR(file, "fopen");
1360
        goto failure;
1361
    }
1362

    
1363
    err = gpgme_data_rewind(sigdata);
1364
    if (err) {
1365
        debug_print("gpgme_data_rewind on sigdata failed: %s\n",
1366
                    gpgme_strerror(err));
1367
        goto failure;
1368
    }
1369

    
1370
    while (!(err = gpgme_data_read(sigdata, buf, sizeof(buf), &nread))) {
1371
        fwrite(buf, nread, 1, fp);
1372
    }
1373
    if (err != GPGME_EOF) {
1374
        debug_print("gpgme_data_read failed: %s\n", gpgme_strerror(err));
1375
        goto failure;
1376
    }
1377

    
1378
    if (fclose(fp) == EOF) {
1379
        FILE_OP_ERROR(file, "fclose");
1380
        fp = NULL;
1381
        goto failure;
1382
    }
1383
    gpgme_data_release(text);
1384
    gpgme_data_release(sigdata);
1385
    return 0;
1386

    
1387
failure:
1388
    if (fp)
1389
        fclose(fp);
1390
    gpgme_data_release(text);
1391
    gpgme_data_release(sigdata);
1392
    return -1;
1393
}
1394

    
1395
#endif /* USE_GPGME */