1 /*
2  * mbsync - mailbox synchronizer
3  * Copyright (C) 2017 Oswald Buddenhagen <ossi@users.sf.net>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * As a special exception, mbsync may be linked with the OpenSSL library,
19  * despite that library's more restrictive license.
20  */
21 
22 #include "driver.h"
23 
24 #include <assert.h>
25 #include <limits.h>
26 #include <stdlib.h>
27 
28 typedef struct gen_cmd gen_cmd_t;
29 
30 typedef union proxy_store {
31 	store_t gen;
32 	struct {
33 		STORE(union proxy_store)
34 		const char *label;  // foreign
35 		uint ref_count;
36 		driver_t *real_driver;
37 		store_t *real_store;
38 		gen_cmd_t *done_cmds, **done_cmds_append;
39 		gen_cmd_t *check_cmds, **check_cmds_append;
40 		wakeup_t wakeup;
41 
42 		void (*bad_callback)( void *aux );
43 		void *bad_callback_aux;
44 	};
45 } proxy_store_t;
46 
47 static void ATTR_PRINTFLIKE(1, 2)
debug(const char * msg,...)48 debug( const char *msg, ... )
49 {
50 	va_list va;
51 
52 	va_start( va, msg );
53 	vdebug( DEBUG_DRV, msg, va );
54 	va_end( va );
55 }
56 
57 static void ATTR_PRINTFLIKE(1, 2)
debugn(const char * msg,...)58 debugn( const char *msg, ... )
59 {
60 	va_list va;
61 
62 	va_start( va, msg );
63 	vdebugn( DEBUG_DRV, msg, va );
64 	va_end( va );
65 }
66 
67 /* Keep the mailbox driver flag definitions in sync: */
68 /* grep for MAILBOX_DRIVER_FLAG */
69 /* The order is according to alphabetical maildir flag sort */
70 static const char Flags[] = { 'D', 'F', 'P', 'R', 'S', 'T' };
71 
72 static char *
proxy_make_flags(uchar flags,char * buf)73 proxy_make_flags( uchar flags, char *buf )
74 {
75 	uint i, d;
76 
77 	for (d = 0, i = 0; i < as(Flags); i++)
78 		if (flags & (1 << i))
79 			buf[d++] = Flags[i];
80 	buf[d] = 0;
81 	return buf;
82 }
83 
84 static void
proxy_store_deref(proxy_store_t * ctx)85 proxy_store_deref( proxy_store_t *ctx )
86 {
87 	if (!--ctx->ref_count) {
88 		assert( !pending_wakeup( &ctx->wakeup ) );
89 		free( ctx );
90 	}
91 }
92 
93 static int curr_tag;
94 
95 #define GEN_CMD \
96 	uint ref_count; \
97 	int tag; \
98 	proxy_store_t *ctx; \
99 	gen_cmd_t *next; \
100 	void (*queued_cb)( gen_cmd_t *gcmd );
101 
102 struct gen_cmd {
103 	GEN_CMD
104 };
105 
106 #define GEN_STS_CMD \
107 	GEN_CMD \
108 	int sts;
109 
110 typedef union {
111 	gen_cmd_t gen;
112 	struct {
113 		GEN_STS_CMD
114 	};
115 } gen_sts_cmd_t;
116 
117 static gen_cmd_t *
proxy_cmd_new(proxy_store_t * ctx,uint sz)118 proxy_cmd_new( proxy_store_t *ctx, uint sz )
119 {
120 	gen_cmd_t *cmd = nfmalloc( sz );
121 	cmd->ref_count = 2;
122 	cmd->tag = ++curr_tag;
123 	cmd->ctx = ctx;
124 	ctx->ref_count++;
125 	return cmd;
126 }
127 
128 static void
proxy_cmd_done(gen_cmd_t * cmd)129 proxy_cmd_done( gen_cmd_t *cmd )
130 {
131 	if (!--cmd->ref_count) {
132 		proxy_store_deref( cmd->ctx );
133 		free( cmd );
134 	}
135 }
136 
137 static void
proxy_wakeup(void * aux)138 proxy_wakeup( void *aux )
139 {
140 	proxy_store_t *ctx = (proxy_store_t *)aux;
141 
142 	gen_cmd_t *cmd = ctx->done_cmds;
143 	assert( cmd );
144 	if (!(ctx->done_cmds = cmd->next))
145 		ctx->done_cmds_append = &ctx->done_cmds;
146 	else
147 		conf_wakeup( &ctx->wakeup, 0 );
148 	cmd->queued_cb( cmd );
149 	proxy_cmd_done( cmd );
150 }
151 
152 static void
proxy_invoke_cb(gen_cmd_t * cmd,void (* cb)(gen_cmd_t *),int checked,const char * name)153 proxy_invoke_cb( gen_cmd_t *cmd, void (*cb)( gen_cmd_t * ), int checked, const char *name )
154 {
155 	if (DFlags & FORCEASYNC) {
156 		debug( "%s[% 2d] Callback queue %s%s\n", cmd->ctx->label, cmd->tag, name, checked ? " (checked)" : "" );
157 		cmd->queued_cb = cb;
158 		cmd->next = NULL;
159 		if (checked) {
160 			*cmd->ctx->check_cmds_append = cmd;
161 			cmd->ctx->check_cmds_append = &cmd->next;
162 		} else {
163 			*cmd->ctx->done_cmds_append = cmd;
164 			cmd->ctx->done_cmds_append = &cmd->next;
165 			conf_wakeup( &cmd->ctx->wakeup, 0 );
166 		}
167 	} else {
168 		cb( cmd );
169 		proxy_cmd_done( cmd );
170 	}
171 }
172 
173 static void
proxy_flush_checked_cmds(proxy_store_t * ctx)174 proxy_flush_checked_cmds( proxy_store_t *ctx )
175 {
176 	if (ctx->check_cmds) {
177 		*ctx->done_cmds_append = ctx->check_cmds;
178 		ctx->done_cmds_append = ctx->check_cmds_append;
179 		ctx->check_cmds_append = &ctx->check_cmds;
180 		ctx->check_cmds = NULL;
181 		conf_wakeup( &ctx->wakeup, 0 );
182 	}
183 }
184 
185 static void
proxy_cancel_checked_cmds(proxy_store_t * ctx)186 proxy_cancel_checked_cmds( proxy_store_t *ctx )
187 {
188 	gen_cmd_t *cmd;
189 
190 	while ((cmd = ctx->check_cmds)) {
191 		if (!(ctx->check_cmds = cmd->next))
192 			ctx->check_cmds_append = &ctx->check_cmds;
193 		((gen_sts_cmd_t *)cmd)->sts = DRV_CANCELED;
194 		cmd->queued_cb( cmd );
195 	}
196 }
197 
198 #if 0
199 //# TEMPLATE GETTER
200 static @type@proxy_@name@( store_t *gctx )
201 {
202 	proxy_store_t *ctx = (proxy_store_t *)gctx;
203 
204 	@type@rv = ctx->real_driver->@name@( ctx->real_store );
205 	debug( "%sCalled @name@, ret=@fmt@\n", ctx->label, rv );
206 	return rv;
207 }
208 //# END
209 
210 //# TEMPLATE REGULAR
211 static @type@proxy_@name@( store_t *gctx@decl_args@ )
212 {
213 	proxy_store_t *ctx = (proxy_store_t *)gctx;
214 
215 	@pre_print_args@
216 	debug( "%sEnter @name@@print_fmt_args@\n", ctx->label@print_pass_args@ );
217 	@print_args@
218 	@type@rv = ctx->real_driver->@name@( ctx->real_store@pass_args@ );
219 	debug( "%sLeave @name@, ret=@fmt@\n", ctx->label, rv );
220 	return rv;
221 }
222 //# END
223 
224 //# TEMPLATE REGULAR_VOID
225 static @type@proxy_@name@( store_t *gctx@decl_args@ )
226 {
227 	proxy_store_t *ctx = (proxy_store_t *)gctx;
228 
229 	@pre_print_args@
230 	debug( "%sEnter @name@@print_fmt_args@\n", ctx->label@print_pass_args@ );
231 	@print_args@
232 	ctx->real_driver->@name@( ctx->real_store@pass_args@ );
233 	debug( "%sLeave @name@\n", ctx->label );
234 	@action@
235 }
236 //# END
237 
238 //# TEMPLATE CALLBACK
239 typedef union {
240 	@gen_cmd_t@ gen;
241 	struct {
242 		@GEN_CMD@
243 		@decl_cb_state@
244 		void (*callback)( @decl_cb_args@void *aux );
245 		void *callback_aux;
246 		@decl_state@
247 	};
248 } @name@_cmd_t;
249 
250 static void
251 proxy_do_@name@_cb( gen_cmd_t *gcmd )
252 {
253 	@name@_cmd_t *cmd = (@name@_cmd_t *)gcmd;
254 
255 	@pre_print_cb_args@
256 	debug( "%s[% 2d] Callback enter @name@@print_fmt_cb_args@\n", cmd->ctx->label, cmd->tag@print_pass_cb_args@ );
257 	@print_cb_args@
258 	cmd->callback( @pass_cb_args@cmd->callback_aux );
259 	debug( "%s[% 2d] Callback leave @name@\n", cmd->ctx->label, cmd->tag );
260 }
261 
262 static void
263 proxy_@name@_cb( @decl_cb_args@void *aux )
264 {
265 	@name@_cmd_t *cmd = (@name@_cmd_t *)aux;
266 
267 	@save_cb_args@
268 	proxy_invoke_cb( @gen_cmd@, proxy_do_@name@_cb, @checked@, "@name@" );
269 }
270 
271 static @type@proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), void *aux )
272 {
273 	proxy_store_t *ctx = (proxy_store_t *)gctx;
274 
275 	@name@_cmd_t *cmd = (@name@_cmd_t *)proxy_cmd_new( ctx, sizeof(@name@_cmd_t) );
276 	cmd->callback = cb;
277 	cmd->callback_aux = aux;
278 	@assign_state@
279 	@pre_print_args@
280 	debug( "%s[% 2d] Enter @name@@print_fmt_args@\n", ctx->label, cmd->tag@print_pass_args@ );
281 	@print_args@
282 	ctx->real_driver->@name@( ctx->real_store@pass_args@, proxy_@name@_cb, cmd );
283 	debug( "%s[% 2d] Leave @name@\n", ctx->label, cmd->tag );
284 	proxy_cmd_done( @gen_cmd@ );
285 }
286 //# END
287 
288 //# UNDEFINE list_store_print_fmt_cb_args
289 //# UNDEFINE list_store_print_pass_cb_args
290 //# DEFINE list_store_print_cb_args
291 	if (cmd->sts == DRV_OK) {
292 		for (string_list_t *box = cmd->boxes; box; box = box->next)
293 			debug( "  %s\n", box->string );
294 	}
295 //# END
296 
297 //# DEFINE load_box_pre_print_args
298 	static char ubuf[12];
299 //# END
300 //# DEFINE load_box_print_fmt_args , [%u,%s] (find >= %u, paired <= %u, new > %u)
301 //# DEFINE load_box_print_pass_args , minuid, (maxuid == UINT_MAX) ? "inf" : (nfsnprintf( ubuf, sizeof(ubuf), "%u", maxuid ), ubuf), finduid, pairuid, newuid
302 //# DEFINE load_box_print_args
303 	if (excs.size) {
304 		debugn( "  excs:" );
305 		for (uint t = 0; t < excs.size; t++)
306 			debugn( " %u", excs.data[t] );
307 		debug( "\n" );
308 	}
309 //# END
310 //# DEFINE load_box_print_fmt_cb_args , sts=%d, total=%d, recent=%d
311 //# DEFINE load_box_print_pass_cb_args , cmd->sts, cmd->total_msgs, cmd->recent_msgs
312 //# DEFINE load_box_print_cb_args
313 	if (cmd->sts == DRV_OK) {
314 		static char fbuf[as(Flags) + 1];
315 		for (message_t *msg = cmd->msgs; msg; msg = msg->next)
316 			debug( "  uid=%-5u flags=%-4s size=%-6u tuid=%." stringify(TUIDL) "s\n",
317 			       msg->uid, (msg->status & M_FLAGS) ? (proxy_make_flags( msg->flags, fbuf ), fbuf) : "?", msg->size, *msg->tuid ? msg->tuid : "?" );
318 	}
319 //# END
320 
321 //# DEFINE find_new_msgs_print_fmt_cb_args , sts=%d
322 //# DEFINE find_new_msgs_print_pass_cb_args , cmd->sts
323 //# DEFINE find_new_msgs_print_cb_args
324 	if (cmd->sts == DRV_OK) {
325 		for (message_t *msg = cmd->msgs; msg; msg = msg->next)
326 			debug( "  uid=%-5u tuid=%." stringify(TUIDL) "s\n", msg->uid, msg->tuid );
327 	}
328 //# END
329 
330 //# DEFINE fetch_msg_decl_state
331 	msg_data_t *data;
332 //# END
333 //# DEFINE fetch_msg_assign_state
334 	cmd->data = data;
335 //# END
336 //# DEFINE fetch_msg_print_fmt_args , uid=%u, want_flags=%s, want_date=%s
337 //# DEFINE fetch_msg_print_pass_args , msg->uid, !(msg->status & M_FLAGS) ? "yes" : "no", data->date ? "yes" : "no"
338 //# DEFINE fetch_msg_pre_print_cb_args
339 	static char fbuf[as(Flags) + 1];
340 	proxy_make_flags( cmd->data->flags, fbuf );
341 //# END
342 //# DEFINE fetch_msg_print_fmt_cb_args , flags=%s, date=%lld, size=%u
343 //# DEFINE fetch_msg_print_pass_cb_args , fbuf, (long long)cmd->data->date, cmd->data->len
344 //# DEFINE fetch_msg_print_cb_args
345 	if (cmd->sts == DRV_OK && (DFlags & DEBUG_DRV_ALL)) {
346 		printf( "%s=========\n", cmd->ctx->label );
347 		fwrite( cmd->data->data, cmd->data->len, 1, stdout );
348 		printf( "%s=========\n", cmd->ctx->label );
349 		fflush( stdout );
350 	}
351 //# END
352 
353 //# DEFINE store_msg_pre_print_args
354 	static char fbuf[as(Flags) + 1];
355 	proxy_make_flags( data->flags, fbuf );
356 //# END
357 //# DEFINE store_msg_print_fmt_args , flags=%s, date=%lld, size=%u, to_trash=%s
358 //# DEFINE store_msg_print_pass_args , fbuf, (long long)data->date, data->len, to_trash ? "yes" : "no"
359 //# DEFINE store_msg_print_args
360 	if (DFlags & DEBUG_DRV_ALL) {
361 		printf( "%s>>>>>>>>>\n", ctx->label );
362 		fwrite( data->data, data->len, 1, stdout );
363 		printf( "%s>>>>>>>>>\n", ctx->label );
364 		fflush( stdout );
365 	}
366 //# END
367 
368 //# DEFINE set_msg_flags_pre_print_args
369 	static char fbuf1[as(Flags) + 1], fbuf2[as(Flags) + 1];
370 	proxy_make_flags( add, fbuf1 );
371 	proxy_make_flags( del, fbuf2 );
372 //# END
373 //# DEFINE set_msg_flags_print_fmt_args , uid=%u, add=%s, del=%s
374 //# DEFINE set_msg_flags_print_pass_args , uid, fbuf1, fbuf2
375 //# DEFINE set_msg_flags_checked sts == DRV_OK
376 
377 //# DEFINE trash_msg_print_fmt_args , uid=%u
378 //# DEFINE trash_msg_print_pass_args , msg->uid
379 
380 //# DEFINE commit_cmds_print_args
381 	proxy_flush_checked_cmds( ctx );
382 //# END
383 
384 //# DEFINE cancel_cmds_print_cb_args
385 	proxy_cancel_checked_cmds( cmd->ctx );
386 //# END
387 
388 //# DEFINE free_store_print_args
389 	proxy_cancel_checked_cmds( ctx );
390 //# END
391 //# DEFINE free_store_action
392 	proxy_store_deref( ctx );
393 //# END
394 
395 //# DEFINE cancel_store_print_args
396 	proxy_cancel_checked_cmds( ctx );
397 //# END
398 //# DEFINE cancel_store_action
399 	proxy_store_deref( ctx );
400 //# END
401 #endif
402 
403 //# SPECIAL set_bad_callback
404 static void
proxy_set_bad_callback(store_t * gctx,void (* cb)(void * aux),void * aux)405 proxy_set_bad_callback( store_t *gctx, void (*cb)( void *aux ), void *aux )
406 {
407 	proxy_store_t *ctx = (proxy_store_t *)gctx;
408 
409 	ctx->bad_callback = cb;
410 	ctx->bad_callback_aux = aux;
411 }
412 
413 static void
proxy_invoke_bad_callback(proxy_store_t * ctx)414 proxy_invoke_bad_callback( proxy_store_t *ctx )
415 {
416 	ctx->ref_count++;
417 	debug( "%sCallback enter bad store\n", ctx->label );
418 	ctx->bad_callback( ctx->bad_callback_aux );
419 	debug( "%sCallback leave bad store\n", ctx->label );
420 	proxy_store_deref( ctx );
421 }
422 
423 //# EXCLUDE alloc_store
424 store_t *
proxy_alloc_store(store_t * real_ctx,const char * label)425 proxy_alloc_store( store_t *real_ctx, const char *label )
426 {
427 	proxy_store_t *ctx;
428 
429 	ctx = nfcalloc( sizeof(*ctx) );
430 	ctx->driver = &proxy_driver;
431 	ctx->gen.conf = real_ctx->conf;
432 	ctx->ref_count = 1;
433 	ctx->label = label;
434 	ctx->done_cmds_append = &ctx->done_cmds;
435 	ctx->check_cmds_append = &ctx->check_cmds;
436 	ctx->real_driver = real_ctx->driver;
437 	ctx->real_store = real_ctx;
438 	ctx->real_driver->set_bad_callback( ctx->real_store, (void (*)(void *))proxy_invoke_bad_callback, ctx );
439 	init_wakeup( &ctx->wakeup, proxy_wakeup, ctx );
440 	return &ctx->gen;
441 }
442 
443 //# EXCLUDE parse_store
444 //# EXCLUDE cleanup
445 //# EXCLUDE get_fail_state
446 
447 #include "drv_proxy.inc"
448