|
tss@8590
|
1 |
/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
|
|
tss@1043
|
2 |
|
|
tss@1043
|
3 |
#include "common.h"
|
|
tss@2722
|
4 |
#include "buffer.h"
|
|
tss@1043
|
5 |
#include "ioloop.h"
|
|
tss@1043
|
6 |
#include "network.h"
|
|
tss@1043
|
7 |
#include "istream.h"
|
|
tss@1043
|
8 |
#include "ostream.h"
|
|
tss@2421
|
9 |
#include "str.h"
|
|
tss@3384
|
10 |
#include "var-expand.h"
|
|
tss@1043
|
11 |
#include "mail-storage.h"
|
|
tss@1043
|
12 |
#include "commands.h"
|
|
tss@7642
|
13 |
#include "mail-search-build.h"
|
|
tss@5500
|
14 |
#include "mail-namespace.h"
|
|
tss@1043
|
15 |
|
|
tss@1043
|
16 |
#include <stdlib.h>
|
|
tss@3960
|
17 |
#include <unistd.h>
|
|
tss@1043
|
18 |
|
|
tss@1053
|
19 |
/* max. length of input command line (spec says 512) */
|
|
tss@1053
|
20 |
#define MAX_INBUF_SIZE 2048
|
|
tss@1043
|
21 |
|
|
tss@2421
|
22 |
/* Stop reading input when output buffer has this many bytes. Once the buffer
|
|
tss@2421
|
23 |
size has dropped to half of it, start reading input again. */
|
|
tss@2421
|
24 |
#define OUTBUF_THROTTLE_SIZE 4096
|
|
tss@2421
|
25 |
|
|
tss@1043
|
26 |
/* Disconnect client when it sends too many bad commands in a row */
|
|
tss@1043
|
27 |
#define CLIENT_MAX_BAD_COMMANDS 20
|
|
tss@1043
|
28 |
|
|
tss@7103
|
29 |
/* Disconnect client after idling this many milliseconds */
|
|
tss@7103
|
30 |
#define CLIENT_IDLE_TIMEOUT_MSECS (10*60*1000)
|
|
tss@8718
|
31 |
/* If client starts idling for this many milliseconds, commit the current
|
|
tss@8718
|
32 |
transaction. This allows the mailbox to become unlocked. */
|
|
tss@8718
|
33 |
#define CLIENT_COMMIT_TIMEOUT_MSECS (10*1000)
|
|
tss@1043
|
34 |
|
|
tss@1043
|
35 |
static struct client *my_client; /* we don't need more than one currently */
|
|
tss@1043
|
36 |
|
|
tss@4907
|
37 |
static void client_input(struct client *client);
|
|
tss@4907
|
38 |
static int client_output(struct client *client);
|
|
tss@1043
|
39 |
|
|
tss@8718
|
40 |
static void client_commit_timeout(struct client *client)
|
|
tss@8718
|
41 |
{
|
|
tss@8718
|
42 |
if (client->cmd != NULL) {
|
|
tss@8718
|
43 |
/* Can't commit while commands are running */
|
|
tss@8718
|
44 |
return;
|
|
tss@8718
|
45 |
}
|
|
tss@8718
|
46 |
|
|
tss@8718
|
47 |
(void)mailbox_transaction_commit(&client->trans);
|
|
tss@8718
|
48 |
client->trans = mailbox_transaction_begin(client->mailbox, 0);
|
|
tss@8718
|
49 |
}
|
|
tss@8718
|
50 |
|
|
tss@7103
|
51 |
static void client_idle_timeout(struct client *client)
|
|
tss@7103
|
52 |
{
|
|
tss@7103
|
53 |
if (client->cmd != NULL) {
|
|
tss@7103
|
54 |
client_destroy(client,
|
|
tss@7103
|
55 |
"Disconnected for inactivity in reading our output");
|
|
tss@7103
|
56 |
} else {
|
|
tss@7103
|
57 |
client_send_line(client, "-ERR Disconnected for inactivity.");
|
|
tss@7103
|
58 |
client_destroy(client, "Disconnected for inactivity");
|
|
tss@7103
|
59 |
}
|
|
tss@7103
|
60 |
}
|
|
tss@7103
|
61 |
|
|
tss@6311
|
62 |
static bool init_mailbox(struct client *client, const char **error_r)
|
|
tss@1044
|
63 |
{
|
|
tss@7642
|
64 |
struct mail_search_args *search_args;
|
|
tss@1915
|
65 |
struct mailbox_transaction_context *t;
|
|
tss@1845
|
66 |
struct mail_search_context *ctx;
|
|
tss@2976
|
67 |
struct mailbox_status status;
|
|
tss@1044
|
68 |
struct mail *mail;
|
|
tss@2722
|
69 |
buffer_t *message_sizes_buf;
|
|
tss@6311
|
70 |
uint32_t failed_uid = 0;
|
|
tss@6280
|
71 |
uoff_t size;
|
|
tss@3863
|
72 |
int i;
|
|
tss@6311
|
73 |
bool failed, expunged;
|
|
tss@1044
|
74 |
|
|
tss@2722
|
75 |
message_sizes_buf = buffer_create_dynamic(default_pool, 512);
|
|
tss@2722
|
76 |
|
|
tss@7642
|
77 |
search_args = mail_search_build_init();
|
|
tss@7642
|
78 |
mail_search_build_add_all(search_args);
|
|
tss@1845
|
79 |
|
|
tss@1044
|
80 |
for (i = 0; i < 2; i++) {
|
|
tss@6652
|
81 |
expunged = FALSE;
|
|
tss@6463
|
82 |
if (mailbox_sync(client->mailbox, MAILBOX_SYNC_FLAG_FULL_READ,
|
|
tss@6463
|
83 |
STATUS_UIDVALIDITY, &status) < 0) {
|
|
tss@2322
|
84 |
client_send_storage_error(client);
|
|
tss@2722
|
85 |
break;
|
|
tss@2322
|
86 |
}
|
|
tss@2976
|
87 |
client->uid_validity = status.uidvalidity;
|
|
tss@2092
|
88 |
|
|
tss@3209
|
89 |
t = mailbox_transaction_begin(client->mailbox, 0);
|
|
tss@7642
|
90 |
ctx = mailbox_search_init(t, search_args, NULL);
|
|
tss@1044
|
91 |
|
|
tss@2722
|
92 |
client->last_seen = 0;
|
|
tss@2722
|
93 |
client->total_size = 0;
|
|
tss@2722
|
94 |
buffer_set_used_size(message_sizes_buf, 0);
|
|
tss@2722
|
95 |
|
|
tss@1044
|
96 |
failed = FALSE;
|
|
tss@3209
|
97 |
mail = mail_alloc(t, MAIL_FETCH_VIRTUAL_SIZE, NULL);
|
|
tss@3209
|
98 |
while (mailbox_search_next(ctx, mail) > 0) {
|
|
tss@6280
|
99 |
if (mail_get_virtual_size(mail, &size) < 0) {
|
|
tss@6312
|
100 |
expunged = mail->expunged;
|
|
tss@6312
|
101 |
failed = TRUE;
|
|
tss@6311
|
102 |
if (failed_uid == mail->uid) {
|
|
tss@6311
|
103 |
i_error("Getting size of message "
|
|
tss@6311
|
104 |
"UID=%u failed", mail->uid);
|
|
tss@6311
|
105 |
break;
|
|
tss@6311
|
106 |
}
|
|
tss@6311
|
107 |
failed_uid = mail->uid;
|
|
tss@1044
|
108 |
break;
|
|
tss@1044
|
109 |
}
|
|
tss@2621
|
110 |
|
|
tss@3209
|
111 |
if ((mail_get_flags(mail) & MAIL_SEEN) != 0)
|
|
tss@2621
|
112 |
client->last_seen = mail->seq;
|
|
tss@1044
|
113 |
client->total_size += size;
|
|
tss@1044
|
114 |
|
|
tss@2722
|
115 |
buffer_append(message_sizes_buf, &size, sizeof(size));
|
|
tss@1044
|
116 |
}
|
|
tss@2722
|
117 |
client->messages_count =
|
|
tss@2722
|
118 |
message_sizes_buf->used / sizeof(uoff_t);
|
|
tss@1044
|
119 |
|
|
tss@3879
|
120 |
mail_free(&mail);
|
|
tss@6311
|
121 |
if (mailbox_search_deinit(&ctx) < 0 || (failed && !expunged)) {
|
|
tss@1044
|
122 |
client_send_storage_error(client);
|
|
tss@6512
|
123 |
(void)mailbox_transaction_commit(&t);
|
|
tss@2722
|
124 |
break;
|
|
tss@1044
|
125 |
}
|
|
tss@1044
|
126 |
|
|
tss@2010
|
127 |
if (!failed) {
|
|
tss@2722
|
128 |
client->trans = t;
|
|
tss@2722
|
129 |
client->message_sizes =
|
|
tss@6414
|
130 |
buffer_free_without_data(&message_sizes_buf);
|
|
tss@7642
|
131 |
mail_search_args_unref(&search_args);
|
|
tss@1044
|
132 |
return TRUE;
|
|
tss@2010
|
133 |
}
|
|
tss@1044
|
134 |
|
|
tss@5761
|
135 |
/* well, sync and try again. we might have cached virtual
|
|
tss@5761
|
136 |
sizes, make sure they get committed. */
|
|
tss@6512
|
137 |
(void)mailbox_transaction_commit(&t);
|
|
tss@1044
|
138 |
}
|
|
tss@7642
|
139 |
mail_search_args_unref(&search_args);
|
|
tss@1044
|
140 |
|
|
tss@6311
|
141 |
if (expunged) {
|
|
tss@6311
|
142 |
client_send_line(client,
|
|
tss@6311
|
143 |
"-ERR [IN-USE] Couldn't sync mailbox.");
|
|
tss@6311
|
144 |
*error_r = "Can't sync mailbox: Messages keep getting expunged";
|
|
tss@6311
|
145 |
} else {
|
|
tss@6311
|
146 |
struct mail_storage *storage = client->inbox_ns->storage;
|
|
tss@6311
|
147 |
enum mail_error error;
|
|
tss@6311
|
148 |
|
|
tss@6311
|
149 |
*error_r = mail_storage_get_last_error(storage, &error);
|
|
tss@6311
|
150 |
}
|
|
tss@6414
|
151 |
buffer_free(&message_sizes_buf);
|
|
tss@1044
|
152 |
return FALSE;
|
|
tss@1044
|
153 |
}
|
|
tss@1044
|
154 |
|
|
tss@8082
|
155 |
struct client *client_create(int fd_in, int fd_out, struct mail_user *user)
|
|
tss@1043
|
156 |
{
|
|
tss@5500
|
157 |
struct mail_storage *storage;
|
|
tss@5500
|
158 |
const char *inbox;
|
|
tss@1043
|
159 |
struct client *client;
|
|
tss@2039
|
160 |
enum mailbox_open_flags flags;
|
|
tss@5178
|
161 |
const char *errmsg;
|
|
tss@5613
|
162 |
enum mail_error error;
|
|
tss@1043
|
163 |
|
|
tss@2421
|
164 |
/* always use nonblocking I/O */
|
|
tss@3960
|
165 |
net_set_nonblock(fd_in, TRUE);
|
|
tss@3960
|
166 |
net_set_nonblock(fd_out, TRUE);
|
|
tss@2421
|
167 |
|
|
tss@1043
|
168 |
client = i_new(struct client, 1);
|
|
tss@3960
|
169 |
client->fd_in = fd_in;
|
|
tss@3960
|
170 |
client->fd_out = fd_out;
|
|
tss@6162
|
171 |
client->input = i_stream_create_fd(fd_in, MAX_INBUF_SIZE, FALSE);
|
|
tss@6161
|
172 |
client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
|
|
tss@2421
|
173 |
o_stream_set_flush_callback(client->output, client_output, client);
|
|
tss@1043
|
174 |
|
|
tss@3960
|
175 |
client->io = io_add(fd_in, IO_READ, client_input, client);
|
|
tss@1043
|
176 |
client->last_input = ioloop_time;
|
|
tss@7103
|
177 |
client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
|
|
tss@7103
|
178 |
client_idle_timeout, client);
|
|
tss@8718
|
179 |
if (!lock_session) {
|
|
tss@8718
|
180 |
client->to_commit = timeout_add(CLIENT_COMMIT_TIMEOUT_MSECS,
|
|
tss@8718
|
181 |
client_commit_timeout, client);
|
|
tss@8718
|
182 |
}
|
|
tss@1043
|
183 |
|
|
tss@8082
|
184 |
client->user = user;
|
|
tss@5500
|
185 |
|
|
tss@5500
|
186 |
inbox = "INBOX";
|
|
tss@8082
|
187 |
client->inbox_ns = mail_namespace_find(user->namespaces, &inbox);
|
|
tss@5500
|
188 |
if (client->inbox_ns == NULL) {
|
|
tss@5500
|
189 |
client_send_line(client, "-ERR No INBOX namespace for user.");
|
|
tss@5500
|
190 |
client_destroy(client, "No INBOX namespace for user.");
|
|
tss@5500
|
191 |
return NULL;
|
|
tss@5500
|
192 |
}
|
|
tss@5500
|
193 |
|
|
tss@5500
|
194 |
storage = client->inbox_ns->storage;
|
|
tss@1043
|
195 |
|
|
tss@8259
|
196 |
flags = MAILBOX_OPEN_POP3_SESSION;
|
|
tss@2719
|
197 |
if (no_flag_updates)
|
|
tss@2039
|
198 |
flags |= MAILBOX_OPEN_KEEP_RECENT;
|
|
tss@4153
|
199 |
if (lock_session)
|
|
tss@4153
|
200 |
flags |= MAILBOX_OPEN_KEEP_LOCKED;
|
|
tss@8468
|
201 |
client->mailbox = mailbox_open(&storage, "INBOX", NULL, flags);
|
|
tss@1363
|
202 |
if (client->mailbox == NULL) {
|
|
tss@5178
|
203 |
errmsg = t_strdup_printf("Couldn't open INBOX: %s",
|
|
tss@5178
|
204 |
mail_storage_get_last_error(storage,
|
|
tss@5613
|
205 |
&error));
|
|
tss@5178
|
206 |
i_error("%s", errmsg);
|
|
tss@5178
|
207 |
client_send_line(client, "-ERR [IN-USE] %s", errmsg);
|
|
tss@5178
|
208 |
client_destroy(client, "Couldn't open INBOX");
|
|
tss@1363
|
209 |
return NULL;
|
|
tss@1363
|
210 |
}
|
|
tss@1363
|
211 |
|
|
tss@6311
|
212 |
if (!init_mailbox(client, &errmsg)) {
|
|
tss@6311
|
213 |
i_error("Couldn't init INBOX: %s", errmsg);
|
|
tss@3384
|
214 |
client_destroy(client, "Mailbox init failed");
|
|
tss@1363
|
215 |
return NULL;
|
|
tss@1363
|
216 |
}
|
|
tss@1043
|
217 |
|
|
tss@8745
|
218 |
if (!no_flag_updates && client->messages_count > 0)
|
|
tss@8717
|
219 |
client->seen_bitmask = i_malloc(MSGS_BITMASK_SIZE(client));
|
|
tss@8717
|
220 |
|
|
tss@1043
|
221 |
i_assert(my_client == NULL);
|
|
tss@1043
|
222 |
my_client = client;
|
|
tss@1043
|
223 |
|
|
tss@1646
|
224 |
if (hook_client_created != NULL)
|
|
tss@1646
|
225 |
hook_client_created(&client);
|
|
tss@1043
|
226 |
return client;
|
|
tss@1043
|
227 |
}
|
|
tss@1043
|
228 |
|
|
tss@3384
|
229 |
static const char *client_stats(struct client *client)
|
|
tss@1043
|
230 |
{
|
|
tss@3384
|
231 |
static struct var_expand_table static_tab[] = {
|
|
tss@8544
|
232 |
{ 'p', NULL, "top_bytes" },
|
|
tss@8544
|
233 |
{ 't', NULL, "top_count" },
|
|
tss@8544
|
234 |
{ 'b', NULL, "retr_bytes" },
|
|
tss@8544
|
235 |
{ 'r', NULL, "retr_count" },
|
|
tss@8544
|
236 |
{ 'd', NULL, "deleted_count" },
|
|
tss@8544
|
237 |
{ 'm', NULL, "message_count" },
|
|
tss@8544
|
238 |
{ 's', NULL, "message_bytes" },
|
|
tss@8544
|
239 |
{ 'i', NULL, "input" },
|
|
tss@8544
|
240 |
{ 'o', NULL, "output" },
|
|
tss@8544
|
241 |
{ '\0', NULL, NULL }
|
|
tss@3384
|
242 |
};
|
|
tss@3384
|
243 |
struct var_expand_table *tab;
|
|
tss@3384
|
244 |
string_t *str;
|
|
tss@3384
|
245 |
|
|
tss@3384
|
246 |
tab = t_malloc(sizeof(static_tab));
|
|
tss@3384
|
247 |
memcpy(tab, static_tab, sizeof(static_tab));
|
|
tss@3384
|
248 |
|
|
tss@3384
|
249 |
tab[0].value = dec2str(client->top_bytes);
|
|
tss@3384
|
250 |
tab[1].value = dec2str(client->top_count);
|
|
tss@3384
|
251 |
tab[2].value = dec2str(client->retr_bytes);
|
|
tss@3384
|
252 |
tab[3].value = dec2str(client->retr_count);
|
|
tss@4566
|
253 |
tab[4].value = dec2str(client->expunged_count);
|
|
tss@3384
|
254 |
tab[5].value = dec2str(client->messages_count);
|
|
tss@3384
|
255 |
tab[6].value = dec2str(client->total_size);
|
|
tss@5979
|
256 |
tab[7].value = dec2str(client->input->v_offset);
|
|
tss@5979
|
257 |
tab[8].value = dec2str(client->output->offset);
|
|
tss@3384
|
258 |
|
|
tss@3384
|
259 |
str = t_str_new(128);
|
|
tss@3384
|
260 |
var_expand(str, logout_format, tab);
|
|
tss@3384
|
261 |
return str_c(str);
|
|
tss@3384
|
262 |
}
|
|
tss@3384
|
263 |
|
|
tss@7453
|
264 |
static const char *client_get_disconnect_reason(struct client *client)
|
|
tss@7453
|
265 |
{
|
|
tss@7453
|
266 |
errno = client->input->stream_errno != 0 ?
|
|
tss@7453
|
267 |
client->input->stream_errno :
|
|
tss@7453
|
268 |
client->output->stream_errno;
|
|
tss@7453
|
269 |
return errno == 0 || errno == EPIPE ? "Connection closed" :
|
|
tss@7453
|
270 |
t_strdup_printf("Connection closed: %m");
|
|
tss@7453
|
271 |
}
|
|
tss@7453
|
272 |
|
|
tss@3384
|
273 |
void client_destroy(struct client *client, const char *reason)
|
|
tss@3384
|
274 |
{
|
|
tss@8723
|
275 |
if (client->seen_change_count > 0)
|
|
tss@8723
|
276 |
client_update_mails(client);
|
|
tss@8723
|
277 |
|
|
tss@4096
|
278 |
if (!client->disconnected) {
|
|
tss@4096
|
279 |
if (reason == NULL)
|
|
tss@7453
|
280 |
reason = client_get_disconnect_reason(client);
|
|
tss@3384
|
281 |
i_info("%s %s", reason, client_stats(client));
|
|
tss@4096
|
282 |
}
|
|
tss@3384
|
283 |
|
|
tss@2502
|
284 |
if (client->cmd != NULL) {
|
|
tss@2502
|
285 |
/* deinitialize command */
|
|
tss@2502
|
286 |
i_stream_close(client->input);
|
|
tss@2502
|
287 |
o_stream_close(client->output);
|
|
tss@2502
|
288 |
client->cmd(client);
|
|
tss@2695
|
289 |
i_assert(client->cmd == NULL);
|
|
tss@2502
|
290 |
}
|
|
tss@5102
|
291 |
if (client->trans != NULL) {
|
|
tss@5102
|
292 |
/* client didn't QUIT, but we still want to save any changes
|
|
tss@5102
|
293 |
done in this transaction. especially the cached virtual
|
|
tss@5102
|
294 |
message sizes. */
|
|
tss@6512
|
295 |
(void)mailbox_transaction_commit(&client->trans);
|
|
tss@5102
|
296 |
}
|
|
tss@2722
|
297 |
if (client->mailbox != NULL)
|
|
tss@3879
|
298 |
mailbox_close(&client->mailbox);
|
|
tss@8467
|
299 |
mail_user_unref(&client->user);
|
|
tss@1043
|
300 |
|
|
tss@1044
|
301 |
i_free(client->message_sizes);
|
|
tss@1044
|
302 |
i_free(client->deleted_bitmask);
|
|
tss@8717
|
303 |
i_free(client->seen_bitmask);
|
|
tss@1043
|
304 |
|
|
tss@2421
|
305 |
if (client->io != NULL)
|
|
tss@3879
|
306 |
io_remove(&client->io);
|
|
tss@7103
|
307 |
timeout_remove(&client->to_idle);
|
|
tss@8718
|
308 |
if (client->to_commit != NULL)
|
|
tss@8718
|
309 |
timeout_remove(&client->to_commit);
|
|
tss@1043
|
310 |
|
|
tss@4070
|
311 |
i_stream_destroy(&client->input);
|
|
tss@4070
|
312 |
o_stream_destroy(&client->output);
|
|
tss@1043
|
313 |
|
|
tss@3960
|
314 |
if (close(client->fd_in) < 0)
|
|
tss@3960
|
315 |
i_error("close(client in) failed: %m");
|
|
tss@3960
|
316 |
if (client->fd_in != client->fd_out) {
|
|
tss@3960
|
317 |
if (close(client->fd_out) < 0)
|
|
tss@3960
|
318 |
i_error("close(client out) failed: %m");
|
|
tss@3960
|
319 |
}
|
|
tss@3960
|
320 |
|
|
tss@1043
|
321 |
i_free(client);
|
|
tss@1043
|
322 |
|
|
tss@1043
|
323 |
/* quit the program */
|
|
tss@1043
|
324 |
my_client = NULL;
|
|
tss@1043
|
325 |
io_loop_stop(ioloop);
|
|
tss@1043
|
326 |
}
|
|
tss@1043
|
327 |
|
|
tss@3384
|
328 |
void client_disconnect(struct client *client, const char *reason)
|
|
tss@1043
|
329 |
{
|
|
tss@4096
|
330 |
if (client->disconnected)
|
|
tss@4096
|
331 |
return;
|
|
tss@4096
|
332 |
|
|
tss@4096
|
333 |
client->disconnected = TRUE;
|
|
tss@4096
|
334 |
i_info("Disconnected: %s %s", reason, client_stats(client));
|
|
tss@3384
|
335 |
|
|
tss@2421
|
336 |
(void)o_stream_flush(client->output);
|
|
tss@1043
|
337 |
|
|
tss@1043
|
338 |
i_stream_close(client->input);
|
|
tss@1043
|
339 |
o_stream_close(client->output);
|
|
tss@1043
|
340 |
}
|
|
tss@1043
|
341 |
|
|
tss@2421
|
342 |
int client_send_line(struct client *client, const char *fmt, ...)
|
|
tss@1043
|
343 |
{
|
|
tss@1043
|
344 |
va_list va;
|
|
tss@2421
|
345 |
ssize_t ret;
|
|
tss@1043
|
346 |
|
|
tss@1043
|
347 |
if (client->output->closed)
|
|
tss@2421
|
348 |
return -1;
|
|
tss@1043
|
349 |
|
|
tss@1043
|
350 |
va_start(va, fmt);
|
|
tss@2421
|
351 |
|
|
tss@7226
|
352 |
T_BEGIN {
|
|
tss@6940
|
353 |
string_t *str;
|
|
tss@2421
|
354 |
|
|
tss@6940
|
355 |
str = t_str_new(256);
|
|
tss@6940
|
356 |
str_vprintfa(str, fmt, va);
|
|
tss@6940
|
357 |
str_append(str, "\r\n");
|
|
tss@6940
|
358 |
|
|
tss@6940
|
359 |
ret = o_stream_send(client->output,
|
|
tss@6940
|
360 |
str_data(str), str_len(str));
|
|
tss@6940
|
361 |
i_assert(ret < 0 || (size_t)ret == str_len(str));
|
|
tss@7226
|
362 |
} T_END;
|
|
tss@2735
|
363 |
if (ret >= 0) {
|
|
tss@2421
|
364 |
if (o_stream_get_buffer_used_size(client->output) <
|
|
tss@2421
|
365 |
OUTBUF_THROTTLE_SIZE) {
|
|
tss@2421
|
366 |
ret = 1;
|
|
tss@2421
|
367 |
client->last_output = ioloop_time;
|
|
tss@2421
|
368 |
} else {
|
|
tss@2421
|
369 |
ret = 0;
|
|
tss@2421
|
370 |
if (client->io != NULL) {
|
|
tss@2421
|
371 |
/* no more input until client has read
|
|
tss@2421
|
372 |
our output */
|
|
tss@3879
|
373 |
io_remove(&client->io);
|
|
tss@3352
|
374 |
|
|
tss@3352
|
375 |
/* If someone happens to flush output,
|
|
tss@3352
|
376 |
we want to get our IO handler back in
|
|
tss@3352
|
377 |
flush callback */
|
|
tss@3352
|
378 |
o_stream_set_flush_pending(client->output,
|
|
tss@3352
|
379 |
TRUE);
|
|
tss@2421
|
380 |
}
|
|
tss@2421
|
381 |
}
|
|
tss@2421
|
382 |
}
|
|
tss@2421
|
383 |
|
|
tss@1043
|
384 |
va_end(va);
|
|
tss@2421
|
385 |
return (int)ret;
|
|
tss@1043
|
386 |
}
|
|
tss@1043
|
387 |
|
|
tss@1044
|
388 |
void client_send_storage_error(struct client *client)
|
|
tss@1044
|
389 |
{
|
|
tss@5613
|
390 |
enum mail_error error;
|
|
tss@1044
|
391 |
|
|
tss@1915
|
392 |
if (mailbox_is_inconsistent(client->mailbox)) {
|
|
tss@1415
|
393 |
client_send_line(client, "-ERR Mailbox is in inconsistent "
|
|
tss@1415
|
394 |
"state, please relogin.");
|
|
tss@3384
|
395 |
client_disconnect(client, "Mailbox is in inconsistent state.");
|
|
tss@1415
|
396 |
return;
|
|
tss@1415
|
397 |
}
|
|
tss@1415
|
398 |
|
|
tss@5613
|
399 |
client_send_line(client, "-ERR %s",
|
|
tss@5613
|
400 |
mail_storage_get_last_error(client->inbox_ns->storage,
|
|
tss@5613
|
401 |
&error));
|
|
tss@1044
|
402 |
}
|
|
tss@1044
|
403 |
|
|
tss@7927
|
404 |
bool client_handle_input(struct client *client)
|
|
tss@1043
|
405 |
{
|
|
tss@1043
|
406 |
char *line, *args;
|
|
tss@3469
|
407 |
int ret;
|
|
tss@1043
|
408 |
|
|
tss@1043
|
409 |
o_stream_cork(client->output);
|
|
tss@1043
|
410 |
while (!client->output->closed &&
|
|
tss@1043
|
411 |
(line = i_stream_next_line(client->input)) != NULL) {
|
|
tss@1043
|
412 |
args = strchr(line, ' ');
|
|
tss@4816
|
413 |
if (args != NULL)
|
|
tss@1043
|
414 |
*args++ = '\0';
|
|
tss@1043
|
415 |
|
|
tss@7226
|
416 |
T_BEGIN {
|
|
tss@6940
|
417 |
ret = client_command_execute(client, line,
|
|
tss@6940
|
418 |
args != NULL ? args : "");
|
|
tss@7226
|
419 |
} T_END;
|
|
tss@3469
|
420 |
if (ret >= 0) {
|
|
tss@1056
|
421 |
client->bad_counter = 0;
|
|
tss@2421
|
422 |
if (client->cmd != NULL) {
|
|
tss@3336
|
423 |
o_stream_set_flush_pending(client->output,
|
|
tss@3336
|
424 |
TRUE);
|
|
tss@2421
|
425 |
client->waiting_input = TRUE;
|
|
tss@2421
|
426 |
break;
|
|
tss@2421
|
427 |
}
|
|
tss@2421
|
428 |
} else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) {
|
|
tss@1056
|
429 |
client_send_line(client, "-ERR Too many bad commands.");
|
|
tss@3384
|
430 |
client_disconnect(client, "Too many bad commands.");
|
|
tss@1056
|
431 |
}
|
|
tss@1043
|
432 |
}
|
|
tss@2421
|
433 |
o_stream_uncork(client->output);
|
|
tss@1043
|
434 |
|
|
tss@7927
|
435 |
if (client->output->closed) {
|
|
tss@3384
|
436 |
client_destroy(client, NULL);
|
|
tss@7927
|
437 |
return FALSE;
|
|
tss@7927
|
438 |
}
|
|
tss@7927
|
439 |
return TRUE;
|
|
tss@7927
|
440 |
}
|
|
tss@7927
|
441 |
|
|
tss@7927
|
442 |
static void client_input(struct client *client)
|
|
tss@7927
|
443 |
{
|
|
tss@7927
|
444 |
if (client->cmd != NULL) {
|
|
tss@7927
|
445 |
/* we're still processing a command. wait until it's
|
|
tss@7927
|
446 |
finished. */
|
|
tss@7927
|
447 |
io_remove(&client->io);
|
|
tss@7927
|
448 |
client->waiting_input = TRUE;
|
|
tss@7927
|
449 |
return;
|
|
tss@7927
|
450 |
}
|
|
tss@7927
|
451 |
|
|
tss@7927
|
452 |
client->waiting_input = FALSE;
|
|
tss@7927
|
453 |
client->last_input = ioloop_time;
|
|
tss@7927
|
454 |
timeout_reset(client->to_idle);
|
|
tss@8718
|
455 |
if (client->to_commit != NULL)
|
|
tss@8718
|
456 |
timeout_reset(client->to_commit);
|
|
tss@7927
|
457 |
|
|
tss@7927
|
458 |
switch (i_stream_read(client->input)) {
|
|
tss@7927
|
459 |
case -1:
|
|
tss@7927
|
460 |
/* disconnected */
|
|
tss@7927
|
461 |
client_destroy(client, NULL);
|
|
tss@7927
|
462 |
return;
|
|
tss@7927
|
463 |
case -2:
|
|
tss@7927
|
464 |
/* line too long, kill it */
|
|
tss@7927
|
465 |
client_send_line(client, "-ERR Input line too long.");
|
|
tss@7927
|
466 |
client_destroy(client, "Input line too long");
|
|
tss@7927
|
467 |
return;
|
|
tss@7927
|
468 |
}
|
|
tss@7927
|
469 |
|
|
tss@7927
|
470 |
(void)client_handle_input(client);
|
|
tss@1043
|
471 |
}
|
|
tss@1043
|
472 |
|
|
tss@4907
|
473 |
static int client_output(struct client *client)
|
|
tss@2421
|
474 |
{
|
|
tss@2421
|
475 |
int ret;
|
|
tss@2421
|
476 |
|
|
tss@2421
|
477 |
if ((ret = o_stream_flush(client->output)) < 0) {
|
|
tss@3384
|
478 |
client_destroy(client, NULL);
|
|
tss@2790
|
479 |
return 1;
|
|
tss@2421
|
480 |
}
|
|
tss@2421
|
481 |
|
|
tss@2421
|
482 |
client->last_output = ioloop_time;
|
|
tss@7103
|
483 |
timeout_reset(client->to_idle);
|
|
tss@8718
|
484 |
if (client->to_commit != NULL)
|
|
tss@8718
|
485 |
timeout_reset(client->to_commit);
|
|
tss@2421
|
486 |
|
|
tss@3394
|
487 |
if (client->cmd != NULL) {
|
|
tss@3394
|
488 |
o_stream_cork(client->output);
|
|
tss@2494
|
489 |
client->cmd(client);
|
|
tss@3394
|
490 |
o_stream_uncork(client->output);
|
|
tss@3394
|
491 |
}
|
|
tss@2494
|
492 |
|
|
tss@3391
|
493 |
if (client->cmd == NULL) {
|
|
tss@3391
|
494 |
if (o_stream_get_buffer_used_size(client->output) <
|
|
tss@3391
|
495 |
OUTBUF_THROTTLE_SIZE/2 && client->io == NULL) {
|
|
tss@3391
|
496 |
/* enable input again */
|
|
tss@3391
|
497 |
client->io = io_add(i_stream_get_fd(client->input),
|
|
tss@3391
|
498 |
IO_READ, client_input, client);
|
|
tss@3391
|
499 |
}
|
|
tss@3391
|
500 |
if (client->io != NULL && client->waiting_input)
|
|
tss@3391
|
501 |
client_input(client);
|
|
tss@2421
|
502 |
}
|
|
tss@2494
|
503 |
|
|
tss@2790
|
504 |
return client->cmd == NULL;
|
|
tss@2421
|
505 |
}
|
|
tss@2421
|
506 |
|
|
tss@1043
|
507 |
void clients_init(void)
|
|
tss@1043
|
508 |
{
|
|
tss@1043
|
509 |
my_client = NULL;
|
|
tss@1043
|
510 |
}
|
|
tss@1043
|
511 |
|
|
tss@1043
|
512 |
void clients_deinit(void)
|
|
tss@1043
|
513 |
{
|
|
tss@1043
|
514 |
if (my_client != NULL) {
|
|
tss@1043
|
515 |
client_send_line(my_client, "-ERR Server shutting down.");
|
|
tss@4096
|
516 |
client_destroy(my_client, "Server shutting down");
|
|
tss@1043
|
517 |
}
|
|
tss@1043
|
518 |
}
|