5916875e6729553c8cf48cec37a1562c3510ac63
[mono.git] / mcs / class / referencesource / mscorlib / system / security / policy / netcodegroup.cs
1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6 //  NetCodeGroup.cs
7 // 
8 // <OWNER>Microsoft</OWNER>
9 //
10 //  Representation for code groups used for the policy mechanism
11 //
12
13 namespace System.Security.Policy {
14
15     using System;
16     using System.Security.Util;
17     using System.Security;
18     using System.Collections;
19     using System.Reflection;
20     using System.Globalization;
21     using System.Runtime.Serialization;
22     using System.Runtime.Versioning;
23     using System.Diagnostics.Contracts;
24
25     //
26     //This is a simple property bag used to describe connect back access.
27     //
28     [Serializable]
29     [System.Runtime.InteropServices.ComVisible(true)]
30     public class CodeConnectAccess {
31         private string _LowerCaseScheme;
32         private string _LowerCasePort;
33         private int    _IntPort;
34
35         private const  string  DefaultStr = "$default";     //must remain in lower case
36         private const  string  OriginStr  = "$origin";  //must remain in lower case
37
38         internal const  int     NoPort  = -1;
39         internal const  int     AnyPort = -2; // This safely excludes -1 (if we decide to support "any" port later)
40
41         //
42         // public helper fields to deal with special scheme and port values.
43         //
44         public static readonly int     DefaultPort   = -3;
45         public static readonly int     OriginPort    = -4;
46         public static readonly string  OriginScheme  = OriginStr;
47         public static readonly string  AnyScheme     = "*";
48
49         //
50         // A set of public static class factories
51         //
52         public CodeConnectAccess(string allowScheme, int allowPort)
53         {
54             if (!IsValidScheme(allowScheme))
55                 throw new ArgumentOutOfRangeException("allowScheme");
56
57             SetCodeConnectAccess(allowScheme.ToLower(CultureInfo.InvariantCulture), allowPort);
58         }
59         //
60         public static CodeConnectAccess CreateOriginSchemeAccess(int allowPort)
61         {
62             CodeConnectAccess access = new CodeConnectAccess();
63             access.SetCodeConnectAccess(OriginScheme, allowPort);
64             return access;
65         }
66         //
67         public static CodeConnectAccess CreateAnySchemeAccess(int allowPort)
68         {
69             CodeConnectAccess access = new CodeConnectAccess();
70             access.SetCodeConnectAccess(AnyScheme, allowPort);
71             return access;
72         }
73         //
74         private CodeConnectAccess()
75         {
76         }
77         //
78         private void SetCodeConnectAccess(string lowerCaseScheme, int allowPort)
79         {
80             _LowerCaseScheme = lowerCaseScheme;
81
82             if (allowPort == DefaultPort)
83                 _LowerCasePort = DefaultStr;
84             else if (allowPort == OriginPort)
85                 _LowerCasePort = OriginStr;
86             else
87             {
88                 if (allowPort < 0 || allowPort > 0xFFFF)
89                     throw new ArgumentOutOfRangeException("allowPort");
90
91                 _LowerCasePort = allowPort.ToString(CultureInfo.InvariantCulture);
92             }
93
94             _IntPort = allowPort;
95         }
96         //
97         public String   Scheme {
98             get {return _LowerCaseScheme;}
99         }
100         //
101         public int      Port {
102             get {return _IntPort;}
103         }
104         //
105         public override bool Equals(object o)
106         {
107             if ((object)this == (object)o)
108                 return true;
109
110             CodeConnectAccess that = (o as CodeConnectAccess);
111
112             if (that == null)
113                 return false;
114
115             return this.Scheme == that.Scheme && this.Port == that.Port;
116         }
117         //
118         public override int GetHashCode()
119         {
120             return Scheme.GetHashCode() + Port.GetHashCode();
121         }
122         //
123         // internal stuff
124         //
125         //
126         // The valid scheme values are: "*", "$origin", or a valid Uri scheme
127         // The valid port valies are "$origin" "$default" or a valid Uri port
128         //
129         internal CodeConnectAccess(string allowScheme, string allowPort)
130         {
131             if (allowScheme == null || allowScheme.Length == 0)
132                 throw new ArgumentNullException("allowScheme");
133
134             if (allowPort == null || allowPort.Length == 0)
135                 throw new ArgumentNullException("allowPort");
136             Contract.EndContractBlock();
137
138             _LowerCaseScheme = allowScheme.ToLower(CultureInfo.InvariantCulture);
139
140             if (_LowerCaseScheme == OriginScheme)
141                 _LowerCaseScheme = OriginScheme;
142             else if (_LowerCaseScheme == AnyScheme)
143                 _LowerCaseScheme = AnyScheme;
144             else if (!IsValidScheme(_LowerCaseScheme))
145                 throw new ArgumentOutOfRangeException("allowScheme");
146
147             _LowerCasePort = allowPort.ToLower(CultureInfo.InvariantCulture);
148
149             if (_LowerCasePort == DefaultStr)
150                 _IntPort = DefaultPort;
151             else if (_LowerCasePort == OriginStr)
152                 _IntPort = OriginPort;
153             else
154             {
155                 _IntPort = Int32.Parse(allowPort, CultureInfo.InvariantCulture);
156
157                 if (_IntPort < 0 || _IntPort > 0xFFFF)
158                     throw new ArgumentOutOfRangeException("allowPort");
159
160                 _LowerCasePort = _IntPort.ToString(CultureInfo.InvariantCulture);
161             }
162         }
163         //
164         internal bool     IsOriginScheme {
165             get {return (object)_LowerCaseScheme == (object)OriginScheme;}
166         }
167         //
168         internal bool     IsAnyScheme {
169             get {return (object)_LowerCaseScheme == (object)AnyScheme;}
170         }
171         //
172         internal bool     IsDefaultPort {
173             get {return Port == DefaultPort;}
174         }
175         //
176         internal bool     IsOriginPort {
177             get {return Port == OriginPort;}
178         }
179         //
180         // More Internal stuff
181         //
182         internal string  StrPort {
183             get { return _LowerCasePort;}
184         }
185         //
186         [Pure]
187         internal static bool IsValidScheme(string scheme)
188         {
189             if (((object)scheme == null) || (scheme.Length == 0) || !IsAsciiLetter(scheme[0]))
190                 return false;
191
192             for (int i = scheme.Length - 1; i > 0; --i) {
193                 if (!(IsAsciiLetterOrDigit(scheme[i]) || (scheme[i] == '+') || (scheme[i] == '-') || (scheme[i] == '.')))
194                     return false;
195             }
196             return true;
197         }
198         //
199         [Pure]
200         private static bool IsAsciiLetterOrDigit(char character) {
201             return IsAsciiLetter(character) || (character >= '0' && character <= '9');
202         }
203         //
204         [Pure]
205         private static bool IsAsciiLetter(char character) {
206             return (character >= 'a' && character <= 'z') ||
207                    (character >= 'A' && character <= 'Z');
208         }
209     }
210
211     [Serializable]
212 [System.Runtime.InteropServices.ComVisible(true)]
213     sealed public class NetCodeGroup : CodeGroup, IUnionSemanticCodeGroup
214     {
215         [System.Security.SecurityCritical]  // auto-generated
216         [System.Diagnostics.Conditional( "_DEBUG" )]
217         [ResourceExposure(ResourceScope.None)]
218         [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
219         private static void DEBUG_OUT( String str )
220         {
221 #if _DEBUG
222             if (debug)
223             {
224                 if (to_file)
225                 {
226                     System.Text.StringBuilder sb = new System.Text.StringBuilder();
227                     sb.Append( str );
228                     sb.Append ((char)13) ;
229                     sb.Append ((char)10) ;
230                     PolicyManager.DebugOut( file, sb.ToString() );
231                 }
232                 else
233                     Console.WriteLine( str );
234              }
235 #endif
236         }
237
238 #if _DEBUG
239         private static bool debug;
240         private static readonly bool to_file;
241         private const String file = "c:\\com99\\src\\bcl\\debug.txt";
242 #endif
243
244         [OptionalField(VersionAdded = 2)]
245         private ArrayList           m_schemesList;
246         [OptionalField(VersionAdded = 2)]
247         private ArrayList           m_accessList;
248
249         [OnDeserializing]
250         private void OnDeserializing(StreamingContext ctx)
251         {
252             m_schemesList = null;
253             m_accessList = null;
254             
255             
256         }
257         
258
259         private const string c_IgnoreUserInfo = ""; // don't need anymore since WebPermission will ignore userinfo anyway. was: @"([^\\/?#]*@)?";
260         // not exactly correct syntax but should work fine assuming System.Uri will not accept bogus Uri schemes
261         private const string c_AnyScheme = @"([0-9a-z+\-\.]+)://";
262
263         private static readonly char[] c_SomeRegexChars = new char[] {'.', '-', '+', '[', ']', /* rest are unc-only*/ '{', '$', '^', '#', ')', '(', ' '};
264
265         public static readonly string  AnyOtherOriginScheme= CodeConnectAccess.AnyScheme;
266         public static readonly string  AbsentOriginScheme  = string.Empty;
267
268         internal NetCodeGroup()
269             : base()
270         {
271             SetDefaults();
272         }
273
274         public NetCodeGroup( IMembershipCondition membershipCondition )
275             : base( membershipCondition, (PolicyStatement)null )
276         {
277             SetDefaults();
278         }
279
280         //
281         // Reset the talkback access to nothing.
282         // When a new instance of NetCodeGroup is created it's populated with default talkback rules
283         //
284         public void ResetConnectAccess()
285         {
286             m_schemesList = null;
287             m_accessList = null;
288         }
289         //
290         // Added public stuff for programmatic support of the talkback access
291         // The connectAccess can be null means an empty access (no access) is added
292         //
293         public void AddConnectAccess(string originScheme, CodeConnectAccess connectAccess)
294         {
295             if (originScheme == null)
296                 throw new ArgumentNullException("originScheme");
297             Contract.EndContractBlock();
298
299             if (originScheme != AbsentOriginScheme && originScheme != AnyOtherOriginScheme && !CodeConnectAccess.IsValidScheme(originScheme))
300                 throw new ArgumentOutOfRangeException("originScheme");
301
302             if (originScheme == AbsentOriginScheme && connectAccess.IsOriginScheme)
303                 throw new ArgumentOutOfRangeException("connectAccess");
304
305             if (m_schemesList == null)
306             {
307                 m_schemesList = new ArrayList();
308                 m_accessList = new ArrayList();
309             }
310
311             originScheme = originScheme.ToLower(CultureInfo.InvariantCulture);
312
313             for (int i=0; i < m_schemesList.Count; ++i)
314             {
315                 if ((string)m_schemesList[i] == originScheme)
316                 {
317                     // originScheme entry is found and we may want to add nothing to it.
318                     if (connectAccess == null)
319                         return;
320
321                     ArrayList list = (ArrayList)m_accessList[i];
322                     for (i = 0; i < list.Count; ++i)
323                     {
324                         if (((CodeConnectAccess)list[i]).Equals(connectAccess))
325                             return;
326                     }
327                     list.Add(connectAccess);
328                     return;
329                 }
330             }
331
332             // originScheme entry is not found, create a new one.
333             m_schemesList.Add(originScheme);
334             ArrayList newOriginSchemeList = new ArrayList();
335             m_accessList.Add(newOriginSchemeList);
336
337             // we may want to keep it empty.
338             if (connectAccess != null)
339                 newOriginSchemeList.Add(connectAccess);
340
341         }
342         //
343         // Each DictionaryEntry will contain
344         // Key=originScheme and Value=CodeConnectAccess[] array
345         //
346         public DictionaryEntry[] GetConnectAccessRules()
347         {
348             if (m_schemesList == null)
349                 return null;
350
351             DictionaryEntry[] result = new DictionaryEntry[m_schemesList.Count];
352             for (int i = 0; i < result.Length; ++i)
353             {
354                 result[i].Key = m_schemesList[i];
355                 result[i].Value = ((ArrayList)m_accessList[i]).ToArray(typeof(CodeConnectAccess));
356             }
357             return result;
358         }
359
360         [System.Security.SecuritySafeCritical]  // auto-generated
361         public override PolicyStatement Resolve( Evidence evidence )
362         {
363             if (evidence == null)
364                 throw new ArgumentNullException("evidence");
365             Contract.EndContractBlock();
366
367             object usedEvidence = null;
368             if (PolicyManager.CheckMembershipCondition(MembershipCondition,
369                                                        evidence,
370                                                        out usedEvidence))
371             {
372                 PolicyStatement thisPolicy = CalculateAssemblyPolicy( evidence );
373
374                 // If any delay-evidence was used to generate this grant set, then we need to keep track of
375                 // that for potentially later forcing it to be verified.
376                 IDelayEvaluatedEvidence delayEvidence = usedEvidence as IDelayEvaluatedEvidence;
377                 bool delayEvidenceNeedsVerification = delayEvidence != null && !delayEvidence.IsVerified;
378                 if (delayEvidenceNeedsVerification)
379                 {
380                     thisPolicy.AddDependentEvidence(delayEvidence);
381                 }
382
383                 bool foundExclusiveChild = false;
384                 IEnumerator enumerator = this.Children.GetEnumerator();
385                 while (enumerator.MoveNext() && !foundExclusiveChild)
386                 {
387                     PolicyStatement childPolicy = PolicyManager.ResolveCodeGroup(enumerator.Current as CodeGroup,
388                                                                                  evidence);
389                     if (childPolicy != null)
390                     {
391                         thisPolicy.InplaceUnion(childPolicy);
392
393                         if ((childPolicy.Attributes & PolicyStatementAttribute.Exclusive) == PolicyStatementAttribute.Exclusive)
394                         {
395                             foundExclusiveChild = true;
396                         }
397                     }
398                 }
399
400                 return thisPolicy;
401             }
402             else
403             {
404                 return null;
405             }
406         }
407
408         /// <internalonly/>
409         PolicyStatement IUnionSemanticCodeGroup.InternalResolve( Evidence evidence )
410         {
411             if (evidence == null)
412                 throw new ArgumentNullException("evidence");
413
414             Contract.EndContractBlock();
415
416             if (this.MembershipCondition.Check( evidence ))
417             {
418                 return CalculateAssemblyPolicy( evidence );
419             }
420
421             return null;
422         }
423
424         public override CodeGroup ResolveMatchingCodeGroups( Evidence evidence )
425         {
426             if (evidence == null)
427                 throw new ArgumentNullException("evidence");
428             Contract.EndContractBlock();
429
430             if (this.MembershipCondition.Check( evidence ))
431             {
432                 CodeGroup retGroup = this.Copy();
433
434                 retGroup.Children = new ArrayList();
435
436                 IEnumerator enumerator = this.Children.GetEnumerator();
437
438                 while (enumerator.MoveNext())
439                 {
440                     CodeGroup matchingGroups = ((CodeGroup)enumerator.Current).ResolveMatchingCodeGroups( evidence );
441
442                     // If the child has a policy, we are done.
443
444                     if (matchingGroups != null)
445                     {
446                         retGroup.AddChild( matchingGroups );
447                     }
448                 }
449
450                 return retGroup;
451
452             }
453             else
454             {
455                 return null;
456             }
457         }
458         //
459         private string EscapeStringForRegex( string str )
460         {
461             int start = 0;
462             int idx;
463             System.Text.StringBuilder sb = null;
464
465             while (start < str.Length && (idx = str.IndexOfAny(c_SomeRegexChars, start)) != -1)
466             {
467                 if (sb == null) sb = new System.Text.StringBuilder(str.Length*2);
468                 sb.Append(str, start, idx - start).Append('\\').Append(str[idx]);
469                 start = idx+1;
470             }
471             if (sb == null)
472                 return str;
473
474             if (start < str.Length)
475                 sb.Append(str, start, str.Length - start);
476
477             return sb.ToString();
478         }
479
480
481         internal SecurityElement CreateWebPermission(string host,
482                                                      string scheme,
483                                                      string port,
484                                                      string assemblyOverride)
485         {
486             if (scheme == null)
487                 scheme = string.Empty;
488
489             // If there is no OriginScheme host string, no talk back access is possible
490             if (host == null || host.Length == 0)
491                 return null;
492
493             host = host.ToLower(CultureInfo.InvariantCulture);
494             scheme = scheme.ToLower(CultureInfo.InvariantCulture);
495
496             int intPort = CodeConnectAccess.NoPort;
497             if (port != null && port.Length != 0)
498                 intPort = Int32.Parse(port, CultureInfo.InvariantCulture );
499             else
500                 port = string.Empty;
501
502             CodeConnectAccess[] access = FindAccessRulesForScheme(scheme);
503             if (access == null || access.Length == 0)
504                 return null;
505
506             SecurityElement root = new SecurityElement( "IPermission" );
507
508             // If we were given a specific assembly to find the WebPermission type in use that, otherwise use
509             // the current version of System.dll.  This enables us to build WebPermissions targeting older
510             // runtimes for ClickOnce trust decisions that need to target the older runtime.
511             string permissionAssembly = assemblyOverride == null ?
512                 "System, Version=" + ThisAssembly.Version + ", Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken :
513                 assemblyOverride;
514
515             root.AddAttribute( "class", "System.Net.WebPermission, " + permissionAssembly);
516             root.AddAttribute( "version", "1" );
517
518             SecurityElement connectAccess = new SecurityElement( "ConnectAccess" );
519
520             host = EscapeStringForRegex(host);
521             scheme = EscapeStringForRegex(scheme);
522             string uriStr = TryPermissionAsOneString(access, scheme, host, intPort);
523
524             if (uriStr != null)
525             {
526                 SecurityElement uri = new SecurityElement( "URI" );
527                 uri.AddAttribute( "uri", uriStr );
528                 connectAccess.AddChild( uri );
529             }
530             else
531             {
532                 if (port.Length != 0)
533                     port = ":" + port;
534
535                 for (int i = 0; i < access.Length; ++i)
536                 {
537                     uriStr = GetPermissionAccessElementString(access[i], scheme, host, port);
538                     SecurityElement uri = new SecurityElement( "URI" );
539                     uri.AddAttribute( "uri", uriStr );
540                     connectAccess.AddChild( uri );
541                 }
542             }
543
544             root.AddChild( connectAccess );
545             return root;
546         }
547         //
548         //
549         //
550         private CodeConnectAccess[] FindAccessRulesForScheme(string lowerCaseScheme)
551         {
552             if (m_schemesList == null)
553                 return null;
554
555             int i = m_schemesList.IndexOf(lowerCaseScheme);
556             if (i == -1)
557             {
558                 // Trying default rule but only if the passed string is not about "no scheme case"
559                 if (lowerCaseScheme == AbsentOriginScheme || (i = m_schemesList.IndexOf(AnyOtherOriginScheme)) == -1)
560                     return null;
561             }
562
563             ArrayList accessList = (ArrayList)m_accessList[i];
564             return (CodeConnectAccess[])accessList.ToArray(typeof(CodeConnectAccess));
565         }
566         //
567         // This is an attempt to optimize resulting regex if the rules can be combined into one expression string
568         //
569         private string TryPermissionAsOneString(CodeConnectAccess[] access, string escapedScheme, string escapedHost, int intPort)
570         {
571             bool noPort = true;
572             bool originPort = true;
573             bool anyScheme = false;
574             int  sameCustomPort = CodeConnectAccess.AnyPort;
575
576             //
577             // We can compact rules in one regex if the destination port is the same for all granted accesses.
578             // We may have three cases (order is significant)
579             // - No port (empty) in the resulting rule
580             // - Origin server port that is intPort parameter
581             // - Some custom port that is the same for all accesses
582             //
583             for (int i = 0; i < access.Length; ++i)
584             {
585                 noPort    &= (access[i].IsDefaultPort || (access[i].IsOriginPort && intPort == CodeConnectAccess.NoPort));
586                 originPort&= (access[i].IsOriginPort  || access[i].Port == intPort);
587
588                 if (access[i].Port >= 0)
589                 {
590
591                     if (sameCustomPort == CodeConnectAccess.AnyPort)
592                     {
593                         sameCustomPort = access[i].Port;
594                     }
595                     else if (access[i].Port != sameCustomPort)
596                     {
597                         // found conflicting ports
598                         sameCustomPort = CodeConnectAccess.NoPort;
599                     }
600                 }
601                 else
602                 {
603                     // Cannot compress Regex if saw at least one "default port" access rule and another one with exact port.
604                     sameCustomPort = CodeConnectAccess.NoPort;
605                 }
606
607                 if (access[i].IsAnyScheme)
608                     anyScheme = true;
609             }
610
611             if (!noPort && !originPort && sameCustomPort == CodeConnectAccess.NoPort)
612                 return null;
613
614             // We can produce the resulting expression as one string
615             System.Text.StringBuilder sb = new System.Text.StringBuilder(c_AnyScheme.Length * access.Length + c_IgnoreUserInfo.Length*2 + escapedHost.Length);
616             if (anyScheme)
617                 sb.Append(c_AnyScheme);
618             else
619             {
620                 sb.Append('(');
621                 int i = 0;
622                 for (; i < access.Length; ++i)
623                 {
624                     // This is  to avoid output like (http|http|http)
625                     int k = 0;
626                     for (; k < i; ++k)
627                     {
628                         if (access[i].Scheme == access[k].Scheme)
629                             break;
630                     }
631                     if (k == i)
632                     {
633                         if (i != 0)
634                             sb.Append('|');
635                         sb.Append(access[i].IsOriginScheme? escapedScheme: EscapeStringForRegex(access[i].Scheme));
636                     }
637                 }
638                 sb.Append(")://");;
639             }
640
641             sb.Append(c_IgnoreUserInfo).Append(escapedHost);
642
643             if (noPort) {;}
644             else if (originPort) sb.Append(':').Append(intPort);
645             else sb.Append(':').Append(sameCustomPort);
646
647             sb.Append("/.*");
648             return sb.ToString();
649         }
650         //
651         // This tries to return a single element to be added into resulting WebPermission
652         // Returns Null if there is nothing to add.
653         //
654         private string GetPermissionAccessElementString(CodeConnectAccess access, string escapedScheme, string escapedHost, string strPort)
655         {
656             System.Text.StringBuilder sb = new System.Text.StringBuilder(c_AnyScheme.Length*2 + c_IgnoreUserInfo.Length + escapedHost.Length);
657
658             if (access.IsAnyScheme)
659                 sb.Append(c_AnyScheme);
660             else if (access.IsOriginScheme)
661                 sb.Append(escapedScheme).Append("://");
662             else
663                 sb.Append(EscapeStringForRegex(access.Scheme)).Append("://");
664
665             sb.Append(c_IgnoreUserInfo).Append(escapedHost);
666
667             if (access.IsDefaultPort) {;}
668             else if (access.IsOriginPort)
669                  sb.Append(strPort);
670             else sb.Append(':').Append(access.StrPort);
671
672             sb.Append("/.*");
673             return sb.ToString();
674         }
675
676         internal PolicyStatement CalculatePolicy( String host, String scheme, String port )
677         {
678             SecurityElement webPerm = CreateWebPermission( host, scheme, port, null );
679
680             SecurityElement root = new SecurityElement( "PolicyStatement" );
681             SecurityElement permSet = new SecurityElement( "PermissionSet" );
682             permSet.AddAttribute( "class", "System.Security.PermissionSet" );
683             permSet.AddAttribute( "version", "1" );
684
685             if (webPerm != null)
686                 permSet.AddChild( webPerm );
687
688             root.AddChild( permSet );
689
690             PolicyStatement policy = new PolicyStatement();
691             policy.FromXml( root );
692             return policy;
693         }
694
695         private PolicyStatement CalculateAssemblyPolicy( Evidence evidence )
696         {
697
698             PolicyStatement thisPolicy = null;
699
700             Url url = evidence.GetHostEvidence<Url>();
701             if (url != null)
702             {
703                 thisPolicy = CalculatePolicy( url.GetURLString().Host, url.GetURLString().Scheme, url.GetURLString().Port );
704             }
705
706             if (thisPolicy == null)
707             {
708                 Site site = evidence.GetHostEvidence<Site>();
709                 if (site != null)
710                 {
711                     thisPolicy = CalculatePolicy(site.Name, null, null);
712                 }
713             }
714
715             if (thisPolicy == null)
716                 thisPolicy = new PolicyStatement( new PermissionSet( false ), PolicyStatementAttribute.Nothing );
717
718             return thisPolicy;
719         }
720
721         public override CodeGroup Copy()
722         {
723             NetCodeGroup group = new NetCodeGroup( this.MembershipCondition );
724
725             group.Name = this.Name;
726             group.Description = this.Description;
727             if (m_schemesList != null)
728             {
729                 group.m_schemesList = (ArrayList)this.m_schemesList.Clone();
730                 group.m_accessList = new ArrayList(this.m_accessList.Count);
731                 for (int i = 0; i < this.m_accessList.Count; ++i)
732                     group.m_accessList.Add(((ArrayList)this.m_accessList[i]).Clone());
733             }
734
735             IEnumerator enumerator = this.Children.GetEnumerator();
736
737             while (enumerator.MoveNext())
738             {
739                 group.AddChild( (CodeGroup)enumerator.Current );
740             }
741
742
743             return group;
744         }
745
746         public override String MergeLogic
747         {
748             get
749             {
750                 return Environment.GetResourceString( "MergeLogic_Union" );
751             }
752         }
753
754         public override String PermissionSetName
755         {
756             get
757             {
758                 return Environment.GetResourceString( "NetCodeGroup_PermissionSet" );
759             }
760         }
761
762
763         public override String AttributeString
764         {
765             get
766             {
767                 return null;
768             }
769         }
770         //
771         public override bool Equals( Object o)
772         {
773             if ((object)this == (object)o)
774                 return true;
775
776             NetCodeGroup that = (o as NetCodeGroup);
777
778             if (that == null || !base.Equals(that))
779                 return false;
780
781             if ((this.m_schemesList == null) != (that.m_schemesList == null))
782                 return false;
783
784             if (this.m_schemesList == null)
785                 return true;
786
787             if (this.m_schemesList.Count != that.m_schemesList.Count)
788                 return false;
789
790
791             for (int i = 0; i < this.m_schemesList.Count; ++i)
792             {
793                 int idx = that.m_schemesList.IndexOf(this.m_schemesList[i]);
794                 if (idx == -1)
795                     return false;
796
797                 ArrayList thisList = (ArrayList)this.m_accessList[i];
798                 ArrayList thatList = (ArrayList)that.m_accessList[idx];
799                 if (thisList.Count != thatList.Count)
800                     return false;
801
802                 for (int k = 0; k < thisList.Count; ++k)
803                 {
804                     if (!thatList.Contains(thisList[k]))
805                         return false;
806                 }
807             }
808
809             return true;
810         }
811         //
812         //
813         public override int GetHashCode()
814         {
815             return base.GetHashCode() + GetRulesHashCode();
816         }
817         private int GetRulesHashCode()
818         {
819             if (m_schemesList == null)
820                 return 0;
821
822             int result = 0;
823             for(int i = 0; i < m_schemesList.Count; ++i)
824                 result += ((string)m_schemesList[i]).GetHashCode();
825
826             foreach (ArrayList accessList in m_accessList)
827                 for(int i = 0; i < accessList.Count; ++i)
828                     result += ((CodeConnectAccess)accessList[i]).GetHashCode();
829
830             return result;
831         }
832         //
833         protected override void CreateXml( SecurityElement element, PolicyLevel level )
834         {
835             DictionaryEntry[] rules = GetConnectAccessRules();
836             if (rules == null)
837                 return;
838
839             SecurityElement rulesElement = new SecurityElement("connectAccessRules");
840
841             foreach (DictionaryEntry rule in rules)
842             {
843                 SecurityElement codeOriginElement = new SecurityElement("codeOrigin");
844                 codeOriginElement.AddAttribute("scheme", (string) rule.Key);
845                 foreach (CodeConnectAccess access in (CodeConnectAccess[])rule.Value)
846                 {
847                     SecurityElement accessElem = new SecurityElement("connectAccess");
848                     accessElem.AddAttribute("scheme", access.Scheme);
849                     accessElem.AddAttribute("port", access.StrPort);
850                     codeOriginElement.AddChild(accessElem);
851                 }
852                 rulesElement.AddChild(codeOriginElement);
853             }
854             element.AddChild(rulesElement);
855         }
856
857         protected override void ParseXml( SecurityElement e, PolicyLevel level )
858         {
859             //Reset the exiting content
860             ResetConnectAccess();
861
862             SecurityElement et = e.SearchForChildByTag("connectAccessRules");
863
864             if (et == null || et.Children == null)
865             {
866                 // Everett behavior, same as calling a default ctor.
867                 SetDefaults();
868                 return;
869             }
870
871             foreach(SecurityElement codeOriginElem in et.Children)
872             {
873                 if (codeOriginElem.Tag.Equals("codeOrigin"))
874                 {
875                     string originScheme = codeOriginElem.Attribute("scheme");
876                     bool oneAdded = false;
877
878                     if (codeOriginElem.Children != null)
879                     {
880                         foreach(SecurityElement accessElem in codeOriginElem.Children)
881                         {
882                             if (accessElem.Tag.Equals("connectAccess"))
883                             {
884                                 string connectScheme = accessElem.Attribute("scheme");
885                                 string connectPort   = accessElem.Attribute("port");
886                                 AddConnectAccess(originScheme, new CodeConnectAccess(connectScheme, connectPort));
887                                 oneAdded = true;
888                             }
889                             else {
890                                 // improper tag found, just ignore
891                             }
892                         }
893                     }
894
895                     if (!oneAdded)
896                     {
897                         //special case as to no talkback access for a given scheme
898                         AddConnectAccess(originScheme, null);
899                     }
900
901                 }
902                 else {
903                     // improper tag found, just ignore
904                 }
905             }
906         }
907
908         internal override String GetTypeName()
909         {
910             return "System.Security.Policy.NetCodeGroup";
911         }
912         //
913         // This method is called at the ctor time to populate default accesses (V1.1 compat)
914         //
915         private void SetDefaults()
916         {
917             // No access for file://
918             AddConnectAccess("file", null);
919
920             // access fot http://
921             AddConnectAccess("http", new CodeConnectAccess("http", CodeConnectAccess.OriginPort));
922             AddConnectAccess("http", new CodeConnectAccess("https", CodeConnectAccess.OriginPort));
923             /* 
924
925
926
927 */
928
929             // access fot https://
930             AddConnectAccess("https", new CodeConnectAccess("https", CodeConnectAccess.OriginPort));
931             /* 
932
933 */
934
935
936             // access fot ftp://
937             /* 
938
939
940
941
942 */
943
944             // access for no scheme and for any other scheme
945             AddConnectAccess(NetCodeGroup.AbsentOriginScheme, CodeConnectAccess.CreateAnySchemeAccess(CodeConnectAccess.OriginPort));
946             AddConnectAccess(NetCodeGroup.AnyOtherOriginScheme, CodeConnectAccess.CreateOriginSchemeAccess(CodeConnectAccess.OriginPort));
947         }
948
949     }
950
951 }