Fix trigger for tier 1 call counting delay (#17477)
[platform/upstream/coreclr.git] / src / binder / textualidentityparser.cpp
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 *pkProcessorAchitecture)
163         {
164             for (int i = LENGTH_OF(wszKnownArchitectures); i--;)
165             {
166                 if (EqualsCaseInsensitive(processorArchitecture, wszKnownArchitectures[i].strValue))
167                 {
168                     *pkProcessorAchitecture = 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 };