X-Git-Url: http://git.nikiroo.be/?p=jvcard.git;a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Fjvcard%2Fremote%2FServer.java;h=2ad561977c589be31abbcfe8acc85eff51052689;hp=2ead82b19a90d6bf3a84b25c9c44d9394fd0d171;hb=7da41ecd30228908bf2afcd07ff7943ab59d4c01;hpb=ebead449cce5df1849d54796fd2515a9e0556fee diff --git a/src/be/nikiroo/jvcard/remote/Server.java b/src/be/nikiroo/jvcard/remote/Server.java index 2ead82b..2ad5619 100644 --- a/src/be/nikiroo/jvcard/remote/Server.java +++ b/src/be/nikiroo/jvcard/remote/Server.java @@ -5,17 +5,20 @@ import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; +import java.security.InvalidParameterException; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.ResourceBundle; import be.nikiroo.jvcard.Card; +import be.nikiroo.jvcard.Contact; +import be.nikiroo.jvcard.Data; import be.nikiroo.jvcard.parsers.Format; -import be.nikiroo.jvcard.parsers.Parser; +import be.nikiroo.jvcard.parsers.Vcard21Parser; import be.nikiroo.jvcard.remote.Command.Verb; import be.nikiroo.jvcard.resources.Bundles; -import be.nikiroo.jvcard.tui.StringUtils; +import be.nikiroo.jvcard.resources.StringUtils; /** * This class implements a small server that can listen for requests to @@ -40,15 +43,10 @@ public class Server implements Runnable { private Object clientsLock = new Object(); private List clients = new LinkedList(); - private Object cardsLock = new Object(); - - public static void main(String[] args) throws IOException { - Server server = new Server(4444); - server.run(); - } + private Object updateLock = new Object(); /** - * Create a new jVCard sercer on the given port. + * Create a new jVCard server on the given port. * * @param port * the port to run on @@ -124,7 +122,21 @@ public class Server implements Runnable { new Thread(new Runnable() { @Override public void run() { - accept(new SimpleSocket(s, "[request]")); + SimpleSocket ss = new SimpleSocket(s, "[request]"); + + addClient(ss); + try { + ss.open(false); + + while (processCmd(ss)) + ; + + } catch (IOException e) { + e.printStackTrace(); + } finally { + ss.close(); + } + removeClient(ss); } }).start(); } catch (IOException ioe) { @@ -157,83 +169,273 @@ public class Server implements Runnable { } /** - * Accept a client and process it. + * Process a command. * * @param s - * the client to process + * the {@link SimpleSocket} from which to get the command to + * process + * + * @return TRUE if the client is ready for another command, FALSE when the + * client exited + * + * @throws IOException + * in case of IO error */ - private void accept(SimpleSocket s) { - addClient(s); + private boolean processCmd(SimpleSocket s) throws IOException { + Command cmd = s.receiveCommand(); + Command.Verb verb = cmd.getVerb(); - try { - s.open(false); - - boolean clientStop = false; - while (!clientStop) { - Command cmd = s.receiveCommand(); - Command.Verb verb = cmd.getVerb(); - - if (verb == null) - break; - - System.out.println(s + " -> " + verb); - - switch (verb) { - case STOP: - clientStop = true; - break; - case VERSION: - s.sendCommand(Verb.VERSION); - break; - case TIME: - s.sendLine(StringUtils.fromTime(new Date().getTime())); - break; - case GET: - synchronized (cardsLock) { - s.sendBlock(doGetCard(cmd.getParam())); - } - break; - case POST: - synchronized (cardsLock) { - doPostCard(cmd.getParam(), s.receiveBlock()); - s.sendBlock(); - break; - } - case LIST: - for (File file : dataDir.listFiles()) { - if (cmd.getParam() == null - || cmd.getParam().length() == 0 - || file.getName().contains(cmd.getParam())) { - s.send(StringUtils.fromTime(file.lastModified()) - + " " + file.getName()); - } - } - s.sendBlock(); - break; - case HELP: - // TODO: i18n - 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.sendBlock(); - break; - default: + if (verb == null) + return false; + + boolean clientContinue = true; + + System.out.println(s + " -> " + verb); + + switch (verb) { + case STOP: + clientContinue = false; + break; + case VERSION: + s.sendCommand(Verb.VERSION); + break; + case TIME: + s.sendLine(StringUtils.fromTime(new Date().getTime())); + break; + case GET_CARD: + synchronized (updateLock) { + s.sendBlock(doGetCard(cmd.getParam())); + } + break; + case POST_CARD: + synchronized (updateLock) { + s.sendLine(doPostCard(cmd.getParam(), s.receiveBlock())); + } + break; + case PUT_CARD: + synchronized (updateLock) { + File vcf = getFile(cmd.getParam()); + if (vcf == null) { System.err - .println("Unsupported command received from a client connection, closing it: " - + verb); - clientStop = true; - break; + .println("Fail to update a card, file not available: " + + cmd.getParam()); + clientContinue = false; + } else { + Card card = new Card(vcf, Format.VCard21); + try { + while (processContactCmd(s, card)) + ; + card.save(); + } catch (InvalidParameterException e) { + System.err + .println("Unsupported command received from a client connection, closing it: " + + verb + " (" + e.getMessage() + ")"); + clientContinue = false; + } } } - } catch (IOException e) { - e.printStackTrace(); - } finally { - s.close(); + break; + case DELETE_CARD: + // TODO + System.err + .println("Unsupported command received from a client connection, closing it: " + + verb); + clientContinue = false; + break; + case LIST: + for (File file : dataDir.listFiles()) { + if (cmd.getParam() == null || cmd.getParam().length() == 0 + || file.getName().contains(cmd.getParam())) { + s.send(StringUtils.fromTime(file.lastModified()) + " " + + file.getName()); + } + } + s.sendBlock(); + break; + case HELP: + // TODO: i18n + 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.sendBlock(); + break; + default: + System.err + .println("Unsupported command received from a client connection, closing it: " + + verb); + clientContinue = false; + break; } - removeClient(s); + return clientContinue; + } + + /** + * Process a *_CONTACT subcommand. + * + * @param s + * the {@link SimpleSocket} to process + * @param card + * the target {@link Card} + * + * @return TRUE if the client is ready for another command, FALSE when the + * client is done + * + * @throws IOException + * in case of IO error + * + * @throw InvalidParameterException in case of invalid subcommand + */ + private boolean processContactCmd(SimpleSocket s, Card card) + throws IOException { + Command cmd = s.receiveCommand(); + Command.Verb verb = cmd.getVerb(); + + if (verb == null) + return false; + + boolean clientContinue = true; + + System.out.println(s + " -> " + verb); + + switch (verb) { + case GET_CONTACT: { + Contact contact = card.getById(cmd.getParam()); + if (contact != null) + s.sendBlock(Vcard21Parser.toStrings(contact, -1)); + else + s.sendBlock(); + 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); + } + break; + } + case PUT_CONTACT: { + String uid = cmd.getParam(); + Contact contact = card.getById(uid); + if (contact == null) { + throw new InvalidParameterException( + "Cannot find contact to modify for UID: " + uid); + } + while (processDataCmd(s, contact)) + ; + break; + } + case DELETE_CONTACT: { + String uid = cmd.getParam(); + Contact contact = card.getById(uid); + if (contact == null) { + throw new InvalidParameterException( + "Cannot find contact to delete for UID: " + uid); + } + + contact.delete(); + break; + } + case PUT_CARD: { + clientContinue = false; + break; + } + default: { + throw new InvalidParameterException("command invalid here"); + } + } + + return clientContinue; + } + + /** + * Process a *_DATA subcommand. + * + * @param s + * the {@link SimpleSocket} to process + * @param card + * the target {@link Contact} + * + * @return TRUE if the client is ready for another command, FALSE when the + * client is done + * + * @throws IOException + * in case of IO error + * + * @throw InvalidParameterException in case of invalid subcommand + */ + private boolean processDataCmd(SimpleSocket s, Contact contact) + throws IOException { + Command cmd = s.receiveCommand(); + Command.Verb verb = cmd.getVerb(); + + if (verb == null) + return false; + + boolean clientContinue = true; + + System.out.println(s + " -> " + verb); + + switch (verb) { + case GET_DATA: { + Data data = contact.getById(cmd.getParam()); + if (data != null) + s.sendBlock(Vcard21Parser.toStrings(data)); + else + s.sendBlock(); + break; + } + case POST_DATA: { + String cstate = cmd.getParam(); + Data data = null; + for (Data d : contact) { + if (cstate.equals(d.getContentState())) + data = d; + } + + if (data != null) + data.delete(); + List list = Vcard21Parser.parseData(s.receiveBlock()); + if (list.size() > 0) { + contact.add(list.get(0)); + } + break; + } + case DELETE_DATA: { + String cstate = cmd.getParam(); + Data data = null; + for (Data d : contact) { + if (cstate.equals(d.getContentState())) + data = d; + } + + if (data == null) { + throw new InvalidParameterException( + "Cannot find data to delete for content state: " + + cstate); + } + + contact.delete(); + break; + } + case PUT_CONTACT: { + clientContinue = false; + break; + } + default: { + throw new InvalidParameterException("command invalid here"); + } + } + + return clientContinue; } /** @@ -250,21 +452,14 @@ public class Server implements Runnable { private List doGetCard(String name) throws IOException { List lines = new LinkedList(); - if (name != null && name.length() > 0) { - File vcf = new File(dataDir.getAbsolutePath() + File.separator - + name); + File vcf = getFile(name); - if (vcf.exists()) { - Card card = new Card(vcf, Format.VCard21); + if (vcf != null && vcf.exists()) { + Card card = new Card(vcf, Format.VCard21); - // timestamp: - lines.add(StringUtils.fromTime(card.getLastModified())); - - // TODO: !!! fix this !!! - for (String line : card.toString(Format.VCard21).split("\r\n")) { - lines.add(line); - } - } + // timestamp: + lines.add(StringUtils.fromTime(card.getLastModified())); + lines.addAll(Vcard21Parser.toStrings(card)); } return lines; @@ -278,16 +473,40 @@ public class Server implements Runnable { * @param data * the data to save * + * @return the date of last modification + * * @throws IOException * in case of error */ - private void doPostCard(String name, List data) throws IOException { - if (name != null && name.length() > 0) { - File vcf = new File(dataDir.getAbsolutePath() + File.separator - + name); + private String doPostCard(String name, List data) + throws IOException { + + File vcf = getFile(name); - Card card = new Card(Parser.parse(data, Format.VCard21)); + if (vcf != null) { + Card card = new Card(Vcard21Parser.parseContact(data)); card.saveAs(vcf, Format.VCard21); + + return StringUtils.fromTime(vcf.lastModified()); + } + + return ""; + } + + /** + * Return the {@link File} corresponding to the given resource name. + * + * @param name + * the resource name + * + * @return the corresponding {@link File} or NULL if the name was NULL or + * empty + */ + private File getFile(String name) { + if (name != null && name.length() > 0) { + return new File(dataDir.getAbsolutePath() + File.separator + name); } + + return null; } }