1 /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "buffer.h"
6 #include "llist.h"
7 #include "mail-index-view-private.h"
8 #include "mail-transaction-log.h"
9 
10 #undef mail_index_view_clone
11 #undef mail_index_view_dup_private
12 
13 struct mail_index_view *
mail_index_view_dup_private(const struct mail_index_view * src,const char * source_filename,unsigned int source_linenum)14 mail_index_view_dup_private(const struct mail_index_view *src,
15 			    const char *source_filename,
16 			    unsigned int source_linenum)
17 {
18 	struct mail_index_view *view;
19 	struct mail_index_map *map;
20 
21 	view = i_new(struct mail_index_view, 1);
22 	mail_index_view_clone(view, src, source_filename, source_linenum);
23 
24 	map = mail_index_map_clone(view->map);
25 	mail_index_unmap(&view->map);
26 	view->map = map;
27 	return view;
28 }
29 
mail_index_view_clone(struct mail_index_view * dest,const struct mail_index_view * src,const char * source_filename,unsigned int source_linenum)30 void mail_index_view_clone(struct mail_index_view *dest,
31 			   const struct mail_index_view *src,
32 			   const char *source_filename,
33 			   unsigned int source_linenum)
34 {
35 	i_zero(dest);
36 	dest->refcount = 1;
37 	dest->v = src->v;
38 	dest->index = src->index;
39 	if (src->log_view != NULL) {
40 		dest->log_view =
41 			mail_transaction_log_view_open(src->index->log);
42 	}
43 
44 	dest->indexid = src->indexid;
45 	dest->inconsistency_id = src->inconsistency_id;
46 	dest->map = src->map;
47 	if (dest->map != NULL)
48 		dest->map->refcount++;
49 
50 	dest->log_file_expunge_seq = src->log_file_expunge_seq;
51 	dest->log_file_expunge_offset = src->log_file_expunge_offset;
52 	dest->log_file_head_seq = src->log_file_head_seq;
53 	dest->log_file_head_offset = src->log_file_head_offset;
54 
55 	i_array_init(&dest->module_contexts,
56 		     I_MIN(5, mail_index_module_register.id));
57 
58 	dest->source_filename = source_filename;
59 	dest->source_linenum = source_linenum;
60 
61 	DLLIST_PREPEND(&dest->index->views, dest);
62 }
63 
mail_index_view_ref(struct mail_index_view * view)64 void mail_index_view_ref(struct mail_index_view *view)
65 {
66 	view->refcount++;
67 }
68 
view_close(struct mail_index_view * view)69 static void view_close(struct mail_index_view *view)
70 {
71 	i_assert(view->refcount == 0);
72 	i_assert(view->index->views != NULL);
73 
74 	DLLIST_REMOVE(&view->index->views, view);
75 
76 	mail_transaction_log_view_close(&view->log_view);
77 
78 	if (array_is_created(&view->syncs_hidden))
79 		array_free(&view->syncs_hidden);
80 	mail_index_unmap(&view->map);
81 	if (array_is_created(&view->map_refs)) {
82 		mail_index_view_unref_maps(view);
83 		array_free(&view->map_refs);
84 	}
85 	array_free(&view->module_contexts);
86 	i_free(view);
87 }
88 
mail_index_view_is_inconsistent(struct mail_index_view * view)89 bool mail_index_view_is_inconsistent(struct mail_index_view *view)
90 {
91 	if (view->index->indexid != view->indexid ||
92 	    view->index->inconsistency_id != view->inconsistency_id)
93 		view->inconsistent = TRUE;
94 	return view->inconsistent;
95 }
96 
mail_index_view_get_index(struct mail_index_view * view)97 struct mail_index *mail_index_view_get_index(struct mail_index_view *view)
98 {
99 	return view->index;
100 }
101 
mail_index_view_have_transactions(struct mail_index_view * view)102 bool mail_index_view_have_transactions(struct mail_index_view *view)
103 {
104 	return view->transactions_list != NULL;
105 }
106 
mail_index_view_ref_map(struct mail_index_view * view,struct mail_index_map * map)107 static void mail_index_view_ref_map(struct mail_index_view *view,
108 				    struct mail_index_map *map)
109 {
110 	struct mail_index_map *const *maps;
111 	unsigned int i, count;
112 
113 	if (array_is_created(&view->map_refs)) {
114 		maps = array_get(&view->map_refs, &count);
115 
116 		/* if map is already referenced, do nothing */
117 		for (i = 0; i < count; i++) {
118 			if (maps[i] == map)
119 				return;
120 		}
121 	} else {
122 		i_array_init(&view->map_refs, 4);
123 	}
124 
125 	/* reference the given mapping. the reference is dropped when the view
126 	   is synchronized or closed. */
127 	map->refcount++;
128 	array_push_back(&view->map_refs, &map);
129 }
130 
mail_index_view_unref_maps(struct mail_index_view * view)131 void mail_index_view_unref_maps(struct mail_index_view *view)
132 {
133 	struct mail_index_map **maps;
134 	unsigned int i, count;
135 
136 	if (!array_is_created(&view->map_refs))
137 		return;
138 
139 	maps = array_get_modifiable(&view->map_refs, &count);
140 	for (i = 0; i < count; i++)
141 		mail_index_unmap(&maps[i]);
142 
143 	array_clear(&view->map_refs);
144 }
145 
view_get_messages_count(struct mail_index_view * view)146 static uint32_t view_get_messages_count(struct mail_index_view *view)
147 {
148 	return view->map->hdr.messages_count;
149 }
150 
151 static const struct mail_index_header *
view_get_header(struct mail_index_view * view)152 view_get_header(struct mail_index_view *view)
153 {
154 	return &view->map->hdr;
155 }
156 
157 static const struct mail_index_record *
view_lookup_full(struct mail_index_view * view,uint32_t seq,struct mail_index_map ** map_r,bool * expunged_r)158 view_lookup_full(struct mail_index_view *view, uint32_t seq,
159 		 struct mail_index_map **map_r, bool *expunged_r)
160 {
161 	static struct mail_index_record broken_rec;
162 	struct mail_index_map *map;
163 	const struct mail_index_record *rec, *head_rec;
164 
165 	i_assert(seq > 0 && seq <= mail_index_view_get_messages_count(view));
166 
167 	/* look up the record */
168 	rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq);
169 	if (unlikely(rec->uid == 0)) {
170 		if (!view->inconsistent) {
171 			mail_index_set_error(view->index,
172 				"Corrupted Index file %s: Record [%u].uid=0",
173 				view->index->filepath, seq);
174 			(void)mail_index_fsck(view->index);
175 			view->inconsistent = TRUE;
176 		}
177 
178 		/* we'll need to return something so the caller doesn't crash */
179 		*map_r = view->map;
180 		if (expunged_r != NULL)
181 			*expunged_r = TRUE;
182 		return &broken_rec;
183 	}
184 	if (view->map == view->index->map) {
185 		/* view's mapping is latest. we can use it directly. */
186 		*map_r = view->map;
187 		if (expunged_r != NULL)
188 			*expunged_r = FALSE;
189 		return rec;
190 	}
191 
192 	/* look up the record from head mapping. it may contain some changes.
193 
194 	   start looking up from the same sequence as in the old view.
195 	   if there are no expunges, it's there. otherwise it's somewhere
196 	   before (since records can't be inserted).
197 
198 	   usually there are only a few expunges, so just going downwards from
199 	   our initial sequence position is probably faster than binary
200 	   search. */
201 	if (seq > view->index->map->hdr.messages_count)
202 		seq = view->index->map->hdr.messages_count;
203 	if (seq == 0) {
204 		/* everything is expunged from head. use the old record. */
205 		*map_r = view->map;
206 		if (expunged_r != NULL)
207 			*expunged_r = TRUE;
208 		return rec;
209 	}
210 
211 	map = view->index->map;
212 	do {
213 		head_rec = MAIL_INDEX_REC_AT_SEQ(map, seq);
214 		if (head_rec->uid <= rec->uid)
215 			break;
216 	} while (--seq > 0);
217 
218 	if (head_rec->uid == rec->uid) {
219 		/* found it. use it. reference the index mapping so that the
220 		   returned record doesn't get invalidated after next sync. */
221 		mail_index_view_ref_map(view, view->index->map);
222 		*map_r = view->index->map;
223 		if (expunged_r != NULL)
224 			*expunged_r = FALSE;
225 		return head_rec;
226 	} else {
227 		/* expunged from head. use the old record. */
228 		*map_r = view->map;
229 		if (expunged_r != NULL)
230 			*expunged_r = TRUE;
231 		return rec;
232 	}
233 }
234 
view_lookup_uid(struct mail_index_view * view,uint32_t seq,uint32_t * uid_r)235 static void view_lookup_uid(struct mail_index_view *view, uint32_t seq,
236 			    uint32_t *uid_r)
237 {
238 	i_assert(seq > 0 && seq <= mail_index_view_get_messages_count(view));
239 
240 	*uid_r = MAIL_INDEX_REC_AT_SEQ(view->map, seq)->uid;
241 }
242 
view_lookup_seq_range(struct mail_index_view * view,uint32_t first_uid,uint32_t last_uid,uint32_t * first_seq_r,uint32_t * last_seq_r)243 static void view_lookup_seq_range(struct mail_index_view *view,
244 				  uint32_t first_uid, uint32_t last_uid,
245 				  uint32_t *first_seq_r, uint32_t *last_seq_r)
246 {
247 	mail_index_map_lookup_seq_range(view->map, first_uid, last_uid,
248 					first_seq_r, last_seq_r);
249 }
250 
view_lookup_first(struct mail_index_view * view,enum mail_flags flags,uint8_t flags_mask,uint32_t * seq_r)251 static void view_lookup_first(struct mail_index_view *view,
252 			      enum mail_flags flags, uint8_t flags_mask,
253 			      uint32_t *seq_r)
254 {
255 #define LOW_UPDATE(x) \
256 	STMT_START { if ((x) > low_uid) low_uid = x; } STMT_END
257 	const struct mail_index_header *hdr = &view->map->hdr;
258 	const struct mail_index_record *rec;
259 	uint32_t seq, seq2, low_uid = 1;
260 
261 	*seq_r = 0;
262 
263 	if ((flags_mask & MAIL_SEEN) != 0 && (flags & MAIL_SEEN) == 0)
264 		LOW_UPDATE(hdr->first_unseen_uid_lowwater);
265 	if ((flags_mask & MAIL_DELETED) != 0 && (flags & MAIL_DELETED) != 0)
266 		LOW_UPDATE(hdr->first_deleted_uid_lowwater);
267 
268 	if (low_uid == 1)
269 		seq = 1;
270 	else {
271 		if (!mail_index_lookup_seq_range(view, low_uid, hdr->next_uid,
272 						 &seq, &seq2))
273 			return;
274 	}
275 
276 	i_assert(hdr->messages_count <= view->map->rec_map->records_count);
277 	for (; seq <= hdr->messages_count; seq++) {
278 		rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq);
279 		if ((rec->flags & flags_mask) == (uint8_t)flags) {
280 			*seq_r = seq;
281 			break;
282 		}
283 	}
284 }
285 
286 static void
mail_index_data_lookup_keywords(struct mail_index_map * map,const unsigned char * data,ARRAY_TYPE (keyword_indexes)* keyword_idx)287 mail_index_data_lookup_keywords(struct mail_index_map *map,
288 				const unsigned char *data,
289 				ARRAY_TYPE(keyword_indexes) *keyword_idx)
290 {
291 	const unsigned int *keyword_idx_map;
292 	unsigned int i, j, keyword_count, index_idx;
293 	uint32_t idx, hdr_size;
294 	uint16_t record_size, record_align;
295 
296 	array_clear(keyword_idx);
297 	if (data == NULL) {
298 		/* no keywords at all in index */
299 		return;
300 	}
301 	(void)mail_index_ext_get_size(map, map->index->keywords_ext_id,
302 				      &hdr_size, &record_size,
303 				      &record_align);
304 
305 	/* keyword_idx_map[] contains file => index keyword mapping */
306 	if (!array_is_created(&map->keyword_idx_map))
307 		return;
308 
309 	keyword_idx_map = array_get(&map->keyword_idx_map, &keyword_count);
310 	for (i = 0; i < record_size; i++) {
311 		/* first do the quick check to see if there's keywords at all */
312 		if (data[i] == 0)
313 			continue;
314 
315 		idx = i * CHAR_BIT;
316 		for (j = 0; j < CHAR_BIT; j++, idx++) {
317 			if ((data[i] & (1 << j)) == 0)
318 				continue;
319 
320 			if (idx >= keyword_count) {
321 				/* extra bits set in keyword bytes.
322 				   shouldn't happen, but just ignore. */
323 				break;
324 			}
325 
326 			index_idx = keyword_idx_map[idx];
327 			array_push_back(keyword_idx, &index_idx);
328 		}
329 	}
330 }
331 
view_lookup_keywords(struct mail_index_view * view,uint32_t seq,ARRAY_TYPE (keyword_indexes)* keyword_idx)332 static void view_lookup_keywords(struct mail_index_view *view, uint32_t seq,
333 				 ARRAY_TYPE(keyword_indexes) *keyword_idx)
334 {
335 	struct mail_index_map *map;
336 	const void *data;
337 
338 	mail_index_lookup_ext_full(view, seq, view->index->keywords_ext_id,
339 				   &map, &data, NULL);
340 	mail_index_data_lookup_keywords(map, data, keyword_idx);
341 }
342 
343 static const void *
view_map_lookup_ext_full(struct mail_index_map * map,const struct mail_index_record * rec,uint32_t ext_id)344 view_map_lookup_ext_full(struct mail_index_map *map,
345 			 const struct mail_index_record *rec, uint32_t ext_id)
346 {
347 	const struct mail_index_ext *ext;
348 	uint32_t idx;
349 
350 	if (!mail_index_map_get_ext_idx(map, ext_id, &idx))
351 		return NULL;
352 
353 	ext = array_idx(&map->extensions, idx);
354 	return ext->record_offset == 0 ? NULL :
355 		CONST_PTR_OFFSET(rec, ext->record_offset);
356 }
357 
358 static void
view_lookup_ext_full(struct mail_index_view * view,uint32_t seq,uint32_t ext_id,struct mail_index_map ** map_r,const void ** data_r,bool * expunged_r)359 view_lookup_ext_full(struct mail_index_view *view, uint32_t seq,
360 		     uint32_t ext_id, struct mail_index_map **map_r,
361 		     const void **data_r, bool *expunged_r)
362 {
363 	const struct mail_index_record *rec;
364 
365 	rec = view->v.lookup_full(view, seq, map_r, expunged_r);
366 	*data_r = view_map_lookup_ext_full(*map_r, rec, ext_id);
367 }
368 
view_get_header_ext(struct mail_index_view * view,struct mail_index_map * map,uint32_t ext_id,const void ** data_r,size_t * data_size_r)369 static void view_get_header_ext(struct mail_index_view *view,
370 				struct mail_index_map *map, uint32_t ext_id,
371 				const void **data_r, size_t *data_size_r)
372 {
373 	const struct mail_index_ext *ext;
374 	uint32_t idx;
375 
376 	if (map == NULL) {
377 		/* no mapping given, use head mapping */
378 		map = view->index->map;
379 	}
380 
381 	if (!mail_index_map_get_ext_idx(map, ext_id, &idx)) {
382 		/* extension doesn't exist in this index file */
383 		*data_r = NULL;
384 		*data_size_r = 0;
385 		return;
386 	}
387 
388 	ext = array_idx(&map->extensions, idx);
389 	*data_r = MAIL_INDEX_MAP_HDR_OFFSET(map, ext->hdr_offset);
390 	*data_size_r = ext->hdr_size;
391 }
392 
view_ext_get_reset_id(struct mail_index_view * view ATTR_UNUSED,struct mail_index_map * map,uint32_t ext_id,uint32_t * reset_id_r)393 static bool view_ext_get_reset_id(struct mail_index_view *view ATTR_UNUSED,
394 				  struct mail_index_map *map,
395 				  uint32_t ext_id, uint32_t *reset_id_r)
396 {
397 	const struct mail_index_ext *ext;
398 	uint32_t idx;
399 
400 	if (!mail_index_map_get_ext_idx(map, ext_id, &idx))
401 		return FALSE;
402 
403 	ext = array_idx(&map->extensions, idx);
404 	*reset_id_r = ext->reset_id;
405 	return TRUE;
406 }
407 
mail_index_view_close(struct mail_index_view ** _view)408 void mail_index_view_close(struct mail_index_view **_view)
409 {
410 	struct mail_index_view *view = *_view;
411 
412 	*_view = NULL;
413 	if (--view->refcount > 0)
414 		return;
415 
416 	i_assert(view->transactions_list == NULL);
417 
418 	view->v.close(view);
419 }
420 
mail_index_view_get_messages_count(struct mail_index_view * view)421 uint32_t mail_index_view_get_messages_count(struct mail_index_view *view)
422 {
423 	return view->v.get_messages_count(view);
424 }
425 
426 const struct mail_index_header *
mail_index_get_header(struct mail_index_view * view)427 mail_index_get_header(struct mail_index_view *view)
428 {
429 	return view->v.get_header(view);
430 }
431 
432 const struct mail_index_record *
mail_index_lookup(struct mail_index_view * view,uint32_t seq)433 mail_index_lookup(struct mail_index_view *view, uint32_t seq)
434 {
435 	struct mail_index_map *map;
436 
437 	return mail_index_lookup_full(view, seq, &map);
438 }
439 
440 const struct mail_index_record *
mail_index_lookup_full(struct mail_index_view * view,uint32_t seq,struct mail_index_map ** map_r)441 mail_index_lookup_full(struct mail_index_view *view, uint32_t seq,
442 		       struct mail_index_map **map_r)
443 {
444 	return view->v.lookup_full(view, seq, map_r, NULL);
445 }
446 
mail_index_is_expunged(struct mail_index_view * view,uint32_t seq)447 bool mail_index_is_expunged(struct mail_index_view *view, uint32_t seq)
448 {
449 	struct mail_index_map *map;
450 	bool expunged;
451 
452 	(void)view->v.lookup_full(view, seq, &map, &expunged);
453 	return expunged;
454 }
455 
mail_index_map_lookup_keywords(struct mail_index_map * map,uint32_t seq,ARRAY_TYPE (keyword_indexes)* keyword_idx)456 void mail_index_map_lookup_keywords(struct mail_index_map *map, uint32_t seq,
457 				    ARRAY_TYPE(keyword_indexes) *keyword_idx)
458 {
459 	const struct mail_index_ext *ext;
460 	const struct mail_index_record *rec;
461 	const void *data;
462 	uint32_t idx;
463 
464 	if (!mail_index_map_get_ext_idx(map, map->index->keywords_ext_id, &idx))
465 		data = NULL;
466 	else {
467 		rec = MAIL_INDEX_REC_AT_SEQ(map, seq);
468 		ext = array_idx(&map->extensions, idx);
469 		data = ext->record_offset == 0 ? NULL :
470 			CONST_PTR_OFFSET(rec, ext->record_offset);
471 	}
472 	mail_index_data_lookup_keywords(map, data, keyword_idx);
473 }
474 
mail_index_lookup_keywords(struct mail_index_view * view,uint32_t seq,ARRAY_TYPE (keyword_indexes)* keyword_idx)475 void mail_index_lookup_keywords(struct mail_index_view *view, uint32_t seq,
476 				ARRAY_TYPE(keyword_indexes) *keyword_idx)
477 {
478 	view->v.lookup_keywords(view, seq, keyword_idx);
479 }
480 
mail_index_lookup_view_flags(struct mail_index_view * view,uint32_t seq,enum mail_flags * flags_r,ARRAY_TYPE (keyword_indexes)* keyword_idx)481 void mail_index_lookup_view_flags(struct mail_index_view *view, uint32_t seq,
482 				  enum mail_flags *flags_r,
483 				  ARRAY_TYPE(keyword_indexes) *keyword_idx)
484 {
485 	const struct mail_index_record *rec;
486 	const unsigned char *keyword_data;
487 
488 	i_assert(seq > 0 && seq <= mail_index_view_get_messages_count(view));
489 
490 	rec = MAIL_INDEX_REC_AT_SEQ(view->map, seq);
491 	*flags_r = rec->flags;
492 
493 	keyword_data = view_map_lookup_ext_full(view->map, rec,
494 						view->index->keywords_ext_id);
495 	mail_index_data_lookup_keywords(view->map, keyword_data, keyword_idx);
496 }
497 
mail_index_lookup_uid(struct mail_index_view * view,uint32_t seq,uint32_t * uid_r)498 void mail_index_lookup_uid(struct mail_index_view *view, uint32_t seq,
499 			   uint32_t *uid_r)
500 {
501 	view->v.lookup_uid(view, seq, uid_r);
502 }
503 
mail_index_lookup_seq_range(struct mail_index_view * view,uint32_t first_uid,uint32_t last_uid,uint32_t * first_seq_r,uint32_t * last_seq_r)504 bool mail_index_lookup_seq_range(struct mail_index_view *view,
505 				 uint32_t first_uid, uint32_t last_uid,
506 				 uint32_t *first_seq_r, uint32_t *last_seq_r)
507 {
508 	view->v.lookup_seq_range(view, first_uid, last_uid,
509 				 first_seq_r, last_seq_r);
510 	return *first_seq_r != 0;
511 }
512 
mail_index_lookup_seq(struct mail_index_view * view,uint32_t uid,uint32_t * seq_r)513 bool mail_index_lookup_seq(struct mail_index_view *view,
514 			   uint32_t uid, uint32_t *seq_r)
515 {
516 	view->v.lookup_seq_range(view, uid, uid, seq_r, seq_r);
517 	return *seq_r != 0;
518 }
519 
mail_index_lookup_first(struct mail_index_view * view,enum mail_flags flags,uint8_t flags_mask,uint32_t * seq_r)520 void mail_index_lookup_first(struct mail_index_view *view,
521 			     enum mail_flags flags, uint8_t flags_mask,
522 			     uint32_t *seq_r)
523 {
524 	view->v.lookup_first(view, flags, flags_mask, seq_r);
525 }
526 
mail_index_lookup_ext(struct mail_index_view * view,uint32_t seq,uint32_t ext_id,const void ** data_r,bool * expunged_r)527 void mail_index_lookup_ext(struct mail_index_view *view, uint32_t seq,
528 			   uint32_t ext_id, const void **data_r,
529 			   bool *expunged_r)
530 {
531 	struct mail_index_map *map;
532 
533 	mail_index_lookup_ext_full(view, seq, ext_id, &map, data_r, expunged_r);
534 }
535 
mail_index_lookup_ext_full(struct mail_index_view * view,uint32_t seq,uint32_t ext_id,struct mail_index_map ** map_r,const void ** data_r,bool * expunged_r)536 void mail_index_lookup_ext_full(struct mail_index_view *view, uint32_t seq,
537 				uint32_t ext_id, struct mail_index_map **map_r,
538 				const void **data_r, bool *expunged_r)
539 {
540 	view->v.lookup_ext_full(view, seq, ext_id, map_r, data_r, expunged_r);
541 }
542 
mail_index_get_header_ext(struct mail_index_view * view,uint32_t ext_id,const void ** data_r,size_t * data_size_r)543 void mail_index_get_header_ext(struct mail_index_view *view, uint32_t ext_id,
544 			       const void **data_r, size_t *data_size_r)
545 {
546 	view->v.get_header_ext(view, NULL, ext_id, data_r, data_size_r);
547 }
548 
mail_index_map_get_header_ext(struct mail_index_view * view,struct mail_index_map * map,uint32_t ext_id,const void ** data_r,size_t * data_size_r)549 void mail_index_map_get_header_ext(struct mail_index_view *view,
550 				   struct mail_index_map *map, uint32_t ext_id,
551 				   const void **data_r, size_t *data_size_r)
552 {
553 	view->v.get_header_ext(view, map, ext_id, data_r, data_size_r);
554 }
555 
mail_index_ext_get_reset_id(struct mail_index_view * view,struct mail_index_map * map,uint32_t ext_id,uint32_t * reset_id_r)556 bool mail_index_ext_get_reset_id(struct mail_index_view *view,
557 				 struct mail_index_map *map,
558 				 uint32_t ext_id, uint32_t *reset_id_r)
559 {
560 	return view->v.ext_get_reset_id(view, map, ext_id, reset_id_r);
561 }
562 
mail_index_ext_get_size(struct mail_index_map * map,uint32_t ext_id,uint32_t * hdr_size_r,uint16_t * record_size_r,uint16_t * record_align_r)563 void mail_index_ext_get_size(struct mail_index_map *map, uint32_t ext_id,
564 			     uint32_t *hdr_size_r, uint16_t *record_size_r,
565 			     uint16_t *record_align_r)
566 {
567 	const struct mail_index_ext *ext;
568 	uint32_t idx;
569 
570 	i_assert(map != NULL);
571 
572 	if (!mail_index_map_get_ext_idx(map, ext_id, &idx)) {
573 		/* extension doesn't exist in this index file */
574 		*hdr_size_r = 0;
575 		*record_size_r = 0;
576 		*record_align_r = 0;
577 		return;
578 	}
579 
580 	ext = array_idx(&map->extensions, idx);
581 	*hdr_size_r = ext->hdr_size;
582 	*record_size_r = ext->record_size;
583 	*record_align_r = ext->record_align;
584 }
585 
586 static struct mail_index_view_vfuncs view_vfuncs = {
587 	view_close,
588 	view_get_messages_count,
589 	view_get_header,
590 	view_lookup_full,
591 	view_lookup_uid,
592 	view_lookup_seq_range,
593 	view_lookup_first,
594 	view_lookup_keywords,
595 	view_lookup_ext_full,
596 	view_get_header_ext,
597 	view_ext_get_reset_id
598 };
599 
600 struct mail_index_view *
mail_index_view_open_with_map(struct mail_index * index,struct mail_index_map * map)601 mail_index_view_open_with_map(struct mail_index *index,
602 			      struct mail_index_map *map)
603 {
604 	struct mail_index_view *view;
605 
606 	view = i_new(struct mail_index_view, 1);
607 	view->refcount = 1;
608 	view->v = view_vfuncs;
609 	view->index = index;
610 	view->log_view = mail_transaction_log_view_open(index->log);
611 
612 	view->indexid = index->indexid;
613 	view->inconsistency_id = index->inconsistency_id;
614 	view->map = map;
615 	view->map->refcount++;
616 
617 	view->log_file_expunge_seq = view->log_file_head_seq =
618 		view->map->hdr.log_file_seq;
619 	view->log_file_expunge_offset = view->log_file_head_offset =
620 		view->map->hdr.log_file_head_offset;
621 
622 	i_array_init(&view->module_contexts,
623 		     I_MIN(5, mail_index_module_register.id));
624 	DLLIST_PREPEND(&index->views, view);
625 	return view;
626 }
627 
628 #undef mail_index_view_open
629 struct mail_index_view *
mail_index_view_open(struct mail_index * index,const char * source_filename,unsigned int source_linenum)630 mail_index_view_open(struct mail_index *index,
631 		     const char *source_filename, unsigned int source_linenum)
632 {
633 	struct mail_index_view *view;
634 
635 	view = mail_index_view_open_with_map(index, index->map);
636 	/* these can be used to debug mail_index_view_close() leaks */
637 	view->source_filename = source_filename;
638 	view->source_linenum = source_linenum;
639 	return view;
640 }
641 
642 const struct mail_index_ext *
mail_index_view_get_ext(struct mail_index_view * view,uint32_t ext_id)643 mail_index_view_get_ext(struct mail_index_view *view, uint32_t ext_id)
644 {
645 	uint32_t idx;
646 
647 	if (!mail_index_map_get_ext_idx(view->map, ext_id, &idx))
648 		return NULL;
649 
650 	return array_idx(&view->map->extensions, idx);
651 }
652