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

Last change on this file since 7937 was 7937, checked in by bastiK, 9 years ago

add subversion property svn:eol=native

  • Property svn:eol-style set to native
File size: 10.8 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;
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 String req = URLDecoder.decode(this.request, "UTF-8");
196 HashMap<String, String> args = new HashMap<>();
197 if (req.indexOf('?') != -1) {
198 String query = req.substring(req.indexOf('?') + 1);
199 if (query.indexOf('#') != -1) {
200 query = query.substring(0, query.indexOf('#'));
201 }
202 String[] params = query.split("&", -1);
203 for (String param : params) {
204 int eq = param.indexOf('=');
205 if (eq != -1) {
206 args.put(param.substring(0, eq), param.substring(eq + 1));
207 }
208 }
209 }
210 this.args = args;
211 } catch (UnsupportedEncodingException ex) {
212 throw new IllegalStateException(ex);
213 }
214 }
215
216 void checkMandatoryParams() throws RequestHandlerBadRequestException {
217 String[] mandatory = getMandatoryParams();
218 String[] optional = getOptionalParams();
219 List<String> missingKeys = new LinkedList<>();
220 boolean error = false;
221 if(mandatory != null) for (String key : mandatory) {
222 String value = args.get(key);
223 if ((value == null) || (value.length() == 0)) {
224 error = true;
225 Main.warn("'" + myCommand + "' remote control request must have '" + key + "' parameter");
226 missingKeys.add(key);
227 }
228 }
229 HashSet<String> knownParams = new HashSet<>();
230 if (mandatory != null) Collections.addAll(knownParams, mandatory);
231 if (optional != null) Collections.addAll(knownParams, optional);
232 for (String par: args.keySet()) {
233 if (!knownParams.contains(par)) {
234 Main.warn("Unknown remote control parameter {0}, skipping it", par);
235 }
236 }
237 if (error) {
238 throw new RequestHandlerBadRequestException(
239 "The following keys are mandatory, but have not been provided: "
240 + Utils.join(", ", missingKeys));
241 }
242 }
243
244 /**
245 * Save command associated with this handler.
246 *
247 * @param command The command.
248 */
249 public void setCommand(String command)
250 {
251 if (command.charAt(0) == '/') {
252 command = command.substring(1);
253 }
254 myCommand = command;
255 }
256
257 public String getContent() {
258 return content;
259 }
260
261 public String getContentType() {
262 return contentType;
263 }
264
265 protected boolean isLoadInNewLayer() {
266 return args.get("new_layer") != null && !args.get("new_layer").isEmpty()
267 ? Boolean.parseBoolean(args.get("new_layer"))
268 : Main.pref.getBoolean(loadInNewLayerKey, loadInNewLayerDefault);
269 }
270
271 protected final String decodeParam(String param) {
272 try {
273 return URLDecoder.decode(param, "UTF-8");
274 } catch (UnsupportedEncodingException e) {
275 throw new RuntimeException(e);
276 }
277 }
278
279 public void setSender(String sender) {
280 this.sender = sender;
281 }
282
283 public static class RequestHandlerException extends Exception {
284
285 public RequestHandlerException(String message) {
286 super(message);
287 }
288 public RequestHandlerException(String message, Throwable cause) {
289 super(message, cause);
290 }
291 public RequestHandlerException(Throwable cause) {
292 super(cause);
293 }
294 public RequestHandlerException() {
295 }
296 }
297
298 public static class RequestHandlerErrorException extends RequestHandlerException {
299 public RequestHandlerErrorException(Throwable cause) {
300 super(cause);
301 }
302 }
303
304 public static class RequestHandlerBadRequestException extends RequestHandlerException {
305
306 public RequestHandlerBadRequestException(String message) {
307 super(message);
308 }
309 public RequestHandlerBadRequestException(String message, Throwable cause) {
310 super(message, cause);
311 }
312 }
313
314 public static class RequestHandlerForbiddenException extends RequestHandlerException {
315 private static final long serialVersionUID = 2263904699747115423L;
316
317 public RequestHandlerForbiddenException(String message) {
318 super(message);
319 }
320 }
321}
Note: See TracBrowser for help on using the repository browser.