1: using System;
2: using System.Text;
3:
4: namespace Ia.Cl.Model
5: {
6: ////////////////////////////////////////////////////////////////////////////
7:
8: /// <summary publish="true">
9: /// Punycode support class.
10: /// </summary>
11: /// <remarks>
12: /// Copyright © 2001-2015 Jasem Y. Al-Shamlan (info@ia.com.kw), Integrated Applications - Kuwait. All Rights Reserved.
13: ///
14: /// This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
15: /// the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
16: ///
17: /// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
18: /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19: ///
20: /// You should have received a copy of the GNU General Public License along with this library. If not, see http://www.gnu.org/licenses.
21: ///
22: /// Copyright notice: This notice may not be removed or altered from any source distribution.
23: /// </remarks>
24: public class Punycode
25: {
26: ////////////////////////////////////////////////////////////////////////////
27:
28: /*
29: * $Id: punycode.cs,v 1.3 2003/03/30 23:28:41 Mayuki Sawatari Exp $
30: *
31: * Punycode (RFC 3492) encoder/decoder implementation in C# with .NET
32: * RFC 3492: IDNA Punycode
33: * http://www.ietf.org/rfc/rfc3492.txt
34: *
35: * Copyright (C) 2003 Mayuki Sawatari <mayuki@misuzilla.org>, All rights reserved.
36: *
37: * Redistribution and use in source and binary forms, with or without
38: * modification, are permitted provided that the following conditions
39: * are met:
40: * 1. Redistributions of source code must retain the above copyright
41: * notice, this list of conditions and the following disclaimer.
42: * 2. Redistributions in binary form must reproduce the above copyright
43: * notice, this list of conditions and the following disclaimer in the
44: * documentation and/or other materials provided with the distribution.
45: *
46: * THIS LIBRARY IS PROVIDED BY THE MISUZILLA.ORG ``AS IS'' AND
47: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
50: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56: * SUCH DAMAGE.
57: */
58:
59: /// <summary/>
60: public const string Prefix = "xn--";
61:
62: /// <summary/>
63: public static string Decode(string s)
64: {
65: if (s.StartsWith(Punycode.Prefix))
66: return DecodeString(s.Substring(Punycode.Prefix.Length));
67: else
68: return DecodeString(s);
69: }
70:
71: /// <summary/>
72: public static string Encode(string s)
73: {
74: return EncodeString(s).Insert(0, Punycode.Prefix).ToString();
75: }
76:
77: /// <summary/>
78: public static string EncodeWithoutPrefix(string s)
79: {
80: return EncodeString(s).ToString();
81: }
82:
83: /// <summary/>
84: private sealed class PunyParams
85: {
86: public const Int32 Base = 36;
87: public const Int32 Tmin = 1;
88: public const Int32 Tmax = 26;
89: public const Int32 Skew = 38;
90: public const Int32 Damp = 700;
91: public const Int32 InitialBias = 72;
92: public const Int32 InitialN = 0x80;
93: public const Int32 Delimiter = 0x2d;
94: }
95:
96: /// <summary/>
97: private static char DecodeDigit(Int32 cp)
98: {
99: return (char)(cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 :
100: cp - 97 < 26 ? cp - 97 : PunyParams.Base);
101: }
102:
103: /// <summary/>
104: private static char EncodeDigit(Int32 d, Int32 flag)
105: {
106: return (char)(d + 22 + 75 * (d < 26 ? 1 : 0) - (flag << 5));
107: }
108:
109: /// <summary/>
110: private static StringBuilder EncodeString(string input)
111: {
112: StringBuilder output = new StringBuilder();
113: Int32 delta, bias, maxOut;
114: Int32 m, k, t, b, n, h, q, i;
115:
116:
117: maxOut = Int32.MaxValue;
118: n = PunyParams.InitialN;
119: bias = PunyParams.InitialBias;
120: delta = 0;
121:
122: for (i = 0; i < input.Length; i++)
123: {
124: if (input[i] < 0x80)
125: {
126: if (maxOut - input.Length < 2) throw new PunycodeBigOutputException("punycode_big_output");
127: output.Append(input[i]);
128: }
129: }
130:
131: h = b = (Int32)output.Length;
132:
133: if (output.Length > 0)
134: output.Append((char)PunyParams.Delimiter);
135:
136: /* Main encoding loop: */
137: while (h < input.Length)
138: {
139: m = Int32.MaxValue;
140: for (i = 0; i < input.Length; i++)
141: {
142: if (input[i] >= n && input[i] < m)
143: m = input[i];
144: }
145:
146: if (m - n > (Int32.MaxValue - delta) / (h + 1))
147: throw new PunycodeOverflowException("punycode_overflow");
148:
149: delta += (char)(m - n) * (h + 1);
150: n = m;
151:
152: for (i = 0; i < input.Length; i++)
153: {
154: /* Punycode does not need to check whether input[j] is basic: */
155: if (input[i] < n)
156: {
157: if (++delta == 0)
158: throw new PunycodeOverflowException("punycode_overflow");
159: }
160:
161: if (input[i] == n)
162: {
163: /* Represent delta as a generalized variable-length integer: */
164: for (q = delta, k = PunyParams.Base; ; k += PunyParams.Base)
165: {
166: t = (k <= bias) ?
167: PunyParams.Tmin :
168: (k >= bias + PunyParams.Tmax) ?
169: PunyParams.Tmax : k - bias;
170:
171: if (q < t)
172: break;
173:
174: output.Append(EncodeDigit(t + (q - t) % (PunyParams.Base - t), 0));
175: q = (q - t) / (PunyParams.Base - t);
176: }
177: //output[outlen++] = EncodeDigit(q, case_flags && case_flags[j]);
178: output.Append(EncodeDigit(q, 0)); // ignore case
179: bias = (char)Adapt(delta, h + 1, h == b);
180: delta = 0;
181: ++h;
182: }
183: }
184: ++delta; ++n;
185: }
186:
187: return output;
188: }
189:
190: private static Int32 Adapt(Int32 delta, Int32 numpoints, Boolean firsttime)
191: {
192: Int32 k;
193: delta = firsttime ? delta / PunyParams.Damp : delta >> 1; /* delta >> 1 --> delta / 2 */
194: delta += delta / numpoints;
195:
196: for (k = 0; delta > ((PunyParams.Base - PunyParams.Tmin) * PunyParams.Tmax) / 2; k += PunyParams.Base)
197: {
198: delta /= (PunyParams.Base - PunyParams.Tmin);
199: }
200:
201: return k + ((PunyParams.Base - PunyParams.Tmin) + 1) * delta / (delta + PunyParams.Skew);
202: }
203:
204: private static string DecodeString(string input)
205: {
206: StringBuilder output = new StringBuilder();
207: Int32 n, outlen, i, bias, b, j, inl, oldi, w, k, digit, t;
208:
209: /* Initialize the state: */
210:
211: n = PunyParams.InitialN;
212: outlen = i = 0;
213: bias = PunyParams.InitialBias;
214:
215: /* Handle the basic code points: Let b be the number of input code */
216: /* points before the last delimiter, or 0 if there is none, then */
217: /* copy the first b code points to the output. */
218:
219: for (b = j = 0; j < input.Length; ++j)
220: if (input[j] == PunyParams.Delimiter)
221: b = j;
222:
223: if (b > Int32.MaxValue)
224: throw new PunycodeBigOutputException("punycode_big_output");
225:
226: for (j = 0; j < b; ++j)
227: {
228: //if (case_flags)
229: // case_flags[outlen] = flagged(input[j]);
230: if (!(input[j] < 0x80))
231: throw new PunycodeBadInputException("punycode_bad_input");
232: outlen++;
233: output.Append(input[j]);
234: }
235:
236: /* Main decoding loop: Start just after the last delimiter if any */
237: /* basic code points were copied; start at the beginning otherwise. */
238: for (inl = b > 0 ? b + 1 : 0; inl < input.Length; ++outlen)
239: {
240:
241: for (oldi = i, w = 1, k = PunyParams.Base; ; k += PunyParams.Base)
242: {
243: if (inl >= input.Length)
244: throw new PunycodeBadInputException(string.Format("{0} >= {1}", inl, input.Length));
245:
246: digit = DecodeDigit(input[inl++]);
247:
248: if (digit >= PunyParams.Base)
249: throw new PunycodeBadInputException(string.Format("{0} >= {1}", digit, PunyParams.Base));
250: if (digit > (Int32.MaxValue - i) / w)
251: throw new PunycodeOverflowException(string.Format("{0} > ({1} - {2}) / {3}", digit, Int32.MaxValue, i, w));
252:
253: i += digit * w;
254: t = (k <= bias ? PunyParams.Tmin : (k >= bias + PunyParams.Tmax ? PunyParams.Tmax : k - bias));
255: if (digit < t) break;
256:
257: if (w > Int32.MaxValue / (PunyParams.Base - t))
258: throw new PunycodeOverflowException("punycode_overflow");
259:
260: w *= (PunyParams.Base - t);
261: }
262:
263: bias = Adapt(i - oldi, outlen + 1, oldi == 0);
264:
265: /* i was supposed to wrap around from out+1 to 0, */
266: /* incrementing n each time, so we'll fix that now: */
267:
268: if (i / (outlen + 1) > Int32.MaxValue - n)
269: throw new PunycodeOverflowException("punycode_overflow");
270:
271: n += i / (outlen + 1);
272: i %= (outlen + 1);
273:
274: /* Insert n at position i of the output: */
275: if (outlen >= Int32.MaxValue)
276: throw new PunycodeBigOutputException("punycode_big_output");
277: //if (case_flags) {
278: //memmove(case_flags + i + 1, case_flags + i, out - i);
279:
280: /* Case of last character determines uppercase flag: */
281: //case_flags[i] = flagged(input[inl - 1]);
282: //}
283: output.Insert(i, (char)n);
284: i++;
285: }
286: return output.ToString();
287: }
288: }
289:
290: /// <summary/>
291: public class PunycodeBigOutputException : ApplicationException
292: {
293: /// <summary/>
294: public PunycodeBigOutputException(string s) : base(s) { }
295: }
296:
297: /// <summary/>
298: public class PunycodeBadInputException : ApplicationException
299: {
300: /// <summary/>
301: public PunycodeBadInputException(string s) : base(s) { }
302: }
303:
304: /// <summary/>
305: public class PunycodeOverflowException : ApplicationException
306: {
307: /// <summary/>
308: public PunycodeOverflowException(string s) : base(s) { }
309: }
310:
311: ////////////////////////////////////////////////////////////////////////////
312: ////////////////////////////////////////////////////////////////////////////
313: }
314: