/** * set_compress_info - Find the compress hooks for a mailbox * @param m Mailbox to examine * @retval ptr CompressInfo Hook info for the mailbox's path * @retval NULL Error * * When a mailbox is opened, we check if there are any matching hooks. */ static struct CompressInfo *set_compress_info(struct Mailbox *m) { if (!m) return NULL; if (m->compress_info) return m->compress_info; /* Open is compulsory */ const char *o = find_hook(MUTT_OPEN_HOOK, m->path); if (!o) return NULL; const char *c = find_hook(MUTT_CLOSE_HOOK, m->path); const char *a = find_hook(MUTT_APPEND_HOOK, m->path); struct CompressInfo *ci = mutt_mem_calloc(1, sizeof(struct CompressInfo)); m->compress_info = ci; ci->cmd_open = mutt_str_strdup(o); ci->cmd_close = mutt_str_strdup(c); ci->cmd_append = mutt_str_strdup(a); return ci; }
/** * address_dup - Create a copy of an Address object * @param addr Address to duplicate * @retval ptr New Address object */ static struct Address *address_dup(struct Address *addr) { if (!addr) return NULL; /* LCOV_EXCL_LINE */ struct Address *a = mutt_mem_calloc(1, sizeof(*a)); a->personal = mutt_str_strdup(addr->personal); a->mailbox = mutt_str_strdup(addr->mailbox); return a; }
/** * mutt_regex_new - Create an Regex from a string * @param str Regular expression * @param flags Type flags, e.g. #DT_REGEX_MATCH_CASE * @param err Buffer for error messages * @retval ptr New Regex object * @retval NULL Error */ struct Regex *mutt_regex_new(const char *str, int flags, struct Buffer *err) { if (!str) return NULL; int rflags = 0; struct Regex *reg = mutt_mem_calloc(1, sizeof(struct Regex)); reg->regex = mutt_mem_calloc(1, sizeof(regex_t)); reg->pattern = mutt_str_strdup(str); /* Should we use smart case matching? */ if (((flags & DT_REGEX_MATCH_CASE) == 0) && mutt_mb_is_lower(str)) rflags |= REG_ICASE; /* Is a prefix of '!' allowed? */ if (((flags & DT_REGEX_ALLOW_NOT) != 0) && (str[0] == '!')) { reg->not = true; str++; } int rc = REGCOMP(reg->regex, str, rflags); if ((rc != 0) && err) { regerror(rc, reg->regex, err->data, err->dsize); mutt_regex_free(®); return NULL; } return reg; }
/** * address_new - Create an Address from a string * @param addr Email address to parse * @retval ptr New Address object */ struct Address *address_new(const char *addr) { struct Address *a = mutt_mem_calloc(1, sizeof(*a)); // a->personal = mutt_str_strdup(addr); a->mailbox = mutt_str_strdup(addr); return a; }
/** * cs_inherit_variable - Create in inherited config item * @param cs Config items * @param parent HashElem of parent config item * @param name Name of account-specific config item * @retval ptr New HashElem representing the inherited config item */ struct HashElem *cs_inherit_variable(const struct ConfigSet *cs, struct HashElem *parent, const char *name) { if (!cs || !parent) return NULL; /* LCOV_EXCL_LINE */ struct Buffer err; mutt_buffer_init(&err); err.dsize = 256; err.data = calloc(1, err.dsize); struct Inheritance *i = mutt_mem_calloc(1, sizeof(*i)); i->parent = parent; i->name = mutt_str_strdup(name); struct HashElem *he = mutt_hash_typed_insert(cs->hash, i->name, DT_INHERITED, i); if (!he) { FREE(&i->name); FREE(&i); } FREE(&err.data); return he; }
/** * mutt_regex_compile - Create an Regex from a string * @param str Regular expression * @param flags Type flags, e.g. REG_ICASE * @retval ptr New Regex object * @retval NULL Error */ struct Regex *mutt_regex_compile(const char *str, int flags) { struct Regex *rx = mutt_mem_calloc(1, sizeof(struct Regex)); rx->pattern = mutt_str_strdup(str); rx->regex = mutt_mem_calloc(1, sizeof(regex_t)); if (REGCOMP(rx->regex, NONULL(str), flags) != 0) mutt_regex_free(&rx); return rx; }
/** * address_string_set - Set an Address by string - Implements ::cst_string_set() */ static int address_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef, const char *value, struct Buffer *err) { if (!cs || !cdef) return CSR_ERR_CODE; /* LCOV_EXCL_LINE */ struct Address *addr = NULL; /* An empty address "" will be stored as NULL */ if (var && value && (value[0] != '\0')) { addr = mutt_addr_parse_list(NULL, value); } int rc = CSR_SUCCESS; if (var) { if (cdef->validator) { rc = cdef->validator(cs, cdef, (intptr_t) addr, err); if (CSR_RESULT(rc) != CSR_SUCCESS) { address_destroy(cs, &addr, cdef); return rc | CSR_INV_VALIDATOR; } } /* ordinary variable setting */ address_destroy(cs, var, cdef); *(struct Address **) var = addr; if (!addr) rc |= CSR_SUC_EMPTY; } else { /* set the default/initial value */ if (cdef->type & DT_INITIAL_SET) FREE(&cdef->initial); cdef->type |= DT_INITIAL_SET; cdef->initial = IP mutt_str_strdup(value); } return rc; }
/** * mutt_envlist_init - Create a copy of the environment * @param envp Environment variables */ void mutt_envlist_init(char *envp[]) { if (EnvList) mutt_envlist_free(); if (!envp) return; char **src, **dst; int count = 0; for (src = envp; src && *src; src++) count++; EnvList = mutt_mem_calloc(count + 1, sizeof(char *)); for (src = envp, dst = EnvList; src && *src; src++, dst++) *dst = mutt_str_strdup(*src); }
/** * cs_inherit_variable - Create in inherited config item * @param cs Config items * @param parent HashElem of parent config item * @param name Name of account-specific config item * @retval ptr New HashElem representing the inherited config item */ struct HashElem *cs_inherit_variable(const struct ConfigSet *cs, struct HashElem *parent, const char *name) { if (!cs || !parent) return NULL; struct Inheritance *i = mutt_mem_calloc(1, sizeof(*i)); i->parent = parent; i->name = mutt_str_strdup(name); struct HashElem *he = mutt_hash_typed_insert(cs->hash, i->name, DT_INHERITED, i); if (!he) { FREE(&i->name); FREE(&i); } return he; }
/** * mutt_envlist_set - Set an environment variable * @param name Name of the variable * @param value New value * @param overwrite Should the variable be overwritten? * @retval true Success: variable set, or overwritten * @retval false Variable exists and overwrite was false * * It's broken out because some other parts of neomutt (filter.c) need to * set/overwrite environment variables in EnvList before calling exec(). */ bool mutt_envlist_set(const char *name, const char *value, bool overwrite) { char **envp = EnvList; char work[1024]; int count; /* Look for current slot to overwrite */ count = 0; while (envp && *envp) { size_t len = mutt_str_startswith(*envp, name, CASE_MATCH); if ((len != 0) && ((*envp)[len] == '=')) { if (!overwrite) return false; break; } envp++; count++; } /* Format var=value string */ snprintf(work, sizeof(work), "%s=%s", NONULL(name), NONULL(value)); if (envp && *envp) { /* slot found, overwrite */ mutt_str_replace(envp, work); } else { /* not found, add new slot */ mutt_mem_realloc(&EnvList, sizeof(char *) * (count + 2)); EnvList[count] = mutt_str_strdup(work); EnvList[count + 1] = NULL; } return true; }
/** * check_host - Check the host on the certificate * @param x509cert Certificate * @param hostname Hostname * @param err Buffer for error message * @param errlen Length of buffer * @retval 1 Hostname matches the certificate * @retval 0 Error */ static int check_host(X509 *x509cert, const char *hostname, char *err, size_t errlen) { int rc = 0; /* hostname in ASCII format: */ char *hostname_ascii = NULL; /* needed to get the common name: */ X509_NAME *x509_subject = NULL; char *buf = NULL; int bufsize; /* needed to get the DNS subjectAltNames: */ STACK_OF(GENERAL_NAME) * subj_alt_names; int subj_alt_names_count; GENERAL_NAME *subj_alt_name = NULL; /* did we find a name matching hostname? */ bool match_found; /* Check if 'hostname' matches the one of the subjectAltName extensions of * type DNS or the Common Name (CN). */ #ifdef HAVE_LIBIDN if (mutt_idna_to_ascii_lz(hostname, &hostname_ascii, 0) != 0) { hostname_ascii = mutt_str_strdup(hostname); } #else hostname_ascii = mutt_str_strdup(hostname); #endif /* Try the DNS subjectAltNames. */ match_found = false; subj_alt_names = X509_get_ext_d2i(x509cert, NID_subject_alt_name, NULL, NULL); if (subj_alt_names) { subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names); for (int i = 0; i < subj_alt_names_count; i++) { subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i); if (subj_alt_name->type == GEN_DNS) { if ((subj_alt_name->d.ia5->length >= 0) && (mutt_str_strlen((char *) subj_alt_name->d.ia5->data) == (size_t) subj_alt_name->d.ia5->length) && (match_found = hostname_match(hostname_ascii, (char *) (subj_alt_name->d.ia5->data)))) { break; } } } GENERAL_NAMES_free(subj_alt_names); } if (!match_found) { /* Try the common name */ x509_subject = X509_get_subject_name(x509cert); if (!x509_subject) { if (err && errlen) mutt_str_strfcpy(err, _("cannot get certificate subject"), errlen); goto out; } /* first get the space requirements */ bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName, NULL, 0); if (bufsize == -1) { if (err && errlen) mutt_str_strfcpy(err, _("cannot get certificate common name"), errlen); goto out; } bufsize++; /* space for the terminal nul char */ buf = mutt_mem_malloc((size_t) bufsize); if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName, buf, bufsize) == -1) { if (err && errlen) mutt_str_strfcpy(err, _("cannot get certificate common name"), errlen); goto out; } /* cast is safe since bufsize is incremented above, so bufsize-1 is always * zero or greater. */ if (mutt_str_strlen(buf) == (size_t) bufsize - 1) { match_found = hostname_match(hostname_ascii, buf); } } if (!match_found) { if (err && errlen) snprintf(err, errlen, _("certificate owner does not match hostname %s"), hostname); goto out; } rc = 1; out: FREE(&buf); FREE(&hostname_ascii); return rc; }
/** * group_index_format_str - Format a string for the newsgroup menu - Implements ::format_t * * | Expando | Description * |:--------|:-------------------------------------------------------- * | \%C | Current newsgroup number * | \%d | Description of newsgroup (becomes from server) * | \%f | Newsgroup name * | \%M | - if newsgroup not allowed for direct post (moderated for example) * | \%N | N if newsgroup is new, u if unsubscribed, blank otherwise * | \%n | Number of new articles in newsgroup * | \%s | Number of unread articles in newsgroup */ const char *group_index_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, unsigned long data, MuttFormatFlags flags) { char fn[128], fmt[128]; struct Folder *folder = (struct Folder *) data; switch (op) { case 'C': snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, folder->num + 1); break; case 'd': if (folder->ff->nd->desc) { char *desc = mutt_str_strdup(folder->ff->nd->desc); if (C_NewsgroupsCharset && *C_NewsgroupsCharset) mutt_ch_convert_string(&desc, C_NewsgroupsCharset, C_Charset, MUTT_ICONV_HOOK_FROM); mutt_mb_filter_unprintable(&desc); snprintf(fmt, sizeof(fmt), "%%%ss", prec); snprintf(buf, buflen, fmt, desc); FREE(&desc); } else { snprintf(fmt, sizeof(fmt), "%%%ss", prec); snprintf(buf, buflen, fmt, ""); } break; case 'f': mutt_str_strfcpy(fn, folder->ff->name, sizeof(fn)); snprintf(fmt, sizeof(fmt), "%%%ss", prec); snprintf(buf, buflen, fmt, fn); break; case 'M': snprintf(fmt, sizeof(fmt), "%%%sc", prec); if (folder->ff->nd->deleted) snprintf(buf, buflen, fmt, 'D'); else snprintf(buf, buflen, fmt, folder->ff->nd->allowed ? ' ' : '-'); break; case 'N': snprintf(fmt, sizeof(fmt), "%%%sc", prec); if (folder->ff->nd->subscribed) snprintf(buf, buflen, fmt, ' '); else snprintf(buf, buflen, fmt, folder->ff->new ? 'N' : 'u'); break; case 'n': if (Context && (Context->mailbox->mdata == folder->ff->nd)) { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, Context->mailbox->msg_new); } else if (C_MarkOld && (folder->ff->nd->last_cached >= folder->ff->nd->first_message) && (folder->ff->nd->last_cached <= folder->ff->nd->last_message)) { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, folder->ff->nd->last_message - folder->ff->nd->last_cached); } else { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, folder->ff->nd->unread); } break; case 's': if (flags & MUTT_FORMAT_OPTIONAL) { if (folder->ff->nd->unread != 0) { mutt_expando_format(buf, buflen, col, cols, if_str, group_index_format_str, data, flags); } else { mutt_expando_format(buf, buflen, col, cols, else_str, group_index_format_str, data, flags); } } else if (Context && (Context->mailbox->mdata == folder->ff->nd)) { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, Context->mailbox->msg_unread); } else { snprintf(fmt, sizeof(fmt), "%%%sd", prec); snprintf(buf, buflen, fmt, folder->ff->nd->unread); } break; } return src; }
/** * crypt_get_fingerprint_or_id - Get the fingerprint or long key ID * @param[in] p String to examine * @param[out] pphint Start of string to be passed to pgp_add_string_to_hints() or crypt_add_string_to_hints() * @param[out] ppl Start of long key ID if detected, else NULL * @param[out] pps Start of short key ID if detected, else NULL * @retval ptr Copy of fingerprint, if any, stripped of all spaces. Must be FREE'd by caller * @retval NULL Otherwise * * Obtain pointers to fingerprint or short or long key ID, if any. * * Upon return, at most one of return, *ppl and *pps pointers is non-NULL, * indicating the longest fingerprint or ID found, if any. */ const char *crypt_get_fingerprint_or_id(char *p, const char **pphint, const char **ppl, const char **pps) { const char *ps = NULL, *pl = NULL, *phint = NULL; char *pfcopy = NULL, *s1 = NULL, *s2 = NULL; char c; int isid; size_t hexdigits; /* User input may be partial name, fingerprint or short or long key ID, * independent of C_PgpLongIds. * Fingerprint without spaces is 40 hex digits (SHA-1) or 32 hex digits (MD5). * Strip leading "0x" for key ID detection and prepare pl and ps to indicate * if an ID was found and to simplify logic in the key loop's inner * condition of the caller. */ char *pf = mutt_str_skip_whitespace(p); if (mutt_str_startswith(pf, "0x", CASE_IGNORE)) pf += 2; /* Check if a fingerprint is given, must be hex digits only, blanks * separating groups of 4 hex digits are allowed. Also pre-check for ID. */ isid = 2; /* unknown */ hexdigits = 0; s1 = pf; do { c = *(s1++); if ((('0' <= c) && (c <= '9')) || (('A' <= c) && (c <= 'F')) || (('a' <= c) && (c <= 'f'))) { hexdigits++; if (isid == 2) isid = 1; /* it is an ID so far */ } else if (c) { isid = 0; /* not an ID */ if ((c == ' ') && ((hexdigits % 4) == 0)) ; /* skip blank before or after 4 hex digits */ else break; /* any other character or position */ } } while (c); /* If at end of input, check for correct fingerprint length and copy if. */ pfcopy = (!c && ((hexdigits == 40) || (hexdigits == 32)) ? mutt_str_strdup(pf) : NULL); if (pfcopy) { /* Use pfcopy to strip all spaces from fingerprint and as hint. */ s1 = pfcopy; s2 = pfcopy; do { *(s1++) = *(s2 = mutt_str_skip_whitespace(s2)); } while (*(s2++)); phint = pfcopy; ps = NULL; pl = NULL; } else { phint = p; ps = NULL; pl = NULL; if (isid == 1) { if (mutt_str_strlen(pf) == 16) pl = pf; /* long key ID */ else if (mutt_str_strlen(pf) == 8) ps = pf; /* short key ID */ } } *pphint = phint; *ppl = pl; *pps = ps; return pfcopy; }
/** * mh_mbox_check - Implements MxOps::mbox_check() * * This function handles arrival of new mail and reopening of mh/maildir * folders. Things are getting rather complex because we don't have a * well-defined "mailbox order", so the tricks from mbox.c and mx.c won't work * here. * * Don't change this code unless you _really_ understand what happens. */ int mh_mbox_check(struct Mailbox *m, int *index_hint) { if (!m) return -1; char buf[PATH_MAX]; struct stat st, st_cur; bool modified = false, occult = false, flags_changed = false; int num_new = 0; struct Maildir *md = NULL, *p = NULL; struct Maildir **last = NULL; struct MhSequences mhs = { 0 }; int count = 0; struct Hash *fnames = NULL; struct MaildirMboxData *mdata = maildir_mdata_get(m); if (!C_CheckNew) return 0; mutt_str_strfcpy(buf, m->path, sizeof(buf)); if (stat(buf, &st) == -1) return -1; /* create .mh_sequences when there isn't one. */ snprintf(buf, sizeof(buf), "%s/.mh_sequences", m->path); int i = stat(buf, &st_cur); if ((i == -1) && (errno == ENOENT)) { char *tmp = NULL; FILE *fp = NULL; if (mh_mkstemp(m, &fp, &tmp) == 0) { mutt_file_fclose(&fp); if (mutt_file_safe_rename(tmp, buf) == -1) unlink(tmp); FREE(&tmp); } } if ((i == -1) && (stat(buf, &st_cur) == -1)) modified = true; if ((mutt_file_stat_timespec_compare(&st, MUTT_STAT_MTIME, &m->mtime) > 0) || (mutt_file_stat_timespec_compare(&st_cur, MUTT_STAT_MTIME, &mdata->mtime_cur) > 0)) { modified = true; } if (!modified) return 0; /* Update the modification times on the mailbox. * * The monitor code notices changes in the open mailbox too quickly. * In practice, this sometimes leads to all the new messages not being * noticed during the SAME group of mtime stat updates. To work around * the problem, don't update the stat times for a monitor caused check. */ #ifdef USE_INOTIFY if (MonitorContextChanged) MonitorContextChanged = 0; else #endif { mutt_file_get_stat_timespec(&mdata->mtime_cur, &st_cur, MUTT_STAT_MTIME); mutt_file_get_stat_timespec(&m->mtime, &st, MUTT_STAT_MTIME); } md = NULL; last = &md; maildir_parse_dir(m, &last, NULL, &count, NULL); maildir_delayed_parsing(m, &md, NULL); if (mh_read_sequences(&mhs, m->path) < 0) return -1; mh_update_maildir(md, &mhs); mhs_free_sequences(&mhs); /* check for modifications and adjust flags */ fnames = mutt_hash_new(count, MUTT_HASH_NO_FLAGS); for (p = md; p; p = p->next) { /* the hash key must survive past the header, which is freed below. */ p->canon_fname = mutt_str_strdup(p->email->path); mutt_hash_insert(fnames, p->canon_fname, p); } for (i = 0; i < m->msg_count; i++) { m->emails[i]->active = false; p = mutt_hash_find(fnames, m->emails[i]->path); if (p && p->email && mutt_email_cmp_strict(m->emails[i], p->email)) { m->emails[i]->active = true; /* found the right message */ if (!m->emails[i]->changed) if (maildir_update_flags(m, m->emails[i], p->email)) flags_changed = true; mutt_email_free(&p->email); } else /* message has disappeared */ occult = true; } /* destroy the file name hash */ mutt_hash_free(&fnames); /* If we didn't just get new mail, update the tables. */ if (occult) mutt_mailbox_changed(m, MBN_RESORT); /* Incorporate new messages */ num_new = maildir_move_to_mailbox(m, &md); if (num_new > 0) { mutt_mailbox_changed(m, MBN_INVALID); m->changed = true; } if (occult) return MUTT_REOPENED; if (num_new > 0) return MUTT_NEW_MAIL; if (flags_changed) return MUTT_FLAGS; return 0; }