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.

2072 lines
60 KiB

  1. /*----------------------------------------------------------------------------
  2. pbserver.cpp
  3. CPhoneBkServer class implementation
  4. Copyright (c) 1997-1998 Microsoft Corporation
  5. All rights reserved.
  6. Authors:
  7. byao Baogang Yao
  8. History:
  9. 1/23/97 byao -- Created
  10. 5/29/97 t-geetat -- Modified -- added performance counters,
  11. shared memory
  12. 5/02/00 sumitc -- removed db dependency
  13. --------------------------------------------------------------------------*/
  14. #include <windows.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <assert.h>
  18. #include <string.h>
  19. #include <tchar.h>
  20. #include <aclapi.h>
  21. #include "common.h"
  22. #include "pbserver.h"
  23. #include "ntevents.h"
  24. #include "cpsmon.h"
  25. #include "shlobj.h"
  26. #include "shfolder.h"
  27. #include "util.h"
  28. //
  29. // Phone book "database" implementation
  30. //
  31. char g_szPBDataDir[2 * MAX_PATH] = "";
  32. BOOL StrEqualLocaleSafe(LPSTR psz1, LPSTR psz2);
  33. HRESULT GetPhoneBook(char * pszPBName,
  34. int dLCID,
  35. int dOSType,
  36. int dOSArch,
  37. int dPBVerCurrent,
  38. char * pszDownloadPath,
  39. UINT cchDownloadPath);
  40. const DWORD MAX_BUFFER_SIZE = 1024; // maximum size of input buffer
  41. const DWORD SEND_BUFFER_SIZE = 4096; // block size when sending CAB file
  42. const int dDefPBVer = 0; // default phone book version number
  43. const int MISSING_VALUE = -1; // if parameter-pair is empty, it is set to this value
  44. const int MAX_PB_SIZE = 999999; // max pb size is 1 MB
  45. char g_achDBDirectory[2 * MAX_PATH] = "";
  46. // constant strings
  47. char c_szChangeFileName[] = "newpb.txt"; // newpb.txt
  48. char c_szDBName[] = "pbserver"; // "pbserver" -- data source name
  49. // the following error status code/string is copied from ISAPI.CPP
  50. // which is part of the MFC library source code
  51. typedef struct _httpstatinfo {
  52. DWORD dwCode;
  53. LPCTSTR pstrString;
  54. } HTTPStatusInfo;
  55. //
  56. // The following two structures are used in the SystemTimeToGMT function
  57. //
  58. static TCHAR * s_rgchDays[] =
  59. {
  60. TEXT("Sun"),
  61. TEXT("Mon"),
  62. TEXT("Tue"),
  63. TEXT("Wed"),
  64. TEXT("Thu"),
  65. TEXT("Fri"),
  66. TEXT("Sat")
  67. };
  68. static TCHAR * s_rgchMonths[] =
  69. {
  70. TEXT("Jan"),
  71. TEXT("Feb"),
  72. TEXT("Mar"),
  73. TEXT("Apr"),
  74. TEXT("May"),
  75. TEXT("Jun"),
  76. TEXT("Jul"),
  77. TEXT("Aug"),
  78. TEXT("Sep"),
  79. TEXT("Oct"),
  80. TEXT("Nov"),
  81. TEXT("Dec")
  82. };
  83. static HTTPStatusInfo statusStrings[] =
  84. {
  85. { HTTP_STATUS_OK, "OK" },
  86. { HTTP_STATUS_CREATED, "Created" },
  87. { HTTP_STATUS_ACCEPTED, "Accepted" },
  88. { HTTP_STATUS_NO_CONTENT, "No download Necessary" },
  89. { HTTP_STATUS_TEMP_REDIRECT, "Moved Temporarily" },
  90. { HTTP_STATUS_REDIRECT, "Moved Permanently" },
  91. { HTTP_STATUS_NOT_MODIFIED, "Not Modified" },
  92. { HTTP_STATUS_BAD_REQUEST, "Bad Request" },
  93. { HTTP_STATUS_AUTH_REQUIRED, "Unauthorized" },
  94. { HTTP_STATUS_FORBIDDEN, "Forbidden" },
  95. { HTTP_STATUS_NOT_FOUND, "Not Found" },
  96. { HTTP_STATUS_SERVER_ERROR, "Server error, type unknown" },
  97. { HTTP_STATUS_NOT_IMPLEMENTED, "Not Implemented" },
  98. { HTTP_STATUS_BAD_GATEWAY, "Bad Gateway" },
  99. { HTTP_STATUS_SERVICE_NA, "Cannot find service on server, bad request" },
  100. { 0, NULL }
  101. };
  102. // Server asynchronized I/O context
  103. typedef struct _SERVER_CONTEXT
  104. {
  105. EXTENSION_CONTROL_BLOCK * pEcb;
  106. HSE_TF_INFO hseTF;
  107. TCHAR szBuffer[SEND_BUFFER_SIZE];
  108. }
  109. SERVERCONTEXT, *LPSERVERCONTEXT;
  110. DWORD WINAPI MonitorDBFileChangeThread(LPVOID lpParam);
  111. BOOL InitPBFilesPath();
  112. //
  113. // definition of global data
  114. // All the following variable(object) can only have one instance
  115. //
  116. CPhoneBkServer * g_pPBServer; // Phone Book Server object
  117. CNTEvent * g_pEventLog; // event log
  118. HANDLE g_hMonitorThread; // the monitor thread that checks the new file notification
  119. HANDLE g_hProcessHeap; // handle of the global heap for the extension process;
  120. BOOL g_fBeingShutDown = FALSE; // whether the system is being shut down
  121. //
  122. // Variables used in memory mapping
  123. //
  124. CCpsCounter *g_pCpsCounter = NULL; // Pointer to global counter object (contains memory mapped counters)
  125. ////////////////////////////////////////////////////////////////////////
  126. //
  127. // Name: GetExtensionVersion
  128. //
  129. // Class: CPhoneBkServer
  130. //
  131. // Synopsis: implement the first DLL entry point function
  132. //
  133. //
  134. // Return: TRUE succeed
  135. // FALSE
  136. //
  137. // Parameters:
  138. // pszVer[out] version information that needs to be filled out
  139. //
  140. BOOL CPhoneBkServer::GetExtensionVersion(LPHSE_VERSION_INFO pVer)
  141. {
  142. // Set version number
  143. pVer -> dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR,
  144. HSE_VERSION_MAJOR);
  145. // Load description string
  146. lstrcpyn(pVer->lpszExtensionDesc,
  147. "Connection Point Server Application",
  148. HSE_MAX_EXT_DLL_NAME_LEN);
  149. OutputDebugString("CPhoneBkServer.GetExtensionVersion() : succeeded \n");
  150. return TRUE;
  151. }
  152. //////////////////////////////////////////////////////////////////////////////
  153. //
  154. // Name: GetParameterPairs
  155. //
  156. // Class: CPhoneBkServer
  157. //
  158. // Synopsis: Get the parameter-value pairs from an input string(from URL)
  159. //
  160. // Return: number of parameter pairs actually read
  161. // a value of -1 stands for error --> INVALID_QUERY_STRING
  162. //
  163. // Parameter:
  164. // pszInputString[in] Input string (null terminated)
  165. // cchInputString[in] size of input string buffer in chars
  166. // lpPairs[out] Pointer to the parameter/value pairs
  167. // int dMaxPairs Maximum number of parameter pairs allowed
  168. //
  169. int CPhoneBkServer::GetParameterPairs(
  170. IN char *pszInputString,
  171. IN size_t cchInputString,
  172. OUT LPPARAMETER_PAIR lpPairs,
  173. IN int dMaxPairs)
  174. {
  175. int i = 0;
  176. if (NULL == lpPairs)
  177. {
  178. // actually this is an internal error...
  179. return INVALID_QUERY_STRING;
  180. }
  181. if (NULL == pszInputString || IsBadStringPtr(pszInputString, cchInputString))
  182. {
  183. return INVALID_QUERY_STRING;
  184. }
  185. for(i = 0; pszInputString[0] != '\0' && i < dMaxPairs; i++)
  186. {
  187. // m_achVal == 'p=what%3F';
  188. GetWord(lpPairs[i].m_achVal, pszInputString, '&', NAME_VALUE_LEN - 1);
  189. // FUTURE-2002/03/11-SumitC if we can confirm/ensure that cmdl32 won't do escapes, we can remove the Unescape code.
  190. // m_achVal == 'p=what?'
  191. UnEscapeURL(lpPairs[i].m_achVal);
  192. GetWord(lpPairs[i].m_achName,lpPairs[i].m_achVal,'=', NAME_VALUE_LEN - 1);
  193. // m_achVal = what?
  194. // m_achName = p
  195. }
  196. #ifdef _LOG_DEBUG_MESSAGE
  197. LogDebugMessage("inside GetParameterPairs: dNumPairs : %d", i);
  198. if (pszInputString[0] != '\0')
  199. LogDebugMessage("there are more parameters\n");
  200. #endif
  201. if (pszInputString[0] != '\0')
  202. {
  203. // more parameters available
  204. return INVALID_QUERY_STRING;
  205. }
  206. else
  207. {
  208. //succeed
  209. return i;
  210. }
  211. }
  212. ////////////////////////////////////////////////////////////////////////
  213. //
  214. // Name: GetQueryParameter
  215. //
  216. // Class: CPhoneBkServer
  217. //
  218. // Synopsis: scan through the query string, and get the value for all
  219. // query parameters
  220. //
  221. // Return: TRUE all query parameters are correct
  222. // FALSE invalid parameter existed
  223. //
  224. // Parameters:
  225. // pszQuery[in] Query string from the client(URL encripted)
  226. // cchQuery[in] size of pszQuery buffer in characters
  227. // pQueryParameter[out] pointer to the query parameters structure
  228. //
  229. //
  230. BOOL CPhoneBkServer::GetQueryParameter(
  231. IN char *pszQuery,
  232. IN size_t cchQuery,
  233. OUT LPQUERY_PARAMETER lpQueryParameter)
  234. {
  235. const int MAX_PARAMETER_NUM = 7;
  236. PARAMETER_PAIR Pairs[MAX_PARAMETER_NUM];
  237. int dNumPairs, i;
  238. //
  239. // Validate parameters
  240. //
  241. if (IsBadStringPtr(pszQuery, cchQuery))
  242. {
  243. return FALSE;
  244. }
  245. if (IsBadWritePtr(lpQueryParameter, sizeof(QUERY_PARAMETER)))
  246. {
  247. return FALSE;
  248. }
  249. #ifdef _LOG_DEBUG_MESSAGE
  250. LogDebugMessage("pszquery=%s", pszQuery);
  251. #endif
  252. //
  253. // Get the name=value pairs
  254. //
  255. dNumPairs = GetParameterPairs(pszQuery, cchQuery, Pairs, MAX_PARAMETER_NUM);
  256. #ifdef _LOG_DEBUG_MESSAGE
  257. LogDebugMessage("number of pairs : %d", dNumPairs);
  258. #endif
  259. if (INVALID_QUERY_STRING == dNumPairs) // invalid number of parameters in query string
  260. {
  261. return FALSE;
  262. }
  263. //
  264. // initialize the parameter values to invalid values so we can check validity later
  265. //
  266. lpQueryParameter->m_achPB[0] ='\0';
  267. lpQueryParameter->m_dPBVer = MISSING_VALUE;
  268. lpQueryParameter->m_dOSArch = MISSING_VALUE;
  269. lpQueryParameter->m_dOSType = MISSING_VALUE;
  270. lpQueryParameter->m_dLCID = MISSING_VALUE;
  271. lpQueryParameter->m_achCMVer[0] = '\0';
  272. lpQueryParameter->m_achOSVer[0] = '\0';
  273. for (i = 0; i < dNumPairs; i++)
  274. {
  275. // we know this string is null terminated (due to GetParameterPairs/GetWord), so _strlwr is safe to call
  276. _strlwr(Pairs[i].m_achName);
  277. UINT lenValue = lstrlen(Pairs[i].m_achVal);
  278. if (StrEqualLocaleSafe(Pairs[i].m_achName, "osarch"))
  279. {
  280. if (IsValidNumericParam(Pairs[i].m_achVal, NAME_VALUE_LEN))
  281. {
  282. lpQueryParameter->m_dOSArch = atoi(Pairs[i].m_achVal);
  283. }
  284. }
  285. else if (StrEqualLocaleSafe(Pairs[i].m_achName, "ostype"))
  286. {
  287. if (IsValidNumericParam(Pairs[i].m_achVal, NAME_VALUE_LEN))
  288. {
  289. lpQueryParameter->m_dOSType = atoi(Pairs[i].m_achVal);
  290. }
  291. }
  292. else if (StrEqualLocaleSafe(Pairs[i].m_achName,"lcid"))
  293. {
  294. if (IsValidNumericParam(Pairs[i].m_achVal, NAME_VALUE_LEN))
  295. {
  296. lpQueryParameter->m_dLCID = atoi(Pairs[i].m_achVal);
  297. }
  298. }
  299. else if (StrEqualLocaleSafe(Pairs[i].m_achName,"osver"))
  300. {
  301. if (IsValidStringParam(Pairs[i].m_achVal, NAME_VALUE_LEN))
  302. {
  303. if (S_OK != StringCchCopy(lpQueryParameter->m_achOSVer, CELEMS(lpQueryParameter->m_achOSVer), Pairs[i].m_achVal))
  304. {
  305. lpQueryParameter->m_achOSVer[0] = TEXT('\0');
  306. }
  307. }
  308. }
  309. else if (StrEqualLocaleSafe(Pairs[i].m_achName,"cmver"))
  310. {
  311. if (IsValidStringParam(Pairs[i].m_achVal, NAME_VALUE_LEN))
  312. {
  313. if (S_OK != StringCchCopy(lpQueryParameter->m_achCMVer, CELEMS(lpQueryParameter->m_achCMVer), Pairs[i].m_achVal))
  314. {
  315. lpQueryParameter->m_achCMVer[0] = TEXT('\0');
  316. }
  317. }
  318. }
  319. else if (StrEqualLocaleSafe(Pairs[i].m_achName,"pb"))
  320. {
  321. if (IsValidStringParam(Pairs[i].m_achVal, NAME_VALUE_LEN))
  322. {
  323. if (S_OK != StringCchCopy(lpQueryParameter->m_achPB, CELEMS(lpQueryParameter->m_achPB), Pairs[i].m_achVal))
  324. {
  325. lpQueryParameter->m_achPB[0] = TEXT('\0');
  326. }
  327. }
  328. }
  329. else if (StrEqualLocaleSafe(Pairs[i].m_achName,"pbver"))
  330. {
  331. if (IsValidNumericParam(Pairs[i].m_achVal, NAME_VALUE_LEN))
  332. {
  333. lpQueryParameter->m_dPBVer = atoi(Pairs[i].m_achVal);
  334. }
  335. }
  336. // else, we might log/trace that we got a bogus param in the URL
  337. }
  338. #ifdef _LOG_DEBUG_MESSAGE
  339. LogDebugMessage("osarch:%d ostype:%d lcid:%d osver:%s cmver:%s PB:%s PBVer:%d",
  340. lpQueryParameter->m_dOSArch,
  341. lpQueryParameter->m_dOSType,
  342. lpQueryParameter->m_dLCID,
  343. lpQueryParameter->m_achOSVer,
  344. lpQueryParameter->m_achCMVer,
  345. lpQueryParameter->m_achPB,
  346. lpQueryParameter->m_dPBVer);
  347. #endif
  348. return TRUE;
  349. }
  350. //----------------------------------------------------------------------------
  351. //
  352. // Function: GetFileLength()
  353. //
  354. // Class: CPhoneBkServer
  355. //
  356. // Synopsis: Reads the pszFileName file and sends back the file size
  357. //
  358. // Arguments: lpszFileName - Contains the file name (with full path)]
  359. //
  360. // Returns: TRUE if succeed, otherwise FALSE;
  361. //
  362. // History: 03/07/97 byao Created
  363. //
  364. //----------------------------------------------------------------------------
  365. DWORD CPhoneBkServer::GetFileLength(LPSTR lpszFileName)
  366. {
  367. HANDLE hFile = INVALID_HANDLE_VALUE;
  368. DWORD dwFileSize;
  369. //
  370. // Open file
  371. //
  372. hFile = CreateFile(lpszFileName,
  373. GENERIC_READ,
  374. FILE_SHARE_READ,
  375. NULL,
  376. OPEN_EXISTING,
  377. FILE_FLAG_SEQUENTIAL_SCAN,
  378. NULL);
  379. if (INVALID_HANDLE_VALUE == hFile)
  380. return 0L;
  381. //
  382. // Get File Size
  383. //
  384. dwFileSize = GetFileSize(hFile, NULL);
  385. CloseHandle(hFile);
  386. if (INVALID_FILE_SIZE == dwFileSize)
  387. {
  388. dwFileSize = 0;
  389. }
  390. return dwFileSize;
  391. }
  392. ////////////////////////////////////////////////////////////////////////
  393. //
  394. // Name: StrEqualLocaleSafe
  395. //
  396. // Synopsis: Locale-safe case-insensitive string comparison (per PREfast)
  397. //
  398. // Return: BOOL, TRUE => strings psz1 and psz2 are equal
  399. //
  400. BOOL StrEqualLocaleSafe(LPSTR psz1, LPSTR psz2)
  401. {
  402. return (CSTR_EQUAL == CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, psz1, -1, psz2, -1));
  403. }
  404. //----------------------------------------------------------------------------
  405. //
  406. // Function: SystemTimeToGMT
  407. //
  408. // Synopsis: Converts the given system time to string representation
  409. // containing GMT Formatted String
  410. //
  411. // Arguments: [st System time that needs to be converted *Reference*]
  412. // [pstr pointer to string which will contain the GMT time
  413. // on successful return]
  414. // [cbBuff size of pszBuff in chars]
  415. //
  416. // Returns: TRUE on success. FALSE on failure.
  417. //
  418. // History: 04/12/97 VetriV Created (from IIS source)
  419. //
  420. //----------------------------------------------------------------------------
  421. BOOL SystemTimeToGMT(const SYSTEMTIME & st, LPSTR pszBuff, UINT cchBuff)
  422. {
  423. assert(cchBuff < 40); // 40 is an estimated maximum given the current formatting
  424. if (!pszBuff || cchBuff < 40 )
  425. {
  426. return FALSE;
  427. }
  428. //
  429. // Formats a string like: "Thu, 14 Jul 1994 15:26:05 GMT"
  430. //
  431. StringCchPrintf(pszBuff, cchBuff, "%s, %02d %s %d %02d:%02d:%0d GMT",
  432. s_rgchDays[st.wDayOfWeek],
  433. st.wDay,
  434. s_rgchMonths[st.wMonth - 1],
  435. st.wYear,
  436. st.wHour,
  437. st.wMinute,
  438. st.wSecond);
  439. return TRUE;
  440. }
  441. //----------------------------------------------------------------------------
  442. //
  443. // Function: FormHttpHeader
  444. //
  445. // Synopsis: Form's the IIS 3.0 HTTP Header
  446. //
  447. // Arguments: pszBuffer Buffer that will contain both header and status text
  448. // cchBuffer size of buffer in chars
  449. // pszResponse status text
  450. // pszExtraHeader extra header information
  451. //
  452. // Returns: ERROR_SUCCESS on success. Error code on failure.
  453. //
  454. // History: 04/12/97 VetriV Created
  455. // 05/22/97 byao Modified, to make it work with CPS server
  456. //----------------------------------------------------------------------------
  457. HRESULT
  458. FormHttpHeader(LPSTR pszBuffer, UINT cchBuffer, LPSTR pszResponse, LPSTR pszExtraHeader)
  459. {
  460. if (!pszBuffer || !pszResponse || !pszExtraHeader)
  461. {
  462. OutputDebugString("FormHttpHeader: bad params!\n");
  463. return E_INVALIDARG;
  464. }
  465. //
  466. // Get the time in string format
  467. //
  468. SYSTEMTIME SysTime;
  469. CHAR szTime[128] = { 0 };
  470. GetSystemTime(&SysTime);
  471. if (FALSE == SystemTimeToGMT(SysTime, szTime, CELEMS(szTime)))
  472. {
  473. return E_UNEXPECTED;
  474. }
  475. //
  476. // Now create header with
  477. // - standard IIS header
  478. // - date and time
  479. // - extra header string
  480. //
  481. return StringCchPrintf(pszBuffer, cchBuffer,
  482. "HTTP/1.0 %s\r\nServer: Microsoft-IIS/3.0\r\nDate: %s\r\n%s",
  483. pszResponse,
  484. szTime,
  485. pszExtraHeader);
  486. }
  487. //----------------------------------------------------------------------------
  488. //
  489. // Function: HseIoCompletion
  490. //
  491. // Synopsis: Callback routine that handles asynchronous WriteClient
  492. // completion callback
  493. //
  494. // Arguments: [pECB - Extension Control Block]
  495. // [pContext - Pointer to the AsyncWrite structure]
  496. // [cbIO - Number of bytes written]
  497. // [dwError - Error code if there was an error while writing]
  498. //
  499. // Returns: None
  500. //
  501. // History: 04/10/97 VetriV Created
  502. // 05/22/97 byao Modified to make it work for CPS server
  503. //
  504. //----------------------------------------------------------------------------
  505. VOID HseIoCompletion(EXTENSION_CONTROL_BLOCK * pEcb,
  506. PVOID pContext,
  507. DWORD cbIO,
  508. DWORD dwError)
  509. {
  510. LPSERVERCONTEXT lpServerContext = (LPSERVERCONTEXT) pContext;
  511. if (!lpServerContext)
  512. {
  513. return;
  514. }
  515. lpServerContext->pEcb->ServerSupportFunction(
  516. lpServerContext->pEcb->ConnID,
  517. HSE_REQ_DONE_WITH_SESSION,
  518. NULL,
  519. NULL,
  520. NULL);
  521. if (lpServerContext->hseTF.hFile != INVALID_HANDLE_VALUE)
  522. {
  523. CloseHandle(lpServerContext->hseTF.hFile);
  524. }
  525. HeapFree(g_hProcessHeap, 0, lpServerContext);
  526. SetLastError(dwError);
  527. return;
  528. }
  529. ////////////////////////////////////////////////////////////////////////
  530. //
  531. // Name: HttpExtensionProc
  532. //
  533. // Class: CPhoneBkServer
  534. //
  535. // Synopsis: implement the second DLL entry point function
  536. //
  537. // Return: HTTP status code
  538. //
  539. // Parameters:
  540. // pEcb[in/out] - extension control block
  541. //
  542. // History: Modified byao 5/22/97
  543. // new implementation: using asynchronized I/O
  544. // Modified t-geetat : Added PerfMon counters
  545. //
  546. /////////////////////////////////////////////////////////////////////////
  547. DWORD CPhoneBkServer:: HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK pEcb)
  548. {
  549. DWORD dwBufferLen = MAX_BUFFER_SIZE;
  550. char achQuery[MAX_BUFFER_SIZE];
  551. char achMsg[MAX_PATH + 13 + 1]; // 13 = %u + formatting, see uses of achMsg below
  552. char achPhysicalPath[MAX_PATH];
  553. int dVersionDiff; // version difference between client & server's phone books
  554. BOOL fRet;
  555. DWORD dwStatusCode = NOERROR;
  556. int dwRet;
  557. DWORD dwCabFileSize;
  558. BOOL fHasContent = FALSE;
  559. CHAR szResponse[64];
  560. char achExtraHeader[128];
  561. char achHttpHeader[1024];
  562. DWORD dwResponseSize;
  563. LPSERVERCONTEXT lpServerContext;
  564. HSE_TF_INFO hseTF;
  565. QUERY_PARAMETER QueryParameter;
  566. assert(g_pCpsCounter);
  567. if (g_pCpsCounter)
  568. {
  569. g_pCpsCounter->AddHit(TOTAL);
  570. }
  571. // get the query string
  572. fRet = (*pEcb->GetServerVariable)(pEcb->ConnID,
  573. "QUERY_STRING",
  574. achQuery,
  575. &dwBufferLen);
  576. //
  577. // If there is an error, log an NT event and leave.
  578. //
  579. if (!fRet)
  580. {
  581. dwStatusCode = GetLastError();
  582. #ifdef _LOG_DEBUG_MESSAGE
  583. if (0 != FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
  584. NULL,
  585. dwStatusCode,
  586. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  587. achMsg,
  588. CELEMS(achMsg),
  589. NULL))
  590. {
  591. LogDebugMessage(achMsg);
  592. }
  593. #endif
  594. if (S_OK == StringCchPrintf(achMsg, CELEMS(achMsg), "%ld", dwStatusCode))
  595. {
  596. g_pEventLog -> FLogError(PBSERVER_CANT_GET_PARAMETER, achMsg);
  597. }
  598. // if we failed because the request was too big, map the error
  599. if (ERROR_INSUFFICIENT_BUFFER == dwStatusCode)
  600. {
  601. dwStatusCode = HTTP_STATUS_BAD_REQUEST;
  602. }
  603. goto CleanUp;
  604. }
  605. #ifdef _LOG_DEBUG_MESSAGE
  606. LogDebugMessage("prepare to get query parameters");
  607. #endif
  608. //
  609. // parse the query string, get the value of each parameter
  610. //
  611. if (FALSE == GetQueryParameter(achQuery, CELEMS(achQuery), &QueryParameter))
  612. {
  613. dwStatusCode = HTTP_STATUS_BAD_REQUEST;
  614. goto CleanUp;
  615. }
  616. //
  617. // check the validity of the parameters
  618. //
  619. if (MISSING_VALUE == QueryParameter.m_dOSArch ||
  620. MISSING_VALUE == QueryParameter.m_dOSType ||
  621. MISSING_VALUE == QueryParameter.m_dLCID ||
  622. 0 == lstrlen(QueryParameter.m_achCMVer) ||
  623. 0 == lstrlen(QueryParameter.m_achOSVer) ||
  624. 0 == lstrlen(QueryParameter.m_achPB))
  625. {
  626. dwStatusCode = HTTP_STATUS_BAD_REQUEST;
  627. goto CleanUp;
  628. }
  629. //
  630. // Use defaults for some missing values
  631. //
  632. if (MISSING_VALUE == QueryParameter.m_dPBVer)
  633. {
  634. QueryParameter.m_dPBVer = dDefPBVer;
  635. }
  636. // DebugBreak();
  637. HRESULT hr;
  638. hr = GetPhoneBook(QueryParameter.m_achPB,
  639. QueryParameter.m_dLCID,
  640. QueryParameter.m_dOSType,
  641. QueryParameter.m_dOSArch,
  642. QueryParameter.m_dPBVer,
  643. achPhysicalPath,
  644. CELEMS(achPhysicalPath));
  645. fHasContent = FALSE;
  646. if (S_OK == hr)
  647. {
  648. //
  649. // check the size of the phone book
  650. //
  651. DWORD dwSize = GetFileLength(achPhysicalPath);
  652. if ((dwSize == 0) || (dwSize > MAX_PB_SIZE))
  653. {
  654. dwStatusCode = HTTP_STATUS_SERVER_ERROR;
  655. goto CleanUp;
  656. }
  657. fHasContent = TRUE;
  658. dwStatusCode = HTTP_STATUS_OK;
  659. }
  660. else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
  661. {
  662. // we couldn't find the required file (phonebook name is probably bad)
  663. dwStatusCode = HTTP_STATUS_SERVICE_NA;
  664. }
  665. else if (S_FALSE == hr)
  666. {
  667. // you don't need a phone book
  668. dwStatusCode = HTTP_STATUS_NO_CONTENT;
  669. }
  670. else
  671. {
  672. // some other error
  673. dwStatusCode = HTTP_STATUS_SERVER_ERROR;
  674. }
  675. CleanUp:
  676. if (HTTP_STATUS_OK != dwStatusCode && HTTP_STATUS_NO_CONTENT != dwStatusCode)
  677. {
  678. if (g_pCpsCounter)
  679. {
  680. g_pCpsCounter->AddHit(ERRORS);
  681. }
  682. }
  683. // DebugBreak();
  684. #ifdef _LOG_DEBUG_MESSAGE
  685. LogDebugMessage("download file:");
  686. LogDebugMessage(achPhysicalPath);
  687. #endif
  688. // convert virtual path to physical path
  689. if (fHasContent)
  690. {
  691. // get cab file size
  692. dwCabFileSize = GetFileLength(achPhysicalPath);
  693. }
  694. BuildStatusCode(szResponse, CELEMS(szResponse), dwStatusCode);
  695. dwResponseSize = lstrlen(szResponse);
  696. dwRet = HSE_STATUS_SUCCESS;
  697. // prepare for the header
  698. if (HTTP_STATUS_OK == dwStatusCode && dwCabFileSize)
  699. {
  700. // not a NULL cab file
  701. StringCchPrintf(achExtraHeader, CELEMS(achExtraHeader),
  702. "Content-Type: application/octet-stream\r\nContent-Length: %ld\r\n\r\n",
  703. dwCabFileSize);
  704. }
  705. else
  706. {
  707. StringCchCopy(achExtraHeader, CELEMS(achExtraHeader), "\r\n"); // just send back an empty line
  708. }
  709. // set up asynchronized I/O context
  710. lpServerContext = NULL;
  711. lpServerContext = (LPSERVERCONTEXT) HeapAlloc(g_hProcessHeap,
  712. HEAP_ZERO_MEMORY,
  713. sizeof(SERVERCONTEXT));
  714. if (!lpServerContext)
  715. {
  716. StringCchPrintf(achMsg, CELEMS(achMsg), "%ld", GetLastError());
  717. g_pEventLog->FLogError(PBSERVER_ERROR_INTERNAL, achMsg);
  718. return HSE_STATUS_ERROR;
  719. }
  720. lpServerContext->pEcb = pEcb;
  721. lpServerContext->hseTF.hFile = INVALID_HANDLE_VALUE;
  722. if (!pEcb->ServerSupportFunction(pEcb->ConnID,
  723. HSE_REQ_IO_COMPLETION,
  724. HseIoCompletion,
  725. 0,
  726. (LPDWORD) lpServerContext))
  727. {
  728. StringCchPrintf(achMsg, CELEMS(achMsg), "%ld", GetLastError());
  729. g_pEventLog->FLogError(PBSERVER_ERROR_INTERNAL, achMsg);
  730. HeapFree(g_hProcessHeap, 0, lpServerContext);
  731. return HSE_STATUS_ERROR;
  732. }
  733. // if there's no content, send header and status code back using WriteClient();
  734. // otherwise, use TransmitFile to send the file content back
  735. // FUTURE-2002/03/11-SumitC why not use lpservercontext->szBuffer instead of achHttpHeader?
  736. if (FAILED(FormHttpHeader(achHttpHeader, CELEMS(achHttpHeader), szResponse, achExtraHeader)))
  737. {
  738. HeapFree(g_hProcessHeap, 0, lpServerContext);
  739. return HSE_STATUS_ERROR;
  740. }
  741. if (S_OK != StringCchCopy(lpServerContext->szBuffer, CELEMS(lpServerContext->szBuffer), achHttpHeader))
  742. {
  743. HeapFree(g_hProcessHeap, 0, lpServerContext);
  744. return HSE_STATUS_ERROR;
  745. }
  746. //
  747. // send status code or the file back
  748. //
  749. dwRet = HSE_STATUS_PENDING;
  750. if (!fHasContent)
  751. {
  752. // Append status text as the content
  753. StringCchCat(lpServerContext->szBuffer, CELEMS(lpServerContext->szBuffer), szResponse);
  754. dwResponseSize = lstrlen(lpServerContext->szBuffer);
  755. if (pEcb->WriteClient(pEcb->ConnID,
  756. lpServerContext->szBuffer,
  757. &dwResponseSize,
  758. HSE_IO_ASYNC) == FALSE)
  759. {
  760. pEcb->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
  761. dwRet = HSE_STATUS_ERROR;
  762. if (S_OK == StringCchPrintf(achMsg, CELEMS(achMsg), "%ld", GetLastError()))
  763. {
  764. g_pEventLog->FLogError(PBSERVER_ERROR_CANT_SEND_HEADER, achMsg);
  765. }
  766. HeapFree(g_hProcessHeap, 0, lpServerContext);
  767. return dwRet;
  768. }
  769. }
  770. else
  771. {
  772. // send file back using TransmitFile
  773. HANDLE hFile = INVALID_HANDLE_VALUE;
  774. hFile = CreateFile(achPhysicalPath,
  775. GENERIC_READ,
  776. FILE_SHARE_READ,
  777. NULL,
  778. OPEN_EXISTING,
  779. FILE_FLAG_SEQUENTIAL_SCAN,
  780. NULL);
  781. if (INVALID_HANDLE_VALUE == hFile)
  782. {
  783. if (S_OK == StringCchPrintf(achMsg, CELEMS(achMsg), "%s (%u)", achPhysicalPath, GetLastError()))
  784. {
  785. g_pEventLog->FLogError(PBSERVER_ERROR_CANT_OPEN_FILE, achMsg);
  786. }
  787. HeapFree(g_hProcessHeap, 0, lpServerContext);
  788. return HSE_STATUS_ERROR;
  789. }
  790. lpServerContext->hseTF.hFile = hFile;
  791. lpServerContext->hseTF.pfnHseIO = NULL;
  792. lpServerContext->hseTF.pContext = lpServerContext;
  793. lpServerContext->hseTF.BytesToWrite = 0; // entire file
  794. lpServerContext->hseTF.Offset = 0; // from beginning
  795. lpServerContext->hseTF.pHead = lpServerContext->szBuffer;
  796. lpServerContext->hseTF.HeadLength = lstrlen(lpServerContext->szBuffer);
  797. lpServerContext->hseTF.pTail = NULL;
  798. lpServerContext->hseTF.TailLength = 0;
  799. lpServerContext->hseTF.dwFlags = HSE_IO_ASYNC | HSE_IO_DISCONNECT_AFTER_SEND;
  800. if (!pEcb->ServerSupportFunction(pEcb->ConnID,
  801. HSE_REQ_TRANSMIT_FILE,
  802. &(lpServerContext->hseTF),
  803. 0,
  804. NULL))
  805. {
  806. if (S_OK == StringCchPrintf(achMsg, CELEMS(achMsg), "%ld", GetLastError()))
  807. {
  808. g_pEventLog->FLogError(PBSERVER_ERROR_CANT_SEND_CONTENT,achMsg);
  809. }
  810. dwRet = HSE_STATUS_ERROR;
  811. CloseHandle(lpServerContext->hseTF.hFile);
  812. HeapFree(g_hProcessHeap, 0, lpServerContext);
  813. return dwRet;
  814. }
  815. }
  816. return HSE_STATUS_PENDING;
  817. }
  818. //
  819. // build status string from code
  820. //
  821. void
  822. CPhoneBkServer::BuildStatusCode(
  823. IN OUT LPTSTR pszResponse,
  824. IN UINT cchResponse,
  825. IN DWORD dwCode)
  826. {
  827. assert(pszResponse);
  828. if (NULL == pszResponse)
  829. {
  830. return;
  831. }
  832. HTTPStatusInfo * pInfo = statusStrings;
  833. while (pInfo->pstrString)
  834. {
  835. if (dwCode == pInfo->dwCode)
  836. {
  837. break;
  838. }
  839. pInfo++;
  840. }
  841. if (pInfo->pstrString)
  842. {
  843. StringCchPrintf(pszResponse, cchResponse, "%d %s", dwCode, pInfo->pstrString);
  844. }
  845. else
  846. {
  847. assert(dwCode != HTTP_STATUS_OK);
  848. // ISAPITRACE1("Warning: Nonstandard status code %d\n", dwCode);
  849. BuildStatusCode(pszResponse, cchResponse, HTTP_STATUS_OK);
  850. }
  851. }
  852. //
  853. // DLL initialization function
  854. //
  855. BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason,
  856. LPVOID lpReserved)
  857. {
  858. switch (ulReason)
  859. {
  860. case DLL_PROCESS_ATTACH:
  861. //DebugBreak();
  862. OutputDebugString("DllMain: process attach\n");
  863. return InitProcess();
  864. break;
  865. case DLL_PROCESS_DETACH:
  866. OutputDebugString("process detach");
  867. CleanUpProcess();
  868. break;
  869. }
  870. return TRUE;
  871. }
  872. //
  873. // global initialization procedure.
  874. //
  875. BOOL InitProcess()
  876. {
  877. DWORD dwID;
  878. DWORD dwServiceNameLen;
  879. SECURITY_ATTRIBUTES sa;
  880. PACL pAcl = NULL;
  881. g_fBeingShutDown = FALSE;
  882. OutputDebugString("InitProcess: to GetProcessHeap() ... \n");
  883. g_hProcessHeap = GetProcessHeap();
  884. if (NULL == g_hProcessHeap)
  885. {
  886. goto failure;
  887. }
  888. OutputDebugString("InitProcess: to new CNTEvent... \n");
  889. g_pEventLog = new CNTEvent("Phone Book Service");
  890. if (NULL == g_pEventLog)
  891. goto failure;
  892. // Begin Geeta
  893. //
  894. // Create a semaphore for the shared memory
  895. //
  896. // Initialize a default Security attributes, giving world permissions,
  897. // this is basically prevent Semaphores and other named objects from
  898. // being created because of default acls given by winlogon when perfmon
  899. // is being used remotely.
  900. sa.bInheritHandle = FALSE;
  901. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  902. sa.lpSecurityDescriptor = malloc(sizeof(SECURITY_DESCRIPTOR));
  903. if ( !sa.lpSecurityDescriptor )
  904. {
  905. goto failure;
  906. }
  907. if ( !InitializeSecurityDescriptor(sa.lpSecurityDescriptor,SECURITY_DESCRIPTOR_REVISION) )
  908. {
  909. goto failure;
  910. }
  911. // bug 30991: Security issue, don't use NULL DACLs.
  912. //
  913. if (FALSE == SetAclPerms(&pAcl))
  914. {
  915. goto failure;
  916. }
  917. if (FALSE == SetSecurityDescriptorDacl(sa.lpSecurityDescriptor, TRUE, pAcl, FALSE))
  918. {
  919. goto failure;
  920. }
  921. OutputDebugString("InitProcess: To create counters object ...\n");
  922. //
  923. // Create global object for counters
  924. //
  925. g_pCpsCounter = new CCpsCounter;
  926. if (NULL == g_pCpsCounter)
  927. {
  928. goto failure;
  929. }
  930. OutputDebugString("InitProcess: To initialize shared memory ...\n");
  931. //
  932. // initialize Shared memory
  933. //
  934. if (!g_pCpsCounter->InitializeSharedMem(sa))
  935. {
  936. goto failure;
  937. }
  938. // free the memory
  939. free ((void *) sa.lpSecurityDescriptor);
  940. OutputDebugString("InitProcess: To grant permissions SHARED_OBJECT...\n");
  941. //
  942. // initialize Counters
  943. //
  944. OutputDebugString("InitProcess: To initialize perfmon counters\n");
  945. g_pCpsCounter->InitializeCounters();
  946. // End Geeta
  947. //
  948. // Initialize the global variables. Note: must do this before Creating the
  949. // monitor thread (because of g_szPBDataDir, g_achDBFileName etc)
  950. //
  951. if (!InitPBFilesPath())
  952. {
  953. goto failure;
  954. }
  955. //
  956. // initialize PhoneBookServer object
  957. // PhoneBookServer object should be the last to initialize because
  958. // it requires some other objects to be initialized first, such as
  959. // eventlog, critical section, odbc interface, etc.
  960. OutputDebugString("InitProcess: To new a phone book server\n");
  961. g_pPBServer = new CPhoneBkServer;
  962. if (NULL == g_pPBServer)
  963. {
  964. return FALSE;
  965. }
  966. OutputDebugString("InitProcess: To create a thread for DB file change monitoring\n");
  967. // create the thread to monitor file change
  968. g_hMonitorThread = CreateThread(
  969. NULL,
  970. 0,
  971. (LPTHREAD_START_ROUTINE)MonitorDBFileChangeThread,
  972. NULL,
  973. 0,
  974. &dwID
  975. );
  976. if (NULL == g_hMonitorThread)
  977. {
  978. g_pEventLog->FLogError(PBSERVER_ERROR_INTERNAL);
  979. goto failure;
  980. }
  981. SetThreadPriority(g_hMonitorThread, THREAD_PRIORITY_ABOVE_NORMAL);
  982. OutputDebugString("InitProcess: SUCCEEDED.........\n");
  983. return TRUE;
  984. failure: // clean up everything in case of failure
  985. OutputDebugString("InitProcess: failed\n");
  986. // free the memory
  987. if (sa.lpSecurityDescriptor)
  988. {
  989. free ((void *) sa.lpSecurityDescriptor);
  990. }
  991. if (g_pEventLog)
  992. {
  993. delete g_pEventLog;
  994. g_pEventLog = NULL;
  995. }
  996. if (g_pPBServer)
  997. {
  998. delete g_pPBServer;
  999. g_pPBServer = NULL;
  1000. }
  1001. if (pAcl)
  1002. {
  1003. LocalFree(pAcl);
  1004. }
  1005. return FALSE;
  1006. }
  1007. // global cleanup process
  1008. BOOL CleanUpProcess()
  1009. {
  1010. HANDLE hFile; // handle for the temporary file
  1011. DWORD dwResult;
  1012. char achDumbFile[2 * MAX_PATH + 4];
  1013. char achMsg[64];
  1014. OutputDebugString("CleanupProcess: entering\n");
  1015. // kill the change monitor thread
  1016. if (g_hMonitorThread != NULL)
  1017. {
  1018. // now try to synchronize between the main thread and the child thread
  1019. // step1: create a new file in g_szPBDataDir, therefore unblock the child thread
  1020. // which is waiting for such a change in file directory
  1021. g_fBeingShutDown = TRUE;
  1022. if (S_OK == StringCchPrintf(achDumbFile, CELEMS(achDumbFile), "%stemp", (char *)g_szPBDataDir))
  1023. {
  1024. // create a temp file, then delete it!
  1025. // This is to create a change in the directory so the child thread can exit itself
  1026. FILE * fpTemp = fopen(achDumbFile, "w");
  1027. if (fpTemp)
  1028. {
  1029. fclose(fpTemp);
  1030. DeleteFile(achDumbFile);
  1031. }
  1032. // step2: wait for the child thread to terminate
  1033. dwResult = WaitForSingleObject(g_hMonitorThread, 2000L); // wait for two seconds
  1034. if (WAIT_FAILED == dwResult)
  1035. {
  1036. StringCchPrintf(achMsg, CELEMS(achMsg), "%ld", GetLastError());
  1037. g_pEventLog -> FLogError(PBSERVER_ERROR_WAIT_FOR_THREAD, achMsg);
  1038. }
  1039. }
  1040. if (g_hMonitorThread != NULL)
  1041. {
  1042. CloseHandle(g_hMonitorThread);
  1043. g_hMonitorThread = NULL;
  1044. }
  1045. }
  1046. OutputDebugString("CleanupProcess: done deleting monitorchange thread\n");
  1047. if (g_pPBServer)
  1048. {
  1049. delete g_pPBServer;
  1050. g_pPBServer = NULL;
  1051. }
  1052. // clean up all allocated resources
  1053. if (g_pEventLog)
  1054. {
  1055. delete g_pEventLog;
  1056. g_pEventLog = NULL;
  1057. }
  1058. // Begin Geeta
  1059. //
  1060. // Close the shared memory object
  1061. //
  1062. if (g_pCpsCounter)
  1063. {
  1064. g_pCpsCounter->CleanUpSharedMem();
  1065. // End Geeta
  1066. delete g_pCpsCounter;
  1067. g_pCpsCounter = NULL;
  1068. }
  1069. OutputDebugString("CleanupProcess: leaving\n");
  1070. return TRUE;
  1071. }
  1072. // Entry Points of this ISAPI Extension DLL
  1073. // ISA entry point function. Intialize the server object g_pPBServer
  1074. BOOL WINAPI GetExtensionVersion(LPHSE_VERSION_INFO pVer)
  1075. {
  1076. return g_pPBServer ? g_pPBServer->GetExtensionVersion(pVer) : FALSE;
  1077. }
  1078. // ISA entry point function. Implemented through object g_pPBServer
  1079. DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK pEcb)
  1080. {
  1081. DWORD dwRetCode;
  1082. if (NULL == g_pPBServer)
  1083. {
  1084. return HSE_STATUS_ERROR;
  1085. }
  1086. dwRetCode = g_pPBServer->HttpExtensionProc(pEcb);
  1087. return dwRetCode;
  1088. }
  1089. //
  1090. // The standard entry point called by IIS as the last function.
  1091. //
  1092. BOOL WINAPI TerminateExtension(DWORD dwFlags)
  1093. {
  1094. return CleanUpProcess();
  1095. }
  1096. //+---------------------------------------------------------------------------
  1097. //
  1098. // Function: MonitorDBFileChangeThread
  1099. //
  1100. // Synopsis: Call the MonitorDBFileChange method to monitor any write to
  1101. // the database file
  1102. //
  1103. // Arguments: [lpParam] -- additional thread parameter
  1104. //
  1105. // History: 01/28/97 byao Created
  1106. //
  1107. //----------------------------------------------------------------------------
  1108. DWORD WINAPI MonitorDBFileChangeThread(LPVOID lpParam)
  1109. {
  1110. HANDLE hDir = NULL;
  1111. char achMsg[256];
  1112. DWORD dwRet = 0;
  1113. DWORD dwNextEntry, dwAction, dwFileNameLength, dwOffSet;
  1114. char achFileName[MAX_PATH + 1];
  1115. char achLastFileName[MAX_PATH + 1];
  1116. //
  1117. // open a handle to the PBS dir, which we're going to monitor
  1118. //
  1119. hDir = CreateFile (
  1120. g_achDBDirectory, // pointer to the directory name
  1121. FILE_LIST_DIRECTORY, // access (read-write) mode
  1122. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, // share mode
  1123. NULL, // security descriptor
  1124. OPEN_EXISTING, // how to create
  1125. FILE_FLAG_BACKUP_SEMANTICS, // file attributes
  1126. NULL // file with attributes to copy
  1127. );
  1128. if (INVALID_HANDLE_VALUE == hDir)
  1129. {
  1130. if (SUCCEEDED(StringCchPrintf(achMsg, CELEMS(achMsg), "%ld", GetLastError())))
  1131. {
  1132. g_pEventLog->FLogError(PBSERVER_ERROR_CANT_CREATE_FILE, (char *)g_szPBDataDir, achMsg);
  1133. }
  1134. dwRet = 1L;
  1135. goto Cleanup;
  1136. }
  1137. while (1)
  1138. {
  1139. const DWORD c_dwMaxChanges = 1024;
  1140. BYTE arrChanges[c_dwMaxChanges];
  1141. DWORD dwNumChanges;
  1142. //
  1143. // This is a synchronous call - we sit here waiting for something to
  1144. // change in this directory. If something does, we check to see if it
  1145. // is something for which we should log an event.
  1146. //
  1147. if (!ReadDirectoryChangesW(hDir,
  1148. arrChanges,
  1149. c_dwMaxChanges,
  1150. FALSE,
  1151. FILE_NOTIFY_CHANGE_LAST_WRITE,
  1152. &dwNumChanges,
  1153. NULL,
  1154. NULL))
  1155. {
  1156. //
  1157. // if this fails, log the failure and leave
  1158. //
  1159. StringCchPrintf(achMsg, CELEMS(achMsg), "%ld", GetLastError());
  1160. g_pEventLog->FLogError(PBSERVER_ERROR_CANT_DETERMINE_CHANGE, achMsg);
  1161. OutputDebugString(achMsg);
  1162. dwRet = 1L;
  1163. goto Cleanup;
  1164. }
  1165. OutputDebugString("detected a file system change\n");
  1166. achLastFileName[0] = TEXT('\0');
  1167. dwNextEntry = 0;
  1168. do
  1169. {
  1170. DWORD dwBytes;
  1171. FILE_NOTIFY_INFORMATION * pFNI = (FILE_NOTIFY_INFORMATION*) &arrChanges[dwNextEntry];
  1172. // check only the first change
  1173. dwOffSet = pFNI->NextEntryOffset;
  1174. dwNextEntry += dwOffSet;
  1175. dwAction = pFNI->Action;
  1176. dwFileNameLength = pFNI->FileNameLength;
  1177. OutputDebugString("prepare to convert the changed filename\n");
  1178. dwBytes = WideCharToMultiByte(CP_ACP,
  1179. 0,
  1180. pFNI->FileName,
  1181. dwFileNameLength,
  1182. achFileName,
  1183. CELEMS(achFileName),
  1184. NULL,
  1185. NULL);
  1186. if (0 == dwBytes)
  1187. {
  1188. // failed to convert filename
  1189. g_pEventLog->FLogError(PBSERVER_ERROR_CANT_CONVERT_FILENAME, achFileName);
  1190. OutputDebugString("Can't convert filename\n");
  1191. continue;
  1192. }
  1193. //
  1194. // Conversion succeeded. Null-terminate the filename.
  1195. //
  1196. achFileName[dwBytes/sizeof(WCHAR)] = '\0';
  1197. if (0 == _tcsicmp(achLastFileName, achFileName))
  1198. {
  1199. // the same file changed
  1200. OutputDebugString("the same file changed again\n");
  1201. continue;
  1202. }
  1203. // keep the last filename
  1204. StringCchCopy(achLastFileName, CELEMS(achLastFileName), achFileName);
  1205. //
  1206. if (g_fBeingShutDown)
  1207. {
  1208. //
  1209. // Time to go ...
  1210. //
  1211. dwRet = 1L;
  1212. goto Cleanup;
  1213. }
  1214. LogDebugMessage(achLastFileName);
  1215. LogDebugMessage((char *)c_szChangeFileName);
  1216. //
  1217. // now a file has changed. Test whether it's monitored file 'newpb.txt'
  1218. //
  1219. BOOL fNewPhoneBook = FALSE;
  1220. if ((0 == _tcsicmp(achLastFileName, (char *)c_szChangeFileName)) &&
  1221. (FILE_ACTION_ADDED == dwAction || FILE_ACTION_MODIFIED == dwAction))
  1222. {
  1223. fNewPhoneBook = TRUE;
  1224. g_pEventLog->FLogInfo(PBSERVER_INFO_NEW_PHONEBOOK);
  1225. }
  1226. LogDebugMessage("in child thread, fNewPhoneBook = %s;", fNewPhoneBook ? "TRUE" : "FALSE");
  1227. }
  1228. while (dwOffSet);
  1229. }
  1230. Cleanup:
  1231. if (hDir)
  1232. {
  1233. CloseHandle(hDir);
  1234. }
  1235. return dwRet;
  1236. }
  1237. // Begin Geeta
  1238. //----------------------------------------------------------------------------
  1239. //
  1240. // Function: InitializeSharedMem
  1241. //
  1242. // Class: CCpsCounter
  1243. //
  1244. // Synopsis: Sets up the memory mapped file
  1245. //
  1246. // Arguments: SECURITY_ATTRIBUTES sa: security descriptor for this object
  1247. //
  1248. // Returns: TRUE if successful, FALSE otherwise
  1249. //
  1250. // History: 05/29/97 Created by Geeta Tarachandani
  1251. //
  1252. //----------------------------------------------------------------------------
  1253. BOOL
  1254. CCpsCounter::InitializeSharedMem(SECURITY_ATTRIBUTES sa)
  1255. {
  1256. //
  1257. // Create a memory mapped object
  1258. //
  1259. OutputDebugString("InitializeSharedMem: to create file mapping\n");
  1260. m_hSharedFileMapping = CreateFileMapping(
  1261. INVALID_HANDLE_VALUE, // Shared object is in memory
  1262. &sa, // security descriptor
  1263. PAGE_READWRITE| SEC_COMMIT, // Desire R/W access
  1264. 0, // |_
  1265. sizeof(PERF_COUNTERS), // | Size of mapped object
  1266. SHARED_OBJECT ); // Shared Object
  1267. if (NULL == m_hSharedFileMapping)
  1268. {
  1269. #if DBG
  1270. char achMsg[256];
  1271. DWORD dwGLE = GetLastError();
  1272. StringCchPrintf(achMsg, CELEMS(achMsg), "InitializeSharedMem: CreateFileMapping failed, GLE=%d\n", dwGLE);
  1273. OutputDebugString(achMsg);
  1274. #else
  1275. OutputDebugString("InitializeSharedMem: CreateFileMapping failed\n");
  1276. #endif // DBG
  1277. m_hSharedFileMapping = OpenFileMapping(
  1278. FILE_MAP_WRITE | FILE_MAP_READ, // Desire R/W access
  1279. FALSE, // |_
  1280. SHARED_OBJECT ); // Shared Object
  1281. if (NULL == m_hSharedFileMapping)
  1282. {
  1283. #if DBG
  1284. dwGLE = GetLastError();
  1285. StringCchPrintf(achMsg, CELEMS(achMsg), "InitializeSharedMem: OpenFileMapping failed too, GLE=%d\n", dwGLE);
  1286. OutputDebugString(achMsg);
  1287. #else
  1288. OutputDebugString("InitializeSharedMem: OpenFileMapping failed too\n");
  1289. #endif // DBG
  1290. goto CleanUp;
  1291. }
  1292. OutputDebugString("InitializeSharedMem: ... but OpenFileMapping succeeded.");
  1293. }
  1294. OutputDebugString("InitializeSharedMem: MapViewofFileEx\n");
  1295. m_pPerfCtrs = (PERF_COUNTERS *) MapViewOfFileEx(
  1296. m_hSharedFileMapping, // Handle to shared file
  1297. FILE_MAP_WRITE, // Write access desired
  1298. 0, // Mapping offset
  1299. 0, // Mapping offset
  1300. sizeof(PERF_COUNTERS), // Mapping object size
  1301. NULL ); // Any base address
  1302. if (NULL == m_pPerfCtrs)
  1303. {
  1304. DWORD dwErr = GetLastError();
  1305. goto CleanUp;
  1306. }
  1307. return TRUE;
  1308. CleanUp:
  1309. CleanUpSharedMem();
  1310. return FALSE;
  1311. }
  1312. //----------------------------------------------------------------------------
  1313. //
  1314. // Function: InitializeCounters()
  1315. //
  1316. // Class: CCpsCounter
  1317. //
  1318. // Synopsis: Initializes all the Performance Monitoring Counters to 0
  1319. //
  1320. // Arguments: None
  1321. //
  1322. // Returns: void
  1323. //
  1324. // History: 05/29/97 Created by Geeta Tarachandani
  1325. //
  1326. //----------------------------------------------------------------------------
  1327. void
  1328. CCpsCounter::InitializeCounters( void )
  1329. {
  1330. if (NULL == m_hSharedFileMapping || NULL == m_pPerfCtrs)
  1331. {
  1332. return;
  1333. }
  1334. m_pPerfCtrs->dwTotalHits =0;
  1335. m_pPerfCtrs->dwNoUpgradeHits =0;
  1336. m_pPerfCtrs->dwDeltaUpgradeHits=0;
  1337. m_pPerfCtrs->dwFullUpgradeHits =0;
  1338. m_pPerfCtrs->dwErrors =0;
  1339. }
  1340. inline void CCpsCounter::AddHit(enum CPS_COUNTERS eCounter)
  1341. {
  1342. if (NULL == g_pCpsCounter || NULL == g_pCpsCounter->m_pPerfCtrs)
  1343. {
  1344. return;
  1345. }
  1346. switch (eCounter)
  1347. {
  1348. case TOTAL:
  1349. g_pCpsCounter->m_pPerfCtrs->dwTotalHits ++;
  1350. break;
  1351. case NO_UPGRADE:
  1352. g_pCpsCounter->m_pPerfCtrs->dwNoUpgradeHits ++;
  1353. break;
  1354. case DELTA_UPGRADE:
  1355. g_pCpsCounter->m_pPerfCtrs->dwDeltaUpgradeHits ++;
  1356. break;
  1357. case FULL_UPGRADE:
  1358. g_pCpsCounter->m_pPerfCtrs->dwFullUpgradeHits ++;
  1359. break;
  1360. case ERRORS:
  1361. g_pCpsCounter->m_pPerfCtrs->dwErrors ++;
  1362. break;
  1363. default:
  1364. OutputDebugString("Unknown counter type");
  1365. break;
  1366. }
  1367. }
  1368. //----------------------------------------------------------------------------
  1369. //
  1370. // Function: CleanUpSharedMem()
  1371. //
  1372. // Class: CCpsCounter
  1373. //
  1374. // Synopsis: Unmaps the shared file & closes all file handles
  1375. //
  1376. // Arguments: None
  1377. //
  1378. // Returns: Void
  1379. //
  1380. // History: 06/01/97 Created by Geeta Tarachandani
  1381. //
  1382. //----------------------------------------------------------------------------
  1383. void
  1384. CCpsCounter::CleanUpSharedMem()
  1385. {
  1386. OutputDebugString("CleanupSharedMem: entering\n");
  1387. //
  1388. // Unmap the shared file
  1389. //
  1390. if (g_pCpsCounter)
  1391. {
  1392. if ( m_pPerfCtrs )
  1393. {
  1394. UnmapViewOfFile( m_pPerfCtrs );
  1395. m_pPerfCtrs = NULL;
  1396. }
  1397. if ( m_hSharedFileMapping )
  1398. {
  1399. CloseHandle( m_hSharedFileMapping );
  1400. m_hSharedFileMapping = NULL;
  1401. }
  1402. }
  1403. OutputDebugString("CleanupSharedMem: leaving\n");
  1404. }
  1405. // End Geeta
  1406. //+----------------------------------------------------------------------------
  1407. //
  1408. // Func: IsValidNumericParam
  1409. //
  1410. // Desc: Checks if a given string will evaluate to a valid numeric param
  1411. //
  1412. // Args: [pszParam] - IN, phone book name
  1413. // [cchParam] - IN, length of buffer in TCHARs (note, this is not the strlen)
  1414. //
  1415. // Return: BOOL (true if succeeded, false if failed)
  1416. //
  1417. // History: 22-Feb-2000 SumitC Created
  1418. //
  1419. //-----------------------------------------------------------------------------
  1420. BOOL
  1421. IsValidNumericParam(
  1422. IN LPCTSTR pszParam,
  1423. IN UINT cchParam)
  1424. {
  1425. //
  1426. // check for a valid string
  1427. //
  1428. UINT nLenToCheck = min(cchParam, MAX_LEN_FOR_NUMERICAL_VALUE + 1);
  1429. if ((NULL == pszParam) || IsBadStringPtr(pszParam, nLenToCheck))
  1430. {
  1431. return FALSE;
  1432. }
  1433. //
  1434. // check the length
  1435. //
  1436. if (FAILED(StringCchLength((LPTSTR)pszParam, nLenToCheck, NULL))) // 3rd param not needed since we've already limited the length
  1437. {
  1438. return FALSE;
  1439. }
  1440. //
  1441. // check that the characters are all numbers
  1442. //
  1443. for (int i = 0 ; i < lstrlen(pszParam); ++i)
  1444. {
  1445. if (pszParam[i] < TEXT('0') || pszParam[i] > TEXT('9'))
  1446. {
  1447. return FALSE;
  1448. }
  1449. }
  1450. return TRUE;
  1451. }
  1452. //+----------------------------------------------------------------------------
  1453. //
  1454. // Func: IsValidStringParam
  1455. //
  1456. // Desc: Checks if the input is a valid string param for PBS
  1457. //
  1458. // Args: [pszParam] - IN, phone book name
  1459. // [cchParam] - IN, length of buffer in TCHARs (note, this is not the strlen)
  1460. //
  1461. // Return: BOOL (true if succeeded, false if failed)
  1462. //
  1463. // History: 22-Feb-2000 SumitC Created
  1464. //
  1465. //-----------------------------------------------------------------------------
  1466. BOOL
  1467. IsValidStringParam(
  1468. IN LPCTSTR pszParam,
  1469. IN UINT cchParam)
  1470. {
  1471. //
  1472. // check for a valid string
  1473. //
  1474. if ((NULL == pszParam) || IsBadStringPtr(pszParam, cchParam))
  1475. {
  1476. return FALSE;
  1477. }
  1478. //
  1479. // check that the characters are all letters
  1480. //
  1481. for (int i = 0 ; i < lstrlen(pszParam); ++i)
  1482. {
  1483. if (! ((pszParam[i] >= TEXT('a') && pszParam[i] <= TEXT('z')) ||
  1484. (pszParam[i] >= TEXT('A') && pszParam[i] <= TEXT('Z')) ||
  1485. (pszParam[i] >= TEXT('0') && pszParam[i] <= TEXT('9')) ||
  1486. (pszParam[i] == TEXT('.'))))
  1487. {
  1488. return FALSE;
  1489. }
  1490. }
  1491. return TRUE;
  1492. }
  1493. //+----------------------------------------------------------------------------
  1494. //
  1495. // Func: InitPBFilesPath
  1496. //
  1497. // Desc: Initializes the global if not already initialized
  1498. //
  1499. // Args: none
  1500. //
  1501. // Return: BOOL (true if succeeded, false if failed)
  1502. //
  1503. // History: 30-Jun-2000 SumitC Created
  1504. //
  1505. //-----------------------------------------------------------------------------
  1506. BOOL
  1507. InitPBFilesPath()
  1508. {
  1509. if (lstrlen(g_szPBDataDir))
  1510. {
  1511. return TRUE;
  1512. }
  1513. else
  1514. {
  1515. //
  1516. // Get location of PB files on this machine (\program files\phone book service\data)
  1517. //
  1518. if (S_OK == SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, g_szPBDataDir))
  1519. {
  1520. // g_szPBDataDir has to be empty here
  1521. if (S_OK != StringCchCat(g_szPBDataDir, CELEMS(g_szPBDataDir), "\\phone book service\\Data\\"))
  1522. {
  1523. return FALSE;
  1524. }
  1525. // g_szPBDataDir should be: \program files\phone book service\data
  1526. if (S_OK == StringCchPrintf(g_achDBDirectory, CELEMS(g_achDBDirectory), "%sDatabase", g_szPBDataDir))
  1527. {
  1528. // g_achDBDirectory should be: \program files\phone book service\data\database
  1529. return TRUE;
  1530. }
  1531. }
  1532. return FALSE;
  1533. }
  1534. }
  1535. //+----------------------------------------------------------------------------
  1536. //
  1537. // Func: GetCurrentPBVer
  1538. //
  1539. // Desc: Gets the most recent version number for the given phonebook
  1540. //
  1541. // Args: [pszStr] - IN, phone book name
  1542. // [pnCurrentPBVer] - OUT, current pb version number
  1543. //
  1544. // Return: HRESULT
  1545. //
  1546. // History: 30-Jun-2000 SumitC Created
  1547. //
  1548. //-----------------------------------------------------------------------------
  1549. HRESULT
  1550. GetCurrentPBVer(
  1551. IN char * pszPBName,
  1552. OUT int * pnCurrentPBVer)
  1553. {
  1554. HRESULT hr = S_OK;
  1555. char szTmp[2 * MAX_PATH];
  1556. int nNewestPB = 0;
  1557. assert(pszPBName);
  1558. assert(pnCurrentPBVer);
  1559. if (!pszPBName || !pnCurrentPBVer)
  1560. {
  1561. hr = E_INVALIDARG;
  1562. goto Cleanup;
  1563. }
  1564. if (!InitPBFilesPath())
  1565. {
  1566. hr = E_FAIL;
  1567. goto Cleanup;
  1568. }
  1569. //
  1570. // go to subdir named 'pszPBName', and find all FULL cabs.
  1571. //
  1572. StringCchPrintf(szTmp, CELEMS(szTmp), "%s%s\\*full.cab", g_szPBDataDir, pszPBName);
  1573. HANDLE hFind;
  1574. WIN32_FIND_DATA finddata;
  1575. hFind = FindFirstFile(szTmp, &finddata);
  1576. if (INVALID_HANDLE_VALUE == hFind)
  1577. {
  1578. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1579. goto Cleanup;
  1580. }
  1581. //
  1582. // Find the highest-numbered full cab we have, and cache that number
  1583. //
  1584. do
  1585. {
  1586. int nVer;
  1587. int nRet = sscanf(finddata.cFileName, "%dfull.cab", &nVer);
  1588. if (1 == nRet)
  1589. {
  1590. if (nVer > nNewestPB)
  1591. {
  1592. nNewestPB = nVer;
  1593. }
  1594. }
  1595. }
  1596. while (FindNextFile(hFind, &finddata));
  1597. FindClose(hFind);
  1598. *pnCurrentPBVer = nNewestPB;
  1599. #if DBG
  1600. //
  1601. // re-iterate, looking for deltas.
  1602. //
  1603. StringCchPrintf(szTmp, CELEMS(szTmp), "%s%s\\*delta*.cab", g_szPBDataDir, pszPBName);
  1604. hFind = FindFirstFile(szTmp, &finddata);
  1605. if (INVALID_HANDLE_VALUE == hFind)
  1606. {
  1607. OutputDebugString("found Nfull, but no deltas (this is ok if this is the first phonebook)");
  1608. goto Cleanup;
  1609. }
  1610. do
  1611. {
  1612. int nVerTo, nVerFrom;
  1613. int nRet = sscanf(finddata.cFileName, "%ddelta%d.cab", &nVerTo, &nVerFrom);
  1614. if (2 == nRet)
  1615. {
  1616. if (nVerTo > nNewestPB)
  1617. {
  1618. assert(0 && "largest DELTA cab has corresponding FULL cab missing");
  1619. break;
  1620. }
  1621. }
  1622. }
  1623. while (FindNextFile(hFind, &finddata));
  1624. FindClose(hFind);
  1625. #endif
  1626. Cleanup:
  1627. return hr;
  1628. }
  1629. //+----------------------------------------------------------------------------
  1630. //
  1631. // Func: CheckIfFileExists
  1632. //
  1633. // Desc: Test to see if a file is present in the FS.
  1634. //
  1635. // Args: [psz] - filename
  1636. //
  1637. // Return: BOOL (true if file exists, else false)
  1638. //
  1639. // History: 30-Jun-2000 SumitC Created
  1640. //
  1641. //-----------------------------------------------------------------------------
  1642. BOOL
  1643. CheckIfFileExists(const char * psz)
  1644. {
  1645. HANDLE hFile = CreateFile(psz,
  1646. GENERIC_READ,
  1647. FILE_SHARE_READ,
  1648. NULL,
  1649. OPEN_EXISTING,
  1650. FILE_ATTRIBUTE_NORMAL,
  1651. NULL);
  1652. if (INVALID_HANDLE_VALUE == hFile)
  1653. {
  1654. return FALSE;
  1655. }
  1656. else
  1657. {
  1658. CloseHandle(hFile);
  1659. return TRUE;
  1660. }
  1661. }
  1662. //+----------------------------------------------------------------------------
  1663. //
  1664. // Func: GetPhoneBook
  1665. //
  1666. // Desc: Test to see if a file is present in the FS.
  1667. //
  1668. // Args: [see QUERY_PARAMETER for description]
  1669. // [pszDownloadPath] - buffer
  1670. // [cchDownloadPath] - size of the buffer in chars
  1671. //
  1672. // Return: HRESULT
  1673. //
  1674. // History: 30-Jun-2000 SumitC Created
  1675. //
  1676. //-----------------------------------------------------------------------------
  1677. HRESULT
  1678. GetPhoneBook(
  1679. IN char * pszPBName,
  1680. IN int dLCID,
  1681. IN int dOSType,
  1682. IN int dOSArch,
  1683. IN int dPBVerCaller,
  1684. OUT char * pszDownloadPath,
  1685. IN UINT cchDownloadPath)
  1686. {
  1687. HRESULT hr = S_OK;
  1688. int dVersionDiff;
  1689. int nCurrentPBVer;
  1690. assert(pszPBName);
  1691. assert(pszDownloadPath);
  1692. if (!pszPBName || !pszDownloadPath)
  1693. {
  1694. hr = E_INVALIDARG;
  1695. goto Cleanup;
  1696. }
  1697. hr = GetCurrentPBVer(pszPBName, &nCurrentPBVer);
  1698. if (S_OK != hr)
  1699. {
  1700. goto Cleanup;
  1701. }
  1702. dVersionDiff = nCurrentPBVer - dPBVerCaller;
  1703. if (dVersionDiff <= 0)
  1704. {
  1705. //
  1706. // no download
  1707. //
  1708. hr = S_FALSE;
  1709. if (g_pCpsCounter)
  1710. {
  1711. g_pCpsCounter->AddHit(NO_UPGRADE);
  1712. }
  1713. }
  1714. else
  1715. {
  1716. pszDownloadPath[0] = TEXT('\0');
  1717. if (dVersionDiff < 5 && 0 != dPBVerCaller)
  1718. {
  1719. //
  1720. // incremental update => try to find the delta cab
  1721. //
  1722. // Given %d=10chars max. we should only use (2 * %d + formatting)=30
  1723. //
  1724. hr = StringCchPrintf(pszDownloadPath, cchDownloadPath, "%s%s\\%dDELTA%d.cab",
  1725. g_szPBDataDir, pszPBName, nCurrentPBVer, dPBVerCaller);
  1726. if (S_OK == hr)
  1727. {
  1728. // x:\program files\phone book service\data phone_book_name \ nDELTAm.cab
  1729. if (!CheckIfFileExists(pszDownloadPath))
  1730. {
  1731. hr = S_FALSE;
  1732. }
  1733. else
  1734. {
  1735. if (g_pCpsCounter)
  1736. {
  1737. g_pCpsCounter->AddHit(DELTA_UPGRADE);
  1738. }
  1739. hr = S_OK;
  1740. }
  1741. }
  1742. }
  1743. //
  1744. // note that if we tried to find a delta above and failed, hr is set to
  1745. // S_FALSE, so we fall through to the full download below.
  1746. //
  1747. if (dVersionDiff >= 5 || 0 == dPBVerCaller || S_FALSE == hr)
  1748. {
  1749. //
  1750. // bigger than 5, or no pb at all => full download
  1751. //
  1752. // Given %d=10chars max. we should only use (%d + formatting)=19
  1753. //
  1754. hr = StringCchPrintf(pszDownloadPath, cchDownloadPath, "%s%s\\%dFULL.cab",
  1755. g_szPBDataDir, pszPBName, nCurrentPBVer);
  1756. if (S_OK == hr)
  1757. {
  1758. // x:\program files\phone book service\data phone_book_name \ nFULL.cab
  1759. if (!CheckIfFileExists(pszDownloadPath))
  1760. {
  1761. hr = S_OK;
  1762. // return "success", the failure to open the file will be trapped
  1763. // by caller.
  1764. }
  1765. else
  1766. {
  1767. if (S_FALSE == hr)
  1768. {
  1769. hr = S_OK;
  1770. }
  1771. if (g_pCpsCounter)
  1772. {
  1773. g_pCpsCounter->AddHit(FULL_UPGRADE);
  1774. }
  1775. }
  1776. }
  1777. }
  1778. }
  1779. Cleanup:
  1780. return hr;
  1781. }