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

700 lines
20 KiB

  1. //
  2. // CallLog.cpp
  3. //
  4. // Created: ChrisPi 10-17-96
  5. // Updated: RobD 10-30-96
  6. //
  7. // ToDo:
  8. // - Expire records
  9. // - UI to delete record(s)
  10. // - system policy?
  11. //
  12. #include "precomp.h"
  13. #include "rostinfo.h"
  14. #include "CallLog.h"
  15. #include "particip.h" // for MAX_PARTICIPANT_NAME
  16. #include "ConfUtil.h"
  17. #define MAX_DELETED_ENTRIES_BEFORE_REWRITE 10
  18. #define LARGE_ENTRY_SIZE 256
  19. CCallLogEntry::CCallLogEntry( LPCTSTR pcszName,
  20. DWORD dwFlags,
  21. CRosterInfo* pri,
  22. LPVOID pvRosterData,
  23. PBYTE pbCert,
  24. ULONG cbCert,
  25. LPSYSTEMTIME pst,
  26. DWORD dwFileOffset) :
  27. m_dwFlags (dwFlags),
  28. m_pri (NULL),
  29. m_pbCert (NULL),
  30. m_cbCert (0),
  31. m_dwFileOffset (dwFileOffset)
  32. {
  33. DebugEntry(CCallLogEntry::CCallLogEntry);
  34. ASSERT(NULL != pcszName);
  35. ASSERT(NULL != pst);
  36. // Only one of these two parameters should be non-NULL
  37. ASSERT((NULL == pvRosterData) || (NULL == pri));
  38. LPVOID pvData = pvRosterData;
  39. if (NULL != pri)
  40. {
  41. UINT cbData;
  42. if (SUCCEEDED(pri->Save(&pvData, &cbData)))
  43. {
  44. ASSERT(pvData);
  45. }
  46. }
  47. if (NULL != pvData)
  48. {
  49. m_pri = new CRosterInfo();
  50. if (NULL != m_pri)
  51. {
  52. m_pri->Load(pvData);
  53. }
  54. }
  55. if (NULL != pbCert && 0 != cbCert)
  56. {
  57. m_pbCert = new BYTE[cbCert];
  58. if (NULL == m_pbCert)
  59. {
  60. ERROR_OUT(("CCalllogEntry::CCalllogEntry() -- failed to allocate memory"));
  61. }
  62. else
  63. {
  64. memcpy(m_pbCert, pbCert, cbCert);
  65. m_cbCert = cbCert;
  66. }
  67. }
  68. m_st = *pst;
  69. m_pszName = PszAlloc(pcszName);
  70. DebugExitVOID(CCallLogEntry::CCallLogEntry);
  71. }
  72. CCallLogEntry::~CCallLogEntry()
  73. {
  74. DebugEntry(CCallLogEntry::~CCallLogEntry);
  75. delete m_pszName;
  76. // NOTE: m_pri must be new'ed by the function that calls the
  77. // constructor - this is an optimization to avoid unnecessary
  78. // copying - but it's a little unclean.
  79. delete m_pri;
  80. delete []m_pbCert;
  81. DebugExitVOID(CCallLogEntry::~CCallLogEntry);
  82. }
  83. /////////////////////////////////////////////////////////////////////////
  84. CCallLog::CCallLog(LPCTSTR pszKey, LPCTSTR pszDefault) :
  85. m_fUseList (FALSE),
  86. m_fDataRead (FALSE),
  87. m_cTotalEntries (0),
  88. m_cDeletedEntries (0)
  89. {
  90. InitLogData(pszKey, pszDefault);
  91. TRACE_OUT(("Using Call Log file [%s]", m_strFile));
  92. }
  93. CCallLog::~CCallLog()
  94. {
  95. DebugEntry(CCallLog::~CCallLog);
  96. // Check to see if we are more than a number entries over our
  97. // configured maximum and re-write the file if so
  98. TRACE_OUT(("Entry count: total:%d deleted:%d", m_cTotalEntries,
  99. m_cDeletedEntries));
  100. if ( m_fUseList && m_cDeletedEntries > MAX_DELETED_ENTRIES_BEFORE_REWRITE )
  101. RewriteFile();
  102. else
  103. {
  104. int size = GetSize();
  105. for( int i = 0; i < size; i++ )
  106. {
  107. ASSERT( NULL != (*this)[i] );
  108. delete (*this)[i];
  109. }
  110. }
  111. DebugExitVOID(CCallLog::~CCallLog);
  112. }
  113. /* A D D C A L L */
  114. /*-------------------------------------------------------------------------
  115. %%Function: AddCall
  116. -------------------------------------------------------------------------*/
  117. HRESULT CCallLog::AddCall(LPCTSTR pcszName, PLOGHDR pLogHdr, CRosterInfo* pri, PBYTE pbCert, ULONG cbCert)
  118. {
  119. TRACE_OUT( ("CCallLog::AddCall(\"%s\")", pcszName) );
  120. DWORD dwFileOffset;
  121. HRESULT hr = S_OK;
  122. ASSERT(NULL != pLogHdr);
  123. // Grab the current local time
  124. ::GetLocalTime(&(pLogHdr->sysTime));
  125. ApiDebugMsg(("CALL_LOG: [%s] %s", pcszName,
  126. (pLogHdr->dwCLEF & CLEF_ACCEPTED) ? "ACCEPTED" : "REJECTED"));
  127. // Append the data to the file
  128. dwFileOffset = WriteEntry(pcszName, pLogHdr, pri, pbCert, cbCert);
  129. TRACE_OUT(("AddCall: adding entry with %d total, %d deleted, %d max",
  130. m_cTotalEntries, m_cDeletedEntries, m_cMaxEntries ));
  131. // Create list entry only when necessary
  132. if (m_fUseList)
  133. {
  134. CCallLogEntry* pcleNew = new CCallLogEntry( pcszName,
  135. pLogHdr->dwCLEF, pri, NULL, pbCert, cbCert, &(pLogHdr->sysTime), dwFileOffset);
  136. if (NULL == pcleNew)
  137. return E_OUTOFMEMORY;
  138. Add(pcleNew);
  139. m_cTotalEntries++;
  140. // Check to see if this put us over the top of valid entries
  141. // and remove the oldest entry if so
  142. if ( m_cTotalEntries - m_cDeletedEntries > m_cMaxEntries )
  143. {
  144. // Remove oldest entry
  145. DeleteEntry((*this)[0]);
  146. RemoveAt( 0 );
  147. }
  148. }
  149. else
  150. {
  151. // Check to see if the file is getting large based on
  152. // our target number of entries and a heuristic large
  153. // entry size. If our file has grown over this point, load
  154. // the file and trim the list so that we will re-write
  155. // a smaller file on exit.
  156. TRACE_OUT(("Checking file size %d against %d * %d",
  157. dwFileOffset, m_cMaxEntries, LARGE_ENTRY_SIZE));
  158. if ( dwFileOffset > (DWORD)( m_cMaxEntries * LARGE_ENTRY_SIZE ) )
  159. {
  160. TRACE_OUT(("Log file getting large, forcing LoadFileData"));
  161. LoadFileData();
  162. }
  163. }
  164. return S_OK;
  165. }
  166. /* I N I T L O G D A T A */
  167. /*-------------------------------------------------------------------------
  168. %%Function: InitLogData
  169. Get the log data from the registry.
  170. - the expiration information
  171. - the file name (with path)
  172. If there is no entry for the file name, a new, unique file is created.
  173. -------------------------------------------------------------------------*/
  174. VOID CCallLog::InitLogData(LPCTSTR pszKey, LPCTSTR pszDefault)
  175. {
  176. TCHAR szPath[MAX_PATH];
  177. PTSTR pszFileName;
  178. HANDLE hFile;
  179. ASSERT(m_strFile.IsEmpty());
  180. RegEntry reLog(pszKey, HKEY_CURRENT_USER);
  181. m_Expire = reLog.GetNumber(REGVAL_LOG_EXPIRE, 0);
  182. m_strFile = reLog.GetString(REGVAL_LOG_FILE);
  183. m_cMaxEntries = reLog.GetNumber(REGVAL_LOG_MAX_ENTRIES,
  184. DEFAULT_LOG_MAX_ENTRIES );
  185. TRACE_OUT(("Max Entries set to %d", m_cMaxEntries ));
  186. // Make sure file exists and can be read/written
  187. hFile = OpenLogFile();
  188. if (NULL != hFile)
  189. {
  190. // valid file found
  191. CloseHandle(hFile);
  192. return;
  193. }
  194. // String is invalid (or empty) - make sure it's empty
  195. m_strFile.Empty();
  196. // Create the new log file in the NetMeeting directory
  197. if (!GetInstallDirectory(szPath))
  198. {
  199. WARNING_OUT(("InitLogData: Unable to get Install Directory?"));
  200. return;
  201. }
  202. pszFileName = &szPath[lstrlen(szPath)];
  203. // Try to use the default name
  204. wsprintf(pszFileName, TEXT("%s%s"), pszDefault, TEXT(".dat"));
  205. hFile = CreateFile(szPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
  206. NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
  207. if (INVALID_HANDLE_VALUE == hFile)
  208. {
  209. // Use a unique name to avoid other users' files
  210. for (int iFile = 2; iFile < 999; iFile++)
  211. {
  212. wsprintf(pszFileName, TEXT("%s%d.dat"), pszDefault, iFile);
  213. hFile = CreateFile(szPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
  214. NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
  215. if (INVALID_HANDLE_VALUE != hFile)
  216. break;
  217. switch (GetLastError())
  218. {
  219. case ERROR_FILE_EXISTS: // We get this with NT
  220. case ERROR_ALREADY_EXISTS: // and this with Win95
  221. break;
  222. default:
  223. WARNING_OUT(("Unable to create log file [%s] err=0x%08X", szPath, GetLastError()));
  224. break;
  225. } /* switch (GetLastError()) */
  226. }
  227. }
  228. if (INVALID_HANDLE_VALUE != hFile)
  229. {
  230. CloseHandle(hFile);
  231. m_strFile = szPath;
  232. reLog.SetValue(REGVAL_LOG_FILE, szPath);
  233. }
  234. }
  235. /* O P E N L O G F I L E */
  236. /*-------------------------------------------------------------------------
  237. %%Function: OpenLogFile
  238. Open the log file and return a handle to file.
  239. Return NULL if there was a problem.
  240. -------------------------------------------------------------------------*/
  241. HANDLE CCallLog::OpenLogFile(VOID)
  242. {
  243. HANDLE hFile;
  244. if (m_strFile.IsEmpty())
  245. {
  246. WARNING_OUT(("Problem opening call log file"));
  247. return NULL;
  248. }
  249. hFile = CreateFile(m_strFile, GENERIC_READ | GENERIC_WRITE,
  250. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  251. OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  252. if (INVALID_HANDLE_VALUE == hFile)
  253. {
  254. ERROR_OUT(("OpenLogFile: Unable to open call log file"));
  255. hFile = NULL;
  256. }
  257. return hFile;
  258. }
  259. /* R E A D D A T A */
  260. /*-------------------------------------------------------------------------
  261. %%Function: ReadData
  262. -------------------------------------------------------------------------*/
  263. BOOL CCallLog::ReadData(HANDLE hFile, PVOID pv, UINT cb)
  264. {
  265. DWORD cbRead;
  266. ASSERT(NULL != hFile);
  267. ASSERT(NULL != pv);
  268. if (0 == cb)
  269. return TRUE;
  270. if (!ReadFile(hFile, pv, cb, &cbRead, NULL))
  271. return FALSE;
  272. return (cb == cbRead);
  273. }
  274. /* W R I T E D A T A */
  275. /*-------------------------------------------------------------------------
  276. %%Function: WriteData
  277. Write the data to the file.
  278. The file will be automatically opened/close if hFile is NULL.
  279. -------------------------------------------------------------------------*/
  280. HRESULT CCallLog::WriteData(HANDLE hFile, LPDWORD pdwOffset, PVOID pv, DWORD cb)
  281. {
  282. HRESULT hr = E_FAIL;
  283. HANDLE hFileTemp = NULL;
  284. DWORD cbWritten;
  285. if (0 == cb)
  286. return S_OK; // nothing to do
  287. ASSERT(NULL != pv);
  288. ASSERT(NULL != pdwOffset);
  289. ASSERT(INVALID_FILE_SIZE != *pdwOffset);
  290. if (NULL == hFile)
  291. {
  292. // Auto-open the file, if necessary
  293. hFileTemp = OpenLogFile();
  294. if (NULL == hFileTemp)
  295. return E_FAIL;
  296. hFile = hFileTemp;
  297. }
  298. ASSERT(INVALID_HANDLE_VALUE != hFile);
  299. if (INVALID_FILE_SIZE != SetFilePointer(hFile, *pdwOffset, NULL, FILE_BEGIN))
  300. {
  301. if (WriteFile(hFile, pv, cb, &cbWritten, NULL) && (cb == cbWritten))
  302. {
  303. *pdwOffset += cbWritten;
  304. hr = S_OK;
  305. }
  306. }
  307. if (NULL != hFileTemp)
  308. {
  309. // Close the temporary file handle
  310. CloseHandle(hFileTemp);
  311. }
  312. return hr;
  313. }
  314. /* W R I T E E N T R Y */
  315. /*-------------------------------------------------------------------------
  316. %%Function: WriteEntry
  317. Write a call log entry to the end of the log file.
  318. The function returns the file position at which the data was written
  319. or INVALID_FILE_SIZE (0xFFFFFFFF) if there was a problem.
  320. -------------------------------------------------------------------------*/
  321. DWORD CCallLog::WriteEntry(LPCTSTR pcszName, PLOGHDR pLogHdr, CRosterInfo* pri, PBYTE pbCert, ULONG cbCert)
  322. {
  323. PVOID pvData;
  324. HANDLE hFile;
  325. DWORD dwFilePosition;
  326. DWORD dwPos;
  327. BSTR pcwszName;
  328. ASSERT(NULL != pcszName);
  329. ASSERT(NULL != pLogHdr);
  330. hFile = OpenLogFile();
  331. if (NULL == hFile)
  332. return INVALID_FILE_SIZE;
  333. dwFilePosition = SetFilePointer(hFile, 0, NULL, FILE_END);
  334. if (INVALID_FILE_SIZE != dwFilePosition)
  335. {
  336. dwPos = dwFilePosition;
  337. // Always write display name in UNICODE
  338. if(SUCCEEDED(LPTSTR_to_BSTR(&pcwszName, pcszName)))
  339. {
  340. pLogHdr->cbName = (lstrlenW((LPWSTR)pcwszName) + 1) * sizeof(WCHAR);
  341. if ((NULL == pri) ||
  342. (!SUCCEEDED(pri->Save(&pvData, (UINT *) &(pLogHdr->cbData)))) )
  343. {
  344. // No data?
  345. pLogHdr->cbData = 0;
  346. pvData = NULL;
  347. }
  348. pLogHdr->cbCert = cbCert;
  349. // Calculate total size of record
  350. pLogHdr->dwSize = sizeof(LOGHDR) + pLogHdr->cbName + pLogHdr->cbData + pLogHdr->cbCert;
  351. if ((S_OK != WriteData(hFile, &dwPos, pLogHdr, sizeof(LOGHDR))) ||
  352. (S_OK != WriteData(hFile, &dwPos, pcwszName, pLogHdr->cbName)) ||
  353. (S_OK != WriteData(hFile, &dwPos, pvData, pLogHdr->cbData)) ||
  354. (S_OK != WriteData(hFile, &dwPos, pbCert, cbCert)))
  355. {
  356. dwFilePosition = INVALID_FILE_SIZE;
  357. }
  358. SysFreeString(pcwszName);
  359. }
  360. }
  361. CloseHandle(hFile);
  362. return dwFilePosition;
  363. }
  364. /* R E A D E N T R Y */
  365. /*-------------------------------------------------------------------------
  366. %%Function: ReadEntry
  367. Read the next entry from the file.
  368. *ppcle will be set to NULL if the entry was deleted.
  369. Return Values:
  370. S_OK - data was read successfully
  371. S_FALSE - data exists, but was deleted
  372. E_FAIL - problem reading file
  373. -------------------------------------------------------------------------*/
  374. HRESULT CCallLog::ReadEntry(HANDLE hFile, DWORD * pdwFileOffset, CCallLogEntry** ppcle)
  375. {
  376. DWORD dwOffsetSave;
  377. LOGHDR logHdr;
  378. WCHAR wszName[MAX_PARTICIPANT_NAME];
  379. ASSERT(NULL != ppcle);
  380. ASSERT(NULL != hFile);
  381. ASSERT(NULL != pdwFileOffset);
  382. *ppcle = NULL; // initialize this in case we return with an error
  383. dwOffsetSave = *pdwFileOffset;
  384. if (INVALID_FILE_SIZE == SetFilePointer(hFile, dwOffsetSave, NULL, FILE_BEGIN))
  385. return E_FAIL;
  386. // Read record header
  387. if (!ReadData(hFile, &logHdr, sizeof(LOGHDR)) )
  388. return E_FAIL;
  389. // Return pointer to end of record
  390. *pdwFileOffset += logHdr.dwSize;
  391. if (logHdr.dwCLEF & CLEF_DELETED)
  392. {
  393. // Skip deleted record
  394. ASSERT(NULL == *ppcle);
  395. return S_FALSE;
  396. }
  397. if (logHdr.cbName > sizeof(wszName))
  398. logHdr.cbName = sizeof(wszName);
  399. // Read Name
  400. if (!ReadData(hFile, wszName, logHdr.cbName))
  401. return E_FAIL;
  402. // Read Extra Data
  403. PVOID pvData = NULL;
  404. if (logHdr.cbData != 0)
  405. {
  406. pvData = new BYTE[logHdr.cbData];
  407. if (NULL != pvData)
  408. {
  409. if (!ReadData(hFile, pvData, logHdr.cbData))
  410. {
  411. WARNING_OUT(("Problem reading roster data from log"));
  412. }
  413. }
  414. }
  415. PBYTE pbCert = NULL;
  416. if ((logHdr.dwCLEF & CLEF_SECURE ) && logHdr.cbCert != 0)
  417. {
  418. pbCert = new BYTE[logHdr.cbCert];
  419. if (NULL != pbCert)
  420. {
  421. if (!ReadData(hFile, pbCert, logHdr.cbCert))
  422. {
  423. WARNING_OUT(("Problem reading certificate data from log"));
  424. }
  425. }
  426. }
  427. BSTR bstrName = ::SysAllocString(wszName);
  428. if(bstrName)
  429. {
  430. LPTSTR szName;
  431. HRESULT hr = BSTR_to_LPTSTR (&szName, bstrName);
  432. if (SUCCEEDED(hr))
  433. {
  434. // Create the new log entry from the data read
  435. *ppcle = new CCallLogEntry(szName, logHdr.dwCLEF,
  436. NULL, pvData, pbCert, logHdr.cbCert, &logHdr.sysTime, dwOffsetSave);
  437. delete szName;
  438. }
  439. SysFreeString(bstrName);
  440. }
  441. delete [] pvData;
  442. delete [] pbCert;
  443. return S_OK;
  444. }
  445. /* L O A D F I L E D A T A */
  446. /*-------------------------------------------------------------------------
  447. %%Function: LoadFileData
  448. Load the call log data from the file
  449. -------------------------------------------------------------------------*/
  450. VOID CCallLog::LoadFileData(VOID)
  451. {
  452. HANDLE hFile;
  453. DWORD dwFileOffset;
  454. CCallLogEntry * pcle;
  455. hFile = OpenLogFile();
  456. if (NULL == hFile)
  457. return;
  458. m_cTotalEntries = 0;
  459. m_cDeletedEntries = 0;
  460. dwFileOffset = 0;
  461. while (E_FAIL != ReadEntry(hFile, &dwFileOffset, &pcle))
  462. {
  463. m_cTotalEntries++;
  464. if (NULL == pcle)
  465. {
  466. m_cDeletedEntries++;
  467. continue; // deleted record
  468. }
  469. Add(pcle);
  470. TRACE_OUT(("Read Entry: \"%s\" (%02d/%02d/%04d %02d:%02d:%02d) : %s",
  471. pcle->m_pszName,
  472. pcle->m_st.wMonth, pcle->m_st.wDay, pcle->m_st.wYear,
  473. pcle->m_st.wHour, pcle->m_st.wMinute, pcle->m_st.wSecond,
  474. (CLEF_ACCEPTED & pcle->m_dwFlags) ? "ACCEPTED" : "REJECTED"));
  475. }
  476. CloseHandle(hFile);
  477. m_fUseList = TRUE;
  478. m_fDataRead = TRUE;
  479. // Now trim the list down to our configured maximum if
  480. // the count exceeds our target. The file will be compacted
  481. // when we write it out if we have more than a few deleted
  482. // entries.
  483. for( int nn = 0, delCount = m_cTotalEntries - m_cDeletedEntries - m_cMaxEntries; nn < delCount; nn++ )
  484. {
  485. DeleteEntry((*this)[0]);
  486. RemoveAt( 0 );
  487. }
  488. }
  489. /* R E W R I T E F I L E */
  490. /*-------------------------------------------------------------------------
  491. %%Function: RewriteFile
  492. Re-write the log file from the in-memory list, compress deleted entries
  493. -------------------------------------------------------------------------*/
  494. VOID CCallLog::RewriteFile(VOID)
  495. {
  496. HANDLE hFile;
  497. TRACE_OUT(("Rewriting log file"));
  498. // Make sure we don't nuke the file without a list to write out
  499. ASSERT(m_fUseList);
  500. // Reset the file pointer and write the EOF marker
  501. if (!m_strFile.IsEmpty())
  502. {
  503. hFile = OpenLogFile();
  504. if (NULL != hFile)
  505. {
  506. if (INVALID_FILE_SIZE != SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
  507. {
  508. SetEndOfFile(hFile);
  509. m_cTotalEntries = 0;
  510. m_cDeletedEntries = 0;
  511. }
  512. CloseHandle(hFile);
  513. }
  514. }
  515. // Write out all non-deleted records
  516. for( int i = 0; i < GetSize(); ++i )
  517. {
  518. CCallLogEntry* pcle = (*this)[i];
  519. ASSERT(NULL != pcle);
  520. LOGHDR LogHdr;
  521. // Initialize LogHdr items from memory object
  522. LogHdr.dwCLEF = pcle->GetFlags();
  523. LogHdr.dwPF = 0;
  524. LogHdr.sysTime = *pcle->GetTime();
  525. // Write out entry
  526. WriteEntry( pcle->m_pszName,
  527. &LogHdr,
  528. pcle->m_pri,
  529. pcle->m_pbCert,
  530. pcle->m_cbCert);
  531. delete pcle;
  532. }
  533. }
  534. // Grumble... Inefficient
  535. HRESULT CCallLog::DeleteEntry(CCallLogEntry * pcle)
  536. {
  537. HRESULT hr;
  538. DWORD dwFlags;
  539. DWORD dwOffset;
  540. if (NULL == pcle)
  541. {
  542. WARNING_OUT(("DeleteEntry: Unable to find entry"));
  543. return E_FAIL;
  544. }
  545. // Calculate offset to "CLEF"
  546. dwOffset = pcle->GetFileOffset() + offsetof(LOGHDR, dwCLEF);
  547. dwFlags = pcle->GetFlags() | CLEF_DELETED;
  548. hr = WriteData(NULL, &dwOffset, &dwFlags, sizeof(DWORD));
  549. m_cDeletedEntries++;
  550. TRACE_OUT(("Marked [%s] pos=%08X for deletion", pcle->GetName(), pcle->GetFileOffset() ));
  551. delete pcle;
  552. return hr;
  553. }