X-Git-Url: https://git.llucax.com/software/mutt-debian.git/blobdiff_plain/14c29200cb58d3c4a0830265f2433849781858d0..5f2a574d6c8989bc7f90612704e3e5f255b5cb58:/muttlib.c diff --git a/muttlib.c b/muttlib.c index e0820da..2160ceb 100644 --- a/muttlib.c +++ b/muttlib.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 1996-2000 Michael R. Elkins - * Copyright (C) 1999-2000 Thomas Roessler + * Copyright (C) 1996-2000,2007 Michael R. Elkins + * Copyright (C) 1999-2008 Thomas Roessler * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -177,13 +177,13 @@ void mutt_free_body (BODY **p) if (b->parameter) mutt_free_parameter (&b->parameter); - if (b->unlink && b->filename) + if (b->filename) { - dprint (1, (debugfile, "mutt_free_body: Unlinking %s.\n", b->filename)); - unlink (b->filename); + if (b->unlink) + unlink (b->filename); + dprint (1, (debugfile, "mutt_free_body: %sunlinking %s.\n", + b->unlink ? "" : "not ", b->filename)); } - else if (b->filename) - dprint (1, (debugfile, "mutt_free_body: Not unlinking %s.\n", b->filename)); FREE (&b->filename); FREE (&b->content); @@ -252,6 +252,57 @@ LIST *mutt_add_list_n (LIST *head, const void *data, size_t len) return head; } +LIST *mutt_find_list (LIST *l, const char *data) +{ + LIST *p = l; + + while (p) + { + if (data == p->data) + return p; + if (data && p->data && mutt_strcmp (p->data, data) == 0) + return p; + p = p->next; + } + return NULL; +} + +int mutt_remove_from_rx_list (RX_LIST **l, const char *str) +{ + RX_LIST *p, *last = NULL; + int rv = -1; + + if (mutt_strcmp ("*", str) == 0) + { + mutt_free_rx_list (l); /* ``unCMD *'' means delete all current entries */ + rv = 0; + } + else + { + p = *l; + last = NULL; + while (p) + { + if (ascii_strcasecmp (str, p->rx->pattern) == 0) + { + mutt_free_regexp (&p->rx); + if (last) + last->next = p->next; + else + (*l) = p->next; + FREE (&p); + rv = 0; + } + else + { + last = p; + p = p->next; + } + } + } + return (rv); +} + void mutt_free_list (LIST **list) { LIST *p; @@ -607,11 +658,8 @@ int mutt_needs_mailcap (BODY *m) switch (m->type) { case TYPETEXT: - - if (!ascii_strcasecmp ("plain", m->subtype) || - !ascii_strcasecmp ("rfc822-headers", m->subtype) || - !ascii_strcasecmp ("enriched", m->subtype)) - return 0; + /* we can display any text, overridable by auto_view */ + return 0; break; case TYPEAPPLICATION: @@ -731,11 +779,16 @@ void mutt_merge_envelopes(ENVELOPE* base, ENVELOPE** extra) mutt_free_envelope(extra); } -void _mutt_mktemp (char *s, const char *src, int line) +void _mutt_mktemp (char *s, size_t slen, const char *src, int line) { - snprintf (s, _POSIX_PATH_MAX, "%s/mutt-%s-%d-%d-%d", NONULL (Tempdir), NONULL(Hostname), (int) getuid(), (int) getpid (), Counter++); - dprint (1, (debugfile, "%s:%d: mutt_mktemp returns \"%s\".\n", src, line, s)); - unlink (s); + size_t n = snprintf (s, slen, "%s/mutt-%s-%d-%d-%ld%ld", NONULL (Tempdir), NONULL (Hostname), + (int) getuid (), (int) getpid (), random (), random ()); + if (n >= slen) + dprint (1, (debugfile, "%s:%d: ERROR: insufficient buffer space to hold temporary filename! slen=%zu but need %zu\n", + src, line, slen, n)); + dprint (3, (debugfile, "%s:%d: mutt_mktemp returns \"%s\".\n", src, line, s)); + if (unlink (s) && errno != ENOENT) + dprint (1, (debugfile, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src, line, s, strerror (errno), errno)); } void mutt_free_alias (ALIAS **p) @@ -746,6 +799,7 @@ void mutt_free_alias (ALIAS **p) { t = *p; *p = (*p)->next; + mutt_alias_delete_reverse (t); FREE (&t->name); rfc822_free_address (&t->addr); FREE (&t); @@ -753,11 +807,12 @@ void mutt_free_alias (ALIAS **p) } /* collapse the pathname using ~ or = when possible */ -void mutt_pretty_mailbox (char *s) +void mutt_pretty_mailbox (char *s, size_t buflen) { char *p = s, *q = s; size_t len; url_scheme_t scheme; + char tmp[PATH_MAX]; scheme = url_check_scheme (s); @@ -779,24 +834,34 @@ void mutt_pretty_mailbox (char *s) q = strchr (p, '\0'); p = q; } - - /* first attempt to collapse the pathname */ - while (*p) + + /* cleanup path */ + if (strstr (p, "//") || strstr (p, "/./")) { - if (*p == '/' && p[1] == '/') + /* first attempt to collapse the pathname, this is more + * lightweight than realpath() and doesn't resolve links + */ + while (*p) { - *q++ = '/'; - p += 2; - } - else if (p[0] == '/' && p[1] == '.' && p[2] == '/') - { - *q++ = '/'; - p += 3; + if (*p == '/' && p[1] == '/') + { + *q++ = '/'; + p += 2; + } + else if (p[0] == '/' && p[1] == '.' && p[2] == '/') + { + *q++ = '/'; + p += 3; + } + else + *q++ = *p++; } - else - *q++ = *p++; + *q = 0; } - *q = 0; + else if (strstr (p, "..") && + (scheme == U_UNKNOWN || scheme == U_FILE) && + realpath (p, tmp)) + strfcpy (p, tmp, buflen - (p - s)); if (mutt_strncmp (s, Maildir, (len = mutt_strlen (Maildir))) == 0 && s[len] == '/') @@ -1078,35 +1143,51 @@ void mutt_FormatString (char *dest, /* output buffer */ col -= wlen; /* reset to passed in value */ wptr = dest; /* reset write ptr */ wlen = (flags & M_FORMAT_ARROWCURSOR && option (OPTARROWCURSOR)) ? 3 : 0; - if ((pid = mutt_create_filter(command->data, NULL, &filter, NULL))) + if ((pid = mutt_create_filter(command->data, NULL, &filter, NULL)) != -1) { + int rc; + n = fread(dest, 1, destlen /* already decremented */, filter); - fclose(filter); - dest[n] = '\0'; - while (dest[n-1] == '\n' || dest[n-1] == '\r') - dest[--n] = '\0'; - dprint(3, (debugfile, "fmtpipe < %s\n", dest)); - - if (pid != -1) - mutt_wait_filter(pid); - - /* If the result ends with '%', this indicates that the filter - * generated %-tokens that mutt can expand. Eliminate the '%' - * marker and recycle the string through mutt_FormatString(). - * To literally end with "%", use "%%". */ - if (dest[--n] == '%') - { - dest[n] = '\0'; /* remove '%' */ - if (dest[--n] != '%') - { - recycler = safe_strdup(dest); - if (recycler) - { - mutt_FormatString(dest, destlen++, col, recycler, callback, data, flags); - FREE(&recycler); - } - } - } + safe_fclose (&filter); + rc = mutt_wait_filter(pid); + if (rc != 0) + dprint(1, (debugfile, "format pipe command exited code %d\n", rc)); + if (n > 0) { + dest[n] = 0; + while ((n > 0) && (dest[n-1] == '\n' || dest[n-1] == '\r')) + dest[--n] = '\0'; + dprint(3, (debugfile, "fmtpipe < %s\n", dest)); + + /* If the result ends with '%', this indicates that the filter + * generated %-tokens that mutt can expand. Eliminate the '%' + * marker and recycle the string through mutt_FormatString(). + * To literally end with "%", use "%%". */ + if ((n > 0) && dest[n-1] == '%') + { + --n; + dest[n] = '\0'; /* remove '%' */ + if ((n > 0) && dest[n-1] != '%') + { + recycler = safe_strdup(dest); + if (recycler) + { + /* destlen is decremented at the start of this function + * to save space for the terminal nul char. We can add + * it back for the recursive call since the expansion of + * format pipes does not try to append a nul itself. + */ + mutt_FormatString(dest, destlen+1, col, recycler, callback, data, flags); + FREE(&recycler); + } + } + } + } + else + { + /* read error */ + dprint(1, (debugfile, "error reading from fmtpipe: %s (errno=%d)\n", strerror(errno), errno)); + *wptr = 0; + } } else { @@ -1231,12 +1312,13 @@ void mutt_FormatString (char *dest, /* output buffer */ } else if (soft && pad < 0) { + int offset = (flags & M_FORMAT_ARROWCURSOR && option (OPTARROWCURSOR)) ? 3 : 0; /* \0-terminate dest for length computation in mutt_wstr_trunc() */ *wptr = 0; /* make sure right part is at most as wide as display */ - len = mutt_wstr_trunc (buf, destlen, COLS, &wid); + len = mutt_wstr_trunc (buf, destlen, COLS-offset, &wid); /* truncate left so that right part fits completely in */ - wlen = mutt_wstr_trunc (dest, destlen - len, col + pad, &col); + wlen = mutt_wstr_trunc (dest, destlen - len, col + pad*pw -offset, &col); wptr = dest + wlen; } if (len + wlen > destlen) @@ -1379,7 +1461,7 @@ void mutt_FormatString (char *dest, /* output buffer */ /* This function allows the user to specify a command to read stdout from in place of a normal file. If the last character in the string is a pipe (|), - then we assume it is a commmand to run instead of a normal file. */ + then we assume it is a command to run instead of a normal file. */ FILE *mutt_open_read (const char *path, pid_t *thepid) { FILE *f; @@ -1451,10 +1533,7 @@ int mutt_save_confirm (const char *s, struct stat *st) return 1; } } - else -#ifdef USE_IMAP - if (magic != M_IMAP) -#endif /* execute the block unconditionally if we don't use imap */ + else if (magic != M_IMAP) { st->st_mtime = 0; st->st_atime = 0; @@ -1525,6 +1604,31 @@ void state_attach_puts (const char *t, STATE *s) } } +int state_putwc (wchar_t wc, STATE *s) +{ + char mb[MB_LEN_MAX] = ""; + int rc; + + if ((rc = wcrtomb (mb, wc, NULL)) < 0) + return rc; + if (fputs (mb, s->fpout) == EOF) + return -1; + return 0; +} + +int state_putws (const wchar_t *ws, STATE *s) +{ + const wchar_t *p = ws; + + while (p && *p != L'\0') + { + if (state_putwc (*p, s) < 0) + return -1; + p++; + } + return 0; +} + void mutt_display_sanitize (char *s) { for (; *s; s++) @@ -1593,6 +1697,9 @@ int mutt_buffer_printf (BUFFER* buf, const char* fmt, ...) va_start (ap, fmt); va_copy (ap_retry, ap); + if (!buf->dptr) + buf->dptr = buf->data; + doff = buf->dptr - buf->data; blen = buf->dsize - doff; /* solaris 9 vsnprintf barfs when blen is 0 */ @@ -1612,13 +1719,13 @@ int mutt_buffer_printf (BUFFER* buf, const char* fmt, ...) safe_realloc (&buf->data, buf->dsize); buf->dptr = buf->data + doff; len = vsnprintf (buf->dptr, len, fmt, ap_retry); - va_end (ap_retry); } if (len > 0) buf->dptr += len; va_end (ap); - + va_end (ap_retry); + return len; } @@ -1774,16 +1881,21 @@ int mutt_match_rx_list (const char *s, RX_LIST *l) return 0; } -int mutt_match_spam_list (const char *s, SPAM_LIST *l, char *text, int x) +/* Match a string against the patterns defined by the 'spam' command and output + * the expanded format into `text` when there is a match. If textsize<=0, the + * match is performed but the format is not expanded and no assumptions are made + * about the value of `text` so it may be NULL. + * + * Returns 1 if the argument `s` matches a pattern in the spam list, otherwise + * 0. */ +int mutt_match_spam_list (const char *s, SPAM_LIST *l, char *text, int textsize) { static regmatch_t *pmatch = NULL; static int nmatch = 0; - int i, n, tlen; + int tlen = 0; char *p; - if (!s) return 0; - - tlen = 0; + if (!s) return 0; for (; l; l = l->next) { @@ -1801,23 +1913,42 @@ int mutt_match_spam_list (const char *s, SPAM_LIST *l, char *text, int x) dprint (5, (debugfile, "mutt_match_spam_list: %d subs\n", (int)l->rx->rx->re_nsub)); /* Copy template into text, with substitutions. */ - for (p = l->template; *p;) + for (p = l->template; *p && tlen < textsize - 1;) { + /* backreference to pattern match substring, eg. %1, %2, etc) */ if (*p == '%') { - n = atoi(++p); /* find pmatch index */ - while (isdigit((unsigned char)*p)) - ++p; /* skip subst token */ - for (i = pmatch[n].rm_so; (i < pmatch[n].rm_eo) && (tlen < x); i++) - text[tlen++] = s[i]; + char *e; /* used as pointer to end of integer backreference in strtol() call */ + int n; + + ++p; /* skip over % char */ + n = strtol(p, &e, 10); + /* Ensure that the integer conversion succeeded (e!=p) and bounds check. The upper bound check + * should not strictly be necessary since add_to_spam_list() finds the largest value, and + * the static array above is always large enough based on that value. */ + if (e != p && n >= 0 && n <= l->nmatch && pmatch[n].rm_so != -1) { + /* copy as much of the substring match as will fit in the output buffer, saving space for + * the terminating nul char */ + int idx; + for (idx = pmatch[n].rm_so; (idx < pmatch[n].rm_eo) && (tlen < textsize - 1); ++idx) + text[tlen++] = s[idx]; + } + p = e; /* skip over the parsed integer */ } else { text[tlen++] = *p++; } } - text[tlen] = '\0'; - dprint (5, (debugfile, "mutt_match_spam_list: \"%s\"\n", text)); + /* tlen should always be less than textsize except when textsize<=0 + * because the bounds checks in the above code leave room for the + * terminal nul char. This should avoid returning an unterminated + * string to the caller. When textsize<=0 we make no assumption about + * the validity of the text pointer. */ + if (tlen < textsize) { + text[tlen] = '\0'; + dprint (5, (debugfile, "mutt_match_spam_list: \"%s\"\n", text)); + } return 1; } } @@ -1825,3 +1956,10 @@ int mutt_match_spam_list (const char *s, SPAM_LIST *l, char *text, int x) return 0; } +void mutt_encode_path (char *dest, size_t dlen, const char *src) +{ + char *p = safe_strdup (src); + int rc = mutt_convert_string (&p, Charset, "utf-8", 0); + strfcpy (dest, rc == 0 ? p : src, dlen); + FREE (&p); +}