bool cmd_renamescript(struct client_command_context *cmd) { struct client *client = cmd->client; struct sieve_storage *storage = client->storage; const char *scriptname, *newname; struct sieve_script *script; /* <oldname> <newname> */ if (!client_read_string_args(cmd, TRUE, 2, &scriptname, &newname)) return FALSE; script = sieve_storage_open_script (storage, scriptname, NULL); if (script == NULL) { client_send_storage_error(client, storage); return TRUE; } if (sieve_script_rename(script, newname) < 0) { client_send_storage_error(client, storage); } else { client->renamed_count++; client_send_ok(client, "Renamescript completed."); } sieve_script_unref(&script); return TRUE; }
bool cmd_setactive(struct client_command_context *cmd) { struct client *client = cmd->client; struct sieve_storage *storage = client->storage; const char *scriptname; struct sieve_script *script; int ret; /* <scriptname> */ if ( !client_read_string_args(cmd, TRUE, 1, &scriptname) ) return FALSE; /* Activate, or .. */ if ( *scriptname != '\0' ) { string_t *errors = NULL; const char *errormsg = NULL; bool warnings = FALSE; bool success = TRUE; script = sieve_storage_open_script (storage, scriptname, NULL); if ( script == NULL ) { client_send_storage_error(client, storage); return TRUE; } if ( sieve_script_is_active(script) <= 0 ) { /* Script is first being activated; compile it again without the UPLOAD * flag. */ T_BEGIN { struct sieve_error_handler *ehandler; enum sieve_compile_flags cpflags = SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_ACTIVATED; struct sieve_binary *sbin; enum sieve_error error; /* Prepare error handler */ errors = str_new(default_pool, 1024); ehandler = sieve_strbuf_ehandler_create(client->svinst, errors, TRUE, client->set->managesieve_max_compile_errors); /* Compile */ if ( (sbin=sieve_compile_script (script, ehandler, cpflags, &error)) == NULL ) { if (error != SIEVE_ERROR_NOT_VALID) { errormsg = sieve_script_get_last_error(script, &error); if ( error == SIEVE_ERROR_NONE ) errormsg = NULL; } success = FALSE; } else { sieve_close(&sbin); } warnings = ( sieve_get_warnings(ehandler) > 0 ); sieve_error_handler_unref(&ehandler); } T_END; }
bool cmd_create(struct client_command_context *cmd) { enum mailbox_name_status status; struct mail_namespace *ns; const char *mailbox, *storage_name; struct mailbox *box; bool directory; size_t len; /* <mailbox> */ if (!client_read_string_args(cmd, 1, &mailbox)) return FALSE; ns = client_find_namespace(cmd, mailbox, &storage_name, NULL); if (ns == NULL) return TRUE; len = strlen(mailbox); if (len == 0 || mailbox[len-1] != ns->sep) directory = FALSE; else if (*storage_name == '\0') { client_send_tagline(cmd, "NO ["IMAP_RESP_CODE_ALREADYEXISTS "] Namespace already exists."); return TRUE; } else { /* name ends with hierarchy separator - client is just informing us that it wants to create children under this mailbox. */ directory = TRUE; storage_name = t_strndup(storage_name, strlen(storage_name)-1); mailbox = t_strndup(mailbox, len-1); } ns = client_find_namespace(cmd, mailbox, &storage_name, &status); if (ns == NULL) return TRUE; switch (status) { case MAILBOX_NAME_VALID: break; case MAILBOX_NAME_EXISTS_DIR: if (!directory) break; /* fall through */ case MAILBOX_NAME_EXISTS_MAILBOX: case MAILBOX_NAME_INVALID: case MAILBOX_NAME_NOINFERIORS: client_fail_mailbox_name_status(cmd, mailbox, NULL, status); return TRUE; } box = mailbox_alloc(ns->list, storage_name, 0); if (mailbox_create(box, NULL, directory) < 0) client_send_storage_error(cmd, mailbox_get_storage(box)); else client_send_tagline(cmd, "OK Create completed."); mailbox_free(&box); return TRUE; }
bool cmd_listscripts(struct client_command_context *cmd) { struct client *client = cmd->client; struct sieve_storage_list_context *ctx; const char *scriptname; bool active; string_t *str; /* no arguments */ if ( !client_read_no_args(cmd) ) return FALSE; if ( (ctx = sieve_storage_list_init(client->storage)) == NULL ) { client_send_storage_error(client, client->storage); return TRUE; } /* FIXME: This will be quite slow for large script lists. Implement * some buffering to fix this. Wont truely be an issue with managesieve * though. */ while ((scriptname = sieve_storage_list_next(ctx, &active)) != NULL) { T_BEGIN { str = t_str_new(128); managesieve_quote_append_string(str, scriptname, FALSE); if ( active ) str_append(str, " ACTIVE"); client_send_line(client, str_c(str)); } T_END; } if ( sieve_storage_list_deinit(&ctx) < 0 ) { client_send_storage_error(client, client->storage); return TRUE; } client_send_ok(client, "Listscripts completed."); return TRUE; }
static bool cmd_getscript_finish(struct cmd_getscript_context *ctx) { struct client *client = ctx->client; if ( ctx->script != NULL ) sieve_script_unref(&ctx->script); if ( ctx->failed ) { if ( client->output->closed ) { client_disconnect(client, "Disconnected"); return TRUE; } client_send_storage_error(client, client->storage); return TRUE; } client_send_line(client, ""); client_send_ok(client, "Getscript completed."); return TRUE; }
bool cmd_create(struct client_command_context *cmd) { struct mail_namespace *ns; const char *mailbox, *orig_mailbox; struct mailbox *box; bool directory; size_t len; /* <mailbox> */ if (!client_read_string_args(cmd, 1, &mailbox)) return FALSE; orig_mailbox = mailbox; ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; len = strlen(orig_mailbox); if (len == 0 || orig_mailbox[len-1] != mail_namespace_get_sep(ns)) directory = FALSE; else { /* name ends with hierarchy separator - client is just informing us that it wants to create children under this mailbox. */ directory = TRUE; /* drop separator from mailbox. it's already dropped when WORKAROUND_TB_EXTRA_MAILBOX_SEP is enabled */ if (len == strlen(mailbox)) mailbox = t_strndup(mailbox, len-1); } box = mailbox_alloc(ns->list, mailbox, 0); if (mailbox_create(box, NULL, directory) < 0) client_send_storage_error(cmd, mailbox_get_storage(box)); else client_send_tagline(cmd, "OK Create completed."); mailbox_free(&box); return TRUE; }
static bool cmd_copy_full(struct client_command_context *cmd, bool move) { struct client *client = cmd->client; struct mail_storage *dest_storage; struct mailbox *destbox; struct mailbox_transaction_context *t, *src_trans; struct mail_search_args *search_args; const char *messageset, *mailbox, *src_uidset; enum mailbox_sync_flags sync_flags = 0; enum imap_sync_flags imap_flags = 0; struct mail_transaction_commit_changes changes; unsigned int copy_count; string_t *msg; int ret; /* <message set> <mailbox> */ if (!client_read_string_args(cmd, 2, &messageset, &mailbox)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; ret = imap_search_get_seqset(cmd, messageset, cmd->uid, &search_args); if (ret <= 0) return ret < 0; if (client_open_save_dest_box(cmd, mailbox, &destbox) < 0) { mail_search_args_unref(&search_args); return TRUE; } t = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL | MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS); ret = fetch_and_copy(client, move, t, &src_trans, search_args, &src_uidset, ©_count); mail_search_args_unref(&search_args); msg = t_str_new(256); if (ret <= 0) mailbox_transaction_rollback(&t); else if (mailbox_transaction_commit_get_changes(&t, &changes) < 0) ret = -1; else if (copy_count == 0) { str_append(msg, "OK No messages found."); pool_unref(&changes.pool); } else if (seq_range_count(&changes.saved_uids) == 0 || changes.no_read_perm) { /* not supported by backend (virtual) or no read permissions for mailbox */ str_append(msg, move ? "OK Move completed." : "OK Copy completed."); pool_unref(&changes.pool); } else if (move) { i_assert(copy_count == seq_range_count(&changes.saved_uids)); str_printfa(msg, "* OK [COPYUID %u %s ", changes.uid_validity, src_uidset); imap_write_seq_range(msg, &changes.saved_uids); str_append(msg, "] Moved UIDs."); client_send_line(client, str_c(msg)); str_truncate(msg, 0); str_append(msg, "OK Move completed."); pool_unref(&changes.pool); } else { i_assert(copy_count == seq_range_count(&changes.saved_uids)); str_printfa(msg, "OK [COPYUID %u %s ", changes.uid_validity, src_uidset); imap_write_seq_range(msg, &changes.saved_uids); str_append(msg, "] Copy completed."); pool_unref(&changes.pool); } if (ret <= 0 && move) { /* move failed, don't expunge anything */ mailbox_transaction_rollback(&src_trans); } else { if (mailbox_transaction_commit(&src_trans) < 0) ret = -1; } dest_storage = mailbox_get_storage(destbox); if (destbox != client->mailbox) { if (move) sync_flags |= MAILBOX_SYNC_FLAG_EXPUNGE; else sync_flags |= MAILBOX_SYNC_FLAG_FAST; imap_flags |= IMAP_SYNC_FLAG_SAFE; mailbox_free(&destbox); } else if (move) { sync_flags |= MAILBOX_SYNC_FLAG_EXPUNGE; imap_flags |= IMAP_SYNC_FLAG_SAFE; } if (ret > 0) return cmd_sync(cmd, sync_flags, imap_flags, str_c(msg)); else if (ret == 0) { /* some messages were expunged, sync them */ return cmd_sync(cmd, 0, 0, "NO ["IMAP_RESP_CODE_EXPUNGEISSUED"] " "Some of the requested messages no longer exist."); } else { client_send_storage_error(cmd, dest_storage); return TRUE; } }
static bool cmd_putscript_continue_script(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_putscript_context *ctx = cmd->context; size_t size; int ret; if (ctx->save_ctx != NULL) { while (ctx->script_size == 0 || ctx->input->v_offset != ctx->script_size) { if ( ctx->max_script_size > 0 && ctx->input->v_offset > ctx->max_script_size ) { (void)managesieve_quota_check_validsize(client, ctx->input->v_offset); cmd_putscript_finish(ctx); return TRUE; } ret = i_stream_read(ctx->input); if ((ret != -1 || ctx->input->stream_errno != EINVAL || client->input->eof) && sieve_storage_save_continue(ctx->save_ctx) < 0) { /* we still have to finish reading the script from client */ sieve_storage_save_cancel(&ctx->save_ctx); break; } if (ret == -1 || ret == 0) break; } } if (ctx->save_ctx == NULL) { (void)i_stream_read(ctx->input); (void)i_stream_get_data(ctx->input, &size); i_stream_skip(ctx->input, size); } if (ctx->input->eof || client->input->closed) { bool failed = FALSE; bool all_written = FALSE; if ( ctx->script_size == 0 ) { if ( !client->input->eof && ctx->input->stream_errno == EINVAL ) { client_send_command_error(cmd, t_strdup_printf( "Invalid input: %s", i_stream_get_error(ctx->input))); client->input_skip_line = TRUE; failed = TRUE; } all_written = ( ctx->input->eof && ctx->input->stream_errno == 0 ); } else { all_written = ( ctx->input->v_offset == ctx->script_size ); } /* finished */ ctx->input = NULL; if ( !failed ) { if (ctx->save_ctx == NULL) { /* failed above */ client_send_storage_error(client, ctx->storage); failed = TRUE; } else if (!all_written) { /* client disconnected before it finished sending the whole script. */ failed = TRUE; sieve_storage_save_cancel(&ctx->save_ctx); client_disconnect (client, "EOF while appending in PUTSCRIPT/CHECKSCRIPT"); } else if (sieve_storage_save_finish(ctx->save_ctx) < 0) { failed = TRUE; client_send_storage_error(client, ctx->storage); } else { failed = client->input->closed; } } if (failed) { cmd_putscript_finish(ctx); return TRUE; } /* finish */ client->command_pending = FALSE; managesieve_parser_reset(ctx->save_parser); cmd->func = cmd_putscript_finish_parsing; return cmd_putscript_finish_parsing(cmd); } return FALSE; }
static bool cmd_putscript_continue_parsing(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_putscript_context *ctx = cmd->context; const struct managesieve_arg *args; int ret; /* if error occurs, the CRLF is already read. */ client->input_skip_line = FALSE; /* <script literal> */ ret = managesieve_parser_read_args(ctx->save_parser, 0, MANAGESIEVE_PARSE_FLAG_STRING_STREAM, &args); if (ret == -1 || client->output->closed) { cmd_putscript_finish(ctx); client_send_command_error(cmd, "Invalid arguments."); client->input_skip_line = TRUE; return TRUE; } if (ret < 0) { /* need more data */ return FALSE; } /* Validate the script argument */ if ( !managesieve_arg_get_string_stream(args,&ctx->input) ) { client_send_command_error(cmd, "Invalid arguments."); return cmd_putscript_cancel(ctx, FALSE); } if ( i_stream_get_size(ctx->input, FALSE, &ctx->script_size) > 0 ) { if ( ctx->script_size == 0 ) { /* no script content, abort */ if ( ctx->scriptname != NULL ) client_send_no(client, "PUTSCRIPT aborted (empty script)."); else client_send_no(client, "CHECKSCRIPT aborted (empty script)."); cmd_putscript_finish(ctx); return TRUE; /* Check quota */ } else if ( ctx->scriptname == NULL ) { if ( !managesieve_quota_check_validsize(client, ctx->script_size) ) return cmd_putscript_cancel(ctx, TRUE); } else { if ( !managesieve_quota_check_all (client, ctx->scriptname, ctx->script_size) ) return cmd_putscript_cancel(ctx, TRUE); } } else { ctx->max_script_size = managesieve_quota_max_script_size(client); } /* save the script */ ctx->save_ctx = sieve_storage_save_init (ctx->storage, ctx->scriptname, ctx->input); if ( ctx->save_ctx == NULL ) { /* save initialization failed */ client_send_storage_error(client, ctx->storage); return cmd_putscript_cancel(ctx, TRUE); } /* after literal comes CRLF, if we fail make sure we eat it away */ client->input_skip_line = TRUE; client->command_pending = TRUE; cmd->func = cmd_putscript_continue_script; return cmd_putscript_continue_script(cmd); }
static bool cmd_putscript_finish_parsing(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_putscript_context *ctx = cmd->context; const struct managesieve_arg *args; int ret; /* if error occurs, the CRLF is already read. */ client->input_skip_line = FALSE; /* <script literal> */ ret = managesieve_parser_read_args(ctx->save_parser, 0, 0, &args); if (ret == -1 || client->output->closed) { if (ctx->storage != NULL) client_send_command_error(cmd, NULL); cmd_putscript_finish(ctx); return TRUE; } if (ret < 0) { /* need more data */ return FALSE; } if ( MANAGESIEVE_ARG_IS_EOL(&args[0]) ) { struct sieve_script *script; bool success = TRUE; /* Eat away the trailing CRLF */ client->input_skip_line = TRUE; /* Obtain script object for uploaded script */ script = sieve_storage_save_get_tempscript(ctx->save_ctx); /* Check result */ if ( script == NULL ) { client_send_storage_error(client, ctx->storage); cmd_putscript_finish(ctx); return TRUE; } /* If quoted string, the size was not known until now */ if ( ctx->script_size == 0 ) { if (sieve_script_get_size(script, &ctx->script_size) < 0) { client_send_storage_error(client, ctx->storage); cmd_putscript_finish(ctx); return TRUE; } /* Check quota; max size is already checked */ if ( ctx->scriptname != NULL && !managesieve_quota_check_all (client, ctx->scriptname, ctx->script_size) ) { cmd_putscript_finish(ctx); return TRUE; } } /* Try to compile script */ T_BEGIN { struct sieve_error_handler *ehandler; enum sieve_compile_flags cpflags = SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_UPLOADED; struct sieve_binary *sbin; enum sieve_error error; string_t *errors; /* Mark this as an activation when we are replacing the active script */ if ( sieve_storage_save_will_activate(ctx->save_ctx) ) { cpflags |= SIEVE_COMPILE_FLAG_ACTIVATED; } /* Prepare error handler */ errors = str_new(default_pool, 1024); ehandler = sieve_strbuf_ehandler_create(client->svinst, errors, TRUE, client->set->managesieve_max_compile_errors); /* Compile */ if ( (sbin=sieve_compile_script (script, ehandler, cpflags, &error)) == NULL ) { if ( error != SIEVE_ERROR_NOT_VALID ) { const char *errormsg = sieve_script_get_last_error(script, &error); if ( error != SIEVE_ERROR_NONE ) client_send_no(client, errormsg); else client_send_no(client, str_c(errors)); } else { client_send_no(client, str_c(errors)); } success = FALSE; } else { sieve_close(&sbin); /* Commit to save only when this is a putscript command */ if ( ctx->scriptname != NULL ) { ret = sieve_storage_save_commit(&ctx->save_ctx); /* Check commit */ if (ret < 0) { client_send_storage_error(client, ctx->storage); success = FALSE; } } } /* Finish up */ cmd_putscript_finish(ctx); /* Report result to user */ if ( success ) { if ( ctx->scriptname != NULL ) { client->put_count++; client->put_bytes += ctx->script_size; } else { client->check_count++; client->check_bytes += ctx->script_size; } if ( sieve_get_warnings(ehandler) > 0 ) client_send_okresp(client, "WARNINGS", str_c(errors)); else { if ( ctx->scriptname != NULL ) client_send_ok(client, "PUTSCRIPT completed."); else client_send_ok(client, "Script checked successfully."); } } sieve_error_handler_unref(&ehandler); str_free(&errors); } T_END; return TRUE; } client_send_command_error(cmd, "Too many command arguments."); cmd_putscript_finish(ctx); return TRUE; }
void client_send_box_error(struct client_command_context *cmd, struct mailbox *box) { client_send_storage_error(cmd, mailbox_get_storage(box)); }
bool cmd_copy(struct client_command_context *cmd) { struct client *client = cmd->client; struct mail_namespace *dest_ns; struct mail_storage *dest_storage; struct mailbox *destbox; struct mailbox_transaction_context *t; struct mail_search_args *search_args; const char *messageset, *mailbox, *storage_name, *src_uidset; enum mailbox_name_status status; enum mailbox_sync_flags sync_flags = 0; enum imap_sync_flags imap_flags = 0; struct mail_transaction_commit_changes changes; unsigned int copy_count; string_t *msg; int ret; /* <message set> <mailbox> */ if (!client_read_string_args(cmd, 2, &messageset, &mailbox)) return FALSE; if (!client_verify_open_mailbox(cmd)) return TRUE; ret = imap_search_get_seqset(cmd, messageset, cmd->uid, &search_args); if (ret <= 0) return ret < 0; /* open the destination mailbox */ dest_ns = client_find_namespace(cmd, mailbox, &storage_name, &status); if (dest_ns == NULL) { mail_search_args_unref(&search_args); return TRUE; } switch (status) { case MAILBOX_NAME_EXISTS_MAILBOX: break; case MAILBOX_NAME_EXISTS_DIR: status = MAILBOX_NAME_VALID; /* fall through */ case MAILBOX_NAME_VALID: case MAILBOX_NAME_INVALID: case MAILBOX_NAME_NOINFERIORS: client_fail_mailbox_name_status(cmd, mailbox, "TRYCREATE", status); mail_search_args_unref(&search_args); return TRUE; } if (mailbox_equals(client->mailbox, dest_ns, storage_name)) destbox = client->mailbox; else { destbox = mailbox_alloc(dest_ns->list, storage_name, MAILBOX_FLAG_SAVEONLY | MAILBOX_FLAG_KEEP_RECENT); if (mailbox_open(destbox) < 0) { client_send_storage_error(cmd, mailbox_get_storage(destbox)); mailbox_free(&destbox); mail_search_args_unref(&search_args); return TRUE; } if (client->enabled_features != 0) mailbox_enable(destbox, client->enabled_features); } t = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL | MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS); ret = fetch_and_copy(client, t, search_args, &src_uidset, ©_count); mail_search_args_unref(&search_args); msg = t_str_new(256); if (ret <= 0) mailbox_transaction_rollback(&t); else if (mailbox_transaction_commit_get_changes(&t, &changes) < 0) ret = -1; else if (copy_count == 0) { str_append(msg, "OK No messages copied."); pool_unref(&changes.pool); } else if (seq_range_count(&changes.saved_uids) == 0) { /* not supported by backend (virtual) */ str_append(msg, "OK Copy completed."); pool_unref(&changes.pool); } else { i_assert(copy_count == seq_range_count(&changes.saved_uids)); str_printfa(msg, "OK [COPYUID %u %s ", changes.uid_validity, src_uidset); imap_write_seq_range(msg, &changes.saved_uids); str_append(msg, "] Copy completed."); pool_unref(&changes.pool); } dest_storage = mailbox_get_storage(destbox); if (destbox != client->mailbox) { sync_flags |= MAILBOX_SYNC_FLAG_FAST; imap_flags |= IMAP_SYNC_FLAG_SAFE; mailbox_free(&destbox); } if (ret > 0) return cmd_sync(cmd, sync_flags, imap_flags, str_c(msg)); else if (ret == 0) { /* some messages were expunged, sync them */ return cmd_sync(cmd, 0, 0, "NO ["IMAP_RESP_CODE_EXPUNGEISSUED"] " "Some of the requested messages no longer exist."); } else { client_send_storage_error(cmd, dest_storage); return TRUE; } }