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 }