Exemple #1
0
/**
 * 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;
}
Exemple #2
0
/**
 * 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;
}
Exemple #3
0
/**
 * 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(&reg);
    return NULL;
  }

  return reg;
}
Exemple #4
0
/**
 * 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;
}
Exemple #5
0
/**
 * 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;
}
Exemple #6
0
/**
 * 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;
}
Exemple #7
0
/**
 * 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;
}
Exemple #8
0
/**
 * 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);
}
Exemple #9
0
/**
 * 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;
}
Exemple #10
0
/**
 * 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;
}
Exemple #11
0
/**
 * 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;
}
Exemple #12
0
/**
 * 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;
}
Exemple #13
0
/**
 * 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;
}
Exemple #14
0
/**
 * 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;
}