1 /**
2 * Various string utils and parsing functions
3 * 
4 * Copyright:
5 * (C) 1999-2007,2013 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.utils.parsing;
12 
13 import botan.constants;
14 
15 import botan.utils.types;
16 import botan.utils.types;
17 import botan.utils.parsing;
18 import botan.utils.exceptn;
19 import botan.utils.charset;
20 import botan.utils.get_byte;
21 import memutils.hashmap;
22 /**
23 * Parse a SCAN-style algorithm name
24 * Params:
25 *  scan_name = the name
26 * Returns: the name components
27 */
28 /*
29 * Parse a SCAN-style algorithm name
30 */
31 Vector!string parseAlgorithmName(in string scan_name)
32 {
33     import std.array : Appender;
34     if (scan_name.find('(') == -1 &&
35         scan_name.find(')') == -1) {
36         Vector!string str;
37         str ~= scan_name;
38         return str.move();
39     }
40     string name = scan_name;
41     Vector!ubyte substring;
42     Vector!string elems;
43     size_t level = 0;
44     
45     elems.pushBack(name[0 .. name.find('(')].idup);
46     name = name[name.find('(') .. $];
47     
48     foreach(size_t pos, const char c; name)
49     {
50         
51         if (c == '(')
52             ++level;
53         if (c == ')')
54         {
55             if (level == 1 && pos == (name.length - 1))
56             {
57                 if (elems.length == 1)
58                     elems.pushBack(substring[1 .. $].idup);
59                 else
60                     elems.pushBack(substring[].idup);
61                 return elems.move();
62             }
63             
64             if (level == 0 || (level == 1 && pos != (name.length - 1)))
65                 throw new InvalidAlgorithmName(scan_name);
66             --level;
67         }
68         
69         if (c == ',' && level == 1)
70         {
71             if (elems.length == 1)
72                 elems.pushBack(substring[1 .. $].idup);
73             else
74                 elems.pushBack(substring[].idup);
75             substring.clear();
76         }
77         else
78             substring ~= c;
79     }
80     
81     if (substring.length > 0)
82         throw new InvalidAlgorithmName(scan_name);
83     
84     return elems.move();
85 }
86 
87 /**
88 * Split a string
89 * Params:
90 *  str = the input string
91 *  delim = the delimitor
92 * Returns: string split by delim
93 */
94 Vector!string splitter(in string str, char delim)
95 {
96     return splitOnPred(str, (char c) { return c == delim; });
97 }
98 /**
99 * Split a string on a character predicate
100 * Params:
101 *  str = the input string
102 *  pred = the predicate that returns true to split
103 */
104 Vector!string splitOnPred(in string str,
105                          bool delegate(char) pred)
106 {
107     Vector!string elems;
108     if (str == "") return elems.move();
109     import std.array : Appender;
110     Vector!ubyte substr;
111     foreach(const char c; str)
112     {
113         if (pred(c))
114         {
115             if (substr.length > 0)
116                 elems.pushBack(substr[].idup);
117             substr.clear();
118         }
119         else
120             substr ~= c;
121     }
122     
123     if (substr.length == 0)
124         throw new InvalidArgument("Unable to split string: " ~ str);
125     elems.pushBack(substr[].idup);
126     
127     return elems.move();
128 }
129 
130 /**
131 * Erase characters from a string
132 */
133 string eraseChars(in string str, in char[] chars)
134 {
135     //logTrace("eraseChars");
136     import std.algorithm : canFind;
137     import std.array : Appender;
138     Appender!string output;
139     
140     foreach(const char c; str)
141         if (!chars.canFind(c))
142             output ~= c;
143     
144     return output.data;
145 }
146 
147 /**
148 * Replace a character in a string
149 * Params:
150 *  str = the input string
151 *  from_char = the character to replace
152 *  to_char = the character to replace it with
153 * Returns: str with all instances of from_char replaced by to_char
154 */
155 string replaceChar(in string str, in char from_char, in char to_char)
156 {   
157     char[] output = str.dup;
158     foreach (ref char c; output)
159         if (c == from_char)
160             c = to_char;
161     
162     return cast(string)output;
163 }
164 
165 /**
166 * Replace a character in a string
167 * Params:
168 *  str = the input string
169 *  chars = the characters to replace
170 *  to_char = the character to replace it with
171 * Returns: str with all instances of chars replaced by to_char
172 */
173 
174 string replaceChars(in string str,
175                     in char[] chars,
176                     in char to_char)
177 {
178     import std.algorithm : canFind;
179     char[] output = str.dup;
180     foreach (ref char c; output)
181         if (chars.canFind(c))
182             c = to_char;
183     
184     return cast(string)output;
185 }
186 
187 /**
188 * Join a string
189 * Params:
190 *  strs = strings to join
191 *  delim = the delimitor
192 * Returns: string joined by delim
193 */
194 string stringJoin(const ref Vector!string strs, char delim)
195 {
196     import std.algorithm : joiner;
197     import std.array : array;
198     return strs[].array.joiner(delim.to!string).to!string;
199 }
200 
201 /**
202 * Parse an ASN.1 OID
203 * Params:
204 *  oid = the OID in string form
205 * Returns: OID components
206 */
207 Vector!uint parseAsn1Oid(in string oid)
208 {
209     import std.array : Appender, array;
210     Vector!char substring;
211     Vector!uint oid_elems;
212 
213     foreach (char c; oid)
214     {
215         if (c == '.')
216         {
217             if (substring.length == 0)
218                 throw new InvalidOID(oid);
219             oid_elems ~= to!uint(substring[]);
220             substring.clear();
221         }
222         else {
223             substring ~= c;
224         }
225     }
226     
227     if (substring.length == 0)
228         throw new InvalidOID(oid);
229     oid_elems ~= to!uint(substring[]);    
230     substring.clear();
231 
232     if (oid_elems.length < 2)
233         throw new InvalidOID(oid);
234     return oid_elems.move();
235 }
236 
237 /**
238 * Compare two names using the X.509 comparison algorithm
239 * Params:
240 *  name1 = the first name
241 *  name2 = the second name
242 * Returns: true if name1 is the same as name2 by the X.509 comparison rules
243 */
244 bool x500NameCmp(in string name1, in string name2)
245 {
246     auto p1 = name1.ptr;
247     auto p2 = name2.ptr;
248     
249     while ((p1 != name1.ptr + name1.length) && isSpace(*p1)) ++p1;
250     while ((p2 != name2.ptr + name2.length) && isSpace(*p2)) ++p2;
251     
252     while (p1 != name1.ptr + name1.length && p2 != name2.ptr + name2.length)
253     {
254         if (isSpace(*p1))
255         {
256             if (!isSpace(*p2))
257                 return false;
258             
259             while ((p1 != name1.ptr + name1.length) && isSpace(*p1)) ++p1;
260             while ((p2 != name2.ptr + name2.length) && isSpace(*p2)) ++p2;
261             
262             if (p1 == name1.ptr + name1.length && p2 == name2.ptr + name2.length)
263                 return true;
264         }
265         
266         if (!caselessCmp(*p1, *p2))
267             return false;
268         ++p1;
269         ++p2;
270     }
271     
272     while ((p1 != name1.ptr + name1.length) && isSpace(*p1)) ++p1;
273     while ((p2 != name2.ptr + name2.length) && isSpace(*p2)) ++p2;
274     
275     if ((p1 != name1.ptr + name1.length) || (p2 != name2.ptr + name2.length))
276         return false;
277     return true;
278 }
279 
280 /**
281 * Convert a string representation of an IPv4 address to a number
282 * Params:
283 *  str = the string representation
284 * Returns: integer IPv4 address
285 */
286 uint stringToIpv4(in string str)
287 {
288     Vector!string parts = splitter(str, '.');
289     
290     if (parts.length != 4)
291         throw new DecodingError("Invalid IP string " ~ str);
292     
293     uint ip = 0;
294     
295     foreach (const string part; parts[])
296     {
297         uint octet = to!uint(part);
298         
299         if (octet > 255)
300             throw new DecodingError("Invalid IP string " ~ str);
301         
302         ip = (ip << 8) | (octet & 0xFF);
303     }
304     
305     return ip;
306 }
307 
308 /**
309 * Convert an IPv4 address to a string
310 * Params:
311 *  ip = the IPv4 address to convert
312 * Returns: string representation of the IPv4 address
313 */
314 string ipv4ToString(uint ip)
315 {
316     import std.array : Appender;
317     Appender!string str;
318     for (size_t i = 0; i != (ip).sizeof; ++i)
319     {
320         if (i)
321             str ~= ".";
322         str ~= to!string(get_byte(i, ip));
323     }
324     
325     return str.data;
326 }
327 
328 private:
329 
330 ptrdiff_t find(string str, char c) {
331     import std.algorithm : countUntil;
332     return countUntil(str, c);
333 }
334 
335 auto end(string str) {
336     return str.ptr + str.length;
337 }