From 541dae36d4d1fd1aed3cffc048c2397e31332613 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Wed, 10 Jan 2018 17:39:58 +0000
Subject: [PATCH] Util: New util to truncate a string that won't split across a
 surrogate pair

---
 .../org/klomp/snark/web/I2PSnarkServlet.java  |  2 +-
 .../src/net/i2p/servlet/util/ServletUtil.java | 19 +++++++++++++++++++
 .../i2p/router/web/helpers/SummaryHelper.java |  3 ++-
 .../src/src/i2p/susi/webmail/Mail.java        |  5 +++--
 4 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
index bb156c7afe..ef99d12d48 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
@@ -1534,7 +1534,7 @@ public class I2PSnarkServlet extends BasicServlet {
         String basename = snark.getBaseName();
         String fullBasename = basename;
         if (basename.length() > MAX_DISPLAYED_FILENAME_LENGTH) {
-            String start = basename.substring(0, MAX_DISPLAYED_FILENAME_LENGTH);
+            String start = ServletUtil.truncate(basename, MAX_DISPLAYED_FILENAME_LENGTH);
             if (start.indexOf(' ') < 0 && start.indexOf('-') < 0) {
                 // browser has nowhere to break it
                 basename = start + HELLIP;
diff --git a/apps/jetty/java/src/net/i2p/servlet/util/ServletUtil.java b/apps/jetty/java/src/net/i2p/servlet/util/ServletUtil.java
index 82c4500d1a..243ffbed74 100644
--- a/apps/jetty/java/src/net/i2p/servlet/util/ServletUtil.java
+++ b/apps/jetty/java/src/net/i2p/servlet/util/ServletUtil.java
@@ -61,4 +61,23 @@ public class ServletUtil {
             ua.startsWith("SEC-") || ua.startsWith("SonyEricsson") ||
             ua.startsWith("Vodafone");
     }
+
+    /**
+      * Truncate a String.
+      * Same as s.substring(0, len) except that
+      * it won't split a surrogate pair.
+      *
+      * @param s non-null
+      * @return s if shorter; s.substring(0, len) if
+      *           the char at len-1 is not a high surrogate;
+      *           s.substring(0, len+1) if it is
+      * @since 0.9.33
+      */
+    public static String truncate(String s, int len) {
+        if (s.length() <= len)
+            return s;
+        if (Character.isHighSurrogate(s.charAt(len - 1)))
+            return s.substring(0, len + 1);
+        return s.substring(0, len);
+    }
 }
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/SummaryHelper.java
index 4596249aaa..a487f84ff1 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/SummaryHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/SummaryHelper.java
@@ -28,6 +28,7 @@ import net.i2p.router.transport.TransportUtil;
 import net.i2p.router.web.CSSHelper;
 import net.i2p.router.web.HelperBase;
 import net.i2p.router.web.NewsHelper;
+import net.i2p.servlet.util.ServletUtil;
 import net.i2p.stat.Rate;
 import net.i2p.stat.RateStat;
 import net.i2p.util.PortMapper;
@@ -575,7 +576,7 @@ public class SummaryHelper extends HelperBase {
                 if (name.length() <= 32)
                     buf.append(DataHelper.escapeHTML(name));
                 else
-                    buf.append(DataHelper.escapeHTML(name.substring(0,29))).append("&hellip;");
+                    buf.append(DataHelper.escapeHTML(ServletUtil.truncate(name, 29))).append("&hellip;");
                 buf.append("</a></b></td>\n");
                 LeaseSet ls = _context.netDb().lookupLeaseSetLocally(h);
                 if (ls != null && _context.tunnelManager().getOutboundClientTunnelCount(h) > 0) {
diff --git a/apps/susimail/src/src/i2p/susi/webmail/Mail.java b/apps/susimail/src/src/i2p/susi/webmail/Mail.java
index f8e8458371..ce11e2bd8b 100644
--- a/apps/susimail/src/src/i2p/susi/webmail/Mail.java
+++ b/apps/susimail/src/src/i2p/susi/webmail/Mail.java
@@ -43,6 +43,7 @@ import java.util.TimeZone;
 import java.util.regex.Pattern;
 
 import net.i2p.data.DataHelper;
+import net.i2p.servlet.util.ServletUtil;
 import net.i2p.util.SystemVersion;
 
 /**
@@ -334,7 +335,7 @@ class Mail {
 								shortSender = '<' + shortSender + '>';  // add missing <> (but thunderbird doesn't...)
 							boolean trim = shortSender.length() > 35;
 							if (trim)
-								shortSender = shortSender.substring( 0, 32 ).trim();
+								shortSender = ServletUtil.truncate(shortSender, 32).trim();
 							shortSender = html.encode( shortSender );
 							if (trim)
 								shortSender += "&hellip;";  // must be after html encode
@@ -361,7 +362,7 @@ class Mail {
 							shortSubject = formattedSubject;
 							boolean trim = formattedSubject.length() > 65;
 							if (trim)
-								shortSubject = formattedSubject.substring( 0, 62 ).trim();
+								shortSubject = ServletUtil.truncate(formattedSubject, 62).trim();
 							shortSubject = html.encode( shortSubject );
 							if (trim)
 								shortSubject += "&hellip;";  // must be after html encode
-- 
GitLab