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 (" ")
+ * <p>
+ * Example: <tt>
+ * <p>
+ * 2016-03-19 11:13:23 Family.vcf
+ * <p>
+ * 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 (" ")
+ * <p>
+ * Example: <tt>
+ * <p>
+ * 5d1db4f26410eae670852b53e6ea80be 6pXXHy8T3b
+ * <p>
+ * 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 (" ")
+ * <p>
+ * Example: <tt>
+ * <p>
+ * 5d1db4f26410eae670852b53e6ea80be FN
+ * <p>
+ * 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
break;
}
case VERSION: {
- s.sendCommand(Command.VERSION);
+ s.sendLine("" + SimpleSocket.CURRENT_VERSION);
break;
}
case TIME: {
}
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());
}
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;
}
break;
}
default: {
- throw new InvalidParameterException("command invalid here");
+ throw new InvalidParameterException("command invalid here: "
+ + command);
}
}
break;
}
case POST_CONTACT: {
- String uid = cmd.getParam();
- Contact contact = card.getById(uid);
- if (contact != null)
- contact.delete();
List<Contact> 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: {
}
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;
break;
}
default: {
- throw new InvalidParameterException("command invalid here");
+ throw new InvalidParameterException("command invalid here: "
+ + command);
}
}
}
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;
break;
}
default: {
- throw new InvalidParameterException("command invalid here");
+ throw new InvalidParameterException("command invalid here: "
+ + command);
}
}
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));
}
/**
- * TODO: describe protocol here.
+ * jVCard remote uses a simple plain text protocol to
+ * transfer/retrieve/delete information from/to the server.
+ *
+ * <p>
+ *
+ * The protocol is based around <b>lines</b> of text and <b>blocks</b> of
+ * <b>lines</b>. The client sends <b>commands</b> while the server only
+ * answers with text data.
+ *
+ * <p>
+ *
+ * Some definitions:
+ * <ul>
+ * <li>A <b>line</b> is the text between two new-line characters (\n).
+ * <li>A <b>block</b> is all the <b>lines</b> between two end-of-block
+ * <b>lines</b>.
+ * <li>An end-of-block <b>line</b> is a special <b>line</b>
+ * containing only a dot (.).
+ * <li>A <b>command</b> is <b>block</b> whose first <b>line</b>
+ * contains the command in upper case text and an optional text
+ * argument, optionally followed by a second <b>block</b> of "input" data
+ * </ul>
+ *
+ * <p>
+ *
+ * Upon connection, the first thing that happen is that the server sends
+ * a VERSION <b>command</b> (a <b>block</b> whose first <b>line</b> is
+ * <b><tt>VERSION 1</tt></b>, possibly followed by some help text
+ * <b>lines</b>).
+ *
+ * <p>
+ *
+ * The client <b>MUST</b> answer with another VERSION <b>command</b>.
+ *
+ * <p>
+ *
+ * From that time on, the client is allowed to send <b>commands</b>
+ * 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
*/