From 776bb6d8b1a0e35492151835f7ac612ede1f8596 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20Sch=C3=A4fer?= <florian@schaeferban.de>
Date: Fri, 22 Jul 2016 15:00:41 +0200
Subject: [PATCH] Fix encoding and HTML-escaping of the error pages of the
remote control
This ensures that special characters on the error pages of the remote control are shown correctly in the users browser.
Setting the encoding to UTF-8 in the HTTP-header and in HTML ensures that browsers will recognize that the page is encoded in UTF-8 and display special characters correctly.
And the localized error messages are now escaped, so that they are displayed correctly even when they contain special characters like <, > or &.
---
.../josm/io/remotecontrol/RequestProcessor.java | 52 +++++++++-------------
.../io/remotecontrol/handler/RequestHandler.java | 2 +-
2 files changed, 23 insertions(+), 31 deletions(-)
diff --git a/src/org/openstreetmap/josm/io/remotecontrol/RequestProcessor.java b/src/org/openstreetmap/josm/io/remotecontrol/RequestProcessor.java
index 85dd37c..a6c3435 100644
a
|
b
|
|
11 | 11 | import java.io.StringWriter; |
12 | 12 | import java.io.Writer; |
13 | 13 | import java.net.Socket; |
| 14 | import java.nio.charset.Charset; |
14 | 15 | import java.nio.charset.StandardCharsets; |
15 | 16 | import java.util.Arrays; |
16 | 17 | import java.util.Date; |
… |
… |
|
44 | 45 | * Processes HTTP "remote control" requests. |
45 | 46 | */ |
46 | 47 | public class RequestProcessor extends Thread { |
| 48 | |
| 49 | private static final Charset RESPONSE_CHARSET = StandardCharsets.UTF_8; |
| 50 | private static final String BASIC_HTML_HEAD = "<!DOCTYPE html><meta charset=\"" + RESPONSE_CHARSET.name() + "\">"; |
| 51 | |
47 | 52 | /** |
48 | 53 | * RemoteControl protocol version. Change minor number for compatible |
49 | 54 | * interface extensions. Change major number in case of incompatible |
… |
… |
public void run() {
|
144 | 149 | Writer out = null; |
145 | 150 | try { |
146 | 151 | OutputStream raw = new BufferedOutputStream(request.getOutputStream()); |
147 | | out = new OutputStreamWriter(raw, StandardCharsets.UTF_8); |
| 152 | out = new OutputStreamWriter(raw, RESPONSE_CHARSET); |
148 | 153 | BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream(), "ASCII")); |
149 | 154 | |
150 | 155 | String get = in.readLine(); |
… |
… |
public void run() {
|
268 | 273 | */ |
269 | 274 | private static void sendError(Writer out) throws IOException { |
270 | 275 | sendHeader(out, "500 Internal Server Error", "text/html", true); |
271 | | out.write("<HTML>\r\n"); |
272 | | out.write("<HEAD><TITLE>Internal Error</TITLE>\r\n"); |
273 | | out.write("</HEAD>\r\n"); |
274 | | out.write("<BODY>"); |
275 | | out.write("<H1>HTTP Error 500: Internal Server Error</H1>\r\n"); |
276 | | out.write("</BODY></HTML>\r\n"); |
| 276 | out.write(BASIC_HTML_HEAD); |
| 277 | out.write("<title>Internal Error</title>"); |
| 278 | out.write("<h1>HTTP Error 500: Internal Server Error</h1>"); |
277 | 279 | out.flush(); |
278 | 280 | } |
279 | 281 | |
… |
… |
private static void sendError(Writer out) throws IOException {
|
287 | 289 | */ |
288 | 290 | private static void sendNotImplemented(Writer out) throws IOException { |
289 | 291 | sendHeader(out, "501 Not Implemented", "text/html", true); |
290 | | out.write("<HTML>\r\n"); |
291 | | out.write("<HEAD><TITLE>Not Implemented</TITLE>\r\n"); |
292 | | out.write("</HEAD>\r\n"); |
293 | | out.write("<BODY>"); |
294 | | out.write("<H1>HTTP Error 501: Not Implemented</h2>\r\n"); |
295 | | out.write("</BODY></HTML>\r\n"); |
| 292 | out.write(BASIC_HTML_HEAD); |
| 293 | out.write("<title>Not Implemented</title>"); |
| 294 | out.write("<h1>HTTP Error 501: Not Implemented</h1>"); |
296 | 295 | out.flush(); |
297 | 296 | } |
298 | 297 | |
… |
… |
private static void sendNotImplemented(Writer out) throws IOException {
|
308 | 307 | */ |
309 | 308 | private static void sendForbidden(Writer out, String help) throws IOException { |
310 | 309 | sendHeader(out, "403 Forbidden", "text/html", true); |
311 | | out.write("<HTML>\r\n"); |
312 | | out.write("<HEAD><TITLE>Forbidden</TITLE>\r\n"); |
313 | | out.write("</HEAD>\r\n"); |
314 | | out.write("<BODY>"); |
315 | | out.write("<H1>HTTP Error 403: Forbidden</h2>\r\n"); |
| 310 | out.write(BASIC_HTML_HEAD); |
| 311 | out.write("<title>Forbidden</title>"); |
| 312 | out.write("<h1>HTTP Error 403: Forbidden</h1>"); |
316 | 313 | if (help != null) { |
317 | | out.write(help); |
| 314 | out.write("<p>" + Utils.escapeReservedCharactersHTML(help) + "</p>"); |
318 | 315 | } |
319 | | out.write("</BODY></HTML>\r\n"); |
320 | 316 | out.flush(); |
321 | 317 | } |
322 | 318 | |
… |
… |
private static void sendForbidden(Writer out, String help) throws IOException {
|
332 | 328 | */ |
333 | 329 | private static void sendBadRequest(Writer out, String help) throws IOException { |
334 | 330 | sendHeader(out, "400 Bad Request", "text/html", true); |
335 | | out.write("<HTML>\r\n"); |
336 | | out.write("<HEAD><TITLE>Bad Request</TITLE>\r\n"); |
337 | | out.write("</HEAD>\r\n"); |
338 | | out.write("<BODY>"); |
339 | | out.write("<H1>HTTP Error 400: Bad Request</h2>\r\n"); |
| 331 | out.write(BASIC_HTML_HEAD); |
| 332 | out.write("<title>Bad Request</title>"); |
| 333 | out.write("<h1>HTTP Error 400: Bad Request</h1>"); |
340 | 334 | if (help != null) { |
341 | | out.write(help); |
| 335 | out.write("<p>" + Utils.escapeReservedCharactersHTML(help) + "</p>"); |
342 | 336 | } |
343 | | out.write("</BODY></HTML>\r\n"); |
344 | 337 | out.flush(); |
345 | 338 | } |
346 | 339 | |
… |
… |
private static void sendBadRequest(Writer out, String help) throws IOException {
|
361 | 354 | private static void sendHeader(Writer out, String status, String contentType, |
362 | 355 | boolean endHeaders) throws IOException { |
363 | 356 | out.write("HTTP/1.1 " + status + "\r\n"); |
364 | | Date now = new Date(); |
365 | | out.write("Date: " + now + "\r\n"); |
| 357 | out.write("Date: " + new Date() + "\r\n"); |
366 | 358 | out.write("Server: JOSM RemoteControl\r\n"); |
367 | | out.write("Content-type: " + contentType + "\r\n"); |
| 359 | out.write("Content-type: " + contentType + "; charset=" + RESPONSE_CHARSET.name().toLowerCase() + "\r\n"); |
368 | 360 | out.write("Access-Control-Allow-Origin: *\r\n"); |
369 | 361 | if (endHeaders) |
370 | 362 | out.write("\r\n"); |
diff --git a/src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java b/src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java
index 6aaa417..105599e 100644
a
|
b
|
void checkMandatoryParams() throws RequestHandlerBadRequestException {
|
246 | 246 | } |
247 | 247 | if (error) { |
248 | 248 | throw new RequestHandlerBadRequestException( |
249 | | "The following keys are mandatory, but have not been provided: " |
| 249 | tr("The following keys are mandatory, but have not been provided:") + ' ' |
250 | 250 | + Utils.join(", ", missingKeys)); |
251 | 251 | } |
252 | 252 | } |