165
154
mailbox_free(&ctx->box);
168
static bool cmd_append_continue_cancel(struct client_command_context *cmd)
170
struct cmd_append_context *ctx = cmd->context;
157
static bool cmd_append_send_literal_continue(struct cmd_append_context *ctx)
160
/* tagline was already sent, we can abort here */
164
o_stream_nsend(ctx->client->output, "+ OK\r\n", 6);
165
o_stream_nflush(ctx->client->output);
166
o_stream_uncork(ctx->client->output);
167
o_stream_cork(ctx->client->output);
172
cmd_append_catenate_mpurl(struct client_command_context *cmd,
173
const char *caturl, struct imap_msgpart_url *mpurl)
175
struct cmd_append_context *ctx = cmd->context;
176
struct imap_msgpart_open_result mpresult;
182
ret = imap_msgpart_url_read_part(mpurl, &mpresult, &error);
184
client_send_storage_error(cmd, ctx->storage);
188
/* invalid url, abort */
189
client_send_tagline(cmd,
190
t_strdup_printf("NO [BADURL %s] %s.", caturl, error));
193
if (mpresult.size == 0) {
198
newsize = ctx->cat_msg_size + mpresult.size;
199
if (newsize < ctx->cat_msg_size) {
200
client_send_tagline(cmd,
201
"NO [TOOBIG] Composed message grows too big.");
205
ctx->cat_msg_size = newsize;
206
/* add this input stream to chain */
207
i_stream_chain_append(ctx->catchain, mpresult.input);
208
/* save by reading the chain stream */
209
while (!i_stream_is_eof(mpresult.input)) {
210
ret = i_stream_read(mpresult.input);
211
i_assert(ret != 0); /* we can handle only blocking input here */
212
if (mailbox_save_continue(ctx->save_ctx) < 0 || ret == -1)
216
if (mpresult.input->stream_errno != 0) {
217
errno = mpresult.input->stream_errno;
218
mail_storage_set_critical(ctx->box->storage,
219
"read(%s) failed: %s (for CATENATE URL %s)",
220
i_stream_get_name(mpresult.input),
221
i_stream_get_error(mpresult.input), caturl);
222
client_send_storage_error(cmd, ctx->storage);
224
} else if (!mpresult.input->eof) {
226
client_send_storage_error(cmd, ctx->storage);
229
/* all the input must be consumed, so istream-chain's read()
230
unreferences the stream and we can free its parent mail */
231
i_assert(!i_stream_have_bytes_left(mpresult.input));
238
cmd_append_catenate_url(struct client_command_context *cmd, const char *caturl)
240
struct cmd_append_context *ctx = cmd->context;
241
struct imap_msgpart_url *mpurl;
248
ret = imap_msgpart_url_parse(cmd->client->user, cmd->client->mailbox,
249
caturl, &mpurl, &error);
251
client_send_storage_error(cmd, ctx->storage);
255
/* invalid url, abort */
256
client_send_tagline(cmd,
257
t_strdup_printf("NO [BADURL %s] %s.", caturl, error));
260
ret = cmd_append_catenate_mpurl(cmd, caturl, mpurl);
261
imap_msgpart_url_free(&mpurl);
265
static void cmd_append_catenate_text(struct client_command_context *cmd)
267
struct cmd_append_context *ctx = cmd->context;
269
if (ctx->literal_size > (uoff_t)-1 - ctx->cat_msg_size &&
271
client_send_tagline(cmd,
272
"NO [TOOBIG] Composed message grows too big.");
277
ctx->cat_msg_size += ctx->literal_size;
278
if (ctx->literal_size == 0) {
279
/* zero length literal. RFC doesn't explicitly specify
280
what should be done with this, so we'll simply
281
handle it by skipping the empty text part. */
282
ctx->litinput = i_stream_create_from_data("", 0);
283
ctx->litinput->eof = TRUE;
285
ctx->litinput = i_stream_create_limit(cmd->client->input,
287
i_stream_chain_append(ctx->catchain, ctx->litinput);
292
cmd_append_catenate(struct client_command_context *cmd,
293
const struct imap_arg *args, bool *nonsync_r)
295
struct cmd_append_context *ctx = cmd->context;
300
/* Handle URLs until a TEXT literal is encountered */
301
while (imap_arg_get_atom(args, &catpart)) {
304
if (strcasecmp(catpart, "URL") == 0 ) {
307
if (!imap_arg_get_astring(args, &caturl))
309
if (cmd_append_catenate_url(cmd, caturl) < 0) {
310
/* delay failure until we can stop
314
} else if (strcasecmp(catpart, "TEXT") == 0) {
317
if (!imap_arg_get_literal_size(args, &ctx->literal_size))
319
if (args->literal8 && !ctx->binary_input &&
321
client_send_tagline(cmd,
322
"NO ["IMAP_RESP_CODE_UNKNOWN_CTE"] "
323
"Binary input allowed only when the first part is binary.");
326
*nonsync_r = args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC;
327
cmd_append_catenate_text(cmd);
335
if (IMAP_ARG_IS_EOL(args)) {
340
client_send_command_error(cmd, "Invalid arguments.");
344
static void cmd_append_finish_catenate(struct client_command_context *cmd)
346
struct cmd_append_context *ctx = cmd->context;
348
i_stream_chain_append_eof(ctx->catchain);
349
i_stream_unref(&ctx->input);
350
ctx->catenate = FALSE;
351
ctx->catchain = NULL;
354
/* APPEND has already failed */
355
if (ctx->save_ctx != NULL)
356
mailbox_save_cancel(&ctx->save_ctx);
358
if (mailbox_save_finish(&ctx->save_ctx) < 0) {
359
client_send_storage_error(cmd, ctx->storage);
365
static bool catenate_args_can_stop(struct cmd_append_context *ctx,
366
const struct imap_arg *args)
368
/* eat away literal_sizes from URLs */
369
while (args->type != IMAP_ARG_EOL) {
370
if (imap_arg_atom_equals(args, "TEXT"))
372
if (!imap_arg_atom_equals(args, "URL")) {
373
/* error - handle it later */
377
if (args->type == IMAP_ARG_LITERAL_SIZE ||
378
args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC) {
379
if (args->type == IMAP_ARG_LITERAL_SIZE) {
380
if (!cmd_append_send_literal_continue(ctx))
383
imap_parser_read_last_literal(ctx->save_parser);
391
static bool cmd_append_continue_catenate(struct client_command_context *cmd)
393
struct client *client = cmd->client;
394
struct cmd_append_context *ctx = cmd->context;
395
const struct imap_arg *args;
397
bool fatal, nonsync = FALSE;
173
400
if (cmd->cancel) {
174
cmd_append_finish(ctx);
178
(void)i_stream_read(ctx->input);
179
(void)i_stream_get_data(ctx->input, &size);
180
i_stream_skip(ctx->input, size);
182
if (cmd->client->input->closed) {
183
cmd_append_finish(ctx);
187
if (ctx->input->v_offset == ctx->msg_size) {
188
/* finished, but with MULTIAPPEND and LITERAL+ we may get
190
i_stream_unref(&ctx->input);
193
ctx->message_input = FALSE;
401
/* cancel the command immediately (disconnection) */
402
cmd_append_finish(ctx);
406
/* we're parsing inside CATENATE (..) list after handling a TEXT part.
407
it's fine that this would need to fully fit into input buffer
408
(although clients attempting to DoS could simply insert an extra
409
{1+} between the URLs) */
411
ret = imap_parser_read_args(ctx->save_parser, 0,
412
IMAP_PARSE_FLAG_LITERAL_SIZE |
413
IMAP_PARSE_FLAG_LITERAL8 |
414
IMAP_PARSE_FLAG_INSIDE_LIST, &args);
415
} while (ret > 0 && !catenate_args_can_stop(ctx, args));
417
msg = imap_parser_get_error(ctx->save_parser, &fatal);
419
client_disconnect_with_error(client, msg);
420
else if (!ctx->failed)
421
client_send_command_error(cmd, msg);
422
client->input_skip_line = TRUE;
423
cmd_append_finish(ctx);
431
if ((ret = cmd_append_catenate(cmd, args, &nonsync)) < 0) {
432
/* invalid parameters, abort immediately */
433
cmd_append_finish(ctx);
439
cmd_append_finish_catenate(cmd);
441
/* last catenate part */
194
442
imap_parser_reset(ctx->save_parser);
195
cmd->func = cmd_append_continue_parsing;
196
return cmd_append_continue_parsing(cmd);
443
cmd->func = cmd_append_parse_new_msg;
444
return cmd_append_parse_new_msg(cmd);
202
static bool cmd_append_cancel(struct cmd_append_context *ctx, bool nonsync)
207
cmd_append_finish(ctx);
450
if (!cmd_append_send_literal_continue(ctx)) {
451
cmd_append_finish(ctx);
211
/* we have to read the nonsynced literal so we don't treat the message
213
ctx->input = i_stream_create_limit(ctx->client->input, ctx->msg_size);
456
i_assert(ctx->litinput != NULL);
215
457
ctx->message_input = TRUE;
216
ctx->cmd->func = cmd_append_continue_cancel;
217
ctx->cmd->context = ctx;
218
return cmd_append_continue_cancel(ctx->cmd);
458
cmd->func = cmd_append_continue_message;
459
return cmd_append_continue_message(cmd);
221
static bool cmd_append_continue_parsing(struct client_command_context *cmd)
463
cmd_append_handle_args(struct client_command_context *cmd,
464
const struct imap_arg *args, bool *nonsync_r)
223
466
struct client *client = cmd->client;
224
467
struct cmd_append_context *ctx = cmd->context;
225
const struct imap_arg *args;
226
468
const struct imap_arg *flags_list;
469
const struct imap_arg *cat_list = NULL;
227
470
enum mail_flags flags;
228
471
const char *const *keywords_list;
229
472
struct mail_keywords *keywords;
230
const char *internal_date_str, *msg;
473
struct istream *input;
474
const char *internal_date_str;
231
475
time_t internal_date;
232
476
int ret, timezone_offset;
233
unsigned int save_count;
237
cmd_append_finish(ctx);
241
/* if error occurs, the CRLF is already read. */
242
client->input_skip_line = FALSE;
244
/* [<flags>] [<internal date>] <message literal> */
245
ret = imap_parser_read_args(ctx->save_parser, 0,
246
IMAP_PARSE_FLAG_LITERAL_SIZE, &args);
249
msg = imap_parser_get_error(ctx->save_parser, &fatal);
251
client_disconnect_with_error(client, msg);
253
client_send_command_error(cmd, msg);
255
cmd_append_finish(ctx);
263
if (IMAP_ARG_IS_EOL(args)) {
265
enum mailbox_sync_flags sync_flags;
266
enum imap_sync_flags imap_flags;
267
struct mail_transaction_commit_changes changes;
270
/* eat away the trailing CRLF */
271
client->input_skip_line = TRUE;
274
/* we failed earlier, error message is sent */
275
cmd_append_finish(ctx);
278
if (ctx->count == 0) {
279
client_send_tagline(cmd, "BAD Missing message size.");
280
cmd_append_finish(ctx);
284
ret = mailbox_transaction_commit_get_changes(&ctx->t, &changes);
286
client_send_storage_error(cmd, ctx->storage);
287
cmd_append_finish(ctx);
291
msg = t_str_new(256);
292
save_count = seq_range_count(&changes.saved_uids);
293
if (save_count == 0) {
294
/* not supported by backend (virtual) */
295
str_append(msg, "OK Append completed.");
297
i_assert(ctx->count == save_count);
298
str_printfa(msg, "OK [APPENDUID %u ",
299
changes.uid_validity);
300
imap_write_seq_range(msg, &changes.saved_uids);
301
str_append(msg, "] Append completed.");
303
pool_unref(&changes.pool);
305
if (ctx->box == cmd->client->mailbox) {
307
imap_flags = IMAP_SYNC_FLAG_SAFE;
309
sync_flags = MAILBOX_SYNC_FLAG_FAST;
313
cmd_append_finish(ctx);
314
return cmd_sync(cmd, sync_flags, imap_flags, str_c(msg));
317
if (!validate_args(args, &flags_list, &internal_date_str,
318
&ctx->msg_size, &nonsync)) {
319
client_send_command_error(cmd, "Invalid arguments.");
320
client->input_skip_line = TRUE;
321
return cmd_append_cancel(ctx, nonsync);
325
/* we failed earlier, make sure we just eat nonsync-literal
327
return cmd_append_cancel(ctx, nonsync);
330
if (flags_list != NULL) {
480
if (!imap_arg_get_list(args, &flags_list))
485
/* [<internal date>] */
486
if (args->type != IMAP_ARG_STRING)
487
internal_date_str = NULL;
489
internal_date_str = imap_arg_as_astring(args);
493
/* <message literal> | CATENATE (..) */
496
ctx->catenate = FALSE;
497
if (imap_arg_atom_equals(args, "CATENATE")) {
499
if (imap_arg_get_list(args, &cat_list)) {
501
ctx->catenate = TRUE;
503
/* We'll do BINARY conversion only if the CATENATE's first
504
part is a literal8. If it doesn't and a literal8 is seen
505
later we'll abort the append with UNKNOWN-CTE. */
506
ctx->binary_input = imap_arg_atom_equals(&cat_list[0], "TEXT") &&
507
cat_list[1].literal8;
509
} else if (imap_arg_get_literal_size(args, &ctx->literal_size)) {
510
*nonsync_r = args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC;
511
ctx->binary_input = args->literal8;
514
if (!IMAP_ARG_IS_EOL(&args[1]))
517
client->input_skip_line = TRUE;
519
client_send_command_error(cmd, "Invalid arguments.");
523
if (flags_list == NULL || ctx->failed) {
331
527
if (!client_parse_mail_flags(cmd, flags_list,
332
528
&flags, &keywords_list))
333
return cmd_append_cancel(ctx, nonsync);
334
530
if (keywords_list == NULL)
336
532
else if (mailbox_keywords_create(ctx->box, keywords_list,
337
533
&keywords) < 0) {
534
/* invalid keywords - delay failure */
338
535
client_send_storage_error(cmd, ctx->storage);
339
return cmd_append_cancel(ctx, nonsync);
346
if (internal_date_str == NULL) {
540
if (internal_date_str == NULL || ctx->failed) {
347
541
/* no time given, default to now. */
348
542
internal_date = (time_t)-1;
349
543
timezone_offset = 0;
350
544
} else if (!imap_parse_datetime(internal_date_str,
351
545
&internal_date, &timezone_offset)) {
352
client_send_tagline(cmd, "BAD Invalid internal date.");
353
return cmd_append_cancel(ctx, nonsync);
546
client_send_command_error(cmd, "Invalid internal date.");
547
if (keywords != NULL)
548
mailbox_keywords_unref(&keywords);
356
552
if (internal_date != (time_t)-1 &&
360
556
timezone_offset = 0;
363
if (ctx->msg_size == 0) {
364
/* no message data, abort */
365
client_send_tagline(cmd, "NO Can't save a zero byte message.");
366
return cmd_append_cancel(ctx, nonsync);
370
ctx->input = i_stream_create_limit(client->input, ctx->msg_size);
371
ctx->save_ctx = mailbox_save_alloc(ctx->t);
372
mailbox_save_set_flags(ctx->save_ctx, flags, keywords);
373
mailbox_save_set_received_date(ctx->save_ctx,
374
internal_date, timezone_offset);
375
ret = mailbox_save_begin(&ctx->save_ctx, ctx->input);
559
if (cat_list != NULL) {
560
ctx->cat_msg_size = 0;
561
ctx->input = i_stream_create_chain(&ctx->catchain);
563
if (ctx->literal_size == 0) {
564
/* no message data, abort */
566
client_send_tagline(cmd,
567
"NO Can't save a zero byte message.");
571
if (keywords != NULL)
572
mailbox_keywords_unref(&keywords);
575
/* {0+} used. although there isn't any point in using
576
MULTIAPPEND here and adding more messages, it is
577
technically valid so we'll continue parsing.. */
579
ctx->litinput = i_stream_create_limit(client->input, ctx->literal_size);
580
ctx->input = ctx->litinput;
581
i_stream_ref(ctx->input);
583
if (ctx->binary_input) {
584
input = i_stream_create_binary_converter(ctx->input);
585
i_stream_unref(&ctx->input);
591
ctx->save_ctx = mailbox_save_alloc(ctx->t);
592
mailbox_save_set_flags(ctx->save_ctx, flags, keywords);
593
mailbox_save_set_received_date(ctx->save_ctx,
594
internal_date, timezone_offset);
595
if (mailbox_save_begin(&ctx->save_ctx, ctx->input) < 0) {
596
/* save initialization failed */
597
client_send_storage_error(cmd, ctx->storage);
377
601
if (keywords != NULL)
378
602
mailbox_keywords_unref(&keywords);
381
/* save initialization failed */
382
client_send_storage_error(cmd, ctx->storage);
383
return cmd_append_cancel(ctx, nonsync);
386
/* after literal comes CRLF, if we fail make sure we eat it away */
387
client->input_skip_line = TRUE;
390
o_stream_send(client->output, "+ OK\r\n", 6);
391
o_stream_flush(client->output);
392
o_stream_uncork(client->output);
393
o_stream_cork(client->output);
605
if (cat_list == NULL) {
608
} else if (cat_list->type == IMAP_ARG_EOL) {
611
client_send_command_error(cmd, "Empty CATENATE list.");
612
client->input_skip_line = TRUE;
614
} else if ((ret = cmd_append_catenate(cmd, cat_list, nonsync_r)) < 0) {
615
/* invalid parameters, abort immediately */
617
} else if (ret == 0) {
618
/* CATENATE consisted only of URLs */
621
/* TEXT part found from CATENATE */
626
static bool cmd_append_finish_parsing(struct client_command_context *cmd)
628
struct cmd_append_context *ctx = cmd->context;
629
enum mailbox_sync_flags sync_flags;
630
enum imap_sync_flags imap_flags;
631
struct mail_transaction_commit_changes changes;
632
unsigned int save_count;
636
/* eat away the trailing CRLF */
637
cmd->client->input_skip_line = TRUE;
640
/* we failed earlier, error message is sent */
641
cmd_append_finish(ctx);
644
if (ctx->count == 0) {
645
client_send_command_error(cmd, "Missing message size.");
646
cmd_append_finish(ctx);
650
ret = mailbox_transaction_commit_get_changes(&ctx->t, &changes);
652
client_send_storage_error(cmd, ctx->storage);
653
cmd_append_finish(ctx);
657
msg = t_str_new(256);
658
save_count = seq_range_count(&changes.saved_uids);
659
if (save_count == 0 || changes.no_read_perm) {
660
/* not supported by backend (virtual) */
661
str_append(msg, "OK Append completed.");
663
i_assert(ctx->count == save_count);
664
str_printfa(msg, "OK [APPENDUID %u ",
665
changes.uid_validity);
666
imap_write_seq_range(msg, &changes.saved_uids);
667
str_append(msg, "] Append completed.");
669
pool_unref(&changes.pool);
671
if (ctx->box == cmd->client->mailbox) {
673
imap_flags = IMAP_SYNC_FLAG_SAFE;
675
sync_flags = MAILBOX_SYNC_FLAG_FAST;
679
cmd_append_finish(ctx);
680
return cmd_sync(cmd, sync_flags, imap_flags, str_c(msg));
683
static bool cmd_append_args_can_stop(struct cmd_append_context *ctx,
684
const struct imap_arg *args,
685
bool *last_literal_r)
687
const struct imap_arg *cat_list;
689
*last_literal_r = FALSE;
690
if (args->type == IMAP_ARG_EOL)
693
/* [(flags)] ["internal date"] <message literal> | CATENATE (..) */
694
if (args->type == IMAP_ARG_LIST)
696
if (args->type == IMAP_ARG_STRING)
699
if (args->type == IMAP_ARG_LITERAL_SIZE ||
700
args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC)
702
if (imap_arg_atom_equals(args, "CATENATE") &&
703
imap_arg_get_list(&args[1], &cat_list)) {
704
if (catenate_args_can_stop(ctx, cat_list))
706
*last_literal_r = TRUE;
711
static bool cmd_append_parse_new_msg(struct client_command_context *cmd)
713
struct client *client = cmd->client;
714
struct cmd_append_context *ctx = cmd->context;
715
const struct imap_arg *args;
717
unsigned int arg_min_count;
718
bool fatal, nonsync, last_literal;
721
/* this function gets called 1) after parsing APPEND <mailbox> and
722
2) with MULTIAPPEND extension after already saving one or more
725
/* cancel the command immediately (disconnection) */
726
cmd_append_finish(ctx);
730
/* if error occurs, the CRLF is already read. */
731
client->input_skip_line = FALSE;
733
/* parse the entire line up to the first message literal, or in case
734
the input buffer is full of MULTIAPPEND CATENATE URLs, parse at
735
least until the beginning of the next message */
736
arg_min_count = 0; last_literal = FALSE;
741
/* we only read the literal size. now we read the
744
ret = imap_parser_read_args(ctx->save_parser, arg_min_count,
745
IMAP_PARSE_FLAG_LITERAL_SIZE |
746
IMAP_PARSE_FLAG_LITERAL8, &args);
747
} while (ret >= (int)arg_min_count &&
748
!cmd_append_args_can_stop(ctx, args, &last_literal));
751
msg = imap_parser_get_error(ctx->save_parser, &fatal);
753
client_disconnect_with_error(client, msg);
755
client_send_command_error(cmd, msg);
757
cmd_append_finish(ctx);
765
if (IMAP_ARG_IS_EOL(args)) {
767
return cmd_append_finish_parsing(cmd);
770
ret = cmd_append_handle_args(cmd, args, &nonsync);
772
/* invalid parameters, abort immediately */
773
cmd_append_finish(ctx);
777
/* CATENATE contained only URLs. Finish it and see if there
778
are more messsages. */
779
cmd_append_finish_catenate(cmd);
780
imap_parser_reset(ctx->save_parser);
781
return cmd_append_parse_new_msg(cmd);
785
if (!cmd_append_send_literal_continue(ctx)) {
786
cmd_append_finish(ctx);
791
i_assert(ctx->litinput != NULL);
397
792
ctx->message_input = TRUE;
398
793
cmd->func = cmd_append_continue_message;
399
794
return cmd_append_continue_message(cmd);
428
823
if (ctx->save_ctx == NULL) {
824
/* saving has already failed, we're just eating away the
826
(void)i_stream_read(ctx->litinput);
827
i_stream_skip(ctx->litinput,
828
i_stream_get_data_size(ctx->litinput));
831
if (ctx->litinput->eof || client->input->closed) {
832
uoff_t lit_offset = ctx->litinput->v_offset;
834
/* finished - do one more read, to make sure istream-chain
835
unreferences its stream, which is needed for litinput's
836
unreferencing to seek the client->input to correct
837
position. the seek is needed to avoid trying to seek
838
backwards in the ctx->input's parent stream. */
839
i_stream_seek(ctx->input, ctx->input->v_offset);
429
840
(void)i_stream_read(ctx->input);
430
(void)i_stream_get_data(ctx->input, &size);
431
i_stream_skip(ctx->input, size);
434
if (ctx->input->eof || client->input->closed) {
435
bool all_written = ctx->input->v_offset == ctx->msg_size;
438
i_stream_unref(&ctx->input);
441
if (ctx->save_ctx == NULL) {
841
i_stream_unref(&ctx->litinput);
844
if (ctx->save_ctx != NULL)
845
mailbox_save_cancel(&ctx->save_ctx);
846
} else if (ctx->save_ctx == NULL) {
442
847
/* failed above */
443
848
client_send_storage_error(cmd, ctx->storage);
444
849
ctx->failed = TRUE;
445
} else if (!all_written) {
850
} else if (lit_offset != ctx->literal_size) {
446
851
/* client disconnected before it finished sending the
447
852
whole message. */
448
853
ctx->failed = TRUE;
449
854
mailbox_save_cancel(&ctx->save_ctx);
450
client_disconnect(client, "EOF while appending");
855
client_disconnect(client,
856
get_disconnect_reason(ctx, lit_offset));
857
} else if (ctx->catenate) {
858
/* CATENATE isn't finished yet */
451
859
} else if (mailbox_save_finish(&ctx->save_ctx) < 0) {
860
client_send_storage_error(cmd, ctx->storage);
452
861
ctx->failed = TRUE;
453
client_send_storage_error(cmd, ctx->storage);
455
ctx->save_ctx = NULL;
457
864
if (client->input->closed) {
458
865
cmd_append_finish(ctx);
462
/* prepare for next message */
869
/* prepare for the next message (or its part with catenate) */
463
870
ctx->message_input = FALSE;
464
871
imap_parser_reset(ctx->save_parser);
465
cmd->func = cmd_append_continue_parsing;
466
return cmd_append_continue_parsing(cmd);
874
cmd->func = cmd_append_continue_catenate;
875
return cmd_append_continue_catenate(cmd);
878
i_stream_unref(&ctx->input);
879
cmd->func = cmd_append_parse_new_msg;
880
return cmd_append_parse_new_msg(cmd);