From e4444b0bc462544629d9e7e7ab62b96a4d9cab10 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Sun, 20 Mar 2016 11:05:04 +0100 Subject: [PATCH] Remote: jdoc + description + some fixes: - remote is now done - sync is partially done (post/get, no sync yet) --- src/be/nikiroo/jvcard/BaseClass.java | 18 +- src/be/nikiroo/jvcard/Card.java | 2 +- src/be/nikiroo/jvcard/Contact.java | 2 +- src/be/nikiroo/jvcard/Data.java | 2 +- src/be/nikiroo/jvcard/TypeInfo.java | 2 +- src/be/nikiroo/jvcard/remote/Command.java | 226 ++++++++++++++++-- src/be/nikiroo/jvcard/remote/Server.java | 52 ++-- .../nikiroo/jvcard/remote/SimpleSocket.java | 2 +- .../nikiroo/jvcard/remote/package-info.java | 42 +++- 9 files changed, 298 insertions(+), 50 deletions(-) diff --git a/src/be/nikiroo/jvcard/BaseClass.java b/src/be/nikiroo/jvcard/BaseClass.java index 0aa18e6..df0fcf0 100644 --- a/src/be/nikiroo/jvcard/BaseClass.java +++ b/src/be/nikiroo/jvcard/BaseClass.java @@ -251,7 +251,14 @@ public abstract class BaseClass> implements List { /** * Get the recursive state of the current object, i.e., its children. It - * represents the full state information about this object's children. + * represents the full state information about this object's children. It + * may not contains spaces nor new lines. + * + *

+ * Not that this state is lossy. You cannot retrieve the data from + * the state, it can only be used as an ID to check if thw data are + * identical. + *

* * @return a {@link String} representing the current content state of this * object, i.e., its children included @@ -295,7 +302,14 @@ public abstract class BaseClass> implements List { /** * Get the state of the current object, children not included. It * represents the full state information about this object, but do not check - * its children (see {@link BaseClass#getContentState()} for that). + * its children (see {@link BaseClass#getContentState()} for that). It may + * not contains spaces nor new lines. + * + *

+ * Not that this state is lossy. You cannot retrieve the data from + * the state, it can only be used as an ID to check if thw data are + * identical. + *

* * @return a {@link String} representing the current state of this object, * children not included diff --git a/src/be/nikiroo/jvcard/Card.java b/src/be/nikiroo/jvcard/Card.java index 7939263..d2567a4 100644 --- a/src/be/nikiroo/jvcard/Card.java +++ b/src/be/nikiroo/jvcard/Card.java @@ -228,6 +228,6 @@ public class Card extends BaseClass { @Override public String getState() { - return "" + name + format; + return ("" + name + format).replace(' ', '_'); } } diff --git a/src/be/nikiroo/jvcard/Contact.java b/src/be/nikiroo/jvcard/Contact.java index ce701a2..08c2a51 100644 --- a/src/be/nikiroo/jvcard/Contact.java +++ b/src/be/nikiroo/jvcard/Contact.java @@ -418,7 +418,7 @@ public class Contact extends BaseClass { @Override public String getState() { - return "" + getPreferredDataValue("UID"); + return getId(); } /** diff --git a/src/be/nikiroo/jvcard/Data.java b/src/be/nikiroo/jvcard/Data.java index a0ef2e9..5c09a13 100644 --- a/src/be/nikiroo/jvcard/Data.java +++ b/src/be/nikiroo/jvcard/Data.java @@ -153,6 +153,6 @@ public class Data extends BaseClass { @Override public String getState() { - return "" + name + value + group; + return ("" + name + value + group).replace(' ', '_'); } } diff --git a/src/be/nikiroo/jvcard/TypeInfo.java b/src/be/nikiroo/jvcard/TypeInfo.java index 2585cfb..c5b812c 100644 --- a/src/be/nikiroo/jvcard/TypeInfo.java +++ b/src/be/nikiroo/jvcard/TypeInfo.java @@ -52,6 +52,6 @@ public class TypeInfo extends BaseClass { @Override public String getState() { - return "" + name + value; + return ("" + name + value).replace(' ', '_'); } } \ No newline at end of file diff --git a/src/be/nikiroo/jvcard/remote/Command.java b/src/be/nikiroo/jvcard/remote/Command.java index 6ab044e..3704c26 100644 --- a/src/be/nikiroo/jvcard/remote/Command.java +++ b/src/be/nikiroo/jvcard/remote/Command.java @@ -1,55 +1,233 @@ package be.nikiroo.jvcard.remote; +/** + * This enum list all the possible {@link Command}s you can send to a jVCard + * remote servrer. + * + * @author niki + * + */ public enum Command { - /** VERSION of the protocol */ + /** + * Protocol version. + * + * @return the version of the protocol used by this server + */ VERSION, - /** TIME of the remote server in milliseconds since the Unix epoch */ + /** + * Server time. + * + * @return the TIME of the remote server with the format yyyy-MM-dd HH:mm:ss + */ TIME, - /** STOP the communication (client stops) */ + /** + * STOP the client communication (the server will now close the + * communication) + */ STOP, /** - * 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 + * LIST the cards available on this server. + * + * @param parameter + * (optional) a search term that, if given, must be in the name + * of the card for it to be returned (case insensitive) + * + * @return a LIST of all the cards on the remote server that contain the + * search term, or all cards if no search term given + * + * @note The cards are listed each on their own line, preceded by their last + * modified time and a space character (" ") + *

+ * Example: + *

+ * 2016-03-19 11:13:23 Family.vcf + *

+ * 2016-03-19 11:13:23 CoWorkers.vcf */ LIST_CARD, - /** HELP about the protocol for interactive access */ + /** + * Internationalised help message. + * + * @return some HELP about the protocol for interactive access + */ HELP, - /** SELECT a resource (a card) to work on */ + /** + * SELECT a resource (a card) to work on (you can issue *_CARD commands when + * in SELECT mode), or leave SELECT mode if already enabled. + * + * @param parameter + * the resource name (the card name) to work on to enter SELECT + * mode, or nothing to leave it + * + * @return the last modified date of the selected card + */ SELECT, - /** GET a remote card */ + /** + * GET a remote card and return it as VCF data. + * + * @return VCF data + */ GET_CARD, /** - * PUT mode activation toggle for a card on the remote server (you can issue - * *_CONTACT commands when in PUT mode) + * Enter into PUT_CARD mode for the selected card (you can issue *_CONTACT + * commands when in PUT_CARD mode), or leave PUT_CARD mode if already + * enabled. + * + * @requires SELECT mode must be enabled before */ PUT_CARD, - /** POST a new card to the remote server */ + /** + * POST a new card to the remote server as the selected resource. + * + * @input will wait for the card content as VCF data + * + * @requires SELECT mode must be enabled before + */ POST_CARD, - /** DELETE an existing contact from the remote server */ + /** + * DELETE the selected contact from the remote server. + * + * @requires SELECT mode must be enabled before + */ DELETE_CARD, - /** HASH the given contact and return the hash, or empty if not found */ + /** + * HASH the selected contact and return the hash, or return empty if not + * found. + * + * @param parameter + * the UID of the contact to hash + * + * @return the hash, or an empty message if not found + * + * @requires PUT_CARD mode must be enabled before + */ HASH_CONTACT, - /** LIST all the contacts of the current card; also add their hashes */ + /** + * LIST the contacts available in the selected card. + * + * @param parameter + * (optional) a search term that, if given, must be present in + * the N or FN property of the contact for the contact to be + * returned (case insensitive) + * + * @return a LIST of all the contacts in the selected card that contain the + * search term, or all contacts if no search term given + * + * @requires PUT_CARD mode must be enabled before + * + * @note The contacts are listed each on their own line, preceded by their + * hashes and a space character (" ") + *

+ * Example: + *

+ * 5d1db4f26410eae670852b53e6ea80be 6pXXHy8T3b + *

+ * 477eef8e57a12dffeeb4063d5a138c9a FoYJUyDOwM + */ LIST_CONTACT, - /** GET a remote contact */ + /** + * GET a remote contact if found. + * + * @param parameter + * the UID of the contact to return + * + * @return the contact as VCF data or an empty message if the UID was not + * found + * + * @requires PUT_CARD mode must be enabled before + * + */ GET_CONTACT, /** - * PUT mode activation toggle for a contact on the remote server (you can - * issue *_DATA commands when in PUT mode), param = uid + * Select the given contact by UID and enter into PUT_CONTACT mode (you can + * issue *_DATA commands when in PUT_CONTACT mode), or leave PUT_CONTACT + * mode if already enabled. + * + * @param parameter + * the UID of the contact to select to enter PUT_CONTACT mode, or + * nothing to leave it + * + * @requires PUT_CARD mode must be enabled before */ PUT_CONTACT, - /** POST a new contact to the remote server */ + /** + * POST a new contact to the remote server in the selected card. + * + * @input will wait for the contact VCF data + * + * @requires PUT_CARD mode must be enabled before + */ POST_CONTACT, - /** DELETE an existing contact from the remote server */ + /** + * DELETE an existing contact from the remote server. + * + * @param parameter + * the UID of the contact to delete + * + * @requires PUT_CARD mode must be enabled before + */ DELETE_CONTACT, - /** HASH the data(s) with the given name */ + /** + * HASH the data(s) with the given name. + * + * @param parameter + * the name of the data(s) you want + * + * @return the hashes of all the datas that correspond to the given name + * + * @requires PUT_CONTACT mode must be enabled before + */ HASH_DATA, - /** LIST all the datas of the current contact; also add their hashes */ + /** + * LIST the datas available in the selected contact. + * + * @param parameter + * (optional) a search term that, if given, must be present in + * the name of the data for it to be returned (case insensitive) + * + * @return a LIST of all the datas in the selected contact that contain the + * search term, or all datas if no search term given + * + * @requires PUT_CONTACT mode must be enabled before + * + * @note The datas' names are listed each on their own line, preceded by + * their hashes and a space character (" ") + *

+ * Example: + *

+ * 5d1db4f26410eae670852b53e6ea80be FN + *

+ * 477eef8e57a12dffeeb4063d5a138c9a TEL + */ LIST_DATA, - /** GET a (or more) remote data(s) by name */ + /** + * GET one or more remote data(s) by name. + * + * @param parameter + * the name of the data(s) to return + * + * @return the datas as VCF data or an empty message if no data were found + * with that name + * + * @requires PUT_CONTACT mode must be enabled before + * + */ GET_DATA, - /** POST a new data to the remote server */ + /** + * POST a new data to the remote server in the selected contact. + * + * @input will wait for the data VCF data + * + * @requires PUT_CONTACT mode must be enabled before + */ POST_DATA, - /** DELETE an existing data from the remote server */ + /** + * DELETE an existing data from the remote server. + * + * @param parameter + * the hash of the data to delete + * + * @requires PUT_CONTACT mode must be enabled before + */ DELETE_DATA, } \ No newline at end of file diff --git a/src/be/nikiroo/jvcard/remote/Server.java b/src/be/nikiroo/jvcard/remote/Server.java index 567df69..08091f6 100644 --- a/src/be/nikiroo/jvcard/remote/Server.java +++ b/src/be/nikiroo/jvcard/remote/Server.java @@ -201,7 +201,7 @@ public class Server implements Runnable { break; } case VERSION: { - s.sendCommand(Command.VERSION); + s.sendLine("" + SimpleSocket.CURRENT_VERSION); break; } case TIME: { @@ -259,8 +259,10 @@ public class Server implements Runnable { } case LIST_CARD: { for (File file : dataDir.listFiles()) { - if (cmd.getParam() == null || cmd.getParam().length() == 0 - || file.getName().contains(cmd.getParam())) { + if (cmd.getParam() == null + || cmd.getParam().length() == 0 + || file.getName().toLowerCase() + .contains(cmd.getParam().toLowerCase())) { s.send(StringUtils.fromTime(file.lastModified()) + " " + file.getName()); } @@ -273,8 +275,8 @@ public class Server implements Runnable { s.send("The following commands are available:"); s.send("- TIME: get the server time"); s.send("- HELP: this help screen"); - s.send("- LIST: list the available cards on this server"); - s.send("- VERSION/GET/PUT/POST/DELETE/STOP: TODO"); + s.send("- LIST_CARD: list the available cards on this server"); + s.send("- VERSION/GET_*/PUT_*/POST_*/DELETE_*/STOP: TODO"); s.sendBlock(); break; } @@ -364,7 +366,8 @@ public class Server implements Runnable { break; } default: { - throw new InvalidParameterException("command invalid here"); + throw new InvalidParameterException("command invalid here: " + + command); } } @@ -409,16 +412,16 @@ public class Server implements Runnable { break; } case POST_CONTACT: { - String uid = cmd.getParam(); - Contact contact = card.getById(uid); - if (contact != null) - contact.delete(); List list = Vcard21Parser.parseContact(s.receiveBlock()); if (list.size() > 0) { - contact = list.get(0); - contact.getPreferredData("UID").setValue(uid); - card.add(contact); + Contact newContact = list.get(0); + String uid = newContact.getPreferredDataValue("UID"); + Contact oldContact = card.getById(uid); + if (oldContact != null) + oldContact.delete(); + card.add(newContact); } + break; } case PUT_CONTACT: { @@ -456,7 +459,13 @@ public class Server implements Runnable { } case LIST_CONTACT: { for (Contact contact : card) { - s.send(contact.getContentState() + " " + contact.getId()); + if (cmd.getParam() == null + || cmd.getParam().length() == 0 + || (contact.getPreferredDataValue("FN") + contact + .getPreferredDataValue("N")).toLowerCase() + .contains(cmd.getParam().toLowerCase())) { + s.send(contact.getContentState() + " " + contact.getId()); + } } s.sendBlock(); break; @@ -466,7 +475,8 @@ public class Server implements Runnable { break; } default: { - throw new InvalidParameterException("command invalid here"); + throw new InvalidParameterException("command invalid here: " + + command); } } @@ -557,7 +567,12 @@ public class Server implements Runnable { } case LIST_DATA: { for (Data data : contact) { - s.send(data.getContentState() + " " + data.getName()); + if (cmd.getParam() == null + || cmd.getParam().length() == 0 + || data.getName().toLowerCase() + .contains(cmd.getParam().toLowerCase())) { + s.send(data.getContentState() + " " + data.getName()); + } } s.sendBlock(); break; @@ -567,7 +582,8 @@ public class Server implements Runnable { break; } default: { - throw new InvalidParameterException("command invalid here"); + throw new InvalidParameterException("command invalid here: " + + command); } } @@ -593,7 +609,7 @@ public class Server implements Runnable { if (vcf != null && vcf.exists()) { Card card = new Card(vcf, Format.VCard21); - // timestamp: + // timestamp + data lines.add(StringUtils.fromTime(card.getLastModified())); lines.addAll(Vcard21Parser.toStrings(card)); } diff --git a/src/be/nikiroo/jvcard/remote/SimpleSocket.java b/src/be/nikiroo/jvcard/remote/SimpleSocket.java index acc43b2..d9dcb60 100644 --- a/src/be/nikiroo/jvcard/remote/SimpleSocket.java +++ b/src/be/nikiroo/jvcard/remote/SimpleSocket.java @@ -22,7 +22,7 @@ public class SimpleSocket { /** * The current version of the network protocol. */ - static private final int CURRENT_VERSION = 1; + static public final int CURRENT_VERSION = 1; /** * The end of block marker. diff --git a/src/be/nikiroo/jvcard/remote/package-info.java b/src/be/nikiroo/jvcard/remote/package-info.java index 5cb9de1..4c39be6 100644 --- a/src/be/nikiroo/jvcard/remote/package-info.java +++ b/src/be/nikiroo/jvcard/remote/package-info.java @@ -1,5 +1,45 @@ /** - * TODO: describe protocol here. + * jVCard remote uses a simple plain text protocol to + * transfer/retrieve/delete information from/to the server. + * + *

+ * + * The protocol is based around lines of text and blocks of + * lines. The client sends commands while the server only + * answers with text data. + * + *

+ * + * Some definitions: + *

    + *
  • A line is the text between two new-line characters (\n). + *
  • A block is all the lines between two end-of-block + * lines. + *
  • An end-of-block line is a special line + * containing only a dot (.). + *
  • A command is block whose first line + * contains the command in upper case text and an optional text + * argument, optionally followed by a second block of "input" data + *
+ * + *

+ * + * Upon connection, the first thing that happen is that the server sends + * a VERSION command (a block whose first line is + * VERSION 1, possibly followed by some help text + * lines). + * + *

+ * + * The client MUST answer with another VERSION command. + * + *

+ * + * From that time on, the client is allowed to send commands + * as described by {@link be.nikiroo.jvcard.remote.Command}. + * If the client doesn't follow the rules in + * {@link be.nikiroo.jvcard.remote.Command}, the server will close + * the connection. * * @author niki */ -- 2.27.0