2 // System.Web.CapabilitiesLoader
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.
9 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
11 // (c) 2003 Novell, Inc. (http://www.novell.com)
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:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
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.
36 using System.Collections;
37 using System.Collections.Specialized;
38 using System.Globalization;
40 using System.Text.RegularExpressions;
41 using System.Web.Configuration;
47 static char [] wildchars = new char [] {'*', '?'};
52 java.util.regex.Pattern regex;
58 public BrowserData (string pattern)
60 int norx = pattern.IndexOfAny (wildchars);
64 this.pattern = pattern.Substring (norx);
65 text = pattern.Substring (0, norx);
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 ("*", ".*");
79 public BrowserData Parent {
80 get { return parent; }
81 set { parent = value; }
84 public void Add (string key, string value)
87 data = new ListDictionary ();
89 data.Add (key, value);
92 public Hashtable GetProperties (Hashtable tbl)
95 parent.GetProperties (tbl);
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"] = "*";
103 if (!tbl.ContainsKey ("browsers")) {
104 tbl ["browsers"] = new ArrayList ();
107 ((ArrayList) tbl ["browsers"]).Add (tbl["browser"]);
109 foreach (string key in data.Keys)
110 tbl [key.ToLower (CultureInfo.InvariantCulture).Trim ()] = data [key];
115 public string GetParentName ()
117 return (string)(data.Contains("parent")? data ["parent"] : null);
120 public string GetAlternateBrowser ()
122 return (pattern == null) ? text : null;
125 public string GetBrowser ()
130 return (string) data ["browser"];
133 public bool IsMatch (string expression)
135 if (expression == null || expression.Length == 0)
139 if (text [0] != expression [0] ||
140 String.Compare (text, 1, expression, 1,
141 text.Length - 1, false,
142 CultureInfo.InvariantCulture) != 0) {
145 expression = expression.Substring (text.Length);
149 return expression.Length == 0;
154 regex = java.util.regex.Pattern.compile (pattern);
156 regex = new Regex (pattern);
160 return regex.matcher ((java.lang.CharSequence) (object) expression).matches ();
162 return regex.Match (expression).Success;
167 class CapabilitiesLoader : MarshalByRefObject
169 const int userAgentsCacheSize = 3000;
170 static Hashtable defaultCaps;
171 static readonly object lockobj = new object ();
176 return alldata != null;
180 alldata = new ArrayList ();
184 const string alldataKey = "System.Web.CapabilitiesLoader.alldata";
185 static ICollection alldata {
187 return (ICollection) AppDomain.CurrentDomain.GetData (alldataKey);
190 AppDomain.CurrentDomain.SetData (alldataKey, value);
194 const string userAgentsCacheKey = "System.Web.CapabilitiesLoader.userAgentsCache";
195 static Hashtable userAgentsCache {
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);
209 static volatile bool loaded;
210 static ICollection alldata;
211 static Hashtable userAgentsCache = Hashtable.Synchronized(new Hashtable(userAgentsCacheSize+10));
214 private CapabilitiesLoader () {}
216 static CapabilitiesLoader ()
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");
259 public static Hashtable GetCapabilities (string userAgent)
262 if (userAgent != null)
263 userAgent = userAgent.Trim ();
265 if (alldata == null || userAgent == null || userAgent == "")
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)) {
273 tbl = new Hashtable (defaultCaps);
274 userBrowserCaps = bd.GetProperties (tbl);
279 if (userBrowserCaps == null)
280 userBrowserCaps = defaultCaps;
282 lock (typeof (CapabilitiesLoader)) {
283 if (userAgentsCache.Count >= userAgentsCacheSize)
284 userAgentsCache.Clear ();
286 userAgentsCache [userAgent] = userBrowserCaps;
288 return userBrowserCaps;
300 string filepath = "browscap.ini";
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");
312 } catch (Exception) {}
319 private static TextReader GetJavaTextReader(string filename)
324 java.lang.ClassLoader cl = (java.lang.ClassLoader)
325 AppDomain.CurrentDomain.GetData("GH_ContextClassLoader");
329 string custom = String.Concat("browscap/", filename);
331 java.io.InputStream inputStream = cl.getResourceAsStream(custom);
332 if (inputStream == null)
333 inputStream = cl.getResourceAsStream(filename);
335 s = (Stream)vmw.common.IOUtils.getStream(inputStream);
341 return new StreamReader (s);
345 static void LoadFile (string filename)
348 TextReader input = GetJavaTextReader(filename);
352 if (!File.Exists (filename))
355 TextReader input = new StreamReader (File.OpenRead (filename));
358 Hashtable allhash = new Hashtable ();
360 ArrayList browserData = new ArrayList ();
361 while ((str = input.ReadLine ()) != null) {
362 if (str.Length == 0 || str [0] == ';')
365 string userAgent = str.Substring (1, str.Length - 2);
366 BrowserData data = new BrowserData (userAgent);
367 ReadCapabilities (input, data);
369 /* Ignore default browser and file version information */
370 if (userAgent == "*" || userAgent == "GJK_Browscap_Version")
373 string key = data.GetBrowser ();
374 if (key == null || allhash.ContainsKey (key)) {
375 allhash.Add (aux++, data);
376 browserData.Add (data);
378 allhash.Add (key, data);
379 browserData.Add (data);
383 alldata = browserData;
384 foreach (BrowserData data in alldata) {
385 string pname = data.GetParentName ();
389 data.Parent = (BrowserData) allhash [pname];
393 static char [] eq = new char []{'='};
394 static void ReadCapabilities (TextReader input, BrowserData data)
399 while ((str = input.ReadLine ()) != null && str.Length != 0) {
400 keyvalue = str.Split (eq, 2);
401 key = keyvalue [0].ToLower (CultureInfo.InvariantCulture).Trim ();
404 data.Add (key, keyvalue [1]);