static void imapc_sync_index_flags(struct imapc_sync_context *ctx, const struct mail_index_sync_rec *sync_rec) { string_t *str = t_str_new(128); i_assert(sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS); if (sync_rec->add_flags != 0) { i_assert((sync_rec->add_flags & MAIL_RECENT) == 0); str_printfa(str, "UID STORE %u:%u +FLAGS (", sync_rec->uid1, sync_rec->uid2); imap_write_flags(str, sync_rec->add_flags, NULL); str_append_c(str, ')'); imapc_sync_cmd(ctx, str_c(str)); } if (sync_rec->remove_flags != 0) { i_assert((sync_rec->remove_flags & MAIL_RECENT) == 0); str_truncate(str, 0); str_printfa(str, "UID STORE %u:%u -FLAGS (", sync_rec->uid1, sync_rec->uid2); imap_write_flags(str, sync_rec->remove_flags, NULL); str_append_c(str, ')'); imapc_sync_cmd(ctx, str_c(str)); } }
void client_send_mailbox_flags(struct client *client, bool selecting) { struct mailbox_status status; unsigned int count = array_count(client->keywords.names); const char *const *keywords; string_t *str; if (!selecting && count == client->keywords.announce_count) { /* no changes to keywords and we're not selecting a mailbox */ return; } client->keywords.announce_count = count; mailbox_get_open_status(client->mailbox, STATUS_PERMANENT_FLAGS, &status); keywords = count == 0 ? NULL : array_idx(client->keywords.names, 0); str = t_str_new(128); str_append(str, "* FLAGS ("); imap_write_flags(str, MAIL_FLAGS_NONRECENT, keywords); str_append_c(str, ')'); client_send_line(client, str_c(str)); if (!status.permanent_keywords) keywords = NULL; str_truncate(str, 0); str_append(str, "* OK [PERMANENTFLAGS ("); imap_write_flags(str, status.permanent_flags, keywords); if (status.allow_new_keywords) { if (status.permanent_flags != 0 || keywords != NULL) str_append_c(str, ' '); str_append(str, "\\*"); } str_append(str, ")] "); if (mailbox_is_readonly(client->mailbox)) str_append(str, "Read-only mailbox."); else str_append(str, "Flags permitted."); client_send_line(client, str_c(str)); }
bool mail_search_arg_to_imap(string_t *dest, const struct mail_search_arg *arg, const char **error_r) { unsigned int start_pos; if (arg->match_not) str_append(dest, "NOT "); start_pos = str_len(dest); switch (arg->type) { case SEARCH_OR: if (!mail_search_subargs_to_imap(dest, arg->value.subargs, "OR ", error_r)) return FALSE; break; case SEARCH_SUB: if (!mail_search_subargs_to_imap(dest, arg->value.subargs, "", error_r)) return FALSE; break; case SEARCH_ALL: str_append(dest, "ALL"); break; case SEARCH_SEQSET: imap_write_seq_range(dest, &arg->value.seqset); break; case SEARCH_UIDSET: str_append(dest, "UID "); imap_write_seq_range(dest, &arg->value.seqset); break; case SEARCH_FLAGS: i_assert((arg->value.flags & MAIL_FLAGS_MASK) != 0); str_append_c(dest, '('); if ((arg->value.flags & MAIL_ANSWERED) != 0) str_append(dest, "ANSWERED "); if ((arg->value.flags & MAIL_FLAGGED) != 0) str_append(dest, "FLAGGED "); if ((arg->value.flags & MAIL_DELETED) != 0) str_append(dest, "DELETED "); if ((arg->value.flags & MAIL_SEEN) != 0) str_append(dest, "SEEN "); if ((arg->value.flags & MAIL_DRAFT) != 0) str_append(dest, "DRAFT "); if ((arg->value.flags & MAIL_RECENT) != 0) str_append(dest, "RECENT "); str_truncate(dest, str_len(dest)-1); str_append_c(dest, ')'); break; case SEARCH_KEYWORDS: { const struct mail_keywords *kw = arg->value.keywords; const ARRAY_TYPE(keywords) *names_arr; const char *const *namep; unsigned int i; if (kw == NULL) { /* uninitialized */ str_printfa(dest, "KEYWORD %s", arg->value.str); break; } names_arr = mail_index_get_keywords(kw->index); str_append_c(dest, '('); for (i = 0; i < kw->count; i++) { namep = array_idx(names_arr, kw->idx[i]); if (i > 0) str_append_c(dest, ' '); str_printfa(dest, "KEYWORD %s", *namep); } str_append_c(dest, ')'); break; } case SEARCH_BEFORE: switch (arg->value.date_type) { case MAIL_SEARCH_DATE_TYPE_SENT: str_append(dest, "SENTBEFORE"); break; case MAIL_SEARCH_DATE_TYPE_RECEIVED: str_append(dest, "BEFORE"); break; case MAIL_SEARCH_DATE_TYPE_SAVED: str_append(dest, "X-SAVEDBEFORE"); break; } if (mail_search_arg_to_imap_date(dest, arg)) ; else if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_RECEIVED || arg->value.time > ioloop_time) { *error_r = t_strdup_printf( "SEARCH_BEFORE can't be written as IMAP for timestamp %ld (type=%d, use_tz=%d)", (long)arg->value.time, arg->value.date_type, (arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 0); return FALSE; } else { str_truncate(dest, start_pos); str_printfa(dest, "OLDER %u", (unsigned int)(ioloop_time - arg->value.time + 1)); } break; case SEARCH_ON: switch (arg->value.date_type) { case MAIL_SEARCH_DATE_TYPE_SENT: str_append(dest, "SENTON"); break; case MAIL_SEARCH_DATE_TYPE_RECEIVED: str_append(dest, "ON"); break; case MAIL_SEARCH_DATE_TYPE_SAVED: str_append(dest, "X-SAVEDON"); break; } if (!mail_search_arg_to_imap_date(dest, arg)) { *error_r = t_strdup_printf( "SEARCH_ON can't be written as IMAP for timestamp %ld (type=%d, use_tz=%d)", (long)arg->value.time, arg->value.date_type, (arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 0); return FALSE; } break; case SEARCH_SINCE: switch (arg->value.date_type) { case MAIL_SEARCH_DATE_TYPE_SENT: str_append(dest, "SENTSINCE"); break; case MAIL_SEARCH_DATE_TYPE_RECEIVED: str_append(dest, "SINCE"); break; case MAIL_SEARCH_DATE_TYPE_SAVED: str_append(dest, "X-SAVEDSINCE"); break; } if (mail_search_arg_to_imap_date(dest, arg)) ; else if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_RECEIVED || arg->value.time >= ioloop_time) { *error_r = t_strdup_printf( "SEARCH_SINCE can't be written as IMAP for timestamp %ld (type=%d, use_tz=%d)", (long)arg->value.time, arg->value.date_type, (arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 0); return FALSE; } else { str_truncate(dest, start_pos); str_printfa(dest, "YOUNGER %u", (unsigned int)(ioloop_time - arg->value.time)); } break; case SEARCH_SMALLER: str_printfa(dest, "SMALLER %llu", (unsigned long long)arg->value.size); break; case SEARCH_LARGER: str_printfa(dest, "LARGER %llu", (unsigned long long)arg->value.size); break; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: if (strcasecmp(arg->hdr_field_name, "From") == 0 || strcasecmp(arg->hdr_field_name, "To") == 0 || strcasecmp(arg->hdr_field_name, "Cc") == 0 || strcasecmp(arg->hdr_field_name, "Bcc") == 0 || strcasecmp(arg->hdr_field_name, "Subject") == 0) str_append(dest, t_str_ucase(arg->hdr_field_name)); else { str_append(dest, "HEADER "); imap_append_astring(dest, arg->hdr_field_name); } str_append_c(dest, ' '); imap_append_astring(dest, arg->value.str); break; case SEARCH_BODY: str_append(dest, "BODY "); imap_append_astring(dest, arg->value.str); break; case SEARCH_TEXT: str_append(dest, "TEXT "); imap_append_astring(dest, arg->value.str); break; /* extensions */ case SEARCH_MODSEQ: { bool extended_output = FALSE; str_append(dest, "MODSEQ "); if (arg->value.str != NULL) { str_printfa(dest, "/flags/%s", arg->value.str); extended_output = TRUE; } else if (arg->value.flags != 0) { str_append(dest, "/flags/"); imap_write_flags(dest, arg->value.flags, NULL); extended_output = TRUE; } if (extended_output) { str_append_c(dest, ' '); switch (arg->value.modseq->type) { case MAIL_SEARCH_MODSEQ_TYPE_ANY: str_append(dest, "all"); break; case MAIL_SEARCH_MODSEQ_TYPE_PRIVATE: str_append(dest, "priv"); break; case MAIL_SEARCH_MODSEQ_TYPE_SHARED: str_append(dest, "shared"); break; } str_append_c(dest, ' '); } str_printfa(dest, "%llu", (unsigned long long)arg->value.modseq->modseq); break; } case SEARCH_INTHREAD: str_append(dest, "INTHREAD "); imap_append_astring(dest, mail_thread_type_to_str(arg->value.thread_type)); str_append_c(dest, ' '); if (!mail_search_subargs_to_imap(dest, arg->value.subargs, "", error_r)) return FALSE; break; case SEARCH_GUID: str_append(dest, "X-GUID "); imap_append_astring(dest, arg->value.str); break; case SEARCH_MAILBOX: *error_r = "SEARCH_MAILBOX can't be written as IMAP"; return FALSE; case SEARCH_MAILBOX_GUID: *error_r = "SEARCH_MAILBOX_GUID can't be written as IMAP"; return FALSE; case SEARCH_MAILBOX_GLOB: str_append(dest, "X-MAILBOX "); imap_append_astring(dest, arg->value.str); break; case SEARCH_REAL_UID: str_append(dest, "X-REAL-UID "); imap_write_seq_range(dest, &arg->value.seqset); break; } return TRUE; }
static void mail_log_append_mail_message_real(struct mail_log_mail_txn_context *ctx, struct mail *mail, enum mail_log_event event, const char *desc) { struct mail_log_user *muser = MAIL_LOG_USER_CONTEXT(mail->box->storage->user); struct mail_log_message *msg; string_t *text; uoff_t size; msg = p_new(ctx->pool, struct mail_log_message, 1); text = t_str_new(128); str_append(text, desc); str_append(text, ": "); if ((muser->fields & MAIL_LOG_FIELD_BOX) != 0) { mail_log_append_mailbox_name(text, mail); str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_UID) != 0) { if (event != MAIL_LOG_EVENT_SAVE && event != MAIL_LOG_EVENT_COPY) mail_log_append_uid(ctx, msg, text, mail->uid); else { /* with mbox mail->uid contains the uid, but handle this consistently with all mailbox formats */ mail_log_append_uid(ctx, msg, text, 0); } str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_MSGID) != 0) { mail_log_append_mail_header(text, mail, "msgid", "Message-ID"); str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_PSIZE) != 0) { if (mail_get_physical_size(mail, &size) == 0) str_printfa(text, "size=%"PRIuUOFF_T, size); else str_printfa(text, "size=error"); str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_VSIZE) != 0) { if (mail_get_virtual_size(mail, &size) == 0) str_printfa(text, "vsize=%"PRIuUOFF_T, size); else str_printfa(text, "vsize=error"); str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_FROM) != 0) { mail_log_append_mail_header(text, mail, "from", "From"); str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_SUBJECT) != 0) { mail_log_append_mail_header(text, mail, "subject", "Subject"); str_append(text, ", "); } if ((muser->fields & MAIL_LOG_FIELD_FLAGS) != 0) { str_printfa(text, "flags=("); imap_write_flags(text, mail_get_flags(mail), mail_get_keywords(mail)); str_append(text, "), "); } str_truncate(text, str_len(text)-2); msg->event = event; msg->text = p_strdup(ctx->pool, str_c(text)); DLLIST2_APPEND(&ctx->messages, &ctx->messages_tail, msg); }