/** * mbox_mbox_open_append - Implements MxOps::mbox_open_append() */ static int mbox_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags) { if (!m) return -1; if (init_mailbox(m) != 0) return -1; struct MboxAccountData *adata = mbox_adata_get(m); if (!adata) return -1; adata->fp = mutt_file_fopen(m->path, (flags & MUTT_NEWFOLDER) ? "w" : "a"); if (!adata->fp) { mutt_perror(m->path); return -1; } if (mbox_lock_mailbox(m, true, true) != false) { mutt_error(_("Couldn't lock %s"), m->path); mutt_file_fclose(&adata->fp); return -1; } fseek(adata->fp, 0, SEEK_END); return 0; }
/** * icmd_version - Parse 'version' command - Implements ::icommand_t */ static enum CommandResult icmd_version(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err) { char tempfile[PATH_MAX]; mutt_mktemp(tempfile, sizeof(tempfile)); FILE *fp_out = mutt_file_fopen(tempfile, "w"); if (!fp_out) { mutt_buffer_addstr(err, _("Could not create temporary file")); return MUTT_CMD_ERROR; } print_version(fp_out); mutt_file_fclose(&fp_out); struct Pager info = { 0 }; if (mutt_pager("version", tempfile, 0, &info) == -1) { mutt_buffer_addstr(err, _("Could not create temporary file")); return MUTT_CMD_ERROR; } return MUTT_CMD_SUCCESS; }
/** * crypt_write_signed - Write the message body/part * @param a Body to write * @param s State to use * @param tempfile File to write to * @retval 0 Success * @retval -1 Error * * Body/part A described by state S to the given TEMPFILE. */ int crypt_write_signed(struct Body *a, struct State *s, const char *tempfile) { FILE *fp = NULL; bool hadcr; size_t bytes; if (!WithCrypto) return -1; fp = mutt_file_fopen(tempfile, "w"); if (!fp) { mutt_perror(tempfile); return -1; } fseeko(s->fp_in, a->hdr_offset, SEEK_SET); bytes = a->length + a->offset - a->hdr_offset; hadcr = false; while (bytes > 0) { const int c = fgetc(s->fp_in); if (c == EOF) break; bytes--; if (c == '\r') hadcr = true; else { if ((c == '\n') && !hadcr) fputc('\r', fp); hadcr = false; } fputc(c, fp); } mutt_file_fclose(&fp); return 0; }
/** * setup_paths - Set the mailbox paths * @param m Mailbox to modify * @retval 0 Success * @retval -1 Error * * Save the compressed filename in mailbox->realpath. * Create a temporary filename and put its name in mailbox->path. * The temporary file is created to prevent symlink attacks. */ static int setup_paths(struct Mailbox *m) { if (!m) return -1; char tmp[PATH_MAX]; /* Setup the right paths */ mutt_str_strfcpy(m->realpath, m->path, sizeof(m->realpath)); /* We will uncompress to /tmp */ mutt_mktemp(tmp, sizeof(tmp)); mutt_str_strfcpy(m->path, tmp, sizeof(m->path)); FILE *fp = mutt_file_fopen(m->path, "w"); if (!fp) return -1; mutt_file_fclose(&fp); return 0; }
/** * icmd_set - Parse 'set' command to display config - Implements ::icommand_t */ static enum CommandResult icmd_set(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err) { char tempfile[PATH_MAX]; mutt_mktemp(tempfile, sizeof(tempfile)); FILE *fp_out = mutt_file_fopen(tempfile, "w"); if (!fp_out) { mutt_buffer_addstr(err, _("Could not create temporary file")); return MUTT_CMD_ERROR; } if (mutt_str_strcmp(s->data, "set all") == 0) { dump_config(Config, CS_DUMP_STYLE_NEO, 0, fp_out); } else if (mutt_str_strcmp(s->data, "set") == 0) { dump_config(Config, CS_DUMP_STYLE_NEO, CS_DUMP_ONLY_CHANGED, fp_out); } else { mutt_file_fclose(&fp_out); return MUTT_CMD_ERROR; } mutt_file_fclose(&fp_out); struct Pager info = { 0 }; if (mutt_pager("set", tempfile, 0, &info) == -1) { mutt_buffer_addstr(err, _("Could not create temporary file")); return MUTT_CMD_ERROR; } return MUTT_CMD_SUCCESS; }
/** * icmd_bind - Parse 'bind' and 'macro' commands - Implements ::icommand_t */ static enum CommandResult icmd_bind(struct Buffer *buf, struct Buffer *s, unsigned long data, struct Buffer *err) { FILE *fp_out = NULL; char tempfile[PATH_MAX]; bool dump_all = false, bind = (data == 0); if (!MoreArgs(s)) dump_all = true; else mutt_extract_token(buf, s, 0); if (MoreArgs(s)) { /* More arguments potentially means the user is using the * ::command_t :bind command thus we delegate the task. */ return MUTT_CMD_ERROR; } struct Buffer *filebuf = mutt_buffer_alloc(4096); if (dump_all || (mutt_str_strcasecmp(buf->data, "all") == 0)) { dump_all_menus(filebuf, bind); } else { const int menu_index = mutt_map_get_value(buf->data, Menus); if (menu_index == -1) { mutt_buffer_printf(err, _("%s: no such menu"), buf->data); mutt_buffer_free(&filebuf); return MUTT_CMD_ERROR; } struct Mapping menu = { buf->data, menu_index }; dump_menu(filebuf, &menu, bind); } if (mutt_buffer_is_empty(filebuf)) { mutt_buffer_printf(err, _("%s: no %s for this menu"), dump_all ? "all" : buf->data, bind ? "binds" : "macros"); mutt_buffer_free(&filebuf); return MUTT_CMD_ERROR; } mutt_mktemp(tempfile, sizeof(tempfile)); fp_out = mutt_file_fopen(tempfile, "w"); if (!fp_out) { mutt_buffer_printf(err, _("Could not create temporary file %s"), tempfile); mutt_buffer_free(&filebuf); return MUTT_CMD_ERROR; } fputs(filebuf->data, fp_out); mutt_file_fclose(&fp_out); mutt_buffer_free(&filebuf); struct Pager info = { 0 }; if (mutt_pager((bind) ? "bind" : "macro", tempfile, 0, &info) == -1) { mutt_buffer_printf(err, _("Could not create temporary file %s"), tempfile); return MUTT_CMD_ERROR; } return MUTT_CMD_SUCCESS; }
/** * crypt_extract_keys_from_messages - Extract keys from a message * @param el List of Emails to process * * The extracted keys will be added to the user's keyring. */ void crypt_extract_keys_from_messages(struct EmailList *el) { char tempfname[PATH_MAX], *mbox = NULL; struct Address *tmp = NULL; if (!WithCrypto) return; mutt_mktemp(tempfname, sizeof(tempfname)); FILE *fp_out = mutt_file_fopen(tempfname, "w"); if (!fp_out) { mutt_perror(tempfname); return; } if (WithCrypto & APPLICATION_PGP) OptDontHandlePgpKeys = true; struct EmailNode *en = NULL; STAILQ_FOREACH(en, el, entries) { struct Email *e = en->email; mutt_parse_mime_message(Context->mailbox, e); if (e->security & SEC_ENCRYPT && !crypt_valid_passphrase(e->security)) { mutt_file_fclose(&fp_out); break; } if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & APPLICATION_PGP)) { mutt_copy_message_ctx(fp_out, Context->mailbox, e, MUTT_CM_DECODE | MUTT_CM_CHARCONV, 0); fflush(fp_out); mutt_endwin(); puts(_("Trying to extract PGP keys...\n")); crypt_pgp_invoke_import(tempfname); } if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME)) { if (e->security & SEC_ENCRYPT) { mutt_copy_message_ctx(fp_out, Context->mailbox, e, MUTT_CM_NOHEADER | MUTT_CM_DECODE_CRYPT | MUTT_CM_DECODE_SMIME, 0); } else mutt_copy_message_ctx(fp_out, Context->mailbox, e, 0, 0); fflush(fp_out); if (e->env->from) tmp = mutt_expand_aliases(e->env->from); else if (e->env->sender) tmp = mutt_expand_aliases(e->env->sender); mbox = tmp ? tmp->mailbox : NULL; if (mbox) { mutt_endwin(); puts(_("Trying to extract S/MIME certificates...")); crypt_smime_invoke_import(tempfname, mbox); tmp = NULL; } } rewind(fp_out); } mutt_file_fclose(&fp_out); if (isendwin()) mutt_any_key_to_continue(NULL); mutt_file_unlink(tempfname); if (WithCrypto & APPLICATION_PGP) OptDontHandlePgpKeys = false; }
/** * reopen_mailbox - Close and reopen a mailbox * @param m Mailbox * @param index_hint Current email * @retval >0 Success, e.g. #MUTT_REOPENED, #MUTT_NEW_MAIL * @retval -1 Error */ static int reopen_mailbox(struct Mailbox *m, int *index_hint) { if (!m) return -1; struct MboxAccountData *adata = mbox_adata_get(m); if (!adata) return -1; bool (*cmp_headers)(const struct Email *, const struct Email *) = NULL; struct Email **old_hdrs = NULL; int old_msg_count; bool msg_mod = false; int rc = -1; /* silent operations */ m->quiet = true; if (!m->quiet) mutt_message(_("Reopening mailbox...")); /* our heuristics require the old mailbox to be unsorted */ if (C_Sort != SORT_ORDER) { short old_sort = C_Sort; C_Sort = SORT_ORDER; mutt_mailbox_changed(m, MBN_RESORT); C_Sort = old_sort; } old_hdrs = NULL; old_msg_count = 0; /* simulate a close */ mutt_mailbox_changed(m, MBN_CLOSED); mutt_hash_free(&m->id_hash); mutt_hash_free(&m->subj_hash); mutt_hash_free(&m->label_hash); FREE(&m->v2r); if (m->readonly) { for (int i = 0; i < m->msg_count; i++) mutt_email_free(&(m->emails[i])); /* nothing to do! */ FREE(&m->emails); } else { /* save the old headers */ old_msg_count = m->msg_count; old_hdrs = m->emails; m->emails = NULL; } m->email_max = 0; /* force allocation of new headers */ m->msg_count = 0; m->vcount = 0; m->msg_tagged = 0; m->msg_deleted = 0; m->msg_new = 0; m->msg_unread = 0; m->msg_flagged = 0; m->changed = false; m->id_hash = NULL; m->subj_hash = NULL; mutt_make_label_hash(m); switch (m->magic) { case MUTT_MBOX: case MUTT_MMDF: cmp_headers = mutt_email_cmp_strict; mutt_file_fclose(&adata->fp); adata->fp = mutt_file_fopen(m->path, "r"); if (!adata->fp) rc = -1; else if (m->magic == MUTT_MBOX) rc = mbox_parse_mailbox(m); else rc = mmdf_parse_mailbox(m); break; default: rc = -1; break; } if (rc == -1) { /* free the old headers */ for (int i = 0; i < old_msg_count; i++) mutt_email_free(&(old_hdrs[i])); FREE(&old_hdrs); m->quiet = false; return -1; } mutt_file_touch_atime(fileno(adata->fp)); /* now try to recover the old flags */ if (!m->readonly) { for (int i = 0; i < m->msg_count; i++) { bool found = false; /* some messages have been deleted, and new messages have been * appended at the end; the heuristic is that old messages have then * "advanced" towards the beginning of the folder, so we begin the * search at index "i" */ int j; for (j = i; j < old_msg_count; j++) { if (!old_hdrs[j]) continue; if (cmp_headers(m->emails[i], old_hdrs[j])) { found = true; break; } } if (!found) { for (j = 0; (j < i) && (j < old_msg_count); j++) { if (!old_hdrs[j]) continue; if (cmp_headers(m->emails[i], old_hdrs[j])) { found = true; break; } } } if (found) { /* this is best done here */ if (index_hint && (*index_hint == j)) *index_hint = i; if (old_hdrs[j]->changed) { /* Only update the flags if the old header was changed; * otherwise, the header may have been modified externally, * and we don't want to lose _those_ changes */ mutt_set_flag(m, m->emails[i], MUTT_FLAG, old_hdrs[j]->flagged); mutt_set_flag(m, m->emails[i], MUTT_REPLIED, old_hdrs[j]->replied); mutt_set_flag(m, m->emails[i], MUTT_OLD, old_hdrs[j]->old); mutt_set_flag(m, m->emails[i], MUTT_READ, old_hdrs[j]->read); } mutt_set_flag(m, m->emails[i], MUTT_DELETE, old_hdrs[j]->deleted); mutt_set_flag(m, m->emails[i], MUTT_PURGE, old_hdrs[j]->purge); mutt_set_flag(m, m->emails[i], MUTT_TAG, old_hdrs[j]->tagged); /* we don't need this header any more */ mutt_email_free(&(old_hdrs[j])); } } /* free the remaining old headers */ for (int j = 0; j < old_msg_count; j++) { if (old_hdrs[j]) { mutt_email_free(&(old_hdrs[j])); msg_mod = true; } } FREE(&old_hdrs); } m->quiet = false; return (m->changed || msg_mod) ? MUTT_REOPENED : MUTT_NEW_MAIL; }
/** * tls_check_one_certificate - Check a GnuTLS certificate * @param certdata List of GnuTLS certificates * @param certstat GnuTLS certificate status * @param hostname Hostname * @param idx Index into certificate list * @param len Length of certificate list * @retval 0 Failure * @retval >0 Success */ static int tls_check_one_certificate(const gnutls_datum_t *certdata, gnutls_certificate_status_t certstat, const char *hostname, int idx, size_t len) { int certerr, savedcert; gnutls_x509_crt_t cert; char buf[128]; char fpbuf[128]; size_t buflen; char dn_common_name[128]; char dn_email[128]; char dn_organization[128]; char dn_organizational_unit[128]; char dn_locality[128]; char dn_province[128]; char dn_country[128]; time_t t; char datestr[30]; struct Menu *menu = NULL; char helpstr[1024]; char title[256]; FILE *fp = NULL; gnutls_datum_t pemdata; int row, done, ret; if (tls_check_preauth(certdata, certstat, hostname, idx, &certerr, &savedcert) == 0) return 1; /* interactive check from user */ if (gnutls_x509_crt_init(&cert) < 0) { mutt_error(_("Error initialising gnutls certificate data")); return 0; } if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0) { mutt_error(_("Error processing certificate data")); gnutls_x509_crt_deinit(cert); return 0; } menu = mutt_menu_new(MENU_GENERIC); menu->max = 27; menu->dialog = mutt_mem_calloc(1, menu->max * sizeof(char *)); for (int i = 0; i < menu->max; i++) menu->dialog[i] = mutt_mem_calloc(1, dialog_row_len * sizeof(char)); mutt_menu_push_current(menu); row = 0; mutt_str_strfcpy(menu->dialog[row], _("This certificate belongs to:"), dialog_row_len); row++; buflen = sizeof(dn_common_name); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0) { dn_common_name[0] = '\0'; } buflen = sizeof(dn_email); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0) dn_email[0] = '\0'; buflen = sizeof(dn_organization); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization, &buflen) != 0) { dn_organization[0] = '\0'; } buflen = sizeof(dn_organizational_unit); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, dn_organizational_unit, &buflen) != 0) { dn_organizational_unit[0] = '\0'; } buflen = sizeof(dn_locality); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0) { dn_locality[0] = '\0'; } buflen = sizeof(dn_province); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province, &buflen) != 0) { dn_province[0] = '\0'; } buflen = sizeof(dn_country); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0) { dn_country[0] = '\0'; } snprintf(menu->dialog[row++], dialog_row_len, " %s %s", dn_common_name, dn_email); snprintf(menu->dialog[row++], dialog_row_len, " %s", dn_organization); snprintf(menu->dialog[row++], dialog_row_len, " %s", dn_organizational_unit); snprintf(menu->dialog[row++], dialog_row_len, " %s %s %s", dn_locality, dn_province, dn_country); row++; mutt_str_strfcpy(menu->dialog[row], _("This certificate was issued by:"), dialog_row_len); row++; buflen = sizeof(dn_common_name); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0) { dn_common_name[0] = '\0'; } buflen = sizeof(dn_email); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0) { dn_email[0] = '\0'; } buflen = sizeof(dn_organization); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization, &buflen) != 0) { dn_organization[0] = '\0'; } buflen = sizeof(dn_organizational_unit); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, dn_organizational_unit, &buflen) != 0) { dn_organizational_unit[0] = '\0'; } buflen = sizeof(dn_locality); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0) { dn_locality[0] = '\0'; } buflen = sizeof(dn_province); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province, &buflen) != 0) { dn_province[0] = '\0'; } buflen = sizeof(dn_country); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0) { dn_country[0] = '\0'; } snprintf(menu->dialog[row++], dialog_row_len, " %s %s", dn_common_name, dn_email); snprintf(menu->dialog[row++], dialog_row_len, " %s", dn_organization); snprintf(menu->dialog[row++], dialog_row_len, " %s", dn_organizational_unit); snprintf(menu->dialog[row++], dialog_row_len, " %s %s %s", dn_locality, dn_province, dn_country); row++; snprintf(menu->dialog[row++], dialog_row_len, _("This certificate is valid")); t = gnutls_x509_crt_get_activation_time(cert); mutt_date_make_tls(datestr, sizeof(datestr), t); snprintf(menu->dialog[row++], dialog_row_len, _(" from %s"), datestr); t = gnutls_x509_crt_get_expiration_time(cert); mutt_date_make_tls(datestr, sizeof(datestr), t); snprintf(menu->dialog[row++], dialog_row_len, _(" to %s"), datestr); fpbuf[0] = '\0'; tls_fingerprint(GNUTLS_DIG_SHA, fpbuf, sizeof(fpbuf), certdata); snprintf(menu->dialog[row++], dialog_row_len, _("SHA1 Fingerprint: %s"), fpbuf); fpbuf[0] = '\0'; fpbuf[40] = '\0'; /* Ensure the second printed line is null terminated */ tls_fingerprint(GNUTLS_DIG_SHA256, fpbuf, sizeof(fpbuf), certdata); fpbuf[39] = '\0'; /* Divide into two lines of output */ snprintf(menu->dialog[row++], dialog_row_len, "%s%s", _("SHA256 Fingerprint: "), fpbuf); snprintf(menu->dialog[row++], dialog_row_len, "%*s%s", (int) mutt_str_strlen(_("SHA256 Fingerprint: ")), "", fpbuf + 40); if (certerr & CERTERR_NOTYETVALID) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Server certificate is not yet valid"), dialog_row_len); } if (certerr & CERTERR_EXPIRED) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Server certificate has expired"), dialog_row_len); } if (certerr & CERTERR_REVOKED) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Server certificate has been revoked"), dialog_row_len); } if (certerr & CERTERR_HOSTNAME) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Server hostname does not match certificate"), dialog_row_len); } if (certerr & CERTERR_SIGNERNOTCA) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Signer of server certificate is not a CA"), dialog_row_len); } snprintf(title, sizeof(title), _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len); menu->title = title; /* certificates with bad dates, or that are revoked, must be * accepted manually each and every time */ if (C_CertificateFile && !savedcert && !(certerr & (CERTERR_EXPIRED | CERTERR_NOTYETVALID | CERTERR_REVOKED))) { menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always"); /* L10N: These three letters correspond to the choices in the string: (r)eject, accept (o)nce, (a)ccept always. This is an interactive certificate confirmation prompt for a GNUTLS connection. */ menu->keys = _("roa"); } else { menu->prompt = _("(r)eject, accept (o)nce"); /* L10N: These two letters correspond to the choices in the string: (r)eject, accept (o)nce. These is an interactive certificate confirmation prompt for a GNUTLS connection. */ menu->keys = _("ro"); } helpstr[0] = '\0'; mutt_make_help(buf, sizeof(buf), _("Exit "), MENU_GENERIC, OP_EXIT); mutt_str_strcat(helpstr, sizeof(helpstr), buf); mutt_make_help(buf, sizeof(buf), _("Help"), MENU_GENERIC, OP_HELP); mutt_str_strcat(helpstr, sizeof(helpstr), buf); menu->help = helpstr; done = 0; OptIgnoreMacroEvents = true; while (done == 0) { switch (mutt_menu_loop(menu)) { case -1: /* abort */ case OP_MAX + 1: /* reject */ case OP_EXIT: done = 1; break; case OP_MAX + 3: /* accept always */ done = 0; fp = mutt_file_fopen(C_CertificateFile, "a"); if (fp) { /* save hostname if necessary */ if (certerr & CERTERR_HOSTNAME) { fpbuf[0] = '\0'; tls_fingerprint(GNUTLS_DIG_MD5, fpbuf, sizeof(fpbuf), certdata); fprintf(fp, "#H %s %s\n", hostname, fpbuf); done = 1; } /* Save the cert for all other errors */ if (certerr ^ CERTERR_HOSTNAME) { done = 0; ret = gnutls_pem_base64_encode_alloc("CERTIFICATE", certdata, &pemdata); if (ret == 0) { if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1) { done = 1; } gnutls_free(pemdata.data); } } mutt_file_fclose(&fp); } if (done == 0) { mutt_error(_("Warning: Couldn't save certificate")); } else { mutt_message(_("Certificate saved")); mutt_sleep(0); } /* fallthrough */ case OP_MAX + 2: /* accept once */ done = 2; break; } } OptIgnoreMacroEvents = false; mutt_menu_pop_current(menu); mutt_menu_destroy(&menu); gnutls_x509_crt_deinit(cert); return done == 2; }