/// /// * /// Author: Alexander Gnauck AG-Software, mailto:gnauck@ag-software.de /// * /// This file is part of GNU Libidn. /// * /// This library is free software; you can redistribute it and/or /// modify it under the terms of the GNU Lesser General Public License /// as published by the Free Software Foundation; either version 2.1 of /// the License, or (at your option) any later version. /// * /// This library is distributed in the hope that it will be useful, but /// WITHOUT ANY WARRANTY; without even the implied warranty of /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU /// Lesser General Public License for more details. /// * /// You should have received a copy of the GNU Lesser General Public /// License along with this library; if not, write to the Free Software /// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 /// USA /// using System; using System.Text; namespace Gnu.Inet.Encoding { /// This class offers static methods for preparing internationalized /// strings. It supports the following stringprep profiles: /// /// Note that this implementation only supports 16-bit Unicode code /// points. /// public class Stringprep { /// Preps a name according to the Stringprep profile defined in /// RFC3491. Unassigned code points are not allowed. /// * /// /// the name to prep. /// /// the prepped name. /// @throws StringprepException If the name cannot be prepped with /// this profile. /// @throws NullPointerException If the name is null. /// /// public static string NamePrep(string input) { return NamePrep(input, false); } /// Preps a name according to the Stringprep profile defined in /// RFC3491. /// * /// /// the name to prep. /// /// true if the name may contain unassigned /// code points. /// /// the prepped name. /// @throws StringprepException If the name cannot be prepped with /// this profile. /// @throws NullPointerException If the name is null. /// /// public static string NamePrep(string input, bool allowUnassigned) { if (input == null) { throw new System.NullReferenceException(); } StringBuilder s = new StringBuilder(input); if (!allowUnassigned && Contains(s, RFC3454.A1)) { throw new StringprepException(StringprepException.CONTAINS_UNASSIGNED); } Filter(s, RFC3454.B1); Map(s, RFC3454.B2search, RFC3454.B2replace); s = new StringBuilder(NFKC.NormalizeNFKC(s.ToString())); // B.3 is only needed if NFKC is not used, right? // map(s, RFC3454.B3search, RFC3454.B3replace); if (Contains(s, RFC3454.C12) || Contains(s, RFC3454.C22) || Contains(s, RFC3454.C3) || Contains(s, RFC3454.C4) || Contains(s, RFC3454.C5) || Contains(s, RFC3454.C6) || Contains(s, RFC3454.C7) || Contains(s, RFC3454.C8)) { // Table C.9 only contains code points > 0xFFFF which Java // doesn't handle throw new StringprepException(StringprepException.CONTAINS_PROHIBITED); } // Bidi handling bool r = Contains(s, RFC3454.D1); bool l = Contains(s, RFC3454.D2); // RFC 3454, section 6, requirement 1: already handled above (table C.8) // RFC 3454, section 6, requirement 2 if (r && l) { throw new StringprepException(StringprepException.BIDI_BOTHRAL); } // RFC 3454, section 6, requirement 3 if (r) { if (!Contains(s[0], RFC3454.D1) || !Contains(s[s.Length - 1], RFC3454.D1)) { throw new StringprepException(StringprepException.BIDI_LTRAL); } } return s.ToString(); } /** * Characters prohibited by RFC3920 nodeprep that aren't defined as * part of the RFC3454 tables. */ private static char [] RFC3920_NODEPREP_PROHIBIT = new char [] { '\u0022', '\u0026', '\'', '\u002F', '\u003A', '\u003C', '\u003E', '\u0040' }; /// Preps a node name according to the Stringprep profile defined in /// RFC3920. Unassigned code points are not allowed. /// * /// /// the node name to prep. /// /// the prepped node name. /// @throws StringprepException If the node name cannot be prepped /// with this profile. /// @throws NullPointerException If the node name is null. /// /// public static string NodePrep(string input) { return NodePrep(input, false); } /// /// Preps a node name according to the Stringprep profile defined in RFC3920. /// /// the node name to prep. /// /// true if the node name may contain /// unassigned code points. /// /// the prepped node name. /// @throws StringprepException If the node name cannot be prepped /// with this profile. /// @throws NullPointerException If the node name is null. /// /// public static string NodePrep(string input, bool allowUnassigned) { if (input == null) { throw new System.NullReferenceException(); } StringBuilder s = new StringBuilder(input); if (!allowUnassigned && Contains(s, RFC3454.A1)) { throw new StringprepException(StringprepException.CONTAINS_UNASSIGNED); } Filter(s, RFC3454.B1); Map(s, RFC3454.B2search, RFC3454.B2replace); s = new StringBuilder(NFKC.NormalizeNFKC(s.ToString())); if (Contains(s, RFC3454.C11) || Contains(s, RFC3454.C12) || Contains(s, RFC3454.C21) || Contains(s, RFC3454.C22) || Contains(s, RFC3454.C3) || Contains(s, RFC3454.C4) || Contains(s, RFC3454.C5) || Contains(s, RFC3454.C6) || Contains(s, RFC3454.C7) || Contains(s, RFC3454.C8) || Contains(s, RFC3920_NODEPREP_PROHIBIT)) { // Table C.9 only contains code points > 0xFFFF which Java // doesn't handle throw new StringprepException(StringprepException.CONTAINS_PROHIBITED); } // Bidi handling bool r = Contains(s, RFC3454.D1); bool l = Contains(s, RFC3454.D2); // RFC 3454, section 6, requirement 1: already handled above (table C.8) // RFC 3454, section 6, requirement 2 if (r && l) { throw new StringprepException(StringprepException.BIDI_BOTHRAL); } // RFC 3454, section 6, requirement 3 if (r) { if (!Contains(s[0], RFC3454.D1) || !Contains(s[s.Length - 1], RFC3454.D1)) { throw new StringprepException(StringprepException.BIDI_LTRAL); } } return s.ToString(); } /// /// Preps a resource name according to the Stringprep profile defined /// in RFC3920. Unassigned code points are not allowed. /// /// the resource name to prep. /// /// the prepped node name. /// @throws StringprepException If the resource name cannot be prepped /// with this profile. /// @throws NullPointerException If the resource name is null. /// /// public static string ResourcePrep(string input) { return ResourcePrep(input, false); } /// /// Preps a resource name according to the Stringprep profile defined /// in RFC3920. /// /// the resource name to prep. /// /// true if the resource name may contain /// unassigned code points. /// /// /// the prepped node name. /// @throws StringprepException If the resource name cannot be prepped /// with this profile. /// @throws NullPointerException If the resource name is null. /// /// public static string ResourcePrep(string input, bool allowUnassigned) { if (input == null) { throw new System.NullReferenceException(); } StringBuilder s = new StringBuilder(input); if (!allowUnassigned && Contains(s, RFC3454.A1)) { throw new StringprepException(StringprepException.CONTAINS_UNASSIGNED); } Filter(s, RFC3454.B1); s = new StringBuilder(NFKC.NormalizeNFKC(s.ToString())); if (Contains(s, RFC3454.C12) || Contains(s, RFC3454.C21) || Contains(s, RFC3454.C22) || Contains(s, RFC3454.C3) || Contains(s, RFC3454.C4) || Contains(s, RFC3454.C5) || Contains(s, RFC3454.C6) || Contains(s, RFC3454.C7) || Contains(s, RFC3454.C8)) { // Table C.9 only contains code points > 0xFFFF which Java // doesn't handle throw new StringprepException(StringprepException.CONTAINS_PROHIBITED); } // Bidi handling bool r = Contains(s, RFC3454.D1); bool l = Contains(s, RFC3454.D2); // RFC 3454, section 6, requirement 1: already handled above (table C.8) // RFC 3454, section 6, requirement 2 if (r && l) { throw new StringprepException(StringprepException.BIDI_BOTHRAL); } // RFC 3454, section 6, requirement 3 if (r) { if (!Contains(s[0], RFC3454.D1) || !Contains(s[s.Length - 1], RFC3454.D1)) { throw new StringprepException(StringprepException.BIDI_LTRAL); } } return s.ToString(); } internal static bool Contains(StringBuilder s, char[] p) { for (int i = 0; i < p.Length; i++) { char c = p[i]; for (int j = 0; j < s.Length; j++) { if (c == s[j]) { return true; } } } return false; } internal static bool Contains(StringBuilder s, char[][] p) { for (int i = 0; i < p.Length; i++) { char[] r = p[i]; if (1 == r.Length) { char c = r[0]; for (int j = 0; j < s.Length; j++) { if (c == s[j]) { return true; } } } else if (2 == r.Length) { char f = r[0]; char t = r[1]; for (int j = 0; j < s.Length; j++) { if (f <= s[j] && t >= s[j]) { return true; } } } } return false; } internal static bool Contains(char c, char[][] p) { for (int i = 0; i < p.Length; i++) { char[] r = p[i]; if (1 == r.Length) { if (c == r[0]) { return true; } } else if (2 == r.Length) { char f = r[0]; char t = r[1]; if (f <= c && t >= c) { return true; } } } return false; } internal static void Filter(StringBuilder s, char[] f) { for (int i = 0; i < f.Length; i++) { char c = f[i]; int j = 0; while (j < s.Length) { if (c == s[j]) { //s.deleteCharAt(j); s.Remove(j, 1); } else { j++; } } } } internal static void Filter(StringBuilder s, char[][] f) { for (int i = 0; i < f.Length; i++) { char[] r = f[i]; if (1 == r.Length) { char c = r[0]; int j = 0; while (j < s.Length) { if (c == s[j]) { //s.deleteCharAt(j); s.Remove(j, 1); } else { j++; } } } else if (2 == r.Length) { char from = r[0]; char to = r[1]; int j = 0; while (j < s.Length) { if (from <= s[j] && to >= s[j]) { //s.deleteCharAt(j); s.Remove(j, 1); } else { j++; } } } } } internal static void Map(StringBuilder s, char[] search, string[] replace) { for (int i = 0; i < search.Length; i++) { char c = search[i]; int j = 0; while (j < s.Length) { if (c == s[j]) { //s.deleteCharAt(j); s.Remove(j, 1); if (null != replace[i]) { s.Insert(j, replace[i]); j += replace[i].Length - 1; } } else { j++; } } } } } }