1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2  */
3 
4 #include "lib.h"
5 #include "ostream.h"
6 #include "istream.h"
7 #include "iostream.h"
8 
9 #include "sieve-script.h"
10 #include "sieve-storage.h"
11 
12 #include "managesieve-common.h"
13 #include "managesieve-commands.h"
14 
15 struct cmd_getscript_context {
16 	struct client *client;
17 	struct client_command_context *cmd;
18 	struct sieve_storage *storage;
19 	uoff_t script_size;
20 
21 	const char *scriptname;
22 	struct sieve_script *script;
23 	struct istream *script_stream;
24 
25 	bool failed:1;
26 };
27 
cmd_getscript_finish(struct cmd_getscript_context * ctx)28 static bool cmd_getscript_finish(struct cmd_getscript_context *ctx)
29 {
30 	struct client_command_context *cmd = ctx->cmd;
31 	struct client *client = ctx->client;
32 
33 	if (ctx->script != NULL)
34 		sieve_script_unref(&ctx->script);
35 
36 	if (ctx->failed) {
37 		if (client->output->closed) {
38 			client_disconnect(client, NULL);
39 			return TRUE;
40 		}
41 
42 		client_command_storage_error(
43 			cmd, "Failed to retrieve script `%s'", ctx->scriptname);
44 		return TRUE;
45 	}
46 
47 	client->get_count++;
48 	client->get_bytes += ctx->script_size;
49 
50 	struct event_passthrough *e =
51 		client_command_create_finish_event(cmd);
52 	e_debug(e->event(), "Retrieved script `%s'", ctx->scriptname);
53 
54 	client_send_line(client, "");
55 	client_send_ok(client, "Getscript completed.");
56 	return TRUE;
57 }
58 
cmd_getscript_continue(struct client_command_context * cmd)59 static bool cmd_getscript_continue(struct client_command_context *cmd)
60 {
61 	struct client *client = cmd->client;
62 	struct cmd_getscript_context *ctx = cmd->context;
63 
64 	switch (o_stream_send_istream(client->output, ctx->script_stream)) {
65 	case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
66 		if (ctx->script_stream->v_offset != ctx->script_size &&
67 		    !ctx->failed) {
68 			/* Input stream gave less data than expected */
69 			sieve_storage_set_critical(
70 				ctx->storage, "GETSCRIPT for script `%s' "
71 				"from %s got too little data: "
72 				"%"PRIuUOFF_T" vs %"PRIuUOFF_T,
73 				sieve_script_name(ctx->script),
74 				sieve_script_location(ctx->script),
75 				ctx->script_stream->v_offset, ctx->script_size);
76 			client_disconnect(ctx->client, "GETSCRIPT failed");
77 			ctx->failed = TRUE;
78 		}
79 		break;
80 	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
81 		i_unreached();
82 	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
83 		return FALSE;
84 	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
85 		sieve_storage_set_critical(ctx->storage,
86 			"o_stream_send_istream() failed for script `%s' "
87 			"from %s: %s",
88 			sieve_script_name(ctx->script),
89 			sieve_script_location(ctx->script),
90 			i_stream_get_error(ctx->script_stream));
91 		ctx->failed = TRUE;
92 		break;
93 	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
94 		client_disconnect(ctx->client, NULL);
95 		ctx->failed = TRUE;
96 		break;
97 	}
98 	return cmd_getscript_finish(ctx);
99 }
100 
cmd_getscript(struct client_command_context * cmd)101 bool cmd_getscript(struct client_command_context *cmd)
102 {
103 	struct client *client = cmd->client;
104 	struct cmd_getscript_context *ctx;
105 	const char *scriptname;
106 	enum sieve_error error;
107 
108 	/* <scriptname> */
109 	if (!client_read_string_args(cmd, TRUE, 1, &scriptname))
110 		return FALSE;
111 
112 	event_add_str(cmd->event, "script_name", scriptname);
113 
114 	ctx = p_new(cmd->pool, struct cmd_getscript_context, 1);
115 	ctx->cmd = cmd;
116 	ctx->client = client;
117 	ctx->scriptname = p_strdup(cmd->pool, scriptname);
118 	ctx->storage = client->storage;
119 	ctx->failed = FALSE;
120 
121 	ctx->script = sieve_storage_open_script(client->storage, scriptname,
122 						NULL);
123 	if (ctx->script == NULL) {
124 		ctx->failed = TRUE;
125 		return cmd_getscript_finish(ctx);
126 	}
127 
128 	if (sieve_script_get_stream(ctx->script, &ctx->script_stream,
129 				    &error) < 0 ) {
130 		if (error == SIEVE_ERROR_NOT_FOUND) {
131 			sieve_storage_set_error(client->storage, error,
132 						"Script does not exist.");
133 		}
134 		ctx->failed = TRUE;
135 		return cmd_getscript_finish(ctx);
136 	}
137 
138 	if (sieve_script_get_size(ctx->script, &ctx->script_size) <= 0) {
139 		sieve_storage_set_critical(ctx->storage,
140 			"failed to obtain script size for script `%s' from %s",
141 			sieve_script_name(ctx->script),
142 			sieve_script_location(ctx->script));
143 		ctx->failed = TRUE;
144 		return cmd_getscript_finish(ctx);
145 	}
146 
147 	i_assert(ctx->script_stream->v_offset == 0);
148 
149 	client_send_line(client, t_strdup_printf("{%"PRIuUOFF_T"}",
150 						 ctx->script_size));
151 
152 	client->command_pending = TRUE;
153 	cmd->func = cmd_getscript_continue;
154 	cmd->context = ctx;
155 
156 	return cmd_getscript_continue(cmd);
157 }
158