static struct mailbox_node * imapc_list_update_tree(struct imapc_mailbox_list *list, struct mailbox_tree_context *tree, const struct imap_arg *args) { struct mailbox_node *node; const struct imap_arg *flags; const char *name, *flag; enum mailbox_info_flags info_flag, info_flags = 0; bool created; if (!imap_arg_get_list(&args[0], &flags) || args[1].type == IMAP_ARG_EOL || !imap_arg_get_astring(&args[2], &name)) return NULL; while (imap_arg_get_atom(flags, &flag)) { if (imap_list_flag_parse(flag, &info_flag)) info_flags |= info_flag; flags++; } T_BEGIN { const char *vname = imapc_list_to_vname(list, name); if ((info_flags & MAILBOX_NONEXISTENT) != 0) node = mailbox_tree_lookup(tree, vname); else node = mailbox_tree_get(tree, vname, &created); } T_END; if (node != NULL) node->flags = info_flags; return node; }
static bool cmd_append_args_can_stop(struct cmd_append_context *ctx, const struct imap_arg *args, bool *last_literal_r) { const struct imap_arg *cat_list; *last_literal_r = FALSE; if (args->type == IMAP_ARG_EOL) return TRUE; /* [(flags)] ["internal date"] <message literal> | CATENATE (..) */ if (args->type == IMAP_ARG_LIST) args++; if (args->type == IMAP_ARG_STRING) args++; if (args->type == IMAP_ARG_LITERAL_SIZE || args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC) return TRUE; if (imap_arg_atom_equals(args, "CATENATE") && imap_arg_get_list(&args[1], &cat_list)) { if (catenate_args_can_stop(ctx, cat_list)) return TRUE; *last_literal_r = TRUE; } return FALSE; }
const struct imap_arg * imap_arg_as_list(const struct imap_arg *arg) { const struct imap_arg *ret; if (!imap_arg_get_list(arg, &ret)) i_unreached(); return ret; }
static bool store_parse_args(struct imap_store_context *ctx, const struct imap_arg *args) { struct client_command_context *cmd = ctx->cmd; const struct imap_arg *list_args; const char *type; const char *const *keywords_list = NULL; ctx->max_modseq = (uint64_t)-1; if (imap_arg_get_list(args, &list_args)) { if (!store_parse_modifiers(ctx, list_args)) return FALSE; args++; } if (!imap_arg_get_astring(args, &type) || !get_modify_type(ctx, type)) { client_send_command_error(cmd, "Invalid arguments."); return FALSE; } args++; if (imap_arg_get_list(args, &list_args)) { if (!client_parse_mail_flags(cmd, list_args, &ctx->flags, &keywords_list)) return FALSE; } else { if (!client_parse_mail_flags(cmd, args, &ctx->flags, &keywords_list)) return FALSE; } if (keywords_list != NULL || ctx->modify_type == MODIFY_REPLACE) { if (mailbox_keywords_create(cmd->client->mailbox, keywords_list, &ctx->keywords) < 0) { /* invalid keywords */ client_send_box_error(cmd, cmd->client->mailbox); return FALSE; } } return TRUE; }
bool cmd_sort(struct client_command_context *cmd) { struct imap_search_context *ctx; struct mail_search_args *sargs; enum mail_sort_type sort_program[MAX_SORT_PROGRAM_SIZE]; const struct imap_arg *args, *list_args; const char *charset; int ret; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; ctx = p_new(cmd->pool, struct imap_search_context, 1); ctx->cmd = cmd; if ((ret = cmd_search_parse_return_if_found(ctx, &args)) <= 0) { /* error / waiting for unambiguity */ return ret < 0; } /* sort program */ if (!imap_arg_get_list(args, &list_args)) { client_send_command_error(cmd, "Invalid sort argument."); return TRUE; } if (get_sort_program(cmd, list_args, sort_program) < 0) return TRUE; args++; /* charset */ if (!imap_arg_get_astring(args, &charset)) { client_send_command_error(cmd, "Invalid charset argument."); return TRUE; } args++; ret = imap_search_args_build(cmd, args, charset, &sargs); if (ret <= 0) return ret < 0; return imap_search_start(ctx, sargs, sort_program); }
bool cmd_status(struct client_command_context *cmd) { struct client *client = cmd->client; const struct imap_arg *args, *list_args; struct imap_status_items items; struct imap_status_result result; struct mail_namespace *ns; const char *mailbox, *orig_mailbox; bool selected_mailbox; /* <mailbox> <status items> */ if (!client_read_args(cmd, 2, 0, &args)) return FALSE; if (!imap_arg_get_astring(&args[0], &mailbox) || !imap_arg_get_list(&args[1], &list_args)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } /* get the items client wants */ if (imap_status_parse_items(cmd, list_args, &items) < 0) return TRUE; orig_mailbox = mailbox; ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; selected_mailbox = client->mailbox != NULL && mailbox_equals(client->mailbox, ns, mailbox); if (imap_status_get(cmd, ns, mailbox, &items, &result) < 0) { client_send_tagline(cmd, result.errstr); return TRUE; } imap_status_send(client, orig_mailbox, &items, &result); if (!selected_mailbox) client_send_tagline(cmd, "OK Status completed."); else { client_send_tagline(cmd, "OK ["IMAP_RESP_CODE_CLIENTBUG"] " "Status on selected mailbox completed."); } return TRUE; }
const char *imap_id_args_get_log_reply(const struct imap_arg *args, const char *settings) { const char *const *keys, *key, *value; string_t *reply; bool log_all; if (settings == NULL || *settings == '\0') return NULL; if (!imap_arg_get_list(args, &args)) return NULL; log_all = strcmp(settings, "*") == 0; reply = t_str_new(256); keys = t_strsplit_spaces(settings, " "); while (!IMAP_ARG_IS_EOL(&args[0]) && !IMAP_ARG_IS_EOL(&args[1])) { if (!imap_arg_get_string(args, &key)) { /* broken input */ args += 2; continue; } args++; if (strlen(key) > 30) { /* broken: ID spec requires fields to be max. 30 octets */ args++; continue; } if (log_all || str_array_icase_find(keys, key)) { if (!imap_arg_get_nstring(args, &value)) value = ""; else if (value == NULL) value = "NIL"; if (str_len(reply) > 0) str_append(reply, ", "); str_append(reply, str_sanitize(key, 30)); str_append_c(reply, '='); str_append(reply, str_sanitize(value, 80)); } args++; } return str_len(reply) == 0 ? NULL : str_c(reply); }
static bool cmd_setquota(struct client_command_context *cmd) { struct quota_root *root; const struct imap_arg *args, *list_args; const char *root_name, *name, *value_str, *error; uint64_t value; /* <quota root> <resource limits> */ if (!client_read_args(cmd, 2, 0, &args)) return FALSE; if (!imap_arg_get_astring(&args[0], &root_name) || !imap_arg_get_list(&args[1], &list_args)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } root = quota_root_lookup(cmd->client->user, root_name); if (root == NULL) { client_send_tagline(cmd, "NO Quota root doesn't exist."); return TRUE; } for (; !IMAP_ARG_IS_EOL(list_args); list_args += 2) { if (!imap_arg_get_atom(&list_args[0], &name) || !imap_arg_get_atom(&list_args[1], &value_str) || str_to_uint64(value_str, &value) < 0) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } if (quota_set_resource(root, name, value, &error) < 0) { client_send_command_error(cmd, error); return TRUE; } } client_send_tagline(cmd, "OK Setquota completed."); return TRUE; }
bool cmd_fetch(struct client_command_context *cmd) { struct client *client = cmd->client; struct imap_fetch_context *ctx; const struct imap_arg *args, *next_arg, *list_arg; struct mail_search_args *search_args; struct imap_fetch_qresync_args qresync_args; const char *messageset; bool send_vanished = FALSE; int ret; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; /* <messageset> <field(s)> [(modifiers)] */ if (!imap_arg_get_atom(&args[0], &messageset) || (args[1].type != IMAP_ARG_LIST && args[1].type != IMAP_ARG_ATOM) || (!IMAP_ARG_IS_EOL(&args[2]) && args[2].type != IMAP_ARG_LIST)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } /* UID FETCH VANISHED needs the uidset, so convert it to sequence set later */ ret = imap_search_get_anyset(cmd, messageset, cmd->uid, &search_args); if (ret <= 0) return ret < 0; ctx = imap_fetch_alloc(client, cmd->pool); if (!fetch_parse_args(ctx, cmd, &args[1], &next_arg) || (imap_arg_get_list(next_arg, &list_arg) && !fetch_parse_modifiers(ctx, cmd, search_args, list_arg, &send_vanished))) { imap_fetch_free(&ctx); mail_search_args_unref(&search_args); return TRUE; } if (send_vanished) { memset(&qresync_args, 0, sizeof(qresync_args)); if (imap_fetch_send_vanished(client, client->mailbox, search_args, &qresync_args) < 0) { mail_search_args_unref(&search_args); return cmd_fetch_finish(ctx, cmd); } } imap_fetch_begin(ctx, client->mailbox, search_args); mail_search_args_unref(&search_args); if (imap_fetch_more(ctx, cmd) == 0) { /* unfinished */ cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; cmd->func = cmd_fetch_continue; cmd->context = ctx; return FALSE; } return cmd_fetch_finish(ctx, cmd); }
static int cmd_append_handle_args(struct client_command_context *cmd, const struct imap_arg *args, bool *nonsync_r) { struct client *client = cmd->client; struct cmd_append_context *ctx = cmd->context; const struct imap_arg *flags_list; const struct imap_arg *cat_list = NULL; enum mail_flags flags; const char *const *keywords_list; struct mail_keywords *keywords; struct istream *input; const char *internal_date_str; time_t internal_date; int ret, timezone_offset; bool valid; /* [<flags>] */ if (!imap_arg_get_list(args, &flags_list)) flags_list = NULL; else args++; /* [<internal date>] */ if (args->type != IMAP_ARG_STRING) internal_date_str = NULL; else { internal_date_str = imap_arg_as_astring(args); args++; } /* <message literal> | CATENATE (..) */ valid = FALSE; *nonsync_r = FALSE; ctx->catenate = FALSE; if (imap_arg_get_literal_size(args, &ctx->literal_size)) { *nonsync_r = args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC; ctx->binary_input = args->literal8; valid = TRUE; } else if (!imap_arg_atom_equals(args, "CATENATE")) { /* invalid */ } else if (!imap_arg_get_list(++args, &cat_list)) { /* invalid */ } else { valid = TRUE; ctx->catenate = TRUE; /* We'll do BINARY conversion only if the CATENATE's first part is a literal8. If it doesn't and a literal8 is seen later we'll abort the append with UNKNOWN-CTE. */ ctx->binary_input = imap_arg_atom_equals(&cat_list[0], "TEXT") && cat_list[1].literal8; } if (!IMAP_ARG_IS_EOL(&args[1])) valid = FALSE; if (!valid) { client->input_skip_line = TRUE; if (!ctx->failed) client_send_command_error(cmd, "Invalid arguments."); return -1; } if (flags_list == NULL || ctx->failed) { flags = 0; keywords = NULL; } else { if (!client_parse_mail_flags(cmd, flags_list, &flags, &keywords_list)) return -1; if (keywords_list == NULL) keywords = NULL; else if (mailbox_keywords_create(ctx->box, keywords_list, &keywords) < 0) { /* invalid keywords - delay failure */ client_send_box_error(cmd, ctx->box); ctx->failed = TRUE; keywords = NULL; } } if (internal_date_str == NULL || ctx->failed) { /* no time given, default to now. */ internal_date = (time_t)-1; timezone_offset = 0; } else if (!imap_parse_datetime(internal_date_str, &internal_date, &timezone_offset)) { client_send_command_error(cmd, "Invalid internal date."); if (keywords != NULL) mailbox_keywords_unref(&keywords); return -1; } if (internal_date != (time_t)-1 && internal_date > ioloop_time + INTERNALDATE_MAX_FUTURE_SECS) { /* the client specified a time in the future, set it to now. */ internal_date = (time_t)-1; timezone_offset = 0; } if (cat_list != NULL) { ctx->cat_msg_size = 0; ctx->input = i_stream_create_chain(&ctx->catchain); } else { if (ctx->literal_size == 0) { /* no message data, abort */ if (!ctx->failed) { client_send_tagline(cmd, "NO Can't save a zero byte message."); ctx->failed = TRUE; } if (!*nonsync_r) { if (keywords != NULL) mailbox_keywords_unref(&keywords); return -1; } /* {0+} used. although there isn't any point in using MULTIAPPEND here and adding more messages, it is technically valid so we'll continue parsing.. */ } ctx->litinput = i_stream_create_limit(client->input, ctx->literal_size); ctx->input = ctx->litinput; i_stream_ref(ctx->input); } if (ctx->binary_input) { input = i_stream_create_binary_converter(ctx->input); i_stream_unref(&ctx->input); ctx->input = input; } if (!ctx->failed) { /* save the mail */ ctx->save_ctx = mailbox_save_alloc(ctx->t); mailbox_save_set_flags(ctx->save_ctx, flags, keywords); mailbox_save_set_received_date(ctx->save_ctx, internal_date, timezone_offset); if (mailbox_save_begin(&ctx->save_ctx, ctx->input) < 0) { /* save initialization failed */ client_send_box_error(cmd, ctx->box); ctx->failed = TRUE; } } if (keywords != NULL) mailbox_keywords_unref(&keywords); ctx->count++; if (cat_list == NULL) { /* normal APPEND */ return 1; } else if (cat_list->type == IMAP_ARG_EOL) { /* zero parts */ if (!ctx->failed) client_send_command_error(cmd, "Empty CATENATE list."); client->input_skip_line = TRUE; return -1; } else if ((ret = cmd_append_catenate(cmd, cat_list, nonsync_r)) < 0) { /* invalid parameters, abort immediately */ return -1; } else if (ret == 0) { /* CATENATE consisted only of URLs */ return 0; } else { /* TEXT part found from CATENATE */ return 1; } }
static bool cmd_xapplepushservice(struct client_command_context *cmd) { /* * Parse arguments. We expect four key value pairs. We only take * those that we understand for version 2 of this extension. * * TODO: We are ignoring the mailboxes parameter for now and just * default to INBOX always. */ const struct imap_arg *args; const char *arg_key, *arg_val; const char *aps_version = NULL, *aps_account_id = NULL, *aps_device_token = NULL, *aps_subtopic = NULL; if (!client_read_args(cmd, 0, 0, &args)) { client_send_command_error(cmd, "Invalid arguments."); return FALSE; } for (int i = 0; i < 4; i++) { if (!imap_arg_get_astring(&args[i*2+0], &arg_key)) { client_send_command_error(cmd, "Invalid arguments."); return FALSE; } if (!imap_arg_get_astring(&args[i*2+1], &arg_val)) { client_send_command_error(cmd, "Invalid arguments."); return FALSE; } if (strcasecmp(arg_key, "aps-version") == 0) { aps_version = arg_val; } else if (strcasecmp(arg_key, "aps-account-id") == 0) { aps_account_id = arg_val; } else if (strcasecmp(arg_key, "aps-device-token") == 0) { aps_device_token = arg_val; } else if (strcasecmp(arg_key, "aps-subtopic") == 0) { aps_subtopic = arg_val; } } /* * Check if this is a version we expect */ if (!aps_version || (strcmp(aps_version, "1") != 0 && strcmp(aps_version, "2") != 0)) { client_send_command_error(cmd, "Unknown aps-version."); return FALSE; } /* * If this is version 2 then also need to grab the mailboxes, which * is a list of mailbox names. */ const struct imap_arg *mailboxes = NULL; if (strcmp(aps_version, "2") == 0) { if (!imap_arg_get_astring(&args[8], &arg_key)) { client_send_command_error(cmd, "Invalid arguments."); return FALSE; } if (strcmp(arg_key, "mailboxes") != 0) { client_send_command_error(cmd, "Invalid arguments. (Expected mailboxes)"); return FALSE; } if (!imap_arg_get_list(&args[9], &mailboxes)) { client_send_command_error(cmd, "Invalid arguments."); return FALSE; } } /* * Check if all of the parameters are there. */ if (!aps_account_id || strlen(aps_account_id) == 0) { client_send_command_error(cmd, "Incomplete or empty aps-account-id parameter."); return FALSE; } if (!aps_device_token || strlen(aps_device_token) == 0) { client_send_command_error(cmd, "Incomplete or empty aps-device-token parameter."); return FALSE; } if (!aps_subtopic || strlen(aps_subtopic) == 0) { client_send_command_error(cmd, "Incomplete or empty aps-subtopic parameter."); return FALSE; } /* * Forward to the helper daemon. The helper will return the * aps-topic, which in reality is the subject of the certificate. I * think it is only used to make sure that the binding between the * client and the APS server and the IMAP server stays current. */ struct client *client = cmd->client; struct mail_user *user = client->user; const char *aps_topic = mail_user_plugin_getenv(user, "xaps_topic"); if (xaps_register(aps_account_id, aps_device_token, aps_subtopic, user->username, mailboxes) != 0) { client_send_command_error(cmd, "Registration failed."); i_info("Registration failed."); return FALSE; } /* * Return success. We assume that aps_version and aps_topic do not * contain anything that needs to be escaped. */ client_send_line(cmd->client, t_strdup_printf("* XAPPLEPUSHSERVICE aps-version \"%s\" aps-topic \"%s\"", aps_version, aps_topic)); client_send_tagline(cmd, "OK XAPPLEPUSHSERVICE Registration successful."); return TRUE; }