Source code of Windows XP (NT5)
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.

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