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;
42 using System.Web.Util;
46 sealed class BrowserData
48 static char [] wildchars = new char [] {'*', '?'};
50 object this_lock = new object ();
55 java.util.regex.Pattern regex;
61 public BrowserData (string pattern)
63 int norx = pattern.IndexOfAny (wildchars);
67 this.pattern = pattern.Substring (norx);
68 text = pattern.Substring (0, norx);
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 this.pattern = this.pattern.Replace ("]", "\\]");
77 this.pattern = this.pattern.Replace ('?', '.');
78 this.pattern = this.pattern.Replace ("*", ".*");
82 public BrowserData Parent {
83 get { return parent; }
84 set { parent = value; }
87 public void Add (string key, string value)
90 data = new ListDictionary ();
92 data.Add (key, value);
95 public Hashtable GetProperties (Hashtable tbl)
98 parent.GetProperties (tbl);
100 if (data ["browser"] != null) { // Last one (most derived) will win.
101 tbl ["browser"] = data ["browser"];
102 } else if (tbl ["browser"] == null) { // If none so far defined value set to *
103 tbl ["browser"] = "*";
106 if (!tbl.ContainsKey ("browsers")) {
107 tbl ["browsers"] = new ArrayList ();
110 ((ArrayList) tbl ["browsers"]).Add (tbl["browser"]);
112 foreach (string key in data.Keys)
113 tbl [key.ToLower (Helpers.InvariantCulture).Trim ()] = data [key];
118 public string GetParentName ()
120 return (string)(data.Contains("parent")? data ["parent"] : null);
123 public string GetAlternateBrowser ()
125 return (pattern == null) ? text : null;
128 public string GetBrowser ()
133 return (string) data ["browser"];
136 public bool IsMatch (string expression)
138 if (expression == null || expression.Length == 0)
142 if (text [0] != expression [0] ||
143 String.Compare (text, 1, expression, 1,
144 text.Length - 1, false,
145 Helpers.InvariantCulture) != 0) {
148 expression = expression.Substring (text.Length);
152 return expression.Length == 0;
157 regex = java.util.regex.Pattern.compile (pattern);
159 regex = new Regex (pattern);
163 return regex.matcher ((java.lang.CharSequence) (object) expression).matches ();
165 return regex.Match (expression).Success;
170 sealed class CapabilitiesLoader : MarshalByRefObject
172 const int userAgentsCacheSize = 3000;
173 static Hashtable defaultCaps;
174 static readonly object lockobj = new object ();
179 return alldata != null;
183 alldata = new ArrayList ();
187 const string alldataKey = "System.Web.CapabilitiesLoader.alldata";
188 static ICollection alldata {
190 return (ICollection) AppDomain.CurrentDomain.GetData (alldataKey);
193 AppDomain.CurrentDomain.SetData (alldataKey, value);
197 const string userAgentsCacheKey = "System.Web.CapabilitiesLoader.userAgentsCache";
198 static Hashtable userAgentsCache {
200 lock (typeof (CapabilitiesLoader)) {
201 Hashtable agentsCache = (Hashtable) AppDomain.CurrentDomain.GetData (userAgentsCacheKey);
202 if (agentsCache == null) {
203 agentsCache = Hashtable.Synchronized (new Hashtable (userAgentsCacheSize + 10));
204 AppDomain.CurrentDomain.SetData (userAgentsCacheKey, agentsCache);
212 static volatile bool loaded;
213 static ICollection alldata;
214 static Hashtable userAgentsCache = Hashtable.Synchronized(new Hashtable(userAgentsCacheSize+10));
217 CapabilitiesLoader () {}
219 static CapabilitiesLoader ()
221 defaultCaps = new Hashtable ();
222 defaultCaps.Add ("activexcontrols", "False");
223 defaultCaps.Add ("alpha", "False");
224 defaultCaps.Add ("aol", "False");
225 defaultCaps.Add ("aolversion", "0");
226 defaultCaps.Add ("authenticodeupdate", "");
227 defaultCaps.Add ("backgroundsounds", "False");
228 defaultCaps.Add ("beta", "False");
229 defaultCaps.Add ("browser", "*");
230 defaultCaps.Add ("browsers", new ArrayList ());
231 defaultCaps.Add ("cdf", "False");
232 defaultCaps.Add ("clrversion", "0");
233 defaultCaps.Add ("cookies", "False");
234 defaultCaps.Add ("crawler", "False");
235 defaultCaps.Add ("css", "0");
236 defaultCaps.Add ("cssversion", "0");
237 defaultCaps.Add ("ecmascriptversion", "0.0");
238 defaultCaps.Add ("frames", "False");
239 defaultCaps.Add ("iframes", "False");
240 defaultCaps.Add ("isbanned", "False");
241 defaultCaps.Add ("ismobiledevice", "False");
242 defaultCaps.Add ("issyndicationreader", "False");
243 defaultCaps.Add ("javaapplets", "False");
244 defaultCaps.Add ("javascript", "False");
245 defaultCaps.Add ("majorver", "0");
246 defaultCaps.Add ("minorver", "0");
247 defaultCaps.Add ("msdomversion", "0.0");
248 defaultCaps.Add ("netclr", "False");
249 defaultCaps.Add ("platform", "unknown");
250 defaultCaps.Add ("stripper", "False");
251 defaultCaps.Add ("supportscss", "False");
252 defaultCaps.Add ("tables", "False");
253 defaultCaps.Add ("vbscript", "False");
254 defaultCaps.Add ("version", "0");
255 defaultCaps.Add ("w3cdomversion", "0.0");
256 defaultCaps.Add ("wap", "False");
257 defaultCaps.Add ("win16", "False");
258 defaultCaps.Add ("win32", "False");
259 defaultCaps.Add ("win64", "False");
262 defaultCaps.Add ("adapters", new Hashtable ());
263 defaultCaps.Add ("cancombineformsindeck", "False");
264 defaultCaps.Add ("caninitiatevoicecall", "False");
265 defaultCaps.Add ("canrenderafterinputorselectelement", "False");
266 defaultCaps.Add ("canrenderemptyselects", "False");
267 defaultCaps.Add ("canrenderinputandselectelementstogether", "False");
268 defaultCaps.Add ("canrendermixedselects", "False");
269 defaultCaps.Add ("canrenderoneventandprevelementstogether", "False");
270 defaultCaps.Add ("canrenderpostbackcards", "False");
271 defaultCaps.Add ("canrendersetvarzerowithmultiselectionlist", "False");
272 defaultCaps.Add ("cansendmail", "False");
273 defaultCaps.Add ("defaultsubmitbuttonlimit", "0");
274 defaultCaps.Add ("gatewayminorversion", "0");
275 defaultCaps.Add ("gatewaymajorversion", "0");
276 defaultCaps.Add ("gatewayversion", "None");
277 defaultCaps.Add ("hasbackbutton", "True");
278 defaultCaps.Add ("hidesrightalignedmultiselectscrollbars", "False");
279 defaultCaps.Add ("inputtype", "telephoneKeypad");
280 defaultCaps.Add ("iscolor", "False");
281 defaultCaps.Add ("jscriptversion", "0.0");
282 defaultCaps.Add ("maximumhreflength", "0");
283 defaultCaps.Add ("maximumrenderedpagesize", "2000");
284 defaultCaps.Add ("maximumsoftkeylabellength", "5");
285 defaultCaps.Add ("minorversionstring", "0.0");
286 defaultCaps.Add ("mobiledevicemanufacturer", "Unknown");
287 defaultCaps.Add ("mobiledevicemodel", "Unknown");
288 defaultCaps.Add ("numberofsoftkeys", "0");
289 defaultCaps.Add ("preferredimagemime", "image/gif");
290 defaultCaps.Add ("preferredrenderingmime", "text/html");
291 defaultCaps.Add ("preferredrenderingtype", "html32");
292 defaultCaps.Add ("preferredrequestencoding", "");
293 defaultCaps.Add ("preferredresponseencoding", "");
294 defaultCaps.Add ("rendersbreakbeforewmlselectandinput", "False");
295 defaultCaps.Add ("rendersbreaksafterhtmllists", "True");
296 defaultCaps.Add ("rendersbreaksafterwmlanchor", "False");
297 defaultCaps.Add ("rendersbreaksafterwmlinput", "False");
298 defaultCaps.Add ("renderswmldoacceptsinline", "True");
299 defaultCaps.Add ("renderswmlselectsasmenucards", "False");
300 defaultCaps.Add ("requiredmetatagnamevalue", "");
301 defaultCaps.Add ("requiresattributecolonsubstitution", "False");
302 defaultCaps.Add ("requirescontenttypemetatag", "False");
303 defaultCaps.Add ("requirescontrolstateinsession", "False");
304 defaultCaps.Add ("requiresdbcscharacter", "False");
305 defaultCaps.Add ("requireshtmladaptiveerrorreporting", "False");
306 defaultCaps.Add ("requiresleadingpagebreak", "False");
307 defaultCaps.Add ("requiresnobreakinformatting", "False");
308 defaultCaps.Add ("requiresoutputoptimization", "False");
309 defaultCaps.Add ("requiresphonenumbersasplaintext", "False");
310 defaultCaps.Add ("requiresspecialviewstateencoding", "False");
311 defaultCaps.Add ("requiresuniquefilepathsuffix", "False");
312 defaultCaps.Add ("requiresuniquehtmlcheckboxnames", "False");
313 defaultCaps.Add ("requiresuniquehtmlinputnames", "False");
314 defaultCaps.Add ("requiresurlencodedpostfieldvalues", "False");
315 defaultCaps.Add ("screenbitdepth", "1");
316 defaultCaps.Add ("screencharactersheight", "6");
317 defaultCaps.Add ("screencharacterswidth", "12");
318 defaultCaps.Add ("screenpixelsheight", "72");
319 defaultCaps.Add ("screenpixelswidth", "96");
320 defaultCaps.Add ("supportsaccesskeyattribute", "False");
321 defaultCaps.Add ("supportsbodycolor", "True");
322 defaultCaps.Add ("supportsbold", "False");
323 defaultCaps.Add ("supportscachecontrolmetatag", "True");
324 defaultCaps.Add ("supportscallback", "False");
325 defaultCaps.Add ("supportsdivalign", "True");
326 defaultCaps.Add ("supportsdivnowrap", "False");
327 defaultCaps.Add ("supportsemptystringincookievalue", "False");
328 defaultCaps.Add ("supportsfontcolor", "True");
329 defaultCaps.Add ("supportsfontname", "False");
330 defaultCaps.Add ("supportsfontsize", "False");
331 defaultCaps.Add ("supportsimagesubmit", "False");
332 defaultCaps.Add ("supportsimodesymbols", "False");
333 defaultCaps.Add ("supportsinputistyle", "False");
334 defaultCaps.Add ("supportsinputmode", "False");
335 defaultCaps.Add ("supportsitalic", "False");
336 defaultCaps.Add ("supportsjphonemultimediaattributes", "False");
337 defaultCaps.Add ("supportsjphonesymbols", "False");
338 defaultCaps.Add ("supportsquerystringinformaction", "True");
339 defaultCaps.Add ("supportsredirectwithcookie", "True");
340 defaultCaps.Add ("supportsselectmultiple", "True");
341 defaultCaps.Add ("supportsuncheck", "True");
342 defaultCaps.Add ("supportsxmlhttp", "False");
343 defaultCaps.Add ("type", "Unknown");
347 public static Hashtable GetCapabilities (string userAgent)
350 if (userAgent != null)
351 userAgent = userAgent.Trim ();
353 if (alldata == null || userAgent == null || userAgent.Length == 0)
356 Hashtable userBrowserCaps = (Hashtable) (userAgentsCache.Contains(userAgent)? userAgentsCache [userAgent] : null);
357 if (userBrowserCaps == null) {
358 foreach (BrowserData bd in alldata) {
359 if (bd.IsMatch (userAgent)) {
361 tbl = new Hashtable (defaultCaps);
362 userBrowserCaps = bd.GetProperties (tbl);
367 if (userBrowserCaps == null)
368 userBrowserCaps = defaultCaps;
371 if (userAgentsCache.Count >= userAgentsCacheSize)
372 userAgentsCache.Clear ();
374 userAgentsCache [userAgent] = userBrowserCaps;
376 return userBrowserCaps;
388 string filepath = "browscap.ini";
390 string dir = HttpRuntime.MachineConfigurationDirectory;
391 string filepath = Path.Combine (dir, "browscap.ini");
392 if (!File.Exists (filepath)) {
393 // try removing the trailing version directory
394 dir = Path.GetDirectoryName (dir);
395 filepath = Path.Combine (dir, "browscap.ini");
400 } catch (Exception) {}
407 static TextReader GetJavaTextReader(string filename)
411 java.lang.ClassLoader cl = (java.lang.ClassLoader)
412 AppDomain.CurrentDomain.GetData("GH_ContextClassLoader");
416 string custom = String.Concat("browscap/", filename);
418 java.io.InputStream inputStream = cl.getResourceAsStream(custom);
419 if (inputStream == null)
420 inputStream = cl.getResourceAsStream(filename);
422 if (inputStream == null)
425 return new StreamReader (new System.Web.J2EE.J2EEUtils.InputStreamWrapper (inputStream));
434 static void LoadFile (string filename)
437 TextReader input = GetJavaTextReader(filename);
441 if (!File.Exists (filename))
444 TextReader input = new StreamReader (File.OpenRead (filename));
448 Hashtable allhash = new Hashtable ();
450 ArrayList browserData = new ArrayList ();
451 while ((str = input.ReadLine ()) != null) {
452 if (str.Length == 0 || str [0] == ';')
455 string userAgent = str.Substring (1, str.Length - 2);
456 BrowserData data = new BrowserData (userAgent);
457 ReadCapabilities (input, data);
459 /* Ignore default browser and file version information */
460 if (userAgent == "*" || userAgent == "GJK_Browscap_Version")
463 string key = data.GetBrowser ();
464 if (key == null || allhash.ContainsKey (key)) {
465 allhash.Add (aux++, data);
466 browserData.Add (data);
468 allhash.Add (key, data);
469 browserData.Add (data);
473 alldata = browserData;
474 foreach (BrowserData data in alldata) {
475 string pname = data.GetParentName ();
479 data.Parent = (BrowserData) allhash [pname];
484 static char [] eq = new char []{'='};
485 static void ReadCapabilities (TextReader input, BrowserData data)
490 while ((str = input.ReadLine ()) != null && str.Length != 0) {
491 keyvalue = str.Split (eq, 2);
492 key = keyvalue [0].ToLower (Helpers.InvariantCulture).Trim ();
495 data.Add (key, keyvalue [1]);