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