From 3d5ad70adb8c0276e6bc8a611addb885d17f3e22 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Wed, 2 Aug 2017 10:16:26 +0300 Subject: [PATCH] Safer URI unescaping for CSVFormatter subscription CSV exporter refs #7401 Change-Id: I9d2da9b6725a457abf62020870bdf5759350a079 --- .../simantics/history/csv/CSVFormatter.java | 12 +- .../src/org/simantics/history/csv/URIs.java | 103 ++++++++++++++++++ 2 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 bundles/org.simantics.history/src/org/simantics/history/csv/URIs.java diff --git a/bundles/org.simantics.history/src/org/simantics/history/csv/CSVFormatter.java b/bundles/org.simantics.history/src/org/simantics/history/csv/CSVFormatter.java index 6610d17f2..4e405e2ac 100644 --- a/bundles/org.simantics.history/src/org/simantics/history/csv/CSVFormatter.java +++ b/bundles/org.simantics.history/src/org/simantics/history/csv/CSVFormatter.java @@ -12,9 +12,7 @@ package org.simantics.history.csv; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.math.BigDecimal; -import java.net.URLDecoder; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.Format; @@ -85,20 +83,12 @@ public class CSVFormatter { i.history = history; i.label = label!=null?label:""; i.variableReference = variableReference!=null?variableReference:""; - i.variableReference = unescape(i.variableReference); + i.variableReference = URIs.safeUnescape(i.variableReference); i.historyItemId = historyItemId; i.unit = unit; if ( !items.contains(i) ) items.add( i ); } - private static String unescape(String url) { - try { - return URLDecoder.decode(url, "UTF-8"); - } catch (UnsupportedEncodingException e) { - return url; - } - } - /** * Sort items by variableId, label1, label2 */ diff --git a/bundles/org.simantics.history/src/org/simantics/history/csv/URIs.java b/bundles/org.simantics.history/src/org/simantics/history/csv/URIs.java new file mode 100644 index 000000000..57a819a64 --- /dev/null +++ b/bundles/org.simantics.history/src/org/simantics/history/csv/URIs.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2017 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.history.csv; + +/** + * @author Tuukka Lehtonen + * @since 1.30.0, 1.28.1 + */ +class URIs { + + public static String safeUnescape(String uri) { + try { + return unescape(uri); + } catch (IllegalArgumentException e) { + return uri; + } + } + + public static String unescape(String uri) { + try { + if (!needsUnescaping(uri)) + return uri; + + int len = uri.length(); + String unicode = uri; + char result[] = new char[len]; + int in = 0; + int out = 0; + while (in < len) { + char inCh = unicode.charAt(in++); + if (inCh == '%' && in+1 < len) { + char d1 = unicode.charAt(in); + char d2 = unicode.charAt(in+1); + if (d1 > 127 || d2 > 127) + throw new IllegalArgumentException("Invalid hex digit escape sequence in " + uri + " at " + in); + result[out++] = (char) (hexDecode(d1) * 16 | hexDecode(d2)); + in += 2; + } else { + result[out++] = inCh; + } + } + return new String(result, 0, out); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Problem while unescaping string: " + uri, e); + } catch (IndexOutOfBoundsException ee) { + throw new IllegalArgumentException("Incomplete hex digit escape sequence in " + uri); + } + } + + private static boolean needsUnescaping(String s) { + int l = s.length(); + for (int i = -1; i < l;) { + i = s.indexOf('%', i+1); + if (i < 0) + break; + if (i+2 < l + && isHexDigit(s.charAt(i+1)) + && isHexDigit(s.charAt(i+2))) + return true; + } + return false; + } + + private static int hexDecode(char c) { + switch (c) { + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + return ((c) & 255) - 'a' + 10; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + return c - 'A' + 10; + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + return c - '0'; + default: + throw new IllegalArgumentException("Bad Hex escape character: " + ((c)&255) ); + } + } + + private static boolean isHexDigit(char c) { + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); + } + +// public static void main(String[] args) { +// System.out.println(unescape("%")); +// System.out.println(unescape("%.AI")); +// System.out.println(unescape("%6B")); +// System.out.println(unescape("%6g")); +// System.out.println(unescape("%g5")); +// System.out.println(unescape("%f5")); +// System.out.println(unescape("%A1")); +// System.out.println(unescape("%A")); +// System.out.println(unescape("%A.")); +// System.out.println(unescape("%AI")); +// } + +} -- 2.43.2