changeset: 6538:f542783e257d user: Kevin McCarthy <ke...@8t8.us> date: Tue Dec 08 09:11:30 2015 -0800 link: http://dev.mutt.org/hg/mutt/rev/f542783e257d
Fix hash table key "use after free" in mh_check_mailbox(). (closes #3797) The fnames hash uses the maildir->header->path as the key. As matches are found, the headers are freed. This inadvertantly also freed the key to the hashtable entry; the next hash_find() going to the same bucket might end up comparing keys with a freed string. This patch stores the path in the struct maildir canon_fname field (just as maildir_check_mailbox() does) and uses that as the hash key instead. This field isn't used outside of maildir_check_mailbox(), and is automatically freed for us in the maildir_move_to_context() call at the bottom of both functions. Note there are other ways to fix this problem: - Add a new mode to the hash table, causing it to strdup the keys and free them itself. - Delete the entries in the fnames hash, rather leaving them there. The first seems the cleanest, but would end up touching much more code. The second is also clean, but might have a negative performance impact. Additionally, peeking back in history to changeset 1d45a50b6f9b, it looks like the canon_fname used to be used by mh too, so perhaps removing the strdup may have been a mistake during refactoring at some point. changeset: 6539:02bc14ed1569 user: Kevin McCarthy <ke...@8t8.us> date: Tue Dec 08 09:12:09 2015 -0800 link: http://dev.mutt.org/hg/mutt/rev/02bc14ed1569 merge stable diffs (truncated from 14980 to 950 lines): diff -r a6919571eb59 -r 02bc14ed1569 Makefile.am --- a/Makefile.am Sun Oct 18 19:45:51 2015 +0800 +++ b/Makefile.am Tue Dec 08 09:12:09 2015 -0800 @@ -33,7 +33,7 @@ rfc822.c rfc1524.c rfc2047.c rfc2231.c rfc3676.c \ score.c send.c sendlib.c signal.c sort.c \ status.c system.c thread.c charset.c history.c lib.c \ - muttlib.c editmsg.c mbyte.c \ + muttlib.c editmsg.c mbyte.c mutt_idna.c \ url.c ascii.c crypt-mod.c crypt-mod.h safe_asprintf.c nodist_mutt_SOURCES = $(BUILT_SOURCES) @@ -53,7 +53,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c crypt-gpgme.c crypt-mod-pgp-classic.c \ crypt-mod-pgp-gpgme.c crypt-mod-smime-classic.c \ crypt-mod-smime-gpgme.c dotlock.c gnupgparse.c hcache.c md5.c \ - mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \ + mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \ mutt_tunnel.c pgp.c pgpinvoke.c pgpkey.c pgplib.c pgpmicalg.c \ pgppacket.c pop.c pop_auth.c pop_lib.c remailer.c resize.c sha1.c \ smime.c smtp.c utf8.c wcwidth.c \ diff -r a6919571eb59 -r 02bc14ed1569 UPDATING --- a/UPDATING Sun Oct 18 19:45:51 2015 +0800 +++ b/UPDATING Tue Dec 08 09:12:09 2015 -0800 @@ -4,6 +4,10 @@ The keys used are: !: modified feature, -: deleted feature, +: new feature +default (unreleased): + + New expandos %r and %R for comma separated list of To: and Cc: + recipients respectively + 1.5.24 (2015-08-31): + terminal status-line (TS) support, a.k.a. xterm title. see the diff -r a6919571eb59 -r 02bc14ed1569 alias.c --- a/alias.c Sun Oct 18 19:45:51 2015 +0800 +++ b/alias.c Tue Dec 08 09:12:09 2015 -0800 @@ -256,7 +256,7 @@ mutt_check_alias_name (tmp, buf, sizeof (buf)); retry_name: - /* add a new alias */ + /* L10N: prompt to add a new alias */ if (mutt_get_field (_("Alias as: "), buf, sizeof (buf), 0) != 0 || !buf[0]) return; @@ -290,7 +290,7 @@ else buf[0] = 0; - mutt_addrlist_to_idna (adr, NULL); + mutt_addrlist_to_intl (adr, NULL); do { @@ -302,7 +302,7 @@ if((new->addr = rfc822_parse_adrlist (new->addr, buf)) == NULL) BEEP (); - if (mutt_addrlist_to_idna (new->addr, &err)) + if (mutt_addrlist_to_intl (new->addr, &err)) { mutt_error (_("Error: '%s' is a bad IDN."), err); mutt_sleep (2); diff -r a6919571eb59 -r 02bc14ed1569 browser.c --- a/browser.c Sun Oct 18 19:45:51 2015 +0800 +++ b/browser.c Tue Dec 08 09:12:09 2015 -0800 @@ -896,7 +896,7 @@ else set_option (OPTIMAPLSUB); - mutt_ungetch (0, OP_CHECK_NEW); + mutt_unget_event (0, OP_CHECK_NEW); break; case OP_CREATE_MAILBOX: diff -r a6919571eb59 -r 02bc14ed1569 commands.c --- a/commands.c Sun Oct 18 19:45:51 2015 +0800 +++ b/commands.c Tue Dec 08 09:12:09 2015 -0800 @@ -228,7 +228,7 @@ mutt_set_flag (Context, cur, M_READ, 1); if (r != -1 && option (OPTPROMPTAFTER)) { - mutt_ungetch (mutt_any_key_to_continue _("Command: "), 0); + mutt_unget_event (mutt_any_key_to_continue _("Command: "), 0); rc = km_dokey (MENU_PAGER); } else @@ -294,7 +294,7 @@ adr = mutt_expand_aliases (adr); - if (mutt_addrlist_to_idna (adr, &err) < 0) + if (mutt_addrlist_to_intl (adr, &err) < 0) { mutt_error (_("Bad IDN: '%s'"), err); FREE (&err); diff -r a6919571eb59 -r 02bc14ed1569 compose.c --- a/compose.c Sun Oct 18 19:45:51 2015 +0800 +++ b/compose.c Tue Dec 08 09:12:09 2015 -0800 @@ -292,7 +292,7 @@ return (REDRAW_FULL); } - if (mutt_addrlist_to_idna (*addr, &err) != 0) + if (mutt_addrlist_to_intl (*addr, &err) != 0) { mutt_error (_("Warning: '%s' is a bad IDN."), err); mutt_refresh(); @@ -606,7 +606,7 @@ mutt_env_to_local (msg->env); mutt_edit_headers (NONULL (Editor), msg->content->filename, msg, fcc, fcclen); - if (mutt_env_to_idna (msg->env, &tag, &err)) + if (mutt_env_to_intl (msg->env, &tag, &err)) { mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err); FREE (&err); @@ -754,7 +754,7 @@ ctx = mx_open_mailbox (fname, M_READONLY, NULL); if (ctx == NULL) { - mutt_perror (fname); + mutt_error (_("Unable to open mailbox %s"), fname); break; } @@ -1023,6 +1023,8 @@ { 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; } @@ -1234,14 +1236,20 @@ if ((WithCrypto & APPLICATION_SMIME) && (msg->security & APPLICATION_SMIME)) { - if (mutt_yesorno (_("S/MIME already selected. Clear & continue ? "), - M_YES) != M_YES) - { - mutt_clear_error (); - break; - } + 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); @@ -1261,14 +1269,20 @@ if ((WithCrypto & APPLICATION_PGP) && (msg->security & APPLICATION_PGP)) { - if (mutt_yesorno (_("PGP already selected. Clear & continue ? "), - M_YES) != M_YES) - { - mutt_clear_error (); - break; - } + 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); diff -r a6919571eb59 -r 02bc14ed1569 configure.ac --- a/configure.ac Sun Oct 18 19:45:51 2015 +0800 +++ b/configure.ac Tue Dec 08 09:12:09 2015 -0800 @@ -1196,7 +1196,6 @@ AC_SEARCH_LIBS([stringprep_check_version], [idn], [ AC_DEFINE([HAVE_LIBIDN], 1, [Define to 1 if you have the GNU idn library]) - MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS mutt_idna.o" MUTTLIBS="$MUTTLIBS $LIBS" LIBS="$LIBS $LIBICONV" diff -r a6919571eb59 -r 02bc14ed1569 contrib/smime.rc --- a/contrib/smime.rc Sun Oct 18 19:45:51 2015 +0800 +++ b/contrib/smime.rc Tue Dec 08 09:12:09 2015 -0800 @@ -65,8 +65,12 @@ # Encrypt a message. Input file is a MIME entity. set smime_encrypt_command="openssl smime -encrypt -%a -outform DER -in %f %c" +# Algorithm for the signature message digest. +# Valid choices are md5, sha1, sha224, sha256, sha384, sha512. +set smime_sign_digest_alg="sha256" + # Sign. -set smime_sign_command="openssl smime -sign -signer %c -inkey %k -passin stdin -in %f -certfile %i -outform DER" +set smime_sign_command="openssl smime -sign -md %d -signer %c -inkey %k -passin stdin -in %f -certfile %i -outform DER" @@ -89,7 +93,7 @@ # Sign. If you wish to NOT include the certificate your CA used in signing # your public key, use this command instead. -# set smime_sign_command="openssl smime -sign -signer %c -inkey %k -passin stdin -in %f -outform DER" +# set smime_sign_command="openssl smime -sign -md %d -signer %c -inkey %k -passin stdin -in %f -outform DER" # # In order to verify the signature only and skip checking the certificate chain: # diff -r a6919571eb59 -r 02bc14ed1569 crypt-gpgme.c --- a/crypt-gpgme.c Sun Oct 18 19:45:51 2015 +0800 +++ b/crypt-gpgme.c Tue Dec 08 09:12:09 2015 -0800 @@ -1362,6 +1362,8 @@ continue; if (aka) { + /* TODO: need to account for msg wide characters + * and "aka" translation length */ msglen = mutt_strlen (msg) - 4; for (i = 0; i < msglen; i++) state_attach_puts(" ", s); @@ -1381,6 +1383,8 @@ } msglen = mutt_strlen (msg) - 8; + /* TODO: need to account for msg wide characters + * and "created" translation length */ for (i = 0; i < msglen; i++) state_attach_puts(" ", s); state_attach_puts (_("created: "), s); @@ -1404,6 +1408,7 @@ gpgme_verify_result_t result; gpgme_signature_t sig; gpgme_error_t err = GPG_ERR_NO_ERROR; + char buf[LONG_STRING]; result = gpgme_op_verify_result (ctx); if (result) @@ -1453,11 +1458,10 @@ ; /* No state information so no way to print anything. */ else if (err) { - state_attach_puts (_("Error getting key information for KeyID "), s); - state_attach_puts ( fpr, s ); - state_attach_puts (_(": "), s); - state_attach_puts ( gpgme_strerror (err), s ); - state_attach_puts ("\n", s); + snprintf (buf, sizeof (buf), + _("Error getting key information for KeyID %s: %s\n"), + fpr, gpgme_strerror (err)); + state_attach_puts (buf, s); anybad = 1; } else if ((sum & GPGME_SIGSUM_GREEN)) @@ -1489,6 +1493,9 @@ /* 0 indicates no expiration */ if (sig->exp_timestamp) { + /* L10N: + This is trying to match the width of the + "Problem signature from:" translation just above. */ state_attach_puts (_(" expires: "), s); print_time (sig->exp_timestamp, s); state_attach_puts ("\n", s); @@ -3366,6 +3373,10 @@ continue; s = uid->uid; + /* L10N: + Fill dots to make the DOTFILL entries the same length. + In English, msgid "Fingerprint: " is the longest entry for this menu. + Your language may vary. */ fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp); if (uid->invalid) { @@ -3389,6 +3400,7 @@ #else strftime (shortbuf, sizeof shortbuf, "%c", tm); #endif + /* L10N: DOTFILL */ fprintf (fp, _("Valid From : %s\n"), shortbuf); } @@ -3402,6 +3414,7 @@ #else strftime (shortbuf, sizeof shortbuf, "%c", tm); #endif + /* L10N: DOTFILL */ fprintf (fp, _("Valid To ..: %s\n"), shortbuf); } @@ -3415,8 +3428,10 @@ if (key->subkeys) aval = key->subkeys->length; + /* L10N: DOTFILL */ fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s); + /* L10N: DOTFILL */ fprintf (fp, _("Key Usage .: ")); delim = ""; @@ -3440,6 +3455,7 @@ if (key->subkeys) { s = key->subkeys->fpr; + /* L10N: DOTFILL */ fputs (_("Fingerprint: "), fp); if (is_pgp && strlen (s) == 40) { @@ -3472,6 +3488,7 @@ { s = key->issuer_serial; if (s) + /* L10N: DOTFILL */ fprintf (fp, _("Serial-No .: 0x%s\n"), s); } @@ -3480,6 +3497,7 @@ s = key->issuer_name; if (s) { + /* L10N: DOTFILL */ fprintf (fp, _("Issued By .: ")); parse_and_print_user_id (fp, s); putc ('\n', fp); @@ -3499,6 +3517,7 @@ putc ('\n', fp); if ( strlen (s) == 16) s += 8; /* display only the short keyID */ + /* L10N: DOTFILL */ fprintf (fp, _("Subkey ....: 0x%s"), s); if (subkey->revoked) { @@ -3532,6 +3551,7 @@ #else strftime (shortbuf, sizeof shortbuf, "%c", tm); #endif + /* L10N: DOTFILL */ fprintf (fp, _("Valid From : %s\n"), shortbuf); } @@ -3545,6 +3565,7 @@ #else strftime (shortbuf, sizeof shortbuf, "%c", tm); #endif + /* L10N: DOTFILL */ fprintf (fp, _("Valid To ..: %s\n"), shortbuf); } @@ -3558,8 +3579,10 @@ else aval = 0; + /* L10N: DOTFILL */ fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s); + /* L10N: DOTFILL */ fprintf (fp, _("Key Usage .: ")); delim = ""; @@ -4010,8 +4033,14 @@ ts = _("keys matching"); if (p) + /* L10N: + %1$s is one of the previous four entries. + %2$s is an address. + e.g. "S/MIME keys matching <m...@mutt.org>." */ snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox); else + /* L10N: + e.g. 'S/MIME keys matching "Michael Elkins".' */ snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s); menu->title = buf; } @@ -4590,6 +4619,10 @@ att->use_disp = 0; att->type = TYPEAPPLICATION; att->subtype = safe_strdup ("pgp-keys"); + /* L10N: + MIME description for exported (attached) keys. + You can translate this entry to a non-ASCII string (it will be encoded), + but it may be safer to keep it untranslated. */ snprintf (buff, sizeof (buff), _("PGP Key 0x%s."), crypt_keyid (key)); att->description = safe_strdup (buff); mutt_update_encoding (att); @@ -4678,6 +4711,10 @@ if (is_smime) { prompt = _("S/MIME (s)ign, sign (a)s, (p)gp, (c)lear, or (o)ppenc mode off? "); + /* L10N: The 'f' is from "forget it", an old undocumented synonym of + 'clear'. Please use a corresponding letter in your language. + Alternatively, you may duplicate the letter 'c' is translated to. + This comment also applies to the five following letter sequences. */ letters = _("sapfco"); choices = "SapFCo"; } diff -r a6919571eb59 -r 02bc14ed1569 crypt.c --- a/crypt.c Sun Oct 18 19:45:51 2015 +0800 +++ b/crypt.c Tue Dec 08 09:12:09 2015 -0800 @@ -142,21 +142,34 @@ if ((WithCrypto & APPLICATION_PGP) && ((msg->security & PGPINLINE) == PGPINLINE)) { - /* they really want to send it inline... go for it */ - if (!isendwin ()) mutt_endwin _("Invoking PGP..."); - pbody = crypt_pgp_traditional_encryptsign (msg->content, flags, keylist); - if (pbody) + if ((msg->content->type != TYPETEXT) || + ascii_strcasecmp (msg->content->subtype, "plain")) { - msg->content = pbody; - return 0; + if ((i = query_quadoption (OPT_PGPMIMEAUTO, + _("Inline PGP can't be used with attachments. Revert to PGP/MIME?"))) != M_YES) + { + mutt_error _("Mail not sent: inline PGP can't be used with attachments."); + return -1; + } } + else + { + /* they really want to send it inline... go for it */ + if (!isendwin ()) mutt_endwin _("Invoking PGP..."); + pbody = crypt_pgp_traditional_encryptsign (msg->content, flags, keylist); + if (pbody) + { + msg->content = pbody; + return 0; + } - /* otherwise inline won't work...ask for revert */ - if ((i = query_quadoption (OPT_PGPMIMEAUTO, _("Message can't be sent inline. Revert to using PGP/MIME?"))) != M_YES) + /* otherwise inline won't work...ask for revert */ + if ((i = query_quadoption (OPT_PGPMIMEAUTO, _("Message can't be sent inline. Revert to using PGP/MIME?"))) != M_YES) { - mutt_error _("Mail not sent."); - return -1; + mutt_error _("Mail not sent."); + return -1; } + } /* go ahead with PGP/MIME */ } @@ -879,9 +892,8 @@ int mutt_signed_handler (BODY *a, STATE *s) { char tempfile[_POSIX_PATH_MAX]; - char *protocol; - int protocol_major = TYPEOTHER; - char *protocol_minor = NULL; + int signed_type; + int inconsistent = 0; BODY *b = a; BODY **signatures = NULL; @@ -893,29 +905,44 @@ if (!WithCrypto) return -1; - protocol = mutt_get_parameter ("protocol", a->parameter); a = a->parts; - - /* extract the protocol information */ - - if (protocol) + signed_type = mutt_is_multipart_signed (b); + if (!signed_type) { - char major[STRING]; - char *t; - - if ((protocol_minor = strchr (protocol, '/'))) protocol_minor++; - - strfcpy (major, protocol, sizeof(major)); - if((t = strchr(major, '/'))) - *t = '\0'; - - protocol_major = mutt_check_mime_type (major); + /* A null protocol value is already checked for in mutt_body_handler() */ + state_printf (s, _("[-- Error: " + "Unknown multipart/signed protocol %s! --]\n\n"), + mutt_get_parameter ("protocol", b->parameter)); + return mutt_body_handler (a, s); } - /* consistency check */ - - if (!(a && a->next && a->next->type == protocol_major && - !mutt_strcasecmp (a->next->subtype, protocol_minor))) + if (!(a && a->next)) + inconsistent = 1; + else + { + switch (signed_type) + { + case SIGN: + if (a->next->type != TYPEMULTIPART || + ascii_strcasecmp (a->next->subtype, "mixed")) + inconsistent = 1; + break; + case PGPSIGN: + if (a->next->type != TYPEAPPLICATION || + ascii_strcasecmp (a->next->subtype, "pgp-signature")) + inconsistent = 1; + break; + case SMIMESIGN: + if (a->next->type != TYPEAPPLICATION || + (ascii_strcasecmp (a->next->subtype, "x-pkcs7-signature") && + ascii_strcasecmp (a->next->subtype, "pkcs7-signature"))) + inconsistent = 1; + break; + default: + inconsistent = 1; + } + } + if (inconsistent) { state_attach_puts (_("[-- Error: " "Inconsistent multipart/signed structure! --]\n\n"), @@ -923,27 +950,6 @@ return mutt_body_handler (a, s); } - - if ((WithCrypto & APPLICATION_PGP) - && protocol_major == TYPEAPPLICATION - && !ascii_strcasecmp (protocol_minor, "pgp-signature")) - ; - else if ((WithCrypto & APPLICATION_SMIME) - && protocol_major == TYPEAPPLICATION - && !(ascii_strcasecmp (protocol_minor, "x-pkcs7-signature") - && ascii_strcasecmp (protocol_minor, "pkcs7-signature"))) - ; - else if (protocol_major == TYPEMULTIPART - && !ascii_strcasecmp (protocol_minor, "mixed")) - ; - else - { - state_printf (s, _("[-- Error: " - "Unknown multipart/signed protocol %s! --]\n\n"), - protocol); - return mutt_body_handler (a, s); - } - if (s->flags & M_DISPLAY) { diff -r a6919571eb59 -r 02bc14ed1569 curs_lib.c --- a/curs_lib.c Sun Oct 18 19:45:51 2015 +0800 +++ b/curs_lib.c Tue Dec 08 09:12:09 2015 -0800 @@ -48,9 +48,20 @@ * is impossible to unget function keys in SLang, so roll our own input * buffering routines. */ -size_t UngetCount = 0; -static size_t UngetBufLen = 0; -static event_t *KeyEvent; + +/* These are used for macros and exec/push commands. + * They can be temporarily ignored by setting OPTIGNOREMACROEVENTS + */ +static size_t MacroBufferCount = 0; +static size_t MacroBufferLen = 0; +static event_t *MacroEvents; + +/* These are used in all other "normal" situations, and are not + * ignored when setting OPTIGNOREMACROEVENTS + */ +static size_t UngetCount = 0; +static size_t UngetLen = 0; +static event_t *UngetKeyEvents; void mutt_refresh (void) { @@ -83,8 +94,11 @@ event_t err = {-1, OP_NULL }, ret; event_t timeout = {-2, OP_NULL}; - if (!option(OPTUNBUFFEREDINPUT) && UngetCount) - return (KeyEvent[--UngetCount]); + if (UngetCount) + return (UngetKeyEvents[--UngetCount]); + + if (!option(OPTIGNOREMACROEVENTS) && MacroBufferCount) + return (MacroEvents[--MacroBufferCount]); SigInt = 0; @@ -118,7 +132,7 @@ { /* send ALT-x as ESC-x */ ch &= ~0x80; - mutt_ungetch (ch, 0); + mutt_unget_event (ch, 0); ret.ch = '\033'; ret.op = 0; return ret; @@ -157,9 +171,9 @@ { int rc; - set_option (OPTUNBUFFEREDINPUT); + set_option (OPTIGNOREMACROEVENTS); rc = mutt_get_field (msg, buf, buflen, flags); - unset_option (OPTUNBUFFEREDINPUT); + unset_option (OPTIGNOREMACROEVENTS); return (rc); } @@ -595,7 +609,7 @@ char *pc = safe_malloc (mutt_strlen (prompt) + 3); sprintf (pc, "%s: ", prompt); /* __SPRINTF_CHECKED__ */ - mutt_ungetch (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0); + mutt_unget_event (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0); if (_mutt_get_field (pc, buf, blen, (buffy ? M_EFILE : M_FILE) | M_CLEAR, multiple, files, numfiles) != 0) buf[0] = 0; @@ -606,22 +620,60 @@ return 0; } -void mutt_ungetch (int ch, int op) +void mutt_unget_event (int ch, int op) { event_t tmp; tmp.ch = ch; tmp.op = op; - if (UngetCount >= UngetBufLen) - safe_realloc (&KeyEvent, (UngetBufLen += 128) * sizeof(event_t)); + if (UngetCount >= UngetLen) + safe_realloc (&UngetKeyEvents, (UngetLen += 16) * sizeof(event_t)); - KeyEvent[UngetCount++] = tmp; + UngetKeyEvents[UngetCount++] = tmp; +} + +void mutt_unget_string (char *s) +{ + char *p = s + mutt_strlen (s) - 1; + + while (p >= s) + { + mutt_unget_event ((unsigned char)*p--, 0); + } +} + +/* + * Adds the ch/op to the macro buffer. + * This should be used for macros, push, and exec commands only. + */ +void mutt_push_macro_event (int ch, int op) +{ + event_t tmp; + + tmp.ch = ch; + tmp.op = op; + + if (MacroBufferCount >= MacroBufferLen) + safe_realloc (&MacroEvents, (MacroBufferLen += 128) * sizeof(event_t)); + + MacroEvents[MacroBufferCount++] = tmp; +} + +void mutt_flush_macro_to_endcond (void) +{ + UngetCount = 0; + while (MacroBufferCount > 0) + { + if (MacroEvents[--MacroBufferCount].op == OP_END_COND) + return; + } } void mutt_flushinp (void) { UngetCount = 0; + MacroBufferCount = 0; flushinp (); } @@ -661,7 +713,8 @@ { mutt_refresh (); ch = mutt_getch (); - if (ch.ch < 0 || CI_is_return (ch.ch)) + /* (ch.ch == 0) is technically possible. Treat the same as < 0 (abort) */ + if (ch.ch <= 0 || CI_is_return (ch.ch)) { choice = -1; break; diff -r a6919571eb59 -r 02bc14ed1569 curs_main.c --- a/curs_main.c Sun Oct 18 19:45:51 2015 +0800 +++ b/curs_main.c Tue Dec 08 09:12:09 2015 -0800 @@ -93,7 +93,8 @@ #define CHECK_ACL(aclbit,action) \ if (!mutt_bit_isset(Context->rights,aclbit)) { \ mutt_flushinp(); \ - mutt_error (_("Cannot %s: Operation not permitted by ACL"), action); \ + /* L10N: %s is one of the CHECK_ACL entries below. */ \ + mutt_error (_("%s: Operation not permitted by ACL"), action); \ break; \ } @@ -108,8 +109,6 @@ #define OLDHDR Context->hdrs[Context->v2r[menu->oldcurrent]] #define UNREAD(h) mutt_thread_contains_unread (Context, h) -extern size_t UngetCount; - /* de facto standard escapes for tsl/fsl */ static char *tsl = "\033]0;"; static char *fsl = "\007"; @@ -728,12 +727,7 @@ if (!Context->tagged) { - event_t tmp; - while(UngetCount>0) - { - tmp=mutt_getch(); - if(tmp.op==OP_END_COND)break; - } + mutt_flush_macro_to_endcond (); mutt_message _("Nothing to do."); continue; } @@ -818,7 +812,7 @@ CHECK_MSGCOUNT; CHECK_VISIBLE; - if (isdigit (LastKey)) mutt_ungetch (LastKey, 0); + if (isdigit (LastKey)) mutt_unget_event (LastKey, 0); buf[0] = 0; if (mutt_get_field (_("Jump to message: "), buf, sizeof (buf), 0) != 0 || !buf[0]) @@ -874,7 +868,8 @@ CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; - CHECK_ACL(M_ACL_DELETE, _("delete message(s)")); + /* L10N: CHECK_ACL */ + CHECK_ACL(M_ACL_DELETE, _("Cannot delete message(s)")); CHECK_ATTACH; mutt_pattern_func (M_DELETE, _("Delete messages matching: ")); @@ -903,7 +898,7 @@ else { char buf[STRING]; - /* i18n: ask for a limit to apply */ + /* L10N: ask for a limit to apply */ snprintf (buf, sizeof(buf), _("Limit: %s"),Context->pattern); mutt_message ("%s", buf); } @@ -1047,7 +1042,8 @@ CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; - CHECK_ACL(M_ACL_DELETE, _("undelete message(s)")); + /* L10N: CHECK_ACL */ + CHECK_ACL(M_ACL_DELETE, _("Cannot undelete message(s)")); if (mutt_pattern_func (M_UNDELETE, _("Undelete messages matching: ")) == 0) menu->redraw = REDRAW_INDEX | REDRAW_STATUS; @@ -1354,7 +1350,8 @@ CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; - CHECK_ACL(M_ACL_DELETE, _("link threads")); + /* L10N: CHECK_ACL */ + CHECK_ACL(M_ACL_DELETE, _("Cannot link threads")); if ((Sort & SORT_MASK) != SORT_THREADS) mutt_error _("Threading is not enabled."); @@ -1605,8 +1602,20 @@ if (menu->current == -1) { menu->current = menu->oldcurrent; - mutt_error ("%s%s.", (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW) ? _("No new messages") : _("No unread messages"), - Context->pattern ? _(" in this limited view") : ""); + if (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW) + { + if (Context->pattern) + mutt_error (_("No new messages in this limited view.")); + else + mutt_error (_("No new messages.")); + } + else + { + if (Context->pattern) + mutt_error (_("No unread messages in this limited view.")); + else + mutt_error (_("No unread messages.")); + } } else if (menu->menu == MENU_PAGER) { @@ -1622,7 +1631,8 @@ CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; - CHECK_ACL(M_ACL_WRITE, _("flag message")); + /* L10N: CHECK_ACL */ + CHECK_ACL(M_ACL_WRITE, _("Cannot flag message")); if (tag) { @@ -1659,7 +1669,8 @@ CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; - CHECK_ACL(M_ACL_SEEN, _("toggle new")); + /* L10N: CHECK_ACL */ + CHECK_ACL(M_ACL_SEEN, _("Cannot toggle new")); if (tag) { @@ -1913,7 +1924,8 @@ CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; - CHECK_ACL(M_ACL_DELETE, _("delete message")); + /* L10N: CHECK_ACL */ + CHECK_ACL(M_ACL_DELETE, _("Cannot delete message")); if (tag) { @@ -1954,7 +1966,8 @@ CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; - CHECK_ACL(M_ACL_DELETE, _("delete message(s)")); + /* L10N: CHECK_ACL */ + CHECK_ACL(M_ACL_DELETE, _("Cannot delete message(s)")); rc = mutt_thread_set_flag (CURHDR, M_DELETE, 1, op == OP_DELETE_THREAD ? 0 : 1); @@ -1995,7 +2008,8 @@ CHECK_VISIBLE; CHECK_READONLY; CHECK_ATTACH; - CHECK_ACL(M_ACL_INSERT, _("edit message")); + /* L10N: CHECK_ACL */ + CHECK_ACL(M_ACL_INSERT, _("Cannot edit message")); if (option (OPTPGPAUTODEC) && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))) mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw); @@ -2126,7 +2140,8 @@ CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; - CHECK_ACL(M_ACL_SEEN, _("mark message(s) as read")); + /* L10N: CHECK_ACL */ + CHECK_ACL(M_ACL_SEEN, _("Cannot mark message(s) as read")); rc = mutt_thread_set_flag (CURHDR, M_READ, 1, op == OP_MAIN_READ_THREAD ? 0 : 1); @@ -2221,7 +2236,8 @@ CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; - CHECK_ACL(M_ACL_DELETE, _("undelete message")); + /* L10N: CHECK_ACL */ + CHECK_ACL(M_ACL_DELETE, _("Cannot undelete message")); if (tag) { @@ -2248,7 +2264,8 @@ CHECK_MSGCOUNT; CHECK_VISIBLE; CHECK_READONLY; - CHECK_ACL(M_ACL_DELETE, _("undelete message(s)")); + /* L10N: CHECK_ACL */ + CHECK_ACL(M_ACL_DELETE, _("Cannot undelete message(s)")); rc = mutt_thread_set_flag (CURHDR, M_DELETE, 0, op == OP_UNDELETE_THREAD ? 0 : 1); diff -r a6919571eb59 -r 02bc14ed1569 doc/devel-notes.txt --- a/doc/devel-notes.txt Sun Oct 18 19:45:51 2015 +0800 +++ b/doc/devel-notes.txt Tue Dec 08 09:12:09 2015 -0800 @@ -196,6 +196,10 @@ from the times when this was an iso-8859-1 source code tree, please feel free to fix them. +- prefix translator comments with L10N: + /* L10N: this is a translator comment */ + puts(_("String to translate)); + Documentation ------------- diff -r a6919571eb59 -r 02bc14ed1569 edit.c --- a/edit.c Sun Oct 18 19:45:51 2015 +0800 +++ b/edit.c Tue Dec 08 09:12:09 2015 -0800 @@ -247,7 +247,7 @@ rfc822_free_address (&e->to); e->to = mutt_parse_adrlist (e->to, tmp); e->to = mutt_expand_aliases (e->to); - mutt_addrlist_to_idna (e->to, NULL); /* XXX - IDNA error reporting? */ + mutt_addrlist_to_intl (e->to, NULL); /* XXX - IDNA error reporting? */ tmp[0] = 0; rfc822_write_address (tmp, sizeof (tmp), e->to, 1); mvaddstr (LINES - 1, 4, tmp); @@ -255,7 +255,7 @@ } else { - mutt_addrlist_to_idna (e->to, NULL); /* XXX - IDNA error reporting? */ + mutt_addrlist_to_intl (e->to, NULL); /* XXX - IDNA error reporting? */ addstr (tmp); } addch ('\n'); @@ -281,12 +281,12 @@ e->cc = mutt_parse_adrlist (e->cc, tmp); e->cc = mutt_expand_aliases (e->cc); tmp[0] = 0; - mutt_addrlist_to_idna (e->cc, NULL); + mutt_addrlist_to_intl (e->cc, NULL); rfc822_write_address (tmp, sizeof (tmp), e->cc, 1); mvaddstr (LINES - 1, 4, tmp); } else - mutt_addrlist_to_idna (e->cc, NULL); + mutt_addrlist_to_intl (e->cc, NULL); addch ('\n');