static bool fetch_parse_args(struct imap_fetch_context *ctx, struct client_command_context *cmd, const struct imap_arg *arg, const struct imap_arg **next_arg_r) { const char *str, *const *macro; if (cmd->uid) { if (!imap_fetch_cmd_init_handler(ctx, cmd, "UID", &arg)) return FALSE; } if (imap_arg_get_atom(arg, &str)) { str = t_str_ucase(str); arg++; /* handle macros first */ if (strcmp(str, "ALL") == 0) macro = all_macro; else if (strcmp(str, "FAST") == 0) macro = fast_macro; else if (strcmp(str, "FULL") == 0) macro = full_macro; else { macro = NULL; if (!imap_fetch_cmd_init_handler(ctx, cmd, str, &arg)) return FALSE; } if (macro != NULL) { while (*macro != NULL) { if (!imap_fetch_cmd_init_handler(ctx, cmd, *macro, &arg)) return FALSE; macro++; } } *next_arg_r = arg; } else { *next_arg_r = arg + 1; arg = imap_arg_as_list(arg); if (IMAP_ARG_IS_EOL(arg)) { client_send_command_error(cmd, "FETCH list is empty."); return FALSE; } while (imap_arg_get_atom(arg, &str)) { str = t_str_ucase(str); arg++; if (!imap_fetch_cmd_init_handler(ctx, cmd, str, &arg)) return FALSE; } if (!IMAP_ARG_IS_EOL(arg)) { client_send_command_error(cmd, "FETCH list contains non-atoms."); return FALSE; } } return TRUE; }
static int body_header_fields_parse(struct imap_fetch_init_context *ctx, struct imap_fetch_body_data *body, const char *prefix, const struct imap_arg *args, unsigned int args_count) { string_t *str; const char *value; size_t i; str = str_new(ctx->pool, 128); str_append(str, prefix); str_append(str, " ("); for (i = 0; i < args_count; i++) { if (!imap_arg_get_astring(&args[i], &value)) { ctx->error = "Invalid BODY[..] parameter: " "Header list contains non-strings"; return -1; } value = t_str_ucase(value); if (i != 0) str_append_c(str, ' '); if (args[i].type == IMAP_ARG_ATOM) str_append(str, value); else imap_append_quoted(str, value); } str_append_c(str, ')'); body->section = str_c(str); return 0; }
bool cmd_enable(struct client_command_context *cmd) { const struct imap_arg *args; const char *str; string_t *reply; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; reply = t_str_new(64); str_append(reply, "* ENABLED"); for (; !IMAP_ARG_IS_EOL(args); args++) { if (!imap_arg_get_atom(args, &str)) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } str = t_str_ucase(str); if (strcmp(str, "CONDSTORE") == 0) { client_enable(cmd->client, MAILBOX_FEATURE_CONDSTORE); str_append(reply, " CONDSTORE"); } else if (strcmp(str, "QRESYNC") == 0) { client_enable(cmd->client, MAILBOX_FEATURE_QRESYNC | MAILBOX_FEATURE_CONDSTORE); str_append(reply, " QRESYNC"); } } if (str_len(reply) > 9) client_send_line(cmd->client, str_c(reply)); client_send_tagline(cmd, "OK Enabled."); return TRUE; }
static void script_execute_finish(void) { const char *keys_str, *username, *const *keys, *value; string_t *reply = t_str_new(512); ssize_t ret; keys_str = getenv(ENV_USERDB_KEYS); if (keys_str == NULL) i_fatal(ENV_USERDB_KEYS" environment missing"); username = getenv("USER"); if (username == NULL) i_fatal("USER environment missing"); str_append(reply, username); for (keys = t_strsplit_spaces(keys_str, " "); *keys != NULL; keys++) { value = getenv(t_str_ucase(*keys)); if (value != NULL) { str_append_c(reply, '\t'); str_tabescape_write(reply, t_strconcat(t_str_lcase(*keys), "=", value, NULL)); } } str_append_c(reply, '\n'); ret = fd_send(SCRIPT_COMM_FD, STDOUT_FILENO, str_data(reply), str_len(reply)); if (ret < 0) i_fatal("fd_send() failed: %m"); else if (ret != (ssize_t)str_len(reply)) i_fatal("fd_send() sent partial output"); }
static bool fetch_parse_modifiers(struct imap_fetch_context *ctx, struct client_command_context *cmd, struct mail_search_args *search_args, const struct imap_arg *args, bool *send_vanished_r) { const char *name; *send_vanished_r = FALSE; while (!IMAP_ARG_IS_EOL(args)) { if (!imap_arg_get_atom(args, &name)) { client_send_command_error(cmd, "FETCH modifiers contain non-atoms."); return FALSE; } args++; if (!fetch_parse_modifier(ctx, cmd, search_args, t_str_ucase(name), &args, send_vanished_r)) return FALSE; } if (*send_vanished_r && (search_args->args->next == NULL || search_args->args->next->type != SEARCH_MODSEQ)) { client_send_command_error(cmd, "VANISHED used without CHANGEDSINCE"); return FALSE; } return TRUE; }
static struct mail_search_arg * imap_search_header(struct mail_search_build_context *ctx) { const char *hdr_name; /* <hdr-name> <string> */ if (mail_search_parse_string(ctx->parser, &hdr_name) < 0) return NULL; if (mail_search_build_get_utf8(ctx, hdr_name, &hdr_name) < 0) return NULL; return arg_new_header(ctx, SEARCH_HEADER, t_str_ucase(hdr_name)); }
int imap_status_parse_items(struct client_command_context *cmd, const struct imap_arg *args, struct imap_status_items *items_r) { const char *item; enum mailbox_status_items status = 0; enum mailbox_metadata_items metadata = 0; if (IMAP_ARG_IS_EOL(args)) { client_send_command_error(cmd, "Empty status list."); return -1; } memset(items_r, 0, sizeof(*items_r)); for (; !IMAP_ARG_IS_EOL(args); args++) { if (!imap_arg_get_atom(args, &item)) { /* list may contain only atoms */ client_send_command_error(cmd, "Status list contains non-atoms."); return -1; } item = t_str_ucase(item); if (strcmp(item, "MESSAGES") == 0) status |= STATUS_MESSAGES; else if (strcmp(item, "RECENT") == 0) status |= STATUS_RECENT; else if (strcmp(item, "UIDNEXT") == 0) status |= STATUS_UIDNEXT; else if (strcmp(item, "UIDVALIDITY") == 0) status |= STATUS_UIDVALIDITY; else if (strcmp(item, "UNSEEN") == 0) status |= STATUS_UNSEEN; else if (strcmp(item, "HIGHESTMODSEQ") == 0) status |= STATUS_HIGHESTMODSEQ; else if (strcmp(item, "X-SIZE") == 0) metadata |= MAILBOX_METADATA_VIRTUAL_SIZE; else if (strcmp(item, "X-GUID") == 0) metadata |= MAILBOX_METADATA_GUID; else { client_send_command_error(cmd, t_strconcat( "Invalid status item ", item, NULL)); return -1; } } items_r->status = status; items_r->metadata = metadata; return 0; }
static void env_put_extra_fields(const char *extra_fields) { const char *const *tmp; const char *key, *p; for (tmp = t_strsplit(extra_fields, "\t"); *tmp != NULL; tmp++) { key = t_str_ucase(t_strcut(*tmp, '=')); p = strchr(*tmp, '='); if (p == NULL) env_put(t_strconcat(key, "=1", NULL)); else env_put(t_strconcat(key, p, NULL)); } }
bool client_parse_mail_flags(struct client_command_context *cmd, const struct imap_arg *args, enum mail_flags *flags_r, const char *const **keywords_r) { const char *atom; enum mail_flags flag; ARRAY(const char *) keywords; *flags_r = 0; *keywords_r = NULL; p_array_init(&keywords, cmd->pool, 16); while (!IMAP_ARG_IS_EOL(args)) { if (!imap_arg_get_atom(args, &atom)) { client_send_command_error(cmd, "Flags list contains non-atoms."); return FALSE; } if (*atom == '\\') { /* system flag */ atom = t_str_ucase(atom); flag = imap_parse_system_flag(atom); if (flag != 0 && flag != MAIL_RECENT) *flags_r |= flag; else { client_send_command_error(cmd, t_strconcat( "Invalid system flag ", atom, NULL)); return FALSE; } } else { /* keyword validity checks are done by lib-storage */ array_append(&keywords, &atom, 1); } args++; } if (array_count(&keywords) == 0) *keywords_r = NULL; else { array_append_zero(&keywords); /* NULL-terminate */ *keywords_r = array_idx(&keywords, 0); } return TRUE; }
static int mail_search_build_key_int(struct mail_search_build_context *ctx, struct mail_search_arg *parent, struct mail_search_arg **arg_r) { struct mail_search_arg *sarg; struct mail_search_arg *old_parent = ctx->parent; const char *key; const struct mail_search_register_arg *reg_arg; mail_search_register_fallback_t *fallback; int ret; ctx->parent = parent; if ((ret = mail_search_parse_key(ctx->parser, &key)) <= 0) return ret; if (strcmp(key, MAIL_SEARCH_PARSER_KEY_LIST) == 0) { if (mail_search_build_list(ctx, &sarg) < 0) return -1; if (sarg->value.subargs == NULL) { ctx->_error = "No search parameters inside list"; return -1; } ctx->parent = old_parent; *arg_r = sarg; return 1; } key = t_str_ucase(key); reg_arg = mail_search_register_find(ctx->reg, key); if (reg_arg != NULL) sarg = reg_arg->build(ctx); else if (mail_search_register_get_fallback(ctx->reg, &fallback)) sarg = fallback(ctx, key); else { sarg = NULL; ctx->_error = p_strconcat(ctx->pool, "Unknown argument ", key, NULL); } ctx->parent = old_parent; *arg_r = sarg; return sarg == NULL ? -1 : 1; }
static void script_execute_finish(void) { const char *keys_str, *username, *const *keys, *value; string_t *reply = t_str_new(512); ssize_t ret; keys_str = getenv(ENV_USERDB_KEYS); if (keys_str == NULL) i_fatal(ENV_USERDB_KEYS" environment missing"); username = getenv("USER"); if (username == NULL) i_fatal("USER environment missing"); str_append(reply, username); for (keys = t_strsplit_spaces(keys_str, " "); *keys != NULL; keys++) { value = getenv(t_str_ucase(*keys)); if (value != NULL) { str_append_c(reply, '\t'); str_append_tabescaped(reply, t_strconcat(t_str_lcase(*keys), "=", value, NULL)); } } str_append_c(reply, '\n'); /* finish by sending the fd to the mail process */ ret = fd_send(SCRIPT_COMM_FD, STDOUT_FILENO, str_data(reply), str_len(reply)); if (ret == (ssize_t)str_len(reply)) { /* success */ } else { if (ret < 0) i_error("fd_send() failed: %m"); else i_error("fd_send() sent partial output"); /* exit with 0 even though we failed. non-0 exit just makes master log an unnecessary error. */ } }
void client_send_command_error(struct client_command_context *cmd, const char *msg) { struct client *client = cmd->client; const char *error, *cmd_name; bool fatal; if (msg == NULL) { msg = imap_parser_get_error(cmd->parser, &fatal); if (fatal) { client_disconnect_with_error(client, msg); return; } } if (cmd->tag == NULL) error = t_strconcat("BAD Error in IMAP tag: ", msg, NULL); else if (cmd->name == NULL) error = t_strconcat("BAD Error in IMAP command: ", msg, NULL); else { cmd_name = t_str_ucase(cmd->name); error = t_strconcat("BAD Error in IMAP command ", cmd_name, ": ", msg, NULL); } client_send_tagline(cmd, error); if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) { client_disconnect_with_error(client, "Too many invalid IMAP commands."); } cmd->param_error = TRUE; /* client_read_args() failures rely on this being set, so that the command processing is stopped even while command function returns FALSE. */ cmd->state = CLIENT_COMMAND_STATE_DONE; }
static void client_connected(struct master_service_connection *conn) { enum mail_storage_service_flags flags = MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS; string_t *instr, *keys; const char **args, *key, *value, *error, *version_line, *data_line; struct mail_storage_service_ctx *service_ctx; struct mail_storage_service_input input; struct mail_storage_service_user *user; char buf[1024]; unsigned int i, socket_count; int fd = -1; ssize_t ret; alarm(SCRIPT_LOGIN_READ_TIMEOUT_SECS); net_set_nonblock(conn->fd, FALSE); instr = t_str_new(1024); ret = fd_read(conn->fd, buf, sizeof(buf), &fd); while (ret > 0) { str_append_n(instr, buf, ret); if (buf[ret-1] == '\n' && strchr(str_c(instr), '\n')[1] != '\0') { str_truncate(instr, str_len(instr)-1); break; } ret = read(conn->fd, buf, sizeof(buf)); } version_line = str_c(instr); data_line = strchr(version_line, '\n'); if (data_line != NULL) version_line = t_strdup_until(version_line, data_line++); else version_line = NULL; if (ret > 0 || version_line != NULL) { if (version_line == NULL || !version_string_verify(version_line, "script-login", SCRIPT_LOGIN_PROTOCOL_VERSION_MAJOR)) { i_fatal("Client not compatible with this binary " "(connecting to wrong socket?)"); } } if (ret <= 0) { if (ret < 0) i_fatal("read() failed: %m"); else i_fatal("read() failed: disconnected"); } if (fd == -1) i_fatal("client fd not received"); alarm(0); /* put everything to environment */ env_clean(); keys = t_str_new(256); args = t_strsplit_tab(data_line); if (str_array_length(args) < 3) i_fatal("Missing input fields"); i = 0; memset(&input, 0, sizeof(input)); input.module = "mail"; /* need to get mail_uid, mail_gid */ input.service = "script-login"; (void)net_addr2ip(args[i++], &input.local_ip); (void)net_addr2ip(args[i++], &input.remote_ip); input.username = args[i++]; input.userdb_fields = args + i; env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&input.local_ip), NULL)); env_put(t_strconcat("IP=", net_ip2addr(&input.remote_ip), NULL)); env_put(t_strconcat("USER="******"%s ", key); } } env_put(t_strconcat(ENV_USERDB_KEYS"=", str_c(keys), NULL)); master_service_init_log(master_service, t_strdup_printf("script-login(%s): ", input.username)); if (drop_to_userdb_privileges) { service_ctx = mail_storage_service_init(master_service, NULL, flags); if (mail_storage_service_lookup(service_ctx, &input, &user, &error) <= 0) i_fatal("%s", error); mail_storage_service_restrict_setenv(service_ctx, user); /* we can't exec anything in a chroot */ env_remove("RESTRICT_CHROOT"); restrict_access_by_env(getenv("HOME"), TRUE); } if (dup2(fd, STDIN_FILENO) < 0) i_fatal("dup2() failed: %m"); if (dup2(fd, STDOUT_FILENO) < 0) i_fatal("dup2() failed: %m"); if (close(fd) < 0) i_fatal("close() failed: %m"); if (conn->fd != SCRIPT_COMM_FD) { if (dup2(conn->fd, SCRIPT_COMM_FD) < 0) i_fatal("dup2() failed: %m"); if (close(conn->fd) < 0) i_fatal("close() failed: %m"); } /* close all listener sockets */ socket_count = master_service_get_socket_count(master_service); for (i = 0; i < socket_count; i++) { if (close(MASTER_LISTEN_FD_FIRST + i) < 0) i_error("close(listener) failed: %m"); } if (close(MASTER_STATUS_FD) < 0) i_error("close(status) failed: %m"); execvp_const(exec_args[0], exec_args); }
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; }
array_foreach(extra_fields, field) { key = t_str_ucase(field->key); value = field->value != NULL ? field->value : "1"; env_put(t_strconcat(key, "=", value, NULL)); }
static bool cmd_compress(struct client_command_context *cmd) { struct client *client = cmd->client; struct zlib_client *zclient = IMAP_ZLIB_IMAP_CONTEXT(client); const struct compression_handler *handler; const struct imap_arg *args; struct istream *old_input; struct ostream *old_output; const char *mechanism, *value; unsigned int level; /* <mechanism> */ if (!client_read_args(cmd, 0, 0, &args)) return FALSE; if (!imap_arg_get_atom(args, &mechanism) || !IMAP_ARG_IS_EOL(&args[1])) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } if (zclient->handler != NULL) { client_send_tagline(cmd, t_strdup_printf( "NO [COMPRESSIONACTIVE] COMPRESSION=%s already enabled.", t_str_ucase(zclient->handler->name))); return TRUE; } if (client->tls_compression) { client_send_tagline(cmd, "NO [COMPRESSIONACTIVE] TLS compression already enabled."); return TRUE; } handler = compression_lookup_handler(t_str_lcase(mechanism)); if (handler == NULL || handler->create_istream == NULL) { client_send_tagline(cmd, "NO Unknown compression mechanism."); return TRUE; } imap_zlib_client_skip_line(client); client_send_tagline(cmd, "OK Begin compression."); value = mail_user_plugin_getenv(client->user, "imap_zlib_compress_level"); if (value == NULL || str_to_uint(value, &level) < 0 || level <= 0 || level > 9) level = IMAP_COMPRESS_DEFAULT_LEVEL; old_input = client->input; old_output = client->output; client->input = handler->create_istream(old_input, FALSE); client->output = handler->create_ostream(old_output, level); /* preserve output offset so that the bytes out counter in logout message doesn't get reset here */ client->output->offset = old_output->offset; i_stream_unref(&old_input); o_stream_unref(&old_output); client_update_imap_parser_streams(client); zclient->handler = handler; return TRUE; }