Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

431 lines
13 KiB

  1. // CapMap.cpp: implementation of the CCapMap class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "stdafx.h"
  5. #include "asptlb.h"
  6. #include "context.h"
  7. #include "BrwCap.h"
  8. #include "CapMap.h"
  9. #define MAX_RESSTRINGSIZE 512
  10. #ifdef DBG
  11. #undef THIS_FILE
  12. static char THIS_FILE[]=__FILE__;
  13. #define new DEBUG_NEW
  14. #endif
  15. // Global Browser Capabilities Cache.
  16. //
  17. // This is a doubly indexed list --
  18. // the outer level contains the HTTP_USER_AGENT string. The sub-array is the property in question
  19. //
  20. // Example: g_strmapBrowsCapINI["Mozilla 3.0"]["VBScript"] retrieves VBScript property of browser
  21. // "Mozilla 3.0". (of course, in practice HTTP_USER_AGENT strings are quite long.)
  22. //
  23. //
  24. // A note about the data structure choice:
  25. //
  26. // Many of the keys in BrowsCap.INI are very similiar to each other. Examples:
  27. //
  28. // [Mozilla/2.0 (compatible; MSIE 3.0B3; Windows 95)]
  29. // [Mozilla/2.0 (compatible; MSIE 3.0B3; Windows NT)]
  30. //
  31. // or
  32. //
  33. // [Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)]
  34. // [Mozilla/1.22 (compatible; MSIE 2.0c; Windows 95)]
  35. //
  36. // It's likely that excessive hash collisions could occur (these are hardly random keys!), especially
  37. // with small hash modulus (and the table size would be relatively small.) Therefore, the binary search
  38. // array of the pre-existing TStringMap class seems best.
  39. //
  40. // The subkeys that store the properties could probably be hash tables, but there are so few of them,
  41. // it probably does not matter. We also use the TStringMap class purely for convenience (it happens to
  42. // exist.)
  43. //
  44. // UNDONE: Cleanup must Release() the pointers (since they are not "smart" CComPtr's)
  45. //
  46. typedef TSafeStringMap<CBrowserCap *> CacheMapT;
  47. static CacheMapT g_strmapBrowsCapINI; // cache of BrowsCap objects
  48. static TVector<String> g_rgstrWildcard; // list of wildcards in BrowsCap.INI
  49. static CReadWrite g_rwWildcardLock; // lock for wildcard array
  50. //---------------------------------------------------------------------
  51. // read in wildcards from browscap.ini into g_rgstrWildcard
  52. //---------------------------------------------------------------------
  53. void ReadWildcards(const String &strIniFile)
  54. {
  55. // PERF NOTE: caller should check if rgstrWildcard[] is empty before
  56. // calling this function. However, here we do one extra check
  57. // when we have the lock because caller should not bother to
  58. // secure a write lock when checking rgstrWildcard[].
  59. //
  60. g_rwWildcardLock.EnterWriter();
  61. if (g_rgstrWildcard.size() != 0)
  62. {
  63. g_rwWildcardLock.ExitWriter();
  64. return;
  65. }
  66. // first get all of the profiles sections into a buffer
  67. DWORD dwAllocSize = 16384;
  68. TCHAR *szBuffer = new TCHAR[dwAllocSize];
  69. *szBuffer = _T('\0');
  70. DWORD dwSize;
  71. // ATLTRACE("ReadWildcards(%s)\n", strIniFile.c_str());
  72. while ((dwSize = GetPrivateProfileSectionNames(szBuffer, dwAllocSize, strIniFile.c_str())) == dwAllocSize-2 && dwSize > 0)
  73. {
  74. // reallocate the buffer and try again
  75. delete[] szBuffer;
  76. szBuffer = new TCHAR[dwAllocSize *= 2];
  77. *szBuffer = _T('\0');
  78. }
  79. if (dwSize == 0)
  80. ATLTRACE("ReadWildcards(%s) failed, err=%d\n",
  81. strIniFile.c_str(), GetLastError());
  82. TCHAR *szSave = szBuffer;
  83. // now put all wild-card containing entries into the list
  84. while( *szBuffer != _T('\0') )
  85. {
  86. if (_tcspbrk(szBuffer, "[*?") != NULL)
  87. g_rgstrWildcard.push_back(szBuffer);
  88. // advance to the beginning of the next string
  89. while (*szBuffer != _T('\0'))
  90. szBuffer = CharNext(szBuffer);
  91. // now advance once more to get to the next string
  92. ++szBuffer;
  93. }
  94. delete[] szSave;
  95. g_rwWildcardLock.ExitWriter();
  96. }
  97. //---------------------------------------------------------------------
  98. // compare names to templates, *, ?, [, ], not legal filename characters
  99. //
  100. // Also compute # of matching wildcard characters.
  101. // FOR THIS TO WORK: caller must pass in an initialized counter!
  102. //---------------------------------------------------------------------
  103. bool
  104. match(
  105. LPCTSTR szPattern,
  106. LPCTSTR szSubject,
  107. int *pcchWildcardMatched)
  108. {
  109. LPTSTR rp;
  110. _TCHAR tc;
  111. if (*szPattern == '*')
  112. {
  113. ++szPattern;
  114. do
  115. {
  116. int cchWildcardSubMatch = 0;
  117. if (match(szPattern, szSubject, &cchWildcardSubMatch) == true)
  118. {
  119. *pcchWildcardMatched += cchWildcardSubMatch;
  120. return true;
  121. }
  122. } while (++*pcchWildcardMatched, *szSubject++ != '\0');
  123. }
  124. else if (*szSubject == '\0')
  125. return *szPattern == '\0';
  126. else if (*szPattern == '[' && (rp = _tcschr(szPattern, ']')) != NULL)
  127. {
  128. while (*++szPattern != ']')
  129. if ((tc = *szPattern) == *szSubject
  130. || (szPattern[1] == '-'
  131. && (*(szPattern += 2) >= *szSubject && tc <= *szSubject)))
  132. {
  133. ++*pcchWildcardMatched;
  134. return match(rp + 1, ++szSubject, pcchWildcardMatched);
  135. }
  136. return false;
  137. }
  138. else if (*szPattern == '?')
  139. {
  140. ++*pcchWildcardMatched;
  141. return match(++szPattern, ++szSubject, pcchWildcardMatched);
  142. }
  143. else if (tolower(*szPattern) == tolower(*szSubject))
  144. return match(++szPattern, ++szSubject, pcchWildcardMatched);
  145. return false;
  146. }
  147. //---------------------------------------------------------------------
  148. // FindBrowser
  149. //
  150. // match the User Agent against all the wildcards in browscap.ini and
  151. // return the best match. "Best Match" is defined here to mean the match
  152. // requiring the fewest amount of wildcard substitutions.
  153. //---------------------------------------------------------------------
  154. #define INT_MAX int(unsigned(~0) >> 1)
  155. String FindBrowser(const String &strUserAgent, const String &strIniFile)
  156. {
  157. TVector<String>::iterator iter;
  158. String strT;
  159. if (g_rgstrWildcard.size() == 0)
  160. ReadWildcards(strIniFile);
  161. g_rwWildcardLock.EnterReader();
  162. int cchWildMatchMin = INT_MAX;
  163. for (iter = g_rgstrWildcard.begin(); iter < g_rgstrWildcard.end(); ++iter)
  164. {
  165. int cchWildMatchCurrent = 0;
  166. if (match((*iter).c_str(), strUserAgent.c_str(), &cchWildMatchCurrent) &&
  167. cchWildMatchCurrent < cchWildMatchMin)
  168. {
  169. cchWildMatchMin = cchWildMatchCurrent;
  170. strT = *iter;
  171. }
  172. }
  173. g_rwWildcardLock.ExitReader();
  174. // Backward compatibility: If nothing matches, then use
  175. // "Default Browser Capability Settings". In the new
  176. // model, the catch all rule, "*" can also be used.
  177. //
  178. if (strT.length() == 0)
  179. strT = "Default Browser Capability Settings";
  180. return strT;
  181. }
  182. //---------------------------------------------------------------------
  183. // CCapNotify
  184. //---------------------------------------------------------------------
  185. CCapNotify::CCapNotify()
  186. : m_isNotified(0)
  187. {
  188. }
  189. void
  190. CCapNotify::Notify()
  191. {
  192. ::InterlockedExchange( &m_isNotified, 1 );
  193. }
  194. bool
  195. CCapNotify::IsNotified()
  196. {
  197. return ( ::InterlockedExchange( &m_isNotified, 0 ) ? true : false );
  198. }
  199. //---------------------------------------------------------------------
  200. // CCapMap
  201. //---------------------------------------------------------------------
  202. //////////////////////////////////////////////////////////////////////
  203. // Construction/Destruction
  204. //////////////////////////////////////////////////////////////////////
  205. CCapMap::CCapMap()
  206. {
  207. static const String cszIniFile = _T("Browscap.ini");
  208. // get the path to the inifile containing the browser cap info
  209. _TCHAR szModule[ _MAX_PATH ];
  210. ::GetModuleFileName(_Module.GetModuleInstance(), szModule, sizeof(szModule));
  211. ATLTRACE("CapMap: Module(%s)\n", szModule);
  212. // remove the filename and tack on the ini file name
  213. _TCHAR* pch = _tcsrchr(szModule, '\\');
  214. if (pch == NULL)
  215. {
  216. // the path should have at least one backslash
  217. _ASSERT(0);
  218. pch = szModule;
  219. }
  220. *(pch+1) = _T('\0');
  221. m_strIniFile = szModule + cszIniFile;
  222. ATLTRACE("CCapMap::CCapMap(%s)\n", m_strIniFile.c_str());
  223. // start monitoring the file
  224. m_pSink = new CCapNotify();
  225. }
  226. void
  227. CCapMap::StartMonitor()
  228. {
  229. if ( _Module.Monitor() )
  230. {
  231. _Module.Monitor()->MonitorFile( m_strIniFile.c_str(), m_pSink );
  232. ATLTRACE("CCapMap::StartMonitor(%s)\n", m_strIniFile.c_str());
  233. }
  234. else
  235. ATLTRACE("CCapMap::StartMonitor -- no monitor\n");
  236. }
  237. void
  238. CCapMap::StopMonitor()
  239. {
  240. if ( _Module.Monitor() )
  241. {
  242. _Module.Monitor()->StopMonitoringFile( m_strIniFile.c_str() );
  243. ATLTRACE("CCapMap::StopMonitor(%s)\n", m_strIniFile.c_str());
  244. }
  245. else
  246. ATLTRACE("CCapMap::StopMonitor -- no monitor\n");
  247. }
  248. CBrowserCap *
  249. CCapMap::LookUp(
  250. const String& szBrowser)
  251. {
  252. Refresh();
  253. CLock csT(g_strmapBrowsCapINI);
  254. CacheMapT::referent_type &rpBCobj = g_strmapBrowsCapINI[szBrowser];
  255. if (rpBCobj == NULL)
  256. {
  257. rpBCobj = new CComObject<CBrowserCap>;
  258. // Complete construction and AddRef copy we keep in cache.
  259. // NOTE: Since caller (class factory) does implicit AddRef via QueryInterface,
  260. // the convention of this function is slightly different from COM std.
  261. // CALLER IS RESPONSIBLE TO ADDREF RETURNED OBJECT
  262. //
  263. rpBCobj->FinalConstruct();
  264. rpBCobj->AddRef();
  265. // ATLTRACE("LookUp(%s)\n", szBrowser.c_str());
  266. // Get Browser Properties
  267. _TCHAR szSection[DWSectionBufSize];
  268. if (GetPrivateProfileSection
  269. (
  270. szBrowser.c_str(), // section
  271. szSection, // return buffer
  272. DWSectionBufSize, // size of return buffer
  273. m_strIniFile.c_str() // .INI name
  274. ) == 0)
  275. {
  276. // If this call fails, that means the default browser does not exist either, so
  277. // everything is "unknown".
  278. //
  279. String szT = FindBrowser(szBrowser, m_strIniFile);
  280. if (GetPrivateProfileSection
  281. (
  282. szT.c_str(), // section
  283. szSection, // return buffer
  284. DWSectionBufSize, // size of return buffer
  285. m_strIniFile.c_str() // .INI name
  286. ) == 0)
  287. {
  288. ATLTRACE("GPPS(%s) failed, err=%d\n",
  289. szT.c_str(), GetLastError());
  290. return rpBCobj;
  291. }
  292. }
  293. // Loop through szSection, which contains all the key=value pairs and add them
  294. // to the browser instance property list. If we find a "Parent=" Key, save the
  295. // value to add the parent's properties later.
  296. //
  297. TCHAR *szParent;
  298. do
  299. {
  300. szParent = NULL;
  301. TCHAR *szKeyAndValue = szSection;
  302. while (*szKeyAndValue)
  303. {
  304. TCHAR *szKey = szKeyAndValue; // save the key
  305. TCHAR *szValue = _tcschr(szKey, '='); // find address of value part (-1)
  306. szKeyAndValue += _tcslen(szKeyAndValue) + 1; // advance KeyAndValue to the next pair
  307. if (szValue == NULL)
  308. continue;
  309. *szValue++ = '\0'; // separate key and value with NUL; advance
  310. if (_tcsicmp(szKey, _T("Parent")) == 0)
  311. szParent = szValue;
  312. else
  313. rpBCobj->AddProperty(szKey, szValue);
  314. }
  315. // We stored all the attributes on this level. Ascend to parent level (if it exists)
  316. if (szParent)
  317. {
  318. if (GetPrivateProfileSection
  319. (
  320. szParent, // section
  321. szSection, // return buffer
  322. DWSectionBufSize, // size of return buffer
  323. m_strIniFile.c_str() // .INI name
  324. ) == 0)
  325. {
  326. // If this call fails, quit now.
  327. //
  328. String szT = FindBrowser(szParent, m_strIniFile);
  329. if (GetPrivateProfileSection
  330. (
  331. szT.c_str(), // section
  332. szSection, // return buffer
  333. DWSectionBufSize, // size of return buffer
  334. m_strIniFile.c_str() // .INI name
  335. ) == 0)
  336. {
  337. ATLTRACE("GPPS(%s) failed, err=%d\n",
  338. szT.c_str(), GetLastError());
  339. return rpBCobj;
  340. }
  341. }
  342. }
  343. } while (szParent);
  344. }
  345. return rpBCobj;
  346. }
  347. //---------------------------------------------------------------------------
  348. //
  349. // Refresh will check to see if the cached information is out of date with
  350. // the ini file. If so, the cached will be purged
  351. //
  352. //---------------------------------------------------------------------------
  353. bool
  354. CCapMap::Refresh()
  355. {
  356. bool rc = false;
  357. if ( m_pSink->IsNotified() )
  358. {
  359. // Clear the cache
  360. CLock csT(g_strmapBrowsCapINI);
  361. g_strmapBrowsCapINI.clear();
  362. rc = true;
  363. // clear the list of wildcards.
  364. // NOTE: each browser request creates new CCapMap object.
  365. // the constructor will see the size is zero and reconstruct
  366. g_rwWildcardLock.EnterWriter();
  367. g_rgstrWildcard.clear();
  368. g_rwWildcardLock.ExitWriter();
  369. }
  370. return rc;
  371. }