X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Fjvcard%2Fremote%2FServer.java;h=ac29e972a562fe94685358c909221a9ffd38d119;hb=59597d59aa262e31c2e1b7f66b4cb299f88ebd1b;hp=2ad561977c589be31abbcfe8acc85eff51052689;hpb=7da41ecd30228908bf2afcd07ff7943ab59d4c01;p=jvcard.git diff --git a/src/be/nikiroo/jvcard/remote/Server.java b/src/be/nikiroo/jvcard/remote/Server.java index 2ad5619..ac29e97 100644 --- a/src/be/nikiroo/jvcard/remote/Server.java +++ b/src/be/nikiroo/jvcard/remote/Server.java @@ -7,18 +7,20 @@ import java.net.Socket; import java.net.UnknownHostException; import java.security.InvalidParameterException; import java.util.Date; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.ResourceBundle; +import java.util.Map; 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.Vcard21Parser; -import be.nikiroo.jvcard.remote.Command.Verb; -import be.nikiroo.jvcard.resources.Bundles; +import be.nikiroo.jvcard.remote.SimpleSocket.BlockAppendable; import be.nikiroo.jvcard.resources.StringUtils; +import be.nikiroo.jvcard.resources.bundles.RemoteBundle; +import be.nikiroo.jvcard.resources.enums.RemotingOption; /** * This class implements a small server that can listen for requests to @@ -44,6 +46,7 @@ public class Server implements Runnable { private List clients = new LinkedList(); private Object updateLock = new Object(); + private Map updates = new HashMap(); /** * Create a new jVCard server on the given port. @@ -56,9 +59,9 @@ public class Server implements Runnable { */ public Server(int port) throws IOException { this.port = port; - ResourceBundle bundle = Bundles.getBundle("remote"); + RemoteBundle bundle = new RemoteBundle(); try { - String dir = bundle.getString("SERVER_DATA_PATH"); + String dir = bundle.getString(RemotingOption.SERVER_DATA_PATH); dataDir = new File(dir); dataDir.mkdir(); @@ -85,7 +88,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(); @@ -169,7 +172,7 @@ public class Server implements Runnable { } /** - * Process a command. + * Process a first-level command. * * @param s * the {@link SimpleSocket} from which to get the command to @@ -182,92 +185,192 @@ 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) { - case STOP: + switch (command) { + case STOP: { clientContinue = false; break; - case VERSION: - s.sendCommand(Verb.VERSION); + } + case VERSION: { + s.sendLine("" + SimpleSocket.CURRENT_VERSION); break; - case TIME: + } + 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("Fail to update a card, file not available: " - + cmd.getParam()); - clientContinue = false; - } else { - Card card = new Card(vcf, Format.VCard21); + } + case SELECT: { + String name = cmd.getParam(); + File file = new File(dataDir.getAbsolutePath() + File.separator + + name); + if (name == null || name.length() == 0 || !file.exists()) { + System.err + .println("SELECT: resource not found, closing connection: " + + name); + clientContinue = false; + } else { + synchronized (updateLock) { + for (File f : updates.keySet()) { + if (f.getCanonicalPath() + .equals(file.getCanonicalPath())) { + file = f; + break; + } + } + + if (!updates.containsKey(file)) + updates.put(file, 0); + updates.put(file, updates.get(file) + 1); + } + + synchronized (file) { try { - while (processContactCmd(s, card)) + s.sendLine(StringUtils.fromTime(file.lastModified())); + + while (processLockedCmd(s, name)) ; - card.save(); } catch (InvalidParameterException e) { System.err .println("Unsupported command received from a client connection, closing it: " - + verb + " (" + e.getMessage() + ")"); + + command + " (" + e.getMessage() + ")"); clientContinue = false; } } + + synchronized (updateLock) { + int num = updates.get(file) - 1; + if (num == 0) { + updates.remove(file); + } else { + updates.put(file, num); + } + } } break; - case DELETE_CARD: - // TODO - System.err - .println("Unsupported command received from a client connection, closing it: " - + verb); - clientContinue = false; - break; - case LIST: + } + 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.sendBlock(); break; - case HELP: + } + 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.send("- LIST_CARD: list the available cards on this server"); + s.send("- VERSION/GET_*/PUT_*/POST_*/DELETE_*/STOP: TODO"); s.sendBlock(); break; - default: + } + default: { + System.err + .println("Unsupported command received from a client connection, closing it: " + + command); + clientContinue = false; + break; + } + } + + return clientContinue; + } + + /** + * Process a subcommand while protected for resource name. + * + * @param s + * the {@link SimpleSocket} to process + * + * @param name + * the resource that is protected (and to target) + * + * @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 processLockedCmd(SimpleSocket s, String name) + throws IOException { + CommandInstance cmd = s.receiveCommand(); + Command command = cmd.getCommand(); + + if (command == null) + return false; + + boolean clientContinue = true; + + System.out.println(s + " -> " + command); + + switch (command) { + case GET_CARD: { + sendCardBlock(s, name); + break; + } + case POST_CARD: { + s.sendLine(doPostCard(name, s.receiveBlock())); + break; + } + case PUT_CARD: { + File vcf = getFile(name); + if (vcf == null) { + System.err + .println("Fail to update a card, file not available: " + + name); + clientContinue = false; + } else { + Card card = new Card(vcf, Format.VCard21); + try { + 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: " + + command + " (" + e.getMessage() + ")"); + clientContinue = false; + } + } + break; + } + case DELETE_CARD: { + // TODO System.err .println("Unsupported command received from a client connection, closing it: " - + verb); + + command); clientContinue = false; break; } + case SELECT: { + clientContinue = false; + break; + } + default: { + throw new InvalidParameterException("command invalid here: " + + command); + } + } return clientContinue; } @@ -290,36 +393,39 @@ 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) - s.sendBlock(Vcard21Parser.toStrings(contact, -1)); - else + if (contact != null) { + BlockAppendable app = s.createBlockAppendable(); + Vcard21Parser.write(app, contact, -1); + app.close(); + } 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); + 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: { @@ -344,12 +450,38 @@ 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(true)); + } + break; + } + case LIST_CONTACT: { + for (Contact contact : card) { + if (cmd.getParam() == null + || cmd.getParam().length() == 0 + || (contact.getPreferredDataValue("FN") + contact + .getPreferredDataValue("N")).toLowerCase() + .contains(cmd.getParam().toLowerCase())) { + s.send(contact.getContentState(true) + " " + + contact.getId()); + } + } + s.sendBlock(); + break; + } case PUT_CARD: { clientContinue = false; break; } default: { - throw new InvalidParameterException("command invalid here"); + throw new InvalidParameterException("command invalid here: " + + command); } } @@ -374,30 +506,33 @@ 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())) { + BlockAppendable app = s.createBlockAppendable(); + Vcard21Parser.write(app, data); + // note: we do NOT close 'app', since it would send an EOB + } + } + s.sendBlock(); break; } case POST_DATA: { String cstate = cmd.getParam(); Data data = null; for (Data d : contact) { - if (cstate.equals(d.getContentState())) + if (cstate.equals(d.getContentState(true))) data = d; } @@ -413,7 +548,7 @@ public class Server implements Runnable { String cstate = cmd.getParam(); Data data = null; for (Data d : contact) { - if (cstate.equals(d.getContentState())) + if (cstate.equals(d.getContentState(true))) data = d; } @@ -426,12 +561,34 @@ 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(true)); + } + } + s.sendBlock(); + break; + } + case LIST_DATA: { + for (Data data : contact) { + if (cmd.getParam() == null + || cmd.getParam().length() == 0 + || data.getName().toLowerCase() + .contains(cmd.getParam().toLowerCase())) { + s.send(data.getContentState(true) + " " + data.getName()); + } + } + s.sendBlock(); + break; + } case PUT_CONTACT: { clientContinue = false; break; } default: { - throw new InvalidParameterException("command invalid here"); + throw new InvalidParameterException("command invalid here: " + + command); } } @@ -449,20 +606,19 @@ public class Server implements Runnable { * @throws IOException * in case of error */ - private List doGetCard(String name) throws IOException { - List lines = new LinkedList(); - + private void sendCardBlock(SimpleSocket s, String name) throws IOException { File vcf = getFile(name); + BlockAppendable app = s.createBlockAppendable(); if (vcf != null && vcf.exists()) { Card card = new Card(vcf, Format.VCard21); - // timestamp: - lines.add(StringUtils.fromTime(card.getLastModified())); - lines.addAll(Vcard21Parser.toStrings(card)); + // timestamp + data + app.append(StringUtils.fromTime(card.getLastModified()) + "\r\n"); + Vcard21Parser.write(app, card); } - return lines; + app.close(); } /**