A correction to 78114
[mono.git] / mcs / class / System.Web / System.Web / CapabilitiesLoader.cs
1 // 
2 // System.Web.CapabilitiesLoader
3 //
4 // Loads data from browscap.ini file provided by Gary J. Keith from
5 // http://www.GaryKeith.com/browsers. Please don't abuse the
6 // site when updating browscap.ini file. Use the update-browscap.exe tool.
7 //
8 // Authors:
9 //   Gonzalo Paniagua Javier (gonzalo@ximian.com)
10 //
11 // (c) 2003 Novell, Inc. (http://www.novell.com)
12 //
13
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System;
36 using System.Collections;
37 using System.Collections.Specialized;
38 using System.Globalization;
39 using System.IO;
40 using System.Text.RegularExpressions;
41 using System.Web.Configuration;
42
43 namespace System.Web
44 {
45         class BrowserData
46         {
47                 static char [] wildchars = new char [] {'*', '?'};
48                 BrowserData parent;
49                 string text;
50                 string pattern;
51 #if TARGET_JVM
52                 java.util.regex.Pattern regex;
53 #else
54                 Regex regex;
55 #endif
56                 ListDictionary data;
57
58                 public BrowserData (string pattern)
59                 {
60                         int norx = pattern.IndexOfAny (wildchars);
61                         if (norx == -1) {
62                                 text = pattern;
63                         } else {
64                                 this.pattern = pattern.Substring (norx);
65                                 text = pattern.Substring (0, norx);
66                                 if (text.Length == 0)
67                                         text = null;
68
69                                 this.pattern = this.pattern.Replace (".", "\\.");
70                                 this.pattern = this.pattern.Replace ("(", "\\(");
71                                 this.pattern = this.pattern.Replace (")", "\\)");
72                                 this.pattern = this.pattern.Replace ("[", "\\[");
73                                 this.pattern = this.pattern.Replace ("]", "\\]");
74                                 this.pattern = this.pattern.Replace ("?", ".");
75                                 this.pattern = this.pattern.Replace ("*", ".*");
76                         }
77                 }
78
79                 public BrowserData Parent {
80                         get { return parent; }
81                         set { parent = value; }
82                 }
83
84                 public void Add (string key, string value)
85                 {
86                         if (data == null)
87                                 data = new ListDictionary ();
88
89                         data.Add (key, value);
90                 }
91
92                 public Hashtable GetProperties (Hashtable tbl)
93                 {
94                         if (parent != null)
95                                 parent.GetProperties (tbl);
96
97                         if (data ["browser"] != null) { // Last one (most derived) will win.
98                                 tbl ["browser"] = data ["browser"];
99                         } else if (tbl ["browser"] == null) { // If none so far defined value set to *
100                                 tbl ["browser"] = "*";
101                         }
102
103                         if (!tbl.ContainsKey ("browsers")) {
104                                 tbl ["browsers"] = new ArrayList ();
105                         }
106
107                         ((ArrayList) tbl ["browsers"]).Add (tbl["browser"]);
108
109                         foreach (string key in data.Keys)
110                                 tbl [key.ToLower (CultureInfo.InvariantCulture).Trim ()] = data [key];
111                         
112                         return tbl;
113                 }
114                 
115                 public string GetParentName ()
116                 {
117                         return (string)(data.Contains("parent")? data ["parent"] : null);
118                 }
119                 
120                 public string GetAlternateBrowser ()
121                 {
122                         return (pattern == null) ? text : null;
123                 }
124
125                 public string GetBrowser ()
126                 {
127                         if (pattern == null)
128                                 return text;
129
130                         return (string) data ["browser"];
131                 }
132                 
133                 public bool IsMatch (string expression)
134                 {
135                         if (expression == null || expression.Length == 0)
136                                 return false;
137
138                         if (text != null) {
139                                 if (text [0] != expression [0] ||
140                                     String.Compare (text, 1, expression, 1,
141                                                     text.Length - 1, false,
142                                                     CultureInfo.InvariantCulture) != 0) {
143                                         return false;
144                                 }
145                                 expression = expression.Substring (text.Length);
146                         }
147                         
148                         if (pattern == null)
149                                 return expression.Length == 0;
150
151                         lock (this) {
152                                 if (regex == null)
153 #if TARGET_JVM
154                                         regex = java.util.regex.Pattern.compile (pattern);
155 #else
156                                 regex = new Regex (pattern);
157 #endif
158                         }
159 #if TARGET_JVM
160                         return regex.matcher ((java.lang.CharSequence) (object) expression).matches ();
161 #else
162                         return regex.Match (expression).Success;
163 #endif
164                 }
165         }
166         
167         class CapabilitiesLoader : MarshalByRefObject
168         {
169                 const int userAgentsCacheSize = 3000;
170                 static Hashtable defaultCaps;
171                 static readonly object lockobj = new object ();
172
173 #if TARGET_JVM
174                 static bool loaded {
175                         get {
176                                 return alldata != null;
177                         }
178                         set {
179                                 if (alldata == null)
180                                         alldata = new ArrayList ();
181                         }
182                 }
183
184                 const string alldataKey = "System.Web.CapabilitiesLoader.alldata";
185                 static ICollection alldata {
186                         get {
187                                 return (ICollection) AppDomain.CurrentDomain.GetData (alldataKey);
188                         }
189                         set {
190                                 AppDomain.CurrentDomain.SetData (alldataKey, value);
191                         }
192                 }
193
194                 const string userAgentsCacheKey = "System.Web.CapabilitiesLoader.userAgentsCache";
195                 static Hashtable userAgentsCache {
196                         get {
197                                 lock (typeof (CapabilitiesLoader)) {
198                                         Hashtable agentsCache = (Hashtable) AppDomain.CurrentDomain.GetData (userAgentsCacheKey);
199                                         if (agentsCache == null) {
200                                                 agentsCache = Hashtable.Synchronized (new Hashtable (userAgentsCacheSize + 10));
201                                                 AppDomain.CurrentDomain.SetData (userAgentsCacheKey, agentsCache);
202                                         }
203
204                                         return agentsCache;
205                                 }
206                         }
207                 }
208 #else
209                 static volatile bool loaded;
210                 static ICollection alldata;
211                 static Hashtable userAgentsCache = Hashtable.Synchronized(new Hashtable(userAgentsCacheSize+10));
212 #endif
213
214                 private CapabilitiesLoader () {}
215
216                 static CapabilitiesLoader ()
217                 {
218                         defaultCaps = new Hashtable ();
219                         defaultCaps.Add ("activexcontrols", "False");
220                         defaultCaps.Add ("alpha", "False");
221                         defaultCaps.Add ("aol", "False");
222                         defaultCaps.Add ("aolversion", "0");
223                         defaultCaps.Add ("authenticodeupdate", "");
224                         defaultCaps.Add ("backgroundsounds", "False");
225                         defaultCaps.Add ("beta", "False");
226                         defaultCaps.Add ("browser", "*");
227                         defaultCaps.Add ("browsers", new ArrayList ());
228                         defaultCaps.Add ("cdf", "False");
229                         defaultCaps.Add ("clrversion", "0");
230                         defaultCaps.Add ("cookies", "False");
231                         defaultCaps.Add ("crawler", "False");
232                         defaultCaps.Add ("css", "0");
233                         defaultCaps.Add ("cssversion", "0");
234                         defaultCaps.Add ("ecmascriptversion", "0.0");
235                         defaultCaps.Add ("frames", "False");
236                         defaultCaps.Add ("iframes", "False");
237                         defaultCaps.Add ("isbanned", "False");
238                         defaultCaps.Add ("ismobiledevice", "False");
239                         defaultCaps.Add ("issyndicationreader", "False");
240                         defaultCaps.Add ("javaapplets", "False");
241                         defaultCaps.Add ("javascript", "False");
242                         defaultCaps.Add ("majorver", "0");
243                         defaultCaps.Add ("minorver", "0");
244                         defaultCaps.Add ("msdomversion", "0.0");
245                         defaultCaps.Add ("netclr", "False");
246                         defaultCaps.Add ("platform", "unknown");
247                         defaultCaps.Add ("stripper", "False");
248                         defaultCaps.Add ("supportscss", "False");
249                         defaultCaps.Add ("tables", "False");
250                         defaultCaps.Add ("vbscript", "False");
251                         defaultCaps.Add ("version", "0");
252                         defaultCaps.Add ("w3cdomversion", "0.0");
253                         defaultCaps.Add ("wap", "False");
254                         defaultCaps.Add ("win16", "False");
255                         defaultCaps.Add ("win32", "False");
256                         defaultCaps.Add ("win64", "False");
257                 }
258                 
259                 public static Hashtable GetCapabilities (string userAgent)
260                 {
261                         Init ();
262                         if (userAgent != null)
263                                 userAgent = userAgent.Trim ();
264
265                         if (alldata == null || userAgent == null || userAgent == "")
266                                 return defaultCaps;
267
268                         Hashtable userBrowserCaps = (Hashtable) (userAgentsCache.Contains(userAgent)? userAgentsCache [userAgent] : null);
269                         if (userBrowserCaps == null) {
270                                 foreach (BrowserData bd in alldata) {
271                                         if (bd.IsMatch (userAgent)) {
272                                                 Hashtable tbl;
273                                                 tbl = new Hashtable (defaultCaps);
274                                                 userBrowserCaps = bd.GetProperties (tbl);
275                                                 break;
276                                         }
277                                 }
278
279                                 if (userBrowserCaps == null)
280                                         userBrowserCaps = defaultCaps;
281
282                                 lock (typeof (CapabilitiesLoader)) {
283                                         if (userAgentsCache.Count >= userAgentsCacheSize)
284                                                 userAgentsCache.Clear ();
285                                 }
286                                 userAgentsCache [userAgent] = userBrowserCaps;
287                         }
288                         return userBrowserCaps;
289                 }
290
291                 static void Init ()
292                 {
293                         if (loaded)
294                                 return;
295
296                         lock (lockobj) {
297                                 if (loaded)
298                                         return;
299 #if TARGET_J2EE
300                                 string filepath = "browscap.ini";
301 #else
302                                 string dir = HttpRuntime.MachineConfigurationDirectory;
303                                 string filepath = Path.Combine (dir, "browscap.ini");
304                                 if (!File.Exists (filepath)) {
305                                         // try removing the trailing version directory
306                                         dir = Path.GetDirectoryName (dir);
307                                         filepath = Path.Combine (dir, "browscap.ini");
308                                 }
309 #endif
310                                 try {
311                                         LoadFile (filepath);
312                                 } catch (Exception) {}
313
314                                 loaded = true;
315                         }
316                 }
317
318 #if TARGET_J2EE
319                 private static TextReader GetJavaTextReader(string filename)
320                 {
321                         Stream s;
322                         try
323                         {
324                                 java.lang.ClassLoader cl = (java.lang.ClassLoader)
325                                         AppDomain.CurrentDomain.GetData("GH_ContextClassLoader");
326                                 if (cl == null)
327                                         return null;
328
329                                 string custom = String.Concat("browscap/", filename);
330                                 
331                                 java.io.InputStream inputStream = cl.getResourceAsStream(custom);
332                                 if (inputStream == null)
333                                         inputStream = cl.getResourceAsStream(filename);
334
335                                 s = (Stream)vmw.common.IOUtils.getStream(inputStream);
336                         }
337                         catch (Exception e)
338                         {
339                                 return null;
340                         }
341                         return new StreamReader (s);
342                 }
343 #endif
344
345                 static void LoadFile (string filename)
346                 {
347 #if TARGET_J2EE
348                         TextReader input = GetJavaTextReader(filename);
349                         if(input == null)
350                                 return;
351 #else
352                         if (!File.Exists (filename))
353                                 return;
354
355                         TextReader input = new StreamReader (File.OpenRead (filename));
356 #endif
357                         string str;
358                         Hashtable allhash = new Hashtable ();
359                         int aux = 0;
360                         ArrayList browserData = new ArrayList ();
361                         while ((str = input.ReadLine ()) != null) {
362                                 if (str.Length == 0 || str [0] == ';')
363                                         continue;
364
365                                 string userAgent = str.Substring (1, str.Length - 2);
366                                 BrowserData data = new BrowserData (userAgent);
367                                 ReadCapabilities (input, data);
368
369                                 /* Ignore default browser and file version information */
370                                 if (userAgent == "*" || userAgent == "GJK_Browscap_Version")
371                                         continue;
372
373                                 string key = data.GetBrowser ();
374                                 if (key == null || allhash.ContainsKey (key)) {
375                                         allhash.Add (aux++, data);
376                                         browserData.Add (data);
377                                 } else {
378                                         allhash.Add (key, data);
379                                         browserData.Add (data);
380                                 }
381                         }                       
382
383                         alldata = browserData;
384                         foreach (BrowserData data in alldata) {
385                                 string pname = data.GetParentName ();
386                                 if (pname == null)
387                                         continue;
388
389                                 data.Parent = (BrowserData) allhash [pname];
390                         }
391                 }
392
393                 static char [] eq = new char []{'='};
394                 static void ReadCapabilities (TextReader input, BrowserData data)
395                 {
396                         string str, key;
397                         string [] keyvalue;
398                         
399                         while ((str = input.ReadLine ()) != null && str.Length != 0) {
400                                 keyvalue = str.Split (eq, 2);
401                                 key = keyvalue [0].ToLower (CultureInfo.InvariantCulture).Trim ();
402                                 if (key.Length == 0)
403                                         continue;
404                                 data.Add (key, keyvalue [1]);
405                         }
406                 }
407         }
408 }