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.

679 lines
16 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. PWSTR pcwszName;
  328. USES_CONVERSION;
  329. ASSERT(NULL != pcszName);
  330. ASSERT(NULL != pLogHdr);
  331. hFile = OpenLogFile();
  332. if (NULL == hFile)
  333. return INVALID_FILE_SIZE;
  334. dwFilePosition = SetFilePointer(hFile, 0, NULL, FILE_END);
  335. if (INVALID_FILE_SIZE != dwFilePosition)
  336. {
  337. dwPos = dwFilePosition;
  338. // Always write display name in UNICODE
  339. pcwszName = T2W(pcszName);
  340. pLogHdr->cbName = (lstrlenW(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. }
  359. CloseHandle(hFile);
  360. return dwFilePosition;
  361. }
  362. /* R E A D E N T R Y */
  363. /*-------------------------------------------------------------------------
  364. %%Function: ReadEntry
  365. Read the next entry from the file.
  366. *ppcle will be set to NULL if the entry was deleted.
  367. Return Values:
  368. S_OK - data was read successfully
  369. S_FALSE - data exists, but was deleted
  370. E_FAIL - problem reading file
  371. -------------------------------------------------------------------------*/
  372. HRESULT CCallLog::ReadEntry(HANDLE hFile, DWORD * pdwFileOffset, CCallLogEntry** ppcle)
  373. {
  374. DWORD dwOffsetSave;
  375. LOGHDR logHdr;
  376. WCHAR wszName[MAX_PARTICIPANT_NAME];
  377. USES_CONVERSION;
  378. ASSERT(NULL != ppcle);
  379. ASSERT(NULL != hFile);
  380. ASSERT(NULL != pdwFileOffset);
  381. *ppcle = NULL; // initialize this in case we return with an error
  382. dwOffsetSave = *pdwFileOffset;
  383. if (INVALID_FILE_SIZE == SetFilePointer(hFile, dwOffsetSave, NULL, FILE_BEGIN))
  384. return E_FAIL;
  385. // Read record header
  386. if (!ReadData(hFile, &logHdr, sizeof(LOGHDR)) )
  387. return E_FAIL;
  388. // Return pointer to end of record
  389. *pdwFileOffset += logHdr.dwSize;
  390. if (logHdr.dwCLEF & CLEF_DELETED)
  391. {
  392. // Skip deleted record
  393. ASSERT(NULL == *ppcle);
  394. return S_FALSE;
  395. }
  396. if (logHdr.cbName > sizeof(wszName))
  397. logHdr.cbName = sizeof(wszName);
  398. // Read Name
  399. if (!ReadData(hFile, wszName, logHdr.cbName))
  400. return E_FAIL;
  401. // Read Extra Data
  402. PVOID pvData = NULL;
  403. if (logHdr.cbData != 0)
  404. {
  405. pvData = new BYTE[logHdr.cbData];
  406. if (NULL != pvData)
  407. {
  408. if (!ReadData(hFile, pvData, logHdr.cbData))
  409. {
  410. WARNING_OUT(("Problem reading roster data from log"));
  411. }
  412. }
  413. }
  414. PBYTE pbCert = NULL;
  415. if ((logHdr.dwCLEF & CLEF_SECURE ) && logHdr.cbCert != 0)
  416. {
  417. pbCert = new BYTE[logHdr.cbCert];
  418. if (NULL != pbCert)
  419. {
  420. if (!ReadData(hFile, pbCert, logHdr.cbCert))
  421. {
  422. WARNING_OUT(("Problem reading certificate data from log"));
  423. }
  424. }
  425. }
  426. // Create the new log entry from the data read
  427. *ppcle = new CCallLogEntry(W2T(wszName), logHdr.dwCLEF,
  428. NULL, pvData, pbCert, logHdr.cbCert, &logHdr.sysTime, dwOffsetSave);
  429. delete pvData;
  430. delete []pbCert;
  431. return S_OK;
  432. }
  433. /* L O A D F I L E D A T A */
  434. /*-------------------------------------------------------------------------
  435. %%Function: LoadFileData
  436. Load the call log data from the file
  437. -------------------------------------------------------------------------*/
  438. VOID CCallLog::LoadFileData(VOID)
  439. {
  440. HANDLE hFile;
  441. DWORD dwFileOffset;
  442. CCallLogEntry * pcle;
  443. hFile = OpenLogFile();
  444. if (NULL == hFile)
  445. return;
  446. m_cTotalEntries = 0;
  447. m_cDeletedEntries = 0;
  448. dwFileOffset = 0;
  449. while (E_FAIL != ReadEntry(hFile, &dwFileOffset, &pcle))
  450. {
  451. m_cTotalEntries++;
  452. if (NULL == pcle)
  453. {
  454. m_cDeletedEntries++;
  455. continue; // deleted record
  456. }
  457. Add(pcle);
  458. TRACE_OUT(("Read Entry: \"%s\" (%02d/%02d/%04d %02d:%02d:%02d) : %s",
  459. pcle->m_pszName,
  460. pcle->m_st.wMonth, pcle->m_st.wDay, pcle->m_st.wYear,
  461. pcle->m_st.wHour, pcle->m_st.wMinute, pcle->m_st.wSecond,
  462. (CLEF_ACCEPTED & pcle->m_dwFlags) ? "ACCEPTED" : "REJECTED"));
  463. }
  464. CloseHandle(hFile);
  465. m_fUseList = TRUE;
  466. m_fDataRead = TRUE;
  467. // Now trim the list down to our configured maximum if
  468. // the count exceeds our target. The file will be compacted
  469. // when we write it out if we have more than a few deleted
  470. // entries.
  471. for( int nn = 0, delCount = m_cTotalEntries - m_cDeletedEntries - m_cMaxEntries; nn < delCount; nn++ )
  472. {
  473. DeleteEntry((*this)[0]);
  474. RemoveAt( 0 );
  475. }
  476. }
  477. /* R E W R I T E F I L E */
  478. /*-------------------------------------------------------------------------
  479. %%Function: RewriteFile
  480. Re-write the log file from the in-memory list, compress deleted entries
  481. -------------------------------------------------------------------------*/
  482. VOID CCallLog::RewriteFile(VOID)
  483. {
  484. HANDLE hFile;
  485. TRACE_OUT(("Rewriting log file"));
  486. // Make sure we don't nuke the file without a list to write out
  487. ASSERT(m_fUseList);
  488. // Reset the file pointer and write the EOF marker
  489. if (!m_strFile.IsEmpty())
  490. {
  491. hFile = OpenLogFile();
  492. if (NULL != hFile)
  493. {
  494. if (INVALID_FILE_SIZE != SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
  495. {
  496. SetEndOfFile(hFile);
  497. m_cTotalEntries = 0;
  498. m_cDeletedEntries = 0;
  499. }
  500. CloseHandle(hFile);
  501. }
  502. }
  503. // Write out all non-deleted records
  504. for( int i = 0; i < GetSize(); ++i )
  505. {
  506. CCallLogEntry* pcle = (*this)[i];
  507. ASSERT(NULL != pcle);
  508. LOGHDR LogHdr;
  509. // Initialize LogHdr items from memory object
  510. LogHdr.dwCLEF = pcle->GetFlags();
  511. LogHdr.dwPF = 0;
  512. LogHdr.sysTime = *pcle->GetTime();
  513. // Write out entry
  514. WriteEntry( pcle->m_pszName,
  515. &LogHdr,
  516. pcle->m_pri,
  517. pcle->m_pbCert,
  518. pcle->m_cbCert);
  519. delete pcle;
  520. }
  521. }
  522. // Grumble... Inefficient
  523. HRESULT CCallLog::DeleteEntry(CCallLogEntry * pcle)
  524. {
  525. HRESULT hr;
  526. DWORD dwFlags;
  527. DWORD dwOffset;
  528. if (NULL == pcle)
  529. {
  530. WARNING_OUT(("DeleteEntry: Unable to find entry"));
  531. return E_FAIL;
  532. }
  533. // Calculate offset to "CLEF"
  534. dwOffset = pcle->GetFileOffset() + offsetof(LOGHDR, dwCLEF);
  535. dwFlags = pcle->GetFlags() | CLEF_DELETED;
  536. hr = WriteData(NULL, &dwOffset, &dwFlags, sizeof(DWORD));
  537. m_cDeletedEntries++;
  538. TRACE_OUT(("Marked [%s] pos=%08X for deletion", pcle->GetName(), pcle->GetFileOffset() ));
  539. delete pcle;
  540. return hr;
  541. }