e90e42d6ff1e76c5a1caec0d91d2e5c2a80ec808
1 package be
.nikiroo
.jvcard
.remote
;
4 import java
.io
.IOException
;
5 import java
.net
.ServerSocket
;
6 import java
.net
.Socket
;
7 import java
.net
.UnknownHostException
;
9 import java
.util
.LinkedList
;
10 import java
.util
.List
;
11 import java
.util
.ResourceBundle
;
13 import be
.nikiroo
.jvcard
.Card
;
14 import be
.nikiroo
.jvcard
.parsers
.Format
;
15 import be
.nikiroo
.jvcard
.parsers
.Parser
;
16 import be
.nikiroo
.jvcard
.remote
.Command
.Verb
;
17 import be
.nikiroo
.jvcard
.resources
.Bundles
;
18 import be
.nikiroo
.jvcard
.tui
.StringUtils
;
21 * This class implements a small server that can listen for requests to
22 * synchronise, get and put {@link Card}s.
25 * It is <b>NOT</b> secured in any way (it even is nice enough to give you a
26 * help message when you connect in raw mode via nc on how to use it), so do
27 * <b>NOT</b> enable such a server to be accessible from internet. This is not
28 * safe. Use a ssh/openssl tunnel or similar.
34 public class Server
implements Runnable
{
35 private ServerSocket ss
;
40 private Object clientsLock
= new Object();
41 private List
<SimpleSocket
> clients
= new LinkedList
<SimpleSocket
>();
43 private Object cardsLock
= new Object();
45 public static void main(String
[] args
) throws IOException
{
46 Server server
= new Server(4444);
51 * Create a new jVCard sercer on the given port.
59 public Server(int port
) throws IOException
{
61 ResourceBundle bundle
= Bundles
.getBundle("remote");
63 String dir
= bundle
.getString("SERVER_DATA_PATH");
64 dataDir
= new File(dir
);
67 if (!dataDir
.exists()) {
68 throw new IOException("Cannot open or create data store at: "
71 } catch (Exception e
) {
73 throw new IOException("Cannot open or create data store at: "
77 ss
= new ServerSocket(port
);
81 * Stop the server. It may take some time before returning, but will only
82 * return when the server is actually stopped.
87 SimpleSocket c
= new SimpleSocket(new Socket((String
) null, port
),
88 "special STOP client");
90 c
.sendCommand(Verb
.STOP
);
92 } catch (UnknownHostException e
) {
94 } catch (IOException e
) {
98 if (clients
.size() > 0) {
101 } catch (InterruptedException e
) {
104 if (clients
.size() > 0) {
105 synchronized (clientsLock
) {
106 for (SimpleSocket s
: clients
) {
108 .println("Forcefully closing client connection");
122 final Socket s
= ss
.accept();
123 // TODO: thread pool?
124 new Thread(new Runnable() {
127 accept(new SimpleSocket(s
, "[request]"));
130 } catch (IOException ioe
) {
131 ioe
.printStackTrace();
137 * Add a client to the current count.
139 * @return the client index number
141 private void addClient(SimpleSocket s
) {
142 synchronized (clientsLock
) {
148 * Remove a client from the current count.
151 * the client index number
153 private void removeClient(SimpleSocket s
) {
154 synchronized (clientsLock
) {
160 * Accept a client and process it.
163 * the client to process
165 private void accept(SimpleSocket s
) {
171 boolean clientStop
= false;
172 while (!clientStop
) {
173 Command cmd
= s
.receiveCommand();
174 Command
.Verb verb
= cmd
.getVerb();
179 System
.out
.println(s
+ " -> " + verb
);
186 s
.sendCommand(Verb
.VERSION
);
189 s
.sendLine(StringUtils
.fromTime(new Date().getTime()));
192 synchronized (cardsLock
) {
193 s
.sendBlock(doGetCard(cmd
.getParam()));
197 synchronized (cardsLock
) {
198 s
.sendLine(doPostCard(cmd
.getParam(), s
.receiveBlock()));
202 for (File file
: dataDir
.listFiles()) {
203 if (cmd
.getParam() == null
204 || cmd
.getParam().length() == 0
205 || file
.getName().contains(cmd
.getParam())) {
206 s
.send(StringUtils
.fromTime(file
.lastModified())
207 + " " + file
.getName());
214 s
.send("The following commands are available:");
215 s
.send("- TIME: get the server time");
216 s
.send("- HELP: this help screen");
217 s
.send("- LIST: list the available cards on this server");
218 s
.send("- VERSION/GET/PUT/POST/DELETE/STOP: TODO");
223 .println("Unsupported command received from a client connection, closing it: "
229 } catch (IOException e
) {
239 * Return the serialised {@link Card} (with timestamp).
242 * the resource name to load
244 * @return the serialised data
246 * @throws IOException
249 private List
<String
> doGetCard(String name
) throws IOException
{
250 List
<String
> lines
= new LinkedList
<String
>();
252 if (name
!= null && name
.length() > 0) {
253 File vcf
= new File(dataDir
.getAbsolutePath() + File
.separator
257 Card card
= new Card(vcf
, Format
.VCard21
);
260 lines
.add(StringUtils
.fromTime(card
.getLastModified()));
261 lines
.addAll(Parser
.toStrings(card
, Format
.VCard21
));
269 * Save the data to the new given resource.
272 * the resource name to save
276 * @return the date of last modification
278 * @throws IOException
281 private String
doPostCard(String name
, List
<String
> data
)
283 if (name
!= null && name
.length() > 0) {
284 File vcf
= new File(dataDir
.getAbsolutePath() + File
.separator
287 Card card
= new Card(Parser
.parse(data
, Format
.VCard21
));
288 card
.saveAs(vcf
, Format
.VCard21
);
290 return StringUtils
.fromTime(vcf
.lastModified());