[corlib] RemotingConfiguration now try to use the bundled machine.config first.
[mono.git] / mcs / class / corlib / System.Runtime.Remoting / RemotingConfiguration.cs
1 //
2 // System.Runtime.Remoting.RemotingConfiguration.cs
3 //
4 // Author: Jaime Anguiano Olarra (jaime@gnome.org)
5 //         Lluis Sanchez Gual (lluis@ideary.com)
6 //
7 // (C) 2002, Jaime Anguiano Olarra
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Globalization;
35 using System.IO;
36 using System.Reflection;
37 using System.Collections;
38 using System.Runtime.Remoting.Activation;
39 using System.Runtime.Remoting.Channels;
40 using System.Runtime.Remoting.Lifetime;
41 using Mono.Xml;
42
43 namespace System.Runtime.Remoting
44 {       
45         [System.Runtime.InteropServices.ComVisible (true)]
46         public static class RemotingConfiguration
47         {
48                 
49                 static string applicationID = null;
50                 static string applicationName = null;
51 //              static string configFile = "";
52 //              static SmallXmlParser parser = null; 
53                 static string processGuid = null;
54                 static bool defaultConfigRead = false;
55                 static bool defaultDelayedConfigRead = false;
56                 static string _errorMode;
57
58                 static Hashtable wellKnownClientEntries = new Hashtable();
59                 static Hashtable activatedClientEntries = new Hashtable();
60                 static Hashtable wellKnownServiceEntries = new Hashtable();
61                 static Hashtable activatedServiceEntries = new Hashtable();
62                 
63                 static Hashtable channelTemplates = new Hashtable ();
64                 static Hashtable clientProviderTemplates = new Hashtable ();
65                 static Hashtable serverProviderTemplates = new Hashtable ();
66                 
67                 // public properties
68                 // At this time the ID will be the application name 
69                 public static string ApplicationId 
70                 {
71                         get 
72                         {
73                                 applicationID = ApplicationName; 
74                                 return applicationID;
75                         }
76                 }
77                 
78                 public static string ApplicationName 
79                 {
80                         get { return applicationName; }
81                         set { applicationName = value; }
82                 }
83
84                 [MonoTODO]
85                 public static CustomErrorsModes CustomErrorsMode
86                 {
87                         get { throw new NotImplementedException (); }
88                         set { throw new NotImplementedException (); }
89                 }
90
91                 public static string ProcessId 
92                 {
93                         get {
94                                 if (processGuid == null)
95                                         processGuid = AppDomain.GetProcessGuid ();
96
97                                 return processGuid;
98                         }
99                 }
100
101
102                 // public methods
103                 
104                 [MonoTODO ("ensureSecurity support has not been implemented")]
105                 public static void Configure (string filename, bool ensureSecurity) 
106                 {
107                         lock (channelTemplates) {
108                                 if (!defaultConfigRead) {
109                                         var bundled = Environment.GetBundledMachineConfig ();
110                                         if (bundled != null)
111                                                 ReadConfigString (bundled);
112
113                                         if (File.Exists (Environment.GetMachineConfigPath ()))
114                                                 ReadConfigFile (Environment.GetMachineConfigPath ());
115                                         defaultConfigRead = true;
116                                 }
117
118                                 if (filename != null)
119                                         ReadConfigFile (filename);
120                         }
121                 }
122
123                 [Obsolete ("Use Configure(String,Boolean)")]
124                 public static void Configure (string filename) 
125                 {
126                         Configure (filename, false);
127                 }
128
129                 private static void ReadConfigString (string filename)
130                 {
131                         try
132                         {
133                                 SmallXmlParser parser = new SmallXmlParser ();
134                                 using (TextReader rreader = new StringReader (filename)) {
135                                         ConfigHandler handler = new ConfigHandler (false);
136                                         parser.Parse (rreader, handler);
137                                 }
138                         }
139                         catch (Exception ex)
140                         {
141                                 throw new RemotingException ("Configuration string could not be loaded: " + ex.Message, ex);
142                         }
143                 }
144
145                 private static void ReadConfigFile (string filename)
146                 {
147                         try
148                         {
149                                 SmallXmlParser parser = new SmallXmlParser ();
150                                 using (TextReader rreader = new StreamReader (filename)) {
151                                         ConfigHandler handler = new ConfigHandler (false);
152                                         parser.Parse (rreader, handler);
153                                 }
154                         }
155                         catch (Exception ex)
156                         {
157                                 throw new RemotingException ("Configuration file '" + filename + "' could not be loaded: " + ex.Message, ex);
158                         }
159                 }
160                 
161                 internal static void LoadDefaultDelayedChannels ()
162                 {
163                         lock (channelTemplates)
164                         {
165                                 if (defaultDelayedConfigRead || defaultConfigRead) return;
166                                 
167                                 SmallXmlParser parser = new SmallXmlParser ();
168                                 using (TextReader rreader = new StreamReader (Environment.GetMachineConfigPath ())) {
169                                         ConfigHandler handler = new ConfigHandler (true);
170                                         parser.Parse (rreader, handler);
171                                 }
172                                 defaultDelayedConfigRead = true;
173                         }
174                 }
175         
176                 public static ActivatedClientTypeEntry[] GetRegisteredActivatedClientTypes () 
177                 {
178                         lock (channelTemplates)
179                         {
180                                 ActivatedClientTypeEntry[] entries = new ActivatedClientTypeEntry[activatedClientEntries.Count];
181                                 activatedClientEntries.Values.CopyTo (entries,0);
182                                 return entries;
183                         }
184                 }
185
186                 public static ActivatedServiceTypeEntry[] GetRegisteredActivatedServiceTypes () 
187                 {
188                         lock (channelTemplates)
189                         {
190                                 ActivatedServiceTypeEntry[] entries = new ActivatedServiceTypeEntry[activatedServiceEntries.Count];
191                                 activatedServiceEntries.Values.CopyTo (entries,0);
192                                 return entries;
193                         }
194                 }
195
196                 public static WellKnownClientTypeEntry[] GetRegisteredWellKnownClientTypes () 
197                 {
198                         lock (channelTemplates)
199                         {
200                                 WellKnownClientTypeEntry[] entries = new WellKnownClientTypeEntry[wellKnownClientEntries.Count];
201                                 wellKnownClientEntries.Values.CopyTo (entries,0);
202                                 return entries;
203                         }
204                 }
205
206                 public static WellKnownServiceTypeEntry[] GetRegisteredWellKnownServiceTypes () 
207                 {
208                         lock (channelTemplates)
209                         {
210                                 WellKnownServiceTypeEntry[] entries = new WellKnownServiceTypeEntry[wellKnownServiceEntries.Count];
211                                 wellKnownServiceEntries.Values.CopyTo (entries,0);
212                                 return entries;
213                         }
214                 }
215
216                 public static bool IsActivationAllowed (Type svrType) 
217                 {
218                         lock (channelTemplates)
219                         {
220                                 return activatedServiceEntries.ContainsKey (svrType);
221                         }
222                 }
223
224                 public static ActivatedClientTypeEntry IsRemotelyActivatedClientType (Type svrType) 
225                 {
226                         lock (channelTemplates)
227                         {
228                                 return activatedClientEntries [svrType] as ActivatedClientTypeEntry;
229                         }
230                 }
231
232                 public static ActivatedClientTypeEntry IsRemotelyActivatedClientType (string typeName, string assemblyName) 
233                 {
234                         return IsRemotelyActivatedClientType (Assembly.Load(assemblyName).GetType (typeName));
235                 }
236
237                 public static WellKnownClientTypeEntry IsWellKnownClientType (Type svrType) 
238                 {
239                         lock (channelTemplates)
240                         {
241                                 return wellKnownClientEntries [svrType] as WellKnownClientTypeEntry;
242                         }
243                 }
244
245                 public static WellKnownClientTypeEntry IsWellKnownClientType (string typeName, string assemblyName) 
246                 {
247                         return IsWellKnownClientType (Assembly.Load(assemblyName).GetType (typeName));
248                 }
249
250                 public static void RegisterActivatedClientType (ActivatedClientTypeEntry entry) 
251                 {
252                         lock (channelTemplates)
253                         {
254                                 if (wellKnownClientEntries.ContainsKey (entry.ObjectType) || activatedClientEntries.ContainsKey (entry.ObjectType))
255                                         throw new RemotingException ("Attempt to redirect activation of type '" + entry.ObjectType.FullName + "' which is already redirected.");
256         
257                                 activatedClientEntries[entry.ObjectType] = entry;
258                                 ActivationServices.EnableProxyActivation (entry.ObjectType, true);
259                         }
260                 }
261
262                 public static void RegisterActivatedClientType (Type type, string appUrl) 
263                 {
264                         if (type == null) throw new ArgumentNullException ("type");
265                         if (appUrl == null) throw new ArgumentNullException ("appUrl");
266
267                         RegisterActivatedClientType (new ActivatedClientTypeEntry (type, appUrl));
268                 }
269
270                 public static void RegisterActivatedServiceType (ActivatedServiceTypeEntry entry) 
271                 {
272                         lock (channelTemplates)
273                         {
274                                 activatedServiceEntries.Add (entry.ObjectType, entry);
275                         }
276                 }
277
278                 public static void RegisterActivatedServiceType (Type type) 
279                 {
280                         RegisterActivatedServiceType (new ActivatedServiceTypeEntry (type));
281                 }
282
283                 public static void RegisterWellKnownClientType (Type type, string objectUrl) 
284                 {
285                         if (type == null) throw new ArgumentNullException ("type");
286                         if (objectUrl == null) throw new ArgumentNullException ("objectUrl");
287
288                         RegisterWellKnownClientType (new WellKnownClientTypeEntry (type, objectUrl));
289                 }
290
291                 public static void RegisterWellKnownClientType (WellKnownClientTypeEntry entry) 
292                 {
293                         lock (channelTemplates)
294                         {
295                                 if (wellKnownClientEntries.ContainsKey (entry.ObjectType) || activatedClientEntries.ContainsKey (entry.ObjectType))
296                                         throw new RemotingException ("Attempt to redirect activation of type '" + entry.ObjectType.FullName + "' which is already redirected.");
297         
298                                 wellKnownClientEntries[entry.ObjectType] = entry;
299                                 ActivationServices.EnableProxyActivation (entry.ObjectType, true);
300                         }
301                 }
302
303                 public static void RegisterWellKnownServiceType (Type type, string objectUri, WellKnownObjectMode mode) 
304                 {
305                         RegisterWellKnownServiceType (new WellKnownServiceTypeEntry (type, objectUri, mode));
306                 }
307
308                 public static void RegisterWellKnownServiceType (WellKnownServiceTypeEntry entry) 
309                 {
310                         lock (channelTemplates)
311                         {
312                                 wellKnownServiceEntries [entry.ObjectUri] = entry;
313                                 RemotingServices.CreateWellKnownServerIdentity (entry.ObjectType, entry.ObjectUri, entry.Mode);
314                         }
315                 }
316
317                 internal static void RegisterChannelTemplate (ChannelData channel)
318                 {
319                         channelTemplates [channel.Id] = channel;
320                 }
321                 
322                 internal static void RegisterClientProviderTemplate (ProviderData prov)
323                 {
324                         clientProviderTemplates [prov.Id] = prov;
325                 }
326                 
327                 internal static void RegisterServerProviderTemplate (ProviderData prov)
328                 {
329                         serverProviderTemplates [prov.Id] = prov;
330                 }
331                 
332                 internal static void RegisterChannels (ArrayList channels, bool onlyDelayed)
333                 {
334                         foreach (ChannelData channel in channels)
335                         {
336                                 if (onlyDelayed && channel.DelayLoadAsClientChannel != "true")
337                                         continue;
338                                         
339                                 if (defaultDelayedConfigRead && channel.DelayLoadAsClientChannel == "true")
340                                         continue;
341                                         
342                                 if (channel.Ref != null)
343                                 {
344                                         ChannelData template = (ChannelData) channelTemplates [channel.Ref];
345                                         if (template == null) throw new RemotingException ("Channel template '" + channel.Ref + "' not found");
346                                         channel.CopyFrom (template);
347                                 }
348                                 
349                                 foreach (ProviderData prov in channel.ServerProviders)
350                                 {
351                                         if (prov.Ref != null)
352                                         {
353                                                 ProviderData template = (ProviderData) serverProviderTemplates [prov.Ref];
354                                                 if (template == null) throw new RemotingException ("Provider template '" + prov.Ref + "' not found");
355                                                 prov.CopyFrom (template);
356                                         }
357                                 }
358                                 
359                                 foreach (ProviderData prov in channel.ClientProviders)
360                                 {
361                                         if (prov.Ref != null)
362                                         {
363                                                 ProviderData template = (ProviderData) clientProviderTemplates [prov.Ref];
364                                                 if (template == null) throw new RemotingException ("Provider template '" + prov.Ref + "' not found");
365                                                 prov.CopyFrom (template);
366                                         }
367                                 }
368                                 
369                                 ChannelServices.RegisterChannelConfig (channel);
370                         }
371                 }
372                 
373                 internal static void RegisterTypes (ArrayList types)
374                 {
375                         foreach (TypeEntry type in types)
376                         {
377                                 if (type is ActivatedClientTypeEntry)
378                                         RegisterActivatedClientType ((ActivatedClientTypeEntry)type);
379                                 else if (type is ActivatedServiceTypeEntry)
380                                         RegisterActivatedServiceType ((ActivatedServiceTypeEntry)type);
381                                 else if (type is WellKnownClientTypeEntry)
382                                         RegisterWellKnownClientType ((WellKnownClientTypeEntry)type);
383                                 else if (type is WellKnownServiceTypeEntry)
384                                         RegisterWellKnownServiceType ((WellKnownServiceTypeEntry)type);
385                         }
386                 }
387                 
388                 public static bool CustomErrorsEnabled (bool isLocalRequest)
389                 {
390                         if (_errorMode == "off") return false;
391                         if (_errorMode == "on") return true;
392                         return !isLocalRequest;
393                 }
394
395                 internal static void SetCustomErrorsMode (string mode)
396                 {
397                         if (mode == null)
398                                 throw new RemotingException ("mode attribute is required");
399
400                         // the mode is case insensitive
401                         string m = mode.ToLower ();
402
403                         if (m != "on" && m != "off" && m != "remoteonly")
404                                 throw new RemotingException ("Invalid custom error mode: " + mode);
405                                 
406                         _errorMode = m;
407                 }
408         }
409
410         /***************************************************************
411          * Internal classes used by RemotingConfiguration.Configure () *
412          ***************************************************************/
413          
414         internal class ConfigHandler : SmallXmlParser.IContentHandler 
415         {
416                 ArrayList typeEntries = new ArrayList ();
417                 ArrayList channelInstances = new ArrayList ();
418                 
419                 ChannelData currentChannel = null;
420                 Stack currentProviderData = null;
421                 
422                 string currentClientUrl = null;
423                 string appName;
424                 
425                 string currentXmlPath = "";
426                 bool onlyDelayedChannels;
427                 
428                 public ConfigHandler (bool onlyDelayedChannels)
429                 {
430                         this.onlyDelayedChannels = onlyDelayedChannels;
431                 }
432                 
433                 void ValidatePath (string element, params string[] paths)
434                 {
435                         foreach (string path in paths)
436                                 if (CheckPath (path)) return;
437                                 
438                         throw new RemotingException ("Element " + element + " not allowed in this context");
439                 }
440                 
441                 bool CheckPath (string path)
442                 {
443                         CompareInfo ci = CultureInfo.InvariantCulture.CompareInfo;
444                         if (ci.IsPrefix (path, "/", CompareOptions.Ordinal))
445                                 return path == currentXmlPath;
446                         else
447                                 return ci.IsSuffix (currentXmlPath, path, CompareOptions.Ordinal);
448                 }
449                 
450                 public void OnStartParsing (SmallXmlParser parser) {}
451                 
452                 public void OnProcessingInstruction (string name, string text) {}
453
454                 public void OnIgnorableWhitespace (string s) {}
455
456                 public void OnStartElement (string name, SmallXmlParser.IAttrList attrs)
457                 {
458                         try
459                         {
460                                 if (currentXmlPath.StartsWith ("/configuration/system.runtime.remoting"))
461                                         ParseElement (name, attrs);
462                                         
463                                 currentXmlPath += "/" + name;
464                         }
465                         catch (Exception ex)
466                         {
467                                 throw new RemotingException ("Error in element " + name + ": " + ex.Message, ex);
468                         }
469                 }
470                 
471                 public void ParseElement (string name, SmallXmlParser.IAttrList attrs)
472                 {
473                         if (currentProviderData != null)
474                         {
475                                 ReadCustomProviderData (name, attrs);
476                                 return;
477                         }
478                         
479                         switch (name) 
480                         {
481                                 case "application":
482                                         ValidatePath (name, "system.runtime.remoting");
483                                         if (attrs.Names.Length > 0)
484                                                 appName = attrs.Values[0];
485                                         break;
486                                         
487                                 case "lifetime":
488                                         ValidatePath (name, "application");
489                                         ReadLifetine (attrs);
490                                         break;
491                                         
492                                 case "channels":
493                                         ValidatePath (name, "system.runtime.remoting", "application");
494                                         break;
495                                         
496                                 case "channel":
497                                         ValidatePath (name, "channels");
498                                         if (currentXmlPath.IndexOf ("application") != -1)
499                                                 ReadChannel (attrs, false);
500                                         else
501                                                 ReadChannel (attrs, true);
502                                         break;
503                                         
504                                 case "serverProviders":
505                                         ValidatePath (name, "channelSinkProviders", "channel");
506                                         break;
507                                         
508                                 case "clientProviders":
509                                         ValidatePath (name, "channelSinkProviders", "channel");
510                                         break;
511                                         
512                                 case "provider":
513                                 case "formatter":
514                                         ProviderData prov;
515                                         
516                                         if (CheckPath ("application/channels/channel/serverProviders") ||
517                                                 CheckPath ("channels/channel/serverProviders"))
518                                         {
519                                                 prov = ReadProvider (name, attrs, false);
520                                                 currentChannel.ServerProviders.Add (prov);
521                                         }
522                                         else if (CheckPath ("application/channels/channel/clientProviders") ||
523                                                 CheckPath ("channels/channel/clientProviders"))
524                                         {
525                                                 prov = ReadProvider (name, attrs, false);
526                                                 currentChannel.ClientProviders.Add (prov);
527                                         }
528                                         else if (CheckPath ("channelSinkProviders/serverProviders"))
529                                         {
530                                                 prov = ReadProvider (name, attrs, true);
531                                                 RemotingConfiguration.RegisterServerProviderTemplate (prov);
532                                         }
533                                         else if (CheckPath ("channelSinkProviders/clientProviders"))
534                                         {
535                                                 prov = ReadProvider (name, attrs, true);
536                                                 RemotingConfiguration.RegisterClientProviderTemplate (prov);
537                                         }
538                                         else 
539                                                 ValidatePath (name);
540                                         break;
541                                         
542                                 case "client":
543                                         ValidatePath (name, "application");
544                                         currentClientUrl = attrs.GetValue ("url");
545                                         break;
546                                         
547                                 case "service":
548                                         ValidatePath (name, "application");
549                                         break;
550                                         
551                                 case "wellknown":
552                                         ValidatePath (name, "client", "service");
553                                         if (CheckPath ("client"))
554                                                 ReadClientWellKnown (attrs);
555                                         else
556                                                 ReadServiceWellKnown (attrs);
557                                         break;
558                                         
559                                 case "activated":
560                                         ValidatePath (name, "client", "service");
561                                         if (CheckPath ("client"))
562                                                 ReadClientActivated (attrs);
563                                         else
564                                                 ReadServiceActivated (attrs);
565                                         break;
566                                         
567                                 case "soapInterop":
568                                         ValidatePath (name, "application");
569                                         break;
570                                         
571                                 case "interopXmlType":
572                                         ValidatePath (name, "soapInterop");
573                                         ReadInteropXml (attrs, false);
574                                         break;
575                                         
576                                 case "interopXmlElement":
577                                         ValidatePath (name, "soapInterop");
578                                         ReadInteropXml (attrs, false);
579                                         break;
580                                         
581                                 case "preLoad":
582                                         ValidatePath (name, "soapInterop");
583                                         ReadPreload (attrs);
584                                         break;
585                                         
586                                 case "debug":
587                                         ValidatePath (name, "system.runtime.remoting");
588                                         break;
589                                         
590                                 case "channelSinkProviders":
591                                         ValidatePath (name, "system.runtime.remoting");
592                                         break;
593                                         
594                                 case "customErrors":
595                                         ValidatePath (name, "system.runtime.remoting");
596                                         RemotingConfiguration.SetCustomErrorsMode (attrs.GetValue ("mode"));
597                                         break;
598                                         
599                                 default:
600                                         throw new RemotingException ("Element '" + name + "' is not valid in system.remoting.configuration section");
601                         }
602                 }
603                 
604                 public void OnEndElement (string name)
605                 {
606                         if (currentProviderData != null)
607                         {
608                                 currentProviderData.Pop ();
609                                 if (currentProviderData.Count == 0) 
610                                         currentProviderData = null;
611                         }
612                         
613                         currentXmlPath = currentXmlPath.Substring (0, currentXmlPath.Length - name.Length - 1);
614                 }
615                 
616                 void ReadCustomProviderData (string name, SmallXmlParser.IAttrList attrs)
617                 {
618                         SinkProviderData parent = (SinkProviderData) currentProviderData.Peek ();
619                         
620                         SinkProviderData data = new SinkProviderData (name);
621                         for (int i=0; i < attrs.Names.Length; ++i) 
622                                 data.Properties [attrs.Names[i]] = attrs.GetValue (i);
623                                 
624                         parent.Children.Add (data);
625                         currentProviderData.Push (data);
626                 }
627
628                 void ReadLifetine (SmallXmlParser.IAttrList attrs)
629                 {
630                         for (int i=0; i < attrs.Names.Length; ++i) {
631                                 switch (attrs.Names[i]) {
632                                 case "leaseTime":
633                                         LifetimeServices.LeaseTime = ParseTime (attrs.GetValue(i));
634                                         break;
635                                 case "sponsorshipTimeout":
636                                         LifetimeServices.SponsorshipTimeout = ParseTime (attrs.GetValue(i));
637                                         break;
638                                 case "renewOnCallTime":
639                                         LifetimeServices.RenewOnCallTime = ParseTime (attrs.GetValue(i));
640                                         break;
641                                 case "leaseManagerPollTime":
642                                         LifetimeServices.LeaseManagerPollTime = ParseTime (attrs.GetValue(i));
643                                         break;
644                                 default:
645                                         throw new RemotingException ("Invalid attribute: " + attrs.Names[i]);
646                                 }
647                         }
648                 }
649                 
650                 TimeSpan ParseTime (string s)
651                 {
652                         if (s == "" || s == null) throw new RemotingException ("Invalid time value");
653                         
654                         int i = s.IndexOfAny (new char[] { 'D','H','M','S' });
655                         
656                         string unit;
657                         if (i == -1) 
658                                 unit = "S";
659                         else { 
660                                 unit = s.Substring (i);
661                                 s = s.Substring (0,i);
662                         }
663                         double val;
664                         
665                         try {
666                                 val = double.Parse (s);
667                         }
668                         catch {
669                                 throw new RemotingException ("Invalid time value: " + s);
670                         }
671                         
672                         if (unit == "D") return TimeSpan.FromDays (val);
673                         if (unit == "H") return TimeSpan.FromHours (val);
674                         if (unit == "M") return TimeSpan.FromMinutes (val);
675                         if (unit == "S") return TimeSpan.FromSeconds (val);
676                         if (unit == "MS") return TimeSpan.FromMilliseconds (val);
677                         throw new RemotingException ("Invalid time unit: " + unit);
678                 }
679                 
680                 void ReadChannel (SmallXmlParser.IAttrList attrs, bool isTemplate)
681                 {
682                         ChannelData channel = new ChannelData ();
683                         
684                         for (int i=0; i < attrs.Names.Length; ++i) 
685                         {
686                                 string at = attrs.Names[i];
687                                 string val = attrs.Values[i];
688                                 
689                                 if (at == "ref" && !isTemplate)
690                                         channel.Ref = val;
691                                 else if (at == "delayLoadAsClientChannel")
692                                         channel.DelayLoadAsClientChannel = val;
693                                 else if (at == "id" && isTemplate)
694                                         channel.Id = val;
695                                 else if (at == "type")
696                                         channel.Type = val;
697                                 else
698                                         channel.CustomProperties.Add (at, val);
699                         }
700                         
701                         if (isTemplate)
702                         {
703                                 if (channel.Id == null) throw new RemotingException ("id attribute is required");
704                                 if (channel.Type == null) throw new RemotingException ("id attribute is required");
705                                 RemotingConfiguration.RegisterChannelTemplate (channel);
706                         }
707                         else
708                                 channelInstances.Add (channel);
709                                 
710                         currentChannel = channel;
711                 }
712                 
713                 ProviderData ReadProvider (string name, SmallXmlParser.IAttrList attrs, bool isTemplate)
714                 {
715                         ProviderData prov = (name == "provider") ? new ProviderData () : new FormatterData ();
716                         SinkProviderData data = new SinkProviderData ("root");
717                         prov.CustomData = data.Children;
718                         
719                         currentProviderData = new Stack ();
720                         currentProviderData.Push (data);
721                         
722                         for (int i=0; i < attrs.Names.Length; ++i) 
723                         {
724                                 string at = attrs.Names[i];
725                                 string val = attrs.Values[i];
726                                 
727                                 if (at == "id" && isTemplate)
728                                         prov.Id = val;
729                                 else if (at == "type")
730                                         prov.Type = val;
731                                 else if (at == "ref" && !isTemplate)
732                                         prov.Ref = val;
733                                 else
734                                         prov.CustomProperties.Add (at, val);
735                         }
736                         
737                         if (prov.Id == null && isTemplate) throw new RemotingException ("id attribute is required");
738                         return prov;
739                 }
740                 
741                 void ReadClientActivated (SmallXmlParser.IAttrList attrs)
742                 {
743                         string type = GetNotNull (attrs, "type");
744                         string assm = ExtractAssembly (ref type);
745                         
746                         if (currentClientUrl == null || currentClientUrl == "") 
747                                 throw new RemotingException ("url attribute is required in client element when it contains activated entries");
748
749                         typeEntries.Add (new ActivatedClientTypeEntry (type, assm, currentClientUrl));
750                 }
751                 
752                 void ReadServiceActivated (SmallXmlParser.IAttrList attrs)
753                 {
754                         string type = GetNotNull (attrs, "type");
755                         string assm = ExtractAssembly (ref type);
756                         
757                         typeEntries.Add (new ActivatedServiceTypeEntry (type, assm));
758                 }
759                 
760                 void ReadClientWellKnown (SmallXmlParser.IAttrList attrs)
761                 {
762                         string url = GetNotNull (attrs, "url");
763                         string type = GetNotNull (attrs, "type");
764                         string assm = ExtractAssembly (ref type);
765                         
766                         typeEntries.Add (new WellKnownClientTypeEntry (type, assm, url));
767                 }
768                 
769                 void ReadServiceWellKnown (SmallXmlParser.IAttrList attrs)
770                 {
771                         string objectUri = GetNotNull (attrs, "objectUri");
772                         string smode = GetNotNull (attrs, "mode");
773                         string type = GetNotNull (attrs, "type");
774                         string assm = ExtractAssembly (ref type);
775                         
776                         WellKnownObjectMode mode;
777                         if (smode == "SingleCall") mode = WellKnownObjectMode.SingleCall;
778                         else if (smode == "Singleton") mode = WellKnownObjectMode.Singleton;
779                         else throw new RemotingException ("wellknown object mode '" + smode + "' is invalid");
780                         
781                         typeEntries.Add (new WellKnownServiceTypeEntry (type, assm, objectUri, mode));
782                 }
783                 
784                 void ReadInteropXml (SmallXmlParser.IAttrList attrs, bool isElement)
785                 {
786                         Type t = Type.GetType (GetNotNull (attrs, "clr"));
787                         string[] xmlName = GetNotNull (attrs, "xml").Split (',');
788                         string localName = xmlName [0].Trim ();
789                         string ns = xmlName.Length > 0 ? xmlName[1].Trim() : null;
790                         
791                         if (isElement) SoapServices.RegisterInteropXmlElement (localName, ns, t);
792                         else SoapServices.RegisterInteropXmlType (localName, ns, t);
793                 }
794                 
795                 void ReadPreload (SmallXmlParser.IAttrList attrs)
796                 {
797                         string type = attrs.GetValue ("type");
798                         string assm = attrs.GetValue ("assembly");
799                         
800                         if (type != null && assm != null)
801                                 throw new RemotingException ("Type and assembly attributes cannot be specified together");
802                                 
803                         if (type != null)
804                                 SoapServices.PreLoad (Type.GetType (type));
805                         else if (assm != null)
806                                 SoapServices.PreLoad (Assembly.Load (assm));
807                         else
808                                 throw new RemotingException ("Either type or assembly attributes must be specified");
809                 }
810                                         
811                 string GetNotNull (SmallXmlParser.IAttrList attrs, string name)
812                 {
813                         string value = attrs.GetValue (name);
814                         if (value == null || value == "") 
815                                 throw new RemotingException (name + " attribute is required");
816                         return value;
817                 }
818                 
819                 string ExtractAssembly (ref string type)
820                 {
821                         int i = type.IndexOf (',');
822                         if (i == -1) return "";
823                         
824                         string asm = type.Substring (i+1).Trim();
825                         type = type.Substring (0, i).Trim();
826                         return asm;
827                 }
828                 
829                 public void OnChars (string ch) {}
830                 
831                 public void OnEndParsing (SmallXmlParser parser)
832                 {
833                         RemotingConfiguration.RegisterChannels (channelInstances, onlyDelayedChannels);
834                         if (appName != null) RemotingConfiguration.ApplicationName = appName;
835                         
836                         if (!onlyDelayedChannels)
837                                 RemotingConfiguration.RegisterTypes (typeEntries);
838                 }
839         }
840
841
842                 /*******************************************************************
843          * Internal data structures used by ConfigHandler, to store             *
844          * machine.config's remoting related data.                         *
845          * If having them implemented this way, makes configuration too    *
846          * slow, we can use string arrays.                                 *
847          *******************************************************************/
848                  
849         internal class ChannelData {
850                 internal string Ref;
851                 internal string Type;
852                 internal string Id;
853                 internal string DelayLoadAsClientChannel;
854                 
855                 ArrayList _serverProviders = new ArrayList ();
856                 ArrayList _clientProviders = new ArrayList ();
857                 Hashtable _customProperties = new Hashtable ();
858                 
859                 internal ArrayList ServerProviders {
860                         get {
861                                 if (_serverProviders == null) _serverProviders = new ArrayList ();
862                                 return _serverProviders;
863                         }
864                 }
865                 
866                 public ArrayList ClientProviders {
867                         get {
868                                 if (_clientProviders == null) _clientProviders = new ArrayList ();
869                                 return _clientProviders;
870                         }
871                 }
872                 
873                 public Hashtable CustomProperties {
874                         get {
875                                 if (_customProperties == null) _customProperties = new Hashtable ();
876                                 return _customProperties;
877                         }
878                 }
879                 
880                 public void CopyFrom (ChannelData other)
881                 {
882                         if (Ref == null) Ref = other.Ref;
883                         if (Id == null) Id = other.Id;
884                         if (Type == null) Type = other.Type;
885                         if (DelayLoadAsClientChannel == null) DelayLoadAsClientChannel = other.DelayLoadAsClientChannel;
886
887                         if (other._customProperties != null)
888                         {
889                                 foreach (DictionaryEntry entry in other._customProperties)
890                                         if (!CustomProperties.ContainsKey (entry.Key))
891                                                 CustomProperties [entry.Key] = entry.Value;
892                         }
893                         
894                         if (_serverProviders == null && other._serverProviders != null)
895                         {
896                                 foreach (ProviderData prov in other._serverProviders)
897                                 {
898                                         ProviderData np = new ProviderData();
899                                         np.CopyFrom (prov);
900                                         ServerProviders.Add (np);
901                                 }
902                         }
903                         
904                         if (_clientProviders == null && other._clientProviders != null)
905                         {
906                                 foreach (ProviderData prov in other._clientProviders)
907                                 {
908                                         ProviderData np = new ProviderData();
909                                         np.CopyFrom (prov);
910                                         ClientProviders.Add (np);
911                                 }
912                         }
913                 }
914         }
915         
916         internal class ProviderData {
917                 internal string Ref;
918                 internal string Type;
919                 internal string Id;
920                 
921                 internal Hashtable CustomProperties = new Hashtable ();
922                 internal IList CustomData;
923                 
924                 public void CopyFrom (ProviderData other)
925                 {
926                         if (Ref == null) Ref = other.Ref;
927                         if (Id == null) Id = other.Id;
928                         if (Type == null) Type = other.Type;
929                         
930                         foreach (DictionaryEntry entry in other.CustomProperties)
931                                 if (!CustomProperties.ContainsKey (entry.Key))
932                                         CustomProperties [entry.Key] = entry.Value;
933                                         
934                         if (other.CustomData != null)
935                         {
936                                 if (CustomData == null) CustomData = new ArrayList ();
937                                 foreach (SinkProviderData data in other.CustomData)
938                                         CustomData.Add (data);
939                         }
940                 }
941         }
942         
943         internal class FormatterData: ProviderData {
944         }       
945 }