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