Statistics
| Revision:

root / src / rfc2015.c @ 42

History | View | Annotate | Download (35.3 kB)

1 1 hiro
/*
2 1 hiro
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 1 hiro
 * Copyright (C) 2001 Werner Koch (dd9jn)
4 1 hiro
 *
5 1 hiro
 * This program is free software; you can redistribute it and/or modify
6 1 hiro
 * it under the terms of the GNU General Public License as published by
7 1 hiro
 * the Free Software Foundation; either version 2 of the License, or
8 1 hiro
 * (at your option) any later version.
9 1 hiro
 *
10 1 hiro
 * This program is distributed in the hope that it will be useful,
11 1 hiro
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 1 hiro
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 1 hiro
 * GNU General Public License for more details.
14 1 hiro
 *
15 1 hiro
 * You should have received a copy of the GNU General Public License
16 1 hiro
 * along with this program; if not, write to the Free Software
17 1 hiro
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 1 hiro
 */
19 1 hiro
20 1 hiro
#ifdef HAVE_CONFIG_H
21 1 hiro
#  include "config.h"
22 1 hiro
#endif
23 1 hiro
24 1 hiro
#if USE_GPGME
25 1 hiro
26 1 hiro
#include "defs.h"
27 1 hiro
28 1 hiro
#include <glib.h>
29 1 hiro
#include <stdio.h>
30 1 hiro
#include <string.h>
31 1 hiro
#include <locale.h>
32 1 hiro
#include <ctype.h>
33 1 hiro
34 1 hiro
#include <gpgme.h>
35 1 hiro
36 1 hiro
#include "intl.h"
37 1 hiro
#include "procmime.h"
38 1 hiro
#include "procheader.h"
39 1 hiro
#include "base64.h"
40 1 hiro
#include "uuencode.h"
41 1 hiro
#include "unmime.h"
42 1 hiro
#include "codeconv.h"
43 1 hiro
#include "utils.h"
44 1 hiro
#include "prefs_common.h"
45 1 hiro
#include "passphrase.h"
46 1 hiro
#include "select-keys.h"
47 1 hiro
#include "sigstatus.h"
48 1 hiro
#include "rfc2015.h"
49 1 hiro
50 1 hiro
#define DIM(v)     (sizeof(v)/sizeof((v)[0]))
51 1 hiro
52 1 hiro
static char *content_names[] = {
53 1 hiro
    "Content-Type",
54 1 hiro
    "Content-Disposition",
55 1 hiro
    "Content-Transfer-Encoding",
56 1 hiro
    NULL
57 1 hiro
};
58 1 hiro
59 1 hiro
static char *mime_version_name[] = {
60 1 hiro
    "Mime-Version",
61 1 hiro
    NULL
62 1 hiro
};
63 1 hiro
64 1 hiro
#if 0
65 1 hiro
static void dump_mimeinfo (const char *text, MimeInfo *x)
66 1 hiro
{
67 1 hiro
    debug_print ("MimeInfo[%s] %p  level=%d\n",
68 1 hiro
               text, x, x? x->level:0 );
69 1 hiro
    if (!x)
70 1 hiro
        return;
71 1 hiro
72 1 hiro
    debug_print ("      enc=`%s' enc_type=%d mime_type=%d\n",
73 1 hiro
               x->encoding, x->encoding_type, x->mime_type );
74 1 hiro
    debug_print ("      cont_type=`%s' cs=`%s' name=`%s' bnd=`%s'\n",
75 1 hiro
               x->content_type, x->charset, x->name, x->boundary );
76 1 hiro
    debug_print ("      cont_disp=`%s' fname=`%s' fpos=%ld size=%u, lvl=%d\n",
77 1 hiro
               x->content_disposition, x->filename, x->fpos, x->size,
78 1 hiro
               x->level );
79 1 hiro
    dump_mimeinfo (".main", x->main );
80 1 hiro
    dump_mimeinfo (".sub", x->sub );
81 1 hiro
    dump_mimeinfo (".next", x->next );
82 1 hiro
    debug_print ("MimeInfo[.parent] %p\n", x );
83 1 hiro
    dump_mimeinfo (".children", x->children );
84 1 hiro
    dump_mimeinfo (".plaintext", x->plaintext );
85 1 hiro
}
86 1 hiro
87 1 hiro
static void dump_part ( MimeInfo *mimeinfo, FILE *fp )
88 1 hiro
{
89 1 hiro
    unsigned int size = mimeinfo->size;
90 1 hiro
    int c;
91 1 hiro
92 1 hiro
    if (fseek (fp, mimeinfo->fpos, SEEK_SET)) {
93 1 hiro
        debug_print ("dump_part: fseek error\n");
94 1 hiro
        return;
95 1 hiro
    }
96 1 hiro
97 1 hiro
    debug_print ("--- begin dump_part ----\n");
98 1 hiro
    while (size-- && (c = getc (fp)) != EOF)
99 1 hiro
        putc (c, stderr);
100 1 hiro
    if (ferror (fp))
101 1 hiro
        debug_print ("dump_part: read error\n");
102 1 hiro
    debug_print ("--- end dump_part ----\n");
103 1 hiro
}
104 1 hiro
#endif
105 1 hiro
106 1 hiro
void
107 1 hiro
rfc2015_disable_all (void)
108 1 hiro
{
109 1 hiro
    /* FIXME: set a flag, so that we don't bother the user with failed
110 1 hiro
     * gpgme messages */
111 1 hiro
}
112 1 hiro
113 1 hiro
114 1 hiro
void
115 1 hiro
rfc2015_secure_remove (const char *fname)
116 1 hiro
{
117 1 hiro
    if (!fname)
118 1 hiro
        return;
119 1 hiro
    /* fixme: overwrite the file first */
120 1 hiro
    remove (fname);
121 1 hiro
}
122 1 hiro
123 1 hiro
124 1 hiro
static const gchar *
125 1 hiro
sig_status_to_string (GpgmeSigStat status)
126 1 hiro
{
127 1 hiro
    const gchar *result;
128 1 hiro
129 1 hiro
    switch (status) {
130 1 hiro
      case GPGME_SIG_STAT_NONE:
131 1 hiro
        result = _("Oops: Signature not verified");
132 1 hiro
        break;
133 1 hiro
      case GPGME_SIG_STAT_NOSIG:
134 1 hiro
        result = _("No signature found");
135 1 hiro
        break;
136 1 hiro
      case GPGME_SIG_STAT_GOOD:
137 1 hiro
        result = _("Good signature");
138 1 hiro
        break;
139 1 hiro
      case GPGME_SIG_STAT_BAD:
140 1 hiro
        result = _("BAD signature");
141 1 hiro
        break;
142 1 hiro
      case GPGME_SIG_STAT_NOKEY:
143 1 hiro
        result = _("No public key to verify the signature");
144 1 hiro
        break;
145 1 hiro
      case GPGME_SIG_STAT_ERROR:
146 1 hiro
        result = _("Error verifying the signature");
147 1 hiro
        break;
148 1 hiro
      case GPGME_SIG_STAT_DIFF:
149 1 hiro
        result = _("Different results for signatures");
150 1 hiro
        break;
151 1 hiro
      default:
152 1 hiro
        result = _("Error: Unknown status");
153 1 hiro
        break;
154 1 hiro
    }
155 1 hiro
156 1 hiro
    return result;
157 1 hiro
}
158 1 hiro
159 1 hiro
static const gchar *
160 1 hiro
sig_status_with_name (GpgmeSigStat status)
161 1 hiro
{
162 1 hiro
    const gchar *result;
163 1 hiro
164 1 hiro
    switch (status) {
165 1 hiro
      case GPGME_SIG_STAT_NONE:
166 1 hiro
        result = _("Oops: Signature not verified");
167 1 hiro
        break;
168 1 hiro
      case GPGME_SIG_STAT_NOSIG:
169 1 hiro
        result = _("No signature found");
170 1 hiro
        break;
171 1 hiro
      case GPGME_SIG_STAT_GOOD:
172 1 hiro
        result = _("Good signature from \"%s\"");
173 1 hiro
        break;
174 1 hiro
      case GPGME_SIG_STAT_BAD:
175 1 hiro
        result = _("BAD signature from \"%s\"");
176 1 hiro
        break;
177 1 hiro
      case GPGME_SIG_STAT_NOKEY:
178 1 hiro
        result = _("No public key to verify the signature");
179 1 hiro
        break;
180 1 hiro
      case GPGME_SIG_STAT_ERROR:
181 1 hiro
        result = _("Error verifying the signature");
182 1 hiro
        break;
183 1 hiro
      case GPGME_SIG_STAT_DIFF:
184 1 hiro
        result = _("Different results for signatures");
185 1 hiro
        break;
186 1 hiro
      default:
187 1 hiro
        result = _("Error: Unknown status");
188 1 hiro
        break;
189 1 hiro
    }
190 1 hiro
191 1 hiro
    return result;
192 1 hiro
}
193 1 hiro
194 1 hiro
static void
195 1 hiro
sig_status_for_key(GString *str, GpgmeCtx ctx, GpgmeSigStat status,
196 1 hiro
                   GpgmeKey key, const gchar *fpr)
197 1 hiro
{
198 1 hiro
        gint idx = 0;
199 1 hiro
        const char *uid;
200 1 hiro
201 1 hiro
        uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, NULL, idx);
202 1 hiro
        if (uid == NULL) {
203 1 hiro
                g_string_sprintfa (str, "%s\n",
204 1 hiro
                                   sig_status_to_string (status));
205 1 hiro
                if ((fpr != NULL) && (*fpr != '\0'))
206 1 hiro
                        g_string_sprintfa (str, "Key fingerprint: %s\n", fpr);
207 1 hiro
                g_string_append (str, _("Cannot find user ID for this key."));
208 1 hiro
                return;
209 1 hiro
        }
210 1 hiro
        g_string_sprintfa (str, sig_status_with_name (status), uid);
211 1 hiro
        g_string_append (str, "\n");
212 1 hiro
213 1 hiro
        while (1) {
214 1 hiro
                uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID,
215 1 hiro
                                                 NULL, ++idx);
216 1 hiro
                if (uid == NULL)
217 1 hiro
                        break;
218 1 hiro
                g_string_sprintfa (str, _("                aka \"%s\"\n"),
219 1 hiro
                                   uid);
220 1 hiro
        }
221 1 hiro
}
222 1 hiro
223 1 hiro
static gchar *
224 1 hiro
sig_status_full (GpgmeCtx ctx)
225 1 hiro
{
226 1 hiro
        GString *str;
227 1 hiro
        gint sig_idx = 0;
228 1 hiro
        GpgmeError err;
229 1 hiro
        GpgmeSigStat status;
230 1 hiro
        GpgmeKey key;
231 1 hiro
        const char *fpr;
232 1 hiro
        time_t created;
233 1 hiro
        struct tm *ctime_val;
234 1 hiro
        char ctime_str[80];
235 1 hiro
        gchar *retval;
236 1 hiro
237 1 hiro
        str = g_string_new ("");
238 1 hiro
239 1 hiro
        fpr = gpgme_get_sig_status (ctx, sig_idx, &status, &created);
240 1 hiro
        while (fpr != NULL) {
241 1 hiro
                if (created != 0) {
242 1 hiro
                        ctime_val = localtime (&created);
243 1 hiro
                        strftime (ctime_str, sizeof (ctime_str), "%c",
244 1 hiro
                                  ctime_val);
245 1 hiro
                        g_string_sprintfa (str,
246 1 hiro
                                           _("Signature made at %s\n"),
247 1 hiro
                                           ctime_str);
248 1 hiro
                }
249 1 hiro
                err = gpgme_get_sig_key (ctx, sig_idx, &key);
250 1 hiro
                if (err != 0) {
251 1 hiro
                        g_string_sprintfa (str, "%s\n",
252 1 hiro
                                           sig_status_to_string (status));
253 1 hiro
                        if ((fpr != NULL) && (*fpr != '\0'))
254 1 hiro
                                g_string_sprintfa (str,
255 1 hiro
                                                   _("Key fingerprint: %s\n"),
256 1 hiro
                                                   fpr);
257 1 hiro
                } else {
258 1 hiro
                        sig_status_for_key (str, ctx, status, key, fpr);
259 1 hiro
                        gpgme_key_unref (key);
260 1 hiro
                }
261 1 hiro
                g_string_append (str, "\n\n");
262 1 hiro
263 1 hiro
                fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created);
264 1 hiro
        }
265 1 hiro
266 1 hiro
        retval = str->str;
267 1 hiro
        g_string_free (str, FALSE);
268 1 hiro
        return retval;
269 1 hiro
}
270 1 hiro
271 1 hiro
static void check_signature (MimeInfo *mimeinfo, MimeInfo *partinfo, FILE *fp)
272 1 hiro
{
273 1 hiro
    GpgmeCtx ctx = NULL;
274 1 hiro
    GpgmeError err;
275 1 hiro
    GpgmeData sig = NULL, text = NULL;
276 1 hiro
    GpgmeSigStat status = GPGME_SIG_STAT_NONE;
277 1 hiro
    GpgmegtkSigStatus statuswindow = NULL;
278 1 hiro
    const char *result = NULL;
279 1 hiro
    gchar *tmp_file;
280 1 hiro
    gint n_exclude_chars = 0;
281 1 hiro
282 1 hiro
    if (prefs_common.gpg_signature_popup)
283 1 hiro
        statuswindow = gpgmegtk_sig_status_create ();
284 1 hiro
285 1 hiro
    err = gpgme_new (&ctx);
286 1 hiro
    if (err) {
287 1 hiro
        debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
288 1 hiro
        goto leave;
289 1 hiro
    }
290 1 hiro
291 1 hiro
    /* don't include the last empty line.
292 1 hiro
       It does not belong to the signed text */
293 1 hiro
    if (mimeinfo->children->size > 0) {
294 1 hiro
        if (fseek(fp, mimeinfo->children->fpos + mimeinfo->children->size - 1,
295 1 hiro
                  SEEK_SET) < 0) {
296 1 hiro
            perror("fseek");
297 1 hiro
            goto leave;
298 1 hiro
        }
299 1 hiro
        if (fgetc(fp) == '\n') {
300 1 hiro
            n_exclude_chars++;
301 1 hiro
            if (mimeinfo->children->size > 1) {
302 1 hiro
                if (fseek(fp, mimeinfo->children->fpos + mimeinfo->children->size - 2,
303 1 hiro
                          SEEK_SET) < 0) {
304 1 hiro
                    perror("fseek");
305 1 hiro
                    goto leave;
306 1 hiro
                }
307 1 hiro
                if (fgetc(fp) == '\r')
308 1 hiro
                    n_exclude_chars++;
309 1 hiro
            }
310 1 hiro
        }
311 1 hiro
    }
312 1 hiro
313 1 hiro
    /* canonicalize the file part. */
314 1 hiro
    tmp_file = get_tmp_file();
315 1 hiro
    if (copy_file_part(fp, mimeinfo->children->fpos,
316 1 hiro
                       mimeinfo->children->size - n_exclude_chars,
317 1 hiro
                       tmp_file) < 0) {
318 1 hiro
        g_free(tmp_file);
319 1 hiro
        goto leave;
320 1 hiro
    }
321 1 hiro
    if (canonicalize_file_replace(tmp_file) < 0) {
322 1 hiro
        unlink(tmp_file);
323 1 hiro
        g_free(tmp_file);
324 1 hiro
        goto leave;
325 1 hiro
    }
326 1 hiro
327 1 hiro
    err = gpgme_data_new_from_file(&text, tmp_file, 1);
328 1 hiro
329 1 hiro
    unlink(tmp_file);
330 1 hiro
    g_free(tmp_file);
331 1 hiro
332 1 hiro
    if (!err)
333 1 hiro
        err = gpgme_data_new_from_filepart (&sig, NULL, fp,
334 1 hiro
                                            partinfo->fpos, partinfo->size);
335 1 hiro
    if (err) {
336 1 hiro
        debug_print ("gpgme_data_new_from_filepart failed: %s\n",
337 1 hiro
                   gpgme_strerror (err));
338 1 hiro
        goto leave;
339 1 hiro
    }
340 1 hiro
341 1 hiro
    err = gpgme_op_verify (ctx, sig, text, &status);
342 1 hiro
    if (err)  {
343 1 hiro
        debug_print ("gpgme_op_verify failed: %s\n", gpgme_strerror (err));
344 1 hiro
        goto leave;
345 1 hiro
    }
346 1 hiro
347 1 hiro
    /* FIXME: check what the heck this sig_status_full stuff is.
348 1 hiro
     * it should better go into sigstatus.c */
349 1 hiro
    g_free (partinfo->sigstatus_full);
350 1 hiro
    partinfo->sigstatus_full = sig_status_full (ctx);
351 1 hiro
352 1 hiro
leave:
353 1 hiro
    result = gpgmegtk_sig_status_to_string(status);
354 1 hiro
    debug_print("verification status: %s\n", result);
355 1 hiro
    if (prefs_common.gpg_signature_popup)
356 1 hiro
        gpgmegtk_sig_status_update (statuswindow, ctx);
357 1 hiro
358 1 hiro
    g_free (partinfo->sigstatus);
359 1 hiro
    partinfo->sigstatus = g_strdup (result);
360 1 hiro
361 1 hiro
    gpgme_data_release (sig);
362 1 hiro
    gpgme_data_release (text);
363 1 hiro
    gpgme_release (ctx);
364 1 hiro
    if (prefs_common.gpg_signature_popup)
365 1 hiro
        gpgmegtk_sig_status_destroy (statuswindow);
366 1 hiro
}
367 1 hiro
368 1 hiro
/*
369 1 hiro
 * Copy a gpgme data object to a temporary file and
370 1 hiro
 * return this filename
371 1 hiro
 */
372 1 hiro
#if 0
373 1 hiro
static char *
374 1 hiro
copy_gpgmedata_to_temp (GpgmeData data, guint *length)
375 1 hiro
{
376 1 hiro
    static int id;
377 1 hiro
    char *tmp;
378 1 hiro
    FILE *fp;
379 1 hiro
    char buf[100];
380 1 hiro
    size_t nread;
381 1 hiro
    GpgmeError err;
382 1 hiro
383 1 hiro
    tmp = g_strdup_printf("%s%cgpgtmp.%08x",
384 1 hiro
                          get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id );
385 1 hiro
386 1 hiro
    if ((fp = fopen(tmp, "wb")) == NULL) {
387 1 hiro
        FILE_OP_ERROR(tmp, "fopen");
388 1 hiro
        g_free(tmp);
389 1 hiro
        return NULL;
390 1 hiro
    }
391 1 hiro
392 1 hiro
    err = gpgme_data_rewind ( data );
393 1 hiro
    if (err)
394 1 hiro
        debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
395 1 hiro
396 1 hiro
    while (!(err = gpgme_data_read (data, buf, 100, &nread))) {
397 1 hiro
        fwrite ( buf, nread, 1, fp );
398 1 hiro
    }
399 1 hiro
400 1 hiro
    if (err != GPGME_EOF)
401 1 hiro
        debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
402 1 hiro
403 1 hiro
    fclose (fp);
404 1 hiro
    *length = nread;
405 1 hiro
406 1 hiro
    return tmp;
407 1 hiro
}
408 1 hiro
#endif
409 1 hiro
410 1 hiro
static GpgmeData
411 1 hiro
pgp_decrypt (MimeInfo *partinfo, FILE *fp)
412 1 hiro
{
413 1 hiro
    GpgmeCtx ctx = NULL;
414 1 hiro
    GpgmeError err;
415 1 hiro
    GpgmeData cipher = NULL, plain = NULL;
416 1 hiro
    struct passphrase_cb_info_s info;
417 1 hiro
418 1 hiro
    memset (&info, 0, sizeof info);
419 1 hiro
420 1 hiro
    err = gpgme_new (&ctx);
421 1 hiro
    if (err) {
422 1 hiro
        debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
423 1 hiro
        goto leave;
424 1 hiro
    }
425 1 hiro
426 1 hiro
    err = gpgme_data_new_from_filepart (&cipher, NULL, fp,
427 1 hiro
                                        partinfo->fpos, partinfo->size);
428 1 hiro
    if (err) {
429 1 hiro
        debug_print ("gpgme_data_new_from_filepart failed: %s\n",
430 1 hiro
                     gpgme_strerror (err));
431 1 hiro
        goto leave;
432 1 hiro
    }
433 1 hiro
434 1 hiro
    err = gpgme_data_new (&plain);
435 1 hiro
    if (err) {
436 1 hiro
        debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
437 1 hiro
        goto leave;
438 1 hiro
    }
439 1 hiro
440 1 hiro
    if (!getenv("GPG_AGENT_INFO")) {
441 1 hiro
        info.c = ctx;
442 1 hiro
        gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
443 1 hiro
    }
444 1 hiro
445 1 hiro
    err = gpgme_op_decrypt (ctx, cipher, plain);
446 1 hiro
447 1 hiro
leave:
448 1 hiro
    gpgme_data_release (cipher);
449 1 hiro
    if (err) {
450 1 hiro
        gpgmegtk_free_passphrase();
451 1 hiro
        debug_print ("decryption failed: %s\n", gpgme_strerror (err));
452 1 hiro
        gpgme_data_release (plain);
453 1 hiro
        plain = NULL;
454 1 hiro
    }
455 1 hiro
    else
456 1 hiro
        debug_print ("** decryption succeeded\n");
457 1 hiro
458 1 hiro
    gpgme_release (ctx);
459 1 hiro
    return plain;
460 1 hiro
}
461 1 hiro
462 1 hiro
MimeInfo * rfc2015_find_signature (MimeInfo *mimeinfo)
463 1 hiro
{
464 1 hiro
    MimeInfo *partinfo;
465 1 hiro
    int n = 0;
466 1 hiro
467 1 hiro
    if (!mimeinfo)
468 1 hiro
        return NULL;
469 1 hiro
    if (g_strcasecmp (mimeinfo->content_type, "multipart/signed"))
470 1 hiro
        return NULL;
471 1 hiro
472 1 hiro
    debug_print ("** multipart/signed encountered\n");
473 1 hiro
474 1 hiro
    /* check that we have at least 2 parts of the correct type */
475 1 hiro
    for (partinfo = mimeinfo->children;
476 1 hiro
         partinfo != NULL; partinfo = partinfo->next) {
477 1 hiro
        if (++n > 1  && !g_strcasecmp (partinfo->content_type,
478 1 hiro
                                       "application/pgp-signature"))
479 1 hiro
            break;
480 1 hiro
    }
481 1 hiro
482 1 hiro
    return partinfo;
483 1 hiro
}
484 1 hiro
485 1 hiro
gboolean rfc2015_has_signature (MimeInfo *mimeinfo)
486 1 hiro
{
487 1 hiro
    return rfc2015_find_signature (mimeinfo) != NULL;
488 1 hiro
}
489 1 hiro
490 1 hiro
void rfc2015_check_signature (MimeInfo *mimeinfo, FILE *fp)
491 1 hiro
{
492 1 hiro
    MimeInfo *partinfo;
493 1 hiro
494 1 hiro
    partinfo = rfc2015_find_signature (mimeinfo);
495 1 hiro
    if (!partinfo)
496 1 hiro
        return;
497 1 hiro
498 1 hiro
#if 0
499 1 hiro
    g_message ("** yep, it is a pgp signature");
500 1 hiro
    dump_mimeinfo ("gpg-signature", partinfo );
501 1 hiro
    dump_part (partinfo, fp );
502 1 hiro
    dump_mimeinfo ("signed text", mimeinfo->children );
503 1 hiro
    dump_part (mimeinfo->children, fp);
504 1 hiro
#endif
505 1 hiro
506 1 hiro
    check_signature (mimeinfo, partinfo, fp);
507 1 hiro
}
508 1 hiro
509 1 hiro
int rfc2015_is_encrypted (MimeInfo *mimeinfo)
510 1 hiro
{
511 1 hiro
    if (!mimeinfo || mimeinfo->mime_type != MIME_MULTIPART)
512 1 hiro
        return 0;
513 1 hiro
    if (g_strcasecmp (mimeinfo->content_type, "multipart/encrypted"))
514 1 hiro
        return 0;
515 1 hiro
    /* fixme: we should check the protocol parameter */
516 1 hiro
    return 1;
517 1 hiro
}
518 1 hiro
519 1 hiro
gboolean rfc2015_msg_is_encrypted (const gchar *file)
520 1 hiro
{
521 1 hiro
        FILE *fp;
522 1 hiro
        MimeInfo *mimeinfo;
523 1 hiro
        int ret;
524 1 hiro
525 1 hiro
        if ((fp = fopen(file, "rb")) == NULL)
526 1 hiro
                return FALSE;
527 1 hiro
528 1 hiro
        mimeinfo = procmime_scan_mime_header(fp);
529 1 hiro
        if(!mimeinfo) {
530 1 hiro
                fclose(fp);
531 1 hiro
                return FALSE;
532 1 hiro
        }
533 1 hiro
534 1 hiro
        ret = rfc2015_is_encrypted(mimeinfo);
535 1 hiro
        procmime_mimeinfo_free_all(mimeinfo);
536 1 hiro
        return ret != 0 ? TRUE : FALSE;
537 1 hiro
}
538 1 hiro
539 1 hiro
static int
540 1 hiro
name_cmp(const char *a, const char *b)
541 1 hiro
{
542 1 hiro
    for( ; *a && *b; a++, b++) {
543 1 hiro
        if(*a != *b
544 1 hiro
           && toupper(*(unsigned char *)a) != toupper(*(unsigned char *)b))
545 1 hiro
            return 1;
546 1 hiro
    }
547 1 hiro
548 1 hiro
    return *a != *b;
549 1 hiro
}
550 1 hiro
551 1 hiro
static int
552 1 hiro
headerp(char *p, char **names)
553 1 hiro
{
554 1 hiro
    int i, c;
555 1 hiro
    char *p2;
556 1 hiro
557 1 hiro
    p2 = strchr(p, ':');
558 1 hiro
    if(!p2 || p == p2) {
559 1 hiro
        return 0;
560 1 hiro
    }
561 1 hiro
    if(p2[-1] == ' ' || p2[-1] == '\t') {
562 1 hiro
        return 0;
563 1 hiro
    }
564 1 hiro
565 1 hiro
    if(!names[0])
566 1 hiro
        return 1;
567 1 hiro
568 1 hiro
    c = *p2;
569 1 hiro
    *p2 = 0;
570 1 hiro
    for(i = 0 ; names[i] != NULL; i++) {
571 1 hiro
        if(!name_cmp (names[i], p))
572 1 hiro
            break;
573 1 hiro
    }
574 1 hiro
    *p2 = c;
575 1 hiro
576 1 hiro
    return names[i] != NULL;
577 1 hiro
}
578 1 hiro
579 1 hiro
580 1 hiro
#define DECRYPTION_ABORT() \
581 1 hiro
{ \
582 1 hiro
    procmime_mimeinfo_free_all(tmpinfo); \
583 1 hiro
    msginfo->decryption_failed = 1; \
584 1 hiro
    return; \
585 1 hiro
}
586 1 hiro
587 1 hiro
void rfc2015_decrypt_message (MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
588 1 hiro
{
589 1 hiro
    static int id;
590 1 hiro
    MimeInfo *tmpinfo, *partinfo;
591 1 hiro
    int ver_ok = 0;
592 1 hiro
    char *fname;
593 1 hiro
    GpgmeData plain;
594 1 hiro
    FILE *dstfp;
595 1 hiro
    size_t nread;
596 1 hiro
    char buf[BUFFSIZE];
597 1 hiro
    int in_cline;
598 1 hiro
    GpgmeError err;
599 1 hiro
600 1 hiro
    g_return_if_fail (msginfo != NULL);
601 1 hiro
    g_return_if_fail (mimeinfo != NULL);
602 1 hiro
    g_return_if_fail (fp != NULL);
603 1 hiro
    g_return_if_fail (mimeinfo->mime_type == MIME_MULTIPART);
604 1 hiro
605 1 hiro
    debug_print ("** decrypting multipart/encrypted message\n");
606 1 hiro
607 1 hiro
    /* skip headers */
608 1 hiro
    if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
609 1 hiro
        perror("fseek");
610 1 hiro
    tmpinfo = procmime_scan_mime_header(fp);
611 1 hiro
    if (!tmpinfo || tmpinfo->mime_type != MIME_MULTIPART) {
612 1 hiro
        DECRYPTION_ABORT();
613 1 hiro
    }
614 1 hiro
615 1 hiro
    procmime_scan_multipart_message(tmpinfo, fp);
616 1 hiro
617 1 hiro
    /* check that we have the 2 parts */
618 1 hiro
    partinfo = tmpinfo->children;
619 1 hiro
    if (!partinfo || !partinfo->next) {
620 1 hiro
        DECRYPTION_ABORT();
621 1 hiro
    }
622 1 hiro
    if (!g_strcasecmp (partinfo->content_type, "application/pgp-encrypted")) {
623 1 hiro
        /* Fixme: check that the version is 1 */
624 1 hiro
        ver_ok = 1;
625 1 hiro
    }
626 1 hiro
    partinfo = partinfo->next;
627 1 hiro
    if (ver_ok &&
628 1 hiro
        !g_strcasecmp (partinfo->content_type, "application/octet-stream")) {
629 1 hiro
        if (partinfo->next)
630 1 hiro
            g_warning ("oops: pgp_encrypted with more than 2 parts");
631 1 hiro
    }
632 1 hiro
    else {
633 1 hiro
        DECRYPTION_ABORT();
634 1 hiro
    }
635 1 hiro
636 1 hiro
    debug_print ("** yep, it is pgp encrypted\n");
637 1 hiro
638 1 hiro
    plain = pgp_decrypt (partinfo, fp);
639 1 hiro
    if (!plain) {
640 1 hiro
        DECRYPTION_ABORT();
641 1 hiro
    }
642 1 hiro
643 1 hiro
    fname = g_strdup_printf("%s%cplaintext.%08x",
644 1 hiro
                            get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
645 1 hiro
646 1 hiro
    if ((dstfp = fopen(fname, "wb")) == NULL) {
647 1 hiro
        FILE_OP_ERROR(fname, "fopen");
648 1 hiro
        g_free(fname);
649 1 hiro
        DECRYPTION_ABORT();
650 1 hiro
    }
651 1 hiro
652 1 hiro
    /* write the orginal header to the new file */
653 1 hiro
    if (fseek(fp, tmpinfo->fpos, SEEK_SET) < 0)
654 1 hiro
        perror("fseek");
655 1 hiro
656 1 hiro
    in_cline = 0;
657 1 hiro
    while (fgets(buf, sizeof(buf), fp)) {
658 1 hiro
        if (headerp (buf, content_names)) {
659 1 hiro
            in_cline = 1;
660 1 hiro
            continue;
661 1 hiro
        }
662 1 hiro
        if (in_cline) {
663 1 hiro
            if (buf[0] == ' ' || buf[0] == '\t')
664 1 hiro
                continue;
665 1 hiro
            in_cline = 0;
666 1 hiro
        }
667 1 hiro
        if (buf[0] == '\r' || buf[0] == '\n')
668 1 hiro
            break;
669 1 hiro
        fputs (buf, dstfp);
670 1 hiro
    }
671 1 hiro
672 1 hiro
    err = gpgme_data_rewind (plain);
673 1 hiro
    if (err)
674 1 hiro
        debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
675 1 hiro
676 1 hiro
    while (!(err = gpgme_data_read (plain, buf, sizeof(buf), &nread))) {
677 1 hiro
        fwrite (buf, nread, 1, dstfp);
678 1 hiro
    }
679 1 hiro
680 1 hiro
    if (err != GPGME_EOF) {
681 1 hiro
        debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
682 1 hiro
    }
683 1 hiro
684 1 hiro
    fclose (dstfp);
685 1 hiro
    procmime_mimeinfo_free_all(tmpinfo);
686 1 hiro
687 1 hiro
    msginfo->plaintext_file = fname;
688 1 hiro
    msginfo->decryption_failed = 0;
689 1 hiro
}
690 1 hiro
691 1 hiro
#undef DECRYPTION_ABORT
692 1 hiro
693 1 hiro
694 1 hiro
/*
695 1 hiro
 * plain contains an entire mime object.
696 1 hiro
 * Encrypt it and return an GpgmeData object with the encrypted version of
697 1 hiro
 * the file or NULL in case of error.
698 1 hiro
 */
699 1 hiro
static GpgmeData
700 1 hiro
pgp_encrypt ( GpgmeData plain, GpgmeRecipients rset )
701 1 hiro
{
702 1 hiro
    GpgmeCtx ctx = NULL;
703 1 hiro
    GpgmeError err;
704 1 hiro
    GpgmeData cipher = NULL;
705 1 hiro
706 1 hiro
    err = gpgme_new (&ctx);
707 1 hiro
    if (!err)
708 1 hiro
        err = gpgme_data_new (&cipher);
709 1 hiro
    if (!err) {
710 1 hiro
        gpgme_set_armor (ctx, 1);
711 1 hiro
        err = gpgme_op_encrypt (ctx, rset, plain, cipher);
712 1 hiro
    }
713 1 hiro
714 1 hiro
    if (err) {
715 1 hiro
        debug_print ("encryption failed: %s\n", gpgme_strerror (err));
716 1 hiro
        gpgme_data_release (cipher);
717 1 hiro
        cipher = NULL;
718 1 hiro
    }
719 1 hiro
    else {
720 1 hiro
        debug_print ("** encryption succeeded\n");
721 1 hiro
    }
722 1 hiro
723 1 hiro
    gpgme_release (ctx);
724 1 hiro
    return cipher;
725 1 hiro
}
726 1 hiro
727 1 hiro
/*
728 1 hiro
 * Create and return a list of keys matching a key id
729 1 hiro
 */
730 1 hiro
731 1 hiro
GSList *rfc2015_create_signers_list (const char *keyid)
732 1 hiro
{
733 1 hiro
        GSList *key_list = NULL;
734 1 hiro
        GpgmeCtx list_ctx = NULL;
735 1 hiro
        GSList *p;
736 1 hiro
        GpgmeError err;
737 1 hiro
        GpgmeKey key;
738 1 hiro
739 1 hiro
        err = gpgme_new (&list_ctx);
740 1 hiro
        if (err)
741 1 hiro
                goto leave;
742 1 hiro
        err = gpgme_op_keylist_start (list_ctx, keyid, 1);
743 1 hiro
        if (err)
744 1 hiro
                goto leave;
745 1 hiro
        while ( !(err = gpgme_op_keylist_next (list_ctx, &key)) ) {
746 1 hiro
                key_list = g_slist_append (key_list, key);
747 1 hiro
        }
748 1 hiro
        if (err != GPGME_EOF)
749 1 hiro
                goto leave;
750 1 hiro
        err = 0;
751 1 hiro
        if (key_list == NULL) {
752 1 hiro
                debug_print ("no keys found for keyid \"%s\"\n", keyid);
753 1 hiro
        }
754 1 hiro
755 1 hiro
leave:
756 1 hiro
        if (err) {
757 1 hiro
                debug_print ("rfc2015_create_signers_list failed: %s\n", gpgme_strerror (err));
758 1 hiro
                for (p = key_list; p != NULL; p = p->next)
759 1 hiro
                        gpgme_key_unref ((GpgmeKey) p->data);
760 1 hiro
                g_slist_free (key_list);
761 1 hiro
        }
762 1 hiro
        if (list_ctx)
763 1 hiro
                gpgme_release (list_ctx);
764 1 hiro
        return err ? NULL : key_list;
765 1 hiro
}
766 1 hiro
767 1 hiro
/*
768 1 hiro
 * Encrypt the file by extracting all recipients and finding the
769 1 hiro
 * encryption keys for all of them.  The file content is then replaced
770 1 hiro
 * by the encrypted one.  */
771 1 hiro
int
772 1 hiro
rfc2015_encrypt (const char *file, GSList *recp_list, gboolean ascii_armored)
773 1 hiro
{
774 1 hiro
    FILE *fp = NULL;
775 1 hiro
    char buf[BUFFSIZE];
776 1 hiro
    int i, clineidx, saved_last;
777 1 hiro
    char *clines[3] = {NULL};
778 1 hiro
    GpgmeError err;
779 1 hiro
    GpgmeData header = NULL;
780 1 hiro
    GpgmeData plain = NULL;
781 1 hiro
    GpgmeData cipher = NULL;
782 1 hiro
    GpgmeRecipients rset = NULL;
783 1 hiro
    size_t nread;
784 1 hiro
    int mime_version_seen = 0;
785 1 hiro
    char *boundary;
786 1 hiro
787 1 hiro
    boundary = generate_mime_boundary ("Encrypt");
788 1 hiro
789 1 hiro
    /* Create the list of recipients */
790 1 hiro
    rset = gpgmegtk_recipient_selection (recp_list);
791 1 hiro
    if (!rset) {
792 1 hiro
        debug_print ("error creating recipient list\n" );
793 1 hiro
        goto failure;
794 1 hiro
    }
795 1 hiro
796 1 hiro
    /* Open the source file */
797 1 hiro
    if ((fp = fopen(file, "rb")) == NULL) {
798 1 hiro
        FILE_OP_ERROR(file, "fopen");
799 1 hiro
        goto failure;
800 1 hiro
    }
801 1 hiro
802 1 hiro
    err = gpgme_data_new (&header);
803 1 hiro
    if (!err)
804 1 hiro
        err = gpgme_data_new (&plain);
805 1 hiro
    if (err) {
806 1 hiro
        debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
807 1 hiro
        goto failure;
808 1 hiro
    }
809 1 hiro
810 1 hiro
    /* get the content header lines from the source */
811 1 hiro
    clineidx = 0;
812 1 hiro
    saved_last = 0;
813 1 hiro
    while (!err && fgets(buf, sizeof(buf), fp)) {
814 1 hiro
        /* fixme: check for overlong lines */
815 1 hiro
        if (headerp (buf, content_names)) {
816 1 hiro
            if (clineidx >= DIM (clines)) {
817 1 hiro
                debug_print ("rfc2015_encrypt: too many content lines\n");
818 1 hiro
                goto failure;
819 1 hiro
            }
820 1 hiro
            clines[clineidx++] = g_strdup (buf);
821 1 hiro
            saved_last = 1;
822 1 hiro
            continue;
823 1 hiro
        }
824 1 hiro
        if (saved_last) {
825 1 hiro
            if (*buf == ' ' || *buf == '\t') {
826 1 hiro
                char *last = clines[clineidx - 1];
827 1 hiro
                clines[clineidx - 1] = g_strconcat (last, buf, NULL);
828 1 hiro
                g_free (last);
829 1 hiro
                continue;
830 1 hiro
            }
831 1 hiro
            saved_last = 0;
832 1 hiro
        }
833 1 hiro
834 1 hiro
        if (headerp (buf, mime_version_name))
835 1 hiro
            mime_version_seen = 1;
836 1 hiro
837 1 hiro
        if (buf[0] == '\r' || buf[0] == '\n')
838 1 hiro
            break;
839 1 hiro
        err = gpgme_data_write (header, buf, strlen (buf));
840 1 hiro
    }
841 1 hiro
    if (ferror (fp)) {
842 1 hiro
        FILE_OP_ERROR (file, "fgets");
843 1 hiro
        goto failure;
844 1 hiro
    }
845 1 hiro
846 1 hiro
    /* write them to the temp data and add the rest of the message */
847 1 hiro
    for (i = 0; !err && i < clineidx; i++) {
848 1 hiro
        debug_print ("%% %s:%d: cline=`%s'", __FILE__ ,__LINE__, clines[i]);
849 1 hiro
        err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
850 1 hiro
    }
851 1 hiro
    if (!err)
852 1 hiro
        err = gpgme_data_write (plain, "\r\n", 2);
853 1 hiro
    while (!err && fgets(buf, sizeof(buf), fp)) {
854 1 hiro
        err = gpgme_data_write (plain, buf, strlen (buf));
855 1 hiro
    }
856 1 hiro
    if (ferror (fp)) {
857 1 hiro
        FILE_OP_ERROR (file, "fgets");
858 1 hiro
        goto failure;
859 1 hiro
    }
860 1 hiro
    if (err) {
861 1 hiro
        debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
862 1 hiro
        goto failure;
863 1 hiro
    }
864 1 hiro
865 1 hiro
    cipher = pgp_encrypt (plain, rset);
866 1 hiro
    gpgme_data_release (plain); plain = NULL;
867 1 hiro
    gpgme_recipients_release (rset); rset = NULL;
868 1 hiro
    if (!cipher)
869 1 hiro
        goto failure;
870 1 hiro
871 1 hiro
    /* we have the encrypted message available in cipher and now we
872 1 hiro
     * are going to rewrite the source file. To be sure that file has
873 1 hiro
     * been truncated we use an approach which should work everywhere:
874 1 hiro
     * close the file and then reopen it for writing. It is important
875 1 hiro
     * that this works, otherwise it may happen that parts of the
876 1 hiro
     * plaintext are still in the file (The encrypted stuff is, due to
877 1 hiro
     * compression, usually shorter than the plaintext).
878 1 hiro
     *
879 1 hiro
     * Yes, there is a race condition here, but everyone, who is so
880 1 hiro
     * stupid to store the temp file with the plaintext in a public
881 1 hiro
     * directory has to live with this anyway. */
882 1 hiro
    if (fclose (fp)) {
883 1 hiro
        FILE_OP_ERROR(file, "fclose");
884 1 hiro
        goto failure;
885 1 hiro
    }
886 1 hiro
    if ((fp = fopen(file, "wb")) == NULL) {
887 1 hiro
        FILE_OP_ERROR(file, "fopen");
888 1 hiro
        goto failure;
889 1 hiro
    }
890 1 hiro
891 1 hiro
    /* Write the header, append new content lines, part 1 and part 2 header */
892 1 hiro
    err = gpgme_data_rewind (header);
893 1 hiro
    if (err) {
894 1 hiro
        debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
895 1 hiro
        goto failure;
896 1 hiro
    }
897 1 hiro
    while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
898 1 hiro
        fwrite (buf, nread, 1, fp);
899 1 hiro
    }
900 1 hiro
    if (err != GPGME_EOF) {
901 1 hiro
        debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
902 1 hiro
        goto failure;
903 1 hiro
    }
904 1 hiro
    if (ferror (fp)) {
905 1 hiro
        FILE_OP_ERROR (file, "fwrite");
906 1 hiro
        goto failure;
907 1 hiro
    }
908 1 hiro
    gpgme_data_release (header); header = NULL;
909 1 hiro
910 1 hiro
    if (!mime_version_seen)
911 1 hiro
        fputs ("MIME-Version: 1\r\n", fp);
912 1 hiro
913 1 hiro
    if (ascii_armored) {
914 1 hiro
        fprintf(fp,
915 1 hiro
            "Content-Type: text/plain; charset=US-ASCII\r\n"
916 1 hiro
            "Content-Transfer-Encoding: 7bit\r\n"
917 1 hiro
            "\r\n");
918 1 hiro
    } else {
919 1 hiro
        fprintf (fp,
920 1 hiro
                "Content-Type: multipart/encrypted;"
921 1 hiro
                " protocol=\"application/pgp-encrypted\";\r\n"
922 1 hiro
                " boundary=\"%s\"\r\n"
923 1 hiro
                "\r\n"
924 1 hiro
                "--%s\r\n"
925 1 hiro
                "Content-Type: application/pgp-encrypted\r\n"
926 1 hiro
                "\r\n"
927 1 hiro
                "Version: 1\r\n"
928 1 hiro
                "\r\n"
929 1 hiro
                "--%s\r\n"
930 1 hiro
                "Content-Type: application/octet-stream\r\n"
931 1 hiro
                "\r\n",
932 1 hiro
                boundary, boundary, boundary);
933 1 hiro
    }
934 1 hiro
935 1 hiro
    /* append the encrypted stuff */
936 1 hiro
    err = gpgme_data_rewind (cipher);
937 1 hiro
    if (err) {
938 1 hiro
        debug_print ("** gpgme_data_rewind on cipher failed: %s\n",
939 1 hiro
                   gpgme_strerror (err));
940 1 hiro
        goto failure;
941 1 hiro
    }
942 1 hiro
943 1 hiro
    while (!(err = gpgme_data_read (cipher, buf, BUFFSIZE, &nread))) {
944 1 hiro
        fwrite (buf, nread, 1, fp);
945 1 hiro
    }
946 1 hiro
    if (err != GPGME_EOF) {
947 1 hiro
        debug_print ("** gpgme_data_read failed: %s\n", gpgme_strerror (err));
948 1 hiro
        goto failure;
949 1 hiro
    }
950 1 hiro
951 1 hiro
    /* and the final boundary */
952 1 hiro
    if (!ascii_armored) {
953 1 hiro
        fprintf (fp,
954 1 hiro
                 "\r\n"
955 1 hiro
                 "--%s--\r\n",
956 1 hiro
                 boundary);
957 1 hiro
    }
958 1 hiro
    fflush (fp);
959 1 hiro
    if (ferror (fp)) {
960 1 hiro
        FILE_OP_ERROR (file, "fwrite");
961 1 hiro
        goto failure;
962 1 hiro
    }
963 1 hiro
    fclose (fp);
964 1 hiro
    gpgme_data_release (cipher);
965 1 hiro
    return 0;
966 1 hiro
967 1 hiro
failure:
968 1 hiro
    if (fp)
969 1 hiro
        fclose (fp);
970 1 hiro
    gpgme_data_release (header);
971 1 hiro
    gpgme_data_release (plain);
972 1 hiro
    gpgme_data_release (cipher);
973 1 hiro
    gpgme_recipients_release (rset);
974 1 hiro
    g_free (boundary);
975 1 hiro
    return -1; /* error */
976 1 hiro
}
977 1 hiro
978 1 hiro
/*
979 1 hiro
 * plain contains an entire mime object.  Sign it and return an
980 1 hiro
 * GpgmeData object with the signature of it or NULL in case of error.
981 1 hiro
 * r_siginfo returns an XML object with information about the signature.
982 1 hiro
 */
983 1 hiro
static GpgmeData
984 1 hiro
pgp_sign (GpgmeData plain, GSList *key_list, gboolean clearsign,
985 1 hiro
          char **r_siginfo)
986 1 hiro
{
987 1 hiro
    GSList *p;
988 1 hiro
    GpgmeCtx ctx = NULL;
989 1 hiro
    GpgmeError err;
990 1 hiro
    GpgmeData sig = NULL;
991 1 hiro
    struct passphrase_cb_info_s info;
992 1 hiro
993 1 hiro
    *r_siginfo = NULL;
994 1 hiro
    memset (&info, 0, sizeof info);
995 1 hiro
996 1 hiro
    err = gpgme_new (&ctx);
997 1 hiro
    if (err)
998 1 hiro
        goto leave;
999 1 hiro
    err = gpgme_data_new (&sig);
1000 1 hiro
    if (err)
1001 1 hiro
        goto leave;
1002 1 hiro
1003 1 hiro
    if (!getenv("GPG_AGENT_INFO")) {
1004 1 hiro
        info.c = ctx;
1005 1 hiro
        gpgme_set_passphrase_cb (ctx, gpgmegtk_passphrase_cb, &info);
1006 1 hiro
    }
1007 1 hiro
    gpgme_set_textmode (ctx, 1);
1008 1 hiro
    gpgme_set_armor (ctx, 1);
1009 1 hiro
    gpgme_signers_clear (ctx);
1010 1 hiro
    for (p = key_list; p != NULL; p = p->next) {
1011 1 hiro
        err = gpgme_signers_add (ctx, (GpgmeKey) p->data);
1012 1 hiro
        if (err)
1013 1 hiro
            goto leave;
1014 1 hiro
    }
1015 1 hiro
    for (p = key_list; p != NULL; p = p->next)
1016 1 hiro
        gpgme_key_unref ((GpgmeKey) p->data);
1017 1 hiro
    g_slist_free (key_list);
1018 1 hiro
1019 1 hiro
    if (err)
1020 1 hiro
        goto leave;
1021 1 hiro
    err = gpgme_op_sign
1022 1 hiro
        (ctx, plain, sig,
1023 1 hiro
         clearsign ? GPGME_SIG_MODE_CLEAR : GPGME_SIG_MODE_DETACH);
1024 1 hiro
    if (!err)
1025 1 hiro
        *r_siginfo = gpgme_get_op_info (ctx, 0);
1026 1 hiro
1027 1 hiro
leave:
1028 1 hiro
    if (err) {
1029 1 hiro
        gpgmegtk_free_passphrase();
1030 1 hiro
        debug_print ("signing failed: %s\n", gpgme_strerror (err));
1031 1 hiro
        gpgme_data_release (sig);
1032 1 hiro
        sig = NULL;
1033 1 hiro
    }
1034 1 hiro
    else {
1035 1 hiro
        debug_print ("signing succeeded\n");
1036 1 hiro
    }
1037 1 hiro
1038 1 hiro
    gpgme_release (ctx);
1039 1 hiro
    return sig;
1040 1 hiro
}
1041 1 hiro
1042 1 hiro
/*
1043 1 hiro
 * Find TAG in XML and return a pointer into xml set just behind the
1044 1 hiro
 * closing angle.  Return NULL if not found.
1045 1 hiro
 */
1046 1 hiro
static const char *
1047 1 hiro
find_xml_tag (const char *xml, const char *tag)
1048 1 hiro
{
1049 1 hiro
    int taglen = strlen (tag);
1050 1 hiro
    const char *s = xml;
1051 1 hiro
1052 1 hiro
    while ( (s = strchr (s, '<')) ) {
1053 1 hiro
        s++;
1054 1 hiro
        if (!strncmp (s, tag, taglen)) {
1055 1 hiro
            const char *s2 = s + taglen;
1056 1 hiro
            if (*s2 == '>' || isspace (*(const unsigned char*)s2) ) {
1057 1 hiro
                /* found */
1058 1 hiro
                while (*s2 && *s2 != '>') /* skip attributes */
1059 1 hiro
                    s2++;
1060 1 hiro
                /* fixme: do need to handle angles inside attribute vallues? */
1061 1 hiro
                return *s2? (s2+1):NULL;
1062 1 hiro
            }
1063 1 hiro
        }
1064 1 hiro
        while (*s && *s != '>') /* skip to end of tag */
1065 1 hiro
            s++;
1066 1 hiro
    }
1067 1 hiro
    return NULL;
1068 1 hiro
}
1069 1 hiro
1070 1 hiro
1071 1 hiro
/*
1072 1 hiro
 * Extract the micalg from an GnupgOperationInfo XML container.
1073 1 hiro
 */
1074 1 hiro
static char *
1075 1 hiro
extract_micalg (char *xml)
1076 1 hiro
{
1077 1 hiro
    const char *s;
1078 1 hiro
1079 1 hiro
    s = find_xml_tag (xml, "GnupgOperationInfo");
1080 1 hiro
    if (s) {
1081 1 hiro
        const char *s_end = find_xml_tag (s, "/GnupgOperationInfo");
1082 1 hiro
        s = find_xml_tag (s, "signature");
1083 1 hiro
        if (s && s_end && s < s_end) {
1084 1 hiro
            const char *s_end2 = find_xml_tag (s, "/signature");
1085 1 hiro
            if (s_end2 && s_end2 < s_end) {
1086 1 hiro
                s = find_xml_tag (s, "micalg");
1087 1 hiro
                if (s && s < s_end2) {
1088 1 hiro
                    s_end = strchr (s, '<');
1089 1 hiro
                    if (s_end) {
1090 1 hiro
                        char *p = g_malloc (s_end - s + 1);
1091 1 hiro
                        memcpy (p, s, s_end - s);
1092 1 hiro
                        p[s_end-s] = 0;
1093 1 hiro
                        return p;
1094 1 hiro
                    }
1095 1 hiro
                }
1096 1 hiro
            }
1097 1 hiro
        }
1098 1 hiro
    }
1099 1 hiro
    return NULL;
1100 1 hiro
}
1101 1 hiro
1102 1 hiro
1103 1 hiro
/*
1104 1 hiro
 * Sign the file and replace its content with the signed one.
1105 1 hiro
 */
1106 1 hiro
int
1107 1 hiro
rfc2015_sign (const char *file, GSList *key_list)
1108 1 hiro
{
1109 1 hiro
    FILE *fp = NULL;
1110 1 hiro
    char buf[BUFFSIZE];
1111 1 hiro
    int i, clineidx, saved_last;
1112 1 hiro
    char *clines[3] = {NULL};
1113 1 hiro
    GpgmeError err;
1114 1 hiro
    GpgmeData header = NULL;
1115 1 hiro
    GpgmeData plain = NULL;
1116 1 hiro
    GpgmeData sigdata = NULL;
1117 1 hiro
    size_t nread;
1118 1 hiro
    int mime_version_seen = 0;
1119 1 hiro
    char *boundary;
1120 1 hiro
    char *micalg = NULL;
1121 1 hiro
    char *siginfo;
1122 1 hiro
1123 1 hiro
    boundary = generate_mime_boundary ("Signature");
1124 1 hiro
1125 1 hiro
    /* Open the source file */
1126 1 hiro
    if ((fp = fopen(file, "rb")) == NULL) {
1127 1 hiro
        FILE_OP_ERROR(file, "fopen");
1128 1 hiro
        goto failure;
1129 1 hiro
    }
1130 1 hiro
1131 1 hiro
    err = gpgme_data_new (&header);
1132 1 hiro
    if (!err)
1133 1 hiro
        err = gpgme_data_new (&plain);
1134 1 hiro
    if (err) {
1135 1 hiro
        debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
1136 1 hiro
        goto failure;
1137 1 hiro
    }
1138 1 hiro
1139 1 hiro
    /* get the content header lines from the source */
1140 1 hiro
    clineidx = 0;
1141 1 hiro
    saved_last = 0;
1142 1 hiro
    while (!err && fgets(buf, sizeof(buf), fp)) {
1143 1 hiro
        /* fixme: check for overlong lines */
1144 1 hiro
        if (headerp (buf, content_names)) {
1145 1 hiro
            if (clineidx >= DIM (clines)) {
1146 1 hiro
                debug_print ("rfc2015_sign: too many content lines\n");
1147 1 hiro
                goto failure;
1148 1 hiro
            }
1149 1 hiro
            clines[clineidx++] = g_strdup (buf);
1150 1 hiro
            saved_last = 1;
1151 1 hiro
            continue;
1152 1 hiro
        }
1153 1 hiro
        if (saved_last) {
1154 1 hiro
            if (*buf == ' ' || *buf == '\t') {
1155 1 hiro
                char *last = clines[clineidx - 1];
1156 1 hiro
                clines[clineidx - 1] = g_strconcat (last, buf, NULL);
1157 1 hiro
                g_free (last);
1158 1 hiro
                continue;
1159 1 hiro
            }
1160 1 hiro
            saved_last = 0;
1161 1 hiro
        }
1162 1 hiro
1163 1 hiro
        if (headerp (buf, mime_version_name))
1164 1 hiro
            mime_version_seen = 1;
1165 1 hiro
1166 1 hiro
        if (buf[0] == '\r' || buf[0] == '\n')
1167 1 hiro
            break;
1168 1 hiro
        err = gpgme_data_write (header, buf, strlen (buf));
1169 1 hiro
    }
1170 1 hiro
    if (ferror (fp)) {
1171 1 hiro
        FILE_OP_ERROR (file, "fgets");
1172 1 hiro
        goto failure;
1173 1 hiro
    }
1174 1 hiro
1175 1 hiro
    /* write them to the temp data and add the rest of the message */
1176 1 hiro
    for (i = 0; !err && i < clineidx; i++) {
1177 1 hiro
        err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
1178 1 hiro
    }
1179 1 hiro
    if (!err)
1180 1 hiro
        err = gpgme_data_write (plain, "\r\n", 2 );
1181 1 hiro
    while (!err && fgets(buf, sizeof(buf), fp)) {
1182 1 hiro
        err = gpgme_data_write (plain, buf, strlen (buf));
1183 1 hiro
    }
1184 1 hiro
    if (ferror (fp)) {
1185 1 hiro
        FILE_OP_ERROR (file, "fgets");
1186 1 hiro
        goto failure;
1187 1 hiro
    }
1188 1 hiro
    if (err) {
1189 1 hiro
        debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
1190 1 hiro
        goto failure;
1191 1 hiro
    }
1192 1 hiro
1193 1 hiro
    sigdata = pgp_sign (plain, key_list, FALSE, &siginfo);
1194 1 hiro
    if (siginfo) {
1195 1 hiro
        micalg = extract_micalg (siginfo);
1196 1 hiro
        free (siginfo);
1197 1 hiro
    }
1198 1 hiro
    if (!sigdata)
1199 1 hiro
        goto failure;
1200 1 hiro
1201 1 hiro
    /* we have the signed message available in sigdata and now we are
1202 1 hiro
     * going to rewrite the original file. To be sure that file has
1203 1 hiro
     * been truncated we use an approach which should work everywhere:
1204 1 hiro
     * close the file and then reopen it for writing. */
1205 1 hiro
    if (fclose (fp)) {
1206 1 hiro
        FILE_OP_ERROR(file, "fclose");
1207 1 hiro
        goto failure;
1208 1 hiro
    }
1209 1 hiro
    if ((fp = fopen(file, "wb")) == NULL) {
1210 1 hiro
        FILE_OP_ERROR(file, "fopen");
1211 1 hiro
        goto failure;
1212 1 hiro
    }
1213 1 hiro
1214 1 hiro
    /* Write the rfc822 header and add new content lines */
1215 1 hiro
    err = gpgme_data_rewind (header);
1216 1 hiro
    if (err)
1217 1 hiro
        debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
1218 1 hiro
    while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
1219 1 hiro
        fwrite (buf, nread, 1, fp);
1220 1 hiro
    }
1221 1 hiro
    if (err != GPGME_EOF) {
1222 1 hiro
        debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1223 1 hiro
        goto failure;
1224 1 hiro
    }
1225 1 hiro
    if (ferror (fp)) {
1226 1 hiro
        FILE_OP_ERROR (file, "fwrite");
1227 1 hiro
        goto failure;
1228 1 hiro
    }
1229 1 hiro
    gpgme_data_release (header);
1230 1 hiro
    header = NULL;
1231 1 hiro
1232 1 hiro
    if (!mime_version_seen)
1233 1 hiro
        fputs ("MIME-Version: 1.0\r\n", fp);
1234 1 hiro
    fprintf (fp, "Content-Type: multipart/signed; "
1235 1 hiro
             "protocol=\"application/pgp-signature\";\r\n");
1236 1 hiro
    if (micalg)
1237 1 hiro
        fprintf (fp, " micalg=\"%s\";\r\n", micalg);
1238 1 hiro
    fprintf (fp, " boundary=\"%s\"\r\n", boundary);
1239 1 hiro
1240 1 hiro
    /* Part 1: signed material */
1241 1 hiro
    fprintf (fp, "\r\n"
1242 1 hiro
                 "--%s\r\n",
1243 1 hiro
                 boundary);
1244 1 hiro
    err = gpgme_data_rewind (plain);
1245 1 hiro
    if (err) {
1246 1 hiro
        debug_print ("gpgme_data_rewind on plain failed: %s\n",
1247 1 hiro
                   gpgme_strerror (err));
1248 1 hiro
        goto failure;
1249 1 hiro
    }
1250 1 hiro
    while (!(err = gpgme_data_read (plain, buf, BUFFSIZE, &nread))) {
1251 1 hiro
        fwrite (buf, nread, 1, fp);
1252 1 hiro
    }
1253 1 hiro
    if (err != GPGME_EOF) {
1254 1 hiro
        debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1255 1 hiro
        goto failure;
1256 1 hiro
    }
1257 1 hiro
1258 1 hiro
    /* Part 2: signature */
1259 1 hiro
    fprintf (fp, "\r\n"
1260 1 hiro
                 "--%s\r\n",
1261 1 hiro
                 boundary);
1262 1 hiro
    fputs ("Content-Type: application/pgp-signature\r\n"
1263 1 hiro
           "\r\n", fp);
1264 1 hiro
1265 1 hiro
    err = gpgme_data_rewind (sigdata);
1266 1 hiro
    if (err) {
1267 1 hiro
        debug_print ("gpgme_data_rewind on sigdata failed: %s\n",
1268 1 hiro
                   gpgme_strerror (err));
1269 1 hiro
        goto failure;
1270 1 hiro
    }
1271 1 hiro
1272 1 hiro
    while (!(err = gpgme_data_read (sigdata, buf, BUFFSIZE, &nread))) {
1273 1 hiro
        fwrite (buf, nread, 1, fp);
1274 1 hiro
    }
1275 1 hiro
    if (err != GPGME_EOF) {
1276 1 hiro
        debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1277 1 hiro
        goto failure;
1278 1 hiro
    }
1279 1 hiro
1280 1 hiro
    /* Final boundary */
1281 1 hiro
    fprintf (fp, "\r\n"
1282 1 hiro
                 "--%s--\r\n",
1283 1 hiro
                 boundary);
1284 1 hiro
    fflush (fp);
1285 1 hiro
    if (ferror (fp)) {
1286 1 hiro
        FILE_OP_ERROR (file, "fwrite");
1287 1 hiro
        goto failure;
1288 1 hiro
    }
1289 1 hiro
    fclose (fp);
1290 1 hiro
    gpgme_data_release (header);
1291 1 hiro
    gpgme_data_release (plain);
1292 1 hiro
    gpgme_data_release (sigdata);
1293 1 hiro
    g_free (boundary);
1294 1 hiro
    g_free (micalg);
1295 1 hiro
    return 0;
1296 1 hiro
1297 1 hiro
failure:
1298 1 hiro
    if (fp)
1299 1 hiro
        fclose (fp);
1300 1 hiro
    gpgme_data_release (header);
1301 1 hiro
    gpgme_data_release (plain);
1302 1 hiro
    gpgme_data_release (sigdata);
1303 1 hiro
    g_free (boundary);
1304 1 hiro
    g_free (micalg);
1305 1 hiro
    return -1; /* error */
1306 1 hiro
}
1307 1 hiro
1308 1 hiro
1309 1 hiro
/*
1310 1 hiro
 * Sign the file with clear text and replace its content with the signed one.
1311 1 hiro
 */
1312 1 hiro
gint
1313 1 hiro
rfc2015_clearsign (const gchar *file, GSList *key_list)
1314 1 hiro
{
1315 1 hiro
    FILE *fp;
1316 1 hiro
    gchar buf[BUFFSIZE];
1317 1 hiro
    GpgmeError err;
1318 1 hiro
    GpgmeData text = NULL;
1319 1 hiro
    GpgmeData sigdata = NULL;
1320 1 hiro
    size_t nread;
1321 1 hiro
    gchar *siginfo;
1322 1 hiro
1323 1 hiro
    if ((fp = fopen(file, "rb")) == NULL) {
1324 1 hiro
        FILE_OP_ERROR(file, "fopen");
1325 1 hiro
        goto failure;
1326 1 hiro
    }
1327 1 hiro
1328 1 hiro
    err = gpgme_data_new(&text);
1329 1 hiro
    if (err) {
1330 1 hiro
        debug_print("gpgme_data_new failed: %s\n", gpgme_strerror(err));
1331 1 hiro
        goto failure;
1332 1 hiro
    }
1333 1 hiro
1334 1 hiro
    while (!err && fgets(buf, sizeof(buf), fp)) {
1335 1 hiro
        err = gpgme_data_write(text, buf, strlen(buf));
1336 1 hiro
    }
1337 1 hiro
    if (ferror(fp)) {
1338 1 hiro
        FILE_OP_ERROR(file, "fgets");
1339 1 hiro
        goto failure;
1340 1 hiro
    }
1341 1 hiro
    if (err) {
1342 1 hiro
        debug_print("gpgme_data_write failed: %s\n", gpgme_strerror(err));
1343 1 hiro
        goto failure;
1344 1 hiro
    }
1345 1 hiro
1346 1 hiro
    sigdata = pgp_sign(text, key_list, TRUE, &siginfo);
1347 1 hiro
    if (siginfo) {
1348 1 hiro
        g_free(siginfo);
1349 1 hiro
    }
1350 1 hiro
    if (!sigdata)
1351 1 hiro
        goto failure;
1352 1 hiro
1353 1 hiro
    if (fclose(fp) == EOF) {
1354 1 hiro
        FILE_OP_ERROR(file, "fclose");
1355 1 hiro
        fp = NULL;
1356 1 hiro
        goto failure;
1357 1 hiro
    }
1358 1 hiro
    if ((fp = fopen(file, "wb")) == NULL) {
1359 1 hiro
        FILE_OP_ERROR(file, "fopen");
1360 1 hiro
        goto failure;
1361 1 hiro
    }
1362 1 hiro
1363 1 hiro
    err = gpgme_data_rewind(sigdata);
1364 1 hiro
    if (err) {
1365 1 hiro
        debug_print("gpgme_data_rewind on sigdata failed: %s\n",
1366 1 hiro
                    gpgme_strerror(err));
1367 1 hiro
        goto failure;
1368 1 hiro
    }
1369 1 hiro
1370 1 hiro
    while (!(err = gpgme_data_read(sigdata, buf, sizeof(buf), &nread))) {
1371 1 hiro
        fwrite(buf, nread, 1, fp);
1372 1 hiro
    }
1373 1 hiro
    if (err != GPGME_EOF) {
1374 1 hiro
        debug_print("gpgme_data_read failed: %s\n", gpgme_strerror(err));
1375 1 hiro
        goto failure;
1376 1 hiro
    }
1377 1 hiro
1378 1 hiro
    if (fclose(fp) == EOF) {
1379 1 hiro
        FILE_OP_ERROR(file, "fclose");
1380 1 hiro
        fp = NULL;
1381 1 hiro
        goto failure;
1382 1 hiro
    }
1383 1 hiro
    gpgme_data_release(text);
1384 1 hiro
    gpgme_data_release(sigdata);
1385 1 hiro
    return 0;
1386 1 hiro
1387 1 hiro
failure:
1388 1 hiro
    if (fp)
1389 1 hiro
        fclose(fp);
1390 1 hiro
    gpgme_data_release(text);
1391 1 hiro
    gpgme_data_release(sigdata);
1392 1 hiro
    return -1;
1393 1 hiro
}
1394 1 hiro
1395 1 hiro
#endif /* USE_GPGME */