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

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

fix #9635 - remote control: new referrers argument to load_object handler

File size: 10.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.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 send th request?
51 * the host from refrerer 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 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 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 final public void checkPermission() throws RequestHandlerForbiddenException
140 {
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))
150 {
151 if (!Main.pref.getBoolean(permissionPref.pref, permissionPref.defaultVal)) {
152 String err = MessageFormat.format("RemoteControl: ''{0}'' forbidden by preferences", myCommand);
153 Main.info(err);
154 throw new RequestHandlerForbiddenException(err);
155 }
156 }
157
158 /* Does the user want to confirm everything?
159 * If yes, display specific confirmation message.
160 */
161 if (Main.pref.getBoolean(globalConfirmationKey, globalConfirmationDefault)) {
162 // Ensure dialog box does not exceed main window size
163 Integer maxWidth = (int) Math.max(200, Main.parent.getWidth()*0.6);
164 String message = "<html><div>" + getPermissionMessage() +
165 "<br/>" + tr("Do you want to allow this?") + "</div></html>";
166 JLabel label = new JLabel(message);
167 if (label.getPreferredSize().width > maxWidth) {
168 label.setText(message.replaceFirst("<div>", "<div style=\"width:" + maxWidth + "px;\">"));
169 }
170 if (JOptionPane.showConfirmDialog(Main.parent, label,
171 tr("Confirm Remote Control action"),
172 JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
173 String err = MessageFormat.format("RemoteControl: ''{0}'' forbidden by user''s choice", myCommand);
174 throw new RequestHandlerForbiddenException(err);
175 }
176 }
177 }
178
179 /**
180 * Set request URL and parse args.
181 *
182 * @param url The request URL.
183 */
184 public void setUrl(String url) {
185 this.request = url;
186 parseArgs();
187 }
188
189 /**
190 * Parse the request parameters as key=value pairs.
191 * The result will be stored in {@code this.args}.
192 *
193 * Can be overridden by subclass.
194 */
195 protected void parseArgs() {
196 try {
197 String req = URLDecoder.decode(this.request, "UTF-8");
198 HashMap<String, String> args = new HashMap<String, String>();
199 if (req.indexOf('?') != -1) {
200 String query = req.substring(req.indexOf('?') + 1);
201 if (query.indexOf('#') != -1) {
202 query = query.substring(0, query.indexOf('#'));
203 }
204 String[] params = query.split("&", -1);
205 for (String param : params) {
206 int eq = param.indexOf('=');
207 if (eq != -1) {
208 args.put(param.substring(0, eq), param.substring(eq + 1));
209 }
210 }
211 }
212 this.args = args;
213 } catch (UnsupportedEncodingException ex) {
214 throw new IllegalStateException(ex);
215 }
216 }
217
218 void checkMandatoryParams() throws RequestHandlerBadRequestException {
219 String[] mandatory = getMandatoryParams();
220 String[] optional = getOptionalParams();
221 List<String> missingKeys = new LinkedList<String>();
222 boolean error = false;
223 if(mandatory != null) for (String key : mandatory) {
224 String value = args.get(key);
225 if ((value == null) || (value.length() == 0)) {
226 error = true;
227 Main.warn("'" + myCommand + "' remote control request must have '" + key + "' parameter");
228 missingKeys.add(key);
229 }
230 }
231 HashSet<String> knownParams = new HashSet<String>();
232 if (mandatory != null) Collections.addAll(knownParams, mandatory);
233 if (optional != null) Collections.addAll(knownParams, optional);
234 for (String par: args.keySet()) {
235 if (!knownParams.contains(par)) {
236 Main.warn("Unknown remote control parameter {0}, skipping it", par);
237 }
238 }
239 if (error) {
240 throw new RequestHandlerBadRequestException(
241 "The following keys are mandatory, but have not been provided: "
242 + Utils.join(", ", missingKeys));
243 }
244
245 }
246
247 /**
248 * Save command associated with this handler.
249 *
250 * @param command The command.
251 */
252 public void setCommand(String command)
253 {
254 if (command.charAt(0) == '/') {
255 command = command.substring(1);
256 }
257 myCommand = command;
258 }
259
260 public String getContent() {
261 return content;
262 }
263
264 public String getContentType() {
265 return contentType;
266 }
267
268 protected boolean isLoadInNewLayer() {
269 return args.get("new_layer") != null && !args.get("new_layer").isEmpty()
270 ? Boolean.parseBoolean(args.get("new_layer"))
271 : Main.pref.getBoolean(loadInNewLayerKey, loadInNewLayerDefault);
272 }
273
274 protected final String decodeParam(String param) {
275 try {
276 return URLDecoder.decode(param, "UTF-8");
277 } catch (UnsupportedEncodingException e) {
278 throw new RuntimeException();
279 }
280 }
281
282 public void setSender(String sender) {
283 this.sender = sender;
284 }
285
286 public static class RequestHandlerException extends Exception {
287
288 public RequestHandlerException(String message) {
289 super(message);
290 }
291
292 public RequestHandlerException() {
293 }
294 }
295
296 public static class RequestHandlerErrorException extends RequestHandlerException {
297 }
298
299 public static class RequestHandlerBadRequestException extends RequestHandlerException {
300
301 public RequestHandlerBadRequestException(String message) {
302 super(message);
303 }
304 }
305
306 public static class RequestHandlerForbiddenException extends RequestHandlerException {
307 private static final long serialVersionUID = 2263904699747115423L;
308
309 public RequestHandlerForbiddenException(String message) {
310 super(message);
311 }
312 }
313}
Note: See TracBrowser for help on using the repository browser.