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 | |