Fix null sessions in HttpContextWrapper.Session
[mono.git] / mcs / class / IKVM.Reflection / Fusion.cs
1 /*
2   Copyright (C) 2010-2012 Jeroen Frijters
3   Copyright (C) 2011 Marek Safar
4
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20
21   Jeroen Frijters
22   jeroen@frijters.net
23   
24 */
25
26 using System;
27 using System.Runtime.InteropServices;
28 using System.Text;
29
30 namespace IKVM.Reflection
31 {
32         struct ParsedAssemblyName
33         {
34                 internal string Name;
35                 internal Version Version;
36                 internal string Culture;
37                 internal string PublicKeyToken;
38                 internal bool? Retargetable;
39                 internal ProcessorArchitecture ProcessorArchitecture;
40                 internal bool HasPublicKey;
41                 internal bool WindowsRuntime;
42         }
43
44         enum ParseAssemblyResult
45         {
46                 OK,
47                 GenericError,
48                 DuplicateKey,
49         }
50
51         static class Fusion
52         {
53                 internal static bool CompareAssemblyIdentityNative(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
54                 {
55                         bool equivalent;
56                         Marshal.ThrowExceptionForHR(CompareAssemblyIdentity(assemblyIdentity1, unified1, assemblyIdentity2, unified2, out equivalent, out result));
57                         return equivalent;
58                 }
59
60                 [DllImport("fusion", CharSet = CharSet.Unicode)]
61                 private static extern int CompareAssemblyIdentity(string pwzAssemblyIdentity1, bool fUnified1, string pwzAssemblyIdentity2, bool fUnified2, out bool pfEquivalent, out AssemblyComparisonResult pResult);
62
63                 // internal for use by mcs
64                 internal static bool CompareAssemblyIdentityPure(string assemblyIdentity1, bool unified1, string assemblyIdentity2, bool unified2, out AssemblyComparisonResult result)
65                 {
66                         ParsedAssemblyName name1;
67                         ParsedAssemblyName name2;
68
69                         ParseAssemblyResult r = ParseAssemblyName(assemblyIdentity1, out name1);
70                         if (r != ParseAssemblyResult.OK || (r = ParseAssemblyName(assemblyIdentity2, out name2)) != ParseAssemblyResult.OK)
71                         {
72                                 result = AssemblyComparisonResult.NonEquivalent;
73                                 switch (r)
74                                 {
75                                         case ParseAssemblyResult.DuplicateKey:
76                                                 throw new System.IO.FileLoadException();
77                                         case ParseAssemblyResult.GenericError:
78                                         default:
79                                                 throw new ArgumentException();
80                                 }
81                         }
82
83                         bool partial = IsPartial(name1);
84
85                         if ((partial && unified1) || IsPartial(name2))
86                         {
87                                 result = AssemblyComparisonResult.NonEquivalent;
88                                 throw new ArgumentException();
89                         }
90                         if (!name1.Name.Equals(name2.Name, StringComparison.InvariantCultureIgnoreCase))
91                         {
92                                 result = AssemblyComparisonResult.NonEquivalent;
93                                 return false;
94                         }
95                         if (name1.Name.Equals("mscorlib", StringComparison.InvariantCultureIgnoreCase))
96                         {
97                                 result = AssemblyComparisonResult.EquivalentFullMatch;
98                                 return true;
99                         }
100                         if (partial && name1.Culture == null)
101                         {
102                         }
103                         else if (!name1.Culture.Equals(name2.Culture, StringComparison.InvariantCultureIgnoreCase))
104                         {
105                                 result = AssemblyComparisonResult.NonEquivalent;
106                                 return false;
107                         }
108                         if (IsStrongNamed(name2))
109                         {
110                                 if (partial && name1.PublicKeyToken == null)
111                                 {
112                                 }
113                                 else if (name1.PublicKeyToken != name2.PublicKeyToken)
114                                 {
115                                         result = AssemblyComparisonResult.NonEquivalent;
116                                         return false;
117                                 }
118                                 if (partial && name1.Version == null)
119                                 {
120                                         result = AssemblyComparisonResult.EquivalentPartialMatch;
121                                         return true;
122                                 }
123                                 else if (IsFrameworkAssembly(name2))
124                                 {
125                                         result = partial ? AssemblyComparisonResult.EquivalentPartialFXUnified : AssemblyComparisonResult.EquivalentFXUnified;
126                                         return true;
127                                 }
128                                 else if (name1.Version.Revision == -1 || name2.Version.Revision == -1)
129                                 {
130                                         result = AssemblyComparisonResult.NonEquivalent;
131                                         throw new ArgumentException();
132                                 }
133                                 else if (name1.Version < name2.Version)
134                                 {
135                                         if (unified2)
136                                         {
137                                                 result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
138                                                 return true;
139                                         }
140                                         else
141                                         {
142                                                 result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
143                                                 return false;
144                                         }
145                                 }
146                                 else if (name1.Version > name2.Version)
147                                 {
148                                         if (unified1)
149                                         {
150                                                 result = partial ? AssemblyComparisonResult.EquivalentPartialUnified : AssemblyComparisonResult.EquivalentUnified;
151                                                 return true;
152                                         }
153                                         else
154                                         {
155                                                 result = partial ? AssemblyComparisonResult.NonEquivalentPartialVersion : AssemblyComparisonResult.NonEquivalentVersion;
156                                                 return false;
157                                         }
158                                 }
159                                 else
160                                 {
161                                         result = partial ? AssemblyComparisonResult.EquivalentPartialMatch : AssemblyComparisonResult.EquivalentFullMatch;
162                                         return true;
163                                 }
164                         }
165                         else if (IsStrongNamed(name1))
166                         {
167                                 result = AssemblyComparisonResult.NonEquivalent;
168                                 return false;
169                         }
170                         else
171                         {
172                                 result = partial ? AssemblyComparisonResult.EquivalentPartialWeakNamed : AssemblyComparisonResult.EquivalentWeakNamed;
173                                 return true;
174                         }
175                 }
176
177                 static bool IsFrameworkAssembly(ParsedAssemblyName name)
178                 {
179                         // A list of FX assemblies which require some form of remapping
180                         // When 4.0 + 1 version  is release, assemblies introduced in v4.0
181                         // will have to be added
182                         switch (name.Name)
183                         {
184                                 case "System":
185                                 case "System.Core":
186                                 case "System.Data":
187                                 case "System.Data.DataSetExtensions":
188                                 case "System.Data.Linq":
189                                 case "System.Data.OracleClient":
190                                 case "System.Data.Services":
191                                 case "System.Data.Services.Client":
192                                 case "System.IdentityModel":
193                                 case "System.IdentityModel.Selectors":
194                                 case "System.Runtime.Remoting":
195                                 case "System.Runtime.Serialization":
196                                 case "System.ServiceModel":
197                                 case "System.Transactions":
198                                 case "System.Windows.Forms":
199                                 case "System.Xml":
200                                 case "System.Xml.Linq":
201                                         return name.PublicKeyToken == "b77a5c561934e089";
202
203                                 case "System.Configuration":
204                                 case "System.Configuration.Install":
205                                 case "System.Design":
206                                 case "System.DirectoryServices":
207                                 case "System.Drawing":
208                                 case "System.Drawing.Design":
209                                 case "System.EnterpriseServices":
210                                 case "System.Management":
211                                 case "System.Messaging":
212                                 case "System.Runtime.Serialization.Formatters.Soap":
213                                 case "System.Security":
214                                 case "System.ServiceProcess":
215                                 case "System.Web":
216                                 case "System.Web.Mobile":
217                                 case "System.Web.Services":
218                                         return name.PublicKeyToken == "b03f5f7f11d50a3a";
219
220                                 case "System.ComponentModel.DataAnnotations":
221                                 case "System.ServiceModel.Web":
222                                 case "System.Web.Abstractions":
223                                 case "System.Web.Extensions":
224                                 case "System.Web.Extensions.Design":
225                                 case "System.Web.DynamicData":
226                                 case "System.Web.Routing":
227                                         return name.PublicKeyToken == "31bf3856ad364e35";
228                         }
229
230                         return false;
231                 }
232
233                 internal static ParseAssemblyResult ParseAssemblySimpleName(string fullName, out int pos, out string simpleName)
234                 {
235                         StringBuilder sb = new StringBuilder();
236                         pos = 0;
237                         simpleName = null;
238                         while (pos < fullName.Length && char.IsWhiteSpace(fullName[pos]))
239                         {
240                                 pos++;
241                         }
242                         char quoteOrComma = ',';
243                         if (pos < fullName.Length && (fullName[pos] == '\"' || fullName[pos] == '\''))
244                         {
245                                 quoteOrComma = fullName[pos++];
246                         }
247                         while (pos < fullName.Length)
248                         {
249                                 char ch = fullName[pos++];
250                                 if (ch == '\\')
251                                 {
252                                         if (pos == fullName.Length)
253                                         {
254                                                 return ParseAssemblyResult.GenericError;
255                                         }
256                                         ch = fullName[pos++];
257                                         if (ch == '\\')
258                                         {
259                                                 return ParseAssemblyResult.GenericError;
260                                         }
261                                 }
262                                 else if (ch == quoteOrComma)
263                                 {
264                                         if (ch != ',')
265                                         {
266                                                 while (pos != fullName.Length)
267                                                 {
268                                                         ch = fullName[pos++];
269                                                         if (ch == ',')
270                                                         {
271                                                                 break;
272                                                         }
273                                                         if (!char.IsWhiteSpace(ch))
274                                                         {
275                                                                 return ParseAssemblyResult.GenericError;
276                                                         }
277                                                 }
278                                         }
279                                         break;
280                                 }
281                                 else if (ch == '=' || (quoteOrComma == ',' && (ch == '\'' || ch == '"')))
282                                 {
283                                         return ParseAssemblyResult.GenericError;
284                                 }
285                                 sb.Append(ch);
286                         }
287                         simpleName = sb.ToString().Trim();
288                         if (simpleName.Length == 0)
289                         {
290                                 return ParseAssemblyResult.GenericError;
291                         }
292                         if (pos == fullName.Length && fullName[fullName.Length - 1] == ',')
293                         {
294                                 return ParseAssemblyResult.GenericError;
295                         }
296                         return ParseAssemblyResult.OK;
297                 }
298
299                 internal static ParseAssemblyResult ParseAssemblyName(string fullName, out ParsedAssemblyName parsedName)
300                 {
301                         parsedName = new ParsedAssemblyName();
302                         int pos;
303                         ParseAssemblyResult res = ParseAssemblySimpleName(fullName, out pos, out parsedName.Name);
304                         if (res != ParseAssemblyResult.OK || pos == fullName.Length)
305                         {
306                                 return res;
307                         }
308                         else
309                         {
310                                 System.Collections.Generic.Dictionary<string, string> unknownAttributes = null;
311                                 bool hasProcessorArchitecture = false;
312                                 bool hasContentType = false;
313                                 string[] parts = fullName.Substring(pos).Split(',');
314                                 for (int i = 0; i < parts.Length; i++)
315                                 {
316                                         string[] kv = parts[i].Split('=');
317                                         if (kv.Length != 2)
318                                         {
319                                                 return ParseAssemblyResult.GenericError;
320                                         }
321                                         switch (kv[0].Trim().ToLowerInvariant())
322                                         {
323                                                 case "version":
324                                                         if (parsedName.Version != null)
325                                                         {
326                                                                 return ParseAssemblyResult.DuplicateKey;
327                                                         }
328                                                         if (!ParseVersion(kv[1].Trim(), out parsedName.Version))
329                                                         {
330                                                                 return ParseAssemblyResult.GenericError;
331                                                         }
332                                                         break;
333                                                 case "culture":
334                                                         if (parsedName.Culture != null)
335                                                         {
336                                                                 return ParseAssemblyResult.DuplicateKey;
337                                                         }
338                                                         if (!ParseCulture(kv[1].Trim(), out parsedName.Culture))
339                                                         {
340                                                                 return ParseAssemblyResult.GenericError;
341                                                         }
342                                                         break;
343                                                 case "publickeytoken":
344                                                         if (parsedName.PublicKeyToken != null)
345                                                         {
346                                                                 return ParseAssemblyResult.DuplicateKey;
347                                                         }
348                                                         if (!ParsePublicKeyToken(kv[1].Trim(), out parsedName.PublicKeyToken))
349                                                         {
350                                                                 return ParseAssemblyResult.GenericError;
351                                                         }
352                                                         break;
353                                                 case "publickey":
354                                                         if (parsedName.PublicKeyToken != null)
355                                                         {
356                                                                 return ParseAssemblyResult.DuplicateKey;
357                                                         }
358                                                         if (!ParsePublicKey(kv[1].Trim(), out parsedName.PublicKeyToken))
359                                                         {
360                                                                 return ParseAssemblyResult.GenericError;
361                                                         }
362                                                         parsedName.HasPublicKey = true;
363                                                         break;
364                                                 case "retargetable":
365                                                         if (parsedName.Retargetable.HasValue)
366                                                         {
367                                                                 return ParseAssemblyResult.DuplicateKey;
368                                                         }
369                                                         switch (kv[1].Trim().ToLowerInvariant())
370                                                         {
371                                                                 case "yes":
372                                                                         parsedName.Retargetable = true;
373                                                                         break;
374                                                                 case "no":
375                                                                         parsedName.Retargetable = false;
376                                                                         break;
377                                                                 default:
378                                                                         return ParseAssemblyResult.GenericError;
379                                                         }
380                                                         break;
381                                                 case "processorarchitecture":
382                                                         if (hasProcessorArchitecture)
383                                                         {
384                                                                 return ParseAssemblyResult.DuplicateKey;
385                                                         }
386                                                         hasProcessorArchitecture = true;
387                                                         switch (kv[1].Trim().ToLowerInvariant())
388                                                         {
389                                                                 case "none":
390                                                                         parsedName.ProcessorArchitecture = ProcessorArchitecture.None;
391                                                                         break;
392                                                                 case "msil":
393                                                                         parsedName.ProcessorArchitecture = ProcessorArchitecture.MSIL;
394                                                                         break;
395                                                                 case "x86":
396                                                                         parsedName.ProcessorArchitecture = ProcessorArchitecture.X86;
397                                                                         break;
398                                                                 case "ia64":
399                                                                         parsedName.ProcessorArchitecture = ProcessorArchitecture.IA64;
400                                                                         break;
401                                                                 case "amd64":
402                                                                         parsedName.ProcessorArchitecture = ProcessorArchitecture.Amd64;
403                                                                         break;
404                                                                 case "arm":
405                                                                         parsedName.ProcessorArchitecture = ProcessorArchitecture.Arm;
406                                                                         break;
407                                                                 default:
408                                                                         return ParseAssemblyResult.GenericError;
409                                                         }
410                                                         break;
411                                                 case "contenttype":
412                                                         if (hasContentType)
413                                                         {
414                                                                 return ParseAssemblyResult.DuplicateKey;
415                                                         }
416                                                         hasContentType = true;
417                                                         if (kv[1].Trim().ToLowerInvariant() != "windowsruntime")
418                                                         {
419                                                                 return ParseAssemblyResult.GenericError;
420                                                         }
421                                                         parsedName.WindowsRuntime = true;
422                                                         break;
423                                                 default:
424                                                         if (kv[1].Trim() == "")
425                                                         {
426                                                                 return ParseAssemblyResult.GenericError;
427                                                         }
428                                                         if (unknownAttributes == null)
429                                                         {
430                                                                 unknownAttributes = new System.Collections.Generic.Dictionary<string, string>();
431                                                         }
432                                                         if (unknownAttributes.ContainsKey(kv[0].Trim().ToLowerInvariant()))
433                                                         {
434                                                                 return ParseAssemblyResult.DuplicateKey;
435                                                         }
436                                                         unknownAttributes.Add(kv[0].Trim().ToLowerInvariant(), null);
437                                                         break;
438                                         }
439                                 }
440                         }
441                         return ParseAssemblyResult.OK;
442                 }
443
444                 private static bool ParseVersion(string str, out Version version)
445                 {
446                         string[] parts = str.Split('.');
447                         if (parts.Length < 2 || parts.Length > 4)
448                         {
449                                 version = null;
450                                 ushort dummy;
451                                 // if the version consists of a single integer, it is invalid, but not invalid enough to fail the parse of the whole assembly name
452                                 return parts.Length == 1 && ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out dummy);
453                         }
454                         if (parts[0] == "" || parts[1] == "")
455                         {
456                                 // this is a strange scenario, the version is invalid, but not invalid enough to fail the parse of the whole assembly name
457                                 version = null;
458                                 return true;
459                         }
460                         ushort major, minor, build = 65535, revision = 65535;
461                         if (ushort.TryParse(parts[0], System.Globalization.NumberStyles.Integer, null, out major)
462                                 && ushort.TryParse(parts[1], System.Globalization.NumberStyles.Integer, null, out minor)
463                                 && (parts.Length <= 2 || parts[2] == "" || ushort.TryParse(parts[2], System.Globalization.NumberStyles.Integer, null, out build))
464                                 && (parts.Length <= 3 || parts[3] == "" || (parts[2] != "" && ushort.TryParse(parts[3], System.Globalization.NumberStyles.Integer, null, out revision))))
465                         {
466                                 if (parts.Length == 4 && parts[3] != "" && parts[2] != "")
467                                 {
468                                         version = new Version(major, minor, build, revision);
469                                 }
470                                 else if (parts.Length == 3 && parts[2] != "")
471                                 {
472                                         version = new Version(major, minor, build);
473                                 }
474                                 else
475                                 {
476                                         version = new Version(major, minor);
477                                 }
478                                 return true;
479                         }
480                         version = null;
481                         return false;
482                 }
483
484                 private static bool ParseCulture(string str, out string culture)
485                 {
486                         if (str == null)
487                         {
488                                 culture = null;
489                                 return false;
490                         }
491                         culture = str;
492                         return true;
493                 }
494
495                 private static bool ParsePublicKeyToken(string str, out string publicKeyToken)
496                 {
497                         if (str == null)
498                         {
499                                 publicKeyToken = null;
500                                 return false;
501                         }
502                         publicKeyToken = str.ToLowerInvariant();
503                         return true;
504                 }
505
506                 private static bool ParsePublicKey(string str, out string publicKeyToken)
507                 {
508                         if (str == null)
509                         {
510                                 publicKeyToken = null;
511                                 return false;
512                         }
513                         // HACK use AssemblyName to convert PublicKey to PublicKeyToken
514                         byte[] token = new System.Reflection.AssemblyName("Foo, PublicKey=" + str).GetPublicKeyToken();
515                         StringBuilder sb = new StringBuilder(token.Length * 2);
516                         for (int i = 0; i < token.Length; i++)
517                         {
518                                 sb.AppendFormat("{0:x2}", token[i]);
519                         }
520                         publicKeyToken = sb.ToString();
521                         return true;
522                 }
523
524                 private static bool IsPartial(ParsedAssemblyName name)
525                 {
526                         return name.Version == null || name.Culture == null || name.PublicKeyToken == null;
527                 }
528
529                 private static bool IsStrongNamed(ParsedAssemblyName name)
530                 {
531                         return name.PublicKeyToken != null && name.PublicKeyToken != "null";
532                 }
533         }
534 }