doveadm-mail-fetch.c revision 1631885636d15abaf0375304a17928c8c23782cd
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen#include "lib.h"
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen#include "array.h"
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen#include "istream.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "ostream.h"
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen#include "base64.h"
57f5683fd9dc9bc79816c418bb30fdbc33b68a8cTimo Sirainen#include "randgen.h"
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen#include "str.h"
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen#include "message-size.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "imap-utf7.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "imap-util.h"
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen#include "mail-user.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "mail-storage.h"
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen#include "mail-search.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "doveadm-mail.h"
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include "doveadm-mail-list-iter.h"
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen#include "doveadm-mail-iter.h"
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen#include <stdio.h>
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainenstruct fetch_cmd_context {
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen struct doveadm_mail_cmd_context ctx;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen struct ostream *output;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen struct mail *mail;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen ARRAY_DEFINE(fields, const struct fetch_field);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen ARRAY_TYPE(const_string) header_fields;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen enum mail_fetch_field wanted_fields;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen const struct fetch_field *cur_field;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen string_t *hdr;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const char *prefix;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen bool print_field_prefix;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen};
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainenstruct fetch_field {
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen const char *name;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen enum mail_fetch_field wanted_fields;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen int (*print)(struct fetch_cmd_context *ctx);
4c892b0d94c5b1d6853dbe8e0b38059ea5b08ecaTimo Sirainen};
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
abc79eec93e58e0152cd1d483f37be66c26811b9Timo Sirainenstatic int fetch_user(struct fetch_cmd_context *ctx)
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen{
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen str_append(ctx->hdr, ctx->ctx.cur_mail_user->username);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen return 0;
4c892b0d94c5b1d6853dbe8e0b38059ea5b08ecaTimo Sirainen}
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainenstatic int fetch_mailbox(struct fetch_cmd_context *ctx)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen{
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen const char *value;
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen unsigned int len;
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME, &value) < 0)
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen return -1;
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen len = str_len(ctx->hdr);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen if (imap_utf7_to_utf8(value, ctx->hdr) < 0) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* not a valid mUTF-7 name, fallback to showing it as-is */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen str_truncate(ctx->hdr, len);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen str_append(ctx->hdr, value);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return 0;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic int fetch_mailbox_guid(struct fetch_cmd_context *ctx)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen uint8_t guid[MAIL_GUID_128_SIZE];
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (mailbox_get_guid(ctx->mail->box, guid) < 0)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return -1;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen str_append(ctx->hdr, mail_guid_128_to_string(guid));
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return 0;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic int fetch_seq(struct fetch_cmd_context *ctx)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen str_printfa(ctx->hdr, "%u", ctx->mail->seq);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return 0;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic int fetch_uid(struct fetch_cmd_context *ctx)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen str_printfa(ctx->hdr, "%u", ctx->mail->seq);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return 0;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic int fetch_guid(struct fetch_cmd_context *ctx)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const char *value;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (mail_get_special(ctx->mail, MAIL_FETCH_GUID, &value) < 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return -1;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen str_append(ctx->hdr, value);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainenstatic int fetch_flags(struct fetch_cmd_context *ctx)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen imap_write_flags(ctx->hdr, mail_get_flags(ctx->mail),
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mail_get_keywords(ctx->mail));
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstatic void flush_hdr(struct fetch_cmd_context *ctx)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen o_stream_send(ctx->output, str_data(ctx->hdr), str_len(ctx->hdr));
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen str_truncate(ctx->hdr, 0);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic int fetch_hdr(struct fetch_cmd_context *ctx)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct istream *input;
65889a7d8c059e2feb159aee1633b847aba84831Timo Sirainen struct message_size hdr_size;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen int ret = 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainen if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0)
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen return -1;
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen if (ctx->print_field_prefix)
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen str_append_c(ctx->hdr, '\n');
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen flush_hdr(ctx);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen input = i_stream_create_limit(input, hdr_size.physical_size);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen while (!i_stream_is_eof(input)) {
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen if (o_stream_send_istream(ctx->output, input) <= 0)
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen i_fatal("write(stdout) failed: %m");
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen }
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen if (input->stream_errno != 0) {
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen i_error("read() failed: %m");
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen ret = -1;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen }
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen i_stream_unref(&input);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen o_stream_flush(ctx->output);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen return ret;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen}
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainenstatic int fetch_hdr_field(struct fetch_cmd_context *ctx)
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen{
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen const char *const *value;
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen bool add_lf = FALSE;
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen if (mail_get_headers(ctx->mail, ctx->cur_field->name, &value) < 0)
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen return -1;
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen for (; *value != NULL; value++) {
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen if (add_lf)
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen str_append_c(ctx->hdr, '\n');
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen if (ctx->print_field_prefix)
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen str_printfa(ctx->hdr, "hdr.%s: ", ctx->cur_field->name);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen str_append(ctx->hdr, *value);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen add_lf = TRUE;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen }
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen return 0;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen}
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainenstatic int fetch_body(struct fetch_cmd_context *ctx)
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen{
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen struct istream *input;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen struct message_size hdr_size;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen int ret = 0;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0)
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainen return -1;
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (ctx->print_field_prefix)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen str_append_c(ctx->hdr, '\n');
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen flush_hdr(ctx);
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen i_stream_skip(input, hdr_size.physical_size);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen while (!i_stream_is_eof(input)) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (o_stream_send_istream(ctx->output, input) <= 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_fatal("write(stdout) failed: %m");
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (input->stream_errno != 0) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_error("read() failed: %m");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen ret = -1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen o_stream_flush(ctx->output);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return ret;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstatic int fetch_text(struct fetch_cmd_context *ctx)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct istream *input;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen int ret = 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return -1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (ctx->print_field_prefix)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen str_append_c(ctx->hdr, '\n');
a2150da2dc906c26a26219cbefbe28a119aafee2Timo Sirainen flush_hdr(ctx);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen while (!i_stream_is_eof(input)) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (o_stream_send_istream(ctx->output, input) <= 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_fatal("write(stdout) failed: %m");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (input->stream_errno != 0) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_error("read() failed: %m");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen ret = -1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen o_stream_flush(ctx->output);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return ret;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstatic int fetch_size_physical(struct fetch_cmd_context *ctx)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen uoff_t size;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (mail_get_physical_size(ctx->mail, &size) < 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return -1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen str_printfa(ctx->hdr, "%"PRIuUOFF_T, size);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
88b0427d90f1d3c2c5fb3171e53a505c46e2c39dTimo Sirainenstatic int fetch_size_virtual(struct fetch_cmd_context *ctx)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen uoff_t size;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (mail_get_virtual_size(ctx->mail, &size) < 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return -1;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen str_printfa(ctx->hdr, "%"PRIuUOFF_T, size);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen return 0;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen}
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainenstatic int fetch_date_received(struct fetch_cmd_context *ctx)
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen{
94ce7e7700cda14a8342cb08e7285507b4b531daTimo Sirainen time_t t;
94ce7e7700cda14a8342cb08e7285507b4b531daTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (mail_get_received_date(ctx->mail, &t) < 0)
94ce7e7700cda14a8342cb08e7285507b4b531daTimo Sirainen return -1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen str_printfa(ctx->hdr, "%s", unixdate2str(t));
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return 0;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen}
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainenstatic int fetch_date_sent(struct fetch_cmd_context *ctx)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen{
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen time_t t;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen int tz;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen char chr;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (mail_get_date(ctx->mail, &t, &tz) < 0)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return -1;
94ce7e7700cda14a8342cb08e7285507b4b531daTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen chr = tz < 0 ? '-' : '+';
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (tz < 0) tz = -tz;
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen str_printfa(ctx->hdr, "%s (%c%02u%02u)", unixdate2str(t),
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen chr, tz/60, tz%60);
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen return 0;
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen}
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainenstatic int fetch_date_saved(struct fetch_cmd_context *ctx)
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen{
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen time_t t;
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen if (mail_get_save_date(ctx->mail, &t) < 0)
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen return -1;
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen str_printfa(ctx->hdr, "%s", unixdate2str(t));
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen return 0;
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen}
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainenstatic int fetch_imap_envelope(struct fetch_cmd_context *ctx)
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen{
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen const char *value;
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_ENVELOPE, &value) < 0)
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen return -1;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen str_append(ctx->hdr, value);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen return 0;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen}
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainenstatic int fetch_imap_body(struct fetch_cmd_context *ctx)
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen{
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen const char *value;
5736aef6d0abe6796e57c2eda68f5c25db677918Timo Sirainen
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_BODY, &value) < 0)
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen return -1;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen str_append(ctx->hdr, value);
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen return 0;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen}
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainen
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainenstatic int fetch_imap_bodystructure(struct fetch_cmd_context *ctx)
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainen{
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainen const char *value;
72276c90ac2a38c9db7b4458acd3a2f5b61892bbTimo Sirainen
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainen if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, &value) < 0)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return -1;
7afde4b6c600f86ef6f742ea3b01640075ce16a2Timo Sirainen str_append(ctx->hdr, value);
7afde4b6c600f86ef6f742ea3b01640075ce16a2Timo Sirainen return 0;
5e751dbaecf7c337abc149f328c4a13ee5c15134Timo Sirainen}
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainenstatic const struct fetch_field fetch_fields[] = {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen { "user", 0, fetch_user },
72276c90ac2a38c9db7b4458acd3a2f5b61892bbTimo Sirainen { "mailbox", 0, fetch_mailbox },
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen { "mailbox-guid", 0, fetch_mailbox_guid },
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen { "seq", 0, fetch_seq },
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen { "uid", 0, fetch_uid },
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen { "guid", 0, fetch_guid },
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen { "flags", MAIL_FETCH_FLAGS, fetch_flags },
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen { "hdr", MAIL_FETCH_STREAM_HEADER, fetch_hdr },
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen { "body", MAIL_FETCH_STREAM_BODY, fetch_body },
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen { "text", MAIL_FETCH_STREAM_HEADER |
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen MAIL_FETCH_STREAM_BODY, fetch_text },
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen { "size.physical", MAIL_FETCH_PHYSICAL_SIZE, fetch_size_physical },
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen { "size.virtual", MAIL_FETCH_VIRTUAL_SIZE, fetch_size_virtual },
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen { "date.received", MAIL_FETCH_RECEIVED_DATE, fetch_date_received },
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen { "date.sent", MAIL_FETCH_DATE, fetch_date_sent },
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen { "date.saved", MAIL_FETCH_SAVE_DATE, fetch_date_saved },
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen { "imap.envelope", MAIL_FETCH_IMAP_ENVELOPE, fetch_imap_envelope },
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen { "imap.body", MAIL_FETCH_IMAP_BODY, fetch_imap_body },
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen { "imap.bodystructure", MAIL_FETCH_IMAP_BODYSTRUCTURE, fetch_imap_bodystructure }
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen};
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainenstatic const struct fetch_field *fetch_field_find(const char *name)
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen{
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen unsigned int i;
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen for (i = 0; i < N_ELEMENTS(fetch_fields); i++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (strcmp(fetch_fields[i].name, name) == 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return &fetch_fields[i];
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return NULL;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic void print_fetch_fields(void)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen unsigned int i;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen fprintf(stderr, "Available fetch fields: %s", fetch_fields[0].name);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen for (i = 1; i < N_ELEMENTS(fetch_fields); i++)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen fprintf(stderr, " %s", fetch_fields[i].name);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen fprintf(stderr, "\n");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void parse_fetch_fields(struct fetch_cmd_context *ctx, const char *str)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const char *const *fields, *name;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const struct fetch_field *field;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct fetch_field hdr_field;
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen memset(&hdr_field, 0, sizeof(hdr_field));
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen hdr_field.print = fetch_hdr_field;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen
d2d5871fa9e7226df694ff7a4be511167b35b305Timo Sirainen t_array_init(&ctx->fields, 32);
d2c853636ec2d99c9f96da877ff520a3b86a18baTimo Sirainen t_array_init(&ctx->header_fields, 32);
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen fields = t_strsplit_spaces(str, " ");
57f5683fd9dc9bc79816c418bb30fdbc33b68a8cTimo Sirainen for (; *fields != NULL; fields++) {
57f5683fd9dc9bc79816c418bb30fdbc33b68a8cTimo Sirainen name = t_str_lcase(*fields);
57f5683fd9dc9bc79816c418bb30fdbc33b68a8cTimo Sirainen
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen if (strncmp(name, "hdr.", 4) == 0) {
57f5683fd9dc9bc79816c418bb30fdbc33b68a8cTimo Sirainen name += 4;
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen hdr_field.name = name;
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen array_append(&ctx->fields, &hdr_field, 1);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen array_append(&ctx->header_fields, &name, 1);
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen } else {
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen field = fetch_field_find(name);
b16ee3cbbcd18cb86f2f73b5cc163ebfb995ffafTimo Sirainen if (field == NULL) {
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen print_fetch_fields();
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen i_fatal("Unknown fetch field: %s", name);
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen }
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen ctx->wanted_fields |= field->wanted_fields;
b16ee3cbbcd18cb86f2f73b5cc163ebfb995ffafTimo Sirainen array_append(&ctx->fields, field, 1);
b16ee3cbbcd18cb86f2f73b5cc163ebfb995ffafTimo Sirainen }
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen }
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen (void)array_append_space(&ctx->header_fields);
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen ctx->print_field_prefix = array_count(&ctx->fields) > 1;
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen}
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainenstatic void cmd_fetch_mail(struct fetch_cmd_context *ctx)
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen{
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen const struct fetch_field *field;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen struct mail *mail = ctx->mail;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen array_foreach(&ctx->fields, field) {
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen if (ctx->print_field_prefix && field->print != fetch_hdr_field)
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen str_printfa(ctx->hdr, "%s: ", field->name);
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen ctx->cur_field = field;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen if (field->print(ctx) < 0) {
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen struct mail_storage *storage =
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen mailbox_get_storage(mail->box);
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen i_error("fetch(%s) failed for box=%s uid=%u: %s",
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen field->name, mailbox_get_vname(mail->box),
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen mail->uid, mail_storage_get_last_error(storage, NULL));
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen }
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen str_append_c(ctx->hdr, '\n');
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen }
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen flush_hdr(ctx);
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen}
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainenstatic int
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainencmd_fetch_box(struct fetch_cmd_context *ctx, const struct mailbox_info *info)
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen{
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen struct doveadm_mail_iter *iter;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen struct mailbox_transaction_context *trans;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen struct mail *mail;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen struct mailbox_header_lookup_ctx *headers = NULL;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen if (doveadm_mail_iter_init(info, ctx->ctx.search_args,
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen &trans, &iter) < 0)
return -1;
if (array_count(&ctx->header_fields) > 1) {
headers = mailbox_header_lookup_init(
mailbox_transaction_get_mailbox(trans),
array_idx(&ctx->header_fields, 0));
}
mail = mail_alloc(trans, ctx->wanted_fields, headers);
if (headers != NULL)
mailbox_header_lookup_unref(&headers);
while (doveadm_mail_iter_next(iter, mail)) {
str_truncate(ctx->hdr, 0);
str_append(ctx->hdr, ctx->prefix);
ctx->mail = mail;
cmd_fetch_mail(ctx);
ctx->mail = NULL;
}
mail_free(&mail);
return doveadm_mail_iter_deinit(&iter);
}
static bool search_args_have_unique_fetch(struct mail_search_args *args)
{
struct mail_search_arg *arg;
const struct seq_range *seqset;
unsigned int count;
bool have_mailbox = FALSE, have_msg = FALSE;
for (arg = args->args; arg != NULL; arg = arg->next) {
switch (arg->type) {
case SEARCH_MAILBOX:
case SEARCH_MAILBOX_GUID:
if (!arg->not)
have_mailbox = TRUE;
break;
case SEARCH_SEQSET:
case SEARCH_UIDSET:
seqset = array_get(&arg->value.seqset, &count);
if (count == 1 && seqset->seq1 == seqset->seq2 &&
!arg->not)
have_msg = TRUE;
break;
default:
break;
}
}
return have_mailbox && have_msg;
}
static void
cmd_fetch_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user)
{
struct fetch_cmd_context *ctx = (struct fetch_cmd_context *)_ctx;
const enum mailbox_list_iter_flags iter_flags =
MAILBOX_LIST_ITER_VIRTUAL_NAMES |
MAILBOX_LIST_ITER_NO_AUTO_INBOX |
MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
struct doveadm_mail_list_iter *iter;
const struct mailbox_info *info;
iter = doveadm_mail_list_iter_init(user, _ctx->search_args, iter_flags);
while ((info = doveadm_mail_list_iter_next(iter)) != NULL) T_BEGIN {
(void)cmd_fetch_box(ctx, info);
} T_END;
doveadm_mail_list_iter_deinit(&iter);
}
static void cmd_fetch_deinit(struct doveadm_mail_cmd_context *_ctx)
{
struct fetch_cmd_context *ctx = (struct fetch_cmd_context *)_ctx;
o_stream_unref(&ctx->output);
str_free(&ctx->hdr);
}
static void cmd_fetch_init(struct doveadm_mail_cmd_context *_ctx,
const char *const args[])
{
struct fetch_cmd_context *ctx = (struct fetch_cmd_context *)_ctx;
const char *fetch_fields = args[0];
unsigned char prefix_buf[9];
if (fetch_fields == NULL || args[1] == NULL)
doveadm_mail_help_name("fetch");
parse_fetch_fields(ctx, fetch_fields);
_ctx->search_args = doveadm_mail_build_search_args(args + 1);
ctx->output = o_stream_create_fd(STDOUT_FILENO, 0, FALSE);
ctx->hdr = str_new(default_pool, 512);
if (search_args_have_unique_fetch(_ctx->search_args))
ctx->prefix = "";
else {
random_fill_weak(prefix_buf, sizeof(prefix_buf));
str_append(ctx->hdr, "===");
base64_encode(prefix_buf, sizeof(prefix_buf), ctx->hdr);
str_append_c(ctx->hdr, '\n');
ctx->prefix = t_strdup(str_c(ctx->hdr));
str_truncate(ctx->hdr, 0);
}
}
static struct doveadm_mail_cmd_context *cmd_fetch_alloc(void)
{
struct fetch_cmd_context *ctx;
ctx = doveadm_mail_cmd_alloc(struct fetch_cmd_context);
ctx->ctx.v.init = cmd_fetch_init;
ctx->ctx.v.run = cmd_fetch_run;
ctx->ctx.v.deinit = cmd_fetch_deinit;
return &ctx->ctx;
}
struct doveadm_mail_cmd cmd_fetch = {
cmd_fetch_alloc, "fetch", "<fields> <search query>"
};