mirror of
				https://github.com/simon987/antiword.git
				synced 2025-10-30 23:36:52 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			898 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			898 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * summary.c
 | |
|  * Copyright (C) 2002-2005 A.J. van Os; Released under GNU GPL
 | |
|  *
 | |
|  * Description:
 | |
|  * Read the summary information of a Word document
 | |
|  */
 | |
| 
 | |
| #include <time.h>
 | |
| #include <string.h>
 | |
| #include "antiword.h"
 | |
| 
 | |
| #define P_HEADER_SZ		28
 | |
| #define P_SECTIONLIST_SZ	20
 | |
| #define P_LENGTH_SZ		 4
 | |
| #define P_SECTION_MAX_SZ	(2 * P_SECTIONLIST_SZ + P_LENGTH_SZ)
 | |
| #define P_SECTION_SZ(x)		((x) * P_SECTIONLIST_SZ + P_LENGTH_SZ)
 | |
| 
 | |
| #define PID_TITLE		 2
 | |
| #define PID_SUBJECT		 3
 | |
| #define PID_AUTHOR		 4
 | |
| #define PID_CREATE_DTM		12
 | |
| #define PID_LASTSAVE_DTM	13
 | |
| #define PID_APPNAME		18
 | |
| 
 | |
| #define PIDD_MANAGER		14
 | |
| #define PIDD_COMPANY		15
 | |
| 
 | |
| #define VT_LPSTR		30
 | |
| #define VT_FILETIME		64
 | |
| 
 | |
| #define TIME_OFFSET_HI		0x019db1de
 | |
| #define TIME_OFFSET_LO		0xd53e8000
 | |
| 
 | |
| static __thread char	*szTitle = NULL;
 | |
| static __thread char	*szSubject = NULL;
 | |
| static __thread char	*szAuthor = NULL;
 | |
| static __thread time_t	tCreateDtm = (time_t)-1;
 | |
| static __thread time_t	tLastSaveDtm= (time_t)-1;
 | |
| static __thread char	*szAppName = NULL;
 | |
| static __thread char	*szManager = NULL;
 | |
| static __thread char	*szCompany = NULL;
 | |
| static __thread USHORT	usLid = (USHORT)-1;
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * vDestroySummaryInfo - destroy the summary information
 | |
|  */
 | |
| void
 | |
| vDestroySummaryInfo(void)
 | |
| {
 | |
| 	TRACE_MSG("vDestroySummaryInfo");
 | |
| 
 | |
| 	szTitle = xfree(szTitle);
 | |
| 	szSubject = xfree(szSubject);
 | |
| 	szAuthor = xfree(szAuthor);
 | |
| 	tCreateDtm = (time_t)-1;
 | |
| 	tLastSaveDtm = (time_t)-1;
 | |
| 	szAppName = xfree(szAppName);
 | |
| 	szManager = xfree(szManager);
 | |
| 	szCompany = xfree(szCompany);
 | |
| 	usLid = (USHORT)-1;
 | |
| } /* end of vDestroySummaryInfo */
 | |
| 
 | |
| /*
 | |
|  * tConvertDosDate - convert DOS date format
 | |
|  *
 | |
|  * returns Unix time_t or -1
 | |
|  */
 | |
| static time_t
 | |
| tConvertDosDate(const char *szDosDate)
 | |
| {
 | |
| 	struct tm	tTime;
 | |
| 	const char	*pcTmp;
 | |
| 	time_t		tResult;
 | |
| 
 | |
| 	memset(&tTime, 0, sizeof(tTime));
 | |
| 	pcTmp = szDosDate;
 | |
| 	/* Get the month */
 | |
| 	if (!isdigit(*pcTmp)) {
 | |
| 		return (time_t)-1;
 | |
| 	}
 | |
| 	tTime.tm_mon = (int)(*pcTmp - '0');
 | |
| 	pcTmp++;
 | |
| 	if (isdigit(*pcTmp)) {
 | |
| 		tTime.tm_mon *= 10;
 | |
| 		tTime.tm_mon += (int)(*pcTmp - '0');
 | |
| 		pcTmp++;
 | |
| 	}
 | |
| 	/* Get the first separater */
 | |
| 	if (isalnum(*pcTmp)) {
 | |
| 		return (time_t)-1;
 | |
| 	}
 | |
| 	pcTmp++;
 | |
| 	/* Get the day */
 | |
| 	if (!isdigit(*pcTmp)) {
 | |
| 		return (time_t)-1;
 | |
| 	}
 | |
| 	tTime.tm_mday = (int)(*pcTmp - '0');
 | |
| 	pcTmp++;
 | |
| 	if (isdigit(*pcTmp)) {
 | |
| 		tTime.tm_mday *= 10;
 | |
| 		tTime.tm_mday += (int)(*pcTmp - '0');
 | |
| 		pcTmp++;
 | |
| 	}
 | |
| 	/* Get the second separater */
 | |
| 	if (isalnum(*pcTmp)) {
 | |
| 		return (time_t)-1;
 | |
| 	}
 | |
| 	pcTmp++;
 | |
| 	/* Get the year */
 | |
| 	if (!isdigit(*pcTmp)) {
 | |
| 		return (time_t)-1;
 | |
| 	}
 | |
| 	tTime.tm_year = (int)(*pcTmp - '0');
 | |
| 	pcTmp++;
 | |
| 	if (isdigit(*pcTmp)) {
 | |
| 		tTime.tm_year *= 10;
 | |
| 		tTime.tm_year += (int)(*pcTmp - '0');
 | |
| 		pcTmp++;
 | |
| 	}
 | |
| 	/* Check the values */
 | |
| 	if (tTime.tm_mon == 0 || tTime.tm_mday == 0 || tTime.tm_mday > 31) {
 | |
| 		return (time_t)-1;
 | |
| 	}
 | |
| 	/* Correct the values */
 | |
| 	tTime.tm_mon--;		/* From 01-12 to 00-11 */
 | |
| 	if (tTime.tm_year < 80) {
 | |
| 		tTime.tm_year += 100;	/* 00 means 2000 is 100 */
 | |
| 	}
 | |
| 	tTime.tm_isdst = -1;
 | |
| 	tResult = mktime(&tTime);
 | |
| 	NO_DBG_MSG(ctime(&tResult));
 | |
| 	return tResult;
 | |
| } /* end of tConvertDosDate */
 | |
| 
 | |
| /*
 | |
|  * szLpstr - get a zero terminate string property
 | |
|  */
 | |
| static char *
 | |
| szLpstr(ULONG ulOffset, const UCHAR *aucBuffer)
 | |
| {
 | |
| 	char	*szStart, *szResult, *szTmp;
 | |
| 	size_t	tSize;
 | |
| 
 | |
| 	tSize = (size_t)ulGetLong(ulOffset + 4, aucBuffer);
 | |
| 	NO_DBG_DEC(tSize);
 | |
| 	if (tSize == 0) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	/* Remove white space from the start of the string */
 | |
| 	szStart = (char *)aucBuffer + ulOffset + 8;
 | |
| 	NO_DBG_MSG(szStart);
 | |
| 	fail(strlen(szStart) >= tSize);
 | |
| 	while (isspace(*szStart)) {
 | |
| 		szStart++;
 | |
| 	}
 | |
| 	if (szStart[0] == '\0') {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	szResult = xstrdup(szStart);
 | |
| 	/* Remove white space from the end of the string */
 | |
| 	szTmp = szResult + strlen(szResult) - 1;
 | |
| 	while (isspace(*szTmp)) {
 | |
| 		*szTmp = '\0';
 | |
| 		szTmp--;
 | |
| 	}
 | |
| 	NO_DBG_MSG(szResult);
 | |
| 	return szResult;
 | |
| } /* end of szLpstr */
 | |
| 
 | |
| /*
 | |
|  * tFiletime - get a filetime property
 | |
|  */
 | |
| static time_t
 | |
| tFiletime(ULONG ulOffset, const UCHAR *aucBuffer)
 | |
| {
 | |
| 	double	dHi, dLo, dTmp;
 | |
| 	ULONG	ulHi, ulLo;
 | |
| 	time_t	tResult;
 | |
| 
 | |
| 	ulLo = ulGetLong(ulOffset + 4, aucBuffer);
 | |
| 	ulHi = ulGetLong(ulOffset + 8, aucBuffer);
 | |
| 	NO_DBG_HEX(ulHi);
 | |
| 	NO_DBG_HEX(ulLo);
 | |
| 
 | |
| 	/* Move the starting point from 01 Jan 1601 to 01 Jan 1970 */
 | |
| 	dHi = (double)ulHi - (double)TIME_OFFSET_HI;
 | |
| 	dLo = (double)ulLo - (double)TIME_OFFSET_LO;
 | |
| 	NO_DBG_FLT(dHi);
 | |
| 	NO_DBG_FLT(dLo);
 | |
| 
 | |
| 	/* Combine the values and divide by 10^7 to get seconds */
 | |
| 	dTmp  = dLo / 10000000.0;	/* 10^7 */
 | |
| 	dTmp += dHi * 429.4967926;	/* 2^32 / 10^7 */
 | |
| 	NO_DBG_FLT(dTmp);
 | |
| 
 | |
| 	/* Make a time_t */
 | |
| 	if (dTmp - 0.5 < TIME_T_MIN || dTmp + 0.5 > TIME_T_MAX) {
 | |
| 		return (time_t)-1;
 | |
| 	}
 | |
| 	tResult = dTmp < 0.0 ? (time_t)(dTmp - 0.5) : (time_t)(dTmp + 0.5);
 | |
| 	NO_DBG_MSG(ctime(&tResult));
 | |
| 	return tResult;
 | |
| } /* end of tFiletime */
 | |
| 
 | |
| /*
 | |
|  * vAnalyseSummaryInfo - analyse the summary information
 | |
|  */
 | |
| static BOOL
 | |
| vAnalyseSummaryInfo(const UCHAR *aucBuffer)
 | |
| {
 | |
| 	ULONG	ulOffset;
 | |
| 	size_t	tIndex, tCount, tPropID, tPropType;
 | |
| 
 | |
| 	tCount = (size_t)ulGetLong(4, aucBuffer);
 | |
| 	DBG_DEC(tCount);
 | |
| 	for (tIndex = 0; tIndex < tCount; tIndex++) {
 | |
| 		tPropID = (size_t)ulGetLong(8 + tIndex * 8, aucBuffer);
 | |
| 		ulOffset = ulGetLong(12 + tIndex * 8, aucBuffer);
 | |
| 		NO_DBG_DEC(tPropID);
 | |
| 		NO_DBG_HEX(ulOffset);
 | |
| 		if (isOutOfBounds(ulOffset)) {
 | |
| 		    return FALSE;
 | |
| 		}
 | |
| 		tPropType = (size_t)ulGetLong(ulOffset, aucBuffer);
 | |
| 		NO_DBG_DEC(tPropType);
 | |
| 		switch (tPropID) {
 | |
| 		case PID_TITLE:
 | |
| 			if (tPropType == VT_LPSTR && szTitle == NULL) {
 | |
| 				szTitle = szLpstr(ulOffset, aucBuffer);
 | |
| 			}
 | |
| 			break;
 | |
| 		case PID_SUBJECT:
 | |
| 			if (tPropType == VT_LPSTR && szSubject == NULL) {
 | |
| 				szSubject = szLpstr(ulOffset, aucBuffer);
 | |
| 			}
 | |
| 			break;
 | |
| 		case PID_AUTHOR:
 | |
| 			if (tPropType == VT_LPSTR && szAuthor == NULL) {
 | |
| 				szAuthor = szLpstr(ulOffset, aucBuffer);
 | |
| 			}
 | |
| 			break;
 | |
| 		case PID_CREATE_DTM:
 | |
| 			if (tPropType == VT_FILETIME &&
 | |
| 			    tCreateDtm == (time_t)-1) {
 | |
| 				tCreateDtm = tFiletime(ulOffset, aucBuffer);
 | |
| 			}
 | |
| 			break;
 | |
| 		case PID_LASTSAVE_DTM:
 | |
| 			if (tPropType == VT_FILETIME &&
 | |
| 			    tLastSaveDtm == (time_t)-1) {
 | |
| 				tLastSaveDtm = tFiletime(ulOffset, aucBuffer);
 | |
| 			}
 | |
| 			break;
 | |
| 		case PID_APPNAME:
 | |
| 			if (tPropType == VT_LPSTR && szAppName == NULL) {
 | |
| 				szAppName = szLpstr(ulOffset, aucBuffer);
 | |
| 			}
 | |
| 			break;
 | |
| 		default:
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| } /* end of vAnalyseSummaryInfo */
 | |
| 
 | |
| /*
 | |
|  * vAnalyseDocumentSummaryInfo - analyse the document summary information
 | |
|  */
 | |
| static void
 | |
| vAnalyseDocumentSummaryInfo(const UCHAR *aucBuffer)
 | |
| {
 | |
| 	ULONG	ulOffset;
 | |
| 	size_t	tIndex, tCount, tPropID, tPropType;
 | |
| 
 | |
| 	tCount = (size_t)ulGetLong(4, aucBuffer);
 | |
| 	DBG_DEC(tCount);
 | |
| 	for (tIndex = 0; tIndex < tCount; tIndex++) {
 | |
| 		tPropID = (size_t)ulGetLong(8 + tIndex * 8, aucBuffer);
 | |
| 		ulOffset = ulGetLong(12 + tIndex * 8, aucBuffer);
 | |
| 		NO_DBG_DEC(tPropID);
 | |
| 		NO_DBG_HEX(ulOffset);
 | |
| 		if (isOutOfBounds(ulOffset)) {
 | |
| 		    return;
 | |
| 		}
 | |
| 		tPropType = (size_t)ulGetLong(ulOffset, aucBuffer);
 | |
| 		NO_DBG_DEC(tPropType);
 | |
| 		switch (tPropID) {
 | |
| 		case PIDD_MANAGER:
 | |
| 			if (tPropType == VT_LPSTR && szManager == NULL) {
 | |
| 				szManager = szLpstr(ulOffset, aucBuffer);
 | |
| 			}
 | |
| 			break;
 | |
| 		case PIDD_COMPANY:
 | |
| 			if (tPropType == VT_LPSTR && szCompany == NULL) {
 | |
| 				szCompany = szLpstr(ulOffset, aucBuffer);
 | |
| 			}
 | |
| 			break;
 | |
| 		default:
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| } /* end of vAnalyseDocumentSummaryInfo */
 | |
| 
 | |
| /*
 | |
|  * pucAnalyseSummaryInfoHeader-
 | |
|  */
 | |
| static UCHAR *
 | |
| pucAnalyseSummaryInfoHeader(FILE *pFile,
 | |
| 	ULONG ulStartBlock, ULONG ulSize,
 | |
| 	const ULONG *aulBBD, size_t tBBDLen,
 | |
| 	const ULONG *aulSBD, size_t tSBDLen)
 | |
| {
 | |
| 	const ULONG	*aulBlockDepot;
 | |
| 	UCHAR	*aucBuffer;
 | |
| 	size_t	tBlockDepotLen, tBlockSize, tSectionCount, tLength;
 | |
| 	ULONG	ulTmp, ulOffset;
 | |
| 	USHORT	usLittleEndian, usEmpty, usOS, usVersion;
 | |
| 	UCHAR	aucHdr[P_HEADER_SZ], aucSecLst[P_SECTION_MAX_SZ];
 | |
| 
 | |
| 	if (ulSize < MIN_SIZE_FOR_BBD_USE) {
 | |
| 		/* Use the Small Block Depot */
 | |
| 		aulBlockDepot = aulSBD;
 | |
| 		tBlockDepotLen = tSBDLen;
 | |
| 		tBlockSize = SMALL_BLOCK_SIZE;
 | |
| 	} else {
 | |
| 		/* Use the Big Block Depot */
 | |
| 		aulBlockDepot = aulBBD;
 | |
| 		tBlockDepotLen = tBBDLen;
 | |
| 		tBlockSize = BIG_BLOCK_SIZE;
 | |
| 	}
 | |
| 
 | |
| 	if (tBlockDepotLen == 0) {
 | |
| 		DBG_MSG("The Block Depot length is zero");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	/* Read the Summery Information header */
 | |
| 	if (!bReadBuffer(pFile, ulStartBlock,
 | |
| 			aulBlockDepot, tBlockDepotLen, tBlockSize,
 | |
| 			aucHdr, 0, P_HEADER_SZ)) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	NO_DBG_PRINT_BLOCK(aucHdr, P_HEADER_SZ);
 | |
| 
 | |
| 	/* Analyse the Summery Information header */
 | |
| 	usLittleEndian =  usGetWord(0, aucHdr);
 | |
| 	if (usLittleEndian != 0xfffe) {
 | |
| 		DBG_HEX(usLittleEndian);
 | |
| 		DBG_MSG_C(usLittleEndian == 0xfeff, "Big endian");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	usEmpty =  usGetWord(2, aucHdr);
 | |
| 	if (usEmpty != 0x0000) {
 | |
| 		DBG_DEC(usEmpty);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	ulTmp = ulGetLong(4, aucHdr);
 | |
| 	DBG_HEX(ulTmp);
 | |
| 	usOS = (USHORT)(ulTmp >> 16);
 | |
| 	usVersion = (USHORT)(ulTmp & 0xffff);
 | |
| 	switch (usOS) {
 | |
| 	case 0:
 | |
| 		DBG_MSG("Win16");
 | |
| 		DBG_HEX(usVersion);
 | |
| 		break;
 | |
| 	case 1:
 | |
| 		DBG_MSG("MacOS");
 | |
| 		DBG_HEX(usVersion);
 | |
| 		break;
 | |
| 	case 2:
 | |
| 		DBG_MSG("Win32");
 | |
| 		DBG_HEX(usVersion);
 | |
| 		break;
 | |
| 	default:
 | |
| 		DBG_DEC(usOS);
 | |
| 		DBG_HEX(usVersion);
 | |
| 		break;
 | |
| 	}
 | |
| 	tSectionCount = (size_t)ulGetLong(24, aucHdr);
 | |
| 	DBG_DEC_C(tSectionCount != 1 && tSectionCount != 2, tSectionCount);
 | |
| 	if (tSectionCount != 1 && tSectionCount != 2) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	/* Read the Summery Information Section Lists */
 | |
| 	if (!bReadBuffer(pFile, ulStartBlock,
 | |
| 			aulBlockDepot, tBlockDepotLen, tBlockSize,
 | |
| 			aucSecLst, P_HEADER_SZ, P_SECTION_SZ(tSectionCount))) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	NO_DBG_PRINT_BLOCK(aucSecLst, P_SECTION_SZ(tSectionCount));
 | |
| 
 | |
| 	ulTmp = ulGetLong(0, aucSecLst);
 | |
| 	DBG_HEX(ulTmp);
 | |
| 	ulTmp = ulGetLong(4, aucSecLst);
 | |
| 	DBG_HEX(ulTmp);
 | |
| 	ulTmp = ulGetLong(8, aucSecLst);
 | |
| 	DBG_HEX(ulTmp);
 | |
| 	ulTmp = ulGetLong(12, aucSecLst);
 | |
| 	DBG_HEX(ulTmp);
 | |
| 	ulOffset = ulGetLong(16, aucSecLst);
 | |
| 	DBG_DEC_C(ulOffset != P_HEADER_SZ + P_SECTIONLIST_SZ &&
 | |
| 		ulOffset != P_HEADER_SZ + 2 * P_SECTIONLIST_SZ,
 | |
| 		ulOffset);
 | |
| 	fail(ulOffset != P_HEADER_SZ + P_SECTIONLIST_SZ &&
 | |
| 		ulOffset != P_HEADER_SZ + 2 * P_SECTIONLIST_SZ);
 | |
| 	tLength =
 | |
| 		(size_t)ulGetLong(tSectionCount * P_SECTIONLIST_SZ, aucSecLst);
 | |
| 	NO_DBG_HEX(tLength);
 | |
| 	fail(ulOffset + tLength > ulSize);
 | |
| 
 | |
| 	/* Read the Summery Information */
 | |
| 	aucBuffer = xmalloc(tLength);
 | |
| 	setBufferSize(tLength);
 | |
| 	if (!bReadBuffer(pFile, ulStartBlock,
 | |
| 			aulBlockDepot, tBlockDepotLen, tBlockSize,
 | |
| 			aucBuffer, ulOffset, tLength)) {
 | |
| 		aucBuffer = xfree(aucBuffer);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	NO_DBG_PRINT_BLOCK(aucBuffer, tLength);
 | |
| 	return aucBuffer;
 | |
| } /* end of pucAnalyseSummaryInfoHeader */
 | |
| 
 | |
| /*
 | |
|  * vSet0SummaryInfo - set summary information from a Word for DOS file
 | |
|  */
 | |
| void
 | |
| vSet0SummaryInfo(FILE *pFile, const UCHAR *aucHeader)
 | |
| {
 | |
| 	UCHAR	*aucBuffer;
 | |
| 	ULONG	ulBeginSumdInfo, ulBeginNextBlock;
 | |
| 	size_t	tLen;
 | |
| 	USHORT	usCodepage, usOffset;
 | |
| 
 | |
| 	TRACE_MSG("vSet0SummaryInfo");
 | |
| 
 | |
| 	fail(pFile == NULL || aucHeader == NULL);
 | |
| 
 | |
| 	/* First check the header */
 | |
| 	usCodepage = usGetWord(0x7e, aucHeader);
 | |
| 	DBG_DEC(usCodepage);
 | |
| 	switch (usCodepage) {
 | |
| 	case 850: usLid = 0x0809; break; /* Latin1 -> British English */
 | |
| 	case 862: usLid = 0x040d; break; /* Hebrew */
 | |
| 	case 866: usLid = 0x0419; break; /* Russian */
 | |
| 	case 0:
 | |
| 	case 437:
 | |
| 	default: usLid = 0x0409; break; /* ASCII -> American English */
 | |
| 	}
 | |
| 
 | |
| 	/* Second check the summary information block */
 | |
| 	ulBeginSumdInfo = 128 * (ULONG)usGetWord(0x1c, aucHeader);
 | |
| 	DBG_HEX(ulBeginSumdInfo);
 | |
| 	ulBeginNextBlock = 128 * (ULONG)usGetWord(0x6a, aucHeader);
 | |
| 	DBG_HEX(ulBeginNextBlock);
 | |
| 
 | |
| 	if (ulBeginSumdInfo >= ulBeginNextBlock || ulBeginNextBlock == 0) {
 | |
| 		/* There is no summary information block */
 | |
| 		return;
 | |
| 	}
 | |
| 	tLen = (size_t)(ulBeginNextBlock - ulBeginSumdInfo);
 | |
| 	aucBuffer = xmalloc(tLen);
 | |
| 	/* Read the summary information block */
 | |
| 	if (!bReadBytes(aucBuffer, tLen, ulBeginSumdInfo, pFile)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	usOffset = usGetWord(0, aucBuffer);
 | |
| 	if (aucBuffer[usOffset] != 0) {
 | |
| 		NO_DBG_MSG(aucBuffer + usOffset);
 | |
| 		szTitle = xstrdup((char *)aucBuffer + usOffset);
 | |
| 	}
 | |
| 	usOffset = usGetWord(2, aucBuffer);
 | |
| 	if (aucBuffer[usOffset] != 0) {
 | |
| 		NO_DBG_MSG(aucBuffer + usOffset);
 | |
| 		szAuthor = xstrdup((char *)aucBuffer + usOffset);
 | |
| 	}
 | |
| 	usOffset = usGetWord(12, aucBuffer);
 | |
| 	if (aucBuffer[usOffset] != 0) {
 | |
| 		NO_DBG_STRN(aucBuffer + usOffset, 8);
 | |
| 		tLastSaveDtm = tConvertDosDate((char *)aucBuffer + usOffset);
 | |
| 	}
 | |
| 	usOffset = usGetWord(14, aucBuffer);
 | |
| 	if (aucBuffer[usOffset] != 0) {
 | |
| 		NO_DBG_STRN(aucBuffer + usOffset, 8);
 | |
| 		tCreateDtm = tConvertDosDate((char *)aucBuffer + usOffset);
 | |
| 	}
 | |
| 	aucBuffer = xfree(aucBuffer);
 | |
| } /* end of vSet0SummaryInfo */
 | |
| 
 | |
| /*
 | |
|  * vSet2SummaryInfo - set summary information from a WinWord 1/2 file
 | |
|  */
 | |
| void
 | |
| vSet2SummaryInfo(FILE *pFile, int iWordVersion, const UCHAR *aucHeader)
 | |
| {
 | |
| 	UCHAR	*aucBuffer;
 | |
| 	ULONG	ulBeginSumdInfo, ulBeginDocpInfo, ulTmp;
 | |
| 	size_t	tSumdInfoLen, tDocpInfoLen, tLen, tCounter, tStart;
 | |
| 
 | |
| 	TRACE_MSG("vSet2SummaryInfo");
 | |
| 
 | |
| 	fail(pFile == NULL || aucHeader == NULL);
 | |
| 	fail(iWordVersion != 1 && iWordVersion != 2);
 | |
| 
 | |
| 	/* First check the header */
 | |
| 	usLid = usGetWord(0x06, aucHeader); /* Language IDentification */
 | |
| 	DBG_HEX(usLid);
 | |
| 	if (usLid < 999 && iWordVersion == 1) {
 | |
| 		switch (usLid) {
 | |
| 		case   1: usLid = 0x0409; break;	/* American English */
 | |
| 		case   2: usLid = 0x0c0c; break;	/* Canadian French */
 | |
| 		case  31: usLid = 0x0413; break;	/* Dutch */
 | |
| 		case  33: usLid = 0x040c; break;	/* French */
 | |
| 		case  34: usLid = 0x040a; break;	/* Spanish */
 | |
| 		case  36: usLid = 0x040e; break;	/* Hungarian */
 | |
| 		case  39: usLid = 0x0410; break;	/* Italian */
 | |
| 		case  44: usLid = 0x0809; break;	/* British English */
 | |
| 		case  45: usLid = 0x0406; break;	/* Danish */
 | |
| 		case  46: usLid = 0x041f; break;	/* Swedish */
 | |
| 		case  47: usLid = 0x0414; break;	/* Norwegian */
 | |
| 		case  48: usLid = 0x0415; break;	/* Polish */
 | |
| 		case  49: usLid = 0x0407; break;	/* German */
 | |
| 		case 351: usLid = 0x0816; break;	/* Portuguese */
 | |
| 		case 358: usLid = 0x040b; break;	/* Finnish */
 | |
| 		default:
 | |
| 			DBG_DEC(usLid);
 | |
| 			DBG_FIXME();
 | |
| 			usLid = 0x0409;		/* American English */
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (iWordVersion != 2) {
 | |
| 		/* Unknown where to find the associated strings */
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* Second check the associated strings */
 | |
| 	ulBeginSumdInfo = ulGetLong(0x118, aucHeader); /* fcSttbfAssoc */
 | |
| 	DBG_HEX(ulBeginSumdInfo);
 | |
| 	tSumdInfoLen = (size_t)usGetWord(0x11c, aucHeader); /* cbSttbfAssoc */
 | |
| 	DBG_DEC(tSumdInfoLen);
 | |
| 
 | |
| 	if (tSumdInfoLen == 0) {
 | |
| 		/* There is no summary information */
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	aucBuffer = xmalloc(tSumdInfoLen);
 | |
| 	if (!bReadBytes(aucBuffer, tSumdInfoLen, ulBeginSumdInfo, pFile)) {
 | |
| 		aucBuffer = xfree(aucBuffer);
 | |
| 		return;
 | |
| 	}
 | |
| 	NO_DBG_PRINT_BLOCK(aucBuffer, tSumdInfoLen);
 | |
| 	tLen = (size_t)ucGetByte(0, aucBuffer);
 | |
| 	DBG_DEC_C(tSumdInfoLen != tLen, tSumdInfoLen);
 | |
| 	DBG_DEC_C(tSumdInfoLen != tLen, tLen);
 | |
| 	tStart = 1;
 | |
| 	for (tCounter = 0; tCounter < 17; tCounter++) {
 | |
| 		if (tStart >= tSumdInfoLen) {
 | |
| 			break;
 | |
| 		}
 | |
| 		tLen = (size_t)ucGetByte(tStart, aucBuffer);
 | |
| 		if (tLen != 0) {
 | |
| 			NO_DBG_DEC(tCounter);
 | |
| 			NO_DBG_STRN(aucBuffer + tStart + 1, tLen);
 | |
| 			switch (tCounter) {
 | |
| 			case 3:
 | |
| 				szTitle = xmalloc(tLen + 1);
 | |
| 				strncpy(szTitle,
 | |
| 					(char *)aucBuffer + tStart + 1, tLen);
 | |
| 				szTitle[tLen] = '\0';
 | |
| 				break;
 | |
| 			case 4:
 | |
| 				szSubject = xmalloc(tLen + 1);
 | |
| 				strncpy(szSubject,
 | |
| 					(char *)aucBuffer + tStart + 1, tLen);
 | |
| 				szSubject[tLen] = '\0';
 | |
| 				break;
 | |
| 			case 7:
 | |
| 				szAuthor = xmalloc(tLen + 1);
 | |
| 				strncpy(szAuthor,
 | |
| 					(char *)aucBuffer + tStart + 1, tLen);
 | |
| 				szAuthor[tLen] = '\0';
 | |
| 				break;
 | |
| 			default:
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 		tStart += tLen + 1;
 | |
| 	}
 | |
| 	aucBuffer = xfree(aucBuffer);
 | |
| 
 | |
| 	/* Third check the document properties */
 | |
| 	ulBeginDocpInfo = ulGetLong(0x112, aucHeader); /* fcDop */
 | |
| 	DBG_HEX(ulBeginDocpInfo);
 | |
| 	tDocpInfoLen = (size_t)usGetWord(0x116, aucHeader); /* cbDop */
 | |
| 	DBG_DEC(tDocpInfoLen);
 | |
| 	if (tDocpInfoLen < 12) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	aucBuffer = xmalloc(tDocpInfoLen);
 | |
| 	if (!bReadBytes(aucBuffer, tDocpInfoLen, ulBeginDocpInfo, pFile)) {
 | |
| 		aucBuffer = xfree(aucBuffer);
 | |
| 		return;
 | |
| 	}
 | |
|         ulTmp = ulGetLong(0x14, aucBuffer); /* dttmCreated */
 | |
| 	tCreateDtm = tConvertDTTM(ulTmp);
 | |
|         ulTmp = ulGetLong(0x18, aucBuffer); /* dttmRevised */
 | |
| 	tLastSaveDtm = tConvertDTTM(ulTmp);
 | |
| 	aucBuffer = xfree(aucBuffer);
 | |
| } /* end of vSet2SummaryInfo */
 | |
| 
 | |
| /*
 | |
|  * vSetSummaryInfoOLE - set summary information from a Word 6+ file
 | |
|  */
 | |
| static void
 | |
| vSetSummaryInfoOLE(FILE *pFile, const pps_info_type *pPPS,
 | |
| 	const ULONG *aulBBD, size_t tBBDLen,
 | |
| 	const ULONG *aulSBD, size_t tSBDLen)
 | |
| {
 | |
| 	UCHAR	*pucBuffer;
 | |
| 
 | |
| 	fail(pFile == NULL || pPPS == NULL);
 | |
| 	fail(aulBBD == NULL || aulSBD == NULL);
 | |
| 
 | |
| 	/* Summary Information */
 | |
| 	pucBuffer = pucAnalyseSummaryInfoHeader(pFile,
 | |
| 		pPPS->tSummaryInfo.ulSB, pPPS->tSummaryInfo.ulSize,
 | |
| 		aulBBD, tBBDLen, aulSBD, tSBDLen);
 | |
| 	if (pucBuffer != NULL) {
 | |
| 		if (!vAnalyseSummaryInfo(pucBuffer)) {
 | |
| 		    return;
 | |
| 		}
 | |
| 		pucBuffer = xfree(pucBuffer);
 | |
| 	}
 | |
| 
 | |
| 	/* Document Summary Information */
 | |
| 	pucBuffer = pucAnalyseSummaryInfoHeader(pFile,
 | |
| 		pPPS->tDocSummaryInfo.ulSB, pPPS->tDocSummaryInfo.ulSize,
 | |
| 		aulBBD, tBBDLen, aulSBD, tSBDLen);
 | |
| 	if (pucBuffer != NULL) {
 | |
| 		vAnalyseDocumentSummaryInfo(pucBuffer);
 | |
| 		pucBuffer = xfree(pucBuffer);
 | |
| 	}
 | |
| } /* end of vSetSummaryInfoOLE */
 | |
| 
 | |
| /*
 | |
|  * vSet6SummaryInfo - set summary information from a Word 6/7 file
 | |
|  */
 | |
| void
 | |
| vSet6SummaryInfo(FILE *pFile, const pps_info_type *pPPS,
 | |
| 	const ULONG *aulBBD, size_t tBBDLen,
 | |
| 	const ULONG *aulSBD, size_t tSBDLen,
 | |
| 	const UCHAR *aucHeader)
 | |
| {
 | |
| 	TRACE_MSG("vSet6SummaryInfo");
 | |
| 
 | |
| 	/* Header Information */
 | |
| 	usLid = usGetWord(0x06, aucHeader); /* Language IDentification */
 | |
| 	DBG_HEX(usLid);
 | |
| 
 | |
| 	/* Summery Information */
 | |
| 	vSetSummaryInfoOLE(pFile, pPPS, aulBBD, tBBDLen, aulSBD, tSBDLen);
 | |
| } /* end of vSet6SummaryInfo */
 | |
| 
 | |
| /*
 | |
|  * vSet8SummaryInfo - set summary information a Word 8/9/10 file
 | |
|  */
 | |
| void
 | |
| vSet8SummaryInfo(FILE *pFile, const pps_info_type *pPPS,
 | |
| 	const ULONG *aulBBD, size_t tBBDLen,
 | |
| 	const ULONG *aulSBD, size_t tSBDLen,
 | |
| 	const UCHAR *aucHeader)
 | |
| {
 | |
| 	USHORT	usTmp;
 | |
| 
 | |
| 	TRACE_MSG("vSet8SummaryInfo");
 | |
| 
 | |
| 	/* Header Information */
 | |
| 	usTmp = usGetWord(0x0a, aucHeader);
 | |
| 	if (usTmp & BIT(14)) {
 | |
| 		/* Language IDentification Far East */
 | |
| 		usLid = usGetWord(0x3c, aucHeader);
 | |
| 	} else {
 | |
| 		/* Language IDentification */
 | |
| 		usLid = usGetWord(0x06, aucHeader);
 | |
| 	}
 | |
| 	DBG_HEX(usLid);
 | |
| 
 | |
| 	/* Summery Information */
 | |
| 	vSetSummaryInfoOLE(pFile, pPPS, aulBBD, tBBDLen, aulSBD, tSBDLen);
 | |
| } /* end of vSet8SummaryInfo */
 | |
| 
 | |
| /*
 | |
|  * szGetTitle - get the title field
 | |
|  */
 | |
| const char *
 | |
| szGetTitle(void)
 | |
| {
 | |
| 	return szTitle;
 | |
| } /* end of szGetTitle */
 | |
| 
 | |
| /*
 | |
|  * szGetSubject - get the subject field
 | |
|  */
 | |
| const char *
 | |
| szGetSubject(void)
 | |
| {
 | |
| 	return szSubject;
 | |
| } /* end of szGetSubject */
 | |
| 
 | |
| /*
 | |
|  * szGetAuthor - get the author field
 | |
|  */
 | |
| const char *
 | |
| szGetAuthor(void)
 | |
| {
 | |
| 	return szAuthor;
 | |
| } /* end of szGetAuthor */
 | |
| 
 | |
| /*
 | |
|  * szGetLastSaveDtm - get the last save date field
 | |
|  */
 | |
| const char *
 | |
| szGetLastSaveDtm(void)
 | |
| {
 | |
| 	static char	szTime[12];
 | |
| 	struct tm	*pTime;
 | |
| 
 | |
| 	if (tLastSaveDtm == (time_t)-1) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	pTime = localtime(&tLastSaveDtm);
 | |
| 	if (pTime == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	snprintf(szTime, sizeof(szTime), "%04d-%02d-%02d",
 | |
| 		pTime->tm_year + 1900, pTime->tm_mon + 1, pTime->tm_mday);
 | |
| 	return szTime;
 | |
| } /* end of szGetLastSaveDtm */
 | |
| 
 | |
| /*
 | |
|  * szGetModDate - get the last save date field
 | |
|  */
 | |
| const char *
 | |
| szGetModDate(void)
 | |
| {
 | |
| 	static char	szTime[20];
 | |
| 	struct tm	*pTime;
 | |
| 
 | |
| 	if (tLastSaveDtm == (time_t)-1) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	pTime = localtime(&tLastSaveDtm);
 | |
| 	if (pTime == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	snprintf(szTime, sizeof(szTime), "D:%04d%02d%02d%02d%02d",
 | |
| 		pTime->tm_year + 1900, pTime->tm_mon + 1, pTime->tm_mday,
 | |
| 		pTime->tm_hour, pTime->tm_min);
 | |
| 	return szTime;
 | |
| } /* end of szGetModDate */
 | |
| 
 | |
| /*
 | |
|  * szGetCreationDate - get the last save date field
 | |
|  */
 | |
| const char *
 | |
| szGetCreationDate(void)
 | |
| {
 | |
| 	static char	szTime[20];
 | |
| 	struct tm	*pTime;
 | |
| 
 | |
| 	if (tCreateDtm == (time_t)-1) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	pTime = localtime(&tCreateDtm);
 | |
| 	if (pTime == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	snprintf(szTime, sizeof(szTime), "D:%04d%02d%02d%02d%02d",
 | |
| 		pTime->tm_year + 1900, pTime->tm_mon + 1, pTime->tm_mday,
 | |
| 		pTime->tm_hour, pTime->tm_min);
 | |
| 	return szTime;
 | |
| } /* end of szGetCreationDate */
 | |
| 
 | |
| /*
 | |
|  * szGetCompany - get the company field
 | |
|  */
 | |
| const char *
 | |
| szGetCompany(void)
 | |
| {
 | |
| 	return szCompany;
 | |
| } /* end of szGetCompany */
 | |
| 
 | |
| /*
 | |
|  * szGetLanguage - get de language field
 | |
|  */
 | |
| const char *
 | |
| szGetLanguage(void)
 | |
| {
 | |
| 	if (usLid == (USHORT)-1) {
 | |
| 		/* No Language IDentification */
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	if (usLid < 999) {
 | |
| 		/* This is a Locale, not a Language IDentification */
 | |
| 		DBG_DEC(usLid);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	/* Exceptions to the general rule */
 | |
| 	switch (usLid) {
 | |
| 	case 0x0404: return "zh_TW"; /* Traditional Chinese */
 | |
| 	case 0x0804: return "zh_CN"; /* Simplified Chinese */
 | |
| 	case 0x0c04: return "zh_HK"; /* Hong Kong Chinese */
 | |
| 	case 0x1004: return "zh_SG"; /* Singapore Chinese */
 | |
| 	case 0x0807: return "de_CH"; /* Swiss German */
 | |
| 	case 0x0409: return "en_US"; /* American English */
 | |
| 	case 0x0809: return "en_GB"; /* British English */
 | |
| 	case 0x0c09: return "en_AU"; /* Australian English */
 | |
| 	case 0x080a: return "es_MX"; /* Mexican Spanish */
 | |
| 	case 0x080c: return "fr_BE"; /* Belgian French */
 | |
| 	case 0x0c0c: return "fr_CA"; /* Canadian French */
 | |
| 	case 0x100c: return "fr_CH"; /* Swiss French */
 | |
| 	case 0x0810: return "it_CH"; /* Swiss Italian */
 | |
| 	case 0x0813: return "nl_BE"; /* Belgian Dutch */
 | |
| 	case 0x0416: return "pt_BR"; /* Brazilian Portuguese */
 | |
| 	case 0x081a:
 | |
| 	case 0x0c1a: return "sr";    /* Serbian */
 | |
| 	case 0x081d: return "sv_FI"; /* Finland Swedish */
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	/* The general rule */
 | |
| 	switch (usLid & 0x00ff) {
 | |
| 	case 0x01: return "ar";	/* Arabic */
 | |
| 	case 0x02: return "bg";	/* Bulgarian */
 | |
| 	case 0x03: return "ca";	/* Catalan */
 | |
| 	case 0x04: return "zh";	/* Chinese */
 | |
| 	case 0x05: return "cs";	/* Czech */
 | |
| 	case 0x06: return "da";	/* Danish */
 | |
| 	case 0x07: return "de";	/* German */
 | |
| 	case 0x08: return "el";	/* Greek */
 | |
| 	case 0x09: return "en";	/* English */
 | |
| 	case 0x0a: return "es";	/* Spanish */
 | |
| 	case 0x0b: return "fi";	/* Finnish */
 | |
| 	case 0x0c: return "fr";	/* French */
 | |
| 	case 0x0d: return "he";	/* Hebrew */
 | |
| 	case 0x0e: return "hu";	/* Hungarian */
 | |
| 	case 0x0f: return "is";	/* Icelandic */
 | |
| 	case 0x10: return "it";	/* Italian */
 | |
| 	case 0x11: return "ja";	/* Japanese */
 | |
| 	case 0x12: return "ko";	/* Korean */
 | |
| 	case 0x13: return "nl";	/* Dutch */
 | |
| 	case 0x14: return "no";	/* Norwegian */
 | |
| 	case 0x15: return "pl";	/* Polish */
 | |
| 	case 0x16: return "pt";	/* Portuguese */
 | |
| 	case 0x17: return "rm";	/* Rhaeto-Romance */
 | |
| 	case 0x18: return "ro";	/* Romanian */
 | |
| 	case 0x19: return "ru";	/* Russian */
 | |
| 	case 0x1a: return "hr";	/* Croatian */
 | |
| 	case 0x1b: return "sk";	/* Slovak */
 | |
| 	case 0x1c: return "sq";	/* Albanian */
 | |
| 	case 0x1d: return "sv";	/* Swedish */
 | |
| 	case 0x1e: return "th";	/* Thai */
 | |
| 	case 0x1f: return "tr";	/* Turkish */
 | |
| 	case 0x20: return "ur";	/* Urdu */
 | |
| 	case 0x21: return "id";	/* Indonesian */
 | |
| 	case 0x22: return "uk";	/* Ukrainian */
 | |
| 	case 0x23: return "be";	/* Belarusian */
 | |
| 	case 0x24: return "sl";	/* Slovenian */
 | |
| 	case 0x25: return "et";	/* Estonian */
 | |
| 	case 0x26: return "lv";	/* Latvian */
 | |
| 	case 0x27: return "lt";	/* Lithuanian */
 | |
| 	case 0x29: return "fa";	/* Farsi */
 | |
| 	case 0x2a: return "vi";	/* Viet Nam */
 | |
| 	case 0x2b: return "hy";	/* Armenian */
 | |
| 	case 0x2c: return "az";	/* Azeri */
 | |
| 	case 0x2d: return "eu";	/* Basque */
 | |
| 	case 0x2f: return "mk";	/* Macedonian */
 | |
| 	case 0x36: return "af";	/* Afrikaans */
 | |
| 	case 0x37: return "ka";	/* Georgian */
 | |
| 	case 0x38: return "fo";	/* Faeroese */
 | |
| 	case 0x39: return "hi";	/* Hindi */
 | |
| 	case 0x3e: return "ms";	/* Malay */
 | |
| 	case 0x3f: return "kk";	/* Kazakh */
 | |
| 	default:
 | |
| 		DBG_HEX(usLid);
 | |
| 		DBG_FIXME();
 | |
| 		return NULL;
 | |
| 	}
 | |
| } /* end of szGetLanguage */
 |