2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2001 Werner Koch (dd9jn)
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.
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.
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.
38 #include "procheader.h"
44 #include "prefs_common.h"
45 #include "passphrase.h"
46 #include "select-keys.h"
47 #include "sigstatus.h"
50 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
52 static char *content_names[] = {
54 "Content-Disposition",
55 "Content-Transfer-Encoding",
59 static char *mime_version_name[] = {
65 struct passphrase_cb_info_s {
70 static char *create_boundary (void);
73 static void dump_mimeinfo (const char *text, MimeInfo *x)
75 debug_print ("MimeInfo[%s] %p level=%d\n",
76 text, x, x? x->level:0 );
80 debug_print (" enc=`%s' enc_type=%d mime_type=%d\n",
81 x->encoding, x->encoding_type, x->mime_type );
82 debug_print (" cont_type=`%s' cs=`%s' name=`%s' bnd=`%s'\n",
83 x->content_type, x->charset, x->name, x->boundary );
84 debug_print (" cont_disp=`%s' fname=`%s' fpos=%ld size=%u, lvl=%d\n",
85 x->content_disposition, x->filename, x->fpos, x->size,
87 dump_mimeinfo (".main", x->main );
88 dump_mimeinfo (".sub", x->sub );
89 dump_mimeinfo (".next", x->next );
90 debug_print ("MimeInfo[.parent] %p\n", x );
91 dump_mimeinfo (".children", x->children );
92 dump_mimeinfo (".plaintext", x->plaintext );
95 static void dump_part ( MimeInfo *mimeinfo, FILE *fp )
97 unsigned int size = mimeinfo->size;
100 if (fseek (fp, mimeinfo->fpos, SEEK_SET)) {
101 debug_print ("dump_part: fseek error\n");
105 debug_print ("--- begin dump_part ----\n");
106 while (size-- && (c = getc (fp)) != EOF)
109 debug_print ("dump_part: read error\n");
110 debug_print ("--- end dump_part ----\n");
115 rfc2015_disable_all (void)
117 /* FIXME: set a flag, so that we don't bother the user with failed
123 rfc2015_secure_remove (const char *fname)
127 /* fixme: overwrite the file first */
133 sig_status_to_string (GpgmeSigStat status)
138 case GPGME_SIG_STAT_NONE:
139 result = _("Oops: Signature not verified");
141 case GPGME_SIG_STAT_NOSIG:
142 result = _("No signature found");
144 case GPGME_SIG_STAT_GOOD:
145 result = _("Good signature");
147 case GPGME_SIG_STAT_BAD:
148 result = _("BAD signature");
150 case GPGME_SIG_STAT_NOKEY:
151 result = _("No public key to verify the signature");
153 case GPGME_SIG_STAT_ERROR:
154 result = _("Error verifying the signature");
156 case GPGME_SIG_STAT_DIFF:
157 result = _("Different results for signatures");
160 result = _("Error: Unknown status");
168 sig_status_with_name (GpgmeSigStat status)
173 case GPGME_SIG_STAT_NONE:
174 result = _("Oops: Signature not verified");
176 case GPGME_SIG_STAT_NOSIG:
177 result = _("No signature found");
179 case GPGME_SIG_STAT_GOOD:
180 result = _("Good signature from \"%s\"");
182 case GPGME_SIG_STAT_BAD:
183 result = _("BAD signature from \"%s\"");
185 case GPGME_SIG_STAT_NOKEY:
186 result = _("No public key to verify the signature");
188 case GPGME_SIG_STAT_ERROR:
189 result = _("Error verifying the signature");
191 case GPGME_SIG_STAT_DIFF:
192 result = _("Different results for signatures");
195 result = _("Error: Unknown status");
203 sig_status_for_key(GString *str, GpgmeCtx ctx, GpgmeSigStat status,
204 GpgmeKey key, const gchar *fpr)
209 uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID, NULL, idx);
211 g_string_sprintfa (str, "%s\n",
212 sig_status_to_string (status));
213 if ((fpr != NULL) && (*fpr != '\0'))
214 g_string_sprintfa (str, "Key fingerprint: %s\n", fpr);
215 g_string_append (str, _("Cannot find user ID for this key."));
218 g_string_sprintfa (str, sig_status_with_name (status), uid);
219 g_string_append (str, "\n");
222 uid = gpgme_key_get_string_attr (key, GPGME_ATTR_USERID,
226 g_string_sprintfa (str, _(" aka \"%s\"\n"),
232 sig_status_full (GpgmeCtx ctx)
241 struct tm *ctime_val;
245 str = g_string_new ("");
247 fpr = gpgme_get_sig_status (ctx, sig_idx, &status, &created);
248 while (fpr != NULL) {
250 ctime_val = localtime (&created);
251 strftime (ctime_str, sizeof (ctime_str), "%c",
253 g_string_sprintfa (str,
254 _("Signature made %s\n"),
257 err = gpgme_get_sig_key (ctx, sig_idx, &key);
259 g_string_sprintfa (str, "%s\n",
260 sig_status_to_string (status));
261 if ((fpr != NULL) && (*fpr != '\0'))
262 g_string_sprintfa (str,
263 _("Key fingerprint: %s\n"),
266 sig_status_for_key (str, ctx, status, key, fpr);
267 gpgme_key_unref (key);
269 g_string_append (str, "\n\n");
271 fpr = gpgme_get_sig_status (ctx, ++sig_idx, &status, &created);
275 g_string_free (str, FALSE);
279 static void check_signature (MimeInfo *mimeinfo, MimeInfo *partinfo, FILE *fp)
283 GpgmeData sig = NULL, text = NULL;
284 GpgmeSigStat status = GPGME_SIG_STAT_NONE;
285 GpgmegtkSigStatus statuswindow = NULL;
286 const char *result = NULL;
288 if (prefs_common.gpg_signature_popup)
289 statuswindow = gpgmegtk_sig_status_create ();
291 err = gpgme_new (&ctx);
293 debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
297 /* don't include the last character (LF). It does not belong to the
299 err = gpgme_data_new_from_filepart (&text, NULL, fp,
300 mimeinfo->children->fpos,
301 mimeinfo->children->size ?
302 (mimeinfo->children->size - 1) : 0 );
304 err = gpgme_data_new_from_filepart (&sig, NULL, fp,
305 partinfo->fpos, partinfo->size);
307 debug_print ("gpgme_data_new_from_filepart failed: %s\n",
308 gpgme_strerror (err));
312 err = gpgme_op_verify (ctx, sig, text, &status);
314 debug_print ("gpgme_op_verify failed: %s\n", gpgme_strerror (err));
316 /* FIXME: check what the heck this sig_status_full stuff is.
317 * it should better go into sigstatus.c */
318 g_free (partinfo->sigstatus_full);
319 partinfo->sigstatus_full = sig_status_full (ctx);
322 result = gpgmegtk_sig_status_to_string(status);
323 debug_print("verification status: %s\n", result);
324 if (prefs_common.gpg_signature_popup)
325 gpgmegtk_sig_status_update (statuswindow,ctx);
327 g_assert (!err); /* FIXME: Hey: this may indeed happen */
328 g_free (partinfo->sigstatus);
329 partinfo->sigstatus = g_strdup (result);
331 gpgme_data_release (sig);
332 gpgme_data_release (text);
334 if (prefs_common.gpg_signature_popup)
335 gpgmegtk_sig_status_destroy (statuswindow);
339 passphrase_cb (void *opaque, const char *desc, void *r_hd)
341 struct passphrase_cb_info_s *info = opaque;
342 GpgmeCtx ctx = info ? info->c : NULL;
346 /* FIXME: cleanup by looking at *r_hd */
350 gpgmegtk_set_passphrase_grab (prefs_common.passphrase_grab);
351 debug_print ("%% requesting passphrase for `%s': ", desc );
352 pass = gpgmegtk_passphrase_mbox (desc);
354 debug_print ("%% cancel passphrase entry");
358 debug_print ("%% sending passphrase");
364 * Copy a gpgme data object to a temporary file and
365 * return this filename
369 copy_gpgmedata_to_temp (GpgmeData data, guint *length)
378 tmp = g_strdup_printf("%s%cgpgtmp.%08x",
379 get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id );
381 if ((fp = fopen(tmp, "w")) == NULL) {
382 FILE_OP_ERROR(tmp, "fopen");
387 err = gpgme_data_rewind ( data );
389 debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
391 while (!(err = gpgme_data_read (data, buf, 100, &nread))) {
392 fwrite ( buf, nread, 1, fp );
395 if (err != GPGME_EOF)
396 debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
406 pgp_decrypt (MimeInfo *partinfo, FILE *fp)
410 GpgmeData cipher = NULL, plain = NULL;
411 struct passphrase_cb_info_s info;
413 memset (&info, 0, sizeof info);
415 err = gpgme_new (&ctx);
417 debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
421 err = gpgme_data_new_from_filepart (&cipher, NULL, fp,
422 partinfo->fpos, partinfo->size);
424 debug_print ("gpgme_data_new_from_filepart failed: %s\n",
425 gpgme_strerror (err));
429 err = gpgme_data_new (&plain);
431 debug_print ("gpgme_new failed: %s\n", gpgme_strerror (err));
435 if (!getenv("GPG_AGENT_INFO")) {
437 gpgme_set_passphrase_cb (ctx, passphrase_cb, &info);
440 err = gpgme_op_decrypt (ctx, cipher, plain);
443 gpgme_data_release (cipher);
445 debug_print ("decryption failed: %s\n", gpgme_strerror (err));
446 gpgme_data_release (plain);
450 debug_print ("** decryption succeeded");
456 MimeInfo * rfc2015_find_signature (MimeInfo *mimeinfo)
463 if (g_strcasecmp (mimeinfo->content_type, "multipart/signed"))
466 debug_print ("** multipart/signed encountered");
468 /* check that we have at least 2 parts of the correct type */
469 for (partinfo = mimeinfo->children;
470 partinfo != NULL; partinfo = partinfo->next) {
471 if (++n > 1 && !g_strcasecmp (partinfo->content_type,
472 "application/pgp-signature"))
479 gboolean rfc2015_has_signature (MimeInfo *mimeinfo)
481 return rfc2015_find_signature (mimeinfo) != NULL;
484 void rfc2015_check_signature (MimeInfo *mimeinfo, FILE *fp)
488 partinfo = rfc2015_find_signature (mimeinfo);
493 g_message ("** yep, it is a pgp signature");
494 dump_mimeinfo ("gpg-signature", partinfo );
495 dump_part (partinfo, fp );
496 dump_mimeinfo ("signed text", mimeinfo->children );
497 dump_part (mimeinfo->children, fp);
500 check_signature (mimeinfo, partinfo, fp);
503 int rfc2015_is_encrypted (MimeInfo *mimeinfo)
507 if (g_strcasecmp (mimeinfo->content_type, "multipart/encrypted"))
509 /* fixme: we should schek the protocol parameter */
514 name_cmp(const char *a, const char *b)
516 for( ; *a && *b; a++, b++) {
518 && toupper(*(unsigned char *)a) != toupper(*(unsigned char *)b))
526 headerp(char *p, char **names)
535 if(p2[-1] == ' ' || p2[-1] == '\t') {
544 for(i = 0 ; names[i] != NULL; i++) {
545 if(!name_cmp (names[i], p))
550 return names[i] != NULL;
554 #define DECRYPTION_ABORT() \
556 procmime_mimeinfo_free_all(tmpinfo); \
557 msginfo->decryption_failed = 1; \
561 void rfc2015_decrypt_message (MsgInfo *msginfo, MimeInfo *mimeinfo, FILE *fp)
564 MimeInfo *tmpinfo, *partinfo;
573 g_return_if_fail (msginfo != NULL);
574 g_return_if_fail (mimeinfo != NULL);
575 g_return_if_fail (fp != NULL);
576 g_return_if_fail (mimeinfo->mime_type == MIME_MULTIPART);
578 debug_print ("** decrypting multipart/encrypted message");
581 if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
583 tmpinfo = procmime_scan_mime_header(fp);
584 if (!tmpinfo || tmpinfo->mime_type != MIME_MULTIPART) {
588 procmime_scan_multipart_message(tmpinfo, fp);
590 /* check that we have the 2 parts */
591 partinfo = tmpinfo->children;
592 if (!partinfo || !partinfo->next) {
595 if (!g_strcasecmp (partinfo->content_type, "application/pgp-encrypted")) {
596 /* Fixme: check that the version is 1 */
599 partinfo = partinfo->next;
601 !g_strcasecmp (partinfo->content_type, "application/octet-stream")) {
603 g_warning ("oops: pgp_encrypted with more than 2 parts");
609 debug_print ("** yep, it is pgp encrypted");
611 plain = pgp_decrypt (partinfo, fp);
616 fname = g_strdup_printf("%s%cplaintext.%08x",
617 get_mime_tmp_dir(), G_DIR_SEPARATOR, ++id);
619 if ((dstfp = fopen(fname, "w")) == NULL) {
620 FILE_OP_ERROR(fname, "fopen");
625 /* write the orginal header to the new file */
626 if (fseek(fp, tmpinfo->fpos, SEEK_SET) < 0)
629 while (fgets(buf, sizeof(buf), fp)) {
630 if (headerp (buf, content_names))
632 if (buf[0] == '\r' || buf[0] == '\n')
637 err = gpgme_data_rewind (plain);
639 debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
641 while (!(err = gpgme_data_read (plain, buf, sizeof(buf), &nread))) {
642 fwrite (buf, nread, 1, dstfp);
645 if (err != GPGME_EOF) {
646 debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
650 procmime_mimeinfo_free_all(tmpinfo);
652 msginfo->plaintext_file = fname;
653 msginfo->decryption_failed = 0;
656 #undef DECRYPTION_ABORT
660 * plain contains an entire mime object.
661 * Encrypt it and return an GpgmeData object with the encrypted version of
662 * the file or NULL in case of error.
665 pgp_encrypt ( GpgmeData plain, GpgmeRecipients rset )
669 GpgmeData cipher = NULL;
671 err = gpgme_new (&ctx);
673 err = gpgme_data_new (&cipher);
675 gpgme_set_armor (ctx, 1);
676 err = gpgme_op_encrypt (ctx, rset, plain, cipher);
680 debug_print ("encryption failed: %s\n", gpgme_strerror (err));
681 gpgme_data_release (cipher);
685 debug_print ("** encryption succeeded");
693 * Create and return a list of keys matching a key id
696 GSList *rfc2015_create_signers_list (const char *keyid)
698 GSList *key_list = NULL;
699 GpgmeCtx list_ctx = NULL;
704 err = gpgme_new (&list_ctx);
707 err = gpgme_op_keylist_start (list_ctx, keyid, 1);
710 while ( !(err = gpgme_op_keylist_next (list_ctx, &key)) ) {
711 key_list = g_slist_append (key_list, key);
713 if (err != GPGME_EOF)
716 if (key_list == NULL) {
717 debug_print ("no keys found for keyid \"%s\"\n", keyid);
722 debug_print ("rfc2015_create_signers_list failed: %s\n", gpgme_strerror (err));
723 for (p = key_list; p != NULL; p = p->next)
724 gpgme_key_unref ((GpgmeKey) p->data);
725 g_slist_free (key_list);
728 gpgme_release (list_ctx);
729 return err ? NULL : key_list;
733 * Encrypt the file by extracting all recipients and finding the
734 * encryption keys for all of them. The file content is then replaced
735 * by the encrypted one. */
737 rfc2015_encrypt (const char *file, GSList *recp_list, gboolean ascii_armored)
741 int i, clineidx, saved_last;
742 char *clines[3] = {NULL};
744 GpgmeData header = NULL;
745 GpgmeData plain = NULL;
746 GpgmeData cipher = NULL;
747 GpgmeRecipients rset = NULL;
749 int mime_version_seen = 0;
750 char *boundary = create_boundary ();
752 /* Create the list of recipients */
753 rset = gpgmegtk_recipient_selection (recp_list);
755 debug_print ("error creating recipient list\n" );
759 /* Open the source file */
760 if ((fp = fopen(file, "rb")) == NULL) {
761 FILE_OP_ERROR(file, "fopen");
765 err = gpgme_data_new (&header);
767 err = gpgme_data_new (&plain);
769 debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
773 /* get the content header lines from the source */
776 while (!err && fgets(buf, sizeof(buf), fp)) {
777 /* fixme: check for overlong lines */
778 if (headerp (buf, content_names)) {
779 if (clineidx >= DIM (clines)) {
780 debug_print ("rfc2015_encrypt: too many content lines\n");
783 clines[clineidx++] = g_strdup (buf);
789 if (*buf == ' ' || *buf == '\t') {
790 char *last = clines[clineidx-1];
791 clines[clineidx-1] = g_strconcat (last, buf, NULL);
797 if (headerp (buf, mime_version_name))
798 mime_version_seen = 1;
800 if (buf[0] == '\r' || buf[0] == '\n')
802 err = gpgme_data_write (header, buf, strlen (buf));
805 FILE_OP_ERROR (file, "fgets");
809 /* write them to the temp data and add the rest of the message */
810 for (i = 0; !err && i < clineidx; i++) {
811 debug_print ("%% %s:%d: cline=`%s'", __FILE__ ,__LINE__, clines[i]);
812 err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
815 err = gpgme_data_write (plain, "\r\n", 2);
816 while (!err && fgets(buf, sizeof(buf), fp)) {
817 err = gpgme_data_write (plain, buf, strlen (buf));
820 FILE_OP_ERROR (file, "fgets");
824 debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
828 cipher = pgp_encrypt (plain, rset);
829 gpgme_data_release (plain); plain = NULL;
830 gpgme_recipients_release (rset); rset = NULL;
834 /* we have the encrypted message available in cipher and now we
835 * are going to rewrite the source file. To be sure that file has
836 * been truncated we use an approach which should work everywhere:
837 * close the file and then reopen it for writing. It is important
838 * that this works, otherwise it may happen that parts of the
839 * plaintext are still in the file (The encrypted stuff is, due to
840 * compression, usually shorter than the plaintext).
842 * Yes, there is a race condition here, but everyone, who is so
843 * stupid to store the temp file with the plaintext in a public
844 * directory has to live with this anyway. */
846 FILE_OP_ERROR(file, "fclose");
849 if ((fp = fopen(file, "wb")) == NULL) {
850 FILE_OP_ERROR(file, "fopen");
854 /* Write the header, append new content lines, part 1 and part 2 header */
855 err = gpgme_data_rewind (header);
857 debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
860 while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
861 fwrite (buf, nread, 1, fp);
863 if (err != GPGME_EOF) {
864 debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
868 FILE_OP_ERROR (file, "fwrite");
871 gpgme_data_release (header); header = NULL;
873 if (!mime_version_seen)
874 fputs ("MIME-Version: 1\r\n", fp);
878 "Content-Type: text/plain; charset=us-ascii\r\n"
879 "Content-Transfer-Encoding: 7bit\r\n"
883 "Content-Type: multipart/encrypted;"
884 " protocol=\"application/pgp-encrypted\";\r\n"
885 " boundary=\"%s\"\r\n"
888 "Content-Type: application/pgp-encrypted\r\n"
893 "Content-Type: application/octet-stream\r\n"
895 boundary, boundary, boundary);
898 /* append the encrypted stuff */
899 err = gpgme_data_rewind (cipher);
901 debug_print ("** gpgme_data_rewind on cipher failed: %s\n",
902 gpgme_strerror (err));
906 while (!(err = gpgme_data_read (cipher, buf, BUFFSIZE, &nread))) {
907 fwrite (buf, nread, 1, fp);
909 if (err != GPGME_EOF) {
910 debug_print ("** gpgme_data_read failed: %s\n", gpgme_strerror (err));
914 /* and the final boundary */
915 if (!ascii_armored) {
924 FILE_OP_ERROR (file, "fwrite");
928 gpgme_data_release (cipher);
934 gpgme_data_release (header);
935 gpgme_data_release (plain);
936 gpgme_data_release (cipher);
937 gpgme_recipients_release (rset);
939 return -1; /* error */
943 * plain contains an entire mime object. Sign it and return an
944 * GpgmeData object with the signature of it or NULL in case of error.
947 pgp_sign (GpgmeData plain, GSList *key_list)
952 GpgmeData sig = NULL;
953 struct passphrase_cb_info_s info;
955 memset (&info, 0, sizeof info);
957 err = gpgme_new (&ctx);
960 err = gpgme_data_new (&sig);
964 if (!getenv("GPG_AGENT_INFO")) {
966 gpgme_set_passphrase_cb (ctx, passphrase_cb, &info);
968 gpgme_set_textmode (ctx, 1);
969 gpgme_set_armor (ctx, 1);
970 gpgme_signers_clear (ctx);
971 for (p = key_list; p != NULL; p = p->next) {
972 err = gpgme_signers_add (ctx, (GpgmeKey) p->data);
976 for (p = key_list; p != NULL; p = p->next)
977 gpgme_key_unref ((GpgmeKey) p->data);
978 g_slist_free (key_list);
982 err = gpgme_op_sign (ctx, plain, sig, GPGME_SIG_MODE_DETACH);
986 debug_print ("signing failed: %s\n", gpgme_strerror (err));
987 gpgme_data_release (sig);
991 debug_print ("signing succeeded\n");
999 * Sign the file and replace its content with the signed one.
1002 rfc2015_sign (const char *file, GSList *key_list)
1006 int i, clineidx, saved_last;
1007 char *clines[3] = {NULL};
1009 GpgmeData header = NULL;
1010 GpgmeData plain = NULL;
1011 GpgmeData sigdata = NULL;
1013 int mime_version_seen = 0;
1014 char *boundary = create_boundary ();
1016 /* Open the source file */
1017 if ((fp = fopen(file, "rb")) == NULL) {
1018 FILE_OP_ERROR(file, "fopen");
1022 err = gpgme_data_new (&header);
1024 err = gpgme_data_new (&plain);
1026 debug_print ("gpgme_data_new failed: %s\n", gpgme_strerror (err));
1030 /* get the content header lines from the source */
1033 while (!err && fgets(buf, sizeof(buf), fp)) {
1034 /* fixme: check for overlong lines */
1035 if (headerp (buf, content_names)) {
1036 if (clineidx >= DIM (clines)) {
1037 debug_print ("rfc2015_sign: too many content lines\n");
1040 clines[clineidx++] = g_strdup (buf);
1046 if (*buf == ' ' || *buf == '\t') {
1047 char *last = clines[clineidx - 1];
1048 clines[clineidx - 1] = g_strconcat (last, buf, NULL);
1054 if (headerp (buf, mime_version_name))
1055 mime_version_seen = 1;
1057 if (buf[0] == '\r' || buf[0] == '\n')
1059 err = gpgme_data_write (header, buf, strlen (buf));
1062 FILE_OP_ERROR (file, "fgets");
1066 /* write them to the temp data and add the rest of the message */
1067 for (i = 0; !err && i < clineidx; i++) {
1068 err = gpgme_data_write (plain, clines[i], strlen (clines[i]));
1071 err = gpgme_data_write (plain, "\r\n", 2 );
1072 while (!err && fgets(buf, sizeof(buf), fp)) {
1073 err = gpgme_data_write (plain, buf, strlen (buf));
1076 FILE_OP_ERROR (file, "fgets");
1080 debug_print ("gpgme_data_write failed: %s\n", gpgme_strerror (err));
1084 sigdata = pgp_sign (plain, key_list);
1088 /* we have the signed message available in sigdata and now we are
1089 * going to rewrite the original file. To be sure that file has
1090 * been truncated we use an approach which should work everywhere:
1091 * close the file and then reopen it for writing. */
1093 FILE_OP_ERROR(file, "fclose");
1096 if ((fp = fopen(file, "wb")) == NULL) {
1097 FILE_OP_ERROR(file, "fopen");
1101 /* Write the rfc822 header and add new content lines */
1102 err = gpgme_data_rewind (header);
1104 debug_print ("gpgme_data_rewind failed: %s\n", gpgme_strerror (err));
1105 while (!(err = gpgme_data_read (header, buf, BUFFSIZE, &nread))) {
1106 fwrite (buf, nread, 1, fp);
1108 if (err != GPGME_EOF) {
1109 debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1113 FILE_OP_ERROR (file, "fwrite");
1116 gpgme_data_release (header);
1119 if (!mime_version_seen)
1120 fputs ("MIME-Version: 1\r\n", fp);
1121 fprintf (fp, "Content-Type: multipart/signed; "
1122 "protocol=\"application/pgp-signature\";\r\n"
1123 " boundary=\"%s\"\r\n", boundary );
1125 /* Part 1: signed material */
1126 fprintf (fp, "\r\n--%s\r\n", boundary);
1127 err = gpgme_data_rewind (plain);
1129 debug_print ("gpgme_data_rewind on plain failed: %s\n",
1130 gpgme_strerror (err));
1133 while (!(err = gpgme_data_read (plain, buf, BUFFSIZE, &nread))) {
1134 fwrite (buf, nread, 1, fp);
1136 if (err != GPGME_EOF) {
1137 debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1141 /* Part 2: signature */
1142 fprintf (fp, "\r\n--%s\r\n", boundary);
1143 fputs ("Content-Type: application/pgp-signature\r\n"
1146 err = gpgme_data_rewind (sigdata);
1148 debug_print ("gpgme_data_rewind on sigdata failed: %s\n",
1149 gpgme_strerror (err));
1153 while (!(err = gpgme_data_read (sigdata, buf, BUFFSIZE, &nread))) {
1154 fwrite (buf, nread, 1, fp);
1156 if (err != GPGME_EOF) {
1157 debug_print ("gpgme_data_read failed: %s\n", gpgme_strerror (err));
1161 /* Final boundary */
1162 fprintf (fp, "\r\n--%s--\r\n\r\n", boundary);
1165 FILE_OP_ERROR (file, "fwrite");
1169 gpgme_data_release (header);
1170 gpgme_data_release (plain);
1171 gpgme_data_release (sigdata);
1178 gpgme_data_release (header);
1179 gpgme_data_release (plain);
1180 gpgme_data_release (sigdata);
1182 return -1; /* error */
1187 * Create a new boundary in a way that it is very unlikely that this
1188 * will occur in the following text. It would be easy to ensure
1189 * uniqueness if everything is either quoted-printable or base64
1190 * encoded (note that conversion is allowed), but because MIME bodies
1191 * may be nested, it may happen that the same boundary has already
1192 * been used. We avoid scanning the message for conflicts and hope the
1195 * boundary := 0*69<bchars> bcharsnospace
1196 * bchars := bcharsnospace / " "
1197 * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
1198 * "+" / "_" / "," / "-" / "." /
1199 * "/" / ":" / "=" / "?"
1203 create_boundary (void)
1205 static char tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1206 "abcdefghijklmnopqrstuvwxyz"
1207 "1234567890'()+_,./:=?";
1214 /* We make the boundary depend on the pid, so that all running
1215 * processed generate different values even when they have been
1216 * started within the same second and srand48(time(NULL)) has been
1217 * used. I can't see whether this is really an advantage but it
1218 * doesn't do any harm.
1221 for(i = 0; i < sizeof(buf) - 1; i++) {
1222 buf[i] = tbl[(lrand48() ^ pid) % (sizeof(tbl) - 1)]; /* fill with random */
1223 if(buf[i] == '=' && equal == -1)
1228 /* now make sure that we do have the sequence "=." in it which cannot
1229 * be matched by quoted-printable or base64 encoding */
1230 if(equal != -1 && (equal+1) < i)
1237 return g_strdup(buf);
1240 #endif /* USE_GPGME */