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.

683 lines
22 KiB

  1. /*++
  2. Module Name:
  3. psslog.c
  4. Abstract:
  5. Implementation of the fax service provider PSS log.
  6. Author:
  7. Jonathan Barner (t-jonb) Feb, 2001
  8. Revision History:
  9. --*/
  10. #include "prep.h"
  11. #include "faxreg.h"
  12. #include "t30gl.h" // for T30CritSection
  13. #include <time.h>
  14. #include "psslog.h"
  15. /*++
  16. Routine Description:
  17. Prints the log file header. It assumes that the log file is already open. On any
  18. unrecoverable error, the function closes the log file and falls back to no logging.
  19. Arguments:
  20. pTG
  21. szDeviceName - pointer to device name to be included in the header
  22. Return Value:
  23. None
  24. --*/
  25. #define LOG_HEADER_LINE_LEN 256
  26. #define LOG_MAX_DATE 256
  27. void PrintPSSLogHeader(PThrdGlbl pTG, LPSTR szDeviceName)
  28. {
  29. TCHAR szHeader[LOG_HEADER_LINE_LEN*6] = {'\0'}; // Room for 6 lines
  30. TCHAR szDate[LOG_MAX_DATE] = {'\0'};
  31. int iDateRet = 0;
  32. LPSTR lpszUnimodemKeyEnd = NULL;
  33. DWORD dwCharIndex = 0;
  34. DWORD dwNumCharsWritten = 0;
  35. TCHAR szTimeBuff[10] = {'\0'}; // _tstrtime requires 9 chars
  36. BOOL fRet = FALSE;
  37. DEBUG_FUNCTION_NAME(_T("PrintPSSLogHeader"));
  38. iDateRet = GetY2KCompliantDate (LOCALE_USER_DEFAULT,
  39. 0,
  40. NULL,
  41. szDate,
  42. LOG_MAX_DATE);
  43. if (0 == iDateRet)
  44. {
  45. DebugPrintEx(DEBUG_ERR, "GetY2KCompliantDate failed, LastError = %d, date will not be logged", GetLastError());
  46. szDate[0] = '\0'; // We continue with a blank date
  47. }
  48. dwCharIndex += _stprintf(&szHeader[dwCharIndex],
  49. TEXT("[%s %-8s]\r\n"), szDate, _tstrtime(szTimeBuff));
  50. dwCharIndex += _stprintf(&szHeader[dwCharIndex],
  51. TEXT("Device name: %s\r\n"), szDeviceName);
  52. dwCharIndex += _stprintf(&szHeader[dwCharIndex],
  53. TEXT("Permanent TAPI line ID: %8X\r\n"), pTG->dwPermanentLineID);
  54. lpszUnimodemKeyEnd = _tcsrchr(pTG->lpszUnimodemKey, _TEXT('\\'));
  55. if (NULL == lpszUnimodemKeyEnd)
  56. {
  57. lpszUnimodemKeyEnd = _TEXT("");
  58. }
  59. else
  60. {
  61. lpszUnimodemKeyEnd++; // skip the actual '\'
  62. }
  63. dwCharIndex += _stprintf(&szHeader[dwCharIndex],
  64. TEXT("Unimodem registry key: %s\r\n"), lpszUnimodemKeyEnd);
  65. dwCharIndex += _stprintf(&szHeader[dwCharIndex],
  66. TEXT("Job type: %s\r\n"), (pTG->Operation==T30_TX) ? TEXT("Send") : TEXT("Receive") );
  67. // blank line after header
  68. dwCharIndex += _stprintf(&szHeader[dwCharIndex],
  69. TEXT("\r\n"));
  70. fRet = WriteFile(pTG->hPSSLogFile,
  71. szHeader,
  72. dwCharIndex * sizeof(szHeader[0]),
  73. &dwNumCharsWritten,
  74. NULL);
  75. if (FALSE == fRet)
  76. {
  77. DebugPrintEx(DEBUG_WRN,"Can't write log header, LastError = %d", GetLastError());
  78. }
  79. else
  80. {
  81. pTG->dwCurrentFileSize += dwNumCharsWritten;
  82. // Not checking for size here - I assume that MaxLogFileSize is enough for the header!
  83. }
  84. }
  85. /*++
  86. Routine Description:
  87. Reads from the registry whether logging should be enabled. If it should, determines
  88. the logging folder, creates it if it doesn't exist, creates a temporary filename for
  89. the log file, and creates the file. On any unrecoverable error, falls back to no
  90. logging.
  91. Upon exit, if pTG->dwLoggingEnabled==0, there will be no logging.
  92. Arguments:
  93. pTG
  94. szDeviceName [in] - Pointer to device name to be included in the header
  95. note: this value is never saved in ThreadGlobal, which
  96. is why it is passed as a parameter.
  97. Return Value:
  98. None
  99. --*/
  100. void OpenPSSLogFile(PThrdGlbl pTG, LPSTR szDeviceName)
  101. {
  102. HKEY hKey = NULL;
  103. BOOL fEnteredCriticalSection = FALSE;
  104. LPTSTR lpszLoggingFolder = NULL; // Logging folder, read from the registry and expanded
  105. // Another thread can call PSSLogEntry while this function is running
  106. // So, use a private flag for LoggingEnabled, and set pTG->dwLoggingEnabled
  107. // Only after pTG->hPSSLogFile is valid.
  108. DWORD dwLoggingEnabled = PSSLOG_NONE;
  109. DEBUG_FUNCTION_NAME(_T("OpenPSSLogFile"));
  110. _ASSERT(NULL==pTG->hPSSLogFile && PSSLOG_NONE==pTG->LoggingEnabled);
  111. hKey=OpenRegistryKey(HKEY_LOCAL_MACHINE,
  112. REGKEY_DEVICE_PROVIDER_KEY TEXT("\\") REGVAL_T30_PROVIDER_GUID_STRING,
  113. FALSE,
  114. KEY_READ | KEY_WRITE);
  115. if (!hKey)
  116. {
  117. DebugPrintEx(DEBUG_ERR, "OpenRegistryKey failed, ec=%d", GetLastError());
  118. goto exit;
  119. }
  120. // Using T30CritSection to ensure atomic read and write of the registry
  121. // also for creating the logging folder and choosing a temp filename
  122. EnterCriticalSection(&T30CritSection);
  123. fEnteredCriticalSection = TRUE;
  124. if (!GetRegistryDwordDefault(hKey, REGVAL_LOGGINGENABLED, &dwLoggingEnabled, PSSLOG_FAILED_ONLY))
  125. {
  126. DebugPrintEx(DEBUG_ERR, "GetRegistryDwordDefault failed, LastError = %d", GetLastError());
  127. dwLoggingEnabled = PSSLOG_NONE;
  128. goto exit;
  129. }
  130. if (dwLoggingEnabled != PSSLOG_NONE)
  131. {
  132. if (!GetRegistryDwordDefault(hKey, REGVAL_MAXLOGFILESIZE, &(pTG->dwMaxLogFileSize), DEFAULT_MAXLOGFILESIZE))
  133. {
  134. DebugPrintEx(DEBUG_ERR, "GetRegistryDwordDefault failed, LastError = %d", GetLastError());
  135. dwLoggingEnabled = PSSLOG_NONE;
  136. goto exit;
  137. }
  138. lpszLoggingFolder = GetRegistryStringExpand(hKey, (pTG->Operation==T30_TX) ? REGVAL_LOGGINGFOLDER_OUTGOING : REGVAL_LOGGINGFOLDER_INCOMING, NULL);
  139. if (!lpszLoggingFolder)
  140. {
  141. DebugPrintEx(DEBUG_ERR, "GetRegistryStringExpand failed");
  142. dwLoggingEnabled = PSSLOG_NONE;
  143. goto exit;
  144. }
  145. // Generate a temporary filename for the log file
  146. if (!GetTempFileName(lpszLoggingFolder, TEXT("FSP"), 0, pTG->szLogFileName))
  147. {
  148. DebugPrintEx(DEBUG_ERR, "GetTempFileName(%s) failed, ec=%d", lpszLoggingFolder, GetLastError());
  149. dwLoggingEnabled = PSSLOG_NONE;
  150. goto exit;
  151. }
  152. DebugPrintEx(DEBUG_MSG, "Creating log file %s, MaxLogFileSize=%d", pTG->szLogFileName, pTG->dwMaxLogFileSize);
  153. // Create the file
  154. pTG->hPSSLogFile = CreateFile(pTG->szLogFileName,
  155. GENERIC_WRITE,
  156. FILE_SHARE_READ,
  157. NULL,
  158. CREATE_ALWAYS,
  159. FILE_ATTRIBUTE_NORMAL,
  160. NULL);
  161. if (INVALID_HANDLE_VALUE == pTG->hPSSLogFile)
  162. {
  163. DebugPrintEx(DEBUG_ERR, "Can't create log file, LastError = %d", GetLastError());
  164. dwLoggingEnabled = PSSLOG_NONE;
  165. pTG->hPSSLogFile = NULL;
  166. goto exit;
  167. }
  168. pTG->dwLoggingEnabled = dwLoggingEnabled;
  169. pTG->dwCurrentFileSize = 0;
  170. }
  171. exit:
  172. if (fEnteredCriticalSection)
  173. {
  174. LeaveCriticalSection(&T30CritSection);
  175. fEnteredCriticalSection = FALSE;
  176. }
  177. if (lpszLoggingFolder)
  178. {
  179. MemFree(lpszLoggingFolder);
  180. lpszLoggingFolder = NULL;
  181. }
  182. if (NULL != hKey)
  183. {
  184. RegCloseKey(hKey);
  185. hKey = NULL;
  186. }
  187. if (PSSLOG_NONE != pTG->dwLoggingEnabled)
  188. {
  189. PrintPSSLogHeader(pTG, szDeviceName);
  190. }
  191. }
  192. /*++
  193. Routine Description:
  194. Closes the log file. If the file needs to be kept, reads LogFileNumber, advances
  195. it, generates a permanent name, and renames the temp log file to the permanent name.
  196. Arguments:
  197. pTG
  198. RetCode [in] - Whether the call succeeded or failed. This is used when determining
  199. whether to keep the log or delete it.
  200. Return Value:
  201. None
  202. --*/
  203. void ClosePSSLogFile(PThrdGlbl pTG, BOOL RetCode)
  204. {
  205. LONG lError=0;
  206. HKEY hKey = NULL;
  207. LPCSTR lpszRegValLogFileNumber = NULL;
  208. DWORD dwLoggingEnabled = pTG->dwLoggingEnabled;
  209. DWORD dwMaxLogFileCount=0;
  210. DWORD dwLogFileNumber=0;
  211. DWORD dwNextLogFileNumber=0;
  212. TCHAR szFinalLogFileName[MAX_PATH]={TEXT('\0')};
  213. BOOL fKeepFile = FALSE;
  214. DEBUG_FUNCTION_NAME(_T("ClosePSSLogFile"));
  215. if (PSSLOG_NONE == pTG->dwLoggingEnabled)
  216. {
  217. // There was no log - nothing to do!
  218. return;
  219. }
  220. pTG->dwLoggingEnabled = PSSLOG_NONE;
  221. CloseHandle(pTG->hPSSLogFile);
  222. pTG->hPSSLogFile = NULL;
  223. switch (dwLoggingEnabled)
  224. {
  225. case PSSLOG_ALL: DebugPrintEx(DEBUG_MSG, "LoggingEnabled=1, keeping file");
  226. fKeepFile = TRUE;
  227. break;
  228. case PSSLOG_FAILED_ONLY:
  229. default:
  230. fKeepFile = ((!RetCode) && (pTG->fReceivedHDLCflags));
  231. DebugPrintEx(DEBUG_MSG, "LoggingEnabled=2, RetCode=%d, fReceivedHDLCflags=%d, %s file",
  232. RetCode, pTG->fReceivedHDLCflags, fKeepFile ? "keeping" : "deleting");
  233. break;
  234. }
  235. if (fKeepFile)
  236. {
  237. hKey=OpenRegistryKey(HKEY_LOCAL_MACHINE,
  238. REGKEY_DEVICE_PROVIDER_KEY TEXT("\\") REGVAL_T30_PROVIDER_GUID_STRING,
  239. FALSE,
  240. 0);
  241. if (!hKey)
  242. {
  243. DebugPrintEx(DEBUG_ERR, "OpenRegistryKey failed, ec=%d", GetLastError());
  244. goto exit;
  245. }
  246. // Using T30CritSection to ensure atomic read and write of the registry
  247. EnterCriticalSection(&T30CritSection);
  248. // Read MaxLogFileCount, with SKU-dependant default
  249. if (!GetRegistryDwordDefault(hKey, REGVAL_MAXLOGFILECOUNT, &dwMaxLogFileCount,
  250. IsDesktopSKU() ? DEFAULT_MAXLOGFILECOUNT_CLIENT : DEFAULT_MAXLOGFILECOUNT_SERVER))
  251. {
  252. DebugPrintEx(DEBUG_WRN, "GetRegistryDwordDefault(%s) failed, ec=%d",
  253. REGVAL_MAXLOGFILECOUNT, GetLastError());
  254. LeaveCriticalSection(&T30CritSection);
  255. goto exit;
  256. }
  257. // Decide whether to use LogFileNumberOutgoing or LogFileNumberIncoming
  258. lpszRegValLogFileNumber = (pTG->Operation==T30_TX) ? REGVAL_LOGFILENUMBEROUTGOING : REGVAL_LOGFILENUMBERINCOMING;
  259. if (!GetRegistryDwordDefault(hKey, lpszRegValLogFileNumber, &dwLogFileNumber, 0))
  260. {
  261. DebugPrintEx(DEBUG_WRN, "GetRegistryDwordDefault(%s) failed, ec=%d",
  262. lpszRegValLogFileNumber, GetLastError());
  263. LeaveCriticalSection(&T30CritSection);
  264. goto exit;
  265. }
  266. // Advance LogFileNumber to the next number, with rollover
  267. dwNextLogFileNumber = dwLogFileNumber+1;
  268. if (dwNextLogFileNumber >= dwMaxLogFileCount)
  269. {
  270. dwNextLogFileNumber = 0;
  271. }
  272. lError = RegSetValueEx(hKey,
  273. lpszRegValLogFileNumber,
  274. 0,
  275. REG_DWORD,
  276. (LPBYTE)&dwNextLogFileNumber,
  277. sizeof(DWORD));
  278. if (lError != ERROR_SUCCESS)
  279. {
  280. DebugPrintEx(DEBUG_WRN, "Failed to set registry value %s, lError=%d",
  281. lpszRegValLogFileNumber, lError);
  282. LeaveCriticalSection(&T30CritSection);
  283. goto exit;
  284. }
  285. LeaveCriticalSection(&T30CritSection);
  286. // Create final filename
  287. {
  288. TCHAR drive[_MAX_DRIVE];
  289. TCHAR dir[_MAX_DIR];
  290. TCHAR fname[_MAX_FNAME] = {0};
  291. TCHAR ext[_MAX_EXT];
  292. _splitpath(pTG->szLogFileName, drive, dir, fname, ext);
  293. _sntprintf(fname,
  294. ARR_SIZE(fname)-1,
  295. TEXT("FSP%c%04d"),
  296. (pTG->Operation==T30_TX) ? TEXT('S') : TEXT('R'),
  297. dwLogFileNumber);
  298. _makepath(szFinalLogFileName, drive, dir, fname, TEXT("log"));
  299. }
  300. DebugPrintEx(DEBUG_MSG, "Final log filename is %s", szFinalLogFileName);
  301. if (!MoveFileEx(pTG->szLogFileName, szFinalLogFileName, MOVEFILE_REPLACE_EXISTING))
  302. {
  303. DebugPrintEx(DEBUG_WRN, "MoveFileEx failed, ec=%d", GetLastError());
  304. }
  305. }
  306. else
  307. {
  308. if (!DeleteFile(pTG->szLogFileName))
  309. {
  310. DebugPrintEx(DEBUG_WRN, "Failed to delete %s, le=%d", pTG->szLogFileName, GetLastError());
  311. }
  312. }
  313. exit:
  314. if (NULL != hKey)
  315. {
  316. RegCloseKey(hKey);
  317. hKey = NULL;
  318. }
  319. }
  320. /*++
  321. Routine Description:
  322. Delete all '\n' and '\r' from a string.
  323. Arguments:
  324. szDest - [out] pointer to output string
  325. szSource - [in] pointer to input string
  326. Return Value:
  327. Length of new string, in TCHARs
  328. Note: szDest and szSource can be the same string.
  329. --*/
  330. int CopyStrNoNewLines(LPSTR szDest, LPCSTR szSource)
  331. {
  332. int iCharRead = 0, iCharWrite = 0;
  333. while (szSource[iCharRead] != '\0')
  334. {
  335. if (szSource[iCharRead]!='\n' && szSource[iCharRead]!='\r')
  336. {
  337. szDest[iCharWrite] = szSource[iCharRead];
  338. iCharWrite++;
  339. }
  340. iCharRead++;
  341. }
  342. szDest[iCharWrite] = '\0';
  343. return iCharWrite;
  344. }
  345. #define MAX_LOG_FILE_SIZE_MESSAGE TEXT("-------- Maximum log file size reached --------\r\n")
  346. #define MAX_MESSAGE_LEN 2048
  347. #define INDENT_LEN 12
  348. #define HEADER_LEN 38
  349. /*++
  350. Routine Description:
  351. Add an entry to the log, if logging is enabled. If disabled, do nothing.
  352. Arguments:
  353. pTG
  354. nMessageType - message type (PSS_MSG_ERR, PSS_MSG_WRN, PSS_MSG_MSG)
  355. dwFileID - File ID, as set in the beginning of every file by #define FILE_ID
  356. dwLine - Line number
  357. dwIndent - Indent level in "tabs" (0 - leftmost)
  358. pcszFormat - Message text, with any format specifiers
  359. ... - Arguments for message format
  360. Return Value:
  361. None
  362. --*/
  363. void PSSLogEntry(
  364. PThrdGlbl pTG,
  365. PSS_MESSAGE_TYPE const nMessageType,
  366. DWORD const dwFileID,
  367. DWORD const dwLine,
  368. DWORD dwIndent,
  369. LPCTSTR pcszFormat,
  370. ... )
  371. {
  372. TCHAR pcszMessage[MAX_MESSAGE_LEN] = {'\0'};
  373. va_list arg_ptr = NULL;
  374. LPTSTR lptstrMsgPrefix = NULL;
  375. TCHAR szTimeBuff[10] = {'\0'};
  376. int iHeaderIndex = 0;
  377. int iMessageIndex = 0;
  378. DWORD dwBytesWritten = 0;
  379. BOOL fRet = FALSE;
  380. // DEBUG_FUNCTION_NAME(_T("PSSLogEntry"));
  381. // hack: Want only one line in T30DebugLogFile
  382. #ifdef ENABLE_LOGGING
  383. LPCTSTR faxDbgFunction=_T("PSSLogEntry");
  384. #endif // ENABLE_LOGGING
  385. switch (nMessageType)
  386. {
  387. case PSS_MSG_MSG:
  388. lptstrMsgPrefix=TEXT(" ");
  389. break;
  390. case PSS_WRN_MSG:
  391. lptstrMsgPrefix=TEXT("WRN");
  392. break;
  393. case PSS_ERR_MSG:
  394. lptstrMsgPrefix=TEXT("ERR");
  395. break;
  396. default:
  397. _ASSERT(FALSE);
  398. lptstrMsgPrefix=TEXT(" ");
  399. break;
  400. }
  401. iHeaderIndex = _sntprintf(pcszMessage,
  402. MAX_MESSAGE_LEN-1,
  403. TEXT("[%-8s][%09d][%4d%04d][%3s] %*c"),
  404. _tstrtime(szTimeBuff),
  405. GetTickCount(),
  406. dwFileID,
  407. dwLine % 10000,
  408. lptstrMsgPrefix,
  409. dwIndent * INDENT_LEN,
  410. TEXT(' '));
  411. if (iHeaderIndex<0)
  412. {
  413. DebugPrintEx(DEBUG_ERR, "Message header too long, it will not be logged");
  414. return;
  415. }
  416. // Now comes the actual message
  417. va_start(arg_ptr, pcszFormat);
  418. iMessageIndex = _vsntprintf(
  419. &pcszMessage[iHeaderIndex],
  420. MAX_MESSAGE_LEN - (iHeaderIndex + 3), // +3 - room for "\r\n\0"
  421. pcszFormat,
  422. arg_ptr);
  423. if (iMessageIndex<0)
  424. {
  425. DebugPrintEx(DEBUG_ERR, "Message too long, it will not be logged");
  426. return;
  427. }
  428. // Kill all newline chars
  429. iMessageIndex = CopyStrNoNewLines(&pcszMessage[iHeaderIndex], &pcszMessage[iHeaderIndex]);
  430. DebugPrintEx(DEBUG_MSG, "PSSLog: %s", &pcszMessage[iHeaderIndex]);
  431. if (PSSLOG_NONE == pTG->dwLoggingEnabled)
  432. {
  433. return;
  434. }
  435. // End of line
  436. iMessageIndex += iHeaderIndex;
  437. iMessageIndex += _stprintf( &pcszMessage[iMessageIndex],TEXT("\r\n"));
  438. pTG->dwCurrentFileSize += iMessageIndex * sizeof(pcszMessage[0]);
  439. // Check if not exceeding MaxLogFileSize
  440. if ((pTG->dwMaxLogFileSize!=0) && (pTG->dwCurrentFileSize > pTG->dwMaxLogFileSize))
  441. {
  442. fRet = WriteFile(pTG->hPSSLogFile,
  443. MAX_LOG_FILE_SIZE_MESSAGE,
  444. _tcslen(MAX_LOG_FILE_SIZE_MESSAGE) * sizeof(TCHAR),
  445. &dwBytesWritten,
  446. NULL);
  447. if (FALSE == fRet)
  448. {
  449. DebugPrintEx(DEBUG_ERR, "Writefile failed, LE=%d", GetLastError());
  450. }
  451. ClosePSSLogFile(pTG, FALSE); // Always want to keep oversized logs
  452. }
  453. else
  454. {
  455. fRet = WriteFile(pTG->hPSSLogFile,
  456. pcszMessage,
  457. iMessageIndex * sizeof(pcszMessage[0]),
  458. &dwBytesWritten,
  459. NULL);
  460. if (FALSE == fRet)
  461. {
  462. DebugPrintEx(DEBUG_ERR, "Writefile failed, LE=%d", GetLastError());
  463. }
  464. }
  465. }
  466. /*++
  467. Routine Description:
  468. Add an entry to the log, including a binary dump.
  469. Arguments:
  470. pTG
  471. nMessageType - message type (PSS_MSG_ERR, PSS_MSG_WRN, PSS_MSG_MSG)
  472. dwFileID - File ID, as set in the beginning of every file by #define FILE_ID
  473. dwLine - Line number
  474. pcszFormat - Message text, no format specifiers allowed
  475. lpb - byte buffer to dump
  476. dwSize - number of bytes to dump
  477. Return Value:
  478. None
  479. --*/
  480. void PSSLogEntryHex(
  481. PThrdGlbl pTG,
  482. PSS_MESSAGE_TYPE const nMessageType,
  483. DWORD const dwFileID,
  484. DWORD const dwLine,
  485. DWORD dwIndent,
  486. LPB const lpb,
  487. DWORD const dwSize,
  488. LPCTSTR pcszFormat,
  489. ... )
  490. {
  491. TCHAR pcszMessage[MAX_MESSAGE_LEN] = {'\0'};
  492. va_list arg_ptr = NULL;
  493. DWORD dwByte = 0;
  494. int iMessageIndex = 0;
  495. // DEBUG_FUNCTION_NAME(_T("PSSLogEntryHex"));
  496. // hack: Want only one line in T30DebugLogFile
  497. #ifdef ENABLE_LOGGING
  498. LPCTSTR faxDbgFunction=_T("PSSLogEntryHex");
  499. #endif // ENABLE_LOGGING
  500. // Now comes the actual message
  501. va_start(arg_ptr, pcszFormat);
  502. iMessageIndex = _vsntprintf(
  503. pcszMessage,
  504. (sizeof(pcszMessage)/sizeof(pcszMessage[0]) -1),
  505. pcszFormat,
  506. arg_ptr);
  507. if (iMessageIndex<0)
  508. {
  509. DebugPrintEx(DEBUG_ERR, "Message too long, it will not be logged");
  510. return;
  511. }
  512. for (dwByte=0; dwByte<dwSize; dwByte++)
  513. {
  514. iMessageIndex += _stprintf( &pcszMessage[iMessageIndex],TEXT(" %02x"), lpb[dwByte]);
  515. // -4 - room for "...", -3 - room for "\r\n\0"
  516. if (iMessageIndex > (int)(MAX_MESSAGE_LEN - HEADER_LEN - dwIndent*INDENT_LEN - 4 - 3))
  517. {
  518. iMessageIndex += _stprintf( &pcszMessage[iMessageIndex],TEXT("..."));
  519. break;
  520. }
  521. }
  522. PSSLogEntry(pTG, nMessageType, dwFileID, dwLine, dwIndent, pcszMessage);
  523. }
  524. /*++
  525. Routine Description:
  526. Add an entry to the log, including a list of strings.
  527. Arguments:
  528. pTG
  529. nMessageType - message type (PSS_MSG_ERR, PSS_MSG_WRN, PSS_MSG_MSG)
  530. dwFileID - File ID, as set in the beginning of every file by #define FILE_ID
  531. dwLine - Line number
  532. pcszFormat - Message text, no format specifiers allowed
  533. lplpszStrings - pointer to array of strings to log
  534. dwStringNum - number of strings
  535. Return Value:
  536. None
  537. --*/
  538. void PSSLogEntryStrings(
  539. PThrdGlbl pTG,
  540. PSS_MESSAGE_TYPE const nMessageType,
  541. DWORD const dwFileID,
  542. DWORD const dwLine,
  543. DWORD dwIndent,
  544. LPCTSTR const *lplpszStrings,
  545. DWORD const dwStringNum,
  546. LPCTSTR pcszFormat,
  547. ... )
  548. {
  549. TCHAR pcszMessage[MAX_MESSAGE_LEN] = {'\0'};
  550. va_list arg_ptr = NULL;
  551. DWORD dwString = 0;
  552. int iMessageIndex = 0;
  553. int iResult = 0;
  554. // DEBUG_FUNCTION_NAME(_T("PSSLogEntryStrings"));
  555. // hack: Want only one line in T30DebugLogFile
  556. #ifdef ENABLE_LOGGING
  557. LPCTSTR faxDbgFunction=_T("PSSLogEntryStrings");
  558. #endif // ENABLE_LOGGING
  559. // Now comes the actual message
  560. va_start(arg_ptr, pcszFormat);
  561. iMessageIndex = _vsntprintf(
  562. pcszMessage,
  563. (sizeof(pcszMessage)/sizeof(pcszMessage[0]) -1),
  564. pcszFormat,
  565. arg_ptr);
  566. if (iMessageIndex<0)
  567. {
  568. DebugPrintEx(DEBUG_ERR, "Message too long, it will not be logged");
  569. return;
  570. }
  571. for (dwString=0; dwString<dwStringNum; dwString++)
  572. {
  573. iResult = _sntprintf( &pcszMessage[iMessageIndex],
  574. MAX_MESSAGE_LEN - iMessageIndex,
  575. (dwString==0) ? TEXT("%s") : TEXT(", %s"),
  576. lplpszStrings[dwString]);
  577. if (iResult < 0)
  578. {
  579. break;
  580. }
  581. iMessageIndex += iResult;
  582. }
  583. PSSLogEntry(pTG, nMessageType, dwFileID, dwLine, dwIndent, pcszMessage);
  584. }