ICE 3.4.2
[php5-ice-freebsdport.git] / cpp / src / IceSSL / RFC2253.cpp
blobf8db5c596f146915d7809a0b4ac53292d8c11a0c
1 // **********************************************************************
2 //
3 // Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
4 //
5 // This copy of Ice is licensed to you under the terms described in the
6 // ICE_LICENSE file included in this distribution.
7 //
8 // **********************************************************************
10 #include <IceUtil/StringUtil.h>
12 #include <IceSSL/Plugin.h>
13 #include <IceSSL/RFC2253.h>
15 using namespace std;
16 using namespace IceSSL;
18 namespace
22 // See RFC 2253 and RFC 1779.
25 string special = ",=+<>#;";
26 string hexvalid = "0123456789abcdefABCDEF";
30 static char unescapeHex(const string&, size_t);
31 static pair<string,string> parseNameComponent(const string&, size_t&);
32 static pair<string,string> parseAttributeTypeAndValue(const string&, size_t&);
33 static string parseAttributeType(const string&, size_t&);
34 static string parseAttributeValue(const string&, size_t&);
35 static string parsePair(const string&, size_t&);
36 static string parseHexPair(const string&, size_t&, bool);
37 static void eatWhite(const string&, size_t&);
39 IceSSL::RFC2253::RDNEntrySeq
40 IceSSL::RFC2253::parse(const string& data)
42 RDNEntrySeq results;
43 RDNEntry current;
44 current.negate = false;
45 size_t pos = 0;
46 while(pos < data.size())
48 eatWhite(data, pos);
49 if(pos < data.size() && data[pos] == '!')
51 if(!current.rdn.empty())
53 throw ParseException(__FILE__, __LINE__, "negation symbol '!' must appear at start of list");
55 ++pos;
56 current.negate = true;
58 current.rdn.push_back(parseNameComponent(data, pos));
59 eatWhite(data, pos);
60 if(pos < data.size() && data[pos] == ',')
62 ++pos;
64 else if(pos < data.size() && data[pos] == ';')
66 ++pos;
67 results.push_back(current);
68 current.rdn.clear();
69 current.negate = false;
71 else if(pos < data.size())
73 throw ParseException(__FILE__, __LINE__, "expected ',' or ';' at `" + data.substr(pos) + "'");
76 if(!current.rdn.empty())
78 results.push_back(current);
81 return results;
84 IceSSL::RFC2253::RDNSeq
85 IceSSL::RFC2253::parseStrict(const string& data)
87 RDNSeq results;
88 size_t pos = 0;
89 while(pos < data.size())
91 results.push_back(parseNameComponent(data, pos));
92 eatWhite(data, pos);
93 if(pos < data.size() && (data[pos] == ',' || data[pos] == ';'))
95 ++pos;
97 else if(pos < data.size())
99 throw ParseException(__FILE__, __LINE__, "expected ',' or ';' at `" + data.substr(pos) + "'");
102 return results;
105 string
106 IceSSL::RFC2253::unescape(const string& data)
108 if(data.size() == 0)
110 return data;
113 if(data[0] == '"')
115 if(data[data.size() - 1] != '"')
117 throw ParseException(__FILE__, __LINE__, "unescape: missing \"");
121 // Return the string without quotes.
123 return data.substr(1, data.size() - 2);
127 // Unescape the entire string.
129 string result;
130 if(data[0] == '#')
132 size_t pos = 1;
133 while(pos < data.size())
135 result += unescapeHex(data, pos);
136 pos += 2;
139 else
141 size_t pos = 0;
142 while(pos < data.size())
144 if(data[pos] != '\\')
146 result += data[pos];
147 ++pos;
149 else
151 ++pos;
152 if(pos >= data.size())
154 throw ParseException(__FILE__, __LINE__, "unescape: invalid escape sequence");
156 if(special.find(data[pos]) != string::npos || data[pos] != '\\' || data[pos] != '"')
158 result += data[pos];
159 ++pos;
161 else
163 result += unescapeHex(data, pos);
164 pos += 2;
170 return result;
173 static int
174 hexToInt(char v)
176 if(v >= '0' && v <= '9')
178 return v - '0';
180 if(v >= 'a' && v <= 'f')
182 return 10 + (v - 'a');
184 if(v >= 'A' && v <= 'F')
186 return 10 + (v - 'A');
188 throw ParseException(__FILE__, __LINE__, "unescape: invalid hex pair");
189 return 0; // To satisfy the compiler.
192 static char
193 unescapeHex(const string& data, size_t pos)
195 assert(pos < data.size());
196 if(pos + 2 >= data.size())
198 throw ParseException(__FILE__, __LINE__, "unescape: invalid hex pair");
200 return (char)(hexToInt(data[pos]) * 16 + hexToInt(data[pos + 1]));
203 static pair<string,string>
204 parseNameComponent(const string& data, size_t& pos)
206 pair<string, string> final = parseAttributeTypeAndValue(data, pos);
207 while(pos < data.size())
209 eatWhite(data, pos);
210 if(pos < data.size() && data[pos] == '+')
212 ++pos;
214 else
216 break;
218 pair<string, string> p = parseAttributeTypeAndValue(data, pos);
219 final.second += "+";
220 final.second += p.first;
221 final.second += '=';
222 final.second += p.second;
224 return final;
227 static pair<string,string>
228 parseAttributeTypeAndValue(const string& data, size_t& pos)
230 pair<string, string> p;
231 p.first = parseAttributeType(data, pos);
232 eatWhite(data, pos);
233 if(pos >= data.size())
235 throw ParseException(__FILE__, __LINE__, "invalid attribute type/value pair (unexpected end of data)");
237 if(data[pos] != '=')
239 throw ParseException(__FILE__, __LINE__, "invalid attribute type/value pair (missing =)");
241 ++pos;
242 p.second = parseAttributeValue(data, pos);
243 return p;
246 static string
247 parseAttributeType(const string& data, size_t& pos)
249 eatWhite(data, pos);
250 if(pos >= data.size())
252 throw ParseException(__FILE__, __LINE__, "invalid attribute type (expected end of data)");
255 string result;
258 // RFC 1779.
259 // <key> ::= 1*( <keychar> ) | "OID." <oid> | "oid." <oid>
260 // <oid> ::= <digitstring> | <digitstring> "." <oid>
261 // RFC 2253:
262 // attributeType = (ALPHA 1*keychar) | oid
263 // keychar = ALPHA | DIGIT | "-"
264 // oid = 1*DIGIT *("." 1*DIGIT)
266 // In section 4 of RFC 2253 the document says:
267 // Implementations MUST allow an oid in the attribute type to be
268 // prefixed by one of the character strings "oid." or "OID.".
270 // Here we must also check for "oid." and "OID." before parsing
271 // according to the ALPHA KEYCHAR* rule.
273 // First the OID case.
275 if(IceUtilInternal::isDigit(data[pos]) ||
276 (data.size() - pos >= 4 && (data.substr(pos, 4) == "oid." || data.substr(pos, 4) == "OID.")))
278 if(!IceUtilInternal::isDigit(data[pos]))
280 result += data.substr(pos, 4);
281 pos += 4;
284 while(true)
286 // 1*DIGIT
287 while(pos < data.size() && IceUtilInternal::isDigit(data[pos]))
289 result += data[pos];
290 ++pos;
292 // "." 1*DIGIT
293 if(pos < data.size() && data[pos] == '.')
295 result += data[pos];
296 ++pos;
297 // 1*DIGIT must follow "."
298 if(pos < data.size() && !IceUtilInternal::isDigit(data[pos]))
300 throw ParseException(__FILE__, __LINE__, "invalid attribute type (expected end of data)");
303 else
305 break;
309 else if(IceUtilInternal::isAlpha(data[pos]))
312 // The grammar is wrong in this case. It should be ALPHA
313 // KEYCHAR* otherwise it will not accept "O" as a valid
314 // attribute type.
316 result += data[pos];
317 ++pos;
318 // 1* KEYCHAR
319 while(pos < data.size() &&
320 (IceUtilInternal::isAlpha(data[pos]) || IceUtilInternal::isDigit(data[pos]) || data[pos] == '-'))
322 result += data[pos];
323 ++pos;
326 else
328 throw ParseException(__FILE__, __LINE__, "invalid attribute type");
330 return result;
333 static string
334 parseAttributeValue(const string& data, size_t& pos)
336 eatWhite(data, pos);
337 string result;
338 if(pos >= data.size())
340 return result;
344 // RFC 2253
345 // # hexstring
347 if(data[pos] == '#')
349 result += data[pos];
350 ++pos;
351 while(true)
353 string h = parseHexPair(data, pos, true);
354 if(h.size() == 0)
356 break;
358 result += h;
362 // RFC 2253
363 // QUOTATION *( quotechar | pair ) QUOTATION ; only from v2
364 // quotechar = <any character except "\" or QUOTATION >
366 else if(data[pos] == '"')
368 result += data[pos];
369 ++pos;
370 while(true)
372 if(pos >= data.size())
374 throw ParseException(__FILE__, __LINE__, "invalid attribute value (unexpected end of data)");
376 // final terminating "
377 if(data[pos] == '"')
379 result += data[pos];
380 ++pos;
381 break;
383 // any character except '\'
384 else if(data[pos] != '\\')
386 result += data[pos];
387 ++pos;
389 // pair '\'
390 else
392 result += parsePair(data, pos);
397 // RFC 2253
398 // * (stringchar | pair)
399 // stringchar = <any character except one of special, "\" or QUOTATION >
401 else
403 while(pos < data.size())
405 if(data[pos] == '\\')
407 result += parsePair(data, pos);
409 else if(special.find(data[pos]) == string::npos && data[pos] != '"')
411 result += data[pos];
412 ++pos;
414 else
416 break;
420 return result;
424 // RFC2253:
425 // pair = "\" ( special | "\" | QUOTATION | hexpair )
427 static string
428 parsePair(const string& data, size_t& pos)
430 string result;
432 assert(data[pos] == '\\');
433 result += data[pos];
434 ++pos;
436 if(pos >= data.size())
438 throw ParseException(__FILE__, __LINE__, "invalid escape format (unexpected end of data)");
441 if(special.find(data[pos]) != string::npos || data[pos] != '\\' || data[pos] != '"')
443 result += data[pos];
444 ++pos;
445 return result;
447 return parseHexPair(data, pos, false);
451 // RFC 2253
452 // hexpair = hexchar hexchar
454 static string
455 parseHexPair(const string& data, size_t& pos, bool allowEmpty)
457 string result;
458 if(pos < data.size() && hexvalid.find(data[pos]) != string::npos)
460 result += data[pos];
461 ++pos;
463 if(pos < data.size() && hexvalid.find(data[pos]) != string::npos)
465 result += data[pos];
466 ++pos;
468 if(result.size() != 2)
470 if(allowEmpty && result.size() == 0)
472 return result;
474 throw ParseException(__FILE__, __LINE__, "invalid hex format");
476 return result;
480 // RFC 2253:
482 // Implementations MUST allow for space (' ' ASCII 32) characters to be
483 // present between name-component and ',', between attributeTypeAndValue
484 // and '+', between attributeType and '=', and between '=' and
485 // attributeValue. These space characters are ignored when parsing.
487 static void
488 eatWhite(const string& data, size_t& pos)
490 while(pos < data.size() && data[pos] == ' ')
492 ++pos;
496 #ifdef never
497 void
498 print(const list< list<pair<string, string> > >& r)
500 if(r.size() > 1)
502 cout << "result: " << r.size() << " DNs" << endl;
504 for(list< list<pair<string, string> > >::const_iterator q = r.begin(); q != r.end(); ++q)
506 list<pair<string, string> > l = *q;
507 cout << "result: " << l.size() << " RDNs" << endl;
508 for(list<pair<string, string> >::const_iterator p = l.begin(); p != l.end(); ++p)
510 cout << "\t\"" << p->first << "\"=\"" << p->second << "\"" << endl;
516 main()
518 string examples[] = {
519 "CN=Steve Kille,O=Isode Limited,C=GB",
520 "OU=Sales+CN=J. Smith,O=Widget Inc.,C=US",
521 "CN=L. Eagle,O=Sue\\, Grabbit and Runn,C=GB",
522 "CN=Before\\0DAfter,O=Test,C=GB",
523 "1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB",
524 "SN=Lu\\C4\\8Di\\C4\\87",
528 for(int i = 0; i < sizeof(examples)/sizeof(examples[0]); ++i)
530 cout << "string: " << examples[i] << endl;
531 print(RFC2253::parse(examples[i]));
534 catch(const RFC2253::ParseException& e)
536 cout << "error: " << e.reason << endl;
539 #endif