📄 crypt-gpgme.c
字号:
/* crypt-gpgme.c - GPGME based crypto operations * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu> * Copyright (C) 1998,1999,2000 Thomas Roessler <roessler@guug.de> * Copyright (C) 2001 Thomas Roessler <roessler@guug.de> * Oliver Ehli <elmy@acm.org> * Copyright (C) 2002, 2003, 2004 g10 Code GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */#if HAVE_CONFIG_H# include "config.h"#endif#ifdef CRYPT_BACKEND_GPGME#include "mutt.h"#include "mutt_crypt.h"#include "mutt_menu.h"#include "mutt_curses.h"#include "mime.h"#include "copy.h"#include "pager.h"#include "sort.h"#include <sys/wait.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <sys/stat.h>#include <errno.h>#include <ctype.h>#include <gpgme.h>#ifdef HAVE_LOCALE_H#include <locale.h>#endif#ifdef HAVE_LANGINFO_D_T_FMT#include <langinfo.h>#endif#ifdef HAVE_SYS_TIME_H# include <sys/time.h>#endif#ifdef HAVE_SYS_RESOURCE_H# include <sys/resource.h>#endif/* * Helper macros. */#define digitp(p) (*(p) >= '0' && *(p) <= '9')#define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f'))#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))/* Values used for comparing addresses. */#define CRYPT_KV_VALID 1#define CRYPT_KV_ADDR 2#define CRYPT_KV_STRING 4#define CRYPT_KV_STRONGID 8#define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)/* * Type definitions. */struct crypt_cache{ char *what; char *dflt; struct crypt_cache *next;};struct dn_array_s{ char *key; char *value;};/* We work based on user IDs, getting from a user ID to the key is check and does not need any memory (gpgme uses reference counting). */typedef struct crypt_keyinfo{ struct crypt_keyinfo *next; gpgme_key_t kobj; int idx; /* and the user ID at this index */ const char *uid; /* and for convenience point to this user ID */ unsigned int flags; /* global and per uid flags (for convenience)*/} crypt_key_t;typedef struct crypt_entry{ size_t num; crypt_key_t *key;} crypt_entry_t;static struct crypt_cache *id_defaults = NULL;static gpgme_key_t signature_key = NULL;/* * General helper functions. *//* return true when S pints to a didgit or letter. */static intdigit_or_letter (const unsigned char *s){ return ( (*s >= '0' && *s < '9') || (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z'));}/* Print the utf-8 encoded string BUF of length LEN bytes to stream FP. Convert the character set. */static voidprint_utf8 (FILE *fp, const char *buf, size_t len){ char *tstr; tstr = safe_malloc (len+1); memcpy (tstr, buf, len); tstr[len] = 0; mutt_convert_string (&tstr, "utf-8", Charset, M_ICONV_HOOK_FROM); fputs (tstr, fp); FREE (&tstr);}/* * Key management. *//* Return the keyID for the key K. Note that this string is valid as long as K is valid */static const char *crypt_keyid (crypt_key_t *k){ const char *s = "????????"; if (k->kobj && k->kobj->subkeys) { s = k->kobj->subkeys->keyid; if ((! option (OPTPGPLONGIDS)) && (strlen (s) == 16)) /* Return only the short keyID. */ s += 8; } return s;}/* Return the hexstring fingerprint from the key K. */static const char *crypt_fpr (crypt_key_t *k){ const char *s = ""; if (k->kobj && k->kobj->subkeys) s = k->kobj->subkeys->fpr; return s;}/* Parse FLAGS and return a statically allocated(!) string with them. */static char *crypt_key_abilities (int flags){ static char buff[3]; if (!(flags & KEYFLAG_CANENCRYPT)) buff[0] = '-'; else if (flags & KEYFLAG_PREFER_SIGNING) buff[0] = '.'; else buff[0] = 'e'; if (!(flags & KEYFLAG_CANSIGN)) buff[1] = '-'; else if (flags & KEYFLAG_PREFER_ENCRYPTION) buff[1] = '.'; else buff[1] = 's'; buff[2] = '\0'; return buff;}/* Parse FLAGS and return a character describing the most important flag. */static char crypt_flags (int flags){ if (flags & KEYFLAG_REVOKED) return 'R'; else if (flags & KEYFLAG_EXPIRED) return 'X'; else if (flags & KEYFLAG_DISABLED) return 'd'; else if (flags & KEYFLAG_CRITICAL) return 'c'; else return ' ';}/* Return a copy of KEY. */static crypt_key_t *crypt_copy_key (crypt_key_t *key){ crypt_key_t *k; k = safe_calloc (1, sizeof *k); k->kobj = key->kobj; gpgme_key_ref (key->kobj); k->idx = key->idx; k->uid = key->uid; k->flags = key->flags; return k;}/* Release all the keys at the address of KEYLIST and set the address to NULL. */static void crypt_free_key (crypt_key_t **keylist){ while (*keylist) { crypt_key_t *k = (*keylist)->next; FREE (&k); *keylist = k; }}/* Return trute when key K is valid. */static int crypt_key_is_valid (crypt_key_t *k){ if (k->flags & KEYFLAG_CANTUSE) return 0; return 1;}/* Return true whe validity of KEY is sufficient. */static int crypt_id_is_strong (crypt_key_t *key){ gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN; gpgme_user_id_t uid = NULL; unsigned int is_strong = 0; unsigned int i = 0; if ((key->flags & KEYFLAG_ISX509)) return 1; for (i = 0, uid = key->kobj->uids; (i < key->idx) && uid; i++, uid = uid->next) ; if (uid) val = uid->validity; switch (val) { case GPGME_VALIDITY_UNKNOWN: case GPGME_VALIDITY_UNDEFINED: case GPGME_VALIDITY_NEVER: case GPGME_VALIDITY_MARGINAL: is_strong = 0; break; case GPGME_VALIDITY_FULL: case GPGME_VALIDITY_ULTIMATE: is_strong = 1; break; } return is_strong;}/* Return true when the KEY is valid, i.e. not marked as unusable. */static int crypt_id_is_valid (crypt_key_t *key){ return ! (key->flags & KEYFLAG_CANTUSE);}/* Return a bit vector describing how well the addresses ADDR and U_ADDR match and whether KEY is valid. */static int crypt_id_matches_addr (ADDRESS *addr, ADDRESS *u_addr, crypt_key_t *key){ int rv = 0; if (crypt_id_is_valid (key)) rv |= CRYPT_KV_VALID; if (crypt_id_is_strong (key)) rv |= CRYPT_KV_STRONGID; if (addr->mailbox && u_addr->mailbox && mutt_strcasecmp (addr->mailbox, u_addr->mailbox) == 0) rv |= CRYPT_KV_ADDR; if (addr->personal && u_addr->personal && mutt_strcasecmp (addr->personal, u_addr->personal) == 0) rv |= CRYPT_KV_STRING; return rv;}/* * GPGME convenient functions. *//* Create a new gpgme context and return it. With FOR_SMIME set to true, the protocol of the context is set to CMS. */static gpgme_ctx_t create_gpgme_context (int for_smime){ gpgme_error_t err; gpgme_ctx_t ctx; err = gpgme_new (&ctx); if (err) { mutt_error (_("error creating gpgme context: %s\n"), gpgme_strerror (err)); sleep (2); mutt_exit (1); } if (for_smime) { err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS); if (err) { mutt_error (_("error enabling CMS protocol: %s\n"), gpgme_strerror (err)); sleep (2); mutt_exit (1); } } return ctx;}/* Create a new gpgme data object. This is a wrapper to die on error. */static gpgme_data_t create_gpgme_data (void){ gpgme_error_t err; gpgme_data_t data; err = gpgme_data_new (&data); if (err) { mutt_error (_("error creating gpgme data object: %s\n"), gpgme_strerror (err)); sleep (2); mutt_exit (1); } return data;}/* Create a new GPGME Data object from the mail body A. With CONVERT passed as true, the lines are converted to CR,LF if required. Return NULL on error or the gpgme_data_t object on success. */static gpgme_data_t body_to_data_object (BODY *a, int convert){ char tempfile[_POSIX_PATH_MAX]; FILE *fptmp; int err = 0; gpgme_data_t data; mutt_mktemp (tempfile); fptmp = safe_fopen (tempfile, "w+"); if (!fptmp) { mutt_perror (tempfile); return NULL; } mutt_write_mime_header (a, fptmp); fputc ('\n', fptmp); mutt_write_mime_body (a, fptmp); if (convert) { int c, hadcr = 0; unsigned char buf[1]; data = create_gpgme_data (); rewind (fptmp); while ((c = fgetc (fptmp)) != EOF) { if (c == '\r') hadcr = 1; else { if (c == '\n' && !hadcr) { buf[0] = '\r'; gpgme_data_write (data, buf, 1); } hadcr = 0; } /* FIXME: This is quite suboptimal */ buf[0] = c; gpgme_data_write (data, buf, 1); } fclose(fptmp); gpgme_data_seek (data, 0, SEEK_SET); } else { fclose(fptmp); err = gpgme_data_new_from_file (&data, tempfile, 1); } unlink (tempfile); if (err) { mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err)); return NULL; } return data;}/* Create a GPGME data object from the stream FP but limit the object to LENGTH bytes starting at OFFSET bytes from the beginning of the file. */static gpgme_data_t file_to_data_object (FILE *fp, long offset, long length){ int err = 0; gpgme_data_t data; err = gpgme_data_new_from_filepart (&data, NULL, fp, offset, length); if (err) { mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err)); return NULL; } return data;}/* Write a GPGME data object to the stream FP. */static int data_object_to_stream (gpgme_data_t data, FILE *fp){ int err; char buf[4096], *p; ssize_t nread; err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1) ? gpgme_error_from_errno (errno) : 0); if (err) { mutt_error (_("error rewinding data object: %s\n"), gpgme_strerror (err)); return -1; } while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) { /* fixme: we are not really converting CRLF to LF but just skipping CR. Doing it correctly needs a more complex logic */ for (p=buf; nread; p++, nread--) { if (*p != '\r') putc (*p, fp); } if (ferror (fp)) { mutt_perror ("[tempfile]"); return -1; } } if (nread == -1) { mutt_error (_("error reading data object: %s\n"), strerror (errno)); return -1; } return 0;}/* Copy a data object to a newly created temporay file and return that filename. Caller must free. With RET_FP not NULL, don't close the stream but return it there. */static char *data_object_to_tempfile (gpgme_data_t data, FILE **ret_fp){ int err; char tempfile[_POSIX_PATH_MAX]; FILE *fp; size_t nread = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -