// // rdpfstore.cpp // // Implementation of CRdpFileStore, implements ISettingsStore // // CRdpFileStore implements a persistent settings store for // ts client settings. // // // Copyright(C) Microsoft Corporation 2000 // Author: Nadim Abdo (nadima) // // // Notes for improvement: // Can add a hash lookup table to speedup FindRecord. // (FindRecord is called at least once for each property that // is read/written during OpenStore/SaveStore operations) // Most files contain maybe 5-10 records so speeding up // the find is probably not that important. // // Copyright(C) Microsoft Corporation 2000 // Author: Nadim Abdo (nadima) // #include "stdafx.h" #define TRC_GROUP TRC_GROUP_UI #define TRC_FILE "rdpfstore.cpp" #include #include "rdpfstore.h" #include "autil.h" //for StringToBinary/BinaryToString // // Index values must match RDPF_RECTYPE_* // LPCTSTR g_szTypeCodeMap[] = { TEXT(":i:"), //RDPF_RECTYPE_UINT TEXT(":s:"), //RDPF_RECTYPE_SZ TEXT(":b:") //RDPF_RECTYPE_BINARY }; CRdpFileStore::CRdpFileStore() { _cRef = 1; _fReadOnly = FALSE; _fOpenForRead = FALSE; _fOpenForWrite = FALSE; _fIsDirty = FALSE; _pRecordListHead= NULL; _pRecordListTail= NULL; _szFileName[0] = 0; } CRdpFileStore::~CRdpFileStore() { DeleteRecords(); } ULONG __stdcall CRdpFileStore::AddRef() { DC_BEGIN_FN("AddRef"); ULONG cref = InterlockedIncrement(&_cRef); TRC_ASSERT(cref > 0, (TB,_T("AddRef: cref is not > 0!"))); DC_END_FN(); return cref; } ULONG __stdcall CRdpFileStore::Release() { DC_BEGIN_FN("Release"); TRC_ASSERT(_cRef > 0, (TB,_T("AddRef: cref is not > 0!"))); ULONG cref = InterlockedDecrement(&_cRef); if(0 == cref) { TRC_DBG((TB,_T("CRdpFileStore::Release deleting object"))); delete this; } DC_END_FN(); return cref; } STDMETHODIMP CRdpFileStore::QueryInterface(REFIID iid, void** p) { UNREFERENCED_PARAMETER(iid); UNREFERENCED_PARAMETER(p); return E_NOTIMPL; } // // OpenStore // opens the RDP file, creating one if one doesn't exist // The file existed it is parsed and readied for fast queries // // parameters: // szStoreMoniker - path to file // bReadyOnly - specifies if file is to be opened readonly // BOOL CRdpFileStore::OpenStore(LPCTSTR szStoreMoniker, BOOL bReadOnly) { DC_BEGIN_FN("OpenStore"); TRC_ASSERT(szStoreMoniker, (TB, _T("szStoreMoniker parameter is NULL"))); if(szStoreMoniker) { _fReadOnly = bReadOnly; HRESULT hr = StringCchCopy(_szFileName, SIZECHAR(_szFileName), szStoreMoniker); if (FAILED(hr)) { TRC_ERR((TB, _T("String copy failed: hr = 0x%x"), hr)); memset(_szFileName, 0, sizeof(_szFileName)); return FALSE; } // // Open the file, creating it if it doesn't exist // // // First try to open existing for rw // if (ERR_SUCCESS == _fs.OpenForRead( (LPTSTR)szStoreMoniker)) { _fOpenForRead = _fs.IsOpenForRead(); _fOpenForWrite = !bReadOnly; } else { TRC_DBG((TB, _T("OpenStore could not _tfopen: %s."), szStoreMoniker)); return FALSE; } ParseFile(); } else { return FALSE; } DC_END_FN(); return TRUE; } // // CommitStore // commits the in memory representation of the file to the store // this overwrites any existing contents in the file. // // File MUST have been opened with OpenStore // BOOL CRdpFileStore::CommitStore() { DC_BEGIN_FN("CommitStore"); PRDPF_RECORD node = NULL; TCHAR szBuf[LINEBUF_SIZE]; int ret; if(_fOpenForWrite) { if(_fs.IsOpenForRead() || _fs.IsOpenForWrite()) { _fs.Close(); } //Reopen for write, nuking previous contents //Open as binary to allow UNICODE output if(ERR_SUCCESS == _fs.OpenForWrite(_szFileName, TRUE)) { node = _pRecordListHead; while(node) { if(RecordToString(node, szBuf, LINEBUF_SIZE)) { ret = _fs.Write(szBuf); if(ERR_SUCCESS != ret) { TRC_ABORT((TB,_T("Error writing to _fs: %d"),ret)); return FALSE; } } else { return FALSE; } node = node->pNext; } return TRUE; } else { TRC_ERR((TB,_T("OpenForWrite failed on file:%s"),_szFileName)); return FALSE; } } else { TRC_ERR((TB,_T("Files was not opened for write:%s"),_szFileName)); return FALSE; } DC_END_FN(); } // // CloseStore // Closes the file, does NOT do a commit. // BOOL CRdpFileStore::CloseStore() { DC_BEGIN_FN("CloseStore"); if(_fs.IsOpenForRead() || _fs.IsOpenForWrite()) { if(ERR_SUCCESS == _fs.Close()) { _fReadOnly = FALSE; _fOpenForRead = FALSE; _fOpenForWrite = FALSE; return TRUE; } else { return FALSE; } } else { return FALSE; } DC_END_FN(); } BOOL CRdpFileStore::IsOpenForRead() { return _fOpenForRead; } BOOL CRdpFileStore::IsOpenForWrite() { return _fOpenForWrite; } BOOL CRdpFileStore::IsDirty() { return _fIsDirty; } BOOL CRdpFileStore::SetDirtyFlag(BOOL bIsDirty) { _fIsDirty = bIsDirty; return _fIsDirty; } // // Typed read/write functions // BOOL CRdpFileStore::ReadString(LPCTSTR szName, LPTSTR szDefault, LPTSTR szOutBuf, UINT strLen) { PRDPF_RECORD node = NULL; HRESULT hr; DC_BEGIN_FN("ReadString"); TRC_ASSERT(szName && szDefault && szOutBuf && strLen, (TB,_T("Invalid params to ReadString"))); if(szName && szDefault && szOutBuf && strLen) { node = FindRecord(szName); if(node && node->recType == RDPF_RECTYPE_SZ) { hr = StringCchCopy(szOutBuf, strLen, node->u.szVal); if (SUCCEEDED(hr)) { return TRUE; } else { TRC_ERR((TB, _T("String copy failed: hr = 0x%x"), hr)); return FALSE; } } else { //Fill with default hr = StringCchCopy(szOutBuf, strLen, szDefault); if (SUCCEEDED(hr)) { return TRUE; } else { TRC_ERR((TB, _T("String copy failed: hr = 0x%x"), hr)); return FALSE; } } } else { return FALSE; } DC_END_FN(); } // // Writes a string to the store // params // szName - key name // szDefault - default value (if writing default settings gets deleted) // szValue - value to write out // fIgnoreDefault - if set then always write ignoring szDefault // BOOL CRdpFileStore::WriteString(LPCTSTR szName, LPTSTR szDefault, LPTSTR szValue, BOOL fIgnoreDefault) { DC_BEGIN_FN("WriteString"); TRC_ASSERT(szName && szValue, (TB,_T("Invalid params to WriteString"))); if(szName && szValue) { if(szDefault && !fIgnoreDefault && !_tcscmp(szDefault,szValue)) { // // Don't write out defaults // PRDPF_RECORD node = FindRecord(szName); if(node) { return DeleteRecord(node); } return TRUE; } else { BOOL bRet = InsertRecord(szName, RDPF_RECTYPE_SZ, szValue); return bRet; } } else { return FALSE; } DC_END_FN(); } BOOL CRdpFileStore::ReadBinary(LPCTSTR szName, PBYTE pOutBuf, UINT cbBufLen) { PRDPF_RECORD node = NULL; DC_BEGIN_FN("ReadBinary"); TRC_ASSERT(szName && pOutBuf && cbBufLen, (TB,_T("Invalid params to ReadBinary"))); if(szName && pOutBuf && cbBufLen) { node = FindRecord(szName); if(node && node->recType == RDPF_RECTYPE_BINARY) { if(node->dwBinValLen <= cbBufLen) { memcpy(pOutBuf, node->u.pBinVal, node->dwBinValLen); return TRUE; } else { TRC_ERR((TB,_T("Insufficient space in outbuf buf"))); return FALSE; } } else { return FALSE; } } else { return FALSE; } DC_END_FN(); } BOOL CRdpFileStore::WriteBinary(LPCTSTR szName,PBYTE pBuf, UINT cbBufLen) { DC_BEGIN_FN("WriteInt"); TRC_ASSERT(szName && pBuf, (TB,_T("Invalid params to WriteBinary"))); if(!cbBufLen) { return TRUE; } if(szName && pBuf) { BOOL bRet = InsertBinaryRecord(szName, pBuf, (DWORD)cbBufLen); return bRet; } else { return FALSE; } DC_END_FN(); } BOOL CRdpFileStore::ReadInt(LPCTSTR szName, UINT defaultVal, PUINT pval) { PRDPF_RECORD node = NULL; DC_BEGIN_FN("ReadInt"); TRC_ASSERT(szName && pval, (TB,_T("Invalid params to ReadInt"))); if(szName && pval) { node = FindRecord(szName); if(node && node->recType == RDPF_RECTYPE_UINT) { *pval = node->u.iVal; return TRUE; } else { *pval = defaultVal; return TRUE; } } else { return FALSE; } DC_END_FN(); } // // Writes an int to the store // params // szName - key name // defaultVal - default value (if writing default settings gets deleted) // val - value to write out // fIgnoreDefault - if set then always write ignoring szDefault // BOOL CRdpFileStore::WriteInt(LPCTSTR szName, UINT defaultVal, UINT val, BOOL fIgnoreDefault) { DC_BEGIN_FN("WriteInt"); TRC_ASSERT(szName, (TB,_T("Invalid params to WriteInt"))); if(szName) { if(!fIgnoreDefault && defaultVal == val) { // // Don't write out default // PRDPF_RECORD node = FindRecord(szName); if(node) { return DeleteRecord(node); } return TRUE; } else { BOOL bRet = InsertIntRecord(szName, val); return bRet; } } else { return FALSE; } DC_END_FN(); } BOOL CRdpFileStore::ReadBool(LPCTSTR szName, UINT defaultVal, PBOOL pbVal) { DC_BEGIN_FN("ReadBool"); UINT val; TRC_ASSERT(szName && pbVal, (TB,_T("Invalid params to ReadBool"))); if(szName && pbVal) { if(ReadInt(szName, defaultVal, &val)) { *pbVal = (BOOL)val; return TRUE; } } else { return FALSE; } DC_END_FN(); return TRUE; } // // Writes a bool to the store // params // szName - key name // defaultVal - default value (if writing default settings gets deleted) // bVal - value to write out // fIgnoreDefault - if set then always write ignoring szDefault // BOOL CRdpFileStore::WriteBool(LPCTSTR szName, UINT defaultVal,BOOL bVal, BOOL fIgnoreDefault) { DC_BEGIN_FN("WriteBool"); UINT iVal = bVal; BOOL bRet = WriteInt( szName, defaultVal, iVal, fIgnoreDefault); return bRet; DC_END_FN(); } // // ParseFile // parses the _hFile into a reclist and associated namemap hash // BOOL CRdpFileStore::ParseFile() { DC_BEGIN_FN("ParseFile"); TRC_ASSERT(_fs.IsOpenForRead(), (TB,_T("Can't ParseFile on a closed FS"))); if(!_fs.IsOpenForRead()) { return FALSE; } // // Nuke any current in-memory state // DeleteRecords(); // // Parse the file line by line into a RDPF_RECORD list // TCHAR szBuf[LINEBUF_SIZE]; while(ERR_SUCCESS == _fs.ReadNextLine(szBuf, sizeof(szBuf))) { if(!InsertRecordFromLine(szBuf)) { TRC_DBG((TB,_T("Parse error, aborting file parse"))); return FALSE; } } _fs.Close(); DC_END_FN(); return TRUE; } // // InsertRecordFromLine // parses szLine into a record and adds to the record list // BOOL CRdpFileStore::InsertRecordFromLine(LPTSTR szLine) { DC_BEGIN_FN("InsertRecordFromLine"); TCHAR szNameField[LINEBUF_SIZE]; UINT typeCode; TCHAR szValueField[LINEBUF_SIZE]; BOOL fParseOk = FALSE; memset(szNameField,0,sizeof(szNameField)); memset(szValueField,0,sizeof(szValueField)); fParseOk = ParseLine(szLine, &typeCode, szNameField, szValueField); TRC_DBG((TB,_T("Parsed line into fields- name:'%s', value:'%s', typecode:'%d'"), szNameField, szValueField, typeCode)); TRC_ASSERT(IS_VALID_RDPF_TYPECODE(typeCode), (TB,_T("typeCode %d is invalid"), typeCode)); if(IS_VALID_RDPF_TYPECODE(typeCode)) { //Create a new record for this line //and insert it into the reclist if(typeCode == RDPF_RECTYPE_UNPARSED) { //Unparsed line: Value is the whole line. Name ignored HRESULT hr = StringCchCopy(szValueField, SIZECHAR(szValueField), szLine); if (FAILED(hr)) { TRC_ERR((TB, _T("String copy failed: hr = 0x%x"), hr)); return FALSE; } } //names are always lower case if(InsertRecord(_tcslwr(szNameField), typeCode, szValueField)) { return TRUE; } else { return FALSE; } } else { // // Invalid typecode // return FALSE; } DC_END_FN(); return TRUE; } // // ParseLine // parses the lines into tokens, returns false // if the line does not match the expected format // // params // szLine - line to parse // pTypeCode - [OUT] typecode // szNameField - [OUT] name field. (must be at least LINEBUF_SIZE) // szValueField - [OUT] value field. (must be at least LINEBUF_SIZE) // // BOOL CRdpFileStore::ParseLine(LPTSTR szLine, PUINT pTypeCode, LPTSTR szNameField, LPTSTR szValueField) { PTCHAR szWrite = NULL; TCHAR szTypeCode; INT writeCount = 0; DC_BEGIN_FN("ParseLine"); // // Try to parse the line, if unable to parse it return // false. // // Format is "fieldname:szTypeCode:value" // e.g "server:s:localhost" // // szTypeCodes are: // s - string // i - UINT // b - binary blob (encoded) // TRC_ASSERT(szLine, (TB,_T("szLine is null"))); TRC_ASSERT(pTypeCode, (TB,_T("pTypeCode is null"))); TRC_ASSERT(szNameField, (TB,_T("szNameField is null"))); TRC_ASSERT(szValueField, (TB,_T("szValueField is null"))); if(szLine && pTypeCode && szNameField && szValueField) { // // parse the whole line in one pass // goto used on error case to avoid horrible nesting // PTCHAR sz = szLine; while(*sz && *sz == TCHAR(' ')) sz++; //eat leading whitespace if(!*sz) { goto parse_error; } //Copy field 1 PTCHAR szWrite = szNameField; writeCount = 0; while(*sz && *sz != TCHAR(':')) { *szWrite++ = *sz++; if(++writeCount > LINEBUF_SIZE) { TRC_ERR((TB,_T("Field1 exceeds max size. size: %d"), writeCount)); goto parse_error; } } *szWrite = NULL; if(*sz != TCHAR(':')) { goto parse_error; sz++; } sz++; //eat ':' while(*sz && *sz == TCHAR(' ')) sz++; //eat whitespace if( *sz ) { szTypeCode = isupper(*sz) ? #ifndef OS_WINCE _tolower(*sz++) #else towlower(*sz++) #endif : *sz++; switch(szTypeCode) { case TCHAR('s'): *pTypeCode = RDPF_RECTYPE_SZ; break; case TCHAR('i'): *pTypeCode = RDPF_RECTYPE_UINT; break; case TCHAR('b'): *pTypeCode = RDPF_RECTYPE_BINARY; break; default: TRC_ERR((TB,_T("Invalid szTypeCode in szLine '%s'"), szLine)); *pTypeCode = RDPF_RECTYPE_UNPARSED; goto parse_error; break; } } else { TRC_ERR((TB,_T("Invalid szTypeCode in szLine '%s'"), szLine)); goto parse_error; } while(*sz && *sz == TCHAR(' ')) sz++; //eat whitespace if(*sz != TCHAR(':')) { goto parse_error; } sz++; //eat ':' while(*sz && *sz == TCHAR(' ')) sz++; //eat leading whitespace //rest of line is field3 szWrite = szValueField; writeCount = 0; while(*sz && *sz != TCHAR('\n')) { *szWrite++ = *sz++; if(++writeCount > LINEBUF_SIZE) { TRC_ERR((TB,_T("Field1 exceeds max size. size: %d"), writeCount)); goto parse_error; } } *szWrite = NULL; return TRUE; } parse_error: TRC_ERR((TB,_T("Parse error in line"))); //Add an unknown record..it will be persisted back out to //the file (it could be from a newer file version) *pTypeCode = RDPF_RECTYPE_UNPARSED; DC_END_FN(); return FALSE; } // // InsertRecord // inserts new record, modifies existing record // if one exists with the same name field // BOOL CRdpFileStore::InsertRecord(LPCTSTR szName, UINT TypeCode, LPCTSTR szValue) { DC_BEGIN_FN("InsertRecord"); TRC_ASSERT(IS_VALID_RDPF_TYPECODE(TypeCode), (TB,_T("typeCode %d is invalid"), TypeCode)); TRC_ASSERT(szName && szValue, (TB,_T("Invalid szName or szValue"))); if(szName && szValue) { PRDPF_RECORD node; node = FindRecord(szName); if(node) { if(node->recType == TypeCode) { // // Existing record found, modify it's contents // first free any allocated memory in the current // node. // switch(TypeCode) { case RDPF_RECTYPE_SZ: { if(node->u.szVal) { LocalFree(node->u.szVal); } } break; case RDPF_RECTYPE_BINARY: { if(node->u.pBinVal) { LocalFree(node->u.pBinVal); } } break; case RDPF_RECTYPE_UNPARSED: { if(node->u.szUnparsed) { LocalFree(node->u.szUnparsed); } } break; default: { return FALSE; } break; } // // Set the node value from the typecode // if(SetNodeValue(node, TypeCode, szValue)) { return TRUE; } } else { // // dup record of differing type // TRC_ASSERT(FALSE,(TB,_T("found duplicate record of differing type"))); return FALSE; } } else { PRDPF_RECORD node = NewRecord(szName, TypeCode); if(node) { if(SetNodeValue(node, TypeCode, szValue)) { //Append the node to the end of the reclist if(AppendRecord(node)) { return TRUE; } } } return FALSE; } } DC_END_FN(); return FALSE; } // // Sets node value based on a typecode // this coaxes the value from the string form // // This function is the generic version that accepts the value as a string // parameter. Automatic conversion are done to the appropriate type. // inline BOOL CRdpFileStore::SetNodeValue(PRDPF_RECORD pNode, RDPF_RECTYPE TypeCode, LPCTSTR szValue) { DC_BEGIN_FN("SetNodeValue"); TRC_ASSERT(pNode && szValue && IS_VALID_RDPF_TYPECODE(TypeCode), (TB,_T("Invalid SetNodeValue params"))); if(pNode && szValue) { switch(TypeCode) { case RDPF_RECTYPE_UINT: { pNode->u.iVal = _ttol(szValue); return TRUE; } break; case RDPF_RECTYPE_SZ: { pNode->u.szVal = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR)*(_tcslen(szValue)+1)); if(pNode->u.szVal) { _tcscpy(pNode->u.szVal,szValue); return TRUE; } else { return FALSE; } } break; case RDPF_RECTYPE_BINARY: { //Convert from string form to actual binary bits UINT strLen = _tcslen(szValue); DWORD dwLen = 0; // // First get the buffer length // (binaryToString returns the wrong length when the // null parameter is passed in). dwLen = (strLen >> 1) + 2; pNode->u.pBinVal = (PBYTE) LocalAlloc(LPTR, dwLen); if(!pNode->u.pBinVal) { TRC_ERR((TB,_T("Failed to alloc %d bytes"), dwLen)); return FALSE; } memset(pNode->u.pBinVal,0,dwLen); // // Do the conversion // if(!CUT::BinarytoString( strLen, (LPTSTR)szValue, (PBYTE)pNode->u.pBinVal, &dwLen)) { TRC_ERR((TB,_T("BinaryToString conversion failed"))); return FALSE; } pNode->dwBinValLen = dwLen; } break; case RDPF_RECTYPE_UNPARSED: { pNode->u.szUnparsed = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR)*(_tcslen(szValue)+1)); if(pNode->u.szUnparsed) { _tcscpy(pNode->u.szUnparsed,szValue); return TRUE; } else { return FALSE; } } break; default: { return FALSE; } break; } } else { return FALSE; } DC_END_FN(); return TRUE; } // // Inserts an int record (RDPF_RECTYPE_UINT) // modifies existing record if one is found // BOOL CRdpFileStore::InsertIntRecord(LPCTSTR szName, UINT value) { DC_BEGIN_FN("InsertIntRecord"); TRC_ASSERT(szName, (TB,_T("Invalid szName"))); if(szName) { PRDPF_RECORD node; node = FindRecord(szName); if(node) { if(node->recType == RDPF_RECTYPE_UINT) { // // Existing record found, modify it's contents // node->u.iVal = value; return TRUE; } else { // // dup record of differing type // TRC_ASSERT(FALSE,(TB,_T("found duplicate record of differing type"))); return FALSE; } } else { PRDPF_RECORD node = NewRecord(szName, RDPF_RECTYPE_UINT); if(node) { node->u.iVal = value; //Append the node to the end of the reclist if(AppendRecord(node)) { return TRUE; } } return FALSE; } } else { return FALSE; } DC_END_FN(); } // // Insert a binary buffer record (RDPF_RECTYPE_BINARY) // modifies existing record if one found // BOOL CRdpFileStore::InsertBinaryRecord(LPCTSTR szName, PBYTE pBuf, DWORD dwLen) { DC_BEGIN_FN("InsertBinaryRecord"); TRC_ASSERT(szName && pBuf && dwLen, (TB,_T("Invalid szName or pBuf"))); if(szName) { PRDPF_RECORD node; node = FindRecord(szName); if(node) { if(node->recType == RDPF_RECTYPE_BINARY) { // // Existing record found, modify its contents // if(node->u.pBinVal) { LocalFree(node->u.pBinVal); } node->u.pBinVal = (PBYTE) LocalAlloc(LPTR, dwLen); if(node->u.pBinVal) { memcpy(node->u.pBinVal, pBuf, dwLen); node->dwBinValLen = dwLen; return TRUE; } else { return FALSE; } return TRUE; } else { // // dup record of differing type // TRC_ASSERT(FALSE,(TB,_T("found duplicate record of differing type"))); return FALSE; } } else { PRDPF_RECORD node = NewRecord(szName, RDPF_RECTYPE_BINARY); if(node) { node->u.pBinVal = (PBYTE) LocalAlloc(LPTR, dwLen); if(node->u.pBinVal) { memcpy(node->u.pBinVal, pBuf, dwLen); node->dwBinValLen = dwLen; if(AppendRecord(node)) { return TRUE; } else { return FALSE; } } else { return FALSE; } } return FALSE; } } else { return FALSE; } DC_END_FN(); } // // A worker function to make life easier in RecordToString. This function // takes a source string, cats it to a destination string, and then appends // a carriage return and line-feed. // HRESULT StringCchCatCRLF(LPTSTR pszDest, size_t cchDest, LPCTSTR pszSrc) { HRESULT hr = E_FAIL; DC_BEGIN_FN("StringCchCatCRLF"); hr = StringCchCat(pszDest, cchDest, pszSrc); if (FAILED(hr)) { DC_QUIT; } hr = StringCchCat(pszDest, cchDest, _T("\r\n")); if (FAILED(hr)) { DC_QUIT; } DC_EXIT_POINT: DC_END_FN(); return hr; } // // Flatten a record to a string (szBuf) using format: // name:type:value\r\n // BOOL CRdpFileStore::RecordToString(PRDPF_RECORD pNode, LPTSTR szBuf, UINT strLen) { DC_BEGIN_FN("RecordToString"); TRC_ASSERT(pNode && szBuf && strLen, (TB,_T("Invalid parameters to RecordToString"))); TCHAR szTemp[LINEBUF_SIZE]; INT lenRemain = strLen; HRESULT hr; if(pNode && szBuf && strLen) { TRC_ASSERT(IS_VALID_RDPF_TYPECODE(pNode->recType), (TB,_T("Invalid typecode %d"),pNode->recType)); if(pNode->recType != RDPF_RECTYPE_UNPARSED) { // Space for name field, typecode, two delimiters and a NULL. lenRemain -= _tcslen(pNode->szName) + 4; if(lenRemain >= 0) { hr = StringCchPrintf(szBuf, strLen, _T("%s%s"), pNode->szName, g_szTypeCodeMap[pNode->recType]); if (FAILED(hr)) { TRC_ERR((TB, _T("String printf failed: hr = 0x%x"), hr)); return FALSE; } switch(pNode->recType) { case RDPF_RECTYPE_UINT: { _stprintf(szTemp,TEXT("%d"),pNode->u.iVal); // Need space for a "\r\n" sequence. lenRemain -= _tcslen(szTemp) + 2; if(lenRemain >= 0) { hr = StringCchCatCRLF(szBuf, strLen, szTemp); if (FAILED(hr)) { TRC_ERR((TB, _T("String concatenation failed: hr = 0x%x"), hr)); return FALSE; } return TRUE; } else { return FALSE; } } break; case RDPF_RECTYPE_SZ: { // Need space for a "\r\n" sequence. lenRemain -= _tcslen(pNode->u.szVal) + 2; if(lenRemain >= 0) { hr = StringCchCatCRLF(szBuf, strLen, pNode->u.szVal); if (FAILED(hr)) { TRC_ERR((TB, _T("String concatenation failed: hr = 0x%x"), hr)); return FALSE; } return TRUE; } else { return FALSE; } } break; case RDPF_RECTYPE_BINARY: { DWORD dwLen; // // Convert the binary buffer to string form // // // First get the buffer length // if(!CUT::StringtoBinary( pNode->dwBinValLen, (PBYTE)pNode->u.pBinVal, NULL, &dwLen)) { TRC_ERR((TB, _T("Failed to get StringtoBinary buffer len"))); return FALSE; } lenRemain -= dwLen; if(lenRemain >= 0 && dwLen < LINEBUF_SIZE) { // // Do the conversion // if(CUT::StringtoBinary( pNode->dwBinValLen, (PBYTE)pNode->u.pBinVal, (LPTSTR) szTemp, &dwLen)) { //String to binary appends two trailing //'0' characters. get rid of them. szTemp[dwLen-2] = NULL; hr = StringCchCatCRLF(szBuf, strLen, szTemp); if (FAILED(hr)) { TRC_ERR((TB, _T("String concatenation failed: hr = 0x%x"), hr)); return FALSE; } return TRUE; } else { TRC_ERR((TB,_T("StringtoBinary conversion failed"))); return FALSE; } } else { return FALSE; } } break; } return FALSE; } else { return FALSE; } } else { //Unparsed record, just splat the value hr = StringCchCopy(szBuf, strLen, pNode->u.szUnparsed); if (SUCCEEDED(hr)) { return TRUE; } else { TRC_ERR((TB, _T("String copy failed: hr = 0x%x"), hr)); return FALSE; } } } else { return FALSE; } DC_END_FN(); } // // Search the record list // for the first record with the given name // PRDPF_RECORD CRdpFileStore::FindRecord(LPCTSTR szName) { DC_BEGIN_FN("FindRecord"); if(szName && _pRecordListHead) { TCHAR szCmpName[RDPF_NAME_LEN]; HRESULT hr = StringCchCopy(szCmpName, SIZECHAR(szCmpName), szName); if (FAILED(hr)) { TRC_ERR((TB, _T("String copy failed: hr = 0x%x"), hr)); return NULL; } _tcslwr(szCmpName); PRDPF_RECORD node = _pRecordListHead; while(node) { if(!_tcscmp(szCmpName, node->szName)) { return node; } node=node->pNext; } return NULL; } else { return NULL; } DC_END_FN(); } // // Append record to the end of the record list // for a record with the given name // BOOL CRdpFileStore::AppendRecord(PRDPF_RECORD node) { DC_BEGIN_FN("AppendRecord"); if(node) { node->pNext = NULL; if(_pRecordListHead && _pRecordListTail) { node->pPrev = _pRecordListTail; _pRecordListTail->pNext= node; _pRecordListTail = node; return TRUE; } else { _pRecordListHead = _pRecordListTail = node; node->pPrev = NULL; return TRUE; } } else { return FALSE; } DC_END_FN(); } // // Create a new record with name szName // PRDPF_RECORD CRdpFileStore::NewRecord(LPCTSTR szName, UINT TypeCode) { DC_BEGIN_FN("NewRecord"); PRDPF_RECORD node = NULL; if(szName) { //Need to insert new node node = (PRDPF_RECORD)LocalAlloc(LPTR, sizeof(RDPF_RECORD)); if(node) { node->recType = TypeCode; HRESULT hr = StringCchCopy(node->szName, SIZECHAR(node->szName), szName); if (FAILED(hr)) { TRC_ERR((TB, _T("String copy failed: hr = 0x%x"), hr)); return NULL; } _tcslwr(node->szName); node->pPrev= node->pNext= NULL; } } DC_END_FN(); return node; } // // DeleteRecords // deletes and resets all inmemory record structures // BOOL CRdpFileStore::DeleteRecords() { DC_BEGIN_FN("DeleteRecords"); PRDPF_RECORD node = _pRecordListHead; PRDPF_RECORD prev; while(node) { prev = node; node = node->pNext; switch(prev->recType) { case RDPF_RECTYPE_SZ: LocalFree(prev->u.szVal); break; case RDPF_RECTYPE_BINARY: LocalFree(prev->u.pBinVal); break; case RDPF_RECTYPE_UNPARSED: LocalFree(prev->u.szUnparsed); break; } LocalFree(prev); } _pRecordListHead = NULL; _pRecordListTail = NULL; DC_END_FN(); return TRUE; } inline BOOL CRdpFileStore::DeleteRecord(PRDPF_RECORD node) { DC_BEGIN_FN("DeleteRecord"); TRC_ASSERT(node,(TB,_T("node is null"))); if(node) { if(_pRecordListTail == node) { _pRecordListTail = node->pPrev; } if(_pRecordListHead == node) { _pRecordListHead = node->pNext; } if(node->pPrev) { node->pPrev->pNext = node->pNext; } if(node->pNext) { node->pNext->pPrev = node->pPrev; } switch(node->recType) { case RDPF_RECTYPE_SZ: LocalFree(node->u.szVal); break; case RDPF_RECTYPE_BINARY: LocalFree(node->u.pBinVal); break; case RDPF_RECTYPE_UNPARSED: LocalFree(node->u.szUnparsed); break; } LocalFree(node); return TRUE; } else { return FALSE; } DC_END_FN(); return FALSE; } BOOL CRdpFileStore::DeleteValueIfPresent(LPCTSTR szName) { DC_BEGIN_FN("DeleteValueIfPresent"); TRC_ASSERT(szName,(TB,_T("szName is null"))); if(szName) { PRDPF_RECORD node = FindRecord(szName); if(node) { return DeleteRecord(node); } else { return TRUE; } } else { return FALSE; } DC_END_FN(); } // // Initialize to a NULL store that is readable // BOOL CRdpFileStore::SetToNullStore() { DC_BEGIN_FN("SetToNullStore"); DeleteRecords(); _fOpenForRead = TRUE; _fOpenForWrite = TRUE; DC_END_FN(); return TRUE; } // // Return TRUE if the record is present // BOOL CRdpFileStore::IsValuePresent(LPTSTR szName) { DC_BEGIN_FN("IsValuePresent"); if(szName) { PRDPF_RECORD node = FindRecord(szName); if(node) { return TRUE; } else { return FALSE; } } else { return FALSE; } DC_END_FN(); } DWORD CRdpFileStore::GetDataLength(LPCTSTR szName) { if(szName) { PRDPF_RECORD node = FindRecord(szName); if(node) { switch (node->recType) { case RDPF_RECTYPE_UINT: return sizeof(UINT); break; case RDPF_RECTYPE_SZ: return _tcslen(node->u.szVal) * sizeof(TCHAR); break; case RDPF_RECTYPE_BINARY: return node->dwBinValLen; break; default: return 0; } } else { return 0; } } else { return 0; } }