/** * mutt_any_key_to_continue - Prompt the user to 'press any key' and wait * @param s Message prompt * @retval num Key pressed * @retval EOF Error, or prompt aborted */ int mutt_any_key_to_continue(const char *s) { struct termios t; struct termios old; int f, ch; f = open("/dev/tty", O_RDONLY); if (f < 0) return EOF; tcgetattr(f, &t); memcpy((void *) &old, (void *) &t, sizeof(struct termios)); /* save original state */ t.c_lflag &= ~(ICANON | ECHO); t.c_cc[VMIN] = 1; t.c_cc[VTIME] = 0; tcsetattr(f, TCSADRAIN, &t); fflush(stdout); if (s) fputs(s, stdout); else fputs(_("Press any key to continue..."), stdout); fflush(stdout); ch = fgetc(stdin); fflush(stdin); tcsetattr(f, TCSADRAIN, &old); close(f); fputs("\r\n", stdout); mutt_clear_error(); return (ch >= 0) ? ch : EOF; }
/* logout from POP server */ void pop_logout (CONTEXT *ctx) { int ret = 0; char buf[LONG_STRING]; POP_DATA *pop_data = (POP_DATA *)ctx->data; if (pop_data->status == POP_CONNECTED) { mutt_message _("Closing connection to POP server..."); if (ctx->readonly) { strfcpy (buf, "RSET\r\n", sizeof (buf)); ret = pop_query (pop_data, buf, sizeof (buf)); } if (ret != -1) { strfcpy (buf, "QUIT\r\n", sizeof (buf)); pop_query (pop_data, buf, sizeof (buf)); } mutt_clear_error (); } pop_data->status = POP_DISCONNECTED; return; }
int mutt_query_complete(char *buf, size_t buflen) { QUERY *results = NULL; ADDRESS *tmpa; if (!QueryCmd) { mutt_error _("Query command not defined."); return 0; } results = run_query(buf, 1); if (results) { /* only one response? */ if (results->next == NULL) { tmpa = result_to_addr(results); mutt_addrlist_to_local(tmpa); buf[0] = '\0'; rfc822_write_address(buf, buflen, tmpa, 0); rfc822_free_address(&tmpa); free_query(&results); mutt_clear_error(); return 0; } /* multiple results, choose from query menu */ query_menu(buf, buflen, results, 1); } return 0; }
/* imap_auth_login: Plain LOGIN support */ imap_auth_res_t imap_auth_login (IMAP_DATA* idata, const char* method) { char q_user[SHORT_STRING], q_pass[SHORT_STRING]; char buf[STRING]; int rc; if (bit_val(idata->capabilities, LOGINDISABLED)) { mutt_message("LOGIN disabled on this server."); return IMAP_AUTH_UNAVAIL; } if (mutt_account_getuser (&idata->conn->account)) return IMAP_AUTH_FAILURE; if (mutt_account_getpass (&idata->conn->account)) return IMAP_AUTH_FAILURE; mutt_message("Logging in..."); imap_quote_string (q_user, sizeof (q_user), idata->conn->account.user); imap_quote_string (q_pass, sizeof (q_pass), idata->conn->account.pass); snprintf (buf, sizeof (buf), "LOGIN %s %s", q_user, q_pass); rc = imap_exec (idata, buf, IMAP_CMD_FAIL_OK | IMAP_CMD_PASS); if (!rc) { mutt_clear_error(); /* clear "Logging in...". fixes #3524 */ return IMAP_AUTH_SUCCESS; } mutt_error("Login failed."); mutt_sleep (2); return IMAP_AUTH_FAILURE; }
/** * imap_auth_oauth - Authenticate an IMAP connection using OAUTHBEARER * @param adata Imap Account data * @param method Name of this authentication method (UNUSED) * @retval num Result, e.g. #IMAP_AUTH_SUCCESS */ enum ImapAuthRes imap_auth_oauth(struct ImapAccountData *adata, const char *method) { char *ibuf = NULL; char *oauthbearer = NULL; int ilen; int rc; /* For now, we only support SASL_IR also and over TLS */ if (!(adata->capabilities & IMAP_CAP_AUTH_OAUTHBEARER) || !(adata->capabilities & IMAP_CAP_SASL_IR) || !adata->conn->ssf) { return IMAP_AUTH_UNAVAIL; } /* If they did not explicitly request or configure oauth then fail quietly */ if (!(method || (C_ImapOauthRefreshCommand && *C_ImapOauthRefreshCommand))) return IMAP_AUTH_UNAVAIL; mutt_message(_("Authenticating (OAUTHBEARER)...")); /* We get the access token from the imap_oauth_refresh_command */ oauthbearer = mutt_account_getoauthbearer(&adata->conn->account); if (!oauthbearer) return IMAP_AUTH_FAILURE; ilen = mutt_str_strlen(oauthbearer) + 30; ibuf = mutt_mem_malloc(ilen); snprintf(ibuf, ilen, "AUTHENTICATE OAUTHBEARER %s", oauthbearer); /* This doesn't really contain a password, but the token is good for * an hour, so suppress it anyways. */ rc = imap_exec(adata, ibuf, IMAP_CMD_PASS); FREE(&oauthbearer); FREE(&ibuf); if (rc != IMAP_EXEC_SUCCESS) { /* The error response was in SASL continuation, so continue the SASL * to cause a failure and exit SASL input. See RFC 7628 3.2.3 */ mutt_socket_send(adata->conn, "\001"); rc = imap_exec(adata, ibuf, IMAP_CMD_NO_FLAGS); } if (rc == IMAP_EXEC_SUCCESS) { mutt_clear_error(); return IMAP_AUTH_SUCCESS; } mutt_error(_("OAUTHBEARER authentication failed.")); return IMAP_AUTH_FAILURE; }
/** * mutt_query_exit - Ask the user if they want to leave NeoMutt * * This function is called when the user presses the abort key. */ void mutt_query_exit(void) { mutt_flushinp(); curs_set(1); if (C_Timeout) mutt_getch_timeout(-1); /* restore blocking operation */ if (mutt_yesorno(_("Exit NeoMutt?"), MUTT_YES) == MUTT_YES) { mutt_exit(1); } mutt_clear_error(); mutt_curs_set(-1); SigInt = 0; }
pgp_key_t pgp_ask_for_key (char *tag, char *whatfor, short abilities, pgp_ring_t keyring) { pgp_key_t key; char resp[SHORT_STRING]; struct pgp_cache *l = NULL; mutt_clear_error (); resp[0] = 0; if (whatfor) { for (l = id_defaults; l; l = l->next) if (!mutt_strcasecmp (whatfor, l->what)) { strfcpy (resp, NONULL (l->dflt), sizeof (resp)); break; } } FOREVER { resp[0] = 0; if (mutt_get_field (tag, resp, sizeof (resp), M_CLEAR) != 0) return NULL; if (whatfor) { if (l) mutt_str_replace (&l->dflt, resp); else { l = safe_malloc (sizeof (struct pgp_cache)); l->next = id_defaults; id_defaults = l; l->what = safe_strdup (whatfor); l->dflt = safe_strdup (resp); } } if ((key = pgp_getkeybystr (resp, abilities, keyring))) return key; BEEP (); } /* not reached */ }
/* this function is called when the user presses the abort key */ void mutt_query_exit (void) { mutt_flushinp (); curs_set (1); if (Timeout) timeout (-1); /* restore blocking operation */ if (mutt_yesorno (_("Exit Mutt?"), 1) == 1) { endwin (); exit (0); } mutt_clear_error(); mutt_curs_set (-1); SigInt = 0; }
void menu_redraw_prompt (MUTTMENU * menu) { if (menu->dialog) { if (option (OPTMSGERR)) { mutt_sleep (1); unset_option (OPTMSGERR); } if (*Errorbuf) mutt_clear_error (); SETCOLOR (MT_COLOR_NORMAL); mvaddstr (LINES - 1, 0, menu->prompt); clrtoeol (); } }
/** * ssl_init - Initialise the SSL library * @retval 0 Success * @retval -1 Error * * OpenSSL library needs to be fed with sufficient entropy. On systems with * /dev/urandom, this is done transparently by the library itself, on other * systems we need to fill the entropy pool ourselves. * * Even though only OpenSSL 0.9.5 and later will complain about the lack of * entropy, we try to our best and fill the pool with older versions also. * (That's the reason for the ugly ifdefs and macros, otherwise I could have * simply ifdef'd the whole ssl_init function) */ static int ssl_init(void) { static bool init_complete = false; if (init_complete) return 0; if (!HAVE_ENTROPY()) { /* load entropy from files */ char path[PATH_MAX]; add_entropy(C_EntropyFile); add_entropy(RAND_file_name(path, sizeof(path))); /* load entropy from egd sockets */ #ifdef HAVE_RAND_EGD add_entropy(mutt_str_getenv("EGDSOCKET")); snprintf(path, sizeof(path), "%s/.entropy", NONULL(HomeDir)); add_entropy(path); add_entropy("/tmp/entropy"); #endif /* shuffle $RANDFILE (or ~/.rnd if unset) */ RAND_write_file(RAND_file_name(path, sizeof(path))); mutt_clear_error(); if (!HAVE_ENTROPY()) { mutt_error(_("Failed to find enough entropy on your system")); return -1; } } /* OpenSSL performs automatic initialization as of 1.1. * However LibreSSL does not (as of 2.8.3). */ #if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || \ (defined(LIBRESSL_VERSION_NUMBER)) /* I don't think you can do this just before reading the error. The call * itself might clobber the last SSL error. */ SSL_load_error_strings(); SSL_library_init(); #endif init_complete = true; return 0; }
void pgp_invoke_getkeys (ADDRESS * addr) { char buff[LONG_STRING]; char tmp[LONG_STRING]; char cmd[HUGE_STRING]; int devnull; char *personal; struct pgp_command_context cctx; if (!PgpGetkeysCommand) return; memset (&cctx, 0, sizeof (cctx)); personal = addr->personal; addr->personal = NULL; *tmp = '\0'; mutt_addrlist_to_local (addr); rfc822_write_address_single (tmp, sizeof (tmp), addr, 0); mutt_quote_filename (buff, sizeof (buff), tmp); addr->personal = personal; cctx.ids = buff; mutt_pgp_command (cmd, sizeof (cmd), &cctx, PgpGetkeysCommand); devnull = open ("/dev/null", O_RDWR); if (!isendwin ()) mutt_message _("Fetching PGP key..."); mutt_system (cmd); if (!isendwin ()) mutt_clear_error (); close (devnull); }
/* * OpenSSL library needs to be fed with sufficient entropy. On systems * with /dev/urandom, this is done transparently by the library itself, * on other systems we need to fill the entropy pool ourselves. * * Even though only OpenSSL 0.9.5 and later will complain about the * lack of entropy, we try to our best and fill the pool with older * versions also. (That's the reason for the ugly #ifdefs and macros, * otherwise I could have simply #ifdef'd the whole ssl_init funcion) */ static int ssl_init (void) { char path[_POSIX_PATH_MAX]; static unsigned char init_complete = 0; if (init_complete) return 0; if (! HAVE_ENTROPY()) { /* load entropy from files */ add_entropy (SslEntropyFile); add_entropy (RAND_file_name (path, sizeof (path))); /* load entropy from egd sockets */ #ifdef HAVE_RAND_EGD add_entropy (getenv ("EGDSOCKET")); snprintf (path, sizeof(path), "%s/.entropy", NONULL(Homedir)); add_entropy (path); add_entropy ("/tmp/entropy"); #endif /* shuffle $RANDFILE (or ~/.rnd if unset) */ RAND_write_file (RAND_file_name (path, sizeof (path))); mutt_clear_error (); if (! HAVE_ENTROPY()) { mutt_error (_("Failed to find enough entropy on your system")); mutt_sleep (2); return -1; } } /* I don't think you can do this just before reading the error. The call * itself might clobber the last SSL error. */ SSL_load_error_strings(); SSL_library_init(); init_complete = 1; return 0; }
static notmuch_database_t *do_database_open(const char *filename, int writable, int verbose) { notmuch_database_t *db = NULL; unsigned int ct = 0; notmuch_status_t st = NOTMUCH_STATUS_SUCCESS; dprint(1, (debugfile, "nm: db open '%s' %s (timeout %d)\n", filename, writable ? "[WRITE]" : "[READ]", NotmuchOpenTimeout)); do { #ifdef NOTMUCH_API_3 st = notmuch_database_open(filename, writable ? NOTMUCH_DATABASE_MODE_READ_WRITE : NOTMUCH_DATABASE_MODE_READ_ONLY, &db); #else db = notmuch_database_open(filename, writable ? NOTMUCH_DATABASE_MODE_READ_WRITE : NOTMUCH_DATABASE_MODE_READ_ONLY); #endif if (db || !NotmuchOpenTimeout || ct / 2 > NotmuchOpenTimeout) break; if (verbose && ct && ct % 2 == 0) mutt_error(_("Waiting for notmuch DB... (%d sec)"), ct / 2); usleep(500000); ct++; } while (1); if (verbose) { if (!db) mutt_error (_("Cannot open notmuch database: %s: %s"), filename, st ? notmuch_status_to_string(st) : _("unknown reason")); else if (ct > 1) mutt_clear_error(); } return db; }
int mutt_prepare_template (FILE *fp, CONTEXT *ctx, HEADER *newhdr, HEADER *hdr, short weed) { MESSAGE *msg = NULL; char file[_POSIX_PATH_MAX]; BODY *b; FILE *bfp; int rv = -1; STATE s; memset (&s, 0, sizeof (s)); if (!fp && (msg = mx_open_message (ctx, hdr->msgno)) == NULL) return (-1); if (!fp) fp = msg->fp; bfp = fp; /* parse the message header and MIME structure */ fseeko (fp, hdr->offset, 0); newhdr->offset = hdr->offset; newhdr->env = mutt_read_rfc822_header (fp, newhdr, 1, weed); newhdr->content->length = hdr->content->length; mutt_parse_part (fp, newhdr->content); /* If message_id is set, then we are resending a message and don't want * message_id or mail_followup_to. Otherwise, we are resuming a * postponed message, and want to keep the mail_followup_to. */ if (newhdr->env->message_id != NULL) { FREE (&newhdr->env->message_id); FREE (&newhdr->env->mail_followup_to); } /* decrypt pgp/mime encoded messages */ if ((WithCrypto & (APPLICATION_PGP|APPLICATION_SMIME) & hdr->security) && mutt_is_multipart_encrypted (newhdr->content)) { int ccap = WithCrypto & (APPLICATION_PGP|APPLICATION_SMIME) & hdr->security; newhdr->security |= ENCRYPT | ccap; if (!crypt_valid_passphrase (ccap)) goto err; mutt_message _("Decrypting message..."); if (((ccap & APPLICATION_PGP) && crypt_pgp_decrypt_mime (fp, &bfp, newhdr->content, &b) == -1) || ((ccap & APPLICATION_SMIME) && crypt_smime_decrypt_mime (fp, &bfp, newhdr->content, &b) == -1) || b == NULL) { err: mx_close_message (&msg); mutt_free_envelope (&newhdr->env); mutt_free_body (&newhdr->content); mutt_error _("Decryption failed."); return -1; } mutt_free_body (&newhdr->content); newhdr->content = b; mutt_clear_error (); } /* * remove a potential multipart/signed layer - useful when * resending messages */ if (WithCrypto && mutt_is_multipart_signed (newhdr->content)) { newhdr->security |= SIGN; if ((WithCrypto & APPLICATION_PGP) && ascii_strcasecmp (mutt_get_parameter ("protocol", newhdr->content->parameter), "application/pgp-signature") == 0) newhdr->security |= APPLICATION_PGP; else if ((WithCrypto & APPLICATION_SMIME)) newhdr->security |= APPLICATION_SMIME; /* destroy the signature */ mutt_free_body (&newhdr->content->parts->next); newhdr->content = mutt_remove_multipart (newhdr->content); } /* * We don't need no primary multipart. * Note: We _do_ preserve messages! * * XXX - we don't handle multipart/alternative in any * smart way when sending messages. However, one may * consider this a feature. * */ if (newhdr->content->type == TYPEMULTIPART) newhdr->content = mutt_remove_multipart (newhdr->content); s.fpin = bfp; /* create temporary files for all attachments */ for (b = newhdr->content; b; b = b->next) { /* what follows is roughly a receive-mode variant of * mutt_get_tmp_attachment () from muttlib.c */ file[0] = '\0'; if (b->filename) { strfcpy (file, b->filename, sizeof (file)); b->d_filename = safe_strdup (b->filename); } else { /* avoid Content-Disposition: header with temporary filename */ b->use_disp = 0; } /* set up state flags */ s.flags = 0; if (b->type == TYPETEXT) { if (!ascii_strcasecmp ("yes", mutt_get_parameter ("x-mutt-noconv", b->parameter))) b->noconv = 1; else { s.flags |= M_CHARCONV; b->noconv = 0; } mutt_delete_parameter ("x-mutt-noconv", &b->parameter); } mutt_adv_mktemp (file, sizeof(file)); if ((s.fpout = safe_fopen (file, "w")) == NULL) goto bail; if ((WithCrypto & APPLICATION_PGP) && (mutt_is_application_pgp (b) & (ENCRYPT|SIGN))) { mutt_body_handler (b, &s); newhdr->security |= mutt_is_application_pgp (newhdr->content); b->type = TYPETEXT; mutt_str_replace (&b->subtype, "plain"); mutt_delete_parameter ("x-action", &b->parameter); } else mutt_decode_attachment (b, &s); if (safe_fclose (&s.fpout) != 0) goto bail; mutt_str_replace (&b->filename, file); b->unlink = 1; mutt_stamp_attachment (b); mutt_free_body (&b->parts); if (b->hdr) b->hdr->content = NULL; /* avoid dangling pointer */ } /* Fix encryption flags. */ /* No inline if multipart. */ if (WithCrypto && (newhdr->security & INLINE) && newhdr->content->next) newhdr->security &= ~INLINE; /* Do we even support multiple mechanisms? */ newhdr->security &= WithCrypto | ~(APPLICATION_PGP|APPLICATION_SMIME); /* Theoretically, both could be set. Take the one the user wants to set by default. */ if ((newhdr->security & APPLICATION_PGP) && (newhdr->security & APPLICATION_SMIME)) { if (option (OPTSMIMEISDEFAULT)) newhdr->security &= ~APPLICATION_PGP; else newhdr->security &= ~APPLICATION_SMIME; } rv = 0; bail: /* that's it. */ if (bfp != fp) safe_fclose (&bfp); if (msg) mx_close_message (&msg); if (rv == -1) { mutt_free_envelope (&newhdr->env); mutt_free_body (&newhdr->content); } return rv; }
/* fetch message from POP server */ int pop_fetch_message(MESSAGE *msg, CONTEXT *ctx, int msgno) { int ret; void *uidl; char buf[LONG_STRING]; char path[_POSIX_PATH_MAX]; progress_t progressbar; POP_DATA *pop_data = (POP_DATA *)ctx->data; POP_CACHE *cache; HEADER *h = ctx->hdrs[msgno]; unsigned short bcache = 1; /* see if we already have the message in body cache */ if ((msg->fp = mutt_bcache_get(pop_data->bcache, h->data))) return 0; /* * see if we already have the message in our cache in * case $message_cachedir is unset */ cache = &pop_data->cache[h->index % POP_CACHE_LEN]; if (cache->path) { if (cache->index == h->index) { /* yes, so just return a pointer to the message */ msg->fp = fopen(cache->path, "r"); if (msg->fp) return 0; mutt_perror(cache->path); mutt_sleep(2); return -1; } else { /* clear the previous entry */ unlink(cache->path); safe_free(&cache->path); } } FOREVER { if (pop_reconnect(ctx) < 0) return -1; /* verify that massage index is correct */ if (h->refno < 0) { mutt_error _( "The message index is incorrect. Try reopening the mailbox."); mutt_sleep(2); return -1; } mutt_progress_init(&progressbar, _( "Fetching message..."), M_PROGRESS_SIZE, NetInc, h->content->length + h->content->offset - 1); /* see if we can put in body cache; use our cache as fallback */ if (!(msg->fp = mutt_bcache_put(pop_data->bcache, h->data, 1))) { /* no */ bcache = 0; mutt_mktemp(path, sizeof(path)); if (!(msg->fp = safe_fopen(path, "w+"))) { mutt_perror(path); mutt_sleep(2); return -1; } } snprintf(buf, sizeof(buf), "RETR %d\r\n", h->refno); ret = pop_fetch_data(pop_data, buf, &progressbar, fetch_message, msg->fp); if (ret == 0) break; safe_fclose(&msg->fp); /* if RETR failed (e.g. connection closed), be sure to remove either * the file in bcache or from POP's own cache since the next iteration * of the loop will re-attempt to put() the message */ if (!bcache) unlink(path); if (ret == -2) { mutt_error("%s", pop_data->err_msg); mutt_sleep(2); return -1; } if (ret == -3) { mutt_error _("Can't write message to temporary file!"); mutt_sleep(2); return -1; } } /* Update the header information. Previously, we only downloaded a * portion of the headers, those required for the main display. */ if (bcache) mutt_bcache_commit(pop_data->bcache, h->data); else { cache->index = h->index; cache->path = safe_strdup(path); } rewind(msg->fp); uidl = h->data; /* we replace envelop, key in subj_hash has to be updated as well */ if (ctx->subj_hash && h->env->real_subj) hash_delete(ctx->subj_hash, h->env->real_subj, h, NULL); mutt_free_envelope(&h->env); h->env = mutt_read_rfc822_header(msg->fp, h, 0, 0); if (ctx->subj_hash && h->env->real_subj) hash_insert(ctx->subj_hash, h->env->real_subj, h, 1); h->data = uidl; h->lines = 0; fgets(buf, sizeof(buf), msg->fp); while (!feof(msg->fp)) { ctx->hdrs[msgno]->lines++; fgets(buf, sizeof(buf), msg->fp); } h->content->length = ftello(msg->fp) - h->content->offset; /* This needs to be done in case this is a multipart message */ if (!WithCrypto) h->security = crypt_query(h->content); mutt_clear_error(); rewind(msg->fp); return 0; }
/* * Open connection and authenticate * 0 - successful, * -1 - conection lost, * -2 - invalid command or execution error, * -3 - authentication canceled. */ int pop_open_connection (POP_DATA *pop_data) { int ret; unsigned int n, size; char buf[LONG_STRING]; ret = pop_connect (pop_data); if (ret < 0) { mutt_sleep (2); return ret; } ret = pop_capabilities (pop_data, 0); if (ret == -1) goto err_conn; if (ret == -2) { mutt_sleep (2); return -2; } #if defined(USE_SSL) /* Attempt STLS if available and desired. */ if (!pop_data->conn->ssf && (pop_data->cmd_stls || option(OPTSSLFORCETLS))) { if (option(OPTSSLFORCETLS)) pop_data->use_stls = 2; if (pop_data->use_stls == 0) { ret = query_quadoption (OPT_SSLSTARTTLS, _("Secure connection with TLS?")); if (ret == -1) return -2; pop_data->use_stls = 1; if (ret == M_YES) pop_data->use_stls = 2; } if (pop_data->use_stls == 2) { strfcpy (buf, "STLS\r\n", sizeof (buf)); ret = pop_query (pop_data, buf, sizeof (buf)); if (ret == -1) goto err_conn; if (ret != 0) { mutt_error ("%s", pop_data->err_msg); mutt_sleep (2); } else if (mutt_ssl_starttls (pop_data->conn)) { mutt_error (_("Could not negotiate TLS connection")); mutt_sleep (2); return -2; } else { /* recheck capabilities after STLS completes */ ret = pop_capabilities (pop_data, 1); if (ret == -1) goto err_conn; if (ret == -2) { mutt_sleep (2); return -2; } } } } if (option(OPTSSLFORCETLS) && !pop_data->conn->ssf) { mutt_error _("Encrypted connection unavailable"); mutt_sleep (1); return -2; } #endif ret = pop_authenticate (pop_data); if (ret == -1) goto err_conn; if (ret == -3) mutt_clear_error (); if (ret != 0) return ret; /* recheck capabilities after authentication */ ret = pop_capabilities (pop_data, 2); if (ret == -1) goto err_conn; if (ret == -2) { mutt_sleep (2); return -2; } /* get total size of mailbox */ strfcpy (buf, "STAT\r\n", sizeof (buf)); ret = pop_query (pop_data, buf, sizeof (buf)); if (ret == -1) goto err_conn; if (ret == -2) { mutt_error ("%s", pop_data->err_msg); mutt_sleep (2); return ret; } sscanf (buf, "+OK %u %u", &n, &size); pop_data->size = size; return 0; err_conn: pop_data->status = POP_DISCONNECTED; mutt_error _("Server closed connection!"); mutt_sleep (2); return -1; }
int imap_fetch_message (CONTEXT *ctx, MESSAGE *msg, int msgno) { IMAP_DATA* idata; HEADER* h; ENVELOPE* newenv; char buf[LONG_STRING]; char path[_POSIX_PATH_MAX]; char *pc; long bytes; progress_t progressbar; int uid; int cacheno; IMAP_CACHE *cache; int read; int rc; /* Sam's weird courier server returns an OK response even when FETCH * fails. Thanks Sam. */ short fetched = 0; idata = (IMAP_DATA*) ctx->data; h = ctx->hdrs[msgno]; if ((msg->fp = msg_cache_get (idata, h))) { if (HEADER_DATA(h)->parsed) return 0; else goto parsemsg; } /* we still do some caching even if imap_cachedir is unset */ /* see if we already have the message in our cache */ cacheno = HEADER_DATA(h)->uid % IMAP_CACHE_LEN; cache = &idata->cache[cacheno]; if (cache->path) { /* don't treat cache errors as fatal, just fall back. */ if (cache->uid == HEADER_DATA(h)->uid && (msg->fp = fopen (cache->path, "r"))) return 0; else { unlink (cache->path); FREE (&cache->path); } } if (!isendwin()) mutt_message _("Fetching message..."); if (!(msg->fp = msg_cache_put (idata, h))) { cache->uid = HEADER_DATA(h)->uid; mutt_mktemp (path, sizeof (path)); cache->path = safe_strdup (path); if (!(msg->fp = safe_fopen (path, "w+"))) { FREE (&cache->path); return -1; } } /* mark this header as currently inactive so the command handler won't * also try to update it. HACK until all this code can be moved into the * command handler */ h->active = 0; snprintf (buf, sizeof (buf), "UID FETCH %u %s", HEADER_DATA(h)->uid, (mutt_bit_isset (idata->capabilities, IMAP4REV1) ? (option (OPTIMAPPEEK) ? "BODY.PEEK[]" : "BODY[]") : "RFC822")); imap_cmd_start (idata, buf); do { if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE) break; pc = idata->buf; pc = imap_next_word (pc); pc = imap_next_word (pc); if (!ascii_strncasecmp ("FETCH", pc, 5)) { while (*pc) { pc = imap_next_word (pc); if (pc[0] == '(') pc++; if (ascii_strncasecmp ("UID", pc, 3) == 0) { pc = imap_next_word (pc); uid = atoi (pc); if (uid != HEADER_DATA(h)->uid) mutt_error (_("The message index is incorrect. Try reopening the mailbox.")); } else if ((ascii_strncasecmp ("RFC822", pc, 6) == 0) || (ascii_strncasecmp ("BODY[]", pc, 6) == 0)) { pc = imap_next_word (pc); if (imap_get_literal_count(pc, &bytes) < 0) { imap_error ("imap_fetch_message()", buf); goto bail; } mutt_progress_init (&progressbar, _("Fetching message..."), MUTT_PROGRESS_SIZE, NetInc, bytes); if (imap_read_literal (msg->fp, idata, bytes, &progressbar) < 0) goto bail; /* pick up trailing line */ if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE) goto bail; pc = idata->buf; fetched = 1; } /* UW-IMAP will provide a FLAGS update here if the FETCH causes a * change (eg from \Unseen to \Seen). * Uncommitted changes in mutt take precedence. If we decide to * incrementally update flags later, this won't stop us syncing */ else if ((ascii_strncasecmp ("FLAGS", pc, 5) == 0) && !h->changed) { if ((pc = imap_set_flags (idata, h, pc)) == NULL) goto bail; } } } } while (rc == IMAP_CMD_CONTINUE); /* see comment before command start. */ h->active = 1; fflush (msg->fp); if (ferror (msg->fp)) { mutt_perror (cache->path); goto bail; } if (rc != IMAP_CMD_OK) goto bail; if (!fetched || !imap_code (idata->buf)) goto bail; msg_cache_commit (idata, h); parsemsg: /* Update the header information. Previously, we only downloaded a * portion of the headers, those required for the main display. */ rewind (msg->fp); /* It may be that the Status header indicates a message is read, but the * IMAP server doesn't know the message has been \Seen. So we capture * the server's notion of 'read' and if it differs from the message info * picked up in mutt_read_rfc822_header, we mark the message (and context * changed). Another possibility: ignore Status on IMAP?*/ read = h->read; newenv = mutt_read_rfc822_header (msg->fp, h, 0, 0); mutt_merge_envelopes(h->env, &newenv); /* see above. We want the new status in h->read, so we unset it manually * and let mutt_set_flag set it correctly, updating context. */ if (read != h->read) { h->read = read; mutt_set_flag (ctx, h, MUTT_NEW, read); } h->lines = 0; fgets (buf, sizeof (buf), msg->fp); while (!feof (msg->fp)) { h->lines++; fgets (buf, sizeof (buf), msg->fp); } h->content->length = ftell (msg->fp) - h->content->offset; /* This needs to be done in case this is a multipart message */ #if defined(HAVE_PGP) || defined(HAVE_SMIME) h->security = crypt_query (h->content); #endif mutt_clear_error(); rewind (msg->fp); HEADER_DATA(h)->parsed = 1; return 0; bail: safe_fclose (&msg->fp); imap_cache_del (idata, h); if (cache->path) { unlink (cache->path); FREE (&cache->path); } return -1; }
/** * mutt_yesorno - Ask the user a Yes/No question * @param msg Prompt * @param def Default answer, see #QuadOption * @retval num Selection made, see #QuadOption */ enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def) { struct Event ch; char *yes = _("yes"); char *no = _("no"); char *answer_string = NULL; int answer_string_wid, msg_wid; size_t trunc_msg_len; bool redraw = true; int prompt_lines = 1; char *expr = NULL; regex_t reyes; regex_t reno; char answer[2]; answer[1] = '\0'; bool reyes_ok = (expr = nl_langinfo(YESEXPR)) && (expr[0] == '^') && (REGCOMP(&reyes, expr, REG_NOSUB) == 0); bool reno_ok = (expr = nl_langinfo(NOEXPR)) && (expr[0] == '^') && (REGCOMP(&reno, expr, REG_NOSUB) == 0); /* In order to prevent the default answer to the question to wrapped * around the screen in the even the question is wider than the screen, * ensure there is enough room for the answer and truncate the question * to fit. */ safe_asprintf(&answer_string, " ([%s]/%s): ", (def == MUTT_YES) ? yes : no, (def == MUTT_YES) ? no : yes); answer_string_wid = mutt_strwidth(answer_string); msg_wid = mutt_strwidth(msg); while (true) { if (redraw || SigWinch) { redraw = false; if (SigWinch) { SigWinch = 0; mutt_resize_screen(); clearok(stdscr, TRUE); mutt_menu_current_redraw(); } if (MuttMessageWindow->cols) { prompt_lines = (msg_wid + answer_string_wid + MuttMessageWindow->cols - 1) / MuttMessageWindow->cols; prompt_lines = MAX(1, MIN(3, prompt_lines)); } if (prompt_lines != MuttMessageWindow->rows) { mutt_window_reflow_message_rows(prompt_lines); mutt_menu_current_redraw(); } /* maxlen here is sort of arbitrary, so pick a reasonable upper bound */ trunc_msg_len = mutt_wstr_trunc( msg, 4 * prompt_lines * MuttMessageWindow->cols, prompt_lines * MuttMessageWindow->cols - answer_string_wid, NULL); mutt_window_move(MuttMessageWindow, 0, 0); SETCOLOR(MT_COLOR_PROMPT); addnstr(msg, trunc_msg_len); addstr(answer_string); NORMAL_COLOR; mutt_window_clrtoeol(MuttMessageWindow); } mutt_refresh(); /* SigWinch is not processed unless timeout is set */ mutt_getch_timeout(30 * 1000); ch = mutt_getch(); mutt_getch_timeout(-1); if (ch.ch == -2) continue; if (CI_is_return(ch.ch)) break; if (ch.ch < 0) { def = MUTT_ABORT; break; } answer[0] = ch.ch; if (reyes_ok ? (regexec(&reyes, answer, 0, 0, 0) == 0) : (tolower(ch.ch) == 'y')) { def = MUTT_YES; break; } else if (reno_ok ? (regexec(&reno, answer, 0, 0, 0) == 0) : (tolower(ch.ch) == 'n')) { def = MUTT_NO; break; } else { BEEP(); } } FREE(&answer_string); if (reyes_ok) regfree(&reyes); if (reno_ok) regfree(&reno); if (MuttMessageWindow->rows != 1) { mutt_window_reflow_message_rows(1); mutt_menu_current_redraw(); } else mutt_window_clearline(MuttMessageWindow, 0); if (def != MUTT_ABORT) { addstr((char *) ((def == MUTT_YES) ? yes : no)); mutt_refresh(); } else { /* when the users cancels with ^G, clear the message stored with * mutt_message() so it isn't displayed when the screen is refreshed. */ mutt_clear_error(); } return def; }
int mutt_menuLoop (MUTTMENU * menu) { int i = OP_NULL; FOREVER { if (option (OPTMENUCALLER)) { unset_option (OPTMENUCALLER); return OP_NULL; } mutt_curs_set (0); #ifdef USE_IMAP imap_keepalive (); #endif if (menu_redraw (menu) == OP_REDRAW) return OP_REDRAW; menu->oldcurrent = menu->current; if (option (OPTARROWCURSOR)) move (menu->current - menu->top + menu->offset, SW + 2); else if (option (OPTBRAILLEFRIENDLY)) move (menu->current - menu->top + menu->offset, SW); else move (menu->current - menu->top + menu->offset, COLS - 1); mutt_refresh (); /* try to catch dialog keys before ops */ if (menu->dialog && menu_dialog_dokey (menu, &i) == 0) return i; i = km_dokey (menu->menu); if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND) { if (menu->tagged) { mvaddstr (LINES - 1, 0, "Tag-"); clrtoeol (); i = km_dokey (menu->menu); menu->tagprefix = 1; CLEARLINE (LINES - 1); } else if (i == OP_TAG_PREFIX) { mutt_error _("No tagged entries."); i = -1; } else { /* None tagged, OP_TAG_PREFIX_COND */ event_t tmp; while (UngetCount > 0) { tmp = mutt_getch (); if (tmp.op == OP_END_COND) break; } mutt_message _("Nothing to do."); i = -1; } } else if (menu->tagged && option (OPTAUTOTAG)) menu->tagprefix = 1; else menu->tagprefix = 0; mutt_curs_set (1); #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM) if (SigWinch) { mutt_resize_screen (); menu->redraw = REDRAW_FULL; SigWinch = 0; clearok (stdscr, TRUE); /*force complete redraw */ } #endif if (i == -1) continue; if (!menu->dialog) mutt_clear_error (); /* Convert menubar movement to scrolling */ if (menu->dialog) i = menu_dialog_translate_op (i); switch (i) { case OP_NEXT_ENTRY: menu_next_entry (menu); break; case OP_PREV_ENTRY: menu_prev_entry (menu); break; case OP_HALF_DOWN: menu_half_down (menu); break; case OP_HALF_UP: menu_half_up (menu); break; case OP_NEXT_PAGE: menu_next_page (menu); break; case OP_PREV_PAGE: menu_prev_page (menu); break; case OP_NEXT_LINE: menu_next_line (menu); break; case OP_PREV_LINE: menu_prev_line (menu); break; case OP_FIRST_ENTRY: menu_first_entry (menu); break; case OP_LAST_ENTRY: menu_last_entry (menu); break; case OP_TOP_PAGE: menu_top_page (menu); break; case OP_MIDDLE_PAGE: menu_middle_page (menu); break; case OP_BOTTOM_PAGE: menu_bottom_page (menu); break; case OP_CURRENT_TOP: menu_current_top (menu); break; case OP_CURRENT_MIDDLE: menu_current_middle (menu); break; case OP_CURRENT_BOTTOM: menu_current_bottom (menu); break; case OP_SEARCH: case OP_SEARCH_REVERSE: case OP_SEARCH_NEXT: case OP_SEARCH_OPPOSITE: if (menu->search && !menu->dialog) { /* Searching dialogs won't work */ menu->oldcurrent = menu->current; if ((menu->current = menu_search (menu, i)) != -1) menu->redraw = REDRAW_MOTION; else menu->current = menu->oldcurrent; } else mutt_error _("Search is not implemented for this menu."); break; case OP_JUMP: if (menu->dialog) mutt_error (_("Jumping is not implemented for dialogs.")); else menu_jump (menu); break; case OP_ENTER_COMMAND: CurrentMenu = menu->menu; mutt_enter_command (); if (option (OPTFORCEREDRAWINDEX)) { menu->redraw = REDRAW_FULL; unset_option (OPTFORCEREDRAWINDEX); unset_option (OPTFORCEREDRAWPAGER); } break; case OP_TAG: if (menu->tag && !menu->dialog) { if (menu->tagprefix && !option (OPTAUTOTAG)) { for (i = 0; i < menu->max; i++) menu->tagged += menu->tag (menu, i, 0); menu->redraw = REDRAW_INDEX; } else if (menu->max) { int i = menu->tag (menu, menu->current, -1); menu->tagged += i; if (i && option (OPTRESOLVE) && menu->current < menu->max - 1) { menu->current++; menu->redraw = REDRAW_MOTION_RESYNCH; } else menu->redraw = REDRAW_CURRENT; } else mutt_error _("No entries."); } else mutt_error _("Tagging is not supported."); break; case OP_SHELL_ESCAPE: mutt_shell_escape (); MAYBE_REDRAW (menu->redraw); break; case OP_WHAT_KEY: mutt_what_key (); break; case OP_REBUILD_CACHE: mx_rebuild_cache (); break; case OP_REDRAW: clearok (stdscr, TRUE); menu->redraw = REDRAW_FULL; break; case OP_HELP: mutt_help (menu->menu); menu->redraw = REDRAW_FULL; break; case OP_NULL: km_error_key (menu->menu); break; case OP_END_COND: break; default: return (i); } } /* not reached */ }
static pgp_key_t pgp_select_key (pgp_key_t keys, ADDRESS * p, const char *s) { int keymax; pgp_uid_t **KeyTable; MUTTMENU *menu; int i, done = 0; char helpstr[LONG_STRING], buf[LONG_STRING], tmpbuf[STRING]; char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX]; FILE *fp, *devnull; pid_t thepid; pgp_key_t kp; pgp_uid_t *a; int (*f) (const void *, const void *); int unusable = 0; keymax = 0; KeyTable = NULL; for (i = 0, kp = keys; kp; kp = kp->next) { if (!option (OPTPGPSHOWUNUSABLE) && (kp->flags & KEYFLAG_CANTUSE)) { unusable = 1; continue; } for (a = kp->address; a; a = a->next) { if (!option (OPTPGPSHOWUNUSABLE) && (a->flags & KEYFLAG_CANTUSE)) { unusable = 1; continue; } if (i == keymax) { keymax += 5; safe_realloc (&KeyTable, sizeof (pgp_uid_t *) * keymax); } KeyTable[i++] = a; } } if (!i && unusable) { mutt_error _("All matching keys are expired, revoked, or disabled."); mutt_sleep (1); return NULL; } switch (PgpSortKeys & SORT_MASK) { case SORT_DATE: f = pgp_compare_date; break; case SORT_KEYID: f = pgp_compare_keyid; break; case SORT_ADDRESS: f = pgp_compare_address; break; case SORT_TRUST: default: f = pgp_compare_trust; break; } qsort (KeyTable, i, sizeof (pgp_uid_t *), f); helpstr[0] = 0; mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_PGP, OP_EXIT); strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ mutt_make_help (buf, sizeof (buf), _("Select "), MENU_PGP, OP_GENERIC_SELECT_ENTRY); strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ mutt_make_help (buf, sizeof (buf), _("Check key "), MENU_PGP, OP_VERIFY_KEY); strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ mutt_make_help (buf, sizeof (buf), _("Help"), MENU_PGP, OP_HELP); strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ menu = mutt_new_menu (MENU_PGP); menu->max = i; menu->make_entry = pgp_entry; menu->help = helpstr; menu->data = KeyTable; if (p) snprintf (buf, sizeof (buf), _("PGP keys matching <%s>."), p->mailbox); else snprintf (buf, sizeof (buf), _("PGP keys matching \"%s\"."), s); menu->title = buf; kp = NULL; mutt_clear_error (); while (!done) { switch (mutt_menuLoop (menu)) { case OP_VERIFY_KEY: mutt_mktemp (tempfile, sizeof (tempfile)); if ((devnull = fopen ("/dev/null", "w")) == NULL) /* __FOPEN_CHECKED__ */ { mutt_perror _("Can't open /dev/null"); break; } if ((fp = safe_fopen (tempfile, "w")) == NULL) { safe_fclose (&devnull); mutt_perror _("Can't create temporary file"); break; } mutt_message _("Invoking PGP..."); snprintf (tmpbuf, sizeof (tmpbuf), "0x%s", pgp_fpr_or_lkeyid (pgp_principal_key (KeyTable[menu->current]->parent))); if ((thepid = pgp_invoke_verify_key (NULL, NULL, NULL, -1, fileno (fp), fileno (devnull), tmpbuf)) == -1) { mutt_perror _("Can't create filter"); unlink (tempfile); safe_fclose (&fp); safe_fclose (&devnull); } mutt_wait_filter (thepid); safe_fclose (&fp); safe_fclose (&devnull); mutt_clear_error (); snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), pgp_keyid (pgp_principal_key (KeyTable[menu->current]->parent))); mutt_do_pager (cmd, tempfile, 0, NULL); menu->redraw = REDRAW_FULL; break; case OP_VIEW_ID: mutt_message ("%s", NONULL (KeyTable[menu->current]->addr)); break; case OP_GENERIC_SELECT_ENTRY: /* XXX make error reporting more verbose */ if (option (OPTPGPCHECKTRUST)) if (!pgp_key_is_valid (KeyTable[menu->current]->parent)) { mutt_error _("This key can't be used: expired/disabled/revoked."); break; } if (option (OPTPGPCHECKTRUST) && (!pgp_id_is_valid (KeyTable[menu->current]) || !pgp_id_is_strong (KeyTable[menu->current]))) { char *s = ""; char buff[LONG_STRING]; if (KeyTable[menu->current]->flags & KEYFLAG_CANTUSE) s = N_("ID is expired/disabled/revoked."); else switch (KeyTable[menu->current]->trust & 0x03) { case 0: s = N_("ID has undefined validity."); break; case 1: s = N_("ID is not valid."); break; case 2: s = N_("ID is only marginally valid."); break; } snprintf (buff, sizeof (buff), _("%s Do you really want to use the key?"), _(s)); if (mutt_yesorno (buff, M_NO) != M_YES) { mutt_clear_error (); break; } } # if 0 kp = pgp_principal_key (KeyTable[menu->current]->parent); # else kp = KeyTable[menu->current]->parent; # endif done = 1; break; case OP_EXIT: kp = NULL; done = 1; break; } } mutt_menuDestroy (&menu); FREE (&KeyTable); set_option (OPTNEEDREDRAW); return (kp); }
int mutt_prepare_template (FILE *fp, CONTEXT *ctx, HEADER *newhdr, HEADER *hdr, short weed) { MESSAGE *msg = NULL; char file[_POSIX_PATH_MAX]; LIST *p, **q; BODY *b; FILE *bfp; if (!fp && (msg = mx_open_message (ctx, hdr->msgno)) == NULL) return (-1); if (!fp) fp = msg->fp; bfp = fp; /* parse the message header and MIME structure */ fseek (fp, hdr->offset, 0); newhdr->offset = hdr->offset; newhdr->env = mutt_read_rfc822_header (fp, newhdr, 1, weed); newhdr->content->length = hdr->content->length; mutt_parse_part (fp, newhdr->content); /* weed user-agent, x-mailer - we don't want them here */ p = newhdr->env->userhdrs; q = &newhdr->env->userhdrs; while (p) { if (!strncasecmp (p->data, "x-mailer:", 9) || !strncasecmp (p->data, "user-agent:", 11)) { *q = p->next; p->next = NULL; mutt_free_list (&p); } else q = &p->next; p = *q; } safe_free ((void **) &newhdr->env->message_id); safe_free ((void **) &newhdr->env->mail_followup_to); #ifdef HAVE_PGP /* decrypt pgp/mime encoded messages */ if ((hdr->pgp & PGPENCRYPT) && mutt_is_multipart_encrypted (newhdr->content)) { newhdr->pgp |= PGPENCRYPT; if (!pgp_valid_passphrase()) goto err; mutt_message _("Invoking PGP..."); if (pgp_decrypt_mime (fp, &bfp, newhdr->content, &b) == -1) { err: mx_close_message (&msg); mutt_free_envelope (&newhdr->env); mutt_free_body (&newhdr->content); return -1; } mutt_free_body (&newhdr->content); newhdr->content = b; mutt_clear_error (); } /* * remove a potential multipart/signed layer - useful when * resending messages */ if (mutt_is_multipart_signed (newhdr->content)) { newhdr->pgp |= PGPSIGN; /* destroy the signature */ mutt_free_body (&newhdr->content->parts->next); newhdr->content = mutt_remove_multipart (newhdr->content); } #endif /* * We don't need no primary multipart. * Note: We _do_ preserve messages! * * XXX - we don't handle multipart/alternative in any * smart way when sending messages. However, one may * consider this a feature. * */ if (newhdr->content->type == TYPEMULTIPART) newhdr->content = mutt_remove_multipart (newhdr->content); /* create temporary files for all attachments */ for (b = newhdr->content; b; b = b->next) { /* what follows is roughly a receive-mode variant of * mutt_get_tmp_attachment () from muttlib.c */ file[0] = '\0'; if (b->filename) { strfcpy (file, b->filename, sizeof (file)); b->d_filename = safe_strdup (b->filename); } else /* avoid Content-Disposition: header with temporary filename */ b->use_disp = 0; mutt_adv_mktemp (file, sizeof(file)); if (mutt_save_attachment (bfp, b, file, 0, NULL) == -1) { mutt_free_envelope (&newhdr->env); mutt_free_body (&newhdr->content); if (bfp != fp) fclose (bfp); if (msg) mx_close_message (&msg); return -1; } mutt_str_replace (&b->filename, file); b->unlink = 1; if (mutt_is_text_type (b->type, b->subtype)) b->noconv = 1; mutt_stamp_attachment (b); mutt_free_body (&b->parts); if (b->hdr) b->hdr->content = NULL; /* avoid dangling pointer */ } /* that's it. */ if (bfp != fp) fclose (bfp); if (msg) mx_close_message (&msg); return 0; }
/* * Read headers * returns: * 0 on success * -1 - conection lost, * -2 - invalid command or execution error, * -3 - error writing to tempfile */ static int pop_fetch_headers (CONTEXT *ctx) { int i, ret, old_count, new_count, deleted; unsigned short hcached = 0, bcached; POP_DATA *pop_data = (POP_DATA *)ctx->data; progress_t progress; #ifdef USE_HCACHE header_cache_t *hc = NULL; void *data; hc = pop_hcache_open (pop_data, ctx->path); #endif time (&pop_data->check_time); pop_data->clear_cache = 0; for (i = 0; i < ctx->msgcount; i++) ctx->hdrs[i]->refno = -1; old_count = ctx->msgcount; ret = pop_fetch_data (pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx); new_count = ctx->msgcount; ctx->msgcount = old_count; if (pop_data->cmd_uidl == 2) { if (ret == 0) { pop_data->cmd_uidl = 1; dprint (1, (debugfile, "pop_fetch_headers: set UIDL capability\n")); } if (ret == -2 && pop_data->cmd_uidl == 2) { pop_data->cmd_uidl = 0; dprint (1, (debugfile, "pop_fetch_headers: unset UIDL capability\n")); snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), _("Command UIDL is not supported by server.")); } } if (!ctx->quiet) mutt_progress_init (&progress, _("Fetching message headers..."), M_PROGRESS_MSG, ReadInc, new_count - old_count); if (ret == 0) { for (i = 0, deleted = 0; i < old_count; i++) { if (ctx->hdrs[i]->refno == -1) { ctx->hdrs[i]->deleted = 1; deleted++; } } if (deleted > 0) { mutt_error (_("%d messages have been lost. Try reopening the mailbox."), deleted); mutt_sleep (2); } for (i = old_count; i < new_count; i++) { if (!ctx->quiet) mutt_progress_update (&progress, i + 1 - old_count, -1); #if USE_HCACHE if ((data = mutt_hcache_fetch (hc, ctx->hdrs[i]->data, strlen))) { char *uidl = safe_strdup (ctx->hdrs[i]->data); int refno = ctx->hdrs[i]->refno; int index = ctx->hdrs[i]->index; /* * - POP dynamically numbers headers and relies on h->refno * to map messages; so restore header and overwrite restored * refno with current refno, same for index * - h->data needs to a separate pointer as it's driver-specific * data freed separately elsewhere * (the old h->data should point inside a malloc'd block from * hcache so there shouldn't be a memleak here) */ HEADER *h = mutt_hcache_restore ((unsigned char *) data, NULL); mutt_free_header (&ctx->hdrs[i]); ctx->hdrs[i] = h; ctx->hdrs[i]->refno = refno; ctx->hdrs[i]->index = index; ctx->hdrs[i]->data = uidl; ret = 0; hcached = 1; } else #endif if ((ret = pop_read_header (pop_data, ctx->hdrs[i])) < 0) break; #if USE_HCACHE else { mutt_hcache_store (hc, ctx->hdrs[i]->data, ctx->hdrs[i], 0, strlen, M_GENERATE_UIDVALIDITY); } FREE(&data); #endif /* * faked support for flags works like this: * - if 'hcached' is 1, we have the message in our hcache: * - if we also have a body: read * - if we don't have a body: old * (if $mark_old is set which is maybe wrong as * $mark_old should be considered for syncing the * folder and not when opening it XXX) * - if 'hcached' is 0, we don't have the message in our hcache: * - if we also have a body: read * - if we don't have a body: new */ bcached = mutt_bcache_exists (pop_data->bcache, ctx->hdrs[i]->data) == 0; ctx->hdrs[i]->old = 0; ctx->hdrs[i]->read = 0; if (hcached) { if (bcached) ctx->hdrs[i]->read = 1; else if (option (OPTMARKOLD)) ctx->hdrs[i]->old = 1; } else { if (bcached) ctx->hdrs[i]->read = 1; } ctx->msgcount++; } if (i > old_count) mx_update_context (ctx, i - old_count); } #if USE_HCACHE mutt_hcache_close (hc); #endif if (ret < 0) { for (i = ctx->msgcount; i < new_count; i++) mutt_free_header (&ctx->hdrs[i]); return ret; } /* after putting the result into our structures, * clean up cache, i.e. wipe messages deleted outside * the availability of our cache */ if (option (OPTMESSAGECACHECLEAN)) mutt_bcache_list (pop_data->bcache, msg_cache_check, (void*)ctx); mutt_clear_error (); return (new_count - old_count); }
static smime_key_t *smime_select_key(smime_key_t *keys, char *query) { smime_key_t **table = NULL; int table_size = 0; int table_index = 0; smime_key_t *key = NULL; smime_key_t *selected_key = NULL; char helpstr[LONG_STRING]; char buf[LONG_STRING]; char title[256]; MUTTMENU* menu; char *s = ""; int done = 0; for (table_index = 0, key = keys; key; key = key->next) { if (table_index == table_size) { table_size += 5; safe_realloc(&table, sizeof(smime_key_t *) * table_size); } table[table_index++] = key; } snprintf(title, sizeof(title),("S/MIME certificates matching \"%s\"."), query); /* Make Helpstring */ helpstr[0] = 0; mutt_make_help(buf, sizeof(buf),("Exit "), MENU_SMIME, OP_EXIT); strcat(helpstr, buf); /* __STRCAT_CHECKED__ */ mutt_make_help(buf, sizeof(buf),("Select "), MENU_SMIME, OP_GENERIC_SELECT_ENTRY); strcat(helpstr, buf); /* __STRCAT_CHECKED__ */ mutt_make_help(buf, sizeof(buf),("Help"), MENU_SMIME, OP_HELP); strcat(helpstr, buf); /* __STRCAT_CHECKED__ */ /* Create the menu */ menu = mutt_new_menu(MENU_SMIME); menu->max = table_index; menu->make_entry = smime_entry; menu->help = helpstr; menu->data = table; menu->title = title; /* sorting keys might be done later - TODO */ mutt_clear_error (); done = 0; while (!done) { switch(mutt_menuLoop(menu)) { case OP_GENERIC_SELECT_ENTRY: if (table[menu->current]->trust != 't') { switch(table[menu->current]->trust) { case 'i': case 'r': case 'e': s =("ID is expired/disabled/revoked."); break; case 'u': s =("ID has undefined validity."); break; case 'v': s =("ID is not trusted."); break; } snprintf(buf, sizeof(buf),("%s Do you really want to use the key?"), (s)); if (mutt_yesorno(buf, M_NO) != M_YES) { mutt_clear_error (); break; } } selected_key = table[menu->current]; done = 1; break; case OP_EXIT: done = 1; break; } } mutt_menuDestroy(&menu); safe_free(&table); set_bit(options, OPTNEEDREDRAW); return selected_key; }
/* imap_browse: IMAP hook into the folder browser, fills out browser_state, * given a current folder to browse */ int imap_browse (char* path, struct browser_state* state) { IMAP_DATA* idata; char buf[LONG_STRING]; char buf2[LONG_STRING]; char nsbuf[LONG_STRING]; char mbox[LONG_STRING]; char list_cmd[5]; IMAP_NAMESPACE_INFO nsi[16]; int home_namespace = 0; int n; int i; int nsup; char ctmp; int nns; char *cur_folder; short showparents = 0; int noselect; int noinferiors; IMAP_MBOX mx; if (imap_parse_path (path, &mx)) { mutt_error (_("%s is an invalid IMAP path"), path); return -1; } strfcpy (list_cmd, option (OPTIMAPLSUB) ? "LSUB" : "LIST", sizeof (list_cmd)); if (!(idata = imap_conn_find (&(mx.account), 0))) goto fail; if (!mx.mbox) { home_namespace = 1; mbox[0] = '\0'; /* Do not replace "" with "INBOX" here */ mx.mbox = safe_strdup(ImapHomeNamespace); nns = 0; if (mutt_bit_isset(idata->capabilities,NAMESPACE)) { mutt_message _("Getting namespaces..."); if (browse_get_namespace (idata, nsbuf, sizeof (nsbuf), nsi, sizeof (nsi), &nns) != 0) goto fail; if (browse_verify_namespace (idata, nsi, nns) != 0) goto fail; } } mutt_message _("Getting folder list..."); /* skip check for parents when at the root */ if (mx.mbox && mx.mbox[0] != '\0') { imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox)); imap_munge_mbox_name (buf, sizeof (buf), mbox); imap_unquote_string(buf); /* As kludgy as it gets */ mbox[sizeof (mbox) - 1] = '\0'; strncpy (mbox, buf, sizeof (mbox) - 1); n = mutt_strlen (mbox); dprint (3, (debugfile, "imap_browse: mbox: %s\n", mbox)); /* if our target exists and has inferiors, enter it if we * aren't already going to */ if (mbox[n-1] != idata->delim) { snprintf (buf, sizeof (buf), "%s \"\" \"%s\"", list_cmd, mbox); imap_cmd_start (idata, buf); do { if (imap_parse_list_response (idata, &cur_folder, &noselect, &noinferiors, &idata->delim) != 0) goto fail; if (cur_folder) { imap_unmunge_mbox_name (cur_folder); if (!noinferiors && cur_folder[0] && (n = strlen (mbox)) < LONG_STRING-1) { mbox[n++] = idata->delim; mbox[n] = '\0'; } } } while (ascii_strncmp (idata->cmd.buf, idata->cmd.seq, SEQLEN)); } /* if we're descending a folder, mark it as current in browser_state */ if (mbox[n-1] == idata->delim) { /* don't show parents in the home namespace */ if (!home_namespace) showparents = 1; imap_qualify_path (buf, sizeof (buf), &mx, mbox); state->folder = safe_strdup (buf); n--; } /* Find superiors to list * Note: UW-IMAP servers return folder + delimiter when asked to list * folder + delimiter. Cyrus servers don't. So we ask for folder, * and tack on delimiter ourselves. * Further note: UW-IMAP servers return nothing when asked for * NAMESPACES without delimiters at the end. Argh! */ for (n--; n >= 0 && mbox[n] != idata->delim ; n--); if (n > 0) /* "aaaa/bbbb/" -> "aaaa" */ { /* forget the check, it is too delicate (see above). Have we ever * had the parent not exist? */ ctmp = mbox[n]; mbox[n] = '\0'; if (showparents) { dprint (3, (debugfile, "imap_init_browse: adding parent %s\n", mbox)); imap_add_folder (idata->delim, mbox, 1, 0, state, 1); } /* if our target isn't a folder, we are in our superior */ if (!state->folder) { /* store folder with delimiter */ mbox[n++] = ctmp; ctmp = mbox[n]; mbox[n] = '\0'; imap_qualify_path (buf, sizeof (buf), &mx, mbox); state->folder = safe_strdup (buf); } mbox[n] = ctmp; } /* "/bbbb/" -> add "/", "aaaa/" -> add "" */ else { char relpath[2]; /* folder may be "/" */ snprintf (relpath, sizeof (relpath), "%c" , n < 0 ? '\0' : idata->delim); if (showparents) imap_add_folder (idata->delim, relpath, 1, 0, state, 1); if (!state->folder) { imap_qualify_path (buf, sizeof (buf), &mx, relpath); state->folder = safe_strdup (buf); } } } /* no namespace, no folder: set folder to host only */ if (!state->folder) { imap_qualify_path (buf, sizeof (buf), &mx, NULL); state->folder = safe_strdup (buf); } if (home_namespace && mbox[0] != '\0') { /* Listing the home namespace, so INBOX should be included. Home * namespace is not "", so we have to list it explicitly. We ask the * server to see if it has descendants. */ dprint (2, (debugfile, "imap_init_browse: adding INBOX\n")); if (browse_add_list_result (idata, "LIST \"\" \"INBOX\"", state, 0)) goto fail; if (!state->entrylen) { mutt_error _("No such folder"); goto fail; } } nsup = state->entrylen; dprint (3, (debugfile, "imap_browse: Quoting mailbox scan: %s -> ", mbox)); snprintf (buf, sizeof (buf), "%s%%", mbox); imap_quote_string (buf2, sizeof (buf2), buf); dprint (3, (debugfile, "%s\n", buf2)); snprintf (buf, sizeof (buf), "%s \"\" %s", list_cmd, buf2); if (browse_add_list_result (idata, buf, state, 0)) goto fail; mutt_clear_error (); qsort(&(state->entry[nsup]),state->entrylen-nsup,sizeof(state->entry[0]), (int (*)(const void*,const void*)) compare_names); if (home_namespace) { /* List additional namespaces */ for (i = 0; i < nns; i++) if (nsi[i].listable && !nsi[i].home_namespace) { imap_add_folder(nsi[i].delim, nsi[i].prefix, nsi[i].noselect, nsi[i].noinferiors, state, 0); dprint (3, (debugfile, "imap_init_browse: adding namespace: %s\n", nsi[i].prefix)); } } FREE (&mx.mbox); return 0; fail: FREE (&mx.mbox); return -1; }
void mutt_attach_bounce (FILE * fp, struct header * hdr, ATTACHPTR ** idx, short idxlen, struct body * cur) { short i; char prompt[STRING]; char buf[HUGE_STRING]; struct address *adr = NULL; int ret = 0; int p = 0; if (check_all_msg (idx, idxlen, cur, 1) == -1) return; /* one or more messages? */ p = (cur || count_tagged (idx, idxlen) == 1); /* RfC 5322 mandates a From: header, so warn before bouncing * messages without one */ if (cur) { if (!cur->hdr->env->from) { mutt_error("Warning: message contains no From: header"); mutt_sleep(2); mutt_clear_error(); } } else { for (i = 0; i < idxlen; i++) { if (idx[i]->content->tagged) { if (!idx[i]->content->hdr->env->from) { mutt_error("Warning: message contains no From: header"); mutt_sleep (2); mutt_clear_error (); break; } } } } if (p) strfcpy (prompt, ("Bounce message to: "), sizeof (prompt)); else strfcpy (prompt, ("Bounce tagged messages to: "), sizeof (prompt)); buf[0] = '\0'; if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS) || buf[0] == '\0') return; if (!(adr = rfc822_parse_adrlist (adr, buf))) { mutt_error("Error parsing address!"); return; } adr = mutt_expand_aliases (adr); buf[0] = 0; rfc822_write_address (buf, sizeof (buf), adr, 1); #define extra_space (15+7+2) /* * See commands.c. */ snprintf (prompt, sizeof (prompt) - 4, (p ? ("Bounce message to %s") : ("Bounce messages to %s")), buf); if (mutt_strwidth (prompt) > COLS - extra_space) { mutt_format_string (prompt, sizeof (prompt) - 4, 0, COLS-extra_space, FMT_LEFT, 0, prompt, sizeof (prompt), 0); safe_strcat (prompt, sizeof (prompt), "...?"); } else safe_strcat (prompt, sizeof (prompt), "?"); if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) { rfc822_free_address (&adr); CLEARLINE (LINES - 1); mutt_message (p ? ("Message not bounced.") : ("Messages not bounced.")); return; } CLEARLINE (LINES - 1); if (cur) ret = mutt_bounce_message (fp, cur->hdr, adr); else { for (i = 0; i < idxlen; i++) { if (idx[i]->content->tagged) if (mutt_bounce_message (fp, idx[i]->content->hdr, adr)) ret = 1; } } if (!ret) mutt_message (p ? ("Message bounced.") : ("Messages bounced.")); else mutt_error (p ? ("Error bouncing message!") : ("Error bouncing messages!")); }
/* * Read headers * returns: * 0 on success * -1 - connection lost, * -2 - invalid command or execution error, * -3 - error writing to tempfile */ static int pop_fetch_headers(CONTEXT *ctx) { int i, ret, old_count, new_count, deleted; unsigned short hcached = 0, bcached; POP_DATA *pop_data = (POP_DATA *)ctx->data; progress_t progress; time(&pop_data->check_time); pop_data->clear_cache = 0; for (i = 0; i < ctx->msgcount; i++) ctx->hdrs[i]->refno = -1; old_count = ctx->msgcount; ret = pop_fetch_data(pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx); new_count = ctx->msgcount; ctx->msgcount = old_count; if (pop_data->cmd_uidl == 2) { if (ret == 0) { pop_data->cmd_uidl = 1; dprint(1, "pop_fetch_headers: set UIDL capability\n"); } if ((ret == -2) && (pop_data->cmd_uidl == 2)) { pop_data->cmd_uidl = 0; dprint(1, "pop_fetch_headers: unset UIDL capability\n"); snprintf(pop_data->err_msg, sizeof(pop_data->err_msg), _("Command UIDL is not supported by server.")); } } if (!ctx->quiet) mutt_progress_init(&progress, _("Fetching message headers..."), M_PROGRESS_MSG, ReadInc, new_count - old_count); if (ret == 0) { for (i = 0, deleted = 0; i < old_count; i++) { if (ctx->hdrs[i]->refno == -1) { ctx->hdrs[i]->deleted = 1; deleted++; } } if (deleted > 0) { mutt_error(_( "%d messages have been lost. Try reopening the mailbox."), deleted); mutt_sleep(2); } for (i = old_count; i < new_count; i++) { if (!ctx->quiet) mutt_progress_update(&progress, i + 1 - old_count, -1); if ((ret = pop_read_header(pop_data, ctx->hdrs[i])) < 0) break; /* * faked support for flags works like this: * - if 'hcached' is 1, we have the message in our hcache: * - if we also have a body: read * - if we don't have a body: old * (if $mark_old is set which is maybe wrong as * $mark_old should be considered for syncing the * folder and not when opening it XXX) * - if 'hcached' is 0, we don't have the message in our hcache: * - if we also have a body: read * - if we don't have a body: new */ bcached = mutt_bcache_exists(pop_data->bcache, ctx->hdrs[i]->data) == 0; ctx->hdrs[i]->old = 0; ctx->hdrs[i]->read = 0; if (hcached) { if (bcached) ctx->hdrs[i]->read = 1; else if (option(OPTMARKOLD)) ctx->hdrs[i]->old = 1; } else { if (bcached) ctx->hdrs[i]->read = 1; } ctx->msgcount++; } if (i > old_count) mx_update_context(ctx, i - old_count); } if (ret < 0) { for (i = ctx->msgcount; i < new_count; i++) mutt_free_header(&ctx->hdrs[i]); return ret; } /* after putting the result into our structures, * clean up cache, i.e. wipe messages deleted outside * the availability of our cache */ if (option(OPTMESSAGECACHECLEAN)) mutt_bcache_list(pop_data->bcache, msg_cache_check, (void *)ctx); mutt_clear_error(); return new_count - old_count; }
/* return values: * * 1 message should be postponed * 0 normal exit * -1 abort message */ int mutt_compose_menu (HEADER *msg, /* structure for new message */ char *fcc, /* where to save a copy of the message */ size_t fcclen, HEADER *cur, /* current message */ int flags) { char helpstr[LONG_STRING]; char buf[LONG_STRING]; char fname[_POSIX_PATH_MAX]; MUTTMENU *menu; ATTACHPTR **idx = NULL; short idxlen = 0; short idxmax = 0; int i, close = 0; int r = -1; /* return value */ int op = 0; int loop = 1; int fccSet = 0; /* has the user edited the Fcc: field ? */ CONTEXT *ctx = NULL, *this = NULL; /* Sort, SortAux could be changed in mutt_index_menu() */ int oldSort, oldSortAux; struct stat st; mutt_attach_init (msg->content); idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1); menu = mutt_new_menu (MENU_COMPOSE); menu->offset = HDR_ATTACH; menu->max = idxlen; menu->make_entry = snd_entry; menu->tag = mutt_tag_attach; menu->data = idx; menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp); while (loop) { switch (op = mutt_menuLoop (menu)) { case OP_REDRAW: draw_envelope (msg, fcc); menu->offset = HDR_ATTACH; menu->pagelen = LINES - HDR_ATTACH - 2; break; case OP_COMPOSE_EDIT_FROM: menu->redraw = edit_address_list (HDR_FROM, &msg->env->from); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_TO: menu->redraw = edit_address_list (HDR_TO, &msg->env->to); if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) { crypt_opportunistic_encrypt (msg); redraw_crypt_lines (msg); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_BCC: menu->redraw = edit_address_list (HDR_BCC, &msg->env->bcc); if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) { crypt_opportunistic_encrypt (msg); redraw_crypt_lines (msg); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_CC: menu->redraw = edit_address_list (HDR_CC, &msg->env->cc); if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) { crypt_opportunistic_encrypt (msg); redraw_crypt_lines (msg); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_SUBJECT: if (msg->env->subject) strfcpy (buf, msg->env->subject, sizeof (buf)); else buf[0] = 0; if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) == 0) { mutt_str_replace (&msg->env->subject, buf); move (HDR_SUBJECT, HDR_XOFFSET); if (msg->env->subject) mutt_paddstr (W, msg->env->subject); else clrtoeol(); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_REPLY_TO: menu->redraw = edit_address_list (HDR_REPLYTO, &msg->env->reply_to); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_FCC: strfcpy (buf, fcc, sizeof (buf)); if (mutt_get_field ("Fcc: ", buf, sizeof (buf), M_FILE | M_CLEAR) == 0) { strfcpy (fcc, buf, fcclen); mutt_pretty_mailbox (fcc, fcclen); move (HDR_FCC, HDR_XOFFSET); mutt_paddstr (W, fcc); fccSet = 1; } MAYBE_REDRAW (menu->redraw); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_MESSAGE: if (Editor && (mutt_strcmp ("builtin", Editor) != 0) && !option (OPTEDITHDRS)) { mutt_edit_file (Editor, msg->content->filename); mutt_update_encoding (msg->content); menu->redraw = REDRAW_FULL; mutt_message_hook (NULL, msg, M_SEND2HOOK); break; } /* fall through */ case OP_COMPOSE_EDIT_HEADERS: if (mutt_strcmp ("builtin", Editor) != 0 && (op == OP_COMPOSE_EDIT_HEADERS || (op == OP_COMPOSE_EDIT_MESSAGE && option (OPTEDITHDRS)))) { char *tag = NULL, *err = NULL; mutt_env_to_local (msg->env); mutt_edit_headers (NONULL (Editor), msg->content->filename, msg, fcc, fcclen); if (mutt_env_to_intl (msg->env, &tag, &err)) { mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err); FREE (&err); } if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) crypt_opportunistic_encrypt (msg); } else { /* this is grouped with OP_COMPOSE_EDIT_HEADERS because the attachment list could change if the user invokes ~v to edit the message with headers, in which we need to execute the code below to regenerate the index array */ mutt_builtin_editor (msg->content->filename, msg, cur); } mutt_update_encoding (msg->content); /* attachments may have been added */ if (idxlen && idx[idxlen - 1]->content->next) { for (i = 0; i < idxlen; i++) { FREE (&idx[i]->tree); FREE (&idx[i]); } idxlen = 0; idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1); menu->data = idx; menu->max = idxlen; } menu->redraw = REDRAW_FULL; mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_ATTACH_KEY: if (!(WithCrypto & APPLICATION_PGP)) break; if (idxlen == idxmax) { safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5)); menu->data = idx; } idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); if ((idx[idxlen]->content = crypt_pgp_make_key_attachment(NULL)) != NULL) { update_idx (menu, idx, idxlen++); menu->redraw |= REDRAW_INDEX; } else FREE (&idx[idxlen]); menu->redraw |= REDRAW_STATUS; if (option(OPTNEEDREDRAW)) { menu->redraw = REDRAW_FULL; unset_option(OPTNEEDREDRAW); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_ATTACH_FILE: { char *prompt, **files; int error, numfiles; fname[0] = 0; prompt = _("Attach file"); numfiles = 0; files = NULL; if (_mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 0, 1, &files, &numfiles) == -1 || *fname == '\0') break; if (idxlen + numfiles >= idxmax) { safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5 + numfiles)); menu->data = idx; } error = 0; if (numfiles > 1) mutt_message _("Attaching selected files..."); for (i = 0; i < numfiles; i++) { char *att = files[i]; idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); idx[idxlen]->unowned = 1; idx[idxlen]->content = mutt_make_file_attach (att); if (idx[idxlen]->content != NULL) update_idx (menu, idx, idxlen++); else { error = 1; mutt_error (_("Unable to attach %s!"), att); FREE (&idx[idxlen]); } } FREE (&files); if (!error) mutt_clear_error (); menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_ATTACH_MESSAGE: { char *prompt; HEADER *h; fname[0] = 0; prompt = _("Open mailbox to attach message from"); if (Context) { strfcpy (fname, NONULL (Context->path), sizeof (fname)); mutt_pretty_mailbox (fname, sizeof (fname)); } if (mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 1) == -1 || !fname[0]) break; mutt_expand_path (fname, sizeof (fname)); #ifdef USE_IMAP if (!mx_is_imap (fname)) #endif #ifdef USE_POP if (!mx_is_pop (fname)) #endif /* check to make sure the file exists and is readable */ if (access (fname, R_OK) == -1) { mutt_perror (fname); break; } menu->redraw = REDRAW_FULL; ctx = mx_open_mailbox (fname, M_READONLY, NULL); if (ctx == NULL) { mutt_error (_("Unable to open mailbox %s"), fname); break; } if (!ctx->msgcount) { mx_close_mailbox (ctx, NULL); FREE (&ctx); mutt_error _("No messages in that folder."); break; } this = Context; /* remember current folder and sort methods*/ oldSort = Sort; oldSortAux = SortAux; Context = ctx; set_option(OPTATTACHMSG); mutt_message _("Tag the messages you want to attach!"); close = mutt_index_menu (); unset_option(OPTATTACHMSG); if (!Context) { /* go back to the folder we started from */ Context = this; /* Restore old $sort and $sort_aux */ Sort = oldSort; SortAux = oldSortAux; menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; break; } if (idxlen + Context->tagged >= idxmax) { safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5 + Context->tagged)); menu->data = idx; } for (i = 0; i < Context->msgcount; i++) { h = Context->hdrs[i]; if (h->tagged) { idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); idx[idxlen]->content = mutt_make_message_attach (Context, h, 1); if (idx[idxlen]->content != NULL) update_idx (menu, idx, idxlen++); else { mutt_error _("Unable to attach!"); FREE (&idx[idxlen]); } } } menu->redraw |= REDRAW_FULL; if (close == OP_QUIT) mx_close_mailbox (Context, NULL); else mx_fastclose_mailbox (Context); FREE (&Context); /* go back to the folder we started from */ Context = this; /* Restore old $sort and $sort_aux */ Sort = oldSort; SortAux = oldSortAux; } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_DELETE: CHECK_COUNT; if (idx[menu->current]->unowned) idx[menu->current]->content->unlink = 0; if (delete_attachment (menu, &idxlen, menu->current) == -1) break; mutt_update_tree (idx, idxlen); if (idxlen) { if (menu->current > idxlen - 1) menu->current = idxlen - 1; } else menu->current = 0; if (menu->current == 0) msg->content = idx[0]->content; menu->redraw |= REDRAW_STATUS; mutt_message_hook (NULL, msg, M_SEND2HOOK); break; #define CURRENT idx[menu->current]->content case OP_COMPOSE_TOGGLE_RECODE: { CHECK_COUNT; if (!mutt_is_text_part (CURRENT)) { mutt_error (_("Recoding only affects text attachments.")); break; } CURRENT->noconv = !CURRENT->noconv; if (CURRENT->noconv) mutt_message (_("The current attachment won't be converted.")); else mutt_message (_("The current attachment will be converted.")); menu->redraw = REDRAW_CURRENT; mutt_message_hook (NULL, msg, M_SEND2HOOK); break; } #undef CURRENT case OP_COMPOSE_EDIT_DESCRIPTION: CHECK_COUNT; strfcpy (buf, idx[menu->current]->content->description ? idx[menu->current]->content->description : "", sizeof (buf)); /* header names should not be translated */ if (mutt_get_field ("Description: ", buf, sizeof (buf), 0) == 0) { mutt_str_replace (&idx[menu->current]->content->description, buf); menu->redraw = REDRAW_CURRENT; } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_UPDATE_ENCODING: CHECK_COUNT; if (menu->tagprefix) { BODY *top; for (top = msg->content; top; top = top->next) { if (top->tagged) mutt_update_encoding (top); } menu->redraw = REDRAW_FULL; } else { mutt_update_encoding(idx[menu->current]->content); menu->redraw = REDRAW_CURRENT | REDRAW_STATUS; } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_TOGGLE_DISPOSITION: /* toggle the content-disposition between inline/attachment */ idx[menu->current]->content->disposition = (idx[menu->current]->content->disposition == DISPINLINE) ? DISPATTACH : DISPINLINE; menu->redraw = REDRAW_CURRENT; break; case OP_EDIT_TYPE: CHECK_COUNT; { mutt_edit_content_type (NULL, idx[menu->current]->content, NULL); /* this may have been a change to text/something */ mutt_update_encoding (idx[menu->current]->content); menu->redraw = REDRAW_CURRENT; } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_ENCODING: CHECK_COUNT; strfcpy (buf, ENCODING (idx[menu->current]->content->encoding), sizeof (buf)); if (mutt_get_field ("Content-Transfer-Encoding: ", buf, sizeof (buf), 0) == 0 && buf[0]) { if ((i = mutt_check_encoding (buf)) != ENCOTHER && i != ENCUUENCODED) { idx[menu->current]->content->encoding = i; menu->redraw = REDRAW_CURRENT | REDRAW_STATUS; mutt_clear_error(); } else mutt_error _("Invalid encoding."); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_SEND_MESSAGE: /* Note: We don't invoke send2-hook here, since we want to leave * users an opportunity to change settings from the ":" prompt. */ if(check_attachments(idx, idxlen) != 0) { menu->redraw = REDRAW_FULL; break; } #ifdef MIXMASTER if (msg->chain && mix_check_message (msg) != 0) break; #endif if (!fccSet && *fcc) { if ((i = query_quadoption (OPT_COPY, _("Save a copy of this message?"))) == -1) break; else if (i == M_NO) *fcc = 0; } loop = 0; r = 0; break; case OP_COMPOSE_EDIT_FILE: CHECK_COUNT; mutt_edit_file (NONULL(Editor), idx[menu->current]->content->filename); mutt_update_encoding (idx[menu->current]->content); menu->redraw = REDRAW_CURRENT | REDRAW_STATUS; mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_TOGGLE_UNLINK: CHECK_COUNT; idx[menu->current]->content->unlink = !idx[menu->current]->content->unlink; #if 0 /* OPTRESOLVE is otherwise ignored on this menu. * Where's the bug? */ if (option (OPTRESOLVE) && menu->current + 1 < menu->max) menu->current++; # endif menu->redraw = REDRAW_INDEX; /* No send2hook since this doesn't change the message. */ break; case OP_COMPOSE_GET_ATTACHMENT: CHECK_COUNT; if(menu->tagprefix) { BODY *top; for(top = msg->content; top; top = top->next) { if(top->tagged) mutt_get_tmp_attachment(top); } menu->redraw = REDRAW_FULL; } else if (mutt_get_tmp_attachment(idx[menu->current]->content) == 0) menu->redraw = REDRAW_CURRENT; /* No send2hook since this doesn't change the message. */ break; case OP_COMPOSE_RENAME_FILE: CHECK_COUNT; strfcpy (fname, idx[menu->current]->content->filename, sizeof (fname)); mutt_pretty_mailbox (fname, sizeof (fname)); if (mutt_get_field (_("Rename to: "), fname, sizeof (fname), M_FILE) == 0 && fname[0]) { if (stat(idx[menu->current]->content->filename, &st) == -1) { /* L10N: "stat" is a system call. Do "man 2 stat" for more information. */ mutt_error (_("Can't stat %s: %s"), fname, strerror (errno)); break; } mutt_expand_path (fname, sizeof (fname)); if(mutt_rename_file (idx[menu->current]->content->filename, fname)) break; mutt_str_replace (&idx[menu->current]->content->filename, fname); menu->redraw = REDRAW_CURRENT; if(idx[menu->current]->content->stamp >= st.st_mtime) mutt_stamp_attachment(idx[menu->current]->content); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_NEW_MIME: { char type[STRING]; char *p; int itype; FILE *fp; CLEARLINE (LINES-1); fname[0] = 0; if (mutt_get_field (_("New file: "), fname, sizeof (fname), M_FILE) != 0 || !fname[0]) continue; mutt_expand_path (fname, sizeof (fname)); /* Call to lookup_mime_type () ? maybe later */ type[0] = 0; if (mutt_get_field ("Content-Type: ", type, sizeof (type), 0) != 0 || !type[0]) continue; if (!(p = strchr (type, '/'))) { mutt_error _("Content-Type is of the form base/sub"); continue; } *p++ = 0; if ((itype = mutt_check_mime_type (type)) == TYPEOTHER) { mutt_error (_("Unknown Content-Type %s"), type); continue; } if (idxlen == idxmax) { safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5)); menu->data = idx; } idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); /* Touch the file */ if (!(fp = safe_fopen (fname, "w"))) { mutt_error (_("Can't create file %s"), fname); FREE (&idx[idxlen]); continue; } safe_fclose (&fp); if ((idx[idxlen]->content = mutt_make_file_attach (fname)) == NULL) { mutt_error _("What we have here is a failure to make an attachment"); continue; } update_idx (menu, idx, idxlen++); idx[menu->current]->content->type = itype; mutt_str_replace (&idx[menu->current]->content->subtype, p); idx[menu->current]->content->unlink = 1; menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; if (mutt_compose_attachment (idx[menu->current]->content)) { mutt_update_encoding (idx[menu->current]->content); menu->redraw = REDRAW_FULL; } } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_MIME: CHECK_COUNT; if (mutt_edit_attachment (idx[menu->current]->content)) { mutt_update_encoding (idx[menu->current]->content); menu->redraw = REDRAW_FULL; } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_VIEW_ATTACH: case OP_DISPLAY_HEADERS: CHECK_COUNT; mutt_attach_display_loop (menu, op, NULL, NULL, NULL, &idx, &idxlen, NULL, 0); menu->redraw = REDRAW_FULL; /* no send2hook, since this doesn't modify the message */ break; case OP_SAVE: CHECK_COUNT; mutt_save_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content, NULL, menu); MAYBE_REDRAW (menu->redraw); /* no send2hook, since this doesn't modify the message */ break; case OP_PRINT: CHECK_COUNT; mutt_print_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content); /* no send2hook, since this doesn't modify the message */ break; case OP_PIPE: case OP_FILTER: CHECK_COUNT; mutt_pipe_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content, op == OP_FILTER); if (op == OP_FILTER) /* cte might have changed */ menu->redraw = menu->tagprefix ? REDRAW_FULL : REDRAW_CURRENT; menu->redraw |= REDRAW_STATUS; mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_EXIT: if ((i = query_quadoption (OPT_POSTPONE, _("Postpone this message?"))) == M_NO) { for (i = 0; i < idxlen; i++) if (idx[i]->unowned) idx[i]->content->unlink = 0; if (!(flags & M_COMPOSE_NOFREEHEADER)) { while (idxlen-- > 0) { /* avoid freeing other attachments */ idx[idxlen]->content->next = NULL; idx[idxlen]->content->parts = NULL; mutt_free_body (&idx[idxlen]->content); FREE (&idx[idxlen]->tree); FREE (&idx[idxlen]); } FREE (&idx); idxlen = 0; idxmax = 0; } r = -1; loop = 0; break; } else if (i == -1) break; /* abort */ /* fall through to postpone! */ case OP_COMPOSE_POSTPONE_MESSAGE: if(check_attachments(idx, idxlen) != 0) { menu->redraw = REDRAW_FULL; break; } loop = 0; r = 1; break; case OP_COMPOSE_ISPELL: endwin (); snprintf (buf, sizeof (buf), "%s -x %s", NONULL(Ispell), msg->content->filename); if (mutt_system (buf) == -1) mutt_error (_("Error running \"%s\"!"), buf); else { mutt_update_encoding (msg->content); menu->redraw |= REDRAW_STATUS; } break; case OP_COMPOSE_WRITE_MESSAGE: fname[0] = '\0'; if (Context) { strfcpy (fname, NONULL (Context->path), sizeof (fname)); mutt_pretty_mailbox (fname, sizeof (fname)); } if (idxlen) msg->content = idx[0]->content; if (mutt_enter_fname (_("Write message to mailbox"), fname, sizeof (fname), &menu->redraw, 1) != -1 && fname[0]) { mutt_message (_("Writing message to %s ..."), fname); mutt_expand_path (fname, sizeof (fname)); if (msg->content->next) msg->content = mutt_make_multipart (msg->content); if (mutt_write_fcc (fname, msg, NULL, 0, NULL) < 0) msg->content = mutt_remove_multipart (msg->content); else mutt_message _("Message written."); } break; case OP_COMPOSE_PGP_MENU: if (!(WithCrypto & APPLICATION_PGP)) break; if ((WithCrypto & APPLICATION_SMIME) && (msg->security & APPLICATION_SMIME)) { if (msg->security & (ENCRYPT | SIGN)) { if (mutt_yesorno (_("S/MIME already selected. Clear & continue ? "), M_YES) != M_YES) { mutt_clear_error (); break; } msg->security &= ~(ENCRYPT | SIGN); } msg->security &= ~APPLICATION_SMIME; msg->security |= APPLICATION_PGP; crypt_opportunistic_encrypt (msg); redraw_crypt_lines (msg); } msg->security = crypt_pgp_send_menu (msg, &menu->redraw); redraw_crypt_lines (msg); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_FORGET_PASSPHRASE: crypt_forget_passphrase (); break; case OP_COMPOSE_SMIME_MENU: if (!(WithCrypto & APPLICATION_SMIME)) break; if ((WithCrypto & APPLICATION_PGP) && (msg->security & APPLICATION_PGP)) { if (msg->security & (ENCRYPT | SIGN)) { if (mutt_yesorno (_("PGP already selected. Clear & continue ? "), M_YES) != M_YES) { mutt_clear_error (); break; } msg->security &= ~(ENCRYPT | SIGN); } msg->security &= ~APPLICATION_PGP; msg->security |= APPLICATION_SMIME; crypt_opportunistic_encrypt (msg); redraw_crypt_lines (msg); } msg->security = crypt_smime_send_menu(msg, &menu->redraw); redraw_crypt_lines (msg); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; #ifdef MIXMASTER case OP_COMPOSE_MIX: mix_make_chain (&msg->chain, &menu->redraw); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; #endif } /* Draw formatted compose status line */ if (menu->redraw & REDRAW_STATUS) { compose_status_line (buf, sizeof (buf), 0, menu, NONULL(ComposeFormat)); move(option (OPTSTATUSONTOP) ? 0 : LINES-2, 0); SETCOLOR (MT_COLOR_STATUS); mutt_paddstr (COLS, buf); NORMAL_COLOR; menu->redraw &= ~REDRAW_STATUS; } } mutt_menuDestroy (&menu); if (idxlen) { msg->content = idx[0]->content; for (i = 0; i < idxlen; i++) { idx[i]->content->aptr = NULL; FREE (&idx[i]->tree); FREE (&idx[i]); } } else msg->content = NULL; FREE (&idx); return (r); }
static int generate_body (FILE *tempfp, /* stream for outgoing message */ HEADER *msg, /* header for outgoing message */ int flags, /* compose mode */ CONTEXT *ctx, /* current mailbox */ HEADER *cur) /* current message */ { int i; HEADER *h; BODY *tmp; if (flags & SENDREPLY) { if ((i = query_quadoption (OPT_INCLUDE, _("Include message in reply?"))) == -1) return (-1); if (i == M_YES) { mutt_message _("Including quoted message..."); if (!cur) { for (i = 0; i < ctx->vcount; i++) { h = ctx->hdrs[ctx->v2r[i]]; if (h->tagged) { if (include_reply (ctx, h, tempfp) == -1) { mutt_error _("Could not include all requested messages!"); return (-1); } fputc ('\n', tempfp); } } } else include_reply (ctx, cur, tempfp); } } else if (flags & SENDFORWARD) { if ((i = query_quadoption (OPT_MIMEFWD, _("Forward as attachment?"))) == M_YES) { BODY *last = msg->content; mutt_message _("Preparing forwarded message..."); while (last && last->next) last = last->next; if (cur) { tmp = mutt_make_message_attach (ctx, cur, 0); if (last) last->next = tmp; else msg->content = tmp; } else { for (i = 0; i < ctx->vcount; i++) { if (ctx->hdrs[ctx->v2r[i]]->tagged) { tmp = mutt_make_message_attach (ctx, ctx->hdrs[ctx->v2r[i]], 0); if (last) { last->next = tmp; last = tmp; } else last = msg->content = tmp; } } } } else if (i != -1) { if (cur) include_forward (ctx, cur, tempfp); else for (i=0; i < ctx->vcount; i++) if (ctx->hdrs[ctx->v2r[i]]->tagged) include_forward (ctx, ctx->hdrs[ctx->v2r[i]], tempfp); } else if (i == -1) return -1; } /* if (WithCrypto && (flags & SENDKEY)) */ else if ((WithCrypto & APPLICATION_PGP) && (flags & SENDKEY)) { BODY *tmp; if ((WithCrypto & APPLICATION_PGP) && (tmp = crypt_pgp_make_key_attachment (NULL)) == NULL) return -1; tmp->next = msg->content; msg->content = tmp; } mutt_clear_error (); return (0); }
/** * mutt_progress_update - Update the state of the progress bar * @param progress Progress bar * @param pos Position, or count * @param percent Percentage complete * * If percent is -1, then the percentage will be calculated using pos and the * size in progress. * * If percent is positive, it is displayed as percentage, otherwise * percentage is calculated from progress->size and pos if progress * was initialized with positive size, otherwise no percentage is shown */ void mutt_progress_update(struct Progress *progress, long pos, int percent) { char posstr[128]; bool update = false; struct timeval tv = { 0, 0 }; unsigned int now = 0; if (OptNoCurses) return; if (progress->inc == 0) goto out; /* refresh if size > inc */ if ((progress->flags & MUTT_PROGRESS_SIZE) && (pos >= (progress->pos + (progress->inc << 10)))) { update = true; } else if (pos >= (progress->pos + progress->inc)) update = true; /* skip refresh if not enough time has passed */ if (update && progress->timestamp && (gettimeofday(&tv, NULL) == 0)) { now = ((unsigned int) tv.tv_sec * 1000) + (unsigned int) (tv.tv_usec / 1000); if (now && ((now - progress->timestamp) < C_TimeInc)) update = false; } /* always show the first update */ if (pos == 0) update = true; if (update) { if (progress->flags & MUTT_PROGRESS_SIZE) { pos = pos / (progress->inc << 10) * (progress->inc << 10); mutt_str_pretty_size(posstr, sizeof(posstr), pos); } else snprintf(posstr, sizeof(posstr), "%ld", pos); mutt_debug(4, "updating progress: %s\n", posstr); progress->pos = pos; if (now) progress->timestamp = now; if (progress->size > 0) { message_bar( (percent > 0) ? percent : (int) (100.0 * (double) progress->pos / progress->size), "%s %s/%s (%d%%)", progress->msg, posstr, progress->sizestr, (percent > 0) ? percent : (int) (100.0 * (double) progress->pos / progress->size)); } else { if (percent > 0) message_bar(percent, "%s %s (%d%%)", progress->msg, posstr, percent); else mutt_message("%s %s", progress->msg, posstr); } } out: if (pos >= progress->size) mutt_clear_error(); }