WvDBusMsg::is_reply() had an unnecessary hack for message #1.
[wvapps.git] / wvsync / wvsyncarbiter.cc
blobc2e227c97a28363710ef05061ed5a85195eb0d07
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2004 Net Integration Technologies, Inc.
5 * See wvsyncarbiter.h for details.
7 */
9 #include "wvsyncarbiter.h"
10 #include "wvsyncobj.h"
11 #include "uniconf.h"
13 #include <wvhex.h>
14 #include <wvdigest.h>
16 bool WvSyncArbiter::arblocal(UniConf &root, WvSyncObj &obj, WvBuf &sigbuf)
18 // make a local handle to the file in the given tree
19 UniConf cfg(root[obj.name]);
20 cfg["act"].setme(WvString::null);
22 time_t oldmtime = cfg.xgetint("mtime", -1);
23 WvString oldmeta = cfg["meta"].getme();
25 // remember a few old things for revertability, should a
26 // transfer fail or be unacceptable.
27 if (oldmtime > 0)
28 cfg["old mtime"].setmeint(oldmtime);
29 cfg["old meta"].setme(oldmeta);
30 cfg["old md5sig"].setme(cfg["md5sig"].getme());
32 if (oldmtime < 0)
34 // if it didn't exist, obviously we're adding.
35 sigbuf.zap();
36 int sig_ret = obj.getsig(sigbuf);
37 log(WvLog::Debug5, "getsig: %s\n", sig_ret);
38 int siglen = sigbuf.used();
40 if (obj.isok())
42 log(WvLog::Debug2, " NEW: %s\n", obj.name);
43 WvDynBuf tmpbuf;
44 WvString md5sig;
46 // special case: if isdir(), that's different from
47 // something that DOES have contents, but 0 bytes in it!
48 if (!obj.isdir())
50 // used to unget() here, but sometimes siglen was larger
51 // than maxungettable. Make a copy instead... (sigh)
52 WvDynBuf tmpbuf2;
53 tmpbuf2.put(sigbuf.peek(0, siglen), siglen);
54 WvMD5Digest().flush(tmpbuf2, tmpbuf, true);
55 md5sig = WvHexEncoder().strflushbuf(tmpbuf, true);
57 else
58 md5sig = "none";
60 cfg["md5sig"].setme(md5sig);
61 cfg["meta"].setme(obj.getmeta());
62 cfg["act"].setme("new");
63 cfg["mtime"].setmeint(obj.getlastmodtime());
65 else
66 return false; // couldn't getsig(), so it's broken. skip it.
68 else
70 // if it did exist in cfg, we're (potentially) changing.
71 // check the mtime and signature.
72 if (obj.isunchanged(oldmtime, oldmeta))
74 // if mtimes and meta match, nothing has changed.
75 log(WvLog::Debug5, "UNCHANGED: %s\n", obj.name);
76 cfg["act"].setme("ok");
78 //jdeboer: Lets compute a sig, maybe good things will happen.
79 //dcoombs: FIXME: why is this necessary?
80 sigbuf.zap();
81 obj.getsig(sigbuf);
83 else
85 // mtime mismatch, so we have to think harder.
86 // if the signature has changed, the contents obviously have
87 // too. if the signatures match, but the metadata has changed,
88 // then we can send just the metadata and save time.
90 sigbuf.zap();
91 obj.getsig(sigbuf);
92 int siglen = sigbuf.used();
93 WvString meta = obj.getmeta();
95 if (!obj.isok())
97 // couldn't getsig(), I guess it was deleted.
98 // should still return true!
99 log(WvLog::Debug2, " GONE: %s\n", obj.name);
100 cfg["act"].setme("del");
101 WvSyncObj::forget(cfg);
103 else
105 WvDynBuf tmpbuf;
106 WvString md5sig;
108 // special case: if isdir(), that's different from
109 // something that DOES have contents, but 0 bytes in it!
110 if (!obj.isdir())
112 // used to unget() here, but sometimes siglen was larger
113 // than maxungettable. Make a copy instead... (sigh)
114 WvDynBuf tmpbuf2;
115 tmpbuf2.put(sigbuf.peek(0, siglen), siglen);
116 WvMD5Digest().flush(tmpbuf2, tmpbuf, true);
117 md5sig = WvHexEncoder().strflushbuf(tmpbuf, true);
119 else
120 md5sig = "none";
122 WvString oldmd5sig = cfg["md5sig"].getme();
123 if (strcmp(md5sig, oldmd5sig))
125 // signatures differ
126 log(WvLog::Debug2, " CHANGED: %s\n", obj.name);
127 cfg["act"].setme("mod");
128 cfg["md5sig"].setme(md5sig);
130 else
132 // signatures don't differ. just send metadata.
133 log(WvLog::Debug2, " META: %s\n", obj.name);
134 cfg["act"].setme("meta");
136 cfg["meta"].setme(meta);
137 cfg["mtime"].setmeint(obj.getlastmodtime());
142 cfg.commit();
143 return true;
147 bool WvSyncArbiter::arbremote(UniConf &root, WvSyncObj &obj,
148 WvStringParm remact, WvStringParm remmeta,
149 time_t remmtime, WvStringParm remmd5sig)
151 // make a local handle to the file in the given tree
152 UniConf cfg(root[obj.name]);
154 // First question: Did the remote end know about this object? If not,
155 // remmtime will be -1.
156 if (remmtime < 0)
158 // If the other side didn't know about it, we obviously can't
159 // revert our own, even if we thought we could. And if it's
160 // locally anything other than delete, we might as well call it
161 // new, since the other side will.
162 WvSyncObj::forget(cfg);
164 if (cfg["act"].getme() != "del")
165 cfg["act"].setme("new");
167 cfg.commit();
168 return false; // won't request
171 // So the remote side knew about it.
173 // Next question: Did WE know about it? If not, obj.isok() will be
174 // false because we will have failed to calculate the signature. In
175 // that case, we should consider it new from remote, and request it,
176 // unless the remote side is deleting it.
177 if (!obj.isok())
179 if (remact != "del")
181 log(WvLog::Debug1, "NEW FROM REMOTE: %s\n", obj.name);
182 cfg["md5sig"].setme(remmd5sig);
183 cfg["mtime"].setme(remmtime);
184 cfg["meta"].setme(remmeta);
185 cfg["act"].setme("new");
186 cfg.commit();
187 return true; // will request
189 cfg.commit();
190 return false; // do nothing
193 // OK, both sides knew about the object. Think harder.
194 WvString act(cfg["act"].getme("ok"));
195 bool ret;
196 if (act == "new" || act == "mod")
197 ret = arbr_mod(root, obj, remact, remmeta, remmtime, remmd5sig);
198 else if (act == "meta")
199 ret = arbr_meta(root, obj, remact, remmeta, remmtime, remmd5sig);
200 else if (act == "del")
201 ret = arbr_del(root, obj, remact, remmeta, remmtime, remmd5sig);
202 else
203 ret = arbr_ok(root, obj, remact, remmeta, remmtime, remmd5sig);
205 cfg.commit();
206 return ret;
210 void WvSyncArbiter::arbr_win(UniConf &root, WvSyncObj &obj,
211 WvStringParm remact, WvStringParm remmeta,
212 time_t remmtime, WvStringParm remmd5sig)
214 // make a local handle to the file in the given tree
215 UniConf cfg(root[obj.name]);
217 if (cfg["act"].getme() != "new" || remact != "new")
218 cfg["act"].setme("mod");
220 // if we win, the remote side will request from us.
221 // but we can't revert, because the remote side has renamed its copy.
222 WvSyncObj::forget(cfg);
224 // find out the name of the renamed version we have to download.
225 WvString newname = obj.conflictname(remmtime);
226 log(WvLog::Warning, "CONFLICT: remote '%s' will be called '%s'\n",
227 obj.name, newname);
228 wvcon->print("FIXME: tell protocol it has to download %s\n", newname);
230 // we copy the information (mtime, md5sig, meta) from our own winning
231 // object, since we're in fact copying the winning object to give us
232 // something to rsync. when we request the renamed version later,
233 // we'll figure stuff out.
234 obj.makecopy(newname);
235 UniConf newcfg(root[newname]);
236 cfg.copy(newcfg, false);
237 newcfg["act"].setme("ok");
238 WvSyncObj::forget(newcfg);
242 void WvSyncArbiter::arbr_lose(UniConf &root, WvSyncObj &obj,
243 WvStringParm remact, WvStringParm remmeta,
244 time_t remmtime, WvStringParm remmd5sig)
246 // make a local handle to the file in the given tree
247 UniConf cfg(root[obj.name]);
249 if (cfg["act"].getme() != "new" || remact != "new")
250 cfg["act"].setme("mod");
252 // if we lose, rename the object on this end, and mark it new.
253 WvString newname = obj.conflictname(obj.getlastmodtime());
254 log(WvLog::Warning, "CONFLICT: local '%s' will be renamed to '%s'\n",
255 obj.name, newname);
257 obj.makecopy(newname);
258 UniConf newcfg(root[newname]);
259 cfg.copy(newcfg, false);
260 newcfg["act"].setme("new"); // redundant, since arblocal will
261 // decide the same thing, but it helps
262 // me think straight.
263 WvSyncObj::forget(newcfg);
265 // we still want to receive the winning object!
266 WvSyncObj::forget(cfg);
267 cfg["mtime"].setmeint(remmtime);
268 cfg["md5sig"].setme(remmd5sig);
269 cfg["meta"].setme(remmeta);
273 bool WvSyncArbiter::arbr_conflict(UniConf &root, WvSyncObj &obj,
274 WvStringParm remact, WvStringParm remmeta,
275 time_t remmtime, WvStringParm remmd5sig)
277 // make a local handle to the file in the given tree
278 UniConf cfg(root[obj.name]);
280 // the most recent change wins. If they were at the same time, pick
281 // one randomly using strcmp. Ha ha ha ha ha FIXME.
283 if (obj.getlastmodtime() > remmtime)
285 arbr_win(root, obj, remact, remmeta, remmtime, remmd5sig);
286 return false; // we won, don't request
288 else if (obj.getlastmodtime() < remmtime)
290 arbr_lose(root, obj, remact, remmeta, remmtime, remmd5sig);
291 return true; // we lost, must probably request download
293 else if (strcmp(cfg["md5sig"].getme(), remmd5sig) < 0)
295 arbr_win(root, obj, remact, remmeta, remmtime, remmd5sig);
296 return false; // we won, don't request
298 else
300 arbr_lose(root, obj, remact, remmeta, remmtime, remmd5sig);
301 return true; // we lost, must probably request donwload
306 bool WvSyncArbiter::arbr_ok(UniConf &root, WvSyncObj &obj,
307 WvStringParm remact, WvStringParm remmeta,
308 time_t remmtime, WvStringParm remmd5sig)
310 // make a local handle to the file in the given tree
311 UniConf cfg(root[obj.name]);
313 // no change on our end, so:
314 // 1) no change on remote end, we're fine
315 // 2) remote end new/mod, we surrender
316 // 3) remote end meta, we accept the new meta info
317 // 4) remote end del, delete here too
319 if (remact == "new" || remact == "mod")
321 if (remact == "new")
322 WvSyncObj::forget(cfg);
324 cfg["mtime"].setmeint(remmtime);
325 cfg["md5sig"].setme(remmd5sig);
326 cfg["meta"].setme(remmeta);
327 cfg["act"].setme(remact);
329 // if the object has changed in nature so that it no longer has
330 // contents, consider that a conflict. The one with no contents
331 // wins. This is a little random, but it's probably only used for
332 // synchronizing files, where something with no contents is a
333 // directory, and when directories turn into files bad things can
334 // happen.
335 if (!obj.isdir() && obj.isdir(remmeta))
337 arbr_lose(root, obj, remact, remmeta, remmtime, remmd5sig);
338 return true; // we lost, must request download
340 else if (obj.isdir() && !obj.isdir(remmeta))
342 arbr_win(root, obj, remact, remmeta, remmtime, remmd5sig);
343 return false; // we won, don't request
346 // no "conflict"
347 return true; // will request
350 if (remact == "del")
352 cfg["act"].setme(remact);
353 return false; // nothing to request
356 if (remact == "meta")
358 cfg["meta"].setme(remmeta);
359 cfg["mtime"].setmeint(remmtime);
360 obj.applymeta(remmeta, remmtime);
361 return false; // don't need to request the actual data
364 return false; // don't need to request anything
368 bool WvSyncArbiter::arbr_mod(UniConf &root, WvSyncObj &obj,
369 WvStringParm remact, WvStringParm remmeta,
370 time_t remmtime, WvStringParm remmd5sig)
372 // make a local handle to the file in the given tree
373 UniConf cfg(root[obj.name]);
375 // changed on our end, so
376 // 1) no change on remote end, it'll surrender
377 // 2) remote end new/mod, check for conflict...
378 // 3) remote end meta, conflict
379 // 4) remote end del, it'll forget it
381 // can't revert if it's new
382 if (cfg["act"].getme() == "new")
383 WvSyncObj::forget(cfg);
385 if (remact == "meta")
387 // if it was changed locally, and only the metadata changed on the
388 // remote end, I guarantee you have a conflict.
389 return arbr_conflict(root, obj, remact, remmeta, remmtime, remmd5sig);
392 if (remact == "new" || remact == "mod")
394 // if the object has changed in nature so that it no longer has
395 // contents, consider that a conflict. The one with no contents
396 // wins. This is a little random, but it's probably only used for
397 // synchronizing files, where something with no contents is a
398 // directory, and when directories turn into files bad things can
399 // happen.
400 if (!obj.isdir() && obj.isdir(remmeta))
402 arbr_lose(root, obj, remact, remmeta, remmtime, remmd5sig);
403 return true; // we lost, must request download
405 else if (obj.isdir() && !obj.isdir(remmeta))
407 arbr_win(root, obj, remact, remmeta, remmtime, remmd5sig);
408 return false; // we won, don't request
411 // no conflict, at least not due to type change
413 // if the md5sigs and metadata match, the same change was made on
414 // both ends! just update the mtime in that case.
415 if (cfg["md5sig"].getme() == remmd5sig
416 && obj.getmeta() == remmeta)
418 // most recent mtime and meta information wins
419 if (obj.getlastmodtime() < remmtime)
421 cfg["mtime"].setmeint(remmtime);
422 obj.applymeta(remmeta, remmtime); // remmeta was the same
425 return false;
427 else
429 // if there was an md5sig or metadata conflict...
430 return arbr_conflict(root, obj, remact, remmeta,
431 remmtime, remmd5sig);
435 if (remact == "del")
437 // no point being revertible if other side isn't
438 WvSyncObj::forget(cfg);
441 return false; // other side is requesting, not us
445 bool WvSyncArbiter::arbr_meta(UniConf &root, WvSyncObj &obj,
446 WvStringParm remact, WvStringParm remmeta,
447 time_t remmtime, WvStringParm remmd5sig)
449 // make a local handle to the file in the given tree
450 UniConf cfg(root[obj.name]);
452 // metadata changed on our end, so
453 // 1) no change on remote end, it'll surrender
454 // 2) remote end new/mod, conflict
455 // 3) remote end meta, check for conflict...
456 // 4) remote end del, it'll forget it
458 if (remact == "new" || remact == "mod")
460 // if it was changed remotely, and only the metadata changed on
461 // this end, I guarantee you have a conflict.
462 return arbr_conflict(root, obj, remact, remmeta, remmtime, remmd5sig);
465 if (remact == "meta")
467 // if the metadata matches, the same change was made on both ends!
468 // just update the mtime in that case.
469 if (obj.getmeta() == remmeta)
471 // most recent mtime and meta information wins
472 if (obj.getlastmodtime() < remmtime)
474 cfg["mtime"].setmeint(remmtime);
475 obj.applymeta(remmeta, remmtime); // remmeta was the same
478 return false;
480 else
482 // if there was a metadata conflict...
483 return arbr_conflict(root, obj, remact, remmeta,
484 remmtime, remmd5sig);
488 if (remact == "del")
490 // no point being revertible if other side isn't
491 WvSyncObj::forget(cfg);
494 return false;
498 bool WvSyncArbiter::arbr_del(UniConf &root, WvSyncObj &obj,
499 WvStringParm remact, WvStringParm remmeta,
500 time_t remmtime, WvStringParm remmd5sig)
502 // make a local handle to the file in the given tree
503 UniConf cfg(root[obj.name]);
505 // deleted on our end, so
506 // 1) no change on remote end, it'll delete
507 // 2) remote end new/mod, forget the delete, surrender
508 // 3) remote end meta, be safe, forget the delete, surrender
509 // 4) remote end del, we're fine
511 WvSyncObj::forget(cfg);
513 if (remact == "new" || remact == "mod" || remact == "meta")
515 cfg["mtime"].setmeint(remmtime);
516 cfg["md5sig"].setme(remmd5sig);
517 cfg["meta"].setme(remmeta);
518 cfg["act"].setme(remact);
520 return true; // will request
523 return false;