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

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

see #15182 - deprecate all Main logging methods and introduce suitable replacements in Logging for most of them

  • Property svn:eol-style set to native
File size: 14.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.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;
15import java.util.Set;
16
17import javax.swing.JLabel;
18import javax.swing.JOptionPane;
19
20import org.openstreetmap.josm.Main;
21import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
22import org.openstreetmap.josm.tools.Logging;
23import org.openstreetmap.josm.tools.Utils;
24
25/**
26 * This is the parent of all classes that handle a specific remote control command
27 *
28 * @author Bodo Meissner
29 */
30public abstract class RequestHandler {
31
32 public static final String globalConfirmationKey = "remotecontrol.always-confirm";
33 public static final boolean globalConfirmationDefault = false;
34 public static final String loadInNewLayerKey = "remotecontrol.new-layer";
35 public static final boolean loadInNewLayerDefault = false;
36
37 /** The GET request arguments */
38 protected Map<String, String> args;
39
40 /** The request URL without "GET". */
41 protected String request;
42
43 /** default response */
44 protected String content = "OK\r\n";
45 /** default content type */
46 protected String contentType = "text/plain";
47
48 /** will be filled with the command assigned to the subclass */
49 protected String myCommand;
50
51 /**
52 * who sent the request?
53 * the host from referer header or IP of request sender
54 */
55 protected String sender;
56
57 /**
58 * Check permission and parameters and handle request.
59 *
60 * @throws RequestHandlerForbiddenException if request is forbidden by preferences
61 * @throws RequestHandlerBadRequestException if request is invalid
62 * @throws RequestHandlerErrorException if an error occurs while processing request
63 */
64 public final void handle() throws RequestHandlerForbiddenException, RequestHandlerBadRequestException, RequestHandlerErrorException {
65 checkMandatoryParams();
66 validateRequest();
67 checkPermission();
68 handleRequest();
69 }
70
71 /**
72 * Validates the request before attempting to perform it.
73 * @throws RequestHandlerBadRequestException if request is invalid
74 * @since 5678
75 */
76 protected abstract void validateRequest() throws RequestHandlerBadRequestException;
77
78 /**
79 * Handle a specific command sent as remote control.
80 *
81 * This method of the subclass will do the real work.
82 *
83 * @throws RequestHandlerErrorException if an error occurs while processing request
84 * @throws RequestHandlerBadRequestException if request is invalid
85 */
86 protected abstract void handleRequest() throws RequestHandlerErrorException, RequestHandlerBadRequestException;
87
88 /**
89 * Get a specific message to ask the user for permission for the operation
90 * requested via remote control.
91 *
92 * This message will be displayed to the user if the preference
93 * remotecontrol.always-confirm is true.
94 *
95 * @return the message
96 */
97 public abstract String getPermissionMessage();
98
99 /**
100 * Get a PermissionPref object containing the name of a special permission
101 * preference to individually allow the requested operation and an error
102 * message to be displayed when a disabled operation is requested.
103 *
104 * Default is not to check any special preference. Override this in a
105 * subclass to define permission preference and error message.
106 *
107 * @return the preference name and error message or null
108 */
109 public abstract PermissionPrefWithDefault getPermissionPref();
110
111 public abstract String[] getMandatoryParams();
112
113 public String[] getOptionalParams() {
114 return new String[0];
115 }
116
117 public String getUsage() {
118 return null;
119 }
120
121 public String[] getUsageExamples() {
122 return new String[0];
123 }
124
125 /**
126 * Returns usage examples for the given command. To be overriden only my handlers that define several commands.
127 * @param cmd The command asked
128 * @return Usage examples for the given command
129 * @since 6332
130 */
131 public String[] getUsageExamples(String cmd) {
132 return getUsageExamples();
133 }
134
135 /**
136 * Check permissions in preferences and display error message or ask for permission.
137 *
138 * @throws RequestHandlerForbiddenException if request is forbidden by preferences
139 */
140 public final void checkPermission() throws RequestHandlerForbiddenException {
141 /*
142 * If the subclass defines a specific preference and if this is set
143 * to false, abort with an error message.
144 *
145 * Note: we use the deprecated class here for compatibility with
146 * older versions of WMSPlugin.
147 */
148 PermissionPrefWithDefault permissionPref = getPermissionPref();
149 if (permissionPref != null && permissionPref.pref != null && !Main.pref.getBoolean(permissionPref.pref, permissionPref.defaultVal)) {
150 String err = MessageFormat.format("RemoteControl: ''{0}'' forbidden by preferences", myCommand);
151 Logging.info(err);
152 throw new RequestHandlerForbiddenException(err);
153 }
154
155 /* Does the user want to confirm everything?
156 * If yes, display specific confirmation message.
157 */
158 if (Main.pref.getBoolean(globalConfirmationKey, globalConfirmationDefault)) {
159 // Ensure dialog box does not exceed main window size
160 Integer maxWidth = (int) Math.max(200, Main.parent.getWidth()*0.6);
161 String message = "<html><div>" + getPermissionMessage() +
162 "<br/>" + tr("Do you want to allow this?") + "</div></html>";
163 JLabel label = new JLabel(message);
164 if (label.getPreferredSize().width > maxWidth) {
165 label.setText(message.replaceFirst("<div>", "<div style=\"width:" + maxWidth + "px;\">"));
166 }
167 if (JOptionPane.showConfirmDialog(Main.parent, label,
168 tr("Confirm Remote Control action"),
169 JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
170 String err = MessageFormat.format("RemoteControl: ''{0}'' forbidden by user''s choice", myCommand);
171 throw new RequestHandlerForbiddenException(err);
172 }
173 }
174 }
175
176 /**
177 * Set request URL and parse args.
178 *
179 * @param url The request URL.
180 * @throws RequestHandlerBadRequestException if request URL is invalid
181 */
182 public void setUrl(String url) throws RequestHandlerBadRequestException {
183 this.request = url;
184 try {
185 parseArgs();
186 } catch (URISyntaxException e) {
187 throw new RequestHandlerBadRequestException(e);
188 }
189 }
190
191 /**
192 * Parse the request parameters as key=value pairs.
193 * The result will be stored in {@code this.args}.
194 *
195 * Can be overridden by subclass.
196 * @throws URISyntaxException if request URL is invalid
197 */
198 protected void parseArgs() throws URISyntaxException {
199 this.args = getRequestParameter(new URI(this.request));
200 }
201
202 /**
203 * Returns the request parameters.
204 * @param uri URI as string
205 * @return map of request parameters
206 * @see <a href="http://blog.lunatech.com/2009/02/03/what-every-web-developer-must-know-about-url-encoding">
207 * What every web developer must know about URL encoding</a>
208 */
209 static Map<String, String> getRequestParameter(URI uri) {
210 Map<String, String> r = new HashMap<>();
211 if (uri.getRawQuery() == null) {
212 return r;
213 }
214 for (String kv : uri.getRawQuery().split("&")) {
215 final String[] kvs = Utils.decodeUrl(kv).split("=", 2);
216 r.put(kvs[0], kvs.length > 1 ? kvs[1] : null);
217 }
218 return r;
219 }
220
221 void checkMandatoryParams() throws RequestHandlerBadRequestException {
222 String[] mandatory = getMandatoryParams();
223 String[] optional = getOptionalParams();
224 List<String> missingKeys = new LinkedList<>();
225 boolean error = false;
226 if (mandatory != null && args != null) {
227 for (String key : mandatory) {
228 String value = args.get(key);
229 if (value == null || value.isEmpty()) {
230 error = true;
231 Logging.warn('\'' + myCommand + "' remote control request must have '" + key + "' parameter");
232 missingKeys.add(key);
233 }
234 }
235 }
236 Set<String> knownParams = new HashSet<>();
237 if (mandatory != null)
238 Collections.addAll(knownParams, mandatory);
239 if (optional != null)
240 Collections.addAll(knownParams, optional);
241 if (args != null) {
242 for (String par: args.keySet()) {
243 if (!knownParams.contains(par)) {
244 Logging.warn("Unknown remote control parameter {0}, skipping it", par);
245 }
246 }
247 }
248 if (error) {
249 throw new RequestHandlerBadRequestException(
250 tr("The following keys are mandatory, but have not been provided: {0}",
251 Utils.join(", ", missingKeys)));
252 }
253 }
254
255 /**
256 * Save command associated with this handler.
257 *
258 * @param command The command.
259 */
260 public void setCommand(String command) {
261 if (command.charAt(0) == '/') {
262 command = command.substring(1);
263 }
264 myCommand = command;
265 }
266
267 public String getContent() {
268 return content;
269 }
270
271 public String getContentType() {
272 return contentType;
273 }
274
275 protected boolean isLoadInNewLayer() {
276 return args.get("new_layer") != null && !args.get("new_layer").isEmpty()
277 ? Boolean.parseBoolean(args.get("new_layer"))
278 : Main.pref.getBoolean(loadInNewLayerKey, loadInNewLayerDefault);
279 }
280
281 public void setSender(String sender) {
282 this.sender = sender;
283 }
284
285 public static class RequestHandlerException extends Exception {
286
287 /**
288 * Constructs a new {@code RequestHandlerException}.
289 * @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()} method.
290 */
291 public RequestHandlerException(String message) {
292 super(message);
293 }
294
295 /**
296 * Constructs a new {@code RequestHandlerException}.
297 * @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()} method.
298 * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method).
299 */
300 public RequestHandlerException(String message, Throwable cause) {
301 super(message, cause);
302 }
303
304 /**
305 * Constructs a new {@code RequestHandlerException}.
306 * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method).
307 */
308 public RequestHandlerException(Throwable cause) {
309 super(cause);
310 }
311 }
312
313 public static class RequestHandlerErrorException extends RequestHandlerException {
314
315 /**
316 * Constructs a new {@code RequestHandlerErrorException}.
317 * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method).
318 */
319 public RequestHandlerErrorException(Throwable cause) {
320 super(cause);
321 }
322 }
323
324 public static class RequestHandlerBadRequestException extends RequestHandlerException {
325
326 /**
327 * Constructs a new {@code RequestHandlerBadRequestException}.
328 * @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()} method.
329 */
330 public RequestHandlerBadRequestException(String message) {
331 super(message);
332 }
333
334 /**
335 * Constructs a new {@code RequestHandlerBadRequestException}.
336 * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method).
337 */
338 public RequestHandlerBadRequestException(Throwable cause) {
339 super(cause);
340 }
341
342 /**
343 * Constructs a new {@code RequestHandlerBadRequestException}.
344 * @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()} method.
345 * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method).
346 */
347 public RequestHandlerBadRequestException(String message, Throwable cause) {
348 super(message, cause);
349 }
350 }
351
352 public static class RequestHandlerForbiddenException extends RequestHandlerException {
353
354 /**
355 * Constructs a new {@code RequestHandlerForbiddenException}.
356 * @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()} method.
357 */
358 public RequestHandlerForbiddenException(String message) {
359 super(message);
360 }
361 }
362
363 public abstract static class RawURLParseRequestHandler extends RequestHandler {
364 @Override
365 protected void parseArgs() throws URISyntaxException {
366 Map<String, String> args = new HashMap<>();
367 if (request.indexOf('?') != -1) {
368 String query = request.substring(request.indexOf('?') + 1);
369 if (query.indexOf("url=") == 0) {
370 args.put("url", Utils.decodeUrl(query.substring(4)));
371 } else {
372 int urlIdx = query.indexOf("&url=");
373 if (urlIdx != -1) {
374 args.put("url", Utils.decodeUrl(query.substring(urlIdx + 5)));
375 query = query.substring(0, urlIdx);
376 } else if (query.indexOf('#') != -1) {
377 query = query.substring(0, query.indexOf('#'));
378 }
379 String[] params = query.split("&", -1);
380 for (String param : params) {
381 int eq = param.indexOf('=');
382 if (eq != -1) {
383 args.put(param.substring(0, eq), Utils.decodeUrl(param.substring(eq + 1)));
384 }
385 }
386 }
387 }
388 this.args = args;
389 }
390 }
391}
Note: See TracBrowser for help on using the repository browser.