X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Fjvcard%2Fremote%2FSync.java;h=29344d1e33db3ad6a01341ba56d212347280c930;hb=845fb1d7c3adc0d6cdf9465c0e983ba447cfab6d;hp=9fff9b8985056039e42c14c51dfb7013efb1412f;hpb=cf77cb3542f2aefbebdb9aa00b358dbeb4489a73;p=jvcard.git diff --git a/src/be/nikiroo/jvcard/remote/Sync.java b/src/be/nikiroo/jvcard/remote/Sync.java index 9fff9b8..29344d1 100644 --- a/src/be/nikiroo/jvcard/remote/Sync.java +++ b/src/be/nikiroo/jvcard/remote/Sync.java @@ -12,17 +12,20 @@ import java.io.OutputStreamWriter; import java.net.Socket; import java.net.UnknownHostException; import java.security.InvalidParameterException; -import java.time.LocalDate; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import be.nikiroo.jvcard.Card; +import be.nikiroo.jvcard.Contact; +import be.nikiroo.jvcard.Data; import be.nikiroo.jvcard.parsers.Format; -import be.nikiroo.jvcard.parsers.Parser; -import be.nikiroo.jvcard.remote.Command.Verb; +import be.nikiroo.jvcard.parsers.Vcard21Parser; import be.nikiroo.jvcard.resources.Bundles; -import be.nikiroo.jvcard.tui.StringUtils; +import be.nikiroo.jvcard.resources.StringUtils; /** * This class will synchronise {@link Card}s between a local instance an a @@ -121,7 +124,7 @@ public class Sync { SimpleSocket s = new SimpleSocket(new Socket(host, port), "check avail client"); s.open(true); - s.sendCommand(Verb.LIST); + s.sendCommand(Command.LIST_CARD); List timestampedFiles = s.receiveBlock(); s.close(); @@ -139,15 +142,18 @@ public class Sync { } // return: synced or not - public boolean sync(Card card, boolean force) throws UnknownHostException, - IOException { + // TODO jDoc + public Card sync(boolean force) throws UnknownHostException, IOException { long tsOriginal = getLastModified(); + Card local = new Card(getCache(cacheDir), Format.VCard21); + local.setRemote(true); + // do NOT update unless we are in autoSync or forced mode or we don't // have the file on cache if (!autoSync && !force && tsOriginal != -1) { - return false; + return local; } SimpleSocket s = new SimpleSocket(new Socket(host, port), "sync client"); @@ -156,7 +162,7 @@ public class Sync { long tsServer = -1; try { s.open(true); - s.sendCommand(Verb.LIST); + s.sendCommand(Command.LIST_CARD); List timestampedFiles = s.receiveBlock(); for (String timestampedFile : timestampedFiles) { @@ -168,94 +174,210 @@ public class Sync { break; } } - } catch (IOException e) { - s.close(); - throw e; - } catch (Exception e) { - e.printStackTrace(); - s.close(); - return false; - } - // Error cases: - // - file not preset 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"); - } + // Error cases: + // - file not preset 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 - boolean serverChanges = (tsServer - tsOriginal) > GRACE_TIME; - boolean localChanges = false; - if (tsOriginal != -1) { - Card local = new Card(getCache(cacheDir), Format.VCard21); - Card original = new Card(getCache(cacheDirOrig), Format.VCard21); - localChanges = !local.isEquals(original, true); - } + // Check changes + boolean 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); + } - Verb action = null; + Command action = null; - // Sync to server if: - if (localChanges) { - // TODO: sync instead (with PUT) - action = Verb.POST; - } + // Sync to server if: + if (localChanges) { + action = Command.PUT_CARD; + } - // Sync from server if - if (serverChanges && localChanges) { - // TODO - throw new IOException("Sync operation not supported yet :("); - } + // Sync from server if: + if (serverChanges) { + action = Command.HASH_CONTACT; + } - // PUT the whole file if: - if (tsServer == -1) { - action = Verb.POST; - } + // Sync from/to server if + if (serverChanges && localChanges) { + // TODO + action = Command.HELP; + } - // GET the whole file if: - if (tsOriginal == -1 || serverChanges) { - action = Verb.GET; - } + // PUT the whole file if: + if (tsServer == -1) { + action = Command.POST_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) { - switch (action) { - case GET: - s.sendCommand(Verb.GET, name); - List data = s.receiveBlock(); - setLastModified(data.remove(0)); - Card server = new Card(Parser.parse(data, Format.VCard21)); - card.replaceListContent(server); - - if (card.isDirty()) - card.save(); - card.saveAs(getCache(cacheDirOrig), Format.VCard21); - break; - case POST: - s.sendCommand(Verb.POST, name); - s.sendLine(card.toString(Format.VCard21)); - card.saveAs(getCache(cacheDirOrig), Format.VCard21); - setLastModified(s.receiveLine()); - break; - default: - // TODO - throw new IOException(action - + " operation not supported yet :("); + // GET the whole file if: + if (tsOriginal == -1) { + action = Command.GET_CARD; } - } - s.close(); + 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); + } + + switch (action) { + case GET_CARD: + s.sendCommand(Command.GET_CARD); + List data = s.receiveBlock(); + setLastModified(data.remove(0)); + Card server = new Card(Vcard21Parser.parseContact(data)); + local.replaceListContent(server); + + if (local.isDirty()) + local.save(); + local.saveAs(getCache(cacheDirOrig), Format.VCard21); + break; + case POST_CARD: + s.sendCommand(Command.POST_CARD); + s.sendBlock(Vcard21Parser.toStrings(local)); + local.saveAs(getCache(cacheDirOrig), Format.VCard21); + setLastModified(s.receiveLine()); + break; + case PUT_CARD: { + List added = new LinkedList(); + List removed = new LinkedList(); + List from = new LinkedList(); + List to = new LinkedList(); + original.compare(local, added, removed, from, to); + + s.sendCommand(Command.PUT_CARD); + + for (Contact c : removed) { + s.sendCommand(Command.DELETE_CONTACT, c.getId()); + } + for (Contact c : added) { + s.sendCommand(Command.POST_CONTACT, c.getId()); + s.sendBlock(Vcard21Parser.toStrings(c, -1)); + } + if (from.size() > 0) { + for (int index = 0; index < from.size(); index++) { + Contact f = from.get(index); + Contact t = to.get(index); + + List subadded = new LinkedList(); + List subremoved = new LinkedList(); + f.compare(t, subadded, subremoved, subremoved, + subadded); + s.sendCommand(Command.PUT_CONTACT, name); + for (Data d : subremoved) { + s.sendCommand(Command.DELETE_DATA, + d.getContentState()); + } + for (Data d : subadded) { + s.sendCommand(Command.POST_DATA, + d.getContentState()); + s.sendBlock(Vcard21Parser.toStrings(d)); + } + } + } + + local.saveAs(getCache(cacheDirOrig), Format.VCard21); + s.sendCommand(Command.PUT_CARD); + setLastModified(s.receiveLine()); + + break; + } + case HASH_CONTACT: { + s.sendCommand(Command.PUT_CARD); + + s.sendCommand(Command.LIST_CONTACT); + Map remote = new HashMap(); + for (String line : s.receiveBlock()) { + int indexSp = line.indexOf(" "); + String hash = line.substring(0, indexSp); + String uid = line.substring(indexSp + 1); + + remote.put(uid, hash); + } + + List deleted = new LinkedList(); + List changed = new LinkedList(); + List added = new LinkedList(); + + for (Contact c : local) { + String hash = remote.get(c.getId()); + if (hash == null) { + deleted.add(c); + } else if (!hash.equals(c.getContentState())) { + changed.add(c); + } + } + + for (String uid : remote.keySet()) { + if (local.getById(uid) == null) + added.add(uid); + } + + // process: + + for (Contact c : deleted) { + c.delete(); + } + + for (String uid : added) { + s.sendCommand(Command.GET_CONTACT, uid); + for (Contact cc : Vcard21Parser.parseContact(s + .receiveBlock())) { + local.add(cc); + } + } + + for (Contact c : changed) { + c.delete(); + s.sendCommand(Command.GET_CONTACT, c.getId()); + for (Contact cc : Vcard21Parser.parseContact(s + .receiveBlock())) { + local.add(cc); + } + } + + local.save(); + local.saveAs(getCache(cacheDirOrig), Format.VCard21); + s.sendCommand(Command.PUT_CARD); + setLastModified(s.receiveLine()); + break; + } + default: + // TODO + throw new IOException(action + + " operation not supported yet :("); + } + + s.sendCommand(Command.SELECT); + } + } catch (IOException e) { + throw e; + } catch (Exception e) { + e.printStackTrace(); + return local; + } finally { + s.close(); + } - return true; + return local; } /**