1 /* -----------------------------------------------------------------------
2 * Copyright 2014 Equinox Software, Inc.
3 * Bill Erickson <berick@esilibrary.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * -----------------------------------------------------------------------
16 package org.evergreen_ils.hatch;
18 import java.io.IOException;
20 import java.io.BufferedReader;
21 import org.eclipse.jetty.websocket.api.Session;
22 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
23 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
24 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
25 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
26 import org.eclipse.jetty.websocket.api.annotations.WebSocket;
27 import javax.servlet.ServletConfig;
29 import org.eclipse.jetty.util.ajax.JSON;
30 import org.eclipse.jetty.util.log.Log;
31 import org.eclipse.jetty.util.log.Logger;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.HashMap;
38 public class HatchWebSocketHandler {
40 private Session session;
41 private static String[] trustedDomains;
42 private static boolean trustAllDomains = false;
43 private static String profileDirectory;
44 private static final Logger logger = Log.getLogger("WebSocketHandler");
46 public static void setTrustedDomains(String[] domains) {
47 trustedDomains = domains;
49 if (domains.length > 0 ) {
51 if ("*".equals(domains[0])) {
52 logger.info("All domains trusted");
53 trustAllDomains = true;
57 for(String domain : trustedDomains) {
58 logger.info("Trusted domain: " + domain);
62 logger.warn("No domains are trusted");
66 public static void setProfileDirectory(String directory) {
67 profileDirectory = directory;
72 * config is passed in from our WebSocketServlet container,
73 * hence the public+static. Possible to access directly?
75 //public static void configure(ServletConfig config) {
76 public static void configure() {
77 logger.info("WebSocketHandler.configure()");
79 // default to ~/.evergreen
80 if (profileDirectory == null) {
81 String home = System.getProperty("user.home");
82 profileDirectory = new File(home, ".evergreen").getPath();
83 if (profileDirectory == null) {
84 logger.info("Unable to set profile directory");
89 protected boolean verifyOriginDomain() {
90 logger.info("received connection from IP " +
91 session.getRemoteAddress().getAddress());
93 String origin = session.getUpgradeRequest().getHeader("Origin");
96 logger.warn("No Origin header in request; Dropping connection");
100 logger.info("connection origin is " + origin);
102 if (trustAllDomains) return true;
104 if (java.util.Arrays.asList(trustedDomains).indexOf(origin) < 0) {
105 logger.warn("Request from un-trusted domain: " + origin);
114 public void onConnect(Session session) {
115 this.session = session;
116 if (!verifyOriginDomain()) session.close();
120 public void onClose(int statusCode, String reason) {
121 logger.info("onClose() statusCode=" + statusCode + ", reason=" + reason);
125 protected void reply(Object json, Long msgid) {
126 reply(json, msgid, true);
129 protected void reply(Object json, Long msgid, boolean success) {
131 Map<String, Object> response = new HashMap<String, Object>();
132 response.put("msgid", msgid);
134 response.put("content", json);
136 response.put("error", json);
139 logger.info("replying with : " + JSON.toString(response));
142 String jsonString = JSON.toString(response);
143 if (!success) logger.warn(jsonString);
144 session.getRemote().sendString(jsonString);
145 } catch (IOException e) {
151 @SuppressWarnings("unchecked") // direct casting JSON-parsed objects
152 public void onMessage(String message) {
153 if (session == null || !session.isOpen()) return;
154 logger.info("onMessage() " + message);
156 HashMap<String,Object> params = null;
159 params = (HashMap<String,Object>) JSON.parse(message);
160 } catch (ClassCastException e) {
161 reply("Invalid WebSockets JSON message " + message,
162 new Long(-1), false);
166 Long msgid = (Long) params.get("msgid");
167 String action = (String) params.get("action");
168 String key = (String) params.get("key");
169 String value = (String) params.get("value");
170 String mime = (String) params.get("mime");
172 logger.info("Received request for action " + action);
174 // all requets require a message ID
176 reply("No msgid specified in request", msgid, false);
180 // all requests require an action
181 if (action == null || action.equals("")) {
182 reply("No action specified in request", msgid, false);
186 if (action.equals("keys")) {
187 io = new FileIO(profileDirectory);
188 String[] keys = io.keys(key); // OK for key to be null
192 reply("key lookup error", msgid, false);
197 if (action.equals("printers")) {
198 List printers = new PrintManager().getPrintersAsMaps();
199 reply(printers, msgid);
203 if (action.equals("print")) {
204 // pass ourselves off to the print handler so it can reply
205 // for us after printing has completed.
206 params.put("socket", this);
207 Hatch.enqueueMessage(params);
211 if (action.equals("print-config")) {
214 new PrintManager().configurePrinter(params),
217 } catch(IllegalArgumentException e) {
218 reply(e.toString(), msgid, false);
223 // all remaining requests require a key
224 if (key == null || key.equals("")) {
225 reply("No key specified in request", msgid, false);
229 if (action.equals("get")) {
230 String val = new FileIO(profileDirectory).get(key);
231 // set() calls store bare JSON. We must pass an
232 // Object to reply so that it may be embedded into
233 // a larger JSON response object, hence the JSON.parse().
237 reply(JSON.parse(val), msgid);
242 if (action.equals("remove")) {
243 io = new FileIO(profileDirectory);
244 if (io.delete(key)) {
245 reply("Removal of " + key + " successful", msgid);
247 reply("Removal of " + key + " failed", msgid, false);
252 // all remaining actions require value
254 reply("No value specified in request", msgid, false);
261 io = new FileIO(profileDirectory);
262 if (io.set(key, value)) {
263 reply("setting value for " + key + " succeeded", msgid);
265 reply("setting value for " + key + " succeeded", msgid, false);
270 io = new FileIO(profileDirectory);
271 if (io.append(key, value)) {
272 reply("appending value for " + key + " succeeded", msgid);
274 reply("appending value for " + key + " succeeded", msgid, false);
279 reply("No such action: " + action, msgid, false);