+
+ // Error cases:
+ // - file not present neither in cache nor on server
+ // - remote < previous
+ if ((tsServer == -1 && tsOriginal == -1)
+ || (tsServer != -1 && tsOriginal != -1 && ((tsOriginal - tsServer) > GRACE_TIME))) {
+ throw new IOException(
+ "The timestamps between server and client are invalid");
+ }
+
+ // Check changes
+ serverChanges = (tsServer - tsOriginal) > GRACE_TIME;
+ boolean localChanges = false;
+ Card original = null;
+ if (tsOriginal != -1) {
+ original = new Card(getCache(cacheDirOrig), Format.VCard21);
+ localChanges = !local.isEquals(original, true);
+ }
+
+ Command action = null;
+
+ // Sync to server if:
+ if (localChanges) {
+ action = Command.PUT_CARD;
+ }
+
+ // Sync from server if:
+ if (serverChanges) {
+ action = Command.HASH_CONTACT;
+ }
+
+ // Sync from/to server if
+ if (serverChanges && localChanges) {
+ action = Command.HELP;
+ }
+
+ // POST the whole file if:
+ if (tsServer == -1) {
+ action = Command.POST_CARD;
+ }
+
+ // GET the whole file if:
+ if (tsOriginal == -1) {
+ action = Command.GET_CARD;
+ }
+
+ System.err.println("remote: " + (tsServer / 1000) % 1000 + " ("
+ + tsServer + ")");
+ System.err.println("previous: " + (tsOriginal / 1000) % 1000 + " ("
+ + tsOriginal + ")");
+ System.err.println("local changes: " + localChanges);
+ System.err.println("server changes: " + serverChanges);
+ System.err.println("choosen action: " + action);
+
+ if (action != null) {
+ s.sendCommand(Command.SELECT, name);
+ if (tsServer != StringUtils.toTime(s.receiveLine())) {
+ System.err.println("DEBUG: it changed. retry.");
+ s.sendCommand(Command.SELECT);
+ s.close();
+ return sync(force, callback);
+ }
+
+ switch (action) {
+ case GET_CARD: {
+ s.sendCommand(Command.GET_CARD);
+ List<String> data = s.receiveBlock();
+ setLastModified(data.remove(0));
+ local.replaceListContent(Vcard21Parser.parseContact(data));
+
+ if (local.isDirty())
+ local.save();
+ local.saveAs(getCache(cacheDirOrig), Format.VCard21);
+ break;
+ }
+ case POST_CARD: {
+ s.sendCommand(Command.POST_CARD);
+ BlockAppendable app = s.createBlockAppendable();
+ Vcard21Parser.write(app, local);
+ app.close();
+ local.saveAs(getCache(cacheDirOrig), Format.VCard21);
+ setLastModified(s.receiveLine());
+ break;
+ }
+ case PUT_CARD: {
+ String serverLastModifTime = updateToServer(s, original,
+ local);
+
+ local.saveAs(getCache(cacheDirOrig), Format.VCard21);
+
+ setLastModified(serverLastModifTime);
+ break;
+ }
+ case HASH_CONTACT: {
+ String serverLastModifTime = updateFromServer(s, local);
+
+ local.save();
+ local.saveAs(getCache(cacheDirOrig), Format.VCard21);
+
+ setLastModified(serverLastModifTime);
+ break;
+ }
+ case HELP: {
+ // note: we are holding the server here, so it could throw
+ // us away if we take too long
+
+ // TODO: check if those files are deleted
+ File mergeF = File.createTempFile("contact-merge", ".vcf");
+ File serverF = File
+ .createTempFile("contact-server", ".vcf");
+ original.saveAs(serverF, Format.VCard21);
+
+ Card server = new Card(serverF, Format.VCard21);
+ updateFromServer(s, server);
+
+ // Do an auto sync
+ server.saveAs(mergeF, Format.VCard21);
+ Card merge = new Card(mergeF, Format.VCard21);
+ List<Contact> added = new LinkedList<Contact>();
+ List<Contact> removed = new LinkedList<Contact>();
+ original.compare(local, added, removed, removed, added);
+ for (Contact c : removed)
+ merge.getById(c.getId()).delete();
+ for (Contact c : added)
+ merge.add(Vcard21Parser.clone(c));
+
+ merge.save();
+
+ // defer to client:
+ if (callback == null) {
+ throw new IOException(
+ "Conflicting changes detected and merge operation not allowed");
+ }
+
+ merge = callback.merge(original, local, server, merge);
+ if (merge == null) {
+ throw new IOException(
+ "Conflicting changes detected and merge operation cancelled");
+ }
+
+ // TODO: something like:
+ // String serverLastModifTime = updateToServer(s, original,
+ // merge);
+ // ...but without starting with original since it is not
+ // true here
+ s.sendCommand(Command.POST_CARD);
+ BlockAppendable app = s.createBlockAppendable();
+ Vcard21Parser.write(app, merge);
+ app.close();
+ String serverLastModifTime = s.receiveLine();
+ //
+
+ merge.saveAs(getCache(cacheDir), Format.VCard21);
+ merge.saveAs(getCache(cacheDirOrig), Format.VCard21);
+
+ setLastModified(serverLastModifTime);
+
+ local = merge;
+
+ break;
+ }
+ default:
+ // will not happen
+ break;
+ }
+
+ s.sendCommand(Command.SELECT);
+ }