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

Last change on this file since 8318 was 8318, checked in by Don-vip, 9 years ago

fix various Sonar issues:

  • squid:S1068: Unused private fields should be removed
  • squid:S1155: Collection.isEmpty() should be used to test for emptiness
  • squid:S1185: Overriding methods should do more than simply call the same method in the super class
  • squid:S1694: An abstract class should have both abstract and concrete methods
  • squid:S1905: Redundant casts should not be used
  • squid:S2065: Fields in non-serializable classes should not be "transient"
  • squid:S2583: Conditions should not unconditionally evaluate to "TRUE" or to "FALSE"
  • squid:ModifiersOrderCheck: Modifiers should be declared in the correct order
  • Property svn:eol-style set to native
File size: 11.7 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.net.URI;
7import java.net.URISyntaxException;
8import java.text.MessageFormat;
9import java.util.Collections;
10import java.util.HashMap;
11import java.util.HashSet;
12import java.util.LinkedList;
13import java.util.List;
14import java.util.Map;
15
16import javax.swing.JLabel;
17import javax.swing.JOptionPane;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
21import org.openstreetmap.josm.tools.Utils;
22
23/**
24 * This is the parent of all classes that handle a specific remote control command
25 *
26 * @author Bodo Meissner
27 */
28public abstract class RequestHandler {
29
30 public static final String globalConfirmationKey = "remotecontrol.always-confirm";
31 public static final boolean globalConfirmationDefault = false;
32 public static final String loadInNewLayerKey = "remotecontrol.new-layer";
33 public static final boolean loadInNewLayerDefault = false;
34
35 /** The GET request arguments */
36 protected Map<String,String> args;
37
38 /** The request URL without "GET". */
39 protected String request;
40
41 /** default response */
42 protected String content = "OK\r\n";
43 /** default content type */
44 protected String contentType = "text/plain";
45
46 /** will be filled with the command assigned to the subclass */
47 protected String myCommand;
48
49 /**
50 * who sent the request?
51 * the host from referer header or IP of request sender
52 */
53 protected String sender;
54
55 /**
56 * Check permission and parameters and handle request.
57 *
58 * @throws RequestHandlerForbiddenException
59 * @throws RequestHandlerBadRequestException
60 * @throws RequestHandlerErrorException
61 */
62 public final void handle() throws RequestHandlerForbiddenException, RequestHandlerBadRequestException, RequestHandlerErrorException {
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 public abstract 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 public abstract PermissionPrefWithDefault getPermissionPref();
108
109 public abstract String[] getMandatoryParams();
110
111 public String[] getOptionalParams() {
112 return null;
113 }
114
115 public String getUsage() {
116 return null;
117 }
118
119 public String[] getUsageExamples() {
120 return null;
121 }
122
123 /**
124 * Returns usage examples for the given command. To be overriden only my handlers that define several commands.
125 * @param cmd The command asked
126 * @return Usage examples for the given command
127 * @since 6332
128 */
129 public String[] getUsageExamples(String cmd) {
130 return getUsageExamples();
131 }
132
133 /**
134 * Check permissions in preferences and display error message
135 * or ask for permission.
136 *
137 * @throws RequestHandlerForbiddenException
138 */
139 public final void checkPermission() throws RequestHandlerForbiddenException {
140 /*
141 * If the subclass defines a specific preference and if this is set
142 * to false, abort with an error message.
143 *
144 * Note: we use the deprecated class here for compatibility with
145 * older versions of WMSPlugin.
146 */
147 PermissionPrefWithDefault permissionPref = getPermissionPref();
148 if (permissionPref != null && permissionPref.pref != null) {
149 if (!Main.pref.getBoolean(permissionPref.pref, permissionPref.defaultVal)) {
150 String err = MessageFormat.format("RemoteControl: ''{0}'' forbidden by preferences", myCommand);
151 Main.info(err);
152 throw new RequestHandlerForbiddenException(err);
153 }
154 }
155
156 /* Does the user want to confirm everything?
157 * If yes, display specific confirmation message.
158 */
159 if (Main.pref.getBoolean(globalConfirmationKey, globalConfirmationDefault)) {
160 // Ensure dialog box does not exceed main window size
161 Integer maxWidth = (int) Math.max(200, Main.parent.getWidth()*0.6);
162 String message = "<html><div>" + getPermissionMessage() +
163 "<br/>" + tr("Do you want to allow this?") + "</div></html>";
164 JLabel label = new JLabel(message);
165 if (label.getPreferredSize().width > maxWidth) {
166 label.setText(message.replaceFirst("<div>", "<div style=\"width:" + maxWidth + "px;\">"));
167 }
168 if (JOptionPane.showConfirmDialog(Main.parent, label,
169 tr("Confirm Remote Control action"),
170 JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
171 String err = MessageFormat.format("RemoteControl: ''{0}'' forbidden by user''s choice", myCommand);
172 throw new RequestHandlerForbiddenException(err);
173 }
174 }
175 }
176
177 /**
178 * Set request URL and parse args.
179 *
180 * @param url The request URL.
181 */
182 public void setUrl(String url) {
183 this.request = url;
184 parseArgs();
185 }
186
187 /**
188 * Parse the request parameters as key=value pairs.
189 * The result will be stored in {@code this.args}.
190 *
191 * Can be overridden by subclass.
192 */
193 protected void parseArgs() {
194 try {
195 this.args = getRequestParameter(new URI(this.request));
196 } catch (URISyntaxException ex) {
197 throw new RuntimeException(ex);
198 }
199 }
200
201 /**
202 * @see <a href="http://blog.lunatech.com/2009/02/03/what-every-web-developer-must-know-about-url-encoding">
203 * What every web developer must know about URL encoding</a>
204 */
205 static Map<String, String> getRequestParameter(URI uri) {
206 Map<String, String> r = new HashMap<>();
207 if (uri.getRawQuery() == null) {
208 return r;
209 }
210 for (String kv : uri.getRawQuery().split("&")) {
211 final String[] kvs = Utils.decodeUrl(kv).split("=", 2);
212 r.put(kvs[0], kvs.length > 1 ? kvs[1] : null);
213 }
214 return r;
215 }
216
217 void checkMandatoryParams() throws RequestHandlerBadRequestException {
218 String[] mandatory = getMandatoryParams();
219 String[] optional = getOptionalParams();
220 List<String> missingKeys = new LinkedList<>();
221 boolean error = false;
222 if(mandatory != null) for (String key : mandatory) {
223 String value = args.get(key);
224 if ((value == null) || (value.length() == 0)) {
225 error = true;
226 Main.warn("'" + myCommand + "' remote control request must have '" + key + "' parameter");
227 missingKeys.add(key);
228 }
229 }
230 HashSet<String> knownParams = new HashSet<>();
231 if (mandatory != null) Collections.addAll(knownParams, mandatory);
232 if (optional != null) Collections.addAll(knownParams, optional);
233 for (String par: args.keySet()) {
234 if (!knownParams.contains(par)) {
235 Main.warn("Unknown remote control parameter {0}, skipping it", par);
236 }
237 }
238 if (error) {
239 throw new RequestHandlerBadRequestException(
240 "The following keys are mandatory, but have not been provided: "
241 + Utils.join(", ", missingKeys));
242 }
243 }
244
245 /**
246 * Save command associated with this handler.
247 *
248 * @param command The command.
249 */
250 public void setCommand(String command)
251 {
252 if (command.charAt(0) == '/') {
253 command = command.substring(1);
254 }
255 myCommand = command;
256 }
257
258 public String getContent() {
259 return content;
260 }
261
262 public String getContentType() {
263 return contentType;
264 }
265
266 protected boolean isLoadInNewLayer() {
267 return args.get("new_layer") != null && !args.get("new_layer").isEmpty()
268 ? Boolean.parseBoolean(args.get("new_layer"))
269 : Main.pref.getBoolean(loadInNewLayerKey, loadInNewLayerDefault);
270 }
271
272 public void setSender(String sender) {
273 this.sender = sender;
274 }
275
276 public static class RequestHandlerException extends Exception {
277
278 public RequestHandlerException(String message) {
279 super(message);
280 }
281 public RequestHandlerException(String message, Throwable cause) {
282 super(message, cause);
283 }
284 public RequestHandlerException(Throwable cause) {
285 super(cause);
286 }
287 public RequestHandlerException() {
288 }
289 }
290
291 public static class RequestHandlerErrorException extends RequestHandlerException {
292 public RequestHandlerErrorException(Throwable cause) {
293 super(cause);
294 }
295 }
296
297 public static class RequestHandlerBadRequestException extends RequestHandlerException {
298
299 public RequestHandlerBadRequestException(String message) {
300 super(message);
301 }
302 public RequestHandlerBadRequestException(String message, Throwable cause) {
303 super(message, cause);
304 }
305 }
306
307 public static class RequestHandlerForbiddenException extends RequestHandlerException {
308 private static final long serialVersionUID = 2263904699747115423L;
309
310 public RequestHandlerForbiddenException(String message) {
311 super(message);
312 }
313 }
314
315 public abstract static class RawURLParseRequestHandler extends RequestHandler {
316 @Override
317 protected void parseArgs() {
318 HashMap<String, String> args = new HashMap<>();
319 if (request.indexOf('?') != -1) {
320 String query = request.substring(request.indexOf('?') + 1);
321 if (query.indexOf("url=") == 0) {
322 args.put("url", Utils.decodeUrl(query.substring(4)));
323 } else {
324 int urlIdx = query.indexOf("&url=");
325 if (urlIdx != -1) {
326 args.put("url", Utils.decodeUrl(query.substring(urlIdx + 5)));
327 query = query.substring(0, urlIdx);
328 } else if (query.indexOf('#') != -1) {
329 query = query.substring(0, query.indexOf('#'));
330 }
331 String[] params = query.split("&", -1);
332 for (String param : params) {
333 int eq = param.indexOf('=');
334 if (eq != -1) {
335 args.put(param.substring(0, eq), Utils.decodeUrl(param.substring(eq + 1)));
336 }
337 }
338 }
339 }
340 this.args = args;
341 }
342 }
343}
Note: See TracBrowser for help on using the repository browser.