1 // **********************************************************************
3 // Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
5 // This copy of Ice is licensed to you under the terms described in the
6 // ICE_LICENSE file included in this distribution.
8 // **********************************************************************
10 #include <IceUtil/StringUtil.h>
12 #include <IceSSL/Plugin.h>
13 #include <IceSSL/RFC2253.h>
16 using namespace IceSSL
;
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
)
44 current
.negate
= false;
46 while(pos
< data
.size())
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");
56 current
.negate
= true;
58 current
.rdn
.push_back(parseNameComponent(data
, pos
));
60 if(pos
< data
.size() && data
[pos
] == ',')
64 else if(pos
< data
.size() && data
[pos
] == ';')
67 results
.push_back(current
);
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
);
84 IceSSL::RFC2253::RDNSeq
85 IceSSL::RFC2253::parseStrict(const string
& data
)
89 while(pos
< data
.size())
91 results
.push_back(parseNameComponent(data
, pos
));
93 if(pos
< data
.size() && (data
[pos
] == ',' || data
[pos
] == ';'))
97 else if(pos
< data
.size())
99 throw ParseException(__FILE__
, __LINE__
, "expected ',' or ';' at `" + data
.substr(pos
) + "'");
106 IceSSL::RFC2253::unescape(const string
& data
)
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.
133 while(pos
< data
.size())
135 result
+= unescapeHex(data
, pos
);
142 while(pos
< data
.size())
144 if(data
[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
] != '"')
163 result
+= unescapeHex(data
, pos
);
176 if(v
>= '0' && v
<= '9')
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.
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())
210 if(pos
< data
.size() && data
[pos
] == '+')
218 pair
<string
, string
> p
= parseAttributeTypeAndValue(data
, pos
);
220 final
.second
+= p
.first
;
222 final
.second
+= p
.second
;
227 static pair
<string
,string
>
228 parseAttributeTypeAndValue(const string
& data
, size_t& pos
)
230 pair
<string
, string
> p
;
231 p
.first
= parseAttributeType(data
, pos
);
233 if(pos
>= data
.size())
235 throw ParseException(__FILE__
, __LINE__
, "invalid attribute type/value pair (unexpected end of data)");
239 throw ParseException(__FILE__
, __LINE__
, "invalid attribute type/value pair (missing =)");
242 p
.second
= parseAttributeValue(data
, pos
);
247 parseAttributeType(const string
& data
, size_t& pos
)
250 if(pos
>= data
.size())
252 throw ParseException(__FILE__
, __LINE__
, "invalid attribute type (expected end of data)");
259 // <key> ::= 1*( <keychar> ) | "OID." <oid> | "oid." <oid>
260 // <oid> ::= <digitstring> | <digitstring> "." <oid>
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);
287 while(pos
< data
.size() && IceUtilInternal::isDigit(data
[pos
]))
293 if(pos
< data
.size() && data
[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)");
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
319 while(pos
< data
.size() &&
320 (IceUtilInternal::isAlpha(data
[pos
]) || IceUtilInternal::isDigit(data
[pos
]) || data
[pos
] == '-'))
328 throw ParseException(__FILE__
, __LINE__
, "invalid attribute type");
334 parseAttributeValue(const string
& data
, size_t& pos
)
338 if(pos
>= data
.size())
353 string h
= parseHexPair(data
, pos
, true);
363 // QUOTATION *( quotechar | pair ) QUOTATION ; only from v2
364 // quotechar = <any character except "\" or QUOTATION >
366 else if(data
[pos
] == '"')
372 if(pos
>= data
.size())
374 throw ParseException(__FILE__
, __LINE__
, "invalid attribute value (unexpected end of data)");
376 // final terminating "
383 // any character except '\'
384 else if(data
[pos
] != '\\')
392 result
+= parsePair(data
, pos
);
398 // * (stringchar | pair)
399 // stringchar = <any character except one of special, "\" or QUOTATION >
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
] != '"')
425 // pair = "\" ( special | "\" | QUOTATION | hexpair )
428 parsePair(const string
& data
, size_t& pos
)
432 assert(data
[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
] != '"')
447 return parseHexPair(data
, pos
, false);
452 // hexpair = hexchar hexchar
455 parseHexPair(const string
& data
, size_t& pos
, bool allowEmpty
)
458 if(pos
< data
.size() && hexvalid
.find(data
[pos
]) != string::npos
)
463 if(pos
< data
.size() && hexvalid
.find(data
[pos
]) != string::npos
)
468 if(result
.size() != 2)
470 if(allowEmpty
&& result
.size() == 0)
474 throw ParseException(__FILE__
, __LINE__
, "invalid hex format");
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.
488 eatWhite(const string
& data
, size_t& pos
)
490 while(pos
< data
.size() && data
[pos
] == ' ')
498 print(const list
< list
<pair
<string
, string
> > >& r
)
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
;
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
;