1 /* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "hex-dec.h"
5 #include "istream.h"
6 #include "index/dbox-common/dbox-file.h"
7 #include "doveadm-dump.h"
8 
9 #include <stdio.h>
10 #include <fcntl.h>
11 #include <unistd.h>
12 
13 static void
dump_timestamp(struct istream * input,const char * name,const char * value)14 dump_timestamp(struct istream *input, const char *name, const char *value)
15 {
16 	time_t t;
17 
18 	if (strcmp(value, "0") == 0)
19 		t = 0;
20 	else {
21 		t = hex2dec((const void *)value, strlen(value));
22 		if (t == 0) {
23 			i_fatal("Invalid %s at %"PRIuUOFF_T": %s",
24 				name, input->v_offset, value);
25 		}
26 	}
27 	printf("%s = %ld (%s)\n", name, (long)t, unixdate2str(t));
28 }
29 
30 static uoff_t
dump_size(struct istream * input,const char * name,const char * value)31 dump_size(struct istream *input, const char *name, const char *value)
32 {
33 	uoff_t size;
34 
35 	if (strcmp(value, "0") == 0)
36 		size = 0;
37 	else {
38 		size = hex2dec((const void *)value, strlen(value));
39 		if (size == 0) {
40 			i_fatal("Invalid %s at %"PRIuUOFF_T": %s",
41 				name, input->v_offset, value);
42 		}
43 	}
44 	printf("%s = %"PRIuUOFF_T"\n", name, size);
45 	return size;
46 }
47 
dump_file_hdr(struct istream * input)48 static unsigned int dump_file_hdr(struct istream *input)
49 {
50 	const char *line, *const *arg, *version;
51 	unsigned int msg_hdr_size = 0;
52 
53 	if ((line = i_stream_read_next_line(input)) == NULL)
54 		i_fatal("Empty file");
55 	arg = t_strsplit(line, " ");
56 
57 	/* check version */
58 	version = *arg;
59 	if (version == NULL || !str_is_numeric(version, ' '))
60 		i_fatal("%s is not a dbox file", i_stream_get_name(input));
61 	if (strcmp(version, "2") != 0)
62 		i_fatal("Unsupported dbox file version %s", version);
63 	arg++;
64 
65 	for (; *arg != NULL; arg++) {
66 		switch (**arg) {
67 		case DBOX_HEADER_MSG_HEADER_SIZE:
68 			msg_hdr_size = hex2dec((const void *)(*arg + 1),
69 					       strlen(*arg + 1));
70 			if (msg_hdr_size == 0) {
71 				i_fatal("Invalid msg_header_size header: %s",
72 					*arg + 1);
73 			}
74 			printf("file.msg_header_size = %u\n", msg_hdr_size);
75 			break;
76 		case DBOX_HEADER_CREATE_STAMP:
77 			dump_timestamp(input, "file.create_stamp", *arg + 1);
78 			break;
79 		default:
80 			printf("file.unknown-%c = %s\n", **arg, *arg + 1);
81 			break;
82 		}
83 	}
84 	if (msg_hdr_size == 0)
85 		i_fatal("Missing msg_header_size in file header");
86 	return msg_hdr_size;
87 }
88 
89 static bool
dump_msg_hdr(struct istream * input,unsigned int hdr_size,uoff_t * msg_size_r)90 dump_msg_hdr(struct istream *input, unsigned int hdr_size, uoff_t *msg_size_r)
91 {
92 	struct dbox_message_header hdr;
93 	const unsigned char *data;
94 	size_t size;
95 	uoff_t msg_size;
96 
97 	if (i_stream_read_bytes(input, &data, &size, hdr_size) <= 0) {
98 		if (size == 0)
99 			return FALSE;
100 		i_fatal("Partial message header read at %"PRIuUOFF_T": %zu bytes",
101 			input->v_offset, size);
102 	}
103 	printf("offset %"PRIuUOFF_T":\n", input->v_offset);
104 
105 	if (hdr_size < sizeof(hdr))
106 		i_fatal("file.hdr_size too small: %u", hdr_size);
107 	memcpy(&hdr, data, sizeof(hdr));
108 
109 	if (memcmp(hdr.magic_pre, DBOX_MAGIC_PRE, sizeof(hdr.magic_pre)) != 0)
110 		i_fatal("dbox wrong pre-magic at %"PRIuUOFF_T, input->v_offset);
111 
112 	msg_size = dump_size(input, "msg.size",
113 		t_strndup(hdr.message_size_hex, sizeof(hdr.message_size_hex)));
114 
115 	i_stream_skip(input, hdr_size);
116 	*msg_size_r = msg_size;
117 	return TRUE;
118 }
119 
dump_msg_metadata(struct istream * input)120 static void dump_msg_metadata(struct istream *input)
121 {
122 	struct dbox_metadata_header hdr;
123 	const unsigned char *data;
124 	size_t size;
125 	const char *line;
126 
127 	/* verify magic */
128 	if (i_stream_read_bytes(input, &data, &size, sizeof(hdr)) <= 0) {
129 		i_fatal("dbox missing metadata at %"PRIuUOFF_T,
130 			input->v_offset);
131 	}
132 	memcpy(&hdr, data, sizeof(hdr));
133 	if (memcmp(hdr.magic_post, DBOX_MAGIC_POST, sizeof(hdr.magic_post)) != 0)
134 		i_fatal("dbox wrong post-magic at %"PRIuUOFF_T, input->v_offset);
135 	i_stream_skip(input, sizeof(hdr));
136 
137 	/* dump the metadata */
138 	for (;;) {
139 		if ((line = i_stream_read_next_line(input)) == NULL)
140 			i_fatal("dbox metadata ended unexpectedly at EOF");
141 		if (*line == '\0')
142 			break;
143 
144 		switch (*line) {
145 		case DBOX_METADATA_GUID:
146 			printf("msg.guid = %s\n", line + 1);
147 			break;
148 		case DBOX_METADATA_POP3_UIDL:
149 			printf("msg.pop3-uidl = %s\n", line + 1);
150 			break;
151 		case DBOX_METADATA_POP3_ORDER:
152 			printf("msg.pop3-order = %s\n", line + 1);
153 			break;
154 		case DBOX_METADATA_RECEIVED_TIME:
155 			dump_timestamp(input, "msg.received", line + 1);
156 			break;
157 		case DBOX_METADATA_PHYSICAL_SIZE:
158 			(void)dump_size(input, "msg.physical-size", line + 1);
159 			break;
160 		case DBOX_METADATA_VIRTUAL_SIZE:
161 			(void)dump_size(input, "msg.virtual-size", line + 1);
162 			break;
163 		case DBOX_METADATA_EXT_REF:
164 			printf("msg.ext-ref = %s\n", line + 1);
165 			break;
166 		case DBOX_METADATA_ORIG_MAILBOX:
167 			printf("msg.orig-mailbox = %s\n", line + 1);
168 			break;
169 
170 		case DBOX_METADATA_OLDV1_EXPUNGED:
171 		case DBOX_METADATA_OLDV1_FLAGS:
172 		case DBOX_METADATA_OLDV1_KEYWORDS:
173 		case DBOX_METADATA_OLDV1_SAVE_TIME:
174 		case DBOX_METADATA_OLDV1_SPACE:
175 			printf("msg.obsolete-%c = %s\n", *line, line + 1);
176 			break;
177 		}
178 	}
179 }
180 
dump_msg(struct istream * input,unsigned int hdr_size)181 static bool dump_msg(struct istream *input, unsigned int hdr_size)
182 {
183 	uoff_t msg_size;
184 
185 	if (!dump_msg_hdr(input, hdr_size, &msg_size))
186 		return FALSE;
187 	i_stream_skip(input, msg_size);
188 	dump_msg_metadata(input);
189 	return TRUE;
190 }
191 
cmd_dump_dbox(const char * path,const char * const * args ATTR_UNUSED)192 static void cmd_dump_dbox(const char *path, const char *const *args ATTR_UNUSED)
193 {
194 	struct istream *input;
195 	int fd;
196 	unsigned int hdr_size;
197 	bool ret;
198 
199 	fd = open(path, O_RDONLY);
200 	if (fd < 0)
201 		i_fatal("open(%s) failed: %m", path);
202 
203 	input = i_stream_create_fd_autoclose(&fd, SIZE_MAX);
204 	i_stream_set_name(input, path);
205 	hdr_size = dump_file_hdr(input);
206 	do {
207 		printf("\n");
208 		T_BEGIN {
209 			ret = dump_msg(input, hdr_size);
210 		} T_END;
211 	} while (ret);
212 	i_stream_destroy(&input);
213 }
214 
test_dump_dbox(const char * path)215 static bool test_dump_dbox(const char *path)
216 {
217 	const char *p;
218 
219 	p = strrchr(path, '/');
220 	if (p == NULL)
221 		p = path;
222 	else
223 		p++;
224 	return str_begins(p, "m.") || str_begins(p, "u.");
225 }
226 
227 struct doveadm_cmd_dump doveadm_cmd_dump_dbox = {
228 	"dbox",
229 	test_dump_dbox,
230 	cmd_dump_dbox
231 };
232