/* enter a mutt command */ void mutt_enter_command (void) { BUFFER err, token; char buffer[LONG_STRING]; int r; buffer[0] = 0; if (mutt_get_field (":", buffer, sizeof (buffer), M_COMMAND) != 0 || !buffer[0]) return; mutt_buffer_init (&err); err.dsize = STRING; err.data = safe_malloc(err.dsize); mutt_buffer_init (&token); r = mutt_parse_rc_line (buffer, &token, &err); FREE (&token.data); if (err.data[0]) { /* since errbuf could potentially contain printf() sequences in it, we must call mutt_error() in this fashion so that vsprintf() doesn't expect more arguments that we passed */ if (r == 0) mutt_message ("%s", err.data); else mutt_error ("%s", err.data); } FREE (&err.data); }
void mutt_message_hook (CONTEXT *ctx, HEADER *hdr, int type) { BUFFER err, token; HOOK *hook; current_hook_type = type; mutt_buffer_init (&err); err.dsize = STRING; err.data = safe_malloc (err.dsize); mutt_buffer_init (&token); for (hook = Hooks; hook; hook = hook->next) { if(!hook->command) continue; if (hook->type & type) if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr) > 0) ^ hook->rx.not) if (mutt_parse_rc_line (hook->command, &token, &err) != 0) { FREE (&token.data); mutt_error ("%s", err.data); mutt_sleep (1); current_hook_type = 0; FREE (&err.data); return; } } FREE (&token.data); FREE (&err.data); current_hook_type = 0; }
void config_address(void) { struct Buffer err; mutt_buffer_init(&err); err.dsize = 256; err.data = mutt_mem_calloc(1, err.dsize); mutt_buffer_reset(&err); struct ConfigSet *cs = cs_new(30); address_init(cs); dont_fail = true; if (!cs_register_variables(cs, Vars, 0)) return; dont_fail = false; cs_add_listener(cs, log_listener); set_list(cs); TEST_CHECK(test_initial_values(cs, &err)); TEST_CHECK(test_string_set(cs, &err)); TEST_CHECK(test_string_get(cs, &err)); TEST_CHECK(test_native_set(cs, &err)); TEST_CHECK(test_native_get(cs, &err)); TEST_CHECK(test_reset(cs, &err)); TEST_CHECK(test_validator(cs, &err)); TEST_CHECK(test_inherit(cs, &err)); cs_free(&cs); FREE(&err.data); }
/** * 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; }
void mutt_account_hook (const char* url) { /* parsing commands with URLs in an account hook can cause a recursive * call. We just skip processing if this occurs. Typically such commands * belong in a folder-hook -- perhaps we should warn the user. */ static int inhook = 0; HOOK* hook; BUFFER token; BUFFER err; if (inhook) return; mutt_buffer_init (&err); err.dsize = STRING; err.data = safe_malloc (err.dsize); mutt_buffer_init (&token); for (hook = Hooks; hook; hook = hook->next) { if (! (hook->command && (hook->type & M_ACCOUNTHOOK))) continue; if ((regexec (hook->rx.rx, url, 0, NULL, 0) == 0) ^ hook->rx.not) { inhook = 1; if (mutt_parse_rc_line (hook->command, &token, &err) == -1) { FREE (&token.data); mutt_error ("%s", err.data); FREE (&err.data); mutt_sleep (1); inhook = 0; return; } inhook = 0; } } FREE (&token.data); FREE (&err.data); }
/** * mutt_parse_icommand - Parse an informational command * @param line Command to execute * @param err Buffer for error messages * @retval #MUTT_CMD_SUCCESS Success * @retval #MUTT_CMD_ERROR Error (no message): command not found * @retval #MUTT_CMD_ERROR Error with message: command failed * @retval #MUTT_CMD_WARNING Warning with message: command failed */ enum CommandResult mutt_parse_icommand(/* const */ char *line, struct Buffer *err) { if (!line || !*line || !err) return MUTT_CMD_ERROR; enum CommandResult rc = MUTT_CMD_ERROR; struct Buffer expn, token; mutt_buffer_init(&expn); mutt_buffer_init(&token); expn.data = expn.dptr = line; expn.dsize = mutt_str_strlen(line); mutt_buffer_reset(err); SKIPWS(expn.dptr); while (*expn.dptr) { mutt_extract_token(&token, &expn, 0); for (size_t i = 0; ICommandList[i].name; i++) { if (mutt_str_strcmp(token.data, ICommandList[i].name) != 0) continue; rc = ICommandList[i].func(&token, &expn, ICommandList[i].data, err); if (rc != 0) goto finish; break; /* Continue with next command */ } } finish: if (expn.destroy) FREE(&expn.data); return rc; }
void mutt_folder_hook (char *path) { HOOK *tmp = Hooks; BUFFER err, token; current_hook_type = M_FOLDERHOOK; mutt_buffer_init (&err); err.dsize = STRING; err.data = safe_malloc (err.dsize); mutt_buffer_init (&token); for (; tmp; tmp = tmp->next) { if(!tmp->command) continue; if (tmp->type & M_FOLDERHOOK) { if ((regexec (tmp->rx.rx, path, 0, NULL, 0) == 0) ^ tmp->rx.not) { if (mutt_parse_rc_line (tmp->command, &token, &err) == -1) { mutt_error ("%s", err.data); FREE (&token.data); mutt_sleep (1); /* pause a moment to let the user see the error */ current_hook_type = 0; FREE (&err.data); return; } } } } FREE (&token.data); FREE (&err.data); current_hook_type = 0; }
/* imap_new_idata: Allocate and initialise a new IMAP_DATA structure. * Returns NULL on failure (no mem) */ IMAP_DATA* imap_new_idata (void) { IMAP_DATA* idata = safe_calloc (1, sizeof (IMAP_DATA)); if (!idata) return NULL; if (!(idata->cmdbuf = mutt_buffer_init (NULL))) FREE (&idata); idata->cmdslots = ImapPipelineDepth + 2; if (!(idata->cmds = safe_calloc(idata->cmdslots, sizeof(*idata->cmds)))) { mutt_buffer_free(&idata->cmdbuf); FREE (&idata); } return idata; }
/** * cs_register_variables - Register a set of config items * @param cs Config items * @param vars Variable definition * @param flags Flags, e.g. #CS_REG_DISABLED * @retval bool True, if all variables were registered successfully */ bool cs_register_variables(const struct ConfigSet *cs, struct ConfigDef vars[], int flags) { if (!cs || !vars) return CSR_ERR_CODE; /* LCOV_EXCL_LINE */ struct Buffer err; mutt_buffer_init(&err); err.dsize = 256; err.data = calloc(1, err.dsize); bool rc = true; for (size_t i = 0; vars[i].name; i++) { if (!reg_one_var(cs, &vars[i], &err)) { mutt_debug(LL_DEBUG1, "%s\n", err.data); rc = false; } } FREE(&err.data); return rc; }
int mutt_parse_hook (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) { HOOK *ptr; BUFFER command, pattern; int rc, not = 0; regex_t *rx = NULL; pattern_t *pat = NULL; char path[_POSIX_PATH_MAX]; mutt_buffer_init (&pattern); mutt_buffer_init (&command); if (*s->dptr == '!') { s->dptr++; SKIPWS (s->dptr); not = 1; } mutt_extract_token (&pattern, s, 0); if (!MoreArgs (s)) { strfcpy (err->data, _("too few arguments"), err->dsize); goto error; } mutt_extract_token (&command, s, (data & (M_FOLDERHOOK | M_SENDHOOK | M_SEND2HOOK | M_ACCOUNTHOOK | M_REPLYHOOK)) ? M_TOKEN_SPACE : 0); if (!command.data) { strfcpy (err->data, _("too few arguments"), err->dsize); goto error; } if (MoreArgs (s)) { strfcpy (err->data, _("too many arguments"), err->dsize); goto error; } if (data & (M_FOLDERHOOK | M_MBOXHOOK)) { strfcpy (path, pattern.data, sizeof (path)); _mutt_expand_path (path, sizeof (path), 1); FREE (&pattern.data); memset (&pattern, 0, sizeof (pattern)); pattern.data = safe_strdup (path); } else if (DefaultHook && !(data & (M_CHARSETHOOK | M_ICONVHOOK | M_ACCOUNTHOOK)) && (!WithCrypto || !(data & M_CRYPTHOOK)) ) { char tmp[HUGE_STRING]; /* At this stage remain only message-hooks, reply-hooks, send-hooks, * send2-hooks, save-hooks, and fcc-hooks: All those allowing full * patterns. If given a simple regexp, we expand $default_hook. */ strfcpy (tmp, pattern.data, sizeof (tmp)); mutt_check_simple (tmp, sizeof (tmp), DefaultHook); FREE (&pattern.data); memset (&pattern, 0, sizeof (pattern)); pattern.data = safe_strdup (tmp); } if (data & (M_MBOXHOOK | M_SAVEHOOK | M_FCCHOOK)) { strfcpy (path, command.data, sizeof (path)); mutt_expand_path (path, sizeof (path)); FREE (&command.data); memset (&command, 0, sizeof (command)); command.data = safe_strdup (path); } /* check to make sure that a matching hook doesn't already exist */ for (ptr = Hooks; ptr; ptr = ptr->next) { if (ptr->type == data && ptr->rx.not == not && !mutt_strcmp (pattern.data, ptr->rx.pattern)) { if (data & (M_FOLDERHOOK | M_SENDHOOK | M_SEND2HOOK | M_MESSAGEHOOK | M_ACCOUNTHOOK | M_REPLYHOOK)) { /* these hooks allow multiple commands with the same * pattern, so if we've already seen this pattern/command pair, just * ignore it instead of creating a duplicate */ if (!mutt_strcmp (ptr->command, command.data)) { FREE (&command.data); FREE (&pattern.data); return 0; } } else { /* other hooks only allow one command per pattern, so update the * entry with the new command. this currently does not change the * order of execution of the hooks, which i think is desirable since * a common action to perform is to change the default (.) entry * based upon some other information. */ FREE (&ptr->command); ptr->command = command.data; FREE (&pattern.data); return 0; } } if (!ptr->next) break; } if (data & (M_SENDHOOK | M_SEND2HOOK | M_SAVEHOOK | M_FCCHOOK | M_MESSAGEHOOK | M_REPLYHOOK)) { if ((pat = mutt_pattern_comp (pattern.data, (data & (M_SENDHOOK | M_SEND2HOOK | M_FCCHOOK)) ? 0 : M_FULL_MSG, err)) == NULL) goto error; } else { /* Hooks not allowing full patterns: Check syntax of regexp */ rx = safe_malloc (sizeof (regex_t)); #ifdef M_CRYPTHOOK if ((rc = REGCOMP (rx, NONULL(pattern.data), ((data & (M_CRYPTHOOK|M_CHARSETHOOK|M_ICONVHOOK)) ? REG_ICASE : 0))) != 0) #else if ((rc = REGCOMP (rx, NONULL(pattern.data), (data & (M_CHARSETHOOK|M_ICONVHOOK)) ? REG_ICASE : 0)) != 0) #endif /* M_CRYPTHOOK */ { regerror (rc, rx, err->data, err->dsize); FREE (&rx); goto error; } } if (ptr) { ptr->next = safe_calloc (1, sizeof (HOOK)); ptr = ptr->next; } else Hooks = ptr = safe_calloc (1, sizeof (HOOK)); ptr->type = data; ptr->command = command.data; ptr->pattern = pat; ptr->rx.pattern = pattern.data; ptr->rx.rx = rx; ptr->rx.not = not; return 0; error: FREE (&pattern.data); FREE (&command.data); return (-1); }
void config_set(void) { log_line(__func__); struct Buffer err; mutt_buffer_init(&err); err.dsize = 256; err.data = mutt_mem_calloc(1, err.dsize); mutt_buffer_reset(&err); struct ConfigSet *cs = cs_new(30); if (!TEST_CHECK(cs != NULL)) return; cs_add_listener(cs, log_listener); cs_add_listener(cs, log_listener); /* dupe */ cs_remove_listener(cs, log_listener); cs_remove_listener(cs, log_listener); /* non-existant */ const struct ConfigSetType cst_dummy = { "dummy", NULL, NULL, NULL, NULL, NULL, NULL, }; if (TEST_CHECK(!cs_register_type(cs, DT_STRING, &cst_dummy))) { TEST_MSG("Expected error\n"); } else { TEST_MSG("This test should have failed\n"); return; } const struct ConfigSetType cst_dummy2 = { "dummy2", dummy_string_set, dummy_string_get, dummy_native_set, dummy_native_get, dummy_reset, dummy_destroy, }; if (TEST_CHECK(!cs_register_type(cs, 25, &cst_dummy2))) { TEST_MSG("Expected error\n"); } else { TEST_MSG("This test should have failed\n"); return; } bool_init(cs); bool_init(cs); /* second one should fail */ if (TEST_CHECK(!cs_register_variables(cs, Vars, 0))) { TEST_MSG("Expected error\n"); } else { TEST_MSG("This test should have failed\n"); return; } const char *name = "Unknown"; int result = cs_str_string_set(cs, name, "hello", &err); if (TEST_CHECK(CSR_RESULT(result) == CSR_ERR_UNKNOWN)) { TEST_MSG("Expected error: Unknown var '%s'\n", name); } else { TEST_MSG("This should have failed 1\n"); return; } result = cs_str_string_get(cs, name, &err); if (TEST_CHECK(CSR_RESULT(result) == CSR_ERR_UNKNOWN)) { TEST_MSG("Expected error: Unknown var '%s'\n", name); } else { TEST_MSG("This should have failed 2\n"); return; } result = cs_str_native_set(cs, name, IP "hello", &err); if (TEST_CHECK(CSR_RESULT(result) == CSR_ERR_UNKNOWN)) { TEST_MSG("Expected error: Unknown var '%s'\n", name); } else { TEST_MSG("This should have failed 3\n"); return; } intptr_t native = cs_str_native_get(cs, name, &err); if (TEST_CHECK(native == INT_MIN)) { TEST_MSG("Expected error: Unknown var '%s'\n", name); } else { TEST_MSG("This should have failed 4\n"); return; } struct HashElem *he = cs_get_elem(cs, "Banana"); if (!TEST_CHECK(he != NULL)) return; set_list(cs); const struct ConfigSetType *cst = cs_get_type_def(cs, 15); if (!TEST_CHECK(!cst)) return; cs_free(&cs); FREE(&err.data); log_line(__func__); }
static bool test_initial_values(struct ConfigSet *cs, struct Buffer *err) { log_line(__func__); TEST_MSG("Apple = '%s'\n", VarApple->mailbox); TEST_MSG("Banana = '%s'\n", VarBanana->mailbox); const char *apple_orig = "*****@*****.**"; const char *banana_orig = "*****@*****.**"; if (!TEST_CHECK(mutt_str_strcmp(VarApple->mailbox, apple_orig) == 0)) { TEST_MSG("Error: initial values were wrong\n"); return false; } if (!TEST_CHECK(mutt_str_strcmp(VarBanana->mailbox, banana_orig) == 0)) { TEST_MSG("Error: initial values were wrong\n"); return false; } cs_str_string_set(cs, "Apple", "*****@*****.**", err); cs_str_string_set(cs, "Banana", NULL, err); struct Buffer value; mutt_buffer_init(&value); value.dsize = 256; value.data = mutt_mem_calloc(1, value.dsize); mutt_buffer_reset(&value); int rc; mutt_buffer_reset(&value); rc = cs_str_initial_get(cs, "Apple", &value); if (!TEST_CHECK(CSR_RESULT(rc) == CSR_SUCCESS)) { TEST_MSG("%s\n", value.data); FREE(&value.data); return false; } if (!TEST_CHECK(mutt_str_strcmp(value.data, apple_orig) == 0)) { TEST_MSG("Apple's initial value is wrong: '%s'\n", value.data); FREE(&value.data); return false; } TEST_MSG("Apple = '%s'\n", VarApple->mailbox); TEST_MSG("Apple's initial value is '%s'\n", value.data); mutt_buffer_reset(&value); rc = cs_str_initial_get(cs, "Banana", &value); if (!TEST_CHECK(CSR_RESULT(rc) == CSR_SUCCESS)) { TEST_MSG("%s\n", value.data); FREE(&value.data); return false; } if (!TEST_CHECK(mutt_str_strcmp(value.data, banana_orig) == 0)) { TEST_MSG("Banana's initial value is wrong: '%s'\n", value.data); FREE(&value.data); return false; } TEST_MSG("Banana = '%s'\n", VarBanana ? VarBanana->mailbox : ""); TEST_MSG("Banana's initial value is '%s'\n", NONULL(value.data)); mutt_buffer_reset(&value); rc = cs_str_initial_set(cs, "Cherry", "*****@*****.**", &value); if (!TEST_CHECK(CSR_RESULT(rc) == CSR_SUCCESS)) { TEST_MSG("%s\n", value.data); FREE(&value.data); return false; } mutt_buffer_reset(&value); rc = cs_str_initial_set(cs, "Cherry", "*****@*****.**", &value); if (!TEST_CHECK(CSR_RESULT(rc) == CSR_SUCCESS)) { TEST_MSG("%s\n", value.data); FREE(&value.data); return false; } mutt_buffer_reset(&value); rc = cs_str_initial_get(cs, "Cherry", &value); if (!TEST_CHECK(CSR_RESULT(rc) == CSR_SUCCESS)) { TEST_MSG("%s\n", value.data); FREE(&value.data); return false; } TEST_MSG("Cherry = '%s'\n", VarCherry ? VarCherry->mailbox : ""); TEST_MSG("Cherry's initial value is '%s'\n", NONULL(value.data)); FREE(&value.data); log_line(__func__); return true; }
int mutt_parse_score (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) { SCORE *ptr, *last; char *pattern, *pc; struct pattern_t *pat; mutt_extract_token (buf, s, 0); if (!MoreArgs (s)) { strfcpy (err->data, _("score: too few arguments"), err->dsize); return (-1); } pattern = buf->data; mutt_buffer_init (buf); mutt_extract_token (buf, s, 0); if (MoreArgs (s)) { FREE (&pattern); strfcpy (err->data, _("score: too many arguments"), err->dsize); return (-1); } /* look for an existing entry and update the value, else add it to the end of the list */ for (ptr = Score, last = NULL; ptr; last = ptr, ptr = ptr->next) if (mutt_strcmp (pattern, ptr->str) == 0) break; if (!ptr) { if ((pat = mutt_pattern_comp (pattern, 0, err)) == NULL) { FREE (&pattern); return (-1); } ptr = safe_calloc (1, sizeof (SCORE)); if (last) last->next = ptr; else Score = ptr; ptr->pat = pat; ptr->str = pattern; } else /* 'buf' arg was cleared and 'pattern' holds the only reference; * as here 'ptr' != NULL -> update the value only in which case * ptr->str already has the string, so pattern should be freed. */ FREE (&pattern); pc = buf->data; if (*pc == '=') { ptr->exact = 1; pc++; } if (mutt_atoi (pc, &ptr->val) < 0) { FREE (&pattern); strfcpy (err->data, _("Error: score: invalid number"), err->dsize); return (-1); } set_option (OPTNEEDRESCORE); return 0; }