|
|
/*----------------------------------------------------------------------------
pbserver.cpp CPhoneBkServer class implementation
Copyright (c) 1997-1998 Microsoft Corporation All rights reserved.
Authors: byao Baogang Yao
History: 1/23/97 byao -- Created 5/29/97 t-geetat -- Modified -- added performance counters, shared memory 5/02/00 sumitc -- removed db dependency --------------------------------------------------------------------------*/ #include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <tchar.h>
#include <aclapi.h>
#include "common.h"
#include "pbserver.h"
#include "ntevents.h"
#include "cpsmon.h"
#include "shlobj.h"
#include "shfolder.h"
//
// Phone book "database" implementation
//
char g_szPBDataDir[2 * MAX_PATH] = "";
HRESULT GetPhoneBook(char * pszPBName, int dLCID, int dOSType, int dOSArch, int dPBVerCurrent, char * pszDownloadPath);
extern "C" { #include "util.h"
}
const DWORD MAX_BUFFER_SIZE = 1024; // maximum size of input buffer
const DWORD SEND_BUFFER_SIZE = 4096; // block size when sending CAB file
const char achDefService[] = "Default"; //default service name
const int dDefPBVer = 0; // default phone book version number, this should be 0,
// however, since David's test data used 0, we used 0 too.
// SUBJECT TO CHANGE
#define MAX_PATH_LEN 256
// missing value -- if parameter-pair is empty, it is set to this value
const int MISSING_VALUE = -1;
// make this public so the thread can access it
unsigned char g_achDBDirectory[MAX_PATH_LEN+1]; // full path for all phone book files
// constant strings
unsigned char c_szChangeFileName[] = "newpb.txt"; // newpb.txt
unsigned char c_szDBName[] = "pbserver"; // "pbserver" -- data source name
// the following error status code/string is copied from ISAPI.CPP
// which is part of the MFC library source code
typedef struct _httpstatinfo { DWORD dwCode; LPCTSTR pstrString; } HTTPStatusInfo;
//
// The following two structures are used in the SystemTimeToGMT function
//
static TCHAR * s_rgchDays[] = { TEXT("Sun"), TEXT("Mon"), TEXT("Tue"), TEXT("Wed"), TEXT("Thu"), TEXT("Fri"), TEXT("Sat") };
static TCHAR * s_rgchMonths[] = { TEXT("Jan"), TEXT("Feb"), TEXT("Mar"), TEXT("Apr"), TEXT("May"), TEXT("Jun"), TEXT("Jul"), TEXT("Aug"), TEXT("Sep"), TEXT("Oct"), TEXT("Nov"), TEXT("Dec") };
static HTTPStatusInfo statusStrings[] = { { HTTP_STATUS_OK, "OK" }, { HTTP_STATUS_CREATED, "Created" }, { HTTP_STATUS_ACCEPTED, "Accepted" }, { HTTP_STATUS_NO_CONTENT, "No download Necessary" }, { HTTP_STATUS_TEMP_REDIRECT, "Moved Temporarily" }, { HTTP_STATUS_REDIRECT, "Moved Permanently" }, { HTTP_STATUS_NOT_MODIFIED, "Not Modified" }, { HTTP_STATUS_BAD_REQUEST, "Bad Request" }, { HTTP_STATUS_AUTH_REQUIRED, "Unauthorized" }, { HTTP_STATUS_FORBIDDEN, "Forbidden" }, { HTTP_STATUS_NOT_FOUND, "Not Found" }, { HTTP_STATUS_SERVER_ERROR, "Server error, type unknown" }, { HTTP_STATUS_NOT_IMPLEMENTED, "Not Implemented" }, { HTTP_STATUS_BAD_GATEWAY, "Bad Gateway" }, { HTTP_STATUS_SERVICE_NA, "Cannot find service on server, bad request" }, { 0, NULL } };
// Server asynchronized I/O context
typedef struct _SERVER_CONTEXT { EXTENSION_CONTROL_BLOCK * pEcb; HSE_TF_INFO hseTF; TCHAR szBuffer[SEND_BUFFER_SIZE]; } SERVERCONTEXT, *LPSERVERCONTEXT;
DWORD WINAPI MonitorDBFileChangeThread(LPVOID lpParam); BOOL InitPBFilesPath();
//
// definition of global data
// All the following variable(object) can only have one instance
//
CPhoneBkServer * g_pPBServer; // Phone Book Server object
CNTEvent * g_pEventLog; // event log
CRITICAL_SECTION g_CriticalSection; // critical section
HANDLE g_hMonitorThread; // the monitor thread that checks the new file notification
HANDLE g_hProcessHeap; // handle of the global heap for the extension process;
BOOL g_fNewPhoneBook = FALSE; // whether there's a new phone book
BOOL g_fBeingShutDown = FALSE; // whether the system is being shut down
//
// Variables used in memory mapping
//
CCpsCounter *g_pCpsCounter = NULL; // Pointer to memory mapped object
HANDLE g_hSharedFileMapping = NULL; // Handle to the shared file mapping
HANDLE g_hSemaphore = NULL; // Handle to the semaphore for shared-file
////////////////////////////////////////////////////////////////////////
//
// Name: GetExtensionVersion
//
// Class: CPhoneBkServer
//
// Synopsis: implement the first DLL entry point function
//
//
// Return: TRUE succeed
// FALSE
//
// Parameters:
// pszVer[out] version information that needs to be filled out
//
BOOL CPhoneBkServer::GetExtensionVersion(LPHSE_VERSION_INFO pVer) { // Set version number
pVer -> dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
// Load description string
lstrcpyn(pVer->lpszExtensionDesc, "Connection Point Server Application", HSE_MAX_EXT_DLL_NAME_LEN);
OutputDebugString("CPhoneBkServer.GetExtensionVersion() : succeeded \n"); return TRUE; }
//////////////////////////////////////////////////////////////////////////////
//
// Name: GetParameterPairs
//
// Class: CPhoneBkServer
//
// Synopsis: Get the parameter-value pairs from an input string(from URL)
//
// Return: number of parameter pairs actually read
// a value of -1 stands for error --> INVALID_QUERY_STRING
//
// Parameter:
// pszInputString[in] Input string (null terminated)
// lpPairs[out] Pointer to the parameter/value pairs
// int dMaxPairs Maximum number of parameter pairs allowed
//
int CPhoneBkServer:: GetParameterPairs(char *pszInputString, LPPARAMETER_PAIR lpPairs, int dMaxPairs) { int i = 0;
if (NULL == pszInputString) { return INVALID_QUERY_STRING; }
for(i = 0; pszInputString[0] != '\0' && i < dMaxPairs; i++) { // m_achVal == 'p=what%3F';
GetWord(lpPairs[i].m_achVal, pszInputString, '&', NAME_VALUE_LEN - 1);
// m_achVal == 'p=what?'
UnEscapeURL(lpPairs[i].m_achVal);
GetWord(lpPairs[i].m_achName,lpPairs[i].m_achVal,'=', NAME_VALUE_LEN - 1); // m_achVal = what?
// m_achName = p
}
#ifdef _LOG_DEBUG_MESSAGE
char achMsg[64];
wsprintf(achMsg, "inside GetParameterPairs: dNumPairs : %d", i); LogDebugMessage(achMsg);
if (pszInputString[0] != '\0') LogDebugMessage("there are more parameters\n"); #endif
if (pszInputString[0] != '\0') { // more parameters available
return INVALID_QUERY_STRING; } else { //succeed
return i; } }
////////////////////////////////////////////////////////////////////////
//
// Name: GetQueryParameter
//
// Class: CPhoneBkServer
//
// Synopsis: scan through the query string, and get the value for all
// query parameters
//
// Return: TRUE all query parameters are correct
// FALSE invalid parameter existed
//
// Parameters:
// pszQuery[in] Query string from the client(URL encripted)
// pQueryParameter[out] pointer to the query parameters structure
//
//
BOOL CPhoneBkServer:: GetQueryParameter(char *pszQuery, LPQUERY_PARAMETER lpQueryParameter) { const int MAX_PARAMETER_NUM = 10; PARAMETER_PAIR Pairs[MAX_PARAMETER_NUM]; // maximum 10 pairs -- just to be safe.
int dNumPairs, i; #ifdef _LOG_DEBUG_MESSAGE
char achMsg[MAX_BUFFER_SIZE + 50]; if (0 < _snprintf(achMsg, MAX_BUFFER_SIZE + 50, "pszquery=%s", pszQuery)) { LogDebugMessage(achMsg); } #endif
dNumPairs = GetParameterPairs(pszQuery, Pairs, MAX_PARAMETER_NUM);
#ifdef _LOG_DEBUG_MESSAGE
wsprintf(achMsg, "number of pairs : %d", dNumPairs); LogDebugMessage(achMsg); #endif
// initialize the parameter values
// check the validity of the parameter
m_QueryParameter.m_achPB[0]='\0'; // empty service name
m_QueryParameter.m_dPBVer = MISSING_VALUE; // empty pbversion
m_QueryParameter.m_dOSArch = MISSING_VALUE; m_QueryParameter.m_dOSType = MISSING_VALUE; m_QueryParameter.m_dLCID = MISSING_VALUE; m_QueryParameter.m_achCMVer[0] = '\0'; m_QueryParameter.m_achOSVer[0] = '\0';
if (INVALID_QUERY_STRING == dNumPairs) // invalid number of parameters in query string
{ return FALSE; }
for (i = 0; i < dNumPairs; i++) { _strlwr(Pairs[i].m_achName);
if (lstrcmpi(Pairs[i].m_achName, "osarch") == 0) { if (lstrlen(Pairs[i].m_achVal) == 0) lpQueryParameter->m_dOSArch = MISSING_VALUE; else lpQueryParameter->m_dOSArch = atoi(Pairs[i].m_achVal); } else if (lstrcmpi(Pairs[i].m_achName, "ostype") == 0) { if (lstrlen(Pairs[i].m_achVal) == 0) lpQueryParameter->m_dOSType = MISSING_VALUE; else lpQueryParameter->m_dOSType = atoi(Pairs[i].m_achVal); } else if (lstrcmpi(Pairs[i].m_achName,"lcid") == 0) { if (lstrlen(Pairs[i].m_achVal) == 0) lpQueryParameter->m_dLCID = MISSING_VALUE; else lpQueryParameter->m_dLCID = atoi(Pairs[i].m_achVal); } else if (lstrcmpi(Pairs[i].m_achName,"osver") == 0) { lstrcpy(lpQueryParameter->m_achOSVer,Pairs[i].m_achVal); } else if (lstrcmpi(Pairs[i].m_achName,"cmver") == 0) { lstrcpy(lpQueryParameter->m_achCMVer, Pairs[i].m_achVal); } else if (lstrcmpi(Pairs[i].m_achName,"pb") == 0) { lstrcpy(lpQueryParameter->m_achPB,Pairs[i].m_achVal); } else if (lstrcmpi(Pairs[i].m_achName,"pbver") == 0) { if (lstrlen(Pairs[i].m_achVal) == 0) lpQueryParameter->m_dPBVer = MISSING_VALUE; else lpQueryParameter->m_dPBVer = atoi(Pairs[i].m_achVal); } }
// LogDebug message:
#ifdef _LOG_DEBUG_MESSAGE
sprintf(achMsg, "osarch:%d", m_QueryParameter.m_dOSArch); LogDebugMessage(achMsg);
sprintf(achMsg, "ostype:%d", m_QueryParameter.m_dOSType); LogDebugMessage(achMsg);
sprintf(achMsg, "lcid:%d", m_QueryParameter.m_dLCID); LogDebugMessage(achMsg);
sprintf(achMsg, "osver:%s ", m_QueryParameter.m_achOSVer); LogDebugMessage(achMsg);
sprintf(achMsg, "cmver:%s", m_QueryParameter.m_achCMVer); LogDebugMessage(achMsg);
sprintf(achMsg, "PB :%s ", m_QueryParameter.m_achPB); LogDebugMessage(achMsg);
sprintf(achMsg, "PBVer:%d ", m_QueryParameter.m_dPBVer); LogDebugMessage(achMsg);
#endif
return TRUE; }
#if 0
/*
////////////////////////////////////////////////////////////////////////
//
// Name: FormSQLQuery
//
// Class: CPhoneBkServer
//
// Synopsis: Form a SQL query statement for ODBC database server
//
//
void CPhoneBkServer:: FormSQLQuery(char *pszQuery, char *pszService, int dLCID, int dOSType, int dOSArch) { char achTempStr[128];
lstrcpy(pszQuery,"Select Phonebooks.ISPid, Phonebooks.Version, Phonebooks.LCID"); lstrcat(pszQuery,", Phonebooks.OS, Phonebooks.Arch, Phonebooks.VirtualPath"); lstrcat(pszQuery," FROM ISPs, Phonebooks");
sprintf(achTempStr," WHERE (ISPs.Description Like '%s'", pszService); lstrcat(pszQuery,achTempStr);
lstrcat(pszQuery," AND ISPs.ISPid = Phonebooks.ISPid)");
sprintf(achTempStr, " AND (Phonebooks.OS = %d)", dOSType); lstrcat(pszQuery,achTempStr);
lstrcat(pszQuery," ORDER BY Phonebooks.Version DESC"); }
//----------------------------------------------------------------------------
//
// Function: Virtual2Physical()
//
// Class: CPhoneBkServer
//
// Synopsis: Convert a virtual path to a physical path
//
// Arguments: pEcb - ISAPI extension control block
// *pszFileName - the virtual path]
//
// Returns: TRUE: succeed; otherwise FALSE
//
// History: 05/30/96 VetriV Created
// 1/25/97 byao Modified to be used in the phone book
// server ISAPI
//----------------------------------------------------------------------------
BOOL CPhoneBkServer::Virtual2Physical(EXTENSION_CONTROL_BLOCK * pEcb, char * pszVirtualPath, char * pszPhysicalPath) { DWORD dw = MAX_PATH; LPSTR lpsz; char szLocalFile[MAX_PATH]; BOOL fRet;
// Is this a relative virtual path?
//
if (pszVirtualPath[0] != L'/' && pszVirtualPath[1] != L':') { // Base this path off of the path of our current script
fRet = pEcb->GetServerVariable(pEcb->ConnID, "PATH_INFO", szLocalFile, &dw);
if (FALSE == fRet) { return FALSE; }
lpsz = _tcsrchr(szLocalFile, '/'); assert(lpsz != NULL); if (!lpsz) { return FALSE; } *(++lpsz) = NULL;
dw = sizeof(szLocalFile) - PtrToLong((const void *)(lpsz - szLocalFile)); } else { lstrcpy(szLocalFile, pszVirtualPath); } LogDebugMessage("within Virtual2Physical:"); LogDebugMessage(szLocalFile);
// Map this to a physical file name
dw = sizeof(szLocalFile);
fRet = (*pEcb->ServerSupportFunction)(pEcb->ConnID, HSE_REQ_MAP_URL_TO_PATH, szLocalFile, &dw, NULL);
if (FALSE == fRet) { return FALSE; }
lstrcpy(pszPhysicalPath, szLocalFile); return TRUE; } */ #endif
//----------------------------------------------------------------------------
//
// Function: GetFileLength()
//
// Class: CPhoneBkServer
//
// Synopsis: Reads the pszFileName file and sends back the file size
//
// Arguments: lpszFileName - Contains the file name (with full path)]
//
// Returns: TRUE if succeed, otherwise FALSE;
//
// History: 03/07/97 byao Created
//
//----------------------------------------------------------------------------
DWORD CPhoneBkServer::GetFileLength(LPSTR lpszFileName) { HANDLE hFile = INVALID_HANDLE_VALUE; DWORD dwFileSize;
//
// Open file
//
hFile = CreateFile(lpszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE == hFile) return 0L;
//
// Get File Size
//
dwFileSize = GetFileSize(hFile, NULL); CloseHandle(hFile); if (-1 == dwFileSize) { dwFileSize = 0; }
return dwFileSize; }
//----------------------------------------------------------------------------
//
// Function: SystemTimeToGMT
//
// Synopsis: Converts the given system time to string representation
// containing GMT Formatted String
//
// Arguments: [st System time that needs to be converted *Reference*]
// [pstr pointer to string which will contain the GMT time
// on successful return]
// [cbBuff size of pszBuff in bytes]
//
// Returns: TRUE on success. FALSE on failure.
//
// History: 04/12/97 VetriV Created (from IIS source)
//
//----------------------------------------------------------------------------
BOOL SystemTimeToGMT(const SYSTEMTIME & st, LPSTR pszBuff, DWORD cbBuff) { if (!pszBuff || cbBuff < 40 ) { return FALSE; }
//
// Formats a string like: "Thu, 14 Jul 1994 15:26:05 GMT"
//
sprintf(pszBuff, "%s, %02d %s %d %02d:%02d:%0d GMT", s_rgchDays[st.wDayOfWeek], st.wDay, s_rgchMonths[st.wMonth - 1], st.wYear, st.wHour, st.wMinute, st.wSecond);
return TRUE;
}
//----------------------------------------------------------------------------
//
// Function: FormHttpHeader
//
// Synopsis: Form's the IIS 3.0 HTTP Header
//
// Arguments: pszBuffer Buffer that will contain both the header and the
// status text
// pszResponse status text
// pszExtraHeader extra header information
//
// Returns: ERROR_SUCCESS on success. Error code on failure.
//
// History: 04/12/97 VetriV Created
// 05/22/97 byao Modified, to make it work with CPS server
//----------------------------------------------------------------------------
DWORD FormHttpHeader(LPSTR pszBuffer, LPSTR pszResponse, LPSTR pszExtraHeader) {
// start with stand IIS header
wsprintf(pszBuffer, "HTTP/1.0 %s\r\nServer: Microsoft-IIS/3.0\r\nDate: ", pszResponse); //
// Append the time
//
SYSTEMTIME SysTime; TCHAR szTime[128]; GetSystemTime(&SysTime); if (FALSE == SystemTimeToGMT(SysTime, szTime, 128)) { //
// TODO: Error Handling
//
} lstrcat(pszBuffer, szTime); lstrcat(pszBuffer, "\r\n"); // Append extra header string
lstrcat(pszBuffer, pszExtraHeader);
return ERROR_SUCCESS; }
//----------------------------------------------------------------------------
//
// Function: HseIoCompletion
//
// Synopsis: Callback routine that handles asynchronous WriteClient
// completion callback
//
// Arguments: [pECB - Extension Control Block]
// [pContext - Pointer to the AsyncWrite structure]
// [cbIO - Number of bytes written]
// [dwError - Error code if there was an error while writing]
//
// Returns: None
//
// History: 04/10/97 VetriV Created
// 05/22/97 byao Modified to make it work for CPS server
//
//----------------------------------------------------------------------------
VOID HseIoCompletion(EXTENSION_CONTROL_BLOCK * pEcb, PVOID pContext, DWORD cbIO, DWORD dwError) { LPSERVERCONTEXT lpServerContext = (LPSERVERCONTEXT) pContext;
if (!lpServerContext) { return; }
lpServerContext->pEcb->ServerSupportFunction( lpServerContext->pEcb->ConnID, HSE_REQ_DONE_WITH_SESSION, NULL, NULL, NULL);
if (lpServerContext->hseTF.hFile != INVALID_HANDLE_VALUE) { CloseHandle(lpServerContext->hseTF.hFile); }
HeapFree(g_hProcessHeap, 0, lpServerContext); SetLastError(dwError); return; }
////////////////////////////////////////////////////////////////////////
//
// Name: HttpExtensionProc
//
// Class: CPhoneBkServer
//
// Synopsis: implement the second DLL entry point function
//
// Return: HTTP status code
//
// Parameters:
// pEcb[in/out] - extension control block
//
// History: Modified byao 5/22/97
// new implementation: using asynchronized I/O
// Modified t-geetat : Added PerfMon counters
//
/////////////////////////////////////////////////////////////////////////
DWORD CPhoneBkServer:: HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK pEcb) { DWORD dwBufferLen = MAX_BUFFER_SIZE; char achQuery[MAX_BUFFER_SIZE], achMsg[128]; char achPhysicalPath[MAX_PATH_LEN];
int dVersionDiff; // version difference between client & server's phone books
BOOL fRet; DWORD dwStatusCode = NOERROR; int dwRet; DWORD dwCabFileSize;
BOOL fHasContent = FALSE; CHAR szResponse[64]; char achExtraHeader[128]; char achHttpHeader[1024]; char achBuffer[SEND_BUFFER_SIZE]; DWORD dwResponseSize;
LPSERVERCONTEXT lpServerContext; HSE_TF_INFO hseTF; QUERY_PARAMETER QueryParameter;
g_pCpsCounter->AddHit(TOTAL);
// get the query string
fRet = (*pEcb->GetServerVariable)(pEcb->ConnID, "QUERY_STRING", achQuery, &dwBufferLen);
//
// If there is an error, log an NT event and leave.
//
if (!fRet) { dwStatusCode = GetLastError();
#ifdef _LOG_DEBUG_MESSAGE
switch (dwStatusCode) { case ERROR_INVALID_PARAMETER: lstrcpy(achMsg,"error: invalid parameter"); break; case ERROR_INVALID_INDEX: lstrcpy(achMsg,"error: invalid index"); break; case ERROR_INSUFFICIENT_BUFFER: lstrcpy(achMsg,"error: insufficient buffer"); break; case ERROR_MORE_DATA: lstrcpy(achMsg,"error: more data coming"); break; case ERROR_NO_DATA: lstrcpy(achMsg,"error: no data available"); break; }
LogDebugMessage(achMsg); #endif
wsprintf(achMsg, "%ld", dwStatusCode); g_pEventLog -> FLogError(PBSERVER_CANT_GET_PARAMETER, achMsg);
goto CleanUp; }
LogDebugMessage("prepare to get query parameters");
// parse the query string, get the value of each parameter
GetQueryParameter(achQuery, &QueryParameter);
// check the validity of the parameter
if (MISSING_VALUE == QueryParameter.m_dOSArch || MISSING_VALUE == QueryParameter.m_dOSType || MISSING_VALUE == QueryParameter.m_dLCID || 0 == lstrlen(QueryParameter.m_achCMVer) || 0 == lstrlen(QueryParameter.m_achOSVer)) { // invalid data
dwStatusCode = HTTP_STATUS_BAD_REQUEST; goto CleanUp; }
//
// Use defaults for some missing values
//
if (0 == lstrlen(QueryParameter.m_achPB)) { lstrcpy(QueryParameter.m_achPB, achDefService); } if (MISSING_VALUE == QueryParameter.m_dPBVer) { QueryParameter.m_dPBVer = dDefPBVer; }
// DebugBreak();
#ifdef _LOG_DEBUG_MESSAGE
sprintf(achMsg, "in main thread, g_fNewPhoneBook = %s;", g_fNewPhoneBook ? "TRUE" : "FALSE"); LogDebugMessage(achMsg); #endif
HRESULT hr;
hr = GetPhoneBook(QueryParameter.m_achPB, QueryParameter.m_dLCID, QueryParameter.m_dOSType, QueryParameter.m_dOSArch, QueryParameter.m_dPBVer, achPhysicalPath);
fHasContent = FALSE; if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { // we couldn't find the required file (phonebook name is probably bad)
dwStatusCode = HTTP_STATUS_SERVICE_NA; } else if (FAILED(hr)) { // some other error
dwStatusCode = HTTP_STATUS_SERVER_ERROR; } else if (S_FALSE == hr) { // you don't need a phone book
dwStatusCode = HTTP_STATUS_NO_CONTENT; } else { // we have a phone book for you...
fHasContent = TRUE; dwStatusCode = HTTP_STATUS_OK; }
CleanUp:
if (HTTP_STATUS_OK != dwStatusCode && HTTP_STATUS_NO_CONTENT != dwStatusCode) { g_pCpsCounter->AddHit(ERRORS); }
// DebugBreak();
LogDebugMessage("download file:"); LogDebugMessage(achPhysicalPath);
// convert virtual path to physical path
if (fHasContent) { // get cab file size
dwCabFileSize = GetFileLength(achPhysicalPath); }
BuildStatusCode(szResponse, dwStatusCode); dwResponseSize = lstrlen(szResponse);
dwRet = HSE_STATUS_SUCCESS;
// prepare for the header
if (HTTP_STATUS_OK == dwStatusCode && dwCabFileSize) { // not a NULL cab file
wsprintf(achExtraHeader, "Content-Type: application/octet-stream\r\nContent-Length: %ld\r\n\r\n", dwCabFileSize); } else { lstrcpy(achExtraHeader, "\r\n"); // just send back an empty line
}
// set up asynchronized I/O context
lpServerContext = NULL; lpServerContext = (LPSERVERCONTEXT) HeapAlloc(g_hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SERVERCONTEXT)); if (!lpServerContext) { wsprintf(achMsg, "%ld", GetLastError()); g_pEventLog->FLogError(PBSERVER_ERROR_INTERNAL, achMsg); return HSE_STATUS_ERROR; }
lpServerContext->pEcb = pEcb; lpServerContext->hseTF.hFile = INVALID_HANDLE_VALUE;
if (!pEcb->ServerSupportFunction(pEcb->ConnID, HSE_REQ_IO_COMPLETION, HseIoCompletion, 0, (LPDWORD) lpServerContext)) { wsprintf(achMsg, "%ld", GetLastError()); g_pEventLog->FLogError(PBSERVER_ERROR_INTERNAL, achMsg);
HeapFree(g_hProcessHeap, 0, lpServerContext); return HSE_STATUS_ERROR; }
// if there's no content, send header and status code back using WriteClient();
// otherwise, use TransmitFile to send the file content back
FormHttpHeader(achHttpHeader, szResponse, achExtraHeader); lstrcpy(lpServerContext->szBuffer, achHttpHeader);
//
// send status code or the file back
//
dwRet = HSE_STATUS_PENDING; if (!fHasContent) { // Append status text as the content
lstrcat(lpServerContext->szBuffer, szResponse); dwResponseSize = lstrlen(lpServerContext->szBuffer);
if (pEcb->WriteClient(pEcb->ConnID, lpServerContext->szBuffer, &dwResponseSize, HSE_IO_ASYNC) == FALSE) { pEcb->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR; dwRet = HSE_STATUS_ERROR; wsprintf(achMsg, "%ld", GetLastError()); g_pEventLog->FLogError(PBSERVER_ERROR_CANT_SEND_HEADER,achMsg);
HeapFree(g_hProcessHeap, 0, lpServerContext); return dwRet; } } else { // send file back using TransmitFile
HANDLE hFile = INVALID_HANDLE_VALUE; hFile = CreateFile(achPhysicalPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (INVALID_HANDLE_VALUE == hFile) { wsprintf(achMsg, "%s (%u)", achPhysicalPath, GetLastError()); g_pEventLog->FLogError(PBSERVER_ERROR_CANT_OPEN_FILE, achMsg);
HeapFree(g_hProcessHeap, 0, lpServerContext); return HSE_STATUS_ERROR; }
lpServerContext->hseTF.hFile = hFile;
lpServerContext->hseTF.pfnHseIO = NULL; lpServerContext->hseTF.pContext = lpServerContext;
lpServerContext->hseTF.BytesToWrite = 0; // entire file
lpServerContext->hseTF.Offset = 0; // from beginning
lpServerContext->hseTF.pHead = lpServerContext->szBuffer; lpServerContext->hseTF.HeadLength = lstrlen(lpServerContext->szBuffer);
lpServerContext->hseTF.pTail = NULL; lpServerContext->hseTF.TailLength = 0;
lpServerContext->hseTF.dwFlags = HSE_IO_ASYNC | HSE_IO_DISCONNECT_AFTER_SEND; if (!pEcb->ServerSupportFunction(pEcb->ConnID, HSE_REQ_TRANSMIT_FILE, &(lpServerContext->hseTF), 0, NULL)) { wsprintf(achMsg, "%ld", GetLastError()); g_pEventLog->FLogError(PBSERVER_ERROR_CANT_SEND_CONTENT,achMsg); dwRet = HSE_STATUS_ERROR;
CloseHandle(lpServerContext->hseTF.hFile); HeapFree(g_hProcessHeap, 0, lpServerContext); return dwRet; } }
return HSE_STATUS_PENDING; }
//
// build status string from code
//
void CPhoneBkServer::BuildStatusCode(LPTSTR pszResponse, DWORD dwCode) { assert(pszResponse);
HTTPStatusInfo * pInfo = statusStrings;
while (pInfo->pstrString) { if (dwCode == pInfo->dwCode) { break; } pInfo++; }
if (pInfo->pstrString) { wsprintf(pszResponse, "%d %s", dwCode, pInfo->pstrString); } else { assert(dwCode != HTTP_STATUS_OK); // ISAPITRACE1("Warning: Nonstandard status code %d\n", dwCode);
BuildStatusCode(pszResponse, HTTP_STATUS_OK); } }
//
// DLL initialization function
//
BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason, LPVOID lpReserved) { switch (ulReason) { case DLL_PROCESS_ATTACH: //DebugBreak();
OutputDebugString("DllMain: process attach\n"); return InitProcess(); break;
case DLL_PROCESS_DETACH: LogDebugMessage("process detach"); break; }
return TRUE; }
//
// global initialization procedure.
//
BOOL InitProcess() { //TODO: in order to avoid any future problems, any significant initialization
// should be done in GetExtensionVersion()
DWORD dwID; DWORD dwServiceNameLen; SECURITY_ATTRIBUTES sa; PACL pAcl = NULL;
g_fBeingShutDown = FALSE;
//
// May throw STATUS_NO_MEMORY if memory is low. We want to make sure this
// doesn't bring down the process (the admin may have configured pbserver
// to run in-process)
//
__try { OutputDebugString("InitProcess: to InitializeCriticalSection ... \n"); // initialize CriticalSection
InitializeCriticalSection(&g_CriticalSection); } __except(EXCEPTION_EXECUTE_HANDLER) { #if DBG
char achMsg[256]; DWORD dwErr = GetExceptionCode(); wsprintf(achMsg,"InitProcess: InitializeCriticalSection failed, thread=%ld ExceptionCode=%08lx", GetCurrentThreadId(), dwErr); OutputDebugString(achMsg); #endif
return FALSE; }
OutputDebugString("InitProcess: to GetProcessHeap() ... \n"); g_hProcessHeap = GetProcessHeap(); if (NULL == g_hProcessHeap) { goto failure; }
OutputDebugString("InitProcess: to new CNTEvent... \n");
g_pEventLog = new CNTEvent("Phone Book Service"); if (NULL == g_pEventLog) goto failure;
/*
// check for validity of timebomb
dwServiceNameLen = lstrlen(SERVICE_NAME); if (!IsTimeBombValid(SERVICE_NAME, dwServiceNameLen)) { g_pEventLog -> FLogError(PBSERVER_ERROR_SERVICE_EXPIRED); goto failure; } */
// Begin Geeta
//
// Create a semaphore for the shared memory
//
// Initialize a default Security attributes, giving world permissions,
// this is basically prevent Semaphores and other named objects from
// being created because of default acls given by winlogon when perfmon
// is being used remotely.
sa.bInheritHandle = FALSE; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = malloc(sizeof(SECURITY_DESCRIPTOR)); if ( !sa.lpSecurityDescriptor ) { goto failure; }
if ( !InitializeSecurityDescriptor(sa.lpSecurityDescriptor,SECURITY_DESCRIPTOR_REVISION) ) { goto failure; }
// bug 30991: Security issue, don't use NULL DACLs.
//
if (FALSE == SetAclPerms(&pAcl)) { goto failure; }
if (FALSE == SetSecurityDescriptorDacl(sa.lpSecurityDescriptor, TRUE, pAcl, FALSE)) { goto failure; }
OutputDebugString("InitProcess: To create semaphone...\n");
g_hSemaphore = CreateSemaphore( &sa, // Security attributes
1, // Initial Count
1, // Max count
SEMAPHORE_OBJECT ); // Semaphore name -- in "cpsmon.h"
if (ERROR_ALREADY_EXISTS == GetLastError()) { //
// We're not expecting anyone to have this semaphore already created.
// In the interests of security (a pre-existing semaphore could have been
// created by anyone, and we don't want anyone other than ourselves owning
// the pbsmon semaphore), we exit.
//
OutputDebugString("InitProcess: semaphore already exists - exiting.\n"); assert(0); goto failure; // ISSUE-2000/10/30-SumitC Note that if pbserver is taken down without the
// chance to delete the semaphore, we can't restart
// until the machine is rebooted.
}
if ( NULL == g_hSemaphore ) { goto failure; } OutputDebugString("InitProcess: To initialize shared memory ...\n"); //
// initialize Shared memory
//
if (!InitializeSharedMem(sa)) { goto failure; }
// free the memory
free ((void *) sa.lpSecurityDescriptor); OutputDebugString("InitProcess: To grant permissions SHARED_OBJECT...\n");
//
// initialize Counters
//
OutputDebugString("InitProcess: To initialize perfmon counters\n"); g_pCpsCounter->InitializeCounters();
// End Geeta
//
// Initialize the global variables. Note: must do this before Creating the
// monitor thread (because of g_szPBDataDir, g_achDBFileName etc)
//
if (!InitPBFilesPath()) { goto failure; }
char achTempName[MAX_PATH_LEN+1]; char szDBFileName[MAX_PATH_LEN+1]; DWORD dwBufferSize; char *pszFoundPosition;
// get the full path name for the data base
wsprintf(achTempName, "SOFTWARE\\ODBC\\ODBC.INI\\%s", c_szDBName); OutputDebugString(achTempName);
dwBufferSize = sizeof(szDBFileName); if (!GetRegEntry(HKEY_LOCAL_MACHINE, achTempName, "DBQ", REG_SZ, NULL, 0, (unsigned char *)&szDBFileName, &dwBufferSize)) { /*
wsprintf(achMsg,"HKLM\\%s\\DBQ : Error code %ld", achTempName, GetLastError()); g_pEventLog->FLogError(PBSERVER_ERROR_ODBC_CANT_READ_REGISTRY, achMsg); */
goto failure; }
// initialize the NewDBFilename --> actually newpb.mdb
// & the ChangeFileName --> actually newpb.txt
//
lstrcpy(achTempName, szDBFileName); pszFoundPosition = _tcsrchr(achTempName, '\\'); // found the last '\' --> path info
if( pszFoundPosition != NULL) { *(pszFoundPosition+1) = '\0'; }
// store the directory name for the phone book files
lstrcpy((char *)g_achDBDirectory, achTempName);
//
// initialize PhoneBookServer object
// PhoneBookServer object should be the last to initialize because
// it requires some other objects to be initialized first, such as
// eventlog, critical section, odbc interface, etc.
OutputDebugString("InitProcess: To new a phone book server\n"); g_pPBServer = new CPhoneBkServer; if (NULL == g_pPBServer) { return FALSE; }
OutputDebugString("InitProcess: To create a thread for DB file change monitoring\n"); // create the thread to monitor file change
g_hMonitorThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)MonitorDBFileChangeThread, NULL, 0, &dwID );
if (INVALID_HANDLE_VALUE == g_hMonitorThread) { g_pEventLog->FLogError(PBSERVER_ERROR_INTERNAL); goto failure; } SetThreadPriority(g_hMonitorThread, THREAD_PRIORITY_ABOVE_NORMAL);
return TRUE;
failure: // clean up everything in case of failure
OutputDebugString("InitProcess: failed\n");
DeleteCriticalSection(&g_CriticalSection);
// free the memory
if (sa.lpSecurityDescriptor) { free ((void *) sa.lpSecurityDescriptor); } if (g_pEventLog) { delete g_pEventLog; g_pEventLog = NULL; }
if (g_pPBServer) { delete g_pPBServer; g_pPBServer = NULL; }
if (pAcl) { LocalFree(pAcl); } // Begin geeta
if (g_hSemaphore) { CloseHandle(g_hSemaphore); g_hSemaphore = NULL; } // end geeta
return FALSE; }
// global cleanup process
BOOL CleanUpProcess() { HANDLE hFile; // handle for the temporary file
DWORD dwResult; char achDumbFile[2 * MAX_PATH + 4]; char achMsg[64];
// kill the change monitor thread
if (g_hMonitorThread != INVALID_HANDLE_VALUE) { // now try to synchronize between the main thread and the child thread
// step1: create a new file in g_szPBDataDir, therefore unblock the child thread
// which is waiting for such a change in file directory
g_fBeingShutDown = TRUE; lstrcpy(achDumbFile, (char *)g_szPBDataDir); lstrcat(achDumbFile,"temp");
// create a temp file, then delete it!
// This is to create a change in the directory so the child thread can exit itself
FILE * fpTemp = fopen(achDumbFile, "w"); if (fpTemp) { fclose(fpTemp); DeleteFile(achDumbFile); }
// step2: wait for the child thread to terminate
dwResult = WaitForSingleObject(g_hMonitorThread, 2000L); // wait for one second
if (WAIT_FAILED == dwResult) { wsprintf(achMsg, "%ld", GetLastError()); g_pEventLog -> FLogError(PBSERVER_ERROR_WAIT_FOR_THREAD, achMsg); }
if (g_hMonitorThread != INVALID_HANDLE_VALUE) { CloseHandle(g_hMonitorThread); } } // disconnect from ODBC
if (g_pPBServer) { delete g_pPBServer; g_pPBServer = NULL; }
// clean up all allocated resources
if (g_pEventLog) { delete g_pEventLog; g_pEventLog = NULL; }
// Begin Geeta
//
// Close the semaphore
//
if ( NULL != g_hSemaphore ) { CloseHandle(g_hSemaphore); g_hSemaphore = NULL; OutputDebugString("CLEANUPPROCESS: Semaphore deleted\n"); }
//
// Close the shared memory object
//
CleanUpSharedMem(); // End Geeta
DeleteCriticalSection(&g_CriticalSection);
return TRUE; }
// Entry Points of this ISAPI Extension DLL
// ISA entry point function. Intialize the server object g_pPBServer
BOOL WINAPI GetExtensionVersion(LPHSE_VERSION_INFO pVer) { return g_pPBServer ? g_pPBServer->GetExtensionVersion(pVer) : FALSE; }
// ISA entry point function. Implemented through object g_pPBServer
DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK pEcb) { DWORD dwRetCode;
if (NULL == g_pPBServer) { return HSE_STATUS_ERROR; } dwRetCode = g_pPBServer->HttpExtensionProc(pEcb); return dwRetCode; }
//
// The standard entry point called by IIS as the last function.
//
BOOL WINAPI TerminateExtension(DWORD dwFlags) { return CleanUpProcess(); }
//
// StrEqual(achStr, pszStr)
//
// Test whether achStr is equal to pszStr.
// Please note: the point here is: achStr is not zero-ended
BOOL StrEqual(char achStr[], char *pszStr) { int i;
for (i = 0; i < lstrlen(pszStr); i++) { if (achStr[i] != pszStr[i]) { return FALSE; } }
return TRUE; }
//+---------------------------------------------------------------------------
//
// Function: MonitorDBFileChangeThread
//
// Synopsis: Call the MonitorDBFileChange method to monitor any write to
// the database file
//
// Arguments: [lpParam] -- additional thread parameter
//
// History: 01/28/97 byao Created
//
//----------------------------------------------------------------------------
DWORD WINAPI MonitorDBFileChangeThread(LPVOID lpParam) { HANDLE hDir = NULL; char achMsg[256]; DWORD dwRet = 0; DWORD dwNextEntry, dwAction, dwFileNameLength, dwOffSet; char achFileName[MAX_PATH_LEN+1]; char achLastFileName[MAX_PATH_LEN+1]; //
// open a handle to the PBS dir, which we're going to monitor
//
hDir = CreateFile ( (char *)g_achDBDirectory, // pointer to the directory name
FILE_LIST_DIRECTORY, // access (read-write) mode
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, // share mode
NULL, // security descriptor
OPEN_EXISTING, // how to create
FILE_FLAG_BACKUP_SEMANTICS, // file attributes
NULL // file with attributes to copy
);
if (INVALID_HANDLE_VALUE == hDir) { wsprintf(achMsg, "%ld", GetLastError()); g_pEventLog->FLogError(PBSERVER_ERROR_CANT_CREATE_FILE, (char *)g_szPBDataDir, achMsg); dwRet = 1L; goto Cleanup; } while (1) { const DWORD c_dwMaxChanges = 1024; BYTE arrChanges[c_dwMaxChanges]; DWORD dwNumChanges;
//
// This is a synchronous call - we sit here waiting for something to
// change in this directory. If something does, we check to see if it
// is something for which we should log an event.
//
if (!ReadDirectoryChangesW(hDir, arrChanges, c_dwMaxChanges, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, &dwNumChanges, NULL, NULL)) { //
// if this fails, log the failure and leave
//
wsprintf(achMsg, "%ld", GetLastError()); g_pEventLog->FLogError(PBSERVER_ERROR_CANT_DETERMINE_CHANGE, achMsg); OutputDebugString(achMsg); dwRet = 1L; goto Cleanup; }
OutputDebugString("detected a file system change\n"); achLastFileName[0] = TEXT('\0'); dwNextEntry = 0;
do { DWORD dwBytes;
FILE_NOTIFY_INFORMATION * pFNI = (FILE_NOTIFY_INFORMATION*) &arrChanges[dwNextEntry];
// check only the first change
dwOffSet = pFNI->NextEntryOffset; dwNextEntry += dwOffSet; dwAction = pFNI->Action; dwFileNameLength = pFNI->FileNameLength;
OutputDebugString("prepare to convert the changed filename\n"); //TODO: check whether we can use UNICODE for all filenames
dwBytes = WideCharToMultiByte(CP_ACP, 0, pFNI->FileName, dwFileNameLength, achFileName, MAX_PATH_LEN, NULL, NULL);
if (0 == dwBytes) { // failed to convert filename
g_pEventLog->FLogError(PBSERVER_ERROR_CANT_CONVERT_FILENAME, achFileName); OutputDebugString("Can't convert filename\n"); continue; }
//
// Conversion succeeded. Null-terminate the filename.
//
achFileName[dwBytes/sizeof(WCHAR)] = '\0';
if (0 == _tcsicmp(achLastFileName, achFileName)) { // the same file changed
OutputDebugString("the same file changed again\n"); continue; }
// keep the last filename
_tcscpy(achLastFileName, achFileName);
if (g_fBeingShutDown) { //
// Time to go ...
//
dwRet = 1L; goto Cleanup; }
//
// now a file has changed. Test whether it's monitored file 'newpb.txt'
//
LogDebugMessage(achLastFileName); LogDebugMessage((char *)c_szChangeFileName);
if ((0 == _tcsicmp(achLastFileName, (char *)c_szChangeFileName)) && (FILE_ACTION_ADDED == dwAction || FILE_ACTION_MODIFIED == dwAction)) { EnterCriticalSection(&g_CriticalSection); LogDebugMessage("entered critical section!");
g_fNewPhoneBook = TRUE;
LogDebugMessage("leaving critical section!"); LeaveCriticalSection(&g_CriticalSection);
g_pEventLog->FLogInfo(PBSERVER_INFO_NEW_PHONEBOOK); }
#ifdef _LOG_DEBUG_MESSAGE
sprintf(achMsg, "in child thread, g_fNewPhoneBook = %s;", g_fNewPhoneBook ? "TRUE" : "FALSE"); LogDebugMessage(achMsg); #endif
} while (dwOffSet); }
Cleanup:
if (hDir) { CloseHandle(hDir); }
return dwRet; }
// Begin Geeta
//----------------------------------------------------------------------------
//
// Function: GetSemaphore
//
// Synopsis: This function gets hold of the semaphore for accessing shared file.
//
// Arguments: None.
//
// Returns: TRUE if succeeds, FALSE if fails.
//
// History: 06/02/97 t-geetat Created
//
//----------------------------------------------------------------------------
BOOL GetSemaphore() { DWORD WaitRetValue = WaitForSingleObject( g_hSemaphore, INFINITE );
switch (WaitRetValue) { case WAIT_OBJECT_0: return TRUE ; case WAIT_ABANDONED: return TRUE; default: return FALSE; }
return FALSE; }
//----------------------------------------------------------------------------
//
// Function: InitializeSharedMem
//
// Synopsis: Sets up the memory mapped file
//
// Arguments: SECURITY_ATTRIBUTES sa: security descriptor for this object
//
// Returns: TRUE if successful, FALSE otherwise
//
// History: 05/29/97 Created by Geeta Tarachandani
//
//----------------------------------------------------------------------------
BOOL InitializeSharedMem(SECURITY_ATTRIBUTES sa) { //
// Create a memory mapped object
//
OutputDebugString("InitializeSharedMem: to create file mapping\n"); g_hSharedFileMapping = CreateFileMapping( INVALID_HANDLE_VALUE, // Shared object is in memory
&sa, // security descriptor
PAGE_READWRITE| SEC_COMMIT, // Desire R/W access
0, // |_
sizeof(CCpsCounter), // | Size of mapped object
SHARED_OBJECT ); // Shared Object
if (NULL == g_hSharedFileMapping) { goto CleanUp; }
OutputDebugString("InitializeSharedMem: MapViewofFileEx\n"); g_pCpsCounter = (CCpsCounter *) MapViewOfFileEx( g_hSharedFileMapping, // Handle to shared file
FILE_MAP_WRITE, // Write access desired
0, // Mapping offset
0, // Mapping offset
sizeof(CCpsCounter), // Mapping object size
NULL ); // Any base address
if (NULL == g_pCpsCounter) { goto CleanUp; }
return TRUE;
CleanUp: CleanUpSharedMem(); return FALSE;
}
//----------------------------------------------------------------------------
//
// Function: InitializeCounters()
//
// Class: CCpsCounter
//
// Synopsis: Initializes all the Performance Monitoring Counters to 0
//
// Arguments: None
//
// Returns: void
//
// History: 05/29/97 Created by Geeta Tarachandani
//
//----------------------------------------------------------------------------
void CCpsCounter::InitializeCounters( void ) { m_dwTotalHits =0; m_dwNoUpgradeHits =0; m_dwDeltaUpgradeHits=0; m_dwFullUpgradeHits =0; m_dwErrors =0; }
inline void CCpsCounter::AddHit(enum CPS_COUNTERS eCounter) { if (GetSemaphore()) { switch (eCounter) { case TOTAL: g_pCpsCounter->m_dwTotalHits ++; break; case NO_UPGRADE: g_pCpsCounter->m_dwNoUpgradeHits ++; break; case DELTA_UPGRADE: g_pCpsCounter->m_dwDeltaUpgradeHits ++; break; case FULL_UPGRADE: g_pCpsCounter->m_dwFullUpgradeHits ++; break; case ERRORS: g_pCpsCounter->m_dwErrors ++; break; default: OutputDebugString("Unknown counter type"); break; } }
ReleaseSemaphore(g_hSemaphore, 1, NULL); }
//----------------------------------------------------------------------------
//
// Function: CleanUpSharedMem()
//
// Synopsis: Unmaps the shared file & closes all file handles
//
// Arguments: None
//
// Returns: Void
//
// History: 06/01/97 Created by Geeta Tarachandani
//
//----------------------------------------------------------------------------
void CleanUpSharedMem() { //
// Unmap the shared file
//
if (g_pCpsCounter) { UnmapViewOfFile( g_pCpsCounter ); g_pCpsCounter = NULL; }
CloseHandle(g_hSharedFileMapping); g_hSharedFileMapping = NULL; }
// End Geeta
BOOL InitPBFilesPath() { if (lstrlen(g_szPBDataDir)) { return TRUE; } else { //
// Get location of PB files on this machine (\program files\phone book service\data)
//
if (S_OK == SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, g_szPBDataDir)) { lstrcat(g_szPBDataDir, "\\phone book service\\Data\\"); return TRUE; } else { return FALSE; } } }
HRESULT GetCurrentPBVer(char * pszPBName, int * pnCurrentPBVer) { HRESULT hr = S_OK; char szTmp[2 * MAX_PATH]; int nNewestPB = 0;
assert(pszPBName); assert(pnCurrentPBVer);
if (!pszPBName || !pnCurrentPBVer) { hr = E_INVALIDARG; goto Cleanup; }
if (!InitPBFilesPath()) { hr = E_FAIL; goto Cleanup; }
//
// go to subdir named 'pszPBName', and find all FULL cabs.
//
wsprintf(szTmp, "%s%s\\*full.cab", g_szPBDataDir, pszPBName);
HANDLE hFind; WIN32_FIND_DATA finddata;
hFind = FindFirstFile(szTmp, &finddata); if (INVALID_HANDLE_VALUE == hFind) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); goto Cleanup; }
//
// Find the highest-numbered full cab we have, and cache that number
//
do { int nVer; int nRet = sscanf(finddata.cFileName, "%dfull.cab", &nVer); if (1 == nRet) { if (nVer > nNewestPB) { nNewestPB = nVer; } } } while (FindNextFile(hFind, &finddata)); FindClose(hFind);
*pnCurrentPBVer = nNewestPB;
#if DBG
//
// re-iterate, looking for deltas.
//
wsprintf(szTmp, "%s%s\\*delta*.cab", g_szPBDataDir, pszPBName);
hFind = FindFirstFile(szTmp, &finddata); if (INVALID_HANDLE_VALUE == hFind) { OutputDebugString("found Nfull, but no deltas (this is ok if this is the first phonebook)"); goto Cleanup; }
do { int nVerTo, nVerFrom; int nRet = sscanf(finddata.cFileName, "%ddelta%d.cab", &nVerTo, &nVerFrom); if (2 == nRet) { if (nVerTo > nNewestPB) { assert(0 && "largest DELTA cab has corresponding FULL cab missing"); break; } } } while (FindNextFile(hFind, &finddata)); FindClose(hFind); #endif
Cleanup: return hr; }
BOOL CheckIfFileExists(const char * psz) { HANDLE hFile = CreateFile(psz, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { return FALSE; } else { CloseHandle(hFile); return TRUE; } }
HRESULT GetPhoneBook(IN char * pszPBName, IN int dLCID, IN int dOSType, IN int dOSArch, IN int dPBVerCaller, OUT char * pszDownloadPath) { HRESULT hr = S_OK; int dVersionDiff; int nCurrentPBVer; #if DBG
char achMsg[256]; #endif
assert(pszPBName); assert(pszDownloadPath); if (!pszPBName || !pszDownloadPath) { hr = E_INVALIDARG; goto Cleanup; }
hr = GetCurrentPBVer(pszPBName, &nCurrentPBVer); if (S_OK != hr) { goto Cleanup; }
dVersionDiff = nCurrentPBVer - dPBVerCaller;
if (dVersionDiff <= 0) { //
// no download
//
hr = S_FALSE;
g_pCpsCounter->AddHit(NO_UPGRADE); } else { if (dVersionDiff < 5 && 0 != dPBVerCaller) { //
// incremental update => try to find the delta cab
//
wsprintf(pszDownloadPath, "%s%s\\%dDELTA%d.cab", g_szPBDataDir, pszPBName, nCurrentPBVer, dPBVerCaller); // x:\program files\phone book service\ phone_book_name \ nDELTAm.cab
if (!CheckIfFileExists(pszDownloadPath)) { hr = S_FALSE; } else { g_pCpsCounter->AddHit(DELTA_UPGRADE); } }
//
// note that if we tried to find a delta above and failed, hr is set to
// S_FALSE, so we fall through to the full download below.
//
if (dVersionDiff >= 5 || 0 == dPBVerCaller || S_FALSE == hr) { //
// bigger than 5, or no pb at all => full download
//
wsprintf(pszDownloadPath, "%s%s\\%dFULL.cab", g_szPBDataDir, pszPBName, nCurrentPBVer); // x:\program files\phone book service\ phone_book_name \ nFULL.cab
if (!CheckIfFileExists(pszDownloadPath)) { hr = S_OK; // return "success", the failure to open the file will be trapped
// by caller.
} else { if (S_FALSE == hr) { hr = S_OK; } g_pCpsCounter->AddHit(FULL_UPGRADE); } } }
Cleanup: return hr;
}
|