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