From: Niki Roo Date: Thu, 17 Mar 2016 18:45:41 +0000 (+0100) Subject: Remoting: lot of fixes X-Git-Tag: v1.0-beta3~10 X-Git-Url: https://git.nikiroo.be/?p=jvcard.git;a=commitdiff_plain;h=845fb1d7c3adc0d6cdf9465c0e983ba447cfab6d Remoting: lot of fixes --- diff --git a/src/be/nikiroo/jvcard/launcher/Main.java b/src/be/nikiroo/jvcard/launcher/Main.java index a9587d2..925ade4 100644 --- a/src/be/nikiroo/jvcard/launcher/Main.java +++ b/src/be/nikiroo/jvcard/launcher/Main.java @@ -12,7 +12,7 @@ import java.util.List; import be.nikiroo.jvcard.Card; import be.nikiroo.jvcard.parsers.Format; -import be.nikiroo.jvcard.remote.Command.Verb; +import be.nikiroo.jvcard.remote.Command; import be.nikiroo.jvcard.remote.SimpleSocket; import be.nikiroo.jvcard.resources.Bundles; import be.nikiroo.jvcard.resources.StringUtils; @@ -389,17 +389,11 @@ public class Main { InvocationTargetException, IOException { @SuppressWarnings("rawtypes") Class syncClass = Class.forName("be.nikiroo.jvcard.remote.Sync"); - Method getCache = syncClass.getDeclaredMethod("getCache", - new Class[] {}); - Method sync = syncClass.getDeclaredMethod("sync", new Class[] { - Card.class, boolean.class }); + Method sync = syncClass.getDeclaredMethod("sync", + new Class[] { boolean.class }); Object o = syncClass.getConstructor(String.class).newInstance(input); - - File file = (File) getCache.invoke(o); - Card card = new Card(file, Format.VCard21); - card.setRemote(true); - sync.invoke(o, card, false); + Card card = (Card) sync.invoke(o, false); return card; } @@ -463,7 +457,7 @@ public class Main { "sync client"); s.open(true); - s.sendCommand(Verb.LIST); + s.sendCommand(Command.LIST_CARD); for (String p : s.receiveBlock()) { files.add(path + p.substring(StringUtils.fromTime(0).length() + 1)); diff --git a/src/be/nikiroo/jvcard/remote/Command.java b/src/be/nikiroo/jvcard/remote/Command.java index 1132bb2..6ab044e 100644 --- a/src/be/nikiroo/jvcard/remote/Command.java +++ b/src/be/nikiroo/jvcard/remote/Command.java @@ -1,165 +1,55 @@ package be.nikiroo.jvcard.remote; -public class Command { - public enum Verb { - /** VERSION of the protocol */ - VERSION, - /** TIME of the remote server in milliseconds since the Unix epoch */ - TIME, - /** STOP the communication (client stops) */ - STOP, - /** - * LIST all the contacts on the remote server that contain the search - * term, or all contacts if no search term given - */ - LIST, - /** HELP about the protocol for interactive access */ - HELP, - /** SELECT a resource (a card) to work on */ - SELECT, - /** GET a remote card */ - GET_CARD, - /** - * PUT mode activation toggle for a card on the remote server (you can - * issue *_CONTACT commands when in PUT mode) - */ - PUT_CARD, - /** POST a new card to the remote server */ - POST_CARD, - /** DELETE an existing contact from the remote server */ - DELETE_CARD, - /** GET a remote contact */ - GET_CONTACT, - /** - * PUT mode activation toggle for a contact on the remote server (you - * can issue *_DATA commands when in PUT mode) - */ - PUT_CONTACT, - /** POST a new contact to the remote server */ - POST_CONTACT, - /** DELETE an existing contact from the remote server */ - DELETE_CONTACT, - /** GET a remote data */ - GET_DATA, - /** POST a new data to the remote server */ - POST_DATA, - /** DELETE an existing data from the remote server */ - DELETE_DATA, - } - - private Verb verb; - private int version; - private String param; - - /** - * Create a new, empty {@link Command} with the given {@link Verb} and - * version. - * - * @param verb - * the {@link Verb} - * @param version - * the version - */ - public Command(Verb verb, int version) { - this(verb, null, version); - } - - /** - * Create a new, empty {@link Command} with the given {@link Verb} and - * version. - * - * @param verb - * the {@link Verb} - * @param version - * the version - */ - public Command(Verb verb, String param, int version) { - this.verb = verb; - this.version = version; - this.param = param; - } - - /** - * Read a command line (starting with a {@link Verb}) and process its - * content here in a more readable format. - * - * @param input - * the command line - * @param version - * the version (which can be overrided by a {@link Verb#VERSION} - * command) - */ - public Command(String input, int version) { - this.version = version; - - if (input != null) { - String v = input; - int indexSp = input.indexOf(" "); - if (indexSp >= 0) { - v = input.substring(0, indexSp); - } - - for (Verb verb : Verb.values()) { - if (v.equals(verb.name())) { - this.verb = verb; - } - } - - if (verb != null) { - String param = null; - if (indexSp >= 0) - param = input.substring(indexSp + 1); - - this.param = param; - - if (verb == Verb.VERSION) { - try { - version = Integer.parseInt(param); - } catch (NumberFormatException e) { - e.printStackTrace(); - } - } - } - } - } - +public enum Command { + /** VERSION of the protocol */ + VERSION, + /** TIME of the remote server in milliseconds since the Unix epoch */ + TIME, + /** STOP the communication (client stops) */ + STOP, /** - * Return the version - * - * @return the version + * LIST all the cards on the remote server that contain the search term, + * or all contacts if no search term given; also add their timestamps */ - public int getVersion() { - return version; - } - + LIST_CARD, + /** HELP about the protocol for interactive access */ + HELP, + /** SELECT a resource (a card) to work on */ + SELECT, + /** GET a remote card */ + GET_CARD, /** - * Return the {@link Verb} - * - * @return the {@link Verb} + * PUT mode activation toggle for a card on the remote server (you can issue + * *_CONTACT commands when in PUT mode) */ - public Verb getVerb() { - return verb; - } - + PUT_CARD, + /** POST a new card to the remote server */ + POST_CARD, + /** DELETE an existing contact from the remote server */ + DELETE_CARD, + /** HASH the given contact and return the hash, or empty if not found */ + HASH_CONTACT, + /** LIST all the contacts of the current card; also add their hashes */ + LIST_CONTACT, + /** GET a remote contact */ + GET_CONTACT, /** - * Return the parameter of this {@link Command} if any. - * - * @return the parameter or NULL + * PUT mode activation toggle for a contact on the remote server (you can + * issue *_DATA commands when in PUT mode), param = uid */ - public String getParam() { - return param; - } - - @Override - public String toString() { - if (verb == null) - return "[null command]"; - - switch (verb) { - case VERSION: - return verb.name() + " " + version; - default: - return verb.name() + (param == null ? "" : " " + param); - } - } -} + PUT_CONTACT, + /** POST a new contact to the remote server */ + POST_CONTACT, + /** DELETE an existing contact from the remote server */ + DELETE_CONTACT, + /** HASH the data(s) with the given name */ + HASH_DATA, + /** LIST all the datas of the current contact; also add their hashes */ + LIST_DATA, + /** GET a (or more) remote data(s) by name */ + GET_DATA, + /** POST a new data to the remote server */ + POST_DATA, + /** DELETE an existing data from the remote server */ + DELETE_DATA, +} \ No newline at end of file diff --git a/src/be/nikiroo/jvcard/remote/CommandInstance.java b/src/be/nikiroo/jvcard/remote/CommandInstance.java new file mode 100644 index 0000000..446b194 --- /dev/null +++ b/src/be/nikiroo/jvcard/remote/CommandInstance.java @@ -0,0 +1,119 @@ +package be.nikiroo.jvcard.remote; + +public class CommandInstance { + private Command cmd; + private int version; + private String param; + + /** + * Create a new, empty {@link CommandInstance} with the given + * {@link Command} and version. + * + * @param command + * the {@link Command} + * @param version + * the version + */ + public CommandInstance(Command command, int version) { + this(command, null, version); + } + + /** + * Create a new, empty {@link CommandInstance} with the given + * {@link Command} and version. + * + * @param cmd + * the {@link Command} + * @param version + * the version + */ + public CommandInstance(Command cmd, String param, int version) { + this.cmd = cmd; + this.version = version; + this.param = param; + } + + /** + * Read a command line (starting with a {@link Command}) and process its + * content here in a more readable format. + * + * @param input + * the command line + * @param version + * the version (which can be overrided by a + * {@link Command#VERSION} command) + */ + public CommandInstance(String input, int version) { + this.version = version; + + if (input != null) { + String v = input; + int indexSp = input.indexOf(" "); + if (indexSp >= 0) { + v = input.substring(0, indexSp); + } + + for (Command command : Command.values()) { + if (v.equals(command.name())) { + this.cmd = command; + } + } + + if (cmd != null) { + String param = null; + if (indexSp >= 0) + param = input.substring(indexSp + 1); + + this.param = param; + + if (cmd == Command.VERSION) { + try { + version = Integer.parseInt(param); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + } + } + } + + /** + * Return the version + * + * @return the version + */ + public int getVersion() { + return version; + } + + /** + * Return the {@link Command} + * + * @return the {@link Command} + */ + public Command getCommand() { + return cmd; + } + + /** + * Return the parameter of this {@link CommandInstance} if any. + * + * @return the parameter or NULL + */ + public String getParam() { + return param; + } + + @Override + public String toString() { + if (cmd == null) + return "[null command]"; + + switch (cmd) { + case VERSION: + return cmd.name() + " " + version; + default: + return cmd.name() + (param == null ? "" : " " + param); + } + } +} diff --git a/src/be/nikiroo/jvcard/remote/Server.java b/src/be/nikiroo/jvcard/remote/Server.java index 79547c9..567df69 100644 --- a/src/be/nikiroo/jvcard/remote/Server.java +++ b/src/be/nikiroo/jvcard/remote/Server.java @@ -18,7 +18,6 @@ import be.nikiroo.jvcard.Contact; import be.nikiroo.jvcard.Data; import be.nikiroo.jvcard.parsers.Format; import be.nikiroo.jvcard.parsers.Vcard21Parser; -import be.nikiroo.jvcard.remote.Command.Verb; import be.nikiroo.jvcard.resources.Bundles; import be.nikiroo.jvcard.resources.StringUtils; @@ -88,7 +87,7 @@ public class Server implements Runnable { SimpleSocket c = new SimpleSocket(new Socket((String) null, port), "special STOP client"); c.open(true); - c.sendCommand(Verb.STOP); + c.sendCommand(Command.STOP); c.close(); } catch (UnknownHostException e) { e.printStackTrace(); @@ -185,24 +184,24 @@ public class Server implements Runnable { * in case of IO error */ private boolean processCmd(SimpleSocket s) throws IOException { - Command cmd = s.receiveCommand(); - Command.Verb verb = cmd.getVerb(); + CommandInstance cmd = s.receiveCommand(); + Command command = cmd.getCommand(); - if (verb == null) + if (command == null) return false; boolean clientContinue = true; - System.out.println(s + " -> " + verb + System.out.println(s + " -> " + command + (cmd.getParam() == null ? "" : " " + cmd.getParam())); - switch (verb) { + switch (command) { case STOP: { clientContinue = false; break; } case VERSION: { - s.sendCommand(Verb.VERSION); + s.sendCommand(Command.VERSION); break; } case TIME: { @@ -242,7 +241,7 @@ public class Server implements Runnable { } catch (InvalidParameterException e) { System.err .println("Unsupported command received from a client connection, closing it: " - + verb + " (" + e.getMessage() + ")"); + + command + " (" + e.getMessage() + ")"); clientContinue = false; } } @@ -258,7 +257,7 @@ public class Server implements Runnable { } break; } - case LIST: { + case LIST_CARD: { for (File file : dataDir.listFiles()) { if (cmd.getParam() == null || cmd.getParam().length() == 0 || file.getName().contains(cmd.getParam())) { @@ -282,7 +281,7 @@ public class Server implements Runnable { default: { System.err .println("Unsupported command received from a client connection, closing it: " - + verb); + + command); clientContinue = false; break; } @@ -310,17 +309,17 @@ public class Server implements Runnable { */ private boolean processLockedCmd(SimpleSocket s, String name) throws IOException { - Command cmd = s.receiveCommand(); - Command.Verb verb = cmd.getVerb(); + CommandInstance cmd = s.receiveCommand(); + Command command = cmd.getCommand(); - if (verb == null) + if (command == null) return false; boolean clientContinue = true; - System.out.println(s + " -> " + verb); + System.out.println(s + " -> " + command); - switch (verb) { + switch (command) { case GET_CARD: { s.sendBlock(doGetCard(name)); break; @@ -342,10 +341,11 @@ public class Server implements Runnable { while (processContactCmd(s, card)) ; card.save(); + s.sendLine(StringUtils.fromTime(card.getLastModified())); } catch (InvalidParameterException e) { System.err .println("Unsupported command received from a client connection, closing it: " - + verb + " (" + e.getMessage() + ")"); + + command + " (" + e.getMessage() + ")"); clientContinue = false; } } @@ -355,7 +355,7 @@ public class Server implements Runnable { // TODO System.err .println("Unsupported command received from a client connection, closing it: " - + verb); + + command); clientContinue = false; break; } @@ -389,17 +389,17 @@ public class Server implements Runnable { */ private boolean processContactCmd(SimpleSocket s, Card card) throws IOException { - Command cmd = s.receiveCommand(); - Command.Verb verb = cmd.getVerb(); + CommandInstance cmd = s.receiveCommand(); + Command command = cmd.getCommand(); - if (verb == null) + if (command == null) return false; boolean clientContinue = true; - System.out.println(s + " -> " + verb); + System.out.println(s + " -> " + command); - switch (verb) { + switch (command) { case GET_CONTACT: { Contact contact = card.getById(cmd.getParam()); if (contact != null) @@ -443,6 +443,24 @@ public class Server implements Runnable { contact.delete(); break; } + case HASH_CONTACT: { + String uid = cmd.getParam(); + Contact contact = card.getById(uid); + + if (contact == null) { + s.sendBlock(); + } else { + s.sendLine(contact.getContentState()); + } + break; + } + case LIST_CONTACT: { + for (Contact contact : card) { + s.send(contact.getContentState() + " " + contact.getId()); + } + s.sendBlock(); + break; + } case PUT_CARD: { clientContinue = false; break; @@ -473,23 +491,26 @@ public class Server implements Runnable { */ private boolean processDataCmd(SimpleSocket s, Contact contact) throws IOException { - Command cmd = s.receiveCommand(); - Command.Verb verb = cmd.getVerb(); + CommandInstance cmd = s.receiveCommand(); + Command command = cmd.getCommand(); - if (verb == null) + if (command == null) return false; boolean clientContinue = true; - System.out.println(s + " -> " + verb); + System.out.println(s + " -> " + command); - switch (verb) { + switch (command) { case GET_DATA: { - Data data = contact.getById(cmd.getParam()); - if (data != null) - s.sendBlock(Vcard21Parser.toStrings(data)); - else - s.sendBlock(); + for (Data data : contact) { + if (data.getName().equals(cmd.getParam())) { + for (String line : Vcard21Parser.toStrings(data)) { + s.send(line); + } + } + } + s.sendBlock(); break; } case POST_DATA: { @@ -525,6 +546,22 @@ public class Server implements Runnable { contact.delete(); break; } + case HASH_DATA: { + for (Data data : contact) { + if (data.getId().equals(cmd.getParam())) { + s.send(data.getContentState()); + } + } + s.sendBlock(); + break; + } + case LIST_DATA: { + for (Data data : contact) { + s.send(data.getContentState() + " " + data.getName()); + } + s.sendBlock(); + break; + } case PUT_CONTACT: { clientContinue = false; break; diff --git a/src/be/nikiroo/jvcard/remote/SimpleSocket.java b/src/be/nikiroo/jvcard/remote/SimpleSocket.java index e35189b..acc43b2 100644 --- a/src/be/nikiroo/jvcard/remote/SimpleSocket.java +++ b/src/be/nikiroo/jvcard/remote/SimpleSocket.java @@ -10,8 +10,6 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import be.nikiroo.jvcard.remote.Command.Verb; - /** * A client or server connection, that will allow you to connect to, send and * receive data to/from a jVCard remote server. @@ -83,17 +81,18 @@ public class SimpleSocket { in = new BufferedReader(new InputStreamReader(s.getInputStream())); if (client) { - version = new Command(receiveLine(), -1).getVersion(); - sendLine(new Command(Command.Verb.VERSION, CURRENT_VERSION) + version = new CommandInstance(receiveLine(), -1).getVersion(); + sendLine(new CommandInstance(Command.VERSION, CURRENT_VERSION) .toString()); } else { - send(new Command(Command.Verb.VERSION, CURRENT_VERSION).toString()); + send(new CommandInstance(Command.VERSION, CURRENT_VERSION) + .toString()); // TODO: i18n send("[Some help info here]"); send("you need to reply with your VERSION + end of block"); send("please send HELP in a full block or help"); sendBlock(); - version = new Command(receiveLine(), -1).getVersion(); + version = new CommandInstance(receiveLine(), -1).getVersion(); } } @@ -133,7 +132,7 @@ public class SimpleSocket { } /** - * Sends commands to the remote server. Do NOT sends the end-of-block + * Sends lines to the remote server. Do NOT sends the end-of-block * marker. * * @param data @@ -198,20 +197,20 @@ public class SimpleSocket { /** * Sends commands to the remote server, then sends an end-of-block marker. * - * @param verb - * the {@link Verb} to send + * @param command + * the {@link Command} to send * * @throws IOException * in case of IO error */ - public void sendCommand(Command.Verb verb) throws IOException { - sendCommand(verb, null); + public void sendCommand(Command command) throws IOException { + sendCommand(command, null); } /** * Sends commands to the remote server, then sends an end-of-block marker. * - * @param verb + * @param command * the data to send * * @param param @@ -220,8 +219,8 @@ public class SimpleSocket { * @throws IOException * in case of IO error */ - public void sendCommand(Command.Verb verb, String param) throws IOException { - sendLine(new Command(verb, param, CURRENT_VERSION).toString()); + public void sendCommand(Command command, String param) throws IOException { + sendLine(new CommandInstance(command, param, CURRENT_VERSION).toString()); } /** @@ -283,17 +282,17 @@ public class SimpleSocket { } /** - * Read a line from the remote server and convert it to a {@link Command}, - * then read until the next end-of-block marker. + * Read a line from the remote server and convert it to a + * {@link CommandInstance}, then read until the next end-of-block marker. * - * @return the parsed {@link Command} + * @return the parsed {@link CommandInstance} * * @throws IOException * in case of IO error */ - public Command receiveCommand() throws IOException { + public CommandInstance receiveCommand() throws IOException { String line = receive(); - Command cmd = new Command(line, version); + CommandInstance cmd = new CommandInstance(line, version); receiveBlock(); return cmd; } diff --git a/src/be/nikiroo/jvcard/remote/Sync.java b/src/be/nikiroo/jvcard/remote/Sync.java index 53d9632..29344d1 100644 --- a/src/be/nikiroo/jvcard/remote/Sync.java +++ b/src/be/nikiroo/jvcard/remote/Sync.java @@ -12,8 +12,10 @@ import java.io.OutputStreamWriter; import java.net.Socket; import java.net.UnknownHostException; import java.security.InvalidParameterException; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; @@ -22,7 +24,6 @@ import be.nikiroo.jvcard.Contact; import be.nikiroo.jvcard.Data; import be.nikiroo.jvcard.parsers.Format; import be.nikiroo.jvcard.parsers.Vcard21Parser; -import be.nikiroo.jvcard.remote.Command.Verb; import be.nikiroo.jvcard.resources.Bundles; import be.nikiroo.jvcard.resources.StringUtils; @@ -123,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(); @@ -142,15 +143,17 @@ public class Sync { // return: synced or not // TODO jDoc - public boolean sync(Card card, boolean force) throws UnknownHostException, - IOException { + 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"); @@ -159,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) { @@ -184,41 +187,38 @@ public class Sync { // Check changes boolean serverChanges = (tsServer - tsOriginal) > GRACE_TIME; boolean localChanges = false; - Card local = null; Card original = null; if (tsOriginal != -1) { - local = new Card(getCache(cacheDir), Format.VCard21); original = new Card(getCache(cacheDirOrig), Format.VCard21); localChanges = !local.isEquals(original, true); } - Verb action = null; + Command action = null; // Sync to server if: if (localChanges) { - action = Verb.PUT_CARD; + action = Command.PUT_CARD; } // Sync from server if: if (serverChanges) { - // TODO: only sends changed cstate if serverChanges - action = Verb.GET_CARD; + action = Command.HASH_CONTACT; } // Sync from/to server if if (serverChanges && localChanges) { // TODO - action = Verb.HELP; + action = Command.HELP; } // PUT the whole file if: if (tsServer == -1) { - action = Verb.POST_CARD; + action = Command.POST_CARD; } // GET the whole file if: if (tsOriginal == -1) { - action = Verb.GET_CARD; + action = Command.GET_CARD; } System.err.println("remote: " + (tsServer / 1000) % 1000 + " (" @@ -230,47 +230,46 @@ public class Sync { System.err.println("choosen action: " + action); if (action != null) { - - s.sendCommand(Verb.SELECT, name); + s.sendCommand(Command.SELECT, name); if (tsServer != StringUtils.toTime(s.receiveLine())) { System.err.println("DEBUG: it changed. retry."); - s.sendCommand(Verb.SELECT); + s.sendCommand(Command.SELECT); s.close(); - return sync(card, force); + return sync(force); } switch (action) { case GET_CARD: - s.sendCommand(Verb.GET_CARD); + s.sendCommand(Command.GET_CARD); List data = s.receiveBlock(); setLastModified(data.remove(0)); Card server = new Card(Vcard21Parser.parseContact(data)); - card.replaceListContent(server); + local.replaceListContent(server); - if (card.isDirty()) - card.save(); - card.saveAs(getCache(cacheDirOrig), Format.VCard21); + if (local.isDirty()) + local.save(); + local.saveAs(getCache(cacheDirOrig), Format.VCard21); break; case POST_CARD: - s.sendCommand(Verb.POST_CARD); - s.sendBlock(Vcard21Parser.toStrings(card)); - card.saveAs(getCache(cacheDirOrig), Format.VCard21); + s.sendCommand(Command.POST_CARD); + s.sendBlock(Vcard21Parser.toStrings(local)); + local.saveAs(getCache(cacheDirOrig), Format.VCard21); setLastModified(s.receiveLine()); break; - case PUT_CARD: + 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(Verb.PUT_CARD); + s.sendCommand(Command.PUT_CARD); for (Contact c : removed) { - s.sendCommand(Verb.DELETE_CONTACT, c.getId()); + s.sendCommand(Command.DELETE_CONTACT, c.getId()); } for (Contact c : added) { - s.sendCommand(Verb.POST_CONTACT, c.getId()); + s.sendCommand(Command.POST_CONTACT, c.getId()); s.sendBlock(Vcard21Parser.toStrings(c, -1)); } if (from.size() > 0) { @@ -282,39 +281,103 @@ public class Sync { List subremoved = new LinkedList(); f.compare(t, subadded, subremoved, subremoved, subadded); - s.sendCommand(Verb.PUT_CONTACT, name); + s.sendCommand(Command.PUT_CONTACT, name); for (Data d : subremoved) { - s.sendCommand(Verb.DELETE_DATA, + s.sendCommand(Command.DELETE_DATA, d.getContentState()); } for (Data d : subadded) { - s.sendCommand(Verb.POST_DATA, + s.sendCommand(Command.POST_DATA, d.getContentState()); s.sendBlock(Vcard21Parser.toStrings(d)); } } } - s.sendCommand(Verb.PUT_CARD); + 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(Verb.SELECT); + s.sendCommand(Command.SELECT); } } catch (IOException e) { throw e; } catch (Exception e) { e.printStackTrace(); - return false; + return local; } finally { s.close(); } - return true; + return local; } /** diff --git a/src/be/nikiroo/jvcard/resources/remote.properties b/src/be/nikiroo/jvcard/resources/remote.properties index f7a78c4..d6c9e72 100644 --- a/src/be/nikiroo/jvcard/resources/remote.properties +++ b/src/be/nikiroo/jvcard/resources/remote.properties @@ -6,7 +6,7 @@ ############### # when starting as a jVCard remote server, where to look for data: -SERVER_DATA_PATH = /tmp/client/original/ +SERVER_DATA_PATH = /tmp/server/ ############### ### Client: ###