source: josm/trunk/src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java@ 6091

Last change on this file since 6091 was 6091, checked in by akks, 11 years ago

see #8612, #8228: remote control - add examples [patch by simon04, parts], add /feature request to detect commands

File size: 10.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io.remotecontrol.handler;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.UnsupportedEncodingException;
7import java.net.URLDecoder;
8import java.text.MessageFormat;
9import java.util.Collections;
10import java.util.HashMap;
11import java.util.HashSet;
12import java.util.LinkedList;
13import java.util.List;
14
15import javax.swing.JLabel;
16import javax.swing.JOptionPane;
17
18import org.openstreetmap.josm.Main;
19import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
20import org.openstreetmap.josm.tools.Utils;
21
22/**
23 * This is the parent of all classes that handle a specific remote control command
24 *
25 * @author Bodo Meissner
26 */
27public abstract class RequestHandler {
28
29 public static final String globalConfirmationKey = "remotecontrol.always-confirm";
30 public static final boolean globalConfirmationDefault = false;
31 public static final String loadInNewLayerKey = "remotecontrol.new-layer";
32 public static final boolean loadInNewLayerDefault = false;
33
34 /** The GET request arguments */
35 protected HashMap<String,String> args;
36
37 /** The request URL without "GET". */
38 protected String request;
39
40 /** default response */
41 protected String content = "OK\r\n";
42 /** default content type */
43 protected String contentType = "text/plain";
44
45 /** will be filled with the command assigned to the subclass */
46 protected String myCommand;
47
48 /**
49 * who send th request?
50 * the host from refrerer header or IP of request sender
51 */
52 protected String sender;
53
54 /**
55 * Check permission and parameters and handle request.
56 *
57 * @throws RequestHandlerForbiddenException
58 * @throws RequestHandlerBadRequestException
59 * @throws RequestHandlerErrorException
60 */
61 public final void handle() throws RequestHandlerForbiddenException, RequestHandlerBadRequestException, RequestHandlerErrorException
62 {
63 checkMandatoryParams();
64 validateRequest();
65 checkPermission();
66 handleRequest();
67 }
68
69 /**
70 * Validates the request before attempting to perform it.
71 * @throws RequestHandlerBadRequestException
72 * @since 5678
73 */
74 protected abstract void validateRequest() throws RequestHandlerBadRequestException;
75
76 /**
77 * Handle a specific command sent as remote control.
78 *
79 * This method of the subclass will do the real work.
80 *
81 * @throws RequestHandlerErrorException
82 * @throws RequestHandlerBadRequestException
83 */
84 protected abstract void handleRequest() throws RequestHandlerErrorException, RequestHandlerBadRequestException;
85
86 /**
87 * Get a specific message to ask the user for permission for the operation
88 * requested via remote control.
89 *
90 * This message will be displayed to the user if the preference
91 * remotecontrol.always-confirm is true.
92 *
93 * @return the message
94 */
95 abstract public String getPermissionMessage();
96
97 /**
98 * Get a PermissionPref object containing the name of a special permission
99 * preference to individually allow the requested operation and an error
100 * message to be displayed when a disabled operation is requested.
101 *
102 * Default is not to check any special preference. Override this in a
103 * subclass to define permission preference and error message.
104 *
105 * @return the preference name and error message or null
106 */
107 abstract public PermissionPrefWithDefault getPermissionPref();
108
109 abstract public String[] getMandatoryParams();
110
111 public String[] getOptionalParams() {
112 return null;
113 }
114
115 public String[] getUsageExamples() {
116 return null;
117 }
118
119 /**
120 * Check permissions in preferences and display error message
121 * or ask for permission.
122 *
123 * @throws RequestHandlerForbiddenException
124 */
125 final public void checkPermission() throws RequestHandlerForbiddenException
126 {
127 /*
128 * If the subclass defines a specific preference and if this is set
129 * to false, abort with an error message.
130 *
131 * Note: we use the deprecated class here for compatibility with
132 * older versions of WMSPlugin.
133 */
134 PermissionPrefWithDefault permissionPref = getPermissionPref();
135 if((permissionPref != null) && (permissionPref.pref != null))
136 {
137 if (!Main.pref.getBoolean(permissionPref.pref, permissionPref.defaultVal)) {
138 String err = MessageFormat.format("RemoteControl: ''{0}'' forbidden by preferences", myCommand);
139 System.out.println(err);
140 throw new RequestHandlerForbiddenException(err);
141 }
142 }
143
144 /* Does the user want to confirm everything?
145 * If yes, display specific confirmation message.
146 */
147 if (Main.pref.getBoolean(globalConfirmationKey, globalConfirmationDefault)) {
148 // Ensure dialog box does not exceed main window size
149 Integer maxWidth = (int) Math.max(200, Main.parent.getWidth()*0.6);
150 String message = "<html><div>" + getPermissionMessage() +
151 "<br/>" + tr("Do you want to allow this?") + "</div></html>";
152 JLabel label = new JLabel(message);
153 if (label.getPreferredSize().width > maxWidth) {
154 label.setText(message.replaceFirst("<div>", "<div style=\"width:" + maxWidth + "px;\">"));
155 }
156 if (JOptionPane.showConfirmDialog(Main.parent, label,
157 tr("Confirm Remote Control action"),
158 JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
159 String err = MessageFormat.format("RemoteControl: ''{0}'' forbidden by user''s choice", myCommand);
160 throw new RequestHandlerForbiddenException(err);
161 }
162 }
163 }
164
165 /**
166 * Set request URL and parse args.
167 *
168 * @param url The request URL.
169 */
170 public void setUrl(String url) {
171 this.request = url;
172 parseArgs();
173 }
174
175 /**
176 * Parse the request parameters as key=value pairs.
177 * The result will be stored in {@code this.args}.
178 *
179 * Can be overridden by subclass.
180 */
181 protected void parseArgs() {
182 try {
183 String req = URLDecoder.decode(this.request, "UTF-8");
184 HashMap<String, String> args = new HashMap<String, String>();
185 if (req.indexOf('?') != -1) {
186 String query = req.substring(req.indexOf('?') + 1);
187 if (query.indexOf('#') != -1) {
188 query = query.substring(0, query.indexOf('#'));
189 }
190 String[] params = query.split("&", -1);
191 for (String param : params) {
192 int eq = param.indexOf('=');
193 if (eq != -1) {
194 args.put(param.substring(0, eq), param.substring(eq + 1));
195 }
196 }
197 }
198 this.args = args;
199 } catch (UnsupportedEncodingException ex) {
200 throw new IllegalStateException(ex);
201 }
202 }
203
204 void checkMandatoryParams() throws RequestHandlerBadRequestException {
205 String[] mandatory = getMandatoryParams();
206 String[] optional = getOptionalParams();
207 List<String> missingKeys = new LinkedList<String>();
208 boolean error = false;
209 if(mandatory != null) for (String key : mandatory) {
210 String value = args.get(key);
211 if ((value == null) || (value.length() == 0)) {
212 error = true;
213 System.out.println("'" + myCommand + "' remote control request must have '" + key + "' parameter");
214 missingKeys.add(key);
215 }
216 }
217 HashSet<String> knownParams = new HashSet<String>();
218 if (mandatory != null) Collections.addAll(knownParams, mandatory);
219 if (optional != null) Collections.addAll(knownParams, optional);
220 for (String par: args.keySet()) {
221 if (!knownParams.contains(par)) {
222 Main.warn("Unknown remote control parameter {0}, skipping it", par);
223 }
224 }
225 if (error) {
226 throw new RequestHandlerBadRequestException(
227 "The following keys are mandatory, but have not been provided: "
228 + Utils.join(", ", missingKeys));
229 }
230
231 }
232
233 /**
234 * Save command associated with this handler.
235 *
236 * @param command The command.
237 */
238 public void setCommand(String command)
239 {
240 if (command.charAt(0) == '/') {
241 command = command.substring(1);
242 }
243 myCommand = command;
244 }
245
246 public String getContent() {
247 return content;
248 }
249
250 public String getContentType() {
251 return contentType;
252 }
253
254 protected boolean isLoadInNewLayer() {
255 return args.get("new_layer") != null && !args.get("new_layer").isEmpty()
256 ? Boolean.parseBoolean(args.get("new_layer"))
257 : Main.pref.getBoolean(loadInNewLayerKey, loadInNewLayerDefault);
258 }
259
260 protected final String decodeParam(String param) {
261 try {
262 return URLDecoder.decode(param, "UTF-8");
263 } catch (UnsupportedEncodingException e) {
264 throw new RuntimeException();
265 }
266 }
267
268 public void setSender(String sender) {
269 this.sender = sender;
270 }
271
272 public static class RequestHandlerException extends Exception {
273
274 public RequestHandlerException(String message) {
275 super(message);
276 }
277
278 public RequestHandlerException() {
279 }
280 }
281
282 public static class RequestHandlerErrorException extends RequestHandlerException {
283 }
284
285 public static class RequestHandlerBadRequestException extends RequestHandlerException {
286
287 public RequestHandlerBadRequestException(String message) {
288 super(message);
289 }
290 }
291
292 public static class RequestHandlerForbiddenException extends RequestHandlerException {
293 private static final long serialVersionUID = 2263904699747115423L;
294
295 public RequestHandlerForbiddenException(String message) {
296 super(message);
297 }
298 }
299}
Note: See TracBrowser for help on using the repository browser.