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 }