CapabilitiesLoader.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  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 ([email protected])
  10. //
  11. // (c) 2003-2009 Novell, Inc. (http://www.novell.com)
  12. //
  13. //
  14. // Permission is hereby granted, free of charge, to any person obtaining
  15. // a copy of this software and associated documentation files (the
  16. // "Software"), to deal in the Software without restriction, including
  17. // without limitation the rights to use, copy, modify, merge, publish,
  18. // distribute, sublicense, and/or sell copies of the Software, and to
  19. // permit persons to whom the Software is furnished to do so, subject to
  20. // the following conditions:
  21. //
  22. // The above copyright notice and this permission notice shall be
  23. // included in all copies or substantial portions of the Software.
  24. //
  25. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  26. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  27. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  28. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  29. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  30. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  31. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  32. //
  33. using System;
  34. using System.Collections;
  35. using System.Collections.Specialized;
  36. using System.Globalization;
  37. using System.IO;
  38. using System.Text.RegularExpressions;
  39. using System.Web.Configuration;
  40. using System.Web.Util;
  41. namespace System.Web
  42. {
  43. sealed class BrowserData
  44. {
  45. static char [] wildchars = new char [] {'*', '?'};
  46. object this_lock = new object ();
  47. BrowserData parent;
  48. string text;
  49. string pattern;
  50. #if TARGET_JVM
  51. java.util.regex.Pattern regex;
  52. #else
  53. Regex regex;
  54. #endif
  55. ListDictionary data;
  56. public BrowserData (string pattern)
  57. {
  58. int norx = pattern.IndexOfAny (wildchars);
  59. if (norx == -1) {
  60. text = pattern;
  61. } else {
  62. this.pattern = pattern.Substring (norx);
  63. text = pattern.Substring (0, norx);
  64. if (text.Length == 0)
  65. text = null;
  66. this.pattern = this.pattern.Replace (".", "\\.");
  67. this.pattern = this.pattern.Replace ("(", "\\(");
  68. this.pattern = this.pattern.Replace (")", "\\)");
  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. }
  74. }
  75. public BrowserData Parent {
  76. get { return parent; }
  77. set { parent = value; }
  78. }
  79. public void Add (string key, string value)
  80. {
  81. if (data == null)
  82. data = new ListDictionary ();
  83. data.Add (key, value);
  84. }
  85. public Hashtable GetProperties (Hashtable tbl)
  86. {
  87. if (parent != null)
  88. parent.GetProperties (tbl);
  89. if (data ["browser"] != null) { // Last one (most derived) will win.
  90. tbl ["browser"] = data ["browser"];
  91. } else if (tbl ["browser"] == null) { // If none so far defined value set to *
  92. tbl ["browser"] = "*";
  93. }
  94. if (!tbl.ContainsKey ("browsers")) {
  95. tbl ["browsers"] = new ArrayList ();
  96. }
  97. ((ArrayList) tbl ["browsers"]).Add (tbl["browser"]);
  98. foreach (string key in data.Keys)
  99. tbl [key.ToLower (Helpers.InvariantCulture).Trim ()] = data [key];
  100. return tbl;
  101. }
  102. public string GetParentName ()
  103. {
  104. return (string)(data.Contains("parent")? data ["parent"] : null);
  105. }
  106. public string GetAlternateBrowser ()
  107. {
  108. return (pattern == null) ? text : null;
  109. }
  110. public string GetBrowser ()
  111. {
  112. if (pattern == null)
  113. return text;
  114. return (string) data ["browser"];
  115. }
  116. public bool IsMatch (string expression)
  117. {
  118. if (expression == null || expression.Length == 0)
  119. return false;
  120. if (text != null) {
  121. if (text [0] != expression [0] ||
  122. String.Compare (text, 1, expression, 1,
  123. text.Length - 1, false,
  124. Helpers.InvariantCulture) != 0) {
  125. return false;
  126. }
  127. expression = expression.Substring (text.Length);
  128. }
  129. if (pattern == null)
  130. return expression.Length == 0;
  131. lock (this_lock) {
  132. if (regex == null)
  133. #if TARGET_JVM
  134. regex = java.util.regex.Pattern.compile (pattern);
  135. #else
  136. regex = new Regex (pattern);
  137. #endif
  138. }
  139. #if TARGET_JVM
  140. return regex.matcher ((java.lang.CharSequence) (object) expression).matches ();
  141. #else
  142. return regex.Match (expression).Success;
  143. #endif
  144. }
  145. }
  146. sealed class CapabilitiesLoader : MarshalByRefObject
  147. {
  148. const int userAgentsCacheSize = 3000;
  149. static Hashtable defaultCaps;
  150. static readonly object lockobj = new object ();
  151. #if TARGET_JVM
  152. static bool loaded {
  153. get {
  154. return alldata != null;
  155. }
  156. set {
  157. if (alldata == null)
  158. alldata = new ArrayList ();
  159. }
  160. }
  161. const string alldataKey = "System.Web.CapabilitiesLoader.alldata";
  162. static ICollection alldata {
  163. get {
  164. return (ICollection) AppDomain.CurrentDomain.GetData (alldataKey);
  165. }
  166. set {
  167. AppDomain.CurrentDomain.SetData (alldataKey, value);
  168. }
  169. }
  170. const string userAgentsCacheKey = "System.Web.CapabilitiesLoader.userAgentsCache";
  171. static Hashtable userAgentsCache {
  172. get {
  173. lock (typeof (CapabilitiesLoader)) {
  174. Hashtable agentsCache = (Hashtable) AppDomain.CurrentDomain.GetData (userAgentsCacheKey);
  175. if (agentsCache == null) {
  176. agentsCache = Hashtable.Synchronized (new Hashtable (userAgentsCacheSize + 10));
  177. AppDomain.CurrentDomain.SetData (userAgentsCacheKey, agentsCache);
  178. }
  179. return agentsCache;
  180. }
  181. }
  182. }
  183. #else
  184. static volatile bool loaded;
  185. static ICollection alldata;
  186. static Hashtable userAgentsCache = Hashtable.Synchronized(new Hashtable(userAgentsCacheSize+10));
  187. #endif
  188. CapabilitiesLoader () {}
  189. static CapabilitiesLoader ()
  190. {
  191. defaultCaps = new Hashtable ();
  192. defaultCaps.Add ("activexcontrols", "False");
  193. defaultCaps.Add ("alpha", "False");
  194. defaultCaps.Add ("aol", "False");
  195. defaultCaps.Add ("aolversion", "0");
  196. defaultCaps.Add ("authenticodeupdate", "");
  197. defaultCaps.Add ("backgroundsounds", "False");
  198. defaultCaps.Add ("beta", "False");
  199. defaultCaps.Add ("browser", "*");
  200. defaultCaps.Add ("browsers", new ArrayList ());
  201. defaultCaps.Add ("cdf", "False");
  202. defaultCaps.Add ("clrversion", "0");
  203. defaultCaps.Add ("cookies", "False");
  204. defaultCaps.Add ("crawler", "False");
  205. defaultCaps.Add ("css", "0");
  206. defaultCaps.Add ("cssversion", "0");
  207. defaultCaps.Add ("ecmascriptversion", "0.0");
  208. defaultCaps.Add ("frames", "False");
  209. defaultCaps.Add ("iframes", "False");
  210. defaultCaps.Add ("isbanned", "False");
  211. defaultCaps.Add ("ismobiledevice", "False");
  212. defaultCaps.Add ("issyndicationreader", "False");
  213. defaultCaps.Add ("javaapplets", "False");
  214. defaultCaps.Add ("javascript", "False");
  215. defaultCaps.Add ("majorver", "0");
  216. defaultCaps.Add ("minorver", "0");
  217. defaultCaps.Add ("msdomversion", "0.0");
  218. defaultCaps.Add ("netclr", "False");
  219. defaultCaps.Add ("platform", "unknown");
  220. defaultCaps.Add ("stripper", "False");
  221. defaultCaps.Add ("supportscss", "False");
  222. defaultCaps.Add ("tables", "False");
  223. defaultCaps.Add ("vbscript", "False");
  224. defaultCaps.Add ("version", "0");
  225. defaultCaps.Add ("w3cdomversion", "0.0");
  226. defaultCaps.Add ("wap", "False");
  227. defaultCaps.Add ("win16", "False");
  228. defaultCaps.Add ("win32", "False");
  229. defaultCaps.Add ("win64", "False");
  230. defaultCaps.Add ("adapters", new Hashtable ());
  231. defaultCaps.Add ("cancombineformsindeck", "False");
  232. defaultCaps.Add ("caninitiatevoicecall", "False");
  233. defaultCaps.Add ("canrenderafterinputorselectelement", "False");
  234. defaultCaps.Add ("canrenderemptyselects", "False");
  235. defaultCaps.Add ("canrenderinputandselectelementstogether", "False");
  236. defaultCaps.Add ("canrendermixedselects", "False");
  237. defaultCaps.Add ("canrenderoneventandprevelementstogether", "False");
  238. defaultCaps.Add ("canrenderpostbackcards", "False");
  239. defaultCaps.Add ("canrendersetvarzerowithmultiselectionlist", "False");
  240. defaultCaps.Add ("cansendmail", "False");
  241. defaultCaps.Add ("defaultsubmitbuttonlimit", "0");
  242. defaultCaps.Add ("gatewayminorversion", "0");
  243. defaultCaps.Add ("gatewaymajorversion", "0");
  244. defaultCaps.Add ("gatewayversion", "None");
  245. defaultCaps.Add ("hasbackbutton", "True");
  246. defaultCaps.Add ("hidesrightalignedmultiselectscrollbars", "False");
  247. defaultCaps.Add ("inputtype", "telephoneKeypad");
  248. defaultCaps.Add ("iscolor", "False");
  249. defaultCaps.Add ("jscriptversion", "0.0");
  250. defaultCaps.Add ("maximumhreflength", "0");
  251. defaultCaps.Add ("maximumrenderedpagesize", "2000");
  252. defaultCaps.Add ("maximumsoftkeylabellength", "5");
  253. defaultCaps.Add ("minorversionstring", "0.0");
  254. defaultCaps.Add ("mobiledevicemanufacturer", "Unknown");
  255. defaultCaps.Add ("mobiledevicemodel", "Unknown");
  256. defaultCaps.Add ("numberofsoftkeys", "0");
  257. defaultCaps.Add ("preferredimagemime", "image/gif");
  258. defaultCaps.Add ("preferredrenderingmime", "text/html");
  259. defaultCaps.Add ("preferredrenderingtype", "html32");
  260. defaultCaps.Add ("preferredrequestencoding", "");
  261. defaultCaps.Add ("preferredresponseencoding", "");
  262. defaultCaps.Add ("rendersbreakbeforewmlselectandinput", "False");
  263. defaultCaps.Add ("rendersbreaksafterhtmllists", "True");
  264. defaultCaps.Add ("rendersbreaksafterwmlanchor", "False");
  265. defaultCaps.Add ("rendersbreaksafterwmlinput", "False");
  266. defaultCaps.Add ("renderswmldoacceptsinline", "True");
  267. defaultCaps.Add ("renderswmlselectsasmenucards", "False");
  268. defaultCaps.Add ("requiredmetatagnamevalue", "");
  269. defaultCaps.Add ("requiresattributecolonsubstitution", "False");
  270. defaultCaps.Add ("requirescontenttypemetatag", "False");
  271. defaultCaps.Add ("requirescontrolstateinsession", "False");
  272. defaultCaps.Add ("requiresdbcscharacter", "False");
  273. defaultCaps.Add ("requireshtmladaptiveerrorreporting", "False");
  274. defaultCaps.Add ("requiresleadingpagebreak", "False");
  275. defaultCaps.Add ("requiresnobreakinformatting", "False");
  276. defaultCaps.Add ("requiresoutputoptimization", "False");
  277. defaultCaps.Add ("requiresphonenumbersasplaintext", "False");
  278. defaultCaps.Add ("requiresspecialviewstateencoding", "False");
  279. defaultCaps.Add ("requiresuniquefilepathsuffix", "False");
  280. defaultCaps.Add ("requiresuniquehtmlcheckboxnames", "False");
  281. defaultCaps.Add ("requiresuniquehtmlinputnames", "False");
  282. defaultCaps.Add ("requiresurlencodedpostfieldvalues", "False");
  283. defaultCaps.Add ("screenbitdepth", "1");
  284. defaultCaps.Add ("screencharactersheight", "6");
  285. defaultCaps.Add ("screencharacterswidth", "12");
  286. defaultCaps.Add ("screenpixelsheight", "72");
  287. defaultCaps.Add ("screenpixelswidth", "96");
  288. defaultCaps.Add ("supportsaccesskeyattribute", "False");
  289. defaultCaps.Add ("supportsbodycolor", "True");
  290. defaultCaps.Add ("supportsbold", "False");
  291. defaultCaps.Add ("supportscachecontrolmetatag", "True");
  292. defaultCaps.Add ("supportscallback", "False");
  293. defaultCaps.Add ("supportsdivalign", "True");
  294. defaultCaps.Add ("supportsdivnowrap", "False");
  295. defaultCaps.Add ("supportsemptystringincookievalue", "False");
  296. defaultCaps.Add ("supportsfontcolor", "True");
  297. defaultCaps.Add ("supportsfontname", "False");
  298. defaultCaps.Add ("supportsfontsize", "False");
  299. defaultCaps.Add ("supportsimagesubmit", "False");
  300. defaultCaps.Add ("supportsimodesymbols", "False");
  301. defaultCaps.Add ("supportsinputistyle", "False");
  302. defaultCaps.Add ("supportsinputmode", "False");
  303. defaultCaps.Add ("supportsitalic", "False");
  304. defaultCaps.Add ("supportsjphonemultimediaattributes", "False");
  305. defaultCaps.Add ("supportsjphonesymbols", "False");
  306. defaultCaps.Add ("supportsquerystringinformaction", "True");
  307. defaultCaps.Add ("supportsredirectwithcookie", "True");
  308. defaultCaps.Add ("supportsselectmultiple", "True");
  309. defaultCaps.Add ("supportsuncheck", "True");
  310. defaultCaps.Add ("supportsxmlhttp", "False");
  311. defaultCaps.Add ("type", "Unknown");
  312. }
  313. public static Hashtable GetCapabilities (string userAgent)
  314. {
  315. Init ();
  316. if (userAgent != null)
  317. userAgent = userAgent.Trim ();
  318. if (alldata == null || userAgent == null || userAgent.Length == 0)
  319. return defaultCaps;
  320. Hashtable userBrowserCaps = (Hashtable) (userAgentsCache.Contains(userAgent)? userAgentsCache [userAgent] : null);
  321. if (userBrowserCaps == null) {
  322. foreach (BrowserData bd in alldata) {
  323. if (bd.IsMatch (userAgent)) {
  324. Hashtable tbl;
  325. tbl = new Hashtable (defaultCaps);
  326. userBrowserCaps = bd.GetProperties (tbl);
  327. break;
  328. }
  329. }
  330. if (userBrowserCaps == null)
  331. userBrowserCaps = defaultCaps;
  332. lock (lockobj) {
  333. if (userAgentsCache.Count >= userAgentsCacheSize)
  334. userAgentsCache.Clear ();
  335. }
  336. userAgentsCache [userAgent] = userBrowserCaps;
  337. }
  338. return userBrowserCaps;
  339. }
  340. static void Init ()
  341. {
  342. if (loaded)
  343. return;
  344. lock (lockobj) {
  345. if (loaded)
  346. return;
  347. #if TARGET_J2EE
  348. string filepath = "browscap.ini";
  349. #else
  350. string dir = HttpRuntime.MachineConfigurationDirectory;
  351. string filepath = Path.Combine (dir, "browscap.ini");
  352. if (!File.Exists (filepath)) {
  353. // try removing the trailing version directory
  354. dir = Path.GetDirectoryName (dir);
  355. filepath = Path.Combine (dir, "browscap.ini");
  356. }
  357. #endif
  358. try {
  359. LoadFile (filepath);
  360. } catch (Exception) {}
  361. loaded = true;
  362. }
  363. }
  364. #if TARGET_J2EE
  365. static TextReader GetJavaTextReader(string filename)
  366. {
  367. try
  368. {
  369. java.lang.ClassLoader cl = (java.lang.ClassLoader)
  370. AppDomain.CurrentDomain.GetData("GH_ContextClassLoader");
  371. if (cl == null)
  372. return null;
  373. string custom = String.Concat("browscap/", filename);
  374. java.io.InputStream inputStream = cl.getResourceAsStream(custom);
  375. if (inputStream == null)
  376. inputStream = cl.getResourceAsStream(filename);
  377. if (inputStream == null)
  378. return null;
  379. return new StreamReader (new System.Web.J2EE.J2EEUtils.InputStreamWrapper (inputStream));
  380. }
  381. catch (Exception e)
  382. {
  383. return null;
  384. }
  385. }
  386. #endif
  387. static void LoadFile (string filename)
  388. {
  389. #if TARGET_J2EE
  390. TextReader input = GetJavaTextReader(filename);
  391. if(input == null)
  392. return;
  393. #else
  394. if (!File.Exists (filename))
  395. return;
  396. TextReader input = new StreamReader (File.OpenRead (filename));
  397. #endif
  398. using (input) {
  399. string str;
  400. Hashtable allhash = new Hashtable ();
  401. int aux = 0;
  402. ArrayList browserData = new ArrayList ();
  403. while ((str = input.ReadLine ()) != null) {
  404. if (str.Length == 0 || str [0] == ';')
  405. continue;
  406. string userAgent = str.Substring (1, str.Length - 2);
  407. BrowserData data = new BrowserData (userAgent);
  408. ReadCapabilities (input, data);
  409. /* Ignore default browser and file version information */
  410. if (userAgent == "*" || userAgent == "GJK_Browscap_Version")
  411. continue;
  412. string key = data.GetBrowser ();
  413. if (key == null || allhash.ContainsKey (key)) {
  414. allhash.Add (aux++, data);
  415. browserData.Add (data);
  416. } else {
  417. allhash.Add (key, data);
  418. browserData.Add (data);
  419. }
  420. }
  421. alldata = browserData;
  422. foreach (BrowserData data in alldata) {
  423. string pname = data.GetParentName ();
  424. if (pname == null)
  425. continue;
  426. data.Parent = (BrowserData) allhash [pname];
  427. }
  428. }
  429. }
  430. static char [] eq = new char []{'='};
  431. static void ReadCapabilities (TextReader input, BrowserData data)
  432. {
  433. string str, key;
  434. string [] keyvalue;
  435. while ((str = input.ReadLine ()) != null && str.Length != 0) {
  436. keyvalue = str.Split (eq, 2);
  437. key = keyvalue [0].ToLower (Helpers.InvariantCulture).Trim ();
  438. if (key.Length == 0)
  439. continue;
  440. data.Add (key, keyvalue [1]);
  441. }
  442. }
  443. }
  444. }