CINDEX.C

/* 
* CINDEX.C
*
* Create and extend PR_CONVERSATION_INDEX
*/

#include <windows.h>
#include <mapiwin.h>
#include <mapix.h>
#include <mapiutil.h>
#include <mapidbg.h>
#include <memory.h>

#define cbConvIndexHdr 22
#define cbConvIndexComponent 5
#define bConvIndexRes (BYTE)1

/*
* ExtractLastFileTime()
*
* Purpose:
*
* Parses an existing covnersation index and extracts the last
* FILETIME value contained in the index.
*/
VOID
ExtractLastFileTime (LPBYTE lpb, ULONG cb, FILETIME FAR * lpft)
{
FILETIME ft;
FILETIME ftCur;
LPBYTE lpbEnd;

// Lets do some verification on the key
//
Assert (!IsBadReadPtr (lpb, (UINT)cb));
Assert (!IsBadWritePtr (lpft, sizeof(FILETIME)));
Assert (*lpb == bConvIndexRes);
Assert (cb >= cbConvIndexHdr);
Assert (!((cb - cbConvIndexHdr) % cbConvIndexComponent));

// Rebuild the header time\date into FILETIME format
//
ft.dwHighDateTime = (((DWORD)(lpb[1])) << 16) |
(((DWORD)(lpb[2])) << 8) |
((DWORD)(lpb[3]));

ft.dwLowDateTime = (((DWORD)(lpb[4])) << 24) |
(((DWORD)(lpb[5])) << 16);

// See where the last child chunk ends
//
lpbEnd = lpb + cb;
lpb += cbConvIndexHdr;

// Now go through the child chunks to compute
// for the last FILETIME using the delta
//
while (lpb < lpbEnd)
{
// Convert the delta of the current child
// chunk into the FILETIME format. Use the
// delta code in the first bit to get the
// real delta.
//
// Delta code : 1 = mask 10000000 = 0x80
//
if ((*lpb & 0x80) == 0x80)
{
// Mask out the first bit used for the delta code
// *lpb | 0x7F;
//
ftCur.dwHighDateTime = (((DWORD)(lpb[0] & 0x7F)) << 15) |
(((DWORD)(lpb[1])) << 7) |
(((DWORD)(lpb[2])) >> 1);

ftCur.dwLowDateTime = (((DWORD)(lpb[2])) << 31) |
(((DWORD)(lpb[3])) << 23);

ft = FtAddFt (ft, ftCur);
}
else
{
ftCur.dwHighDateTime = (((DWORD)(lpb[0] & 0x7F)) << 10) |
(((DWORD)(lpb[1])) << 2) |
(((DWORD)(lpb[2])) >> 6);

ftCur.dwLowDateTime = (((DWORD)(lpb[2])) << 26) |
(((DWORD)(lpb[3])) << 18);

ft = FtAddFt (ft, ftCur);
}

// Advance to next child
//
lpb += cbConvIndexComponent;
}

// If all went well, we sould have ended up at
// lpbEnd
//
Assert (lpb == lpbEnd);
*lpft = ft;
return;
}

/*
* ScFillConvHeader()
*
* Purpose:
*
* Fills in the header of a conversation index. This function is
* called when a new conversation index is created.
*
* Assumptions:
*
* The buffer passed in should be big enough to hold cbConvIndexHdr
* bytes (22 bytes).
*/
SCODE
ScFillConvHeader (LPBYTE rgb, ULONG cb)
{
SCODE sc = S_OK;
SYSTEMTIME st;
FILETIME ft;
GUID guid;

Assert (cb >= cbConvIndexHdr);
Assert (!IsBadWritePtr (rgb, cbConvIndexHdr));

// (Ha). Put the reserved byte
//
rgb[0] = bConvIndexRes;

// (Hb). Put the current time
//
GetSystemTime (&st);
SystemTimeToFileTime (&st, &ft);

// Construct the date\time one byte at a time
//
rgb[1] = (BYTE) ((ft.dwHighDateTime & 0x00FF0000) >> 16);
rgb[2] = (BYTE) ((ft.dwHighDateTime & 0x0000FF00) >> 8);
rgb[3] = (BYTE) (ft.dwHighDateTime & 0x000000FF);

// Drop the rightmost least significant 2 bytes
//
rgb[4] = (BYTE) ((ft.dwLowDateTime & 0xFF000000) >> 24);
rgb[5] = (BYTE) ((ft.dwLowDateTime & 0x00FF0000) >> 16);

// (Hc). Now put the GUID
// {
// DWORD Data1;
// WORD Data2;
// WORD Data3;
// BYTE Data4[8];
// } GUID;
//
sc = GetScode (CoCreateGuid (&guid));
if (!FAILED (sc))
{
// Again, lets do it one byte at a time
//
rgb[6] = (BYTE) ((guid.Data1 & 0xFF000000) >> 24);
rgb[7] = (BYTE) ((guid.Data1 & 0x00FF0000) >> 16);
rgb[8] = (BYTE) ((guid.Data1 & 0x0000FF00) >> 8);
rgb[9] = (BYTE) ((guid.Data1 & 0x000000FF));
rgb[10] = (BYTE) ((guid.Data2 & 0xFF00) >> 8);
rgb[11] = (BYTE) ((guid.Data2 & 0x00FF));
rgb[12] = (BYTE) ((guid.Data3 & 0xFF00) >> 8);
rgb[13] = (BYTE) ((guid.Data3 & 0x00FF));
}

// Slurp the rest across
//
CopyMemory (&rgb[14], &guid.Data4, 8);
DebugTraceSc (ScFillConvHeader(), sc);
return sc;
}

/*
* ScAddConversationIndex()
*
* Purpose:
*
* Given the conversation index to a message, this function will
* create the conversation of a child message to the original. If
* the no original is suplied, then an index is created that would
* signify the start of a new thread.
*/
SCODE
ScAddConversationIndex (ULONG cbParent,
LPBYTE lpbParent,
ULONG FAR * lpcb,
LPBYTE FAR * lppb)
{
SCODE sc;
DWORD dwTemp;
SYSTEMTIME st;
FILETIME ft;
FILETIME ftLast;
FILETIME ftDelta;
HMODULE hMAPIDll = NULL;
typedef SCODE (STDAPICALLTYPE FAR *MAPICONVIDX)(ULONG, LPBYTE, ULONG FAR *, LPBYTE FAR *);
MAPICONVIDX lpfnMAPIConvIdx = NULL;

#if defined (WIN16)
#define szScCreateConversationIndex "ScCreateConversationIndex"
#elif defined (_WIN32) && defined (_X86_)
#ifndef szScCreateConversationIndex
#define szScCreateConversationIndex "ScCreateConversationIndex@16"
#endif
#elif defined (_ALPHA_) || defined (_MIPS_) || defined (_PPC_)
#define szScCreateConversationIndex "ScCreateConversationIndex"
#endif

#ifdef _WIN32
#define szMAPIDll "mapi32.dll"
#else
#define szMAPIDll "mapi.dll"
#endif

/*
* MAPI is going to export a function that is doing the same thing as this one.
* So if the function is present we'll use it.
*/
hMAPIDll = GetModuleHandle(szMAPIDll);
if(hMAPIDll)
{
lpfnMAPIConvIdx = (MAPICONVIDX)GetProcAddress(hMAPIDll,
szScCreateConversationIndex);
if(lpfnMAPIConvIdx)
{
return (*lpfnMAPIConvIdx)(cbParent, lpbParent, lpcb, lppb);
}
}
// Ensure that the parent is what we think
// it should be
//
if ((cbParent < cbConvIndexHdr) ||
((cbParent - cbConvIndexHdr) % cbConvIndexComponent) ||
(lpbParent[0] != bConvIndexRes))
{
cbParent = 0;
*lpcb = cbConvIndexHdr;
}
else
*lpcb = cbParent + cbConvIndexComponent;

sc = MAPIAllocateBuffer (*lpcb, (LPVOID FAR *)lppb);
if (!FAILED (sc))
{
if (cbParent == 0)
{
// This is a new key, so all it ever contains
// is a header. Fill it in and we are done
//
sc = ScFillConvHeader (*lppb, *lpcb);
if (FAILED (sc))
{
MAPIFreeBuffer (*lppb);
*lppb = NULL;
}
}
else
{
// First copy the old key across
//
CopyMemory (*lppb, lpbParent, (UINT)cbParent);

// (Cb). First get the current time (we'll then get
// the absolute distance between the current time and
// the time in the last chunk)
//
GetSystemTime (&st);
SystemTimeToFileTime (&st, &ft);

// Now get the time of the last chunk
//
ExtractLastFileTime (lpbParent, cbParent, &ftLast);

// Now mask out the bits we don't want from the
// current time
//
ft.dwHighDateTime &= 0x00FFFFFF;
ft.dwLowDateTime &= 0xFFFF0000;

// This assert is here to catch how often the
// 5-byte time can collide and under what scenario,
// to see if 5 bytes + the next byte suffices to
// make this child chunk unique.
//
Assert (!((ftLast.dwHighDateTime == ft.dwHighDateTime) &&
(ftLast.dwLowDateTime == ft.dwLowDateTime)));

// Get the change in time
//
if ((ft.dwHighDateTime > ftLast.dwHighDateTime) ||
((ft.dwHighDateTime == ftLast.dwHighDateTime) &&
(ft.dwLowDateTime > ftLast.dwLowDateTime)))
{
ftDelta = FtSubFt (ft, ftLast);
}
else
ftDelta = FtSubFt (ftLast, ft);

// If the delta is less than 1.7 yrs, use 0
//
if (!(ftDelta.dwHighDateTime & 0x00FE0000))
{
// Just mask out the 31 bits that we
// want from the ftDelta
//
dwTemp = ((DWORD)(ftDelta.dwHighDateTime & 0x0001FFFF)) << 14 |
((DWORD)(ftDelta.dwLowDateTime & 0xFFFC0000)) >> 18;

// Only the first byte is different
//
(*lppb)[cbParent] = (BYTE)((dwTemp & 0xFF000000) >> 24 );
}
else
{
// Just mask out the 31 bits that we
// want from the ftDelta
//
dwTemp = ((DWORD)(ftDelta.dwHighDateTime & 0x003FFFFF)) << 9 |
((DWORD)(ftDelta.dwLowDateTime & 0xFF800000)) >> 23;

// Only the first byte is different
//
(*lppb)[cbParent] = (BYTE)(HIBYTE(HIWORD(dwTemp)) | 0x080);
}

// The remaining delta bytes are the same
//
(*lppb)[cbParent + 1] = (BYTE) ((dwTemp & 0x00FF0000) >> 16);
(*lppb)[cbParent + 2] = (BYTE) ((dwTemp & 0x0000FF00) >> 8);
(*lppb)[cbParent + 3] = (BYTE) ((dwTemp & 0x000000FF) );

// (Cc). Next get the random number
// (Cd). Next get the sequence count
// -- we are going to use part of the tick count
//
(*lppb)[cbParent + 4] = (BYTE) (GetTickCount() & 0x000000FF);
}
}

DebugTraceSc (ScAddConversationIndex(), sc);
return sc;
}