Remoting: lot of fixes
authorNiki Roo <niki@nikiroo.be>
Thu, 17 Mar 2016 18:45:41 +0000 (19:45 +0100)
committerNiki Roo <niki@nikiroo.be>
Thu, 17 Mar 2016 18:45:41 +0000 (19:45 +0100)
src/be/nikiroo/jvcard/launcher/Main.java
src/be/nikiroo/jvcard/remote/Command.java
src/be/nikiroo/jvcard/remote/CommandInstance.java [new file with mode: 0644]
src/be/nikiroo/jvcard/remote/Server.java
src/be/nikiroo/jvcard/remote/SimpleSocket.java
src/be/nikiroo/jvcard/remote/Sync.java
src/be/nikiroo/jvcard/resources/remote.properties

index a9587d275151c7817250714d907f8fb5379cd1d0..925ade4a4e23a0133385162cf4a03297a0fca5de 100644 (file)
@@ -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));
index 1132bb279f8014925c284e75c286636a9d260f9d..6ab044e8ffd09afdc510241f54d27d852508915c 100644 (file)
 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 (file)
index 0000000..446b194
--- /dev/null
@@ -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);
+               }
+       }
+}
index 79547c9ddc38cc10058da648f70e909158751ed4..567df69c83f12b735d33a2c330ff6b4a96a657f3 100644 (file)
@@ -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;
index e35189bce4e2a7a269a34a851e9332374709f135..acc43b295c352bad99f3e09103b632397a27e03a 100644 (file)
@@ -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 <b>NOT</b> sends the end-of-block
+        * Sends lines to the remote server. Do <b>NOT</b> 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;
        }
index 53d96320d47a1416d8d1167e6963f6bf6bfdcfba..29344d1e33db3ad6a01341ba56d212347280c930 100644 (file)
@@ -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<String> 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<String> 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<String> 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<Contact> added = new LinkedList<Contact>();
                                        List<Contact> removed = new LinkedList<Contact>();
                                        List<Contact> from = new LinkedList<Contact>();
                                        List<Contact> to = new LinkedList<Contact>();
                                        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<Data> subremoved = new LinkedList<Data>();
                                                        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<String, String> remote = new HashMap<String, String>();
+                                       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<Contact> deleted = new LinkedList<Contact>();
+                                       List<Contact> changed = new LinkedList<Contact>();
+                                       List<String> added = new LinkedList<String>();
+
+                                       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;
        }
 
        /**
index f7a78c4f07e44a3a50b8a01e48f0cda805060e64..d6c9e726e4dfa25da3927ffda510b09a36dd5a4d 100644 (file)
@@ -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: ###