int mail_set_attachment_keywords(struct mail *mail) { int ret; const struct mail_storage_settings *mail_set = mail_storage_get_settings(mailbox_get_storage(mail->box)); const char *const keyword_has_attachment[] = { MAIL_KEYWORD_HAS_ATTACHMENT, NULL, }; const char *const keyword_has_no_attachment[] = { MAIL_KEYWORD_HAS_NO_ATTACHMENT, NULL }; struct message_part_attachment_settings set = { .content_type_filter = mail_set->parsed_mail_attachment_content_type_filter, .exclude_inlined = mail_set->parsed_mail_attachment_exclude_inlined, }; struct mail_keywords *kw_has = NULL, *kw_has_not = NULL; /* walk all parts and see if there is an attachment */ struct message_part *parts; if (mail_get_parts(mail, &parts) < 0) { mail_set_critical(mail, "Failed to add attachment keywords: " "mail_get_parts() failed: %s", mail_storage_get_last_internal_error(mail->box->storage, NULL)); ret = -1; } else if (parts->data == NULL && mail_parse_parts(mail, &parts) < 0) { ret = -1; } else if (mailbox_keywords_create(mail->box, keyword_has_attachment, &kw_has) < 0 || mailbox_keywords_create(mail->box, keyword_has_no_attachment, &kw_has_not) < 0) { mail_set_critical(mail, "Failed to add attachment keywords: " "mailbox_keywords_create(%s) failed: %s", mailbox_get_vname(mail->box), mail_storage_get_last_internal_error(mail->box->storage, NULL)); ret = -1; } else { bool has_attachment = mail_message_has_attachment(parts, &set); /* make sure only one of the keywords gets set */ mail_update_keywords(mail, MODIFY_REMOVE, has_attachment ? kw_has_not : kw_has); mail_update_keywords(mail, MODIFY_ADD, has_attachment ? kw_has : kw_has_not); ret = has_attachment ? 1 : 0; } if (kw_has != NULL) mailbox_keywords_unref(&kw_has); if (kw_has_not != NULL) mailbox_keywords_unref(&kw_has_not); return ret; }
static int cmd_flags_run_box(struct flags_cmd_context *ctx, const struct mailbox_info *info) { struct doveadm_mail_iter *iter; struct mailbox *box; struct mail *mail; struct mail_keywords *kw = NULL; if (doveadm_mail_iter_init(&ctx->ctx, info, ctx->ctx.search_args, 0, NULL, &iter) < 0) return -1; box = doveadm_mail_iter_get_mailbox(iter); if (ctx->keywords != NULL) { if (mailbox_keywords_create(box, ctx->keywords, &kw) < 0) { i_error("Invalid keywords: %s", mailbox_get_last_error(box, NULL)); (void)doveadm_mail_iter_deinit(&iter); ctx->ctx.exit_code = DOVEADM_EX_NOTPOSSIBLE; return -1; } } while (doveadm_mail_iter_next(iter, &mail)) { mail_update_flags(mail, ctx->modify_type, ctx->flags); if (kw != NULL) mail_update_keywords(mail, ctx->modify_type, kw); } if (kw != NULL) mailbox_keywords_unref(&kw); return doveadm_mail_iter_deinit(&iter); }
bool cmd_store(struct client_command_context *cmd) { struct client *client = cmd->client; const struct imap_arg *args; struct mail_search_args *search_args; struct mail_search_context *search_ctx; struct mailbox_transaction_context *t; struct mail *mail; struct imap_store_context ctx; ARRAY_TYPE(seq_range) modified_set, uids; enum mailbox_transaction_flags flags = 0; enum imap_sync_flags imap_sync_flags = 0; const char *set, *reply, *tagged_reply; string_t *str; int ret; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; if (!imap_arg_get_atom(args, &set)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } ret = imap_search_get_seqset(cmd, set, cmd->uid, &search_args); if (ret <= 0) return ret < 0; memset(&ctx, 0, sizeof(ctx)); ctx.cmd = cmd; if (!store_parse_args(&ctx, ++args)) { mail_search_args_unref(&search_args); return TRUE; } if (client->mailbox_examined) { mail_search_args_unref(&search_args); if (ctx.max_modseq < (uint64_t)-1) reply = "NO CONDSTORE failed: Mailbox is read-only."; else reply = "OK Store ignored with read-only mailbox."; return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST | (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), 0, reply); } if (ctx.silent) flags |= MAILBOX_TRANSACTION_FLAG_HIDE; if (ctx.max_modseq < (uint64_t)-1) { /* update modseqs so we can check them early */ flags |= MAILBOX_TRANSACTION_FLAG_REFRESH; } t = mailbox_transaction_begin(client->mailbox, flags); search_ctx = mailbox_search_init(t, search_args, NULL, MAIL_FETCH_FLAGS, NULL); mail_search_args_unref(&search_args); i_array_init(&modified_set, 64); if (ctx.max_modseq < (uint32_t)-1) { /* STORE UNCHANGEDSINCE is being used */ mailbox_transaction_set_max_modseq(t, ctx.max_modseq, &modified_set); } while (mailbox_search_next(search_ctx, &mail)) { if (ctx.max_modseq < (uint64_t)-1) { /* check early so there's less work for transaction commit if something has to be cancelled */ if (mail_get_modseq(mail) > ctx.max_modseq) { seq_range_array_add(&modified_set, mail->seq); continue; } } if (ctx.modify_type == MODIFY_REPLACE || ctx.flags != 0) mail_update_flags(mail, ctx.modify_type, ctx.flags); if (ctx.modify_type == MODIFY_REPLACE || ctx.keywords != NULL) { mail_update_keywords(mail, ctx.modify_type, ctx.keywords); } } if (ctx.keywords != NULL) mailbox_keywords_unref(&ctx.keywords); ret = mailbox_search_deinit(&search_ctx); if (ret < 0) mailbox_transaction_rollback(&t); else ret = mailbox_transaction_commit(&t); if (ret < 0) { array_free(&modified_set); client_send_box_error(cmd, client->mailbox); return TRUE; } if (array_count(&modified_set) == 0) tagged_reply = "OK Store completed."; else { if (cmd->uid) { i_array_init(&uids, array_count(&modified_set)*2); mailbox_get_uid_range(client->mailbox, &modified_set, &uids); array_free(&modified_set); modified_set = uids; } str = str_new(cmd->pool, 256); str_append(str, "OK [MODIFIED "); imap_write_seq_range(str, &modified_set); str_append(str, "] Conditional store failed."); tagged_reply = str_c(str); } array_free(&modified_set); /* With UID STORE we have to return UID for the flags as well. Unfortunately we don't have the ability to separate those flag changes that were caused by UID STORE and those that came externally, so we'll just send the UID for all flag changes that we see. */ if (cmd->uid && (!ctx.silent || (client->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0)) imap_sync_flags |= IMAP_SYNC_FLAG_SEND_UID; return cmd_sync(cmd, (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), imap_sync_flags, tagged_reply); }
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; } }