1 #ifndef DBOX_FILE_H
2 #define DBOX_FILE_H
3 
4 /* The file begins with a header followed by zero or more messages:
5 
6    <dbox message header>
7    <LF>
8    <message body>
9    <metadata>
10 
11    Metadata block begins with DBOX_MAGIC_POST, followed by zero or more lines
12    in format <key character><value><LF>. The block ends with an empty line.
13    Unknown metadata should be ignored, but preserved when copying.
14 
15    There should be no duplicates for the current metadata, but future
16    extensions may need them so they should be preserved.
17 */
18 #define DBOX_VERSION 2
19 #define DBOX_MAGIC_PRE "\001\002"
20 #define DBOX_MAGIC_POST "\n\001\003\n"
21 
22 /* prefer flock(). fcntl() locking currently breaks if trying to access the
23    same file from multiple mail_storages within same process. that's why we
24    fallback to dotlocks. */
25 #ifdef HAVE_FLOCK
26 #  define DBOX_FILE_LOCK_METHOD_FLOCK
27 #endif
28 
29 struct dbox_file;
30 struct stat;
31 
32 enum dbox_header_key {
33 	/* Must be sizeof(struct dbox_message_header) when appending (hex) */
34 	DBOX_HEADER_MSG_HEADER_SIZE	= 'M',
35 	/* Creation UNIX timestamp (hex) */
36 	DBOX_HEADER_CREATE_STAMP	= 'C',
37 
38 	/* metadata used by old Dovecot versions */
39 	DBOX_HEADER_OLDV1_APPEND_OFFSET	= 'A'
40 };
41 
42 /* NOTE: all valid keys are uppercase characters. if this changes, change
43    dbox-file-fix.c:dbox_file_match_post_magic() to recognize them */
44 enum dbox_metadata_key {
45 	/* Globally unique identifier for the message. Preserved when
46 	   copying. */
47 	DBOX_METADATA_GUID		= 'G',
48 	/* POP3 UIDL overriding the default format */
49 	DBOX_METADATA_POP3_UIDL		= 'P',
50 	/* POP3 message ordering (for migrated mails) */
51 	DBOX_METADATA_POP3_ORDER	= 'O',
52 	/* Received UNIX timestamp in hex */
53 	DBOX_METADATA_RECEIVED_TIME	= 'R',
54 	/* Physical message size in hex. Necessary only if it differs from
55 	   the dbox_message_header.message_size_hex, for example because the
56 	   message is compressed. */
57 	DBOX_METADATA_PHYSICAL_SIZE	= 'Z',
58 	/* Virtual message size in hex (line feeds counted as CRLF) */
59 	DBOX_METADATA_VIRTUAL_SIZE	= 'V',
60 	/* Pointer to external message data. Format is:
61 	   1*(<start offset> <byte count> <options> <ref>) */
62 	DBOX_METADATA_EXT_REF		= 'X',
63 	/* Mailbox name where this message was originally saved to.
64 	   When rebuild finds a message whose mailbox is unknown, it's
65 	   placed to this mailbox. */
66 	DBOX_METADATA_ORIG_MAILBOX	= 'B',
67 
68 	/* metadata used by old Dovecot versions */
69 	DBOX_METADATA_OLDV1_EXPUNGED	= 'E',
70 	DBOX_METADATA_OLDV1_FLAGS	= 'F',
71 	DBOX_METADATA_OLDV1_KEYWORDS	= 'K',
72 	DBOX_METADATA_OLDV1_SAVE_TIME	= 'S',
73 	DBOX_METADATA_OLDV1_SPACE	= ' '
74 };
75 
76 enum dbox_message_type {
77 	/* Normal message */
78 	DBOX_MESSAGE_TYPE_NORMAL	= 'N'
79 };
80 
81 struct dbox_message_header {
82 	unsigned char magic_pre[2];
83 	unsigned char type;
84 	unsigned char space1;
85 	unsigned char oldv1_uid_hex[8];
86 	unsigned char space2;
87 	unsigned char message_size_hex[16];
88 	/* <space reserved for future extensions, LF is always last> */
89 	unsigned char save_lf;
90 };
91 
92 struct dbox_metadata_header {
93 	unsigned char magic_post[sizeof(DBOX_MAGIC_POST)-1];
94 };
95 
96 struct dbox_file {
97 	struct dbox_storage *storage;
98 	int refcount;
99 
100 	time_t create_time;
101 	unsigned int file_version;
102 	unsigned int file_header_size;
103 	unsigned int msg_header_size;
104 
105 	const char *cur_path;
106 	char *primary_path, *alt_path;
107 	int fd;
108 	struct istream *input;
109 #ifdef DBOX_FILE_LOCK_METHOD_FLOCK
110 	struct file_lock *lock;
111 #else
112 	struct dotlock *lock;
113 #endif
114 
115 	uoff_t cur_offset;
116 	uoff_t cur_physical_size;
117 
118 	/* Metadata for the currently seeked metadata block. */
119 	pool_t metadata_pool;
120 	ARRAY(const char *) metadata;
121 	uoff_t metadata_read_offset;
122 
123 	bool appending:1;
124 	bool corrupted:1;
125 };
126 
127 struct dbox_file_append_context {
128 	struct dbox_file *file;
129 
130 	uoff_t first_append_offset, last_checkpoint_offset, last_flush_offset;
131 	struct ostream *output;
132 };
133 
134 #define dbox_file_is_open(file) ((file)->fd != -1)
135 #define dbox_file_is_in_alt(file) ((file)->cur_path == (file)->alt_path)
136 
137 void dbox_file_init(struct dbox_file *file);
138 void dbox_file_unref(struct dbox_file **file);
139 
140 /* Open the file. Returns 1 if ok, 0 if file header is corrupted, -1 if error.
141    If file is deleted, deleted_r=TRUE and 1 is returned. */
142 int dbox_file_open(struct dbox_file *file, bool *deleted_r);
143 /* Try to open file only from primary path. */
144 int dbox_file_open_primary(struct dbox_file *file, bool *notfound_r);
145 /* Close the file handle from the file, but don't free it. */
146 void dbox_file_close(struct dbox_file *file);
147 
148 /* fstat() or stat() the file. If file is already deleted, fails with
149    errno=ENOENT. */
150 int dbox_file_stat(struct dbox_file *file, struct stat *st_r);
151 
152 /* Try to lock the dbox file. Returns 1 if ok, 0 if already locked by someone
153    else, -1 if error. */
154 int dbox_file_try_lock(struct dbox_file *file);
155 void dbox_file_unlock(struct dbox_file *file);
156 
157 /* Seek to given offset in file. Returns 1 if ok/expunged, 0 if file/offset is
158    corrupted, -1 if I/O error. */
159 int dbox_file_seek(struct dbox_file *file, uoff_t offset);
160 /* Start seeking at the beginning of the file. */
161 void dbox_file_seek_rewind(struct dbox_file *file);
162 /* Seek to next message after current one. If there are no more messages,
163    returns 0 and last_r is set to TRUE. Returns 1 if ok, 0 if file is
164    corrupted, -1 if I/O error. */
165 int dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r);
166 
167 /* Start appending to dbox file */
168 struct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file);
169 /* Finish writing appended mails. */
170 int dbox_file_append_commit(struct dbox_file_append_context **ctx);
171 /* Truncate appended mails. */
172 void dbox_file_append_rollback(struct dbox_file_append_context **ctx);
173 /* Get output stream for appending a new message. Returns 1 if ok, 0 if file
174    can't be appended to (old file version or corruption) or -1 if error. */
175 int dbox_file_get_append_stream(struct dbox_file_append_context *ctx,
176 				struct ostream **output_r);
177 /* Call after message has been fully saved. If this isn't done, the writes
178    since the last checkpoint are truncated. */
179 void dbox_file_append_checkpoint(struct dbox_file_append_context *ctx);
180 /* Flush output buffer. */
181 int dbox_file_append_flush(struct dbox_file_append_context *ctx);
182 
183 /* Read current message's metadata. Returns 1 if ok, 0 if metadata is
184    corrupted, -1 if I/O error. */
185 int dbox_file_metadata_read(struct dbox_file *file);
186 /* Return wanted metadata value, or NULL if not found. */
187 const char *dbox_file_metadata_get(struct dbox_file *file,
188 				   enum dbox_metadata_key key);
189 
190 /* Returns DBOX_METADATA_PHYSICAL_SIZE if set, otherwise physical size from
191    header. They differ only for e.g. compressed mails. */
192 uoff_t dbox_file_get_plaintext_size(struct dbox_file *file);
193 
194 /* Fix a broken dbox file by rename()ing over it with a fixed file. Everything
195    before start_offset is assumed to be valid and is simply copied. The file
196    is reopened afterwards. Returns 1 if ok, 0 if the resulting file has no
197    mails and was deleted, -1 if I/O error. */
198 int dbox_file_fix(struct dbox_file *file, uoff_t start_offset);
199 /* Delete the given dbox file. Returns 1 if deleted, 0 if file wasn't found
200    or -1 if error. */
201 int dbox_file_unlink(struct dbox_file *file);
202 
203 /* Fill dbox_message_header with given size. */
204 void dbox_msg_header_fill(struct dbox_message_header *dbox_msg_hdr,
205 			  uoff_t message_size);
206 
207 void dbox_file_set_syscall_error(struct dbox_file *file, const char *function);
208 void dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...)
209 	ATTR_FORMAT(2, 3);
210 
211 /* private: */
212 const char *dbox_generate_tmp_filename(void);
213 void dbox_file_free(struct dbox_file *file);
214 int dbox_file_header_write(struct dbox_file *file, struct ostream *output);
215 int dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r);
216 int dbox_file_metadata_skip_header(struct dbox_file *file);
217 
218 #endif
219