| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | // ============================================================ |
| 5 | // |
| 6 | // TextualIdentityParser.cpp |
| 7 | // |
| 8 | |
| 9 | |
| 10 | // |
| 11 | // Implements the TextualIdentityParser class |
| 12 | // |
| 13 | // ============================================================ |
| 14 | |
| 15 | #define DISABLE_BINDER_DEBUG_LOGGING |
| 16 | |
| 17 | #include "textualidentityparser.hpp" |
| 18 | #include "assemblyidentity.hpp" |
| 19 | #include "utils.hpp" |
| 20 | |
| 21 | #include "ex.h" |
| 22 | |
| 23 | #define GO_IF_SEEN(kAssemblyIdentityFlag) \ |
| 24 | if ((m_dwAttributesSeen & kAssemblyIdentityFlag) != 0) \ |
| 25 | { \ |
| 26 | fIsValid = FALSE; \ |
| 27 | goto Exit; \ |
| 28 | } \ |
| 29 | else \ |
| 30 | { \ |
| 31 | m_dwAttributesSeen |= kAssemblyIdentityFlag; \ |
| 32 | } |
| 33 | |
| 34 | #define GO_IF_WILDCARD(valueString) \ |
| 35 | { \ |
| 36 | SmallStackSString wildCard(W("*")); \ |
| 37 | if (valueString.Equals(wildCard)) \ |
| 38 | { \ |
| 39 | goto Exit; \ |
| 40 | } \ |
| 41 | } |
| 42 | |
| 43 | #define GO_IF_VALIDATE_FAILED(validateProc, kIdentityFlag) \ |
| 44 | if (!validateProc(valueString)) \ |
| 45 | { \ |
| 46 | fIsValid = FALSE; \ |
| 47 | goto Exit; \ |
| 48 | } \ |
| 49 | else \ |
| 50 | { \ |
| 51 | m_pAssemblyIdentity->SetHave(kIdentityFlag); \ |
| 52 | } |
| 53 | |
| 54 | #define FROMHEX(a) ((a)>=W('a') ? a - W('a') + 10 : a - W('0')) |
| 55 | #define TOHEX(a) ((a)>=10 ? W('a')+(a)-10 : W('0')+(a)) |
| 56 | #define TOLOWER(a) (((a) >= W('A') && (a) <= W('Z')) ? (W('a') + (a - W('A'))) : (a)) |
| 57 | |
| 58 | namespace BINDER_SPACE |
| 59 | { |
| 60 | namespace |
| 61 | { |
| 62 | const int iPublicKeyTokenLength = 8; |
| 63 | |
| 64 | const int iPublicKeyMinLength = 0; |
| 65 | const int iPublicKeyMaxLength = 2048; |
| 66 | |
| 67 | const int iVersionMax = 65535; |
| 68 | const int iVersionParts = 4; |
| 69 | |
| 70 | inline void UnicodeHexToBin(LPCWSTR pSrc, UINT cSrc, LPBYTE pDest) |
| 71 | { |
| 72 | BYTE v; |
| 73 | LPBYTE pd = pDest; |
| 74 | LPCWSTR ps = pSrc; |
| 75 | |
| 76 | if (cSrc == 0) |
| 77 | return; |
| 78 | |
| 79 | for (UINT i = 0; i < cSrc-1; i+=2) |
| 80 | { |
| 81 | v = (BYTE)FROMHEX(TOLOWER(ps[i])) << 4; |
| 82 | v |= FROMHEX(TOLOWER(ps[i+1])); |
| 83 | *(pd++) = v; |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | inline void BinToUnicodeHex(const BYTE *pSrc, UINT cSrc, __out_ecount(2*cSrc) LPWSTR pDst) |
| 88 | { |
| 89 | UINT x; |
| 90 | UINT y; |
| 91 | |
| 92 | for (x = 0, y = 0 ; x < cSrc; ++x) |
| 93 | { |
| 94 | UINT v; |
| 95 | |
| 96 | v = pSrc[x]>>4; |
| 97 | pDst[y++] = (WCHAR)TOHEX(v); |
| 98 | v = pSrc[x] & 0x0f; |
| 99 | pDst[y++] = (WCHAR)TOHEX(v); |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | inline BOOL EqualsCaseInsensitive(SString &a, LPCWSTR wzB) |
| 104 | { |
| 105 | SString b(SString::Literal, wzB); |
| 106 | |
| 107 | return ::BINDER_SPACE::EqualsCaseInsensitive(a, b); |
| 108 | } |
| 109 | |
| 110 | BOOL ValidateHex(SString &publicKeyOrToken) |
| 111 | { |
| 112 | if ((publicKeyOrToken.GetCount() == 0) || ((publicKeyOrToken.GetCount() % 2) != 0)) |
| 113 | { |
| 114 | return FALSE; |
| 115 | } |
| 116 | |
| 117 | SString::Iterator cursor = publicKeyOrToken.Begin(); |
| 118 | SString::Iterator end = publicKeyOrToken.End() - 1; |
| 119 | |
| 120 | while (cursor <= end) |
| 121 | { |
| 122 | WCHAR wcCurrentChar = cursor[0]; |
| 123 | |
| 124 | if (((wcCurrentChar >= W('0')) && (wcCurrentChar <= W('9'))) || |
| 125 | ((wcCurrentChar >= W('a')) && (wcCurrentChar <= W('f'))) || |
| 126 | ((wcCurrentChar >= W('A')) && (wcCurrentChar <= W('F')))) |
| 127 | { |
| 128 | cursor++; |
| 129 | continue; |
| 130 | } |
| 131 | |
| 132 | return FALSE; |
| 133 | } |
| 134 | |
| 135 | return TRUE; |
| 136 | } |
| 137 | |
| 138 | inline BOOL ValidatePublicKeyToken(SString &publicKeyToken) |
| 139 | { |
| 140 | return ((publicKeyToken.GetCount() == (iPublicKeyTokenLength * 2)) && |
| 141 | ValidateHex(publicKeyToken)); |
| 142 | } |
| 143 | |
| 144 | inline BOOL ValidatePublicKey(SString &publicKey) |
| 145 | { |
| 146 | |
| 147 | return ((publicKey.GetCount() >= (iPublicKeyMinLength * 2)) && |
| 148 | (publicKey.GetCount() <= (iPublicKeyMaxLength * 2)) && |
| 149 | ValidateHex(publicKey)); |
| 150 | } |
| 151 | |
| 152 | const struct { |
| 153 | LPCWSTR strValue; |
| 154 | PEKIND enumValue; |
| 155 | } wszKnownArchitectures[] = { { W("x86" ), peI386 }, |
| 156 | { W("IA64" ), peIA64 }, |
| 157 | { W("AMD64" ), peAMD64 }, |
| 158 | { W("ARM" ), peARM }, |
| 159 | { W("MSIL" ), peMSIL } }; |
| 160 | |
| 161 | BOOL ValidateAndConvertProcessorArchitecture(SString &processorArchitecture, |
| 162 | PEKIND *pkProcessorArchitecture) |
| 163 | { |
| 164 | for (int i = LENGTH_OF(wszKnownArchitectures); i--;) |
| 165 | { |
| 166 | if (EqualsCaseInsensitive(processorArchitecture, wszKnownArchitectures[i].strValue)) |
| 167 | { |
| 168 | *pkProcessorArchitecture = wszKnownArchitectures[i].enumValue; |
| 169 | return TRUE; |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | return FALSE; |
| 174 | } |
| 175 | |
| 176 | LPCWSTR PeKindToString(PEKIND kProcessorArchitecture) |
| 177 | { |
| 178 | _ASSERTE(kProcessorArchitecture != peNone); |
| 179 | |
| 180 | for (int i = LENGTH_OF(wszKnownArchitectures); i--;) |
| 181 | { |
| 182 | if (wszKnownArchitectures[i].enumValue == kProcessorArchitecture) |
| 183 | { |
| 184 | return wszKnownArchitectures[i].strValue; |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | return NULL; |
| 189 | } |
| 190 | |
| 191 | LPCWSTR ContentTypeToString(AssemblyContentType kContentType) |
| 192 | { |
| 193 | _ASSERTE(kContentType != AssemblyContentType_Default); |
| 194 | |
| 195 | if (kContentType == AssemblyContentType_WindowsRuntime) |
| 196 | { |
| 197 | return W("WindowsRuntime" ); |
| 198 | } |
| 199 | |
| 200 | return NULL; |
| 201 | } |
| 202 | }; // namespace (anonymous) |
| 203 | |
| 204 | TextualIdentityParser::TextualIdentityParser(AssemblyIdentity *pAssemblyIdentity) |
| 205 | { |
| 206 | m_pAssemblyIdentity = pAssemblyIdentity; |
| 207 | m_dwAttributesSeen = AssemblyIdentity::IDENTITY_FLAG_EMPTY; |
| 208 | } |
| 209 | |
| 210 | TextualIdentityParser::~TextualIdentityParser() |
| 211 | { |
| 212 | // Nothing to do here |
| 213 | } |
| 214 | |
| 215 | BOOL TextualIdentityParser::IsSeparatorChar(WCHAR wcChar) |
| 216 | { |
| 217 | return ((wcChar == W(',')) || (wcChar == W('='))); |
| 218 | } |
| 219 | |
| 220 | StringLexer::LEXEME_TYPE TextualIdentityParser::GetLexemeType(WCHAR wcChar) |
| 221 | { |
| 222 | switch (wcChar) |
| 223 | { |
| 224 | case W('='): |
| 225 | return LEXEME_TYPE_EQUALS; |
| 226 | case W(','): |
| 227 | return LEXEME_TYPE_COMMA; |
| 228 | case 0: |
| 229 | return LEXEME_TYPE_END_OF_STREAM; |
| 230 | default: |
| 231 | return LEXEME_TYPE_STRING; |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | /* static */ |
| 236 | HRESULT TextualIdentityParser::Parse(SString &textualIdentity, |
| 237 | AssemblyIdentity *pAssemblyIdentity, |
| 238 | BOOL fPermitUnescapedQuotes) |
| 239 | { |
| 240 | HRESULT hr = S_OK; |
| 241 | BINDER_LOG_ENTER(W("TextualIdentityParser::Parse" )); |
| 242 | |
| 243 | IF_FALSE_GO(pAssemblyIdentity != NULL); |
| 244 | |
| 245 | BINDER_LOG_STRING(W("textualIdentity" ), textualIdentity); |
| 246 | |
| 247 | EX_TRY |
| 248 | { |
| 249 | TextualIdentityParser identityParser(pAssemblyIdentity); |
| 250 | |
| 251 | if (!identityParser.Parse(textualIdentity, fPermitUnescapedQuotes)) |
| 252 | { |
| 253 | IF_FAIL_GO(FUSION_E_INVALID_NAME); |
| 254 | } |
| 255 | } |
| 256 | EX_CATCH_HRESULT(hr); |
| 257 | |
| 258 | Exit: |
| 259 | BINDER_LOG_LEAVE_HR(W("TextualIdentityParser::Parse" ), hr); |
| 260 | return hr; |
| 261 | } |
| 262 | |
| 263 | /* static */ |
| 264 | HRESULT TextualIdentityParser::ToString(AssemblyIdentity *pAssemblyIdentity, |
| 265 | DWORD dwIdentityFlags, |
| 266 | SString &textualIdentity) |
| 267 | { |
| 268 | HRESULT hr = S_OK; |
| 269 | BINDER_LOG_ENTER(W("TextualIdentityParser::ToString" )); |
| 270 | |
| 271 | IF_FALSE_GO(pAssemblyIdentity != NULL); |
| 272 | |
| 273 | EX_TRY |
| 274 | { |
| 275 | SmallStackSString tmpString; |
| 276 | |
| 277 | textualIdentity.Clear(); |
| 278 | |
| 279 | if (pAssemblyIdentity->m_simpleName.IsEmpty()) |
| 280 | { |
| 281 | goto Exit; |
| 282 | } |
| 283 | |
| 284 | EscapeString(pAssemblyIdentity->m_simpleName, tmpString); |
| 285 | textualIdentity.Append(tmpString); |
| 286 | |
| 287 | if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_VERSION)) |
| 288 | { |
| 289 | tmpString.Clear(); |
| 290 | tmpString.Printf(W("%d.%d.%d.%d" ), |
| 291 | (DWORD)(USHORT)pAssemblyIdentity->m_version.GetMajor(), |
| 292 | (DWORD)(USHORT)pAssemblyIdentity->m_version.GetMinor(), |
| 293 | (DWORD)(USHORT)pAssemblyIdentity->m_version.GetBuild(), |
| 294 | (DWORD)(USHORT)pAssemblyIdentity->m_version.GetRevision()); |
| 295 | |
| 296 | textualIdentity.Append(W(", Version=" )); |
| 297 | textualIdentity.Append(tmpString); |
| 298 | } |
| 299 | |
| 300 | if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_CULTURE)) |
| 301 | { |
| 302 | textualIdentity.Append(W(", Culture=" )); |
| 303 | if (pAssemblyIdentity->m_cultureOrLanguage.IsEmpty()) |
| 304 | { |
| 305 | textualIdentity.Append(W("neutral" )); |
| 306 | } |
| 307 | else |
| 308 | { |
| 309 | EscapeString(pAssemblyIdentity->m_cultureOrLanguage, tmpString); |
| 310 | textualIdentity.Append(tmpString); |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY)) |
| 315 | { |
| 316 | textualIdentity.Append(W(", PublicKey=" )); |
| 317 | tmpString.Clear(); |
| 318 | BlobToHex(pAssemblyIdentity->m_publicKeyOrTokenBLOB, tmpString); |
| 319 | textualIdentity.Append(tmpString); |
| 320 | } |
| 321 | else if (AssemblyIdentity::Have(dwIdentityFlags, |
| 322 | AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN)) |
| 323 | { |
| 324 | textualIdentity.Append(W(", PublicKeyToken=" )); |
| 325 | tmpString.Clear(); |
| 326 | BlobToHex(pAssemblyIdentity->m_publicKeyOrTokenBLOB, tmpString); |
| 327 | textualIdentity.Append(tmpString); |
| 328 | } |
| 329 | else if (AssemblyIdentity::Have(dwIdentityFlags, |
| 330 | AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL)) |
| 331 | { |
| 332 | textualIdentity.Append(W(", PublicKeyToken=null" )); |
| 333 | } |
| 334 | |
| 335 | if (AssemblyIdentity::Have(dwIdentityFlags, |
| 336 | AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE)) |
| 337 | { |
| 338 | textualIdentity.Append(W(", processorArchitecture=" )); |
| 339 | textualIdentity.Append(PeKindToString(pAssemblyIdentity->m_kProcessorArchitecture)); |
| 340 | } |
| 341 | |
| 342 | if (AssemblyIdentity::Have(dwIdentityFlags, |
| 343 | AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE)) |
| 344 | { |
| 345 | textualIdentity.Append(W(", Retargetable=Yes" )); |
| 346 | } |
| 347 | |
| 348 | if (AssemblyIdentity::Have(dwIdentityFlags, |
| 349 | AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE)) |
| 350 | { |
| 351 | textualIdentity.Append(W(", ContentType=" )); |
| 352 | textualIdentity.Append(ContentTypeToString(pAssemblyIdentity->m_kContentType)); |
| 353 | } |
| 354 | |
| 355 | if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_CUSTOM)) |
| 356 | { |
| 357 | textualIdentity.Append(W(", Custom=" )); |
| 358 | tmpString.Clear(); |
| 359 | BlobToHex(pAssemblyIdentity->m_customBLOB, tmpString); |
| 360 | textualIdentity.Append(tmpString); |
| 361 | } |
| 362 | else if (AssemblyIdentity::Have(dwIdentityFlags, |
| 363 | AssemblyIdentity::IDENTITY_FLAG_CUSTOM_NULL)) |
| 364 | { |
| 365 | textualIdentity.Append(W(", Custom=null" )); |
| 366 | } |
| 367 | } |
| 368 | EX_CATCH_HRESULT(hr); |
| 369 | |
| 370 | Exit: |
| 371 | BINDER_LOG_LEAVE_HR(W("TextualIdentityParser::ToString" ), hr); |
| 372 | return hr; |
| 373 | } |
| 374 | |
| 375 | /* static */ |
| 376 | BOOL TextualIdentityParser::ParseVersion(SString &versionString, |
| 377 | AssemblyVersion *pAssemblyVersion) |
| 378 | { |
| 379 | BOOL fIsValid = FALSE; |
| 380 | DWORD dwFoundNumbers = 0; |
| 381 | bool foundUnspecifiedComponent = false; |
| 382 | const DWORD UnspecifiedNumber = (DWORD)-1; |
| 383 | DWORD dwCurrentNumber = UnspecifiedNumber; |
| 384 | DWORD dwNumbers[iVersionParts] = {UnspecifiedNumber, UnspecifiedNumber, UnspecifiedNumber, UnspecifiedNumber}; |
| 385 | |
| 386 | BINDER_LOG_ENTER(W("TextualIdentityParser::ParseVersion" )); |
| 387 | |
| 388 | if (versionString.GetCount() > 0) { |
| 389 | SString::Iterator cursor = versionString.Begin(); |
| 390 | SString::Iterator end = versionString.End(); |
| 391 | |
| 392 | while (cursor <= end) |
| 393 | { |
| 394 | WCHAR wcCurrentChar = cursor[0]; |
| 395 | |
| 396 | if (dwFoundNumbers >= static_cast<DWORD>(iVersionParts)) |
| 397 | { |
| 398 | goto Exit; |
| 399 | } |
| 400 | else if (wcCurrentChar == W('.') || wcCurrentChar == W('\0')) |
| 401 | { |
| 402 | if (dwCurrentNumber == UnspecifiedNumber) |
| 403 | { |
| 404 | // Difference from .NET Framework, compat with Version(string) constructor: A missing version component |
| 405 | // is considered invalid. |
| 406 | // |
| 407 | // Examples: |
| 408 | // "MyAssembly, Version=." |
| 409 | // "MyAssembly, Version=1." |
| 410 | // "MyAssembly, Version=.1" |
| 411 | // "MyAssembly, Version=1..1" |
| 412 | goto Exit; |
| 413 | } |
| 414 | |
| 415 | // Compat with .NET Framework: A value of iVersionMax is considered unspecified. Once an unspecified |
| 416 | // component is found, validate the remaining components but consider them as unspecified as well. |
| 417 | if (dwCurrentNumber == iVersionMax) |
| 418 | { |
| 419 | foundUnspecifiedComponent = true; |
| 420 | dwCurrentNumber = UnspecifiedNumber; |
| 421 | } |
| 422 | else if (!foundUnspecifiedComponent) |
| 423 | { |
| 424 | dwNumbers[dwFoundNumbers] = dwCurrentNumber; |
| 425 | dwCurrentNumber = UnspecifiedNumber; |
| 426 | } |
| 427 | |
| 428 | dwFoundNumbers++; |
| 429 | } |
| 430 | else if ((wcCurrentChar >= W('0')) && (wcCurrentChar <= W('9'))) |
| 431 | { |
| 432 | if (dwCurrentNumber == UnspecifiedNumber) |
| 433 | { |
| 434 | dwCurrentNumber = 0; |
| 435 | } |
| 436 | dwCurrentNumber = (dwCurrentNumber * 10) + (wcCurrentChar - W('0')); |
| 437 | |
| 438 | if (dwCurrentNumber > static_cast<DWORD>(iVersionMax)) |
| 439 | { |
| 440 | goto Exit; |
| 441 | } |
| 442 | } |
| 443 | else |
| 444 | { |
| 445 | goto Exit; |
| 446 | } |
| 447 | |
| 448 | cursor++; |
| 449 | } |
| 450 | |
| 451 | // Difference from .NET Framework: If the major or minor version are unspecified, the version is considered invalid. |
| 452 | // |
| 453 | // Examples: |
| 454 | // "MyAssembly, Version=" |
| 455 | // "MyAssembly, Version=1" |
| 456 | // "MyAssembly, Version=65535.1" |
| 457 | // "MyAssembly, Version=1.65535" |
| 458 | if (dwFoundNumbers < 2 || dwNumbers[0] == UnspecifiedNumber || dwNumbers[1] == UnspecifiedNumber) |
| 459 | { |
| 460 | goto Exit; |
| 461 | } |
| 462 | |
| 463 | pAssemblyVersion->SetFeatureVersion(dwNumbers[0], dwNumbers[1]); |
| 464 | pAssemblyVersion->SetServiceVersion(dwNumbers[2], dwNumbers[3]); |
| 465 | fIsValid = TRUE; |
| 466 | } |
| 467 | |
| 468 | Exit: |
| 469 | BINDER_LOG_LEAVE(W("TextualIdentityParser::ParseVersion" )); |
| 470 | return fIsValid; |
| 471 | } |
| 472 | |
| 473 | /* static */ |
| 474 | BOOL TextualIdentityParser::HexToBlob(SString &publicKeyOrToken, |
| 475 | BOOL fValidateHex, |
| 476 | BOOL fIsToken, |
| 477 | SBuffer &publicKeyOrTokenBLOB) |
| 478 | { |
| 479 | // Optional input verification |
| 480 | if (fValidateHex) |
| 481 | { |
| 482 | if ((fIsToken && !ValidatePublicKeyToken(publicKeyOrToken)) || |
| 483 | (!fIsToken && !ValidatePublicKey(publicKeyOrToken))) |
| 484 | { |
| 485 | return FALSE; |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | UINT ccPublicKeyOrToken = publicKeyOrToken.GetCount(); |
| 490 | BYTE *pByteBLOB = publicKeyOrTokenBLOB.OpenRawBuffer(ccPublicKeyOrToken / 2); |
| 491 | |
| 492 | UnicodeHexToBin(publicKeyOrToken.GetUnicode(), ccPublicKeyOrToken, pByteBLOB); |
| 493 | publicKeyOrTokenBLOB.CloseRawBuffer(); |
| 494 | |
| 495 | return TRUE; |
| 496 | } |
| 497 | |
| 498 | /* static */ |
| 499 | void TextualIdentityParser::BlobToHex(SBuffer &publicKeyOrTokenBLOB, |
| 500 | SString &publicKeyOrToken) |
| 501 | { |
| 502 | UINT cbPublicKeyOrTokenBLOB = publicKeyOrTokenBLOB.GetSize(); |
| 503 | WCHAR *pwzpublicKeyOrToken = |
| 504 | publicKeyOrToken.OpenUnicodeBuffer(cbPublicKeyOrTokenBLOB * 2); |
| 505 | |
| 506 | BinToUnicodeHex(publicKeyOrTokenBLOB, cbPublicKeyOrTokenBLOB, pwzpublicKeyOrToken); |
| 507 | publicKeyOrToken.CloseBuffer(cbPublicKeyOrTokenBLOB * 2); |
| 508 | } |
| 509 | |
| 510 | BOOL TextualIdentityParser::Parse(SString &textualIdentity, BOOL fPermitUnescapedQuotes) |
| 511 | { |
| 512 | BOOL fIsValid = TRUE; |
| 513 | BINDER_LOG_ENTER(W("TextualIdentityParser::Parse(textualIdentity)" )); |
| 514 | SString unicodeTextualIdentity; |
| 515 | |
| 516 | // Lexer modifies input string |
| 517 | textualIdentity.ConvertToUnicode(unicodeTextualIdentity); |
| 518 | Init(unicodeTextualIdentity, TRUE /* fSupportEscaping */); |
| 519 | |
| 520 | SmallStackSString currentString; |
| 521 | |
| 522 | // Identity format is simple name (, attr = value)* |
| 523 | GO_IF_NOT_EXPECTED(GetNextLexeme(currentString, fPermitUnescapedQuotes), LEXEME_TYPE_STRING); |
| 524 | m_pAssemblyIdentity->m_simpleName.Set(currentString); |
| 525 | m_pAssemblyIdentity->m_simpleName.Normalize(); |
| 526 | m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_SIMPLE_NAME); |
| 527 | |
| 528 | for (;;) |
| 529 | { |
| 530 | SmallStackSString attributeString; |
| 531 | SmallStackSString valueString; |
| 532 | |
| 533 | GO_IF_END_OR_NOT_EXPECTED(GetNextLexeme(currentString), LEXEME_TYPE_COMMA); |
| 534 | GO_IF_NOT_EXPECTED(GetNextLexeme(attributeString), LEXEME_TYPE_STRING); |
| 535 | GO_IF_NOT_EXPECTED(GetNextLexeme(currentString), LEXEME_TYPE_EQUALS); |
| 536 | GO_IF_NOT_EXPECTED(GetNextLexeme(valueString), LEXEME_TYPE_STRING); |
| 537 | |
| 538 | if (!PopulateAssemblyIdentity(attributeString, valueString)) |
| 539 | { |
| 540 | fIsValid = FALSE; |
| 541 | break; |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | Exit: |
| 546 | BINDER_LOG_LEAVE_BOOL(W("TextualIdentityParser::Parse(textualIdentity)" ), fIsValid); |
| 547 | return fIsValid; |
| 548 | } |
| 549 | |
| 550 | BOOL TextualIdentityParser::ParseString(SString &textualString, |
| 551 | SString &contentString) |
| 552 | { |
| 553 | BOOL fIsValid = TRUE; |
| 554 | BINDER_LOG_ENTER(W("TextualIdentityParser::ParseString" )); |
| 555 | SString unicodeTextualString; |
| 556 | |
| 557 | // Lexer modifies input string |
| 558 | textualString.ConvertToUnicode(unicodeTextualString); |
| 559 | Init(unicodeTextualString, TRUE /* fSupportEscaping */); |
| 560 | |
| 561 | SmallStackSString currentString; |
| 562 | GO_IF_NOT_EXPECTED(GetNextLexeme(currentString), LEXEME_TYPE_STRING); |
| 563 | |
| 564 | contentString.Set(currentString); |
| 565 | currentString.Normalize(); |
| 566 | |
| 567 | Exit: |
| 568 | BINDER_LOG_LEAVE_BOOL(W("TextualIdentityParser::ParseString" ), fIsValid); |
| 569 | return fIsValid; |
| 570 | } |
| 571 | |
| 572 | BOOL TextualIdentityParser::PopulateAssemblyIdentity(SString &attributeString, |
| 573 | SString &valueString) |
| 574 | { |
| 575 | BINDER_LOG_ENTER(W("TextualIdentityParser::PopulateAssemblyIdentity" )); |
| 576 | BOOL fIsValid = TRUE; |
| 577 | |
| 578 | if (EqualsCaseInsensitive(attributeString, W("culture" )) || |
| 579 | EqualsCaseInsensitive(attributeString, W("language" ))) |
| 580 | { |
| 581 | GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_CULTURE); |
| 582 | GO_IF_WILDCARD(valueString); |
| 583 | |
| 584 | if (!EqualsCaseInsensitive(valueString, W("neutral" ))) |
| 585 | { |
| 586 | // culture/language is preserved as is |
| 587 | m_pAssemblyIdentity->m_cultureOrLanguage.Set(valueString); |
| 588 | m_pAssemblyIdentity->m_cultureOrLanguage.Normalize(); |
| 589 | } |
| 590 | |
| 591 | m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_CULTURE); |
| 592 | } |
| 593 | else if (EqualsCaseInsensitive(attributeString, W("version" ))) |
| 594 | { |
| 595 | AssemblyVersion *pAssemblyVersion = &(m_pAssemblyIdentity->m_version); |
| 596 | |
| 597 | GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_VERSION); |
| 598 | GO_IF_WILDCARD(valueString); |
| 599 | |
| 600 | if (ParseVersion(valueString, pAssemblyVersion)) |
| 601 | { |
| 602 | m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_VERSION); |
| 603 | } |
| 604 | else |
| 605 | { |
| 606 | fIsValid = FALSE; |
| 607 | } |
| 608 | } |
| 609 | else if (EqualsCaseInsensitive(attributeString, W("publickeytoken" ))) |
| 610 | { |
| 611 | GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY); |
| 612 | GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN); |
| 613 | GO_IF_WILDCARD(valueString); |
| 614 | |
| 615 | if (!EqualsCaseInsensitive(valueString, W("null" )) && |
| 616 | !EqualsCaseInsensitive(valueString, W("neutral" ))) |
| 617 | { |
| 618 | GO_IF_VALIDATE_FAILED(ValidatePublicKeyToken, |
| 619 | AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN); |
| 620 | HexToBlob(valueString, |
| 621 | FALSE /* fValidateHex */, |
| 622 | TRUE /* fIsToken */, |
| 623 | m_pAssemblyIdentity->m_publicKeyOrTokenBLOB); |
| 624 | } |
| 625 | else |
| 626 | { |
| 627 | m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL); |
| 628 | } |
| 629 | } |
| 630 | else if (EqualsCaseInsensitive(attributeString, W("publickey" ))) |
| 631 | { |
| 632 | GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN); |
| 633 | GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY); |
| 634 | |
| 635 | if (!EqualsCaseInsensitive(valueString, W("null" )) && |
| 636 | !EqualsCaseInsensitive(valueString, W("neutral" ))) |
| 637 | { |
| 638 | GO_IF_VALIDATE_FAILED(ValidatePublicKey, AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY); |
| 639 | HexToBlob(valueString, |
| 640 | FALSE /* fValidateHex */, |
| 641 | FALSE /* fIsToken */, |
| 642 | m_pAssemblyIdentity->m_publicKeyOrTokenBLOB); |
| 643 | } |
| 644 | } |
| 645 | else if (EqualsCaseInsensitive(attributeString, W("processorarchitecture" ))) |
| 646 | { |
| 647 | PEKIND kProcessorArchitecture = peNone; |
| 648 | |
| 649 | GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE); |
| 650 | GO_IF_WILDCARD(valueString); |
| 651 | |
| 652 | if (ValidateAndConvertProcessorArchitecture(valueString, &kProcessorArchitecture)) |
| 653 | { |
| 654 | m_pAssemblyIdentity->m_kProcessorArchitecture = kProcessorArchitecture; |
| 655 | m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE); |
| 656 | } |
| 657 | else |
| 658 | { |
| 659 | fIsValid = FALSE; |
| 660 | } |
| 661 | } |
| 662 | else if (EqualsCaseInsensitive(attributeString, W("retargetable" ))) |
| 663 | { |
| 664 | GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE); |
| 665 | |
| 666 | if (EqualsCaseInsensitive(valueString, W("yes" ))) |
| 667 | { |
| 668 | m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE); |
| 669 | } |
| 670 | else if (!EqualsCaseInsensitive(valueString, W("no" ))) |
| 671 | { |
| 672 | fIsValid = FALSE; |
| 673 | } |
| 674 | } |
| 675 | else if (EqualsCaseInsensitive(attributeString, W("contenttype" ))) |
| 676 | { |
| 677 | GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE); |
| 678 | GO_IF_WILDCARD(valueString); |
| 679 | |
| 680 | if (EqualsCaseInsensitive(valueString, W("windowsruntime" ))) |
| 681 | { |
| 682 | m_pAssemblyIdentity->m_kContentType = AssemblyContentType_WindowsRuntime; |
| 683 | m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE); |
| 684 | } |
| 685 | else |
| 686 | { |
| 687 | fIsValid = FALSE; |
| 688 | } |
| 689 | } |
| 690 | else if (EqualsCaseInsensitive(attributeString, W("custom" ))) |
| 691 | { |
| 692 | GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_CUSTOM); |
| 693 | |
| 694 | if (EqualsCaseInsensitive(valueString, W("null" ))) |
| 695 | { |
| 696 | m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_CUSTOM_NULL); |
| 697 | } |
| 698 | else |
| 699 | { |
| 700 | GO_IF_VALIDATE_FAILED(ValidateHex, AssemblyIdentity::IDENTITY_FLAG_CUSTOM); |
| 701 | HexToBlob(valueString, |
| 702 | FALSE /* fValidateHex */, |
| 703 | FALSE /* fIsToken */, |
| 704 | m_pAssemblyIdentity->m_customBLOB); |
| 705 | } |
| 706 | } |
| 707 | else |
| 708 | { |
| 709 | // Fusion compat: Silently drop unknown attribute/value pair |
| 710 | BINDER_LOG_STRING(W("unknown attribute" ), attributeString); |
| 711 | BINDER_LOG_STRING(W("unknown value" ), valueString); |
| 712 | } |
| 713 | |
| 714 | Exit: |
| 715 | BINDER_LOG_LEAVE_HR(W("TextualIdentityParser::PopulateAssemblyIdentity" ), |
| 716 | (fIsValid ? S_OK : S_FALSE)); |
| 717 | return fIsValid; |
| 718 | } |
| 719 | |
| 720 | /* static */ |
| 721 | void TextualIdentityParser::EscapeString(SString &input, |
| 722 | SString &result) |
| 723 | { |
| 724 | BINDER_LOG_ENTER(W("TextualIdentityParser::EscapeString" )); |
| 725 | |
| 726 | BINDER_LOG_STRING(W("input" ), input); |
| 727 | |
| 728 | BOOL fNeedQuotes = FALSE; |
| 729 | WCHAR wcQuoteCharacter = W('"'); |
| 730 | |
| 731 | SmallStackSString tmpString; |
| 732 | SString::Iterator cursor = input.Begin(); |
| 733 | SString::Iterator end = input.End() - 1; |
| 734 | |
| 735 | // Leading/Trailing white space require quotes |
| 736 | if (IsWhitespace(cursor[0]) || IsWhitespace(end[0])) |
| 737 | { |
| 738 | fNeedQuotes = TRUE; |
| 739 | } |
| 740 | |
| 741 | // Fusion textual identity compat: escape all non-quote characters even if quoted |
| 742 | while (cursor <= end) |
| 743 | { |
| 744 | WCHAR wcCurrentChar = cursor[0]; |
| 745 | |
| 746 | switch (wcCurrentChar) |
| 747 | { |
| 748 | case W('"'): |
| 749 | case W('\''): |
| 750 | if (fNeedQuotes && (wcQuoteCharacter != wcCurrentChar)) |
| 751 | { |
| 752 | tmpString.Append(wcCurrentChar); |
| 753 | } |
| 754 | else if (!fNeedQuotes) |
| 755 | { |
| 756 | fNeedQuotes = TRUE; |
| 757 | wcQuoteCharacter = (wcCurrentChar == W('"') ? W('\'') : W('"')); |
| 758 | tmpString.Append(wcCurrentChar); |
| 759 | } |
| 760 | else |
| 761 | { |
| 762 | tmpString.Append(W('\\')); |
| 763 | tmpString.Append(wcCurrentChar); |
| 764 | } |
| 765 | break; |
| 766 | case W('='): |
| 767 | case W(','): |
| 768 | case W('\\'): |
| 769 | tmpString.Append(W('\\')); |
| 770 | tmpString.Append(wcCurrentChar); |
| 771 | break; |
| 772 | case 9: |
| 773 | tmpString.Append(W("\\t" )); |
| 774 | break; |
| 775 | case 10: |
| 776 | tmpString.Append(W("\\n" )); |
| 777 | break; |
| 778 | case 13: |
| 779 | tmpString.Append(W("\\r" )); |
| 780 | break; |
| 781 | default: |
| 782 | tmpString.Append(wcCurrentChar); |
| 783 | break; |
| 784 | } |
| 785 | |
| 786 | cursor++; |
| 787 | } |
| 788 | |
| 789 | if (fNeedQuotes) |
| 790 | { |
| 791 | result.Clear(); |
| 792 | result.Append(wcQuoteCharacter); |
| 793 | result.Append(tmpString); |
| 794 | result.Append(wcQuoteCharacter); |
| 795 | } |
| 796 | else |
| 797 | { |
| 798 | result.Set(tmpString); |
| 799 | } |
| 800 | |
| 801 | BINDER_LOG_LEAVE(W("TextualIdentityParser::EscapeString" )); |
| 802 | } |
| 803 | }; |
| 804 | |