23 #define WFN_ATTRIBUTES {\
30 Attribute::language, \
31 Attribute::sw_edition,\
32 Attribute::target_sw, \
33 Attribute::target_hw, \
44 inline int heDecodeCh(
char ch )
46 if (
'0' <= ch && ch <=
'9' )
48 if (
'A' <= ch && ch <=
'F' )
49 return( ch -
'A' + 10 );
50 if (
'a' <= ch && ch <=
'f' )
51 return( ch -
'a' + 10 );
56 inline bool chIsValidRange(
char ch )
57 {
return(
'!' <= ch && ch <=
'~' ); }
60 inline bool chIsAlpha(
char ch )
61 {
return( (
'a' <= ch && ch <=
'z' ) || (
'A' <= ch && ch <=
'Z' ) ); }
64 inline bool chIsNum(
char ch )
65 {
return(
'0' <= ch && ch <=
'9' ); }
68 inline bool chIsAlNum(
char ch )
69 {
return( chIsAlpha( ch ) || chIsNum( ch ) ); }
72 inline bool chIsWfnUnescaped(
char ch )
73 {
return( chIsAlNum( ch ) || ch ==
'_' ); }
84 typedef std::array<Value,Attribute::numAttributes>
Wfn;
89 Impl(
const std::string & cpe_r )
94 explicit operator bool()
const
95 {
for (
const auto & val :
_wfn )
if ( ! val.isANY() )
return true;
return false; }
103 ret <<
':' <<
_wfn[ai].asFs();
116 val =
_wfn[ai].asUri();
118 if ( ai == Attribute::edition )
120 if ( ! (
_wfn[Attribute::sw_edition].isANY()
121 &&
_wfn[Attribute::target_sw].isANY()
122 &&
_wfn[Attribute::target_hw].isANY()
123 &&
_wfn[Attribute::other].isANY() ) )
128 <<
'~' <<
_wfn[Attribute::sw_edition].asUri()
129 <<
'~' <<
_wfn[Attribute::target_sw].asUri()
130 <<
'~' <<
_wfn[Attribute::target_hw].asUri()
131 <<
'~' <<
_wfn[Attribute::other].asUri();
138 ret << std::string( colon,
':' );
160 if ( ai ) ret <<
',';
163 ret <<
'"' << val <<
'"';
174 SetCompare ret = SetCompare::equal;
179 case SetCompare::uncomparable:
180 ret = SetCompare::uncomparable;
183 case SetCompare::equal:
186 case SetCompare::properSubset:
187 if ( ret == SetCompare::equal )
188 ret = SetCompare::properSubset;
189 else if ( ret != SetCompare::properSubset )
190 ret = SetCompare::uncomparable;
193 case SetCompare::properSuperset:
194 if ( ret == SetCompare::equal )
195 ret = SetCompare::properSuperset;
196 else if ( ret != SetCompare::properSuperset )
197 ret = SetCompare::uncomparable;
200 case SetCompare::disjoint:
201 ret = SetCompare::disjoint;
204 if ( ret == SetCompare::uncomparable || ret == SetCompare::disjoint )
218 switch ( attr_r.asEnum() )
220 case Attribute::part:
222 const std::string & wfn( val_r.
asWfn() );
228 if ( wfn[1] ==
'\0' )
232 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn:part: '" << wfn <<
"' illegal value; expected: 'h' | 'o' | 'a'" );
240 const std::string & wfn( val_r.
asWfn() );
243 if ( chIsAlpha( wfn[0] ) && chIsAlpha( wfn[1] ) )
245 len = chIsAlpha( wfn[2] ) ? 3 : 2;
246 if ( wfn[len] ==
'-' )
248 if ( chIsAlpha( wfn[len+1] ) && chIsAlpha( wfn[len+2] ) )
250 else if ( chIsNum( wfn[len+1] ) && chIsNum( wfn[len+2] ) && chIsNum( wfn[len+3] ) )
254 if ( wfn.size() != len )
255 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn:language: '" << wfn <<
"' illegal value; expected RFC5646 conform: language ['-' region]" );
264 wfn_r[attr_r.asIntegral()] = val_r;
271 static Wfn unbind(
const std::string & cpe_r );
295 if ( cpe_r[4] ==
'/' )
299 else if ( cpe_r[4] ==
'2'
307 throw std::invalid_argument(
"CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
309 else if ( cpe_r[0] !=
'\0' )
310 throw std::invalid_argument(
"CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
318 static constexpr
unsigned numUriAttr = 7u;
319 std::vector<std::string> field;
320 field.reserve( Attribute::numAttributes );
321 if (
str::splitFields( cpe_r.c_str()+5, std::back_inserter(field),
":" ) > numUriAttr )
322 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri: too many fields (" << field.size() <<
"); expected " << numUriAttr );
323 field.resize( Attribute::numAttributes );
327 if ( ai == Attribute::edition && field[ai][0] ==
'~' )
330 static constexpr
unsigned numPacks = 6u;
331 std::vector<std::string> pack;
332 pack.reserve( numPacks );
333 if (
str::splitFields( field[ai], std::back_inserter(pack),
"~" ) > numPacks )
334 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri:edition: too many packs (" << pack.size() <<
"); expected " << numPacks );
335 pack.resize( numPacks );
337 pack[1].swap( field[Attribute::edition] );
338 pack[2].swap( field[Attribute::sw_edition] );
339 pack[3].swap( field[Attribute::target_sw] );
340 pack[4].swap( field[Attribute::target_hw] );
341 pack[5].swap( field[Attribute::other] );
352 std::vector<std::string> field;
353 field.reserve( Attribute::numAttributes );
354 if (
str::splitFields( cpe_r.c_str()+8, std::back_inserter(field),
":" ) > Attribute::numAttributes )
355 throw std::invalid_argument(
str::Str() <<
"CpeId:Fs: too many fields (" << field.size() <<
"); expected 11" );
356 if ( !field.empty() && field.back().empty() )
358 field.resize( Attribute::numAttributes,
"*" );
379 : _pimpl( new
Impl( cpe_r ) )
400 {
return bool(*_pimpl); }
420 static std::map<Enum,std::string>
_table = {
421 #define OUTS(N) { N, #N }
435 return _table[val_r];
447 if ( value_r.empty() )
450 _value.reset(
new std::string );
454 else if ( value_r !=
"*" )
456 bool starting =
true;
457 for_( chp, value_r.begin(), value_r.end() )
463 if ( ! chIsValidRange( *chp ) )
466 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: illegal quoted character '\\" << reinterpret_cast<void*>(*chp) <<
"'" );
468 throw std::invalid_argument(
"CpeId:Wfn: Backslash escapes nothing" );
470 else if ( chIsWfnUnescaped( *chp ) )
471 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: unnecessarily quoted character '\\" << *chp <<
"'" );
472 else if ( starting && *chp ==
'-' && chp+1 == value_r.end() )
473 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: '\\-' is illegal value" );
477 while ( *(chp+1) ==
'?' )
479 if ( ! ( starting || chp+1 == value_r.end() ) )
480 throw std::invalid_argument(
"CpeId:Wfn: embedded ?" );
484 if ( ! ( starting || chp+1 == value_r.end() ) )
485 throw std::invalid_argument(
"CpeId:Wfn: embedded *" );
489 if ( ! chIsWfnUnescaped( *chp ) )
491 if ( chIsValidRange( *chp ) )
492 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: missing quote before '" << *chp <<
"'" );
494 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: illegal character '" << reinterpret_cast<void*>(*chp) <<
"'" );
501 _value.reset(
new std::string( value_r ) );
507 if ( encoded_r !=
"*" )
509 if ( encoded_r ==
"-" )
516 bool starting =
true;
517 for_( chp, encoded_r.begin(), encoded_r.end() )
523 if ( chIsWfnUnescaped( *chp ) )
525 else if ( chIsValidRange( *chp ) )
526 result <<
'\\' << *chp;
528 throw std::invalid_argument(
str::Str() <<
"CpeId:Fs: illegal quoted character '\\" << *chp <<
"'" );
530 throw std::invalid_argument(
"CpeId:Fs: Backslash escapes nothing" );
535 while ( *(chp+1) ==
'?' )
540 if ( ! ( starting || chp+1 == encoded_r.end() ) )
541 throw std::invalid_argument(
"CpeId:Fs: embedded ?" );
545 if ( starting || chp+1 == encoded_r.end() )
548 throw std::invalid_argument(
"CpeId:Fs: embedded *" );
552 if ( chIsWfnUnescaped( *chp ) )
554 else if ( chIsValidRange( *chp ) )
555 result <<
'\\' << *chp;
557 throw std::invalid_argument(
str::Str() <<
"CpeId:Fs: illegal character '" << reinterpret_cast<void*>(*chp) <<
"'" );
564 throw std::invalid_argument(
"CpeId:Fs: '' value is illegal" );
565 _value.reset(
new std::string( result ) );
572 if ( ! encoded_r.empty() )
574 if ( encoded_r ==
"-" )
581 bool starting =
true;
582 for_( chp, encoded_r.begin(), encoded_r.end() )
588 int d1 = heDecodeCh( *(chp+1) );
591 int d2 = heDecodeCh( *(chp+2) );
600 while ( *(chp+1) ==
'%' && *(chp+2) ==
'0' && *(chp+3) ==
'1' )
605 if ( starting || chp+1 == encoded_r.end() )
611 throw std::invalid_argument(
"CpeId:Uri: embedded %01" );
615 if ( starting || chp+1 == encoded_r.end() )
622 throw std::invalid_argument(
"CpeId:Uri: embedded %02" );
626 if ( ! chIsValidRange( ch ) )
627 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri: illegal % encoded character '" <<
reinterpret_cast<void*
>(ch) <<
"'" );
631 else if ( ! chIsValidRange( ch ) )
632 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri: illegal character '" <<
reinterpret_cast<void*
>(ch) <<
"'" );
634 if ( chIsWfnUnescaped( ch ) )
637 result <<
'\\' << ch;
642 _value.reset(
new std::string( result ) );
652 static const std::string any(
"*" );
665 static const std::string asterisk(
"*" );
670 static const std::string dash(
"-" );
692 throw std::invalid_argument(
"CpeId:Wfn: Backslash escapes nothing" );
696 result <<
'\\' << *chp;
713 static const std::string dash(
"-" );
721 if ( chIsWfnUnescaped( *chp ) )
727 static const char *
const hdig =
"0123456789abcdef";
740 throw std::invalid_argument(
"CpeId:Wfn: Backslash escapes nothing" );
744 result <<
'%' << hdig[(
unsigned char)(*chp)/16] << hdig[(
unsigned char)(*chp)%16];
758 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: illegal char '" << *chp <<
"' in WFN" );
773 inline bool isWildchar(
char ch_r )
774 {
return( ch_r ==
'*' || ch_r ==
'?' ); }
779 inline bool evenNumberOfBackslashes( std::string::const_reverse_iterator rbegin_r, std::string::const_reverse_iterator rend_r )
781 unsigned backslashes = 0;
782 for_( it, rbegin_r, rend_r )
789 return !(backslashes & 1U);
796 for_( it, begin_r, end_r )
799 if ( str_r[it] ==
'\\' )
809 inline bool matchWildcardfreeString(
const std::string & lhs,
const std::string & rhs )
838 inline bool matchWildcardedString( std::string src, std::string trg )
843 switch ( *src.begin() )
846 if ( src.size() == 1 )
849 prefx = std::string::npos;
854 for_( it, ++src.begin(), src.end() )
855 {
if ( *it ==
'?' ) ++prefx;
else break; }
856 if ( src.size() == prefx )
857 return( trg.size() <= prefx );
859 src.erase( 0, prefx );
868 switch ( *src.rbegin() )
871 if ( evenNumberOfBackslashes( ++src.rbegin(), src.rend() ) )
873 suffx = std::string::npos;
874 src.erase( src.size()-1 );
879 for_( it, ++src.rbegin(), src.rend() )
880 {
if ( *it ==
'?' ) ++suffx;
else break; }
881 if ( ! evenNumberOfBackslashes( src.rbegin()+suffx, src.rend() ) )
883 src.erase( src.size()-suffx );
893 match != std::string::npos;
894 match = trg.find( src, match+1 ) )
896 if ( prefx != std::string::npos && trueCharsIn( trg, 0, match ) > prefx )
899 if ( suffx != std::string::npos && trueCharsIn( trg, frontSize, trg.size() ) > suffx )
910 const std::string & value( *
_value );
911 return ( isWildchar( *value.begin() )
912 || ( isWildchar( *value.rbegin() ) && evenNumberOfBackslashes( ++value.rbegin(), value.rend() ) ) );
929 #define WFN_STRICT_SPEC 0
933 static const SetCompare kNeedsCloserLook( SetCompare::Enum(-1) );
934 static const SetCompare matchTabel[4][4] = {{
936 SetCompare::properSuperset,
937 SetCompare::properSuperset,
938 SetCompare::uncomparable,
940 SetCompare::properSubset,
942 SetCompare::disjoint,
943 SetCompare::uncomparable,
945 SetCompare::properSubset,
946 SetCompare::disjoint,
948 SetCompare::uncomparable,
950 SetCompare::properSubset,
951 SetCompare::disjoint,
953 SetCompare::uncomparable,
956 Type srcType = type();
957 Type trgType = trg.type();
958 SetCompare ret = matchTabel[srcType.asIntegral()][trgType.asIntegral()];
959 if ( ret == kNeedsCloserLook )
961 if ( srcType == Type::wildcardfree )
964 ret = matchWildcardfreeString( *
_value, *trg._value ) ? SetCompare::equal : SetCompare::disjoint;
966 else if ( srcType == Type::wildcarded )
969 ret = matchWildcardedString( *
_value, *trg._value ) ? SetCompare::properSuperset : SetCompare::disjoint;
999 SetCompare ret = SetCompare::disjoint;
1003 ret = trg.
isANY() ? SetCompare::equal : SetCompare::properSuperset;
1005 else if ( trg.
isANY() )
1007 ret = SetCompare::properSubset;
1011 if ( trg.
isNA() ) ret = SetCompare::equal;
1013 else if ( ! trg.
isNA() )
1016 if ( isWildcarded() )
1021 ret = matchWildcardfreeString( *
_value, *trg.
_value ) ? SetCompare::equal : SetCompare::uncomparable;
1026 if ( matchWildcardedString( *
_value, *trg.
_value ) ) ret = SetCompare::properSuperset;
1034 if ( matchWildcardedString( *trg.
_value, *
_value ) ) ret = SetCompare::properSubset;
1039 if ( matchWildcardfreeString( *
_value, *trg.
_value ) ) ret = SetCompare::equal;
1045 #endif // WFN_STRICT_SPEC
Value()
Default ctor: ANY.
RWCOW_pointer< Impl > _pimpl
Implementation class.
static std::map< std::string, ServiceType::Type > _table
std::ostream & operator<<(std::ostream &str, const CpeId &obj)
Indicator type for non-trowing ctor.
bool containsWildcard() const
HAs unquoted [*?] at begin and/or end of value.
#define WFN_ATTRIBUTES
Initializer list with all wfn attributes.
static constexpr FsFormatType fsFormat
Indicator argument for ctor arg in FS format.
static const Value NA
Logical value indicating “not applicable/not used".
RWCOW_pointer< std::string > _value
std::array< Value, Attribute::numAttributes > Wfn
const std::string & asString(const std::string &t)
Global asString() that works with std::string too.
Impl(const std::string &cpe_r)
Common Platform Enumearation (2.3) See http://cpe.mitre.org/ for more information on the Common Platf...
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
std::string asFs() const
String representation as in Formated-String (ANY:"*", NA:"-")
static Wfn unbindFs(const std::string &cpe_r)
Parse Fs and unbind.
static Wfn unbindUri(const std::string &cpe_r)
Parse Uri and unbind.
std::string asWfn() const
String representation as in Well-Formed-Name (ANY:"*", NA:"").
static std::string lastMalformed
CpeId()
Default ctor: ANY-Cpeid, all attribute values are ANY.
bool isString() const
Whether it's an attribute value string (not logical value).
static const Value ANY
Logical value matching ANY value.
static Wfn unbind(const std::string &cpe_r)
Parse magic and unbind accordingly.
static void assignAttr(Wfn &wfn_r, Attribute attr_r, const Value &val_r)
Assign val_r if it meets attr_r specific contraints.
boost::noncopyable NonCopyable
Ensure derived classes cannot be copied.
std::string asUri() const
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
bool isNA() const
Whether value is NA.
std::string asFs() const
String representation as Formated-String (in/out).
base::EnumClass< EAttributeDef > Attribute
'enum class Attribute'
std::string toLower(const std::string &s)
Return lowercase version of s.
int compareCI(const C_Str &lhs, const C_Str &rhs)
SetCompare compare(const CpeId &trg) const
Compare sets.
std::string asString() const
Default string representation [asWfn].
unsigned splitFields(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=":")
Split line_r into fields.
SetCompare setRelationMixinCompare(const CpeId &trg) const
CPE name matching hook for SetRelationMixin.
SetCompare setRelationMixinCompare(const Value &trg) const
CPE name matching hook for SetRelationMixin.
std::string asWfn() const
SetCompare setRelationMixinCompare(const Impl &trg) const
std::string asUri() const
String representation as in URI (ANY:"", NA:"-")
static constexpr UriFormatType uriFormat
Indicator argument for ctor arg in URI format.
std::string asWfn() const
String representation as Well-Formed-Name (internal format, out only).
bool isANY() const
Whether value is ANY.
static const std::string & asString(Enum val_r)
string representantion
std::string asUri() const
String representation as URI (in/out).
bool isWildcarded() const
An attribute value string with wildcards ([*?] at begin and/or end)