ROUTE.C

/* 
- ROUTE.C
*
*
* Functions used to implement routing.
*
* To create a new routing message call:
* DialogBoxParam(hInst, "RouteNote", hWnd, RouteNoteDlgProc, (LPARAM) NULL );
* To route a routed message to the next recipient call:
* DialogBoxParam(hInst, "RouteNote", hWnd, RouteNoteDlgProc, (LPARAM) pmsg );
* where pmsg is a pointer to the message you want to route.
* To find out if a message was routed by this program call:
* HasRougingSlip();
*
*
* To use this functions in your own program change the line (in route.h):
* #define lpszSmplRTMsgClass "IPM.Note.SampleRoutingForm"
* to reflect the name of your own class for routing messages.
*
*
* Copyright 1986-1996, Microsoft Corporation. All Rights Reserved.
*/

#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>
#ifdef _WIN32
#include <objerror.h>
#include <objbase.h>
#endif
#ifdef WIN16
#include <compobj.h>
#include <commdlg.h>
#endif
#include <mapiutil.h>
#include <mapix.h>
#include <mapiwin.h>
#include <mapidbg.h>
#include <cindex.h>

#ifdef WIN16
#define GWL_USERDATA DWL_USER
#endif


/*
To use a GUID XXX in a program you have to initialize it in exactly one
obj file of your project.
Line "#define USES_XXX" tells the compiler that you are going to use GUID XXX
in this file.
Lines "#define INITGUID" and "#include<initguid.h>"
tell the compiler to init GUIDs in this file.*/

/* the GUID we'll be using */
#define USES_PS_ROUTING_EMAIL_ADDRESSES 1
#define USES_PS_ROUTING_ADDRTYPE 1
#define USES_PS_ROUTING_DISPLAY_NAME 1
#define USES_PS_ROUTING_ENTRYID 1
#define USES_PS_ROUTING_SEARCH_KEY 1
#define USES_PS_PUBLIC_STRINGS 1
#define USES_IID_IMessage 1
#define USES_IID_IMAPIStatus 1
#define USES_IID_IMAPIForm 1
/*initialize the GUIDs in this file*/
#define INITGUID 1
#include <initguid.h>
#include <mapiguid.h>
#include <pdkver.h>

#include "client.h"
#include "bitmap.h"
#include "route.h"

/* Routing Data. Used in RouteNoteDlgProc that handles composing/sending/reading of
routing messages */
typedef struct _ROUTEDATA
{
LPADRLIST palAddrListOld;
LPADRLIST palAddrListActive;
ULONG nCurrentRouteRecip;
ULONG nTotalRouteRecip;
LPMAPITABLE ptblAttach;
BOOL bNewMessage;
LPMESSAGE pmsgRouteMsg;
ULONG tagCurrent;
ULONG tagTotal;
ULONG cbConvIdx;
LPBYTE lpbConvIdx;
} ROUTEDATA, FAR * LPROUTEDATA;

BOOL MakeNewRouteMessage(LPMESSAGE pmsgRead, LPROUTEDATA FAR * ppRouteData);
BOOL SetRouteProps(LPROUTEDATA pRouteData);
BOOL DelRouteProps(LPROUTEDATA pRouteData);
BOOL GetRoutePropTagArray(ULONG cb, LPMESSAGE lpM, LPSPropTagArray FAR * lppspta);
BOOL GetRoutePropTagArrayFast(ULONG nTotalRecip, LPMESSAGE lpM, BOOL fCreate,
LPSPropTagArray FAR * lppspta);
BOOL GetRouteIndices(LPROUTEDATA pRouteData);
BOOL SetRouteIndices(LPROUTEDATA pRouteData);
BOOL CreateOutMessage(LPMESSAGE FAR * lpmSrcMsgI);
BOOL GetRouteAddrLists(LPROUTEDATA pRouteData);
UINT FirstRecipient(LPADRLIST lpAL);
BOOL SetMessageClass(LPMESSAGE lpM, LPSTR lpszClass);
BOOL HasAttachment(LPMESSAGE lpM);
BOOL PopulateAttachList(HWND hDlg, LPROUTEDATA pRouteData);
BOOL CreateNewAttachment(HWND hDlg);
void SaveAttachment( HWND hDlg, UINT indx);
void RT_OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify);
BOOL RT_OnInitDialog(HWND hDlg, HWND hwndFocus, LPARAM lParam);
BOOL RBox_OnInitDialog(HWND hDlg, HWND hwndFocus, LPARAM lParam);
void RBox_OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify);
void DeInitRouteData(LPROUTEDATA pRouteData);
BOOL CALLBACK RouteBoxDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
void InitRouteNameIDArray(ULONG cb, LPMAPINAMEID lpPropNames);
void PopulateRouteListBox(HWND hDlg);
void ConfigMoveButtons(HWND hDlg, HWND hLB);


typedef enum _ROUTEPROPSETS
{
ROUTEPROPSET_EMAIL_ADDRESS = 0,
ROUTEPROPSET_ADDRTYPE,
ROUTEPROPSET_DISPLAY_NAME,
ROUTEPROPSET_ENTRYID,
ROUTEPROPSET_SEARCH_KEY,
ROUTEPROPSETDIM /* used for dimension*/
} ROUTEPROPSETS;


LPGUID lpguidA[ROUTEPROPSETDIM] =
{
(LPGUID)&PS_ROUTING_EMAIL_ADDRESSES,
(LPGUID)&PS_ROUTING_ADDRTYPE,
(LPGUID)&PS_ROUTING_DISPLAY_NAME,
(LPGUID)&PS_ROUTING_ENTRYID,
(LPGUID)&PS_ROUTING_SEARCH_KEY
};

ULONG ulRoutePropTypes [ROUTEPROPSETDIM] =
{
PT_STRING8,
PT_STRING8,
PT_STRING8,
PT_BINARY,
PT_BINARY
};



/* used for PrepareRecips call*/
SizedSPropTagArray(ROUTEPROPSETDIM, sptRouteProps) =
{
ROUTEPROPSETDIM,
{
PR_EMAIL_ADDRESS,
PR_ADDRTYPE,
PR_DISPLAY_NAME,
PR_ENTRYID,
PR_SEARCH_KEY
}
};

enum {EMSG_SUBJ = 0, EMSG_BODY, EMSG_MSGFLAGS, EMSG_CONVIDX, EMSGPROPDIM};
SizedSPropTagArray( EMSGPROPDIM, tagaMsgProps) =
{
EMSGPROPDIM,
{PR_SUBJECT, PR_BODY, PR_MESSAGE_FLAGS, PR_CONVERSATION_INDEX}
};
/*
//
// the number of entries could vary from
// EXCLUDED_PROPS_ON_REPLY to EXCLUDED_PROPS_ON_REPLY - 1 and vice versa,
// depending if the message is being reply or fowarded. If forwarded, the
// PR_MESSAGE_ATTACHMENTS property is included in the forwarded message
// otherwise it is excluded
*/
#define EXCLUDED_PROPS_ON_REPLY 29
SizedSPropTagArray (EXCLUDED_PROPS_ON_REPLY, sptExcludedProps) =
{
EXCLUDED_PROPS_ON_REPLY,
{
PR_SENDER_NAME,
PR_SENDER_ENTRYID,
PR_SENDER_SEARCH_KEY,
PR_SENDER_EMAIL_ADDRESS,
PR_SENDER_ADDRTYPE,

PR_RECEIVED_BY_NAME,
PR_RECEIVED_BY_ENTRYID,
PR_RECEIVED_BY_SEARCH_KEY,

PR_SENT_REPRESENTING_NAME,
PR_SENT_REPRESENTING_ENTRYID,
PR_SENT_REPRESENTING_SEARCH_KEY,
PR_SENT_REPRESENTING_EMAIL_ADDRESS,
PR_SENT_REPRESENTING_ADDRTYPE,

PR_RCVD_REPRESENTING_NAME,
PR_RCVD_REPRESENTING_ENTRYID,
PR_RCVD_REPRESENTING_SEARCH_KEY,

PR_MESSAGE_FLAGS,
PR_MESSAGE_RECIPIENTS,

PR_READ_RECEIPT_ENTRYID,
PR_REPORT_ENTRYID,

PR_REPLY_RECIPIENT_ENTRIES,
PR_REPLY_RECIPIENT_NAMES,

PR_PARENT_KEY,

PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED,

PR_READ_RECEIPT_REQUESTED,

PR_CLIENT_SUBMIT_TIME,
PR_MESSAGE_DELIVERY_TIME,
PR_MESSAGE_DOWNLOAD_TIME,

// PR_SUBJECT_PREFIX,
PR_MESSAGE_ATTACHMENTS
}
};

LPADRBOOK
OpenAddressBook(HWND hwnd)
{
HRESULT hr;
LPADRBOOK pabAddrBook = NULL;

Assert(pses);
hr = pses->lpVtbl->OpenAddressBook(pses, (ULONG) hwnd, NULL, 0, &pabAddrBook);
if(HR_FAILED(hr))
{
MakeMessageBox(hwnd, GetScode(hr),IDS_OPENAB, NULL, MBS_ERROR);
return NULL;
}
if(hr) /*if we have a warning*/
{
LPMAPIERROR perr = NULL;

pses->lpVtbl->GetLastError(pses, hr, 0, &perr);
MakeMessageBox(hwnd, GetScode(hr), IDS_OPENABWARN, perr, MBS_ERROR);
MAPIFreeBuffer(perr);
}

return pabAddrBook;
}

/*
* Handles the RouteSlipbox
*
* Extracts all necessary data from GWL_USERDATA window long of
* its parent which is RouteNote dialog.
*/
BOOL CALLBACK
RouteBoxDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{

switch (msg)
{
case WM_INITDIALOG:
PopulateRouteListBox(hDlg);
return TRUE;

HANDLE_MSG(hDlg, WM_COMMAND, RBox_OnCommand);
}

return FALSE;
}


void RBox_OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
{
ADRPARM AdrParm = { 0 };
ULONG ulrectps = MAPI_TO;
LPSTR lpszTitles = "Route To ";
UINT nIndex, nIndex1;
LPSPropValue lpspv = NULL;
LPROUTEDATA pRouteData = NULL;
HRESULT hr;

switch (id)
{
case IDC_ADDRLISTACTIVE:
case IDC_ADDRLISTOLD:
/*This is to disallow hitting "Remove" button when
there is something selected in the old address list*/
if(codeNotify == LBN_SETFOCUS)
{
if(id == IDC_ADDRLISTOLD)
{
EnableWindow(GetDlgItem(hDlg, IDC_REMOVEADDR), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_MOVEUP), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_MOVEDOWN), FALSE);
ListBox_SetCurSel(GetDlgItem(hDlg, IDC_ADDRLISTACTIVE), -1);

}

else /*must be IDC_ADDRLISTACTIVE*/
{
EnableWindow(GetDlgItem(hDlg, IDC_REMOVEADDR), TRUE);
ListBox_SetCurSel(GetDlgItem(hDlg, IDC_ADDRLISTOLD), -1);
}
}


if(IDC_ADDRLISTACTIVE == id && LBN_SELCHANGE == codeNotify)
{
ConfigMoveButtons(hDlg, hwndCtl);
}

/* Details */
if(codeNotify == LBN_DBLCLK)
{
LPSPropValue pvalDtls = NULL;
ULONG cVals = 0;

nIndex = (UINT)ListBox_GetCurSel(hwndCtl);
if (nIndex == LB_ERR)
break;
nIndex1 = (UINT)ListBox_GetItemData(hwndCtl, nIndex);
if(nIndex1 == LB_ERR)
{
DebugTrace("Client: error retrieving listbox item data");
break;
}

pRouteData = (LPROUTEDATA) GetWindowLong(GetParent(hDlg), GWL_USERDATA);
Assert(pRouteData);

if(IDC_ADDRLISTACTIVE == id)
{
Assert(nIndex1 < pRouteData->palAddrListActive->cEntries);
pvalDtls = pRouteData->palAddrListActive->aEntries[nIndex1].rgPropVals;
cVals = pRouteData->palAddrListActive->aEntries[nIndex1].cValues;
}
else if(IDC_ADDRLISTOLD == id)
{
Assert(nIndex1 < pRouteData->palAddrListOld->cEntries);
pvalDtls = pRouteData->palAddrListOld->aEntries[nIndex1].rgPropVals;
cVals = pRouteData->palAddrListOld->aEntries[nIndex1].cValues;
}
else
Assert(FALSE);

Assert(pvalDtls);
pvalDtls = PpropFindProp(pvalDtls, cVals, PR_ENTRYID);

Assert(pvalDtls);
if(pvalDtls)
{
hr = pabAddrB->lpVtbl->Details(pabAddrB, (LPULONG) &hDlg, NULL,
NULL, pvalDtls->Value.bin.cb,
(LPENTRYID)pvalDtls->Value.bin.lpb, NULL,
NULL, NULL, DIALOG_MODAL);
}
}
return;

case IDC_MOVEDOWN:
case IDC_MOVEUP:
{
int nInd, nIndNew;
int nIndData, nIndDataNew;
#ifdef DEBUG
int nTotal;
#endif
char szStr[256];
ADRENTRY ae = {0};
HWND hLB = GetDlgItem(hDlg, IDC_ADDRLISTACTIVE);

Assert(hLB);

nInd = ListBox_GetCurSel(hLB);
if (nInd == LB_ERR)
break;
#ifdef DEBUG
Assert(nInd != 0 || id != IDC_MOVEUP);

nTotal = ListBox_GetCount(hLB);
if(nTotal != LB_ERR)
Assert(nInd != nTotal-1 || id != IDC_MOVEDOWN);
#endif

if(IDC_MOVEDOWN == id)
nIndNew = nInd + 1;
else
nIndNew = nInd - 1;

nIndData = ListBox_GetItemData(hLB, nInd);
if(nIndData == LB_ERR)
{
DebugTrace("Client: error retrieving listbox item data");
break;
}

nIndDataNew = ListBox_GetItemData(hLB, nIndNew);
if(nIndDataNew == LB_ERR)
{
DebugTrace("Client: error retrieving listbox item data");
break;
}

pRouteData = (LPROUTEDATA) GetWindowLong(GetParent(hDlg), GWL_USERDATA);
Assert(pRouteData);

ae = pRouteData->palAddrListActive->aEntries[nIndData];
pRouteData->palAddrListActive->aEntries[nIndData] =
pRouteData->palAddrListActive->aEntries[nIndDataNew];
pRouteData->palAddrListActive->aEntries[nIndDataNew] = ae;

/*PopulateRouteListBox(hDlg);*/

/*this works only because the two strings we swap are adjacent*/
ListBox_GetText(hLB, nInd, szStr);
ListBox_DeleteString(hLB, nInd);
ListBox_InsertString(hLB, nIndNew, szStr);

ListBox_SetItemData(hLB, nIndNew, nIndDataNew);
ListBox_SetItemData(hLB, nInd, nIndData);

ListBox_SetCurSel(GetDlgItem(hDlg, IDC_ADDRLISTACTIVE), nIndNew);
ConfigMoveButtons(hDlg, GetDlgItem(hDlg, IDC_ADDRLISTACTIVE));
break;
}


case IDC_ADDADDR:
Assert(pabAddrB != NULL);
memset(&AdrParm, 0, sizeof(AdrParm));
AdrParm.ulFlags = AB_SELECTONLY | DIALOG_MODAL;
AdrParm.lpszCaption = "Routing Slip";
AdrParm.lpszNewEntryTitle = "NEW ENTRY";
AdrParm.cDestFields = 1;
AdrParm.nDestFieldFocus = 0;
AdrParm.lppszDestTitles = &lpszTitles;
AdrParm.lpulDestComps = &ulrectps ;

pRouteData = (LPROUTEDATA) GetWindowLong(GetParent(hDlg), GWL_USERDATA);
Assert(pRouteData);

hr = pabAddrB->lpVtbl->Address(pabAddrB, (LPULONG) &hDlg, &AdrParm, &pRouteData->palAddrListActive);

if(GetScode(hr)!= MAPI_E_USER_CANCEL)
{
PopulateRouteListBox(hDlg);
SetWindowText(GetDlgItem(hDlg, IDCANCEL), "Close");
SetFocus(GetDlgItem(hDlg, IDCANCEL));

ListBox_SetCurSel(GetDlgItem(hDlg, IDC_ADDRLISTACTIVE), 0);
ConfigMoveButtons(hDlg, GetDlgItem(hDlg, IDC_ADDRLISTACTIVE));

}
return;

case IDC_REMOVEADDR:
nIndex = (UINT)ListBox_GetCurSel(GetDlgItem(hDlg, IDC_ADDRLISTACTIVE));
if (nIndex == LB_ERR)
break;

/*items position in ADRLIST is stored with the item in the listbox*/
nIndex1 = (UINT)ListBox_GetItemData(GetDlgItem(hDlg, IDC_ADDRLISTACTIVE), nIndex);
if(nIndex1 == LB_ERR)
{
DebugTrace("Client: error retrieving listbox item data");
break;
}

pRouteData = (LPROUTEDATA) GetWindowLong(GetParent(hDlg), GWL_USERDATA);
Assert(pRouteData);

/* Null out the removed item */
MAPIFreeBuffer(pRouteData->palAddrListActive->aEntries[nIndex1].rgPropVals);
pRouteData->palAddrListActive->aEntries[nIndex1].rgPropVals = NULL;
pRouteData->palAddrListActive->aEntries[nIndex1].cValues = 0;

ListBox_DeleteString(GetDlgItem(hDlg, IDC_ADDRLISTACTIVE), nIndex);

if(nIndex > 0)
--nIndex;

ListBox_SetCurSel(GetDlgItem(hDlg, IDC_ADDRLISTACTIVE), nIndex);
ConfigMoveButtons(hDlg, GetDlgItem(hDlg, IDC_ADDRLISTACTIVE));


SetWindowText(GetDlgItem(hDlg, IDCANCEL), "Close");
return;

case IDCANCEL:

EndDialog (hDlg, TRUE);
return;

default:
break;
}
}

void ConfigMoveButtons(HWND hDlg, HWND hLB)
{
int nIndex;
int nTotal;

EnableWindow(GetDlgItem(hDlg, IDC_MOVEUP), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_MOVEDOWN), FALSE);

nIndex = (UINT)ListBox_GetCurSel(hLB);
if (nIndex == LB_ERR)
return;

nTotal = ListBox_GetCount(hLB);
if(nTotal == LB_ERR)
return;

if(nIndex)
EnableWindow(GetDlgItem(hDlg, IDC_MOVEUP), TRUE);

if(nIndex < nTotal-1)
EnableWindow(GetDlgItem(hDlg, IDC_MOVEDOWN), TRUE);
}

/*
* Handles the Route Note dialog which is used for both composing
* and reading routing messages.
*
* If RotueNoteDlg is called to route an existing msg, a pointer to
* the message is passed as a lParam to WM_INITDIALOG.
*
*/

BOOL CALLBACK
RouteNoteDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
HANDLE_MSG(hDlg, WM_INITDIALOG, RT_OnInitDialog);

HANDLE_MSG(hDlg, WM_COMMAND, RT_OnCommand);

default:
break;
}
return FALSE;
}

BOOL RT_OnInitDialog(HWND hDlg, HWND hwndFocus, LPARAM lParam)
{
HRESULT hr;
ULONG cVals = 0;
LPSPropValue pvalProp = NULL;
LPSTREAM lpstreamBody = NULL;
STATSTG statstg = {0};
LPSTR lpszNoteText = NULL;
ULONG cb = 0;
LPMESSAGE pmsgRead = (LPMESSAGE)lParam;
LPROUTEDATA pRouteData = NULL;
LPMESSAGE pmsgRoute = NULL;

if(!MakeNewRouteMessage(pmsgRead, &pRouteData))
{
DebugTrace("Client: MakeNewRouteMessage failed (RT_OnInitDialog)");
goto err;
}

if (pmsgRead)
{
hr = pmsgRead->lpVtbl->SetReadFlag(pmsgRead, MAPI_DEFERRED_ERRORS);
/* RouteNote is being called to route */
/* a message in the Inbox. So, we'll initialize the */
/* form with data from pmsgRouteMsg */
pmsgRoute = pRouteData->pmsgRouteMsg;
Assert(pmsgRoute);

hr = pmsgRoute->lpVtbl->GetProps(pmsgRoute, (LPSPropTagArray)&tagaMsgProps, 0,
&cVals, &pvalProp);
Assert(cVals == EMSGPROPDIM);
if(HR_SUCCEEDED(hr))
{
if(PROP_TYPE(pvalProp[EMSG_SUBJ].ulPropTag) == PT_ERROR)
{
DebugTrace("Client: Unable to retrieve subject");
/*goto err;*/
}
else
{
SetDlgItemText(hDlg, IDC_RTSUBJECT, pvalProp[EMSG_SUBJ].Value.LPSZ);
/* //$ */
}

if(PR_CONVERSATION_INDEX == pvalProp[EMSG_CONVIDX].ulPropTag)
{
LPSPropValue pval = &pvalProp[EMSG_CONVIDX];

pRouteData->cbConvIdx = pval->Value.bin.cb;
if(MAPIAllocateBuffer(pRouteData->cbConvIdx, &pRouteData->lpbConvIdx))
{
DebugTrace("Client: MAPIAllocateBuffer failed\r\n");
pRouteData->lpbConvIdx = NULL;
pRouteData->cbConvIdx = 0;
}
else
{
CopyMemory(pRouteData->lpbConvIdx, pval->Value.bin.lpb, pRouteData->cbConvIdx);
}
}
else
{
pRouteData->lpbConvIdx = NULL;
pRouteData->cbConvIdx = 0;
}

if(PROP_TYPE(pvalProp[EMSG_BODY].ulPropTag) == PT_ERROR)
{
if(GetScode(pvalProp[EMSG_BODY].Value.l) == MAPI_E_NOT_ENOUGH_MEMORY)
{
hr = pmsgRoute->lpVtbl->OpenProperty(pmsgRoute, PR_BODY, &IID_IStream,
STGM_READ, MAPI_DEFERRED_ERRORS,
(LPUNKNOWN FAR *) &lpstreamBody);
if(S_OK != GetScode(hr))
{
DebugTraceResult(OpenProperty, hr);
goto err;
}
hr = lpstreamBody->lpVtbl->Stat(lpstreamBody, &statstg, STATFLAG_NONAME);
if(S_OK != GetScode(hr))
{
DebugTrace("IStream::Stat failed");
goto err;
}
Assert(statstg.cbSize.HighPart == 0);
if(MAPIAllocateBuffer(statstg.cbSize.LowPart + 1, (LPVOID FAR *) &lpszNoteText))
{
goto err;
}
hr = lpstreamBody->lpVtbl->Read(lpstreamBody, lpszNoteText,
statstg.cbSize.LowPart, &cb);
if(S_OK != GetScode(hr))
{
DebugTrace("IStream::Read failed");
goto err;
}
lpszNoteText[statstg.cbSize.LowPart] = '\0';
SetDlgItemText(hDlg, IDC_RTNOTE, lpszNoteText);
MAPIFreeBuffer(lpszNoteText);
lpszNoteText = NULL;
lpstreamBody->lpVtbl->Release(lpstreamBody);
lpstreamBody = NULL;
}
else /* some other error (too bad) */
{
DebugTrace("Client: error reading body");
/*//$goto err;*/
}
}
else /* everything's fine */
{
SetDlgItemText(hDlg, IDC_RTNOTE, pvalProp[EMSG_BODY].Value.LPSZ);
}
}
else /* a real error*/
{
DebugTrace("Client: error reading body and subject");
goto err;
}

if(pvalProp[EMSG_MSGFLAGS].ulPropTag == PR_MESSAGE_FLAGS)
{
if(pvalProp[EMSG_MSGFLAGS].Value.l & MSGFLAG_HASATTACH)
{
PopulateAttachList(hDlg, pRouteData);
}
}

MAPIFreeBuffer(pvalProp);
pvalProp = NULL;


Assert(pRouteData->palAddrListOld || pRouteData->palAddrListActive);
}
else
{
/* if we are here it means we are creating a new message */
pRouteData->lpbConvIdx = NULL;
pRouteData->cbConvIdx = 0;
}


SetWindowLong(hDlg, GWL_USERDATA, (LONG) pRouteData);
UlRelease(pmsgRead);
return TRUE;

err:
UlRelease(lpstreamBody);
MAPIFreeBuffer(pvalProp);
MAPIFreeBuffer(lpszNoteText);
DeInitRouteData(pRouteData);
UlRelease(pmsgRead);
MakeMessageBox(hDlg, 0, IDS_INIDIAG, NULL, MBS_ERROR);
EndDialog(hDlg, FALSE);
return TRUE;
}

void RT_OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
{
LONG cb, cLines;
ULONG nAttNum;
HRESULT hr;
LPSTR lpszSubject = NULL;
LPSTR lpszNoteText = NULL;
/* +1 for SentMailEID; +2 for conversation topic*/
SPropValue spvProp[EMSGPROPDIM+2] = {0};
LPSTREAM lpstreamBody = NULL;
HCURSOR hcOld;
UINT nFstRecip;
LPROUTEDATA pRouteData = NULL;
LPMESSAGE pmsgRoute = NULL;
LPSPropProblemArray pProblems = NULL;
ULONG cbNewConvIdx = 0;
LPBYTE lpbNewConvIdx = NULL;


switch (id)
{
case IDC_RTATTACH:
if(!CreateNewAttachment(hDlg))
{
DebugTrace("CreateNewAttachment failed");
}
return;

case IDC_RTDELATTACH:
pRouteData = (LPROUTEDATA)GetWindowLong(hDlg, GWL_USERDATA);
Assert(pRouteData);

cb = ListBox_GetCurSel(GetDlgItem(hDlg, IDC_RTATTACHLIST));
if(LB_ERR != cb)
{
nAttNum = ListBox_GetItemData(GetDlgItem(hDlg, IDC_RTATTACHLIST), cb);
if(LB_ERR == nAttNum)
{
DebugTrace("Client:GetItemData failed (routenote)");
break;
}

hr = pRouteData->pmsgRouteMsg->lpVtbl->DeleteAttach(pRouteData->pmsgRouteMsg, nAttNum, 0,
NULL, 0);
if(S_OK != GetScode(hr))
{
DebugTrace("Client: DeleteAttach failed");
break;
}
ListBox_DeleteString(GetDlgItem(hDlg, IDC_RTATTACHLIST), cb);
}

break;

case IDC_RTATTACHLIST:
cb = ListBox_GetCurSel(hwndCtl);
if(cb!=LB_ERR)
{
EnableWindow(GetDlgItem(hDlg, IDC_RTSAVEATTACH), TRUE);
EnableWindow(GetDlgItem(hDlg, IDC_RTDELATTACH), TRUE);
}
else
{
EnableWindow(GetDlgItem(hDlg, IDC_RTSAVEATTACH), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_RTDELATTACH), FALSE);
}
if(codeNotify != LBN_DBLCLK)
break;
/*fall through*/

case IDC_RTSAVEATTACH:
cb = ListBox_GetCurSel(GetDlgItem(hDlg, IDC_RTATTACHLIST));
if(LB_ERR != cb)
{
SaveAttachment(hDlg, (UINT) cb);
}
break;

case IDC_RTEDITROUTESLIP:
DialogBox (hInst, "RouteSlipBox", hDlg, RouteBoxDlgProc);
break;

case IDC_RTROUTE:
pRouteData = (LPROUTEDATA)GetWindowLong(hDlg, GWL_USERDATA);
Assert(pRouteData);

pmsgRoute = pRouteData->pmsgRouteMsg;
Assert(pmsgRoute);
if(!pRouteData->palAddrListActive && pRouteData->palAddrListOld)
{
MakeMessageBox(hDlg,0, IDS_LASTRECIP, NULL, MB_OK);
break;
}
nFstRecip = FirstRecipient(pRouteData->palAddrListActive);
if((!pRouteData->palAddrListActive && !pRouteData->palAddrListOld) || !nFstRecip)
{
MakeMessageBox(hDlg, 0, IDS_NORECIPS, NULL, MB_OK);
break;
}
hcOld = SetCursor(hWaitCur);
if (Edit_GetModify(GetDlgItem(hDlg, IDC_RTSUBJECT)))
{
cb = Edit_LineLength(GetDlgItem(hDlg, IDC_RTSUBJECT), 0);
if (MAPIAllocateBuffer (cb + 1, (LPVOID far *) & lpszSubject))
goto err;

GetDlgItemText (hDlg, IDC_RTSUBJECT, lpszSubject, (int)cb+1);
spvProp[EMSG_SUBJ].ulPropTag = PR_SUBJECT;
spvProp[EMSG_SUBJ].Value.LPSZ = lpszSubject;

}
else
{
spvProp[EMSG_SUBJ].ulPropTag = PR_NULL;
}
if (pRouteData->bNewMessage || Edit_GetModify(GetDlgItem(hDlg, IDC_RTNOTE)))
{
cLines = Edit_GetLineCount(GetDlgItem(hDlg, IDC_RTNOTE));

if (cLines)
{
/* Get the total number of bytes in the multi-line */

cb = Edit_LineIndex(GetDlgItem(hDlg, IDC_RTNOTE),(cLines - 1));
cb += Edit_LineLength(GetDlgItem(hDlg, IDC_RTNOTE), cb);

/* The next line is to account for CR-LF pairs per line. */

cb += cLines * 2;

if (MAPIAllocateBuffer (cb + 1, (LPVOID FAR *) & lpszNoteText))
goto err;

/* Get the Note Text from the edit */

GetDlgItemText (hDlg, IDC_RTNOTE, lpszNoteText, (int)cb);
spvProp[EMSG_BODY].ulPropTag = PR_BODY;
spvProp[EMSG_BODY].Value.LPSZ = lpszNoteText;
}
}
else
{
spvProp[EMSG_BODY].ulPropTag = PR_NULL;
}

/*We want a copy of the message in the "Sent Mail" folder*/
CopyMemory(&spvProp[EMSGPROPDIM], pvalSentMailEID, sizeof(SPropValue));

#ifdef DEBUG
if(pRouteData->bNewMessage)
{
Assert(pRouteData->cbConvIdx == 0);
Assert(pRouteData->lpbConvIdx == NULL);
}
#endif
if(!ScAddConversationIndex(pRouteData->cbConvIdx, pRouteData->lpbConvIdx,
&cbNewConvIdx, &lpbNewConvIdx))
{
spvProp[EMSG_CONVIDX].ulPropTag = PR_CONVERSATION_INDEX;
spvProp[EMSG_CONVIDX].Value.bin.cb = cbNewConvIdx;
spvProp[EMSG_CONVIDX].Value.bin.lpb = lpbNewConvIdx;
}
else
{
spvProp[EMSG_CONVIDX].ulPropTag = PR_NULL;
}

if(pRouteData->bNewMessage)
{
spvProp[EMSGPROPDIM+1].ulPropTag = PR_CONVERSATION_TOPIC;
if(PR_SUBJECT == spvProp[EMSG_SUBJ].ulPropTag)
{
spvProp[EMSGPROPDIM+1].Value.LPSZ = spvProp[EMSG_SUBJ].Value.LPSZ;
}
else
{
spvProp[EMSGPROPDIM+1].Value.LPSZ = "";
}
}
else
{
spvProp[EMSGPROPDIM+1].ulPropTag = PR_NULL;
}

spvProp[EMSG_MSGFLAGS].ulPropTag = PR_NULL;

hr = pmsgRoute->lpVtbl->SetProps(pmsgRoute, EMSGPROPDIM+2, spvProp, &pProblems);
MAPIFreeBuffer(lpbNewConvIdx);
lpbNewConvIdx = NULL;
if(S_OK != GetScode(hr))
{
DebugTrace("Client: SetProps failed (RouteNote)");
goto err;
}

else 
{
if(pProblems)
{ /* use nAttNum as an index for a sec */
for(nAttNum = 0; nAttNum < pProblems->cProblem; ++nAttNum)
{
switch(pProblems->aProblem[nAttNum].ulPropTag)
{
case PR_SENTMAIL_ENTRYID:
DebugTrace("Client: Error setting PR_SENTMAIL_ENTRYID");
goto err;

case PR_SUBJECT:
DebugTrace("Client: Error settting PR_SUBJECT");
goto err;
case PR_BODY:
if(MAPI_E_NOT_ENOUGH_MEMORY != pProblems->aProblem[nAttNum].scode)
{
DebugTrace("Client: Error settting PR_BODY");
goto err;
}
else /* have to use IStream */
{
hr = pmsgRoute->lpVtbl->OpenProperty(pmsgRoute, PR_BODY, &IID_IStream,
STGM_READWRITE, MAPI_DEFERRED_ERRORS,
(LPUNKNOWN FAR *) &lpstreamBody);
if(S_OK != GetScode(hr))
{
DebugTraceResult(OpenProperty, hr);
goto err;
}
hr = lpstreamBody->lpVtbl->Write(lpstreamBody, lpszNoteText, lstrlen(lpszNoteText), NULL);
if(S_OK != GetScode(hr))
{
DebugTrace("IStream::Write failed");
goto err;
}
}
break;
}
}
}
}


DelRouteProps(pRouteData);
if(!SetRouteProps(pRouteData))
{
DebugTrace("Client: SetRouteProps failed (RouteNote)");
goto err;
}

if(!SetMessageClass(pRouteData->pmsgRouteMsg, lpszSmplRTMsgClass))
{
DebugTrace("Client: SetMessageClass failed");
goto err;
}

cb = pRouteData->palAddrListActive->cEntries;
/* nFstRecip is the index of the first non NULL entry in the
palAddrListActive. So we pretend for a sec that palAddrListActive had
only one recipient to set a "TO" recipient in the pmsgRouteMsg */
pRouteData->palAddrListActive->cEntries = nFstRecip;
hr = pRouteData->pmsgRouteMsg->lpVtbl->ModifyRecipients(pRouteData->pmsgRouteMsg,
0, pRouteData->palAddrListActive);
pRouteData->palAddrListActive->cEntries = cb;
if(S_OK != GetScode(hr))
{
DebugTrace("Client: ModifyRecipients faild (routenote)");
goto err;
}

hr = pRouteData->pmsgRouteMsg->lpVtbl->SubmitMessage(pRouteData->pmsgRouteMsg, 0);
if(S_OK != GetScode(hr))
{
MakeMessageBox(hDlg, GetScode(hr), IDS_SENDERROR, NULL, MBS_ERROR);
goto err;
}
/*fall through*/

err:
case IDCANCEL:

if(IDCANCEL == id)
{
pRouteData = (LPROUTEDATA)GetWindowLong(hDlg, GWL_USERDATA);
Assert(pRouteData);
if(pRouteData->palAddrListActive||
Edit_GetModify(GetDlgItem(hDlg, IDC_RTNOTE)) ||
Edit_GetModify(GetDlgItem(hDlg, IDC_RTSUBJECT)))
{
if(MessageBox(hDlg, "Are you sure you want to close this form?",
"?", MB_YESNO) == IDNO) break;
}
}
else
{
SetCursor(hcOld);
}


MAPIFreeBuffer (lpszSubject);
MAPIFreeBuffer (lpszNoteText);
UlRelease(lpstreamBody);
lpstreamBody = NULL;
DeInitRouteData(pRouteData);
pRouteData = NULL;

EndDialog (hDlg, TRUE);
break;

default:
break;
}
}

/*
* DeInitRouteData
*
* releases pRouteData and everything that was in it
*/
void DeInitRouteData(LPROUTEDATA pRouteData)
{
if(!pRouteData) return;

FreePadrlist(pRouteData->palAddrListOld);
FreePadrlist(pRouteData->palAddrListActive);
UlRelease(pRouteData->pmsgRouteMsg);
UlRelease(pRouteData->ptblAttach);
MAPIFreeBuffer(pRouteData->lpbConvIdx);
MAPIFreeBuffer(pRouteData);
}

/* Returns a 1-based index of the first recipent.
returns 0 if all entries in the input address list are NULLs*/
UINT FirstRecipient(LPADRLIST lpAL)
{
UINT idx;

if(!lpAL || !lpAL->cEntries) return 0;

for(idx = 0; idx < lpAL->cEntries; ++idx)
if(lpAL->aEntries[idx].rgPropVals) return (idx+1);

return 0;
}

void PopulateRouteListBox(HWND hDlg)
{
LPADRENTRY lpadrent;
LPSPropValue lpsprop;
LPSTR pStr = NULL;
LPROUTEDATA pRouteData = NULL;
UINT iadrentry = 0, ilbitem = 0;



UINT yOld = MINY, yActive = MINY;
UINT cOld;
UINT cActive;
RECT rect;

pRouteData = (LPROUTEDATA) GetWindowLong(GetParent(hDlg), GWL_USERDATA);

Assert(pRouteData);

if(!pRouteData->palAddrListOld && !pRouteData->palAddrListActive)
return;

/* some arithmetic to resize the listboxes.
Make the listbox that has more entries taller*/
if(pRouteData->palAddrListOld && pRouteData->palAddrListActive)
{
cOld = pRouteData->palAddrListOld->cEntries;
cActive = pRouteData->palAddrListActive->cEntries;

yOld = (UINT)LBLENGTH * cOld / (cOld + cActive);
yActive = (UINT)LBLENGTH - yOld;
}
else if(pRouteData->palAddrListOld)
{
yActive = 0;
}
else
{
yOld = 0;
}
if(yOld < MINY)
{
yOld = MINY;
yActive = LBLENGTH - MINY;
}
else if(yActive < MINY)
{
yActive = MINY;
yOld = LBLENGTH - MINY;
}

rect.left = LBX;
rect.top = OLDY;
rect.right = DELTAX;
rect.bottom = yOld;

MapDialogRect(hDlg, &rect);

SetWindowPos(GetDlgItem(hDlg, IDC_ADDRLISTOLD), 0,
rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW);

rect.left = LBX;
rect.top = OLDY + yOld + DELTA;
rect.right = DELTAX;
rect.bottom = yActive;

MapDialogRect(hDlg, &rect);

SetWindowPos(GetDlgItem(hDlg, IDC_ADDRLISTACTIVE), 0,
rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW);

ListBox_ResetContent(GetDlgItem(hDlg, IDC_ADDRLISTOLD));
ListBox_ResetContent(GetDlgItem(hDlg, IDC_ADDRLISTACTIVE));

if(pRouteData->palAddrListOld)
{

for(iadrentry = 0, lpadrent = pRouteData->palAddrListOld->aEntries;
lpadrent < pRouteData->palAddrListOld->aEntries+pRouteData->palAddrListOld->cEntries;
++lpadrent, ++iadrentry)
{
if(!lpadrent->rgPropVals) continue;

/* IAddrBook::Addres doesn't guarantee the order of the props.
So we have to loop through all of them */
for(lpsprop = lpadrent->rgPropVals;
lpsprop < lpadrent->rgPropVals + lpadrent->cValues; ++lpsprop)
{
if(lpsprop->ulPropTag == PR_DISPLAY_NAME)
{
pStr = lpsprop->Value.LPSZ;
ilbitem = ListBox_AddString(GetDlgItem(hDlg, IDC_ADDRLISTOLD), pStr);
if(LB_ERR != ilbitem)
ListBox_SetItemData(GetDlgItem(hDlg, IDC_ADDRLISTOLD), ilbitem, iadrentry);
break;
}
}
}
}
if(pRouteData->palAddrListActive)
{
for(iadrentry = 0, lpadrent = pRouteData->palAddrListActive->aEntries;
lpadrent < pRouteData->palAddrListActive->aEntries+pRouteData->palAddrListActive->cEntries;
++lpadrent, ++iadrentry)
{
if(!lpadrent->rgPropVals) continue;

/* IAddrBook::Addres doesn't guarantee the order of the props.
So we have to loop through all of them */
for(lpsprop = lpadrent->rgPropVals;
lpsprop < lpadrent->rgPropVals + lpadrent->cValues; ++lpsprop)
{
if(lpsprop->ulPropTag == PR_DISPLAY_NAME)
{
pStr = lpsprop->Value.LPSZ;
ilbitem = ListBox_AddString(GetDlgItem(hDlg, IDC_ADDRLISTACTIVE), pStr);
if(LB_ERR != ilbitem) /* save the position of the entry in the addrlist for future refs*/
ListBox_SetItemData(GetDlgItem(hDlg, IDC_ADDRLISTACTIVE), ilbitem, iadrentry);
break;
}
}
}
}
}


/* Get the index of the current recip and total number of recipients
* from the routing slip of the message
*/
BOOL GetRouteIndices(LPROUTEDATA pRouteData)
{
HRESULT hr;
LPSPropTagArray lpsptProp = NULL;
LPSPropValue lpspv = NULL ;
ULONG cProps = 0;
LPMESSAGE pmsg = NULL;
MAPINAMEID nmidRTPR[2];
MAPINAMEID *pnmidRTPR[2] = {&nmidRTPR[0], &nmidRTPR[1]};

nmidRTPR[0].lpguid = (LPGUID)&PS_PUBLIC_STRINGS;
nmidRTPR[0].ulKind = MNID_STRING;
nmidRTPR[0].Kind.lpwstrName = L"CURRENT_RT_RECIP";
nmidRTPR[1].lpguid = (LPGUID)&PS_PUBLIC_STRINGS;
nmidRTPR[1].ulKind = MNID_STRING;
nmidRTPR[1].Kind.lpwstrName = L"TOTAL_RT_RECIP";

Assert(pRouteData);

pmsg = pRouteData->pmsgRouteMsg;
Assert(pmsg);
hr = pmsg->lpVtbl->GetIDsFromNames(pmsg, 2, pnmidRTPR, 0, &lpsptProp);
if(S_OK != GetScode(hr))
{
DebugTraceResult(GetIDsFromNames, hr);
goto err;
}

lpsptProp->aulPropTag[0] = PROP_TAG(PT_LONG, PROP_ID(lpsptProp->aulPropTag[0]));
lpsptProp->aulPropTag[1] = PROP_TAG(PT_LONG, PROP_ID(lpsptProp->aulPropTag[1]));

/* save for later use in setrouteindices() */
pRouteData->tagCurrent = lpsptProp->aulPropTag[0];
pRouteData->tagTotal = lpsptProp->aulPropTag[1];

hr = pmsg->lpVtbl->GetProps(pmsg, lpsptProp, 0, &cProps, &lpspv);
if(S_OK != GetScode(hr))
{
DebugTraceResult(GetProps, hr);
goto err;
}

Assert(lpspv);
pRouteData->nCurrentRouteRecip = lpspv[0].Value.l;
pRouteData->nTotalRouteRecip = lpspv[1].Value.l;


err:
MAPIFreeBuffer(lpsptProp);
MAPIFreeBuffer(lpspv);
return !hr;
}

BOOL SetRouteIndices(LPROUTEDATA pRouteData)
{

HRESULT hr;
LPSPropTagArray lpsptProp = NULL;
SPropValue spvRTProp[2];
LPSPropProblemArray pProblems = NULL;

MAPINAMEID nmidRTPR[2];
MAPINAMEID *pnmidRTPR[2] = {&nmidRTPR[0], &nmidRTPR[1]};

Assert(pRouteData);

ZeroMemory(&spvRTProp, 2*sizeof(SPropValue));

/* if this is a new message, get PropIDs */
if(pRouteData->bNewMessage)
{
nmidRTPR[0].lpguid = (LPGUID)&PS_PUBLIC_STRINGS;
nmidRTPR[0].ulKind = MNID_STRING;
nmidRTPR[0].Kind.lpwstrName = L"CURRENT_RT_RECIP";
nmidRTPR[1].lpguid = (LPGUID)&PS_PUBLIC_STRINGS;
nmidRTPR[1].ulKind = MNID_STRING;
nmidRTPR[1].Kind.lpwstrName = L"TOTAL_RT_RECIP";

hr = pRouteData->pmsgRouteMsg->lpVtbl->GetIDsFromNames(pRouteData->pmsgRouteMsg, 2, pnmidRTPR, MAPI_CREATE, &lpsptProp);
if(S_OK != GetScode(hr))
{
DebugTraceResult(GetIDsFromNames, hr);
goto err;
}

lpsptProp->aulPropTag[0] = PROP_TAG(PT_LONG, PROP_ID(lpsptProp->aulPropTag[0]));
lpsptProp->aulPropTag[1] = PROP_TAG(PT_LONG, PROP_ID(lpsptProp->aulPropTag[1]));

spvRTProp[0].ulPropTag = lpsptProp->aulPropTag[0];
spvRTProp[1].ulPropTag = lpsptProp->aulPropTag[1];
}
else /* if we created this msg from one in the inbox, we already have the PropIDs*/
{
spvRTProp[0].ulPropTag = pRouteData->tagCurrent;
spvRTProp[1].ulPropTag = pRouteData->tagTotal;

}

spvRTProp[0].Value.l = pRouteData->nCurrentRouteRecip;
spvRTProp[1].Value.l = pRouteData->nTotalRouteRecip;

hr = pRouteData->pmsgRouteMsg->lpVtbl->SetProps(pRouteData->pmsgRouteMsg, 2, spvRTProp, &pProblems);
if(S_OK != GetScode(hr))
{
if(pProblems)
{
DebugTraceProblems("Client: ", pProblems);
MAPIFreeBuffer(pProblems);
pProblems = NULL;
}
DebugTraceResult(SetProps, hr);
goto err;
}


err:
MAPIFreeBuffer(lpsptProp);

return !hr;
}


void InitRouteNameIDArray(ULONG iRecip, LPMAPINAMEID lpPropNames)
{
ULONG idx;

Assert (lpPropNames);
for(idx = 0; idx < ROUTEPROPSETDIM; ++idx)
{
lpPropNames[idx].lpguid = (LPGUID)lpguidA[idx];
lpPropNames[idx].ulKind = MNID_ID;
lpPropNames[idx].Kind.lID = iRecip;
}
}

BOOL GetRoutePropTagArray(ULONG iRecip, LPMESSAGE lpM, LPSPropTagArray FAR * lppspta)
{
MAPINAMEID nmidA[ROUTEPROPSETDIM];
LPMAPINAMEID lppnmidA[5];
HRESULT hr;
ULONG *pulptPropTag = NULL;
UINT ulidx = 0;

Assert(lpM);

/*First create the 5 prop names and get IDs for them*/
for(ulidx = 0; ulidx < ROUTEPROPSETDIM; ++ulidx)
lppnmidA[ulidx] = &nmidA[ulidx];
InitRouteNameIDArray(iRecip, nmidA);
hr = lpM->lpVtbl->GetIDsFromNames(lpM, ROUTEPROPSETDIM, lppnmidA,
MAPI_CREATE, lppspta);

if(S_OK != GetScode(hr))
{
DebugTraceResult(GetIDsFromNames, hr);
return FALSE;
}
Assert((*lppspta)->cValues == ROUTEPROPSETDIM);

/*Now add right prop types */
ulidx = 0;
for(pulptPropTag = (*lppspta)->aulPropTag;
pulptPropTag < (*lppspta)->aulPropTag + ROUTEPROPSETDIM; ++pulptPropTag, ++ulidx)
{
*pulptPropTag = PROP_TAG(ulRoutePropTypes[ulidx], PROP_ID(*pulptPropTag));
}

return TRUE;
}


/* the output PropTag array contains nTotalRecip * ROUTEPROPSETDIM + 1
* prop tags. The last one is PR_NULL.
*/
BOOL GetRoutePropTagArrayFast(ULONG nTotalRecip, LPMESSAGE lpM, BOOL fCreate,
LPSPropTagArray FAR * lppspta)
{
LPMAPINAMEID pnmid = NULL;
LPMAPINAMEID * ppnmid = NULL;
HRESULT hr;
ULONG *pulptPropTag = NULL;
UINT ulidx = 0;
/*//$ hack!!! +1 is to save a spot for PR_RECIPIENT_TYPE */
UINT nTotalProps = nTotalRecip * ROUTEPROPSETDIM + 1;


Assert(lpM);

if(hr = ResultFromScode(MAPIAllocateBuffer(nTotalProps * sizeof(MAPINAMEID),
(LPVOID FAR *)&pnmid)))
return FALSE;

ZeroMemory(pnmid, nTotalProps * sizeof(MAPINAMEID));

if(hr = ResultFromScode(MAPIAllocateBuffer(nTotalProps * sizeof(LPMAPINAMEID),
(LPVOID FAR *)&ppnmid)))
goto err;

for(ulidx = 0; ulidx < nTotalProps; ++ulidx)
ppnmid[ulidx] = &pnmid[ulidx];

for(ulidx = 0; ulidx < nTotalRecip; ++ulidx )
InitRouteNameIDArray(ulidx, &pnmid[ulidx * ROUTEPROPSETDIM]);

//put someting reasonable in
pnmid[nTotalProps-1].lpguid = (LPGUID)lpguidA[0];
pnmid[nTotalProps-1].ulKind = MNID_ID;
pnmid[nTotalProps-1].Kind.lID = 0;

hr = lpM->lpVtbl->GetIDsFromNames(lpM, nTotalProps, ppnmid,
fCreate? MAPI_CREATE : 0, lppspta);
if(hr) /* treat warnings as errors */
goto err;

Assert((*lppspta)->cValues == nTotalProps);

ulidx = 0;
for(pulptPropTag = (*lppspta)->aulPropTag;
pulptPropTag < (*lppspta)->aulPropTag + nTotalProps - 1;
++pulptPropTag , ++ulidx)
{
*pulptPropTag = PROP_TAG(ulRoutePropTypes[ulidx % ROUTEPROPSETDIM],
PROP_ID(*pulptPropTag));
}

*pulptPropTag = PR_NULL;

err:
MAPIFreeBuffer(pnmid);
MAPIFreeBuffer(ppnmid);

DebugTraceResult(ROUTECLI_GetRoutePropTagArray, hr);
return (hr? FALSE : TRUE);
}

BOOL OpenOutFolder(HWND hWnd, LPMAPIFOLDER FAR * lppF)
{
LPMAPIFOLDER lpfOutF = NULL;
HRESULT hr;
LPSPropValue lpspvFEID = NULL;
ULONG ulObjType = 0;

Assert(pmdb);

*lppF = NULL;
hr = HrGetOneProp((LPMAPIPROP) pmdb, PR_IPM_OUTBOX_ENTRYID, &lpspvFEID);
if(hr)
{
DebugTrace("Client: HrGetOneProp failed (OpneOutFolder)");
goto err;
}

Assert(lpspvFEID->ulPropTag == PR_IPM_OUTBOX_ENTRYID);

hr = pmdb->lpVtbl->OpenEntry(pmdb, lpspvFEID->Value.bin.cb,
(LPENTRYID)lpspvFEID->Value.bin.lpb, NULL,
MAPI_MODIFY | MAPI_DEFERRED_ERRORS,
&ulObjType, (LPUNKNOWN FAR *) &lpfOutF);
if(S_OK != GetScode(hr))
{
DebugTrace("Client: OpenEntry (OpenOutFolder)");
goto err;
}

*lppF = lpfOutF;


err:
MAPIFreeBuffer(lpspvFEID);
if(hr)
MakeMessageBox(hWnd, GetScode(hr), IDS_OPENOUTB, NULL, MBS_ERROR);

return !hr;

}

BOOL CreateOutMessage(LPMESSAGE FAR * lpmM)
{

LPMESSAGE lpmResM = NULL;
HRESULT hr;

Assert(pfldOutBox);

hr = pfldOutBox->lpVtbl->CreateMessage(pfldOutBox, NULL, MAPI_DEFERRED_ERRORS,
&lpmResM);

if(S_OK != GetScode(hr)) return FALSE;

*lpmM = lpmResM;

return TRUE;
}

BOOL MakeNewRouteMessage(LPMESSAGE pmsgRead, LPROUTEDATA FAR * ppRouteData)
{
HRESULT hr;
LPROUTEDATA pRouteData = NULL;
LPSPropProblemArray pProblems = NULL;

if(MAPIAllocateBuffer(sizeof(ROUTEDATA), (LPVOID FAR *) &pRouteData))
{
return FALSE;
}
ZeroMemory(pRouteData, sizeof(ROUTEDATA));

if(!CreateOutMessage(&pRouteData->pmsgRouteMsg))
{
goto err;
}

if(!pmsgRead) /*if called to create a new message*/
{
pRouteData->bNewMessage = TRUE;
*ppRouteData = pRouteData;
return TRUE;
}

pRouteData->bNewMessage = FALSE;

/* include attachments*/
sptExcludedProps.cValues = EXCLUDED_PROPS_ON_REPLY - 1;

hr = pmsgRead->lpVtbl->CopyTo(pmsgRead, 0, NULL,
(LPSPropTagArray)&sptExcludedProps,
0, NULL, &IID_IMessage,
pRouteData->pmsgRouteMsg, 0, &pProblems);

if(HR_FAILED(hr))
{
if(pProblems)

{
DebugTraceProblems("Client: ", pProblems);
MAPIFreeBuffer(pProblems);
pProblems = NULL;
}

goto err;
}

if(!GetRouteIndices(pRouteData))
{
goto err;
}

if(!GetRouteAddrLists(pRouteData))
{
goto err;
}

*ppRouteData = pRouteData;

return TRUE;

err:
UlRelease(pRouteData->pmsgRouteMsg);
MAPIFreeBuffer(pRouteData);
*ppRouteData = NULL;
return FALSE;
}

/*Assumes that members nCurrentRouteRecip and nTotalRouteRecip were succesfully
*set. Initializes palAddrListActive and palAddrListOld.
*/

BOOL GetRouteAddrLists(LPROUTEDATA pRouteData)
{
LPSPropTagArray lpspta = NULL;
// SizedSPropTagArray((ROUTEPROPSETDIM + 1), sspta);
LPADRLIST lpalOld = NULL;
LPADRLIST lpalActive = NULL;
ULONG cbal = 0;
HRESULT hr;
UINT ulidx, ulidx1;
ULONG cActiveRecip ;
LPMESSAGE pmsg;
ULONG cvalAllProps = 0;
LPSPropValue pvalAllProps = NULL;

Assert(pRouteData);
Assert(!pRouteData->palAddrListActive);
Assert(!pRouteData->palAddrListOld);

pmsg = pRouteData->pmsgRouteMsg;
Assert(pmsg);

if(!GetRoutePropTagArrayFast(pRouteData->nTotalRouteRecip,
pmsg,
FALSE,
&lpspta))
goto err;

/* hack!! the output PropTag array contains nTotalRecip * ROUTEPROPSETDIM + 1
* prop tags. The last one is PR_NULL. We do this to reserve a spot for
* PR_RECIPIENT_TYPE
*/

hr = pmsg->lpVtbl->GetProps(pmsg, lpspta, 0, &cvalAllProps, &pvalAllProps);
MAPIFreeBuffer(lpspta);
lpspta=NULL;
if(HR_FAILED(hr))
goto err;

Assert(cvalAllProps == pRouteData->nTotalRouteRecip * ROUTEPROPSETDIM + 1);

//
// In case somebody does not handle PR_NULL in GetProps
//
pvalAllProps[cvalAllProps - 1].ulPropTag = PR_NULL;

cActiveRecip = pRouteData->nTotalRouteRecip - pRouteData->nCurrentRouteRecip;

/*First do the recipients already routed to */
cbal = CbNewADRLIST(pRouteData->nCurrentRouteRecip);
if (MAPIAllocateBuffer(cbal, &lpalOld))
goto err;
ZeroMemory(lpalOld, cbal);

lpalOld->cEntries = pRouteData->nCurrentRouteRecip;

for(ulidx = 0; ulidx < pRouteData->nCurrentRouteRecip; ++ulidx)
{
LPSPropValue pval = NULL;

hr = ResultFromScode(ScDupPropset(ROUTEPROPSETDIM +1,
&pvalAllProps[ulidx * ROUTEPROPSETDIM],
MAPIAllocateBuffer,
&pval));

if(hr)
goto err;

lpalOld->aEntries[ulidx].rgPropVals = pval;
lpalOld->aEntries[ulidx].cValues = ROUTEPROPSETDIM + 1;

for(ulidx1 = 0; ulidx1 < ROUTEPROPSETDIM; ++ulidx1)
{
pval[ulidx1].ulPropTag = sptRouteProps.aulPropTag[ulidx1];
pval[ulidx1].dwAlignPad = 0;
}
/*Have to set RECIPIENT_TYPE in order for IAddressBook::Address to show the
recipient in one of its listboxes*/
pval[ROUTEPROPSETDIM].ulPropTag = PR_RECIPIENT_TYPE;
pval[ROUTEPROPSETDIM].dwAlignPad = 0;
pval[ROUTEPROPSETDIM].Value.l = MAPI_TO;

}

if(cActiveRecip == 0)
{
pRouteData->palAddrListOld = lpalOld;
pRouteData->palAddrListActive = NULL;
goto ret;
}

/*Now the same for palAddrListActive*/
cbal = CbNewADRLIST(cActiveRecip);
if (MAPIAllocateBuffer(cbal, &lpalActive))
goto err;
memset(lpalActive, 0, cbal);
lpalActive->cEntries = cActiveRecip;
for(ulidx = 0; ulidx < cActiveRecip; ++ulidx)
{
LPSPropValue pval = NULL;

hr = ResultFromScode(ScDupPropset(ROUTEPROPSETDIM +1,
&pvalAllProps[(ulidx + pRouteData->nCurrentRouteRecip) *
ROUTEPROPSETDIM],
MAPIAllocateBuffer,
&pval));

if(hr)
goto err;

lpalActive->aEntries[ulidx].rgPropVals = pval;
lpalActive->aEntries[ulidx].cValues = ROUTEPROPSETDIM + 1;

for(ulidx1 = 0; ulidx1 < ROUTEPROPSETDIM; ++ulidx1)
{
pval[ulidx1].ulPropTag = sptRouteProps.aulPropTag[ulidx1];
pval[ulidx1].dwAlignPad = 0;
}
/*Have to set RECIPIENT_TYPE in order for IAddressBook::Address to show the
recipient in one of its listboxes*/
pval[ROUTEPROPSETDIM].ulPropTag = PR_RECIPIENT_TYPE;
pval[ROUTEPROPSETDIM].dwAlignPad = 0;
pval[ROUTEPROPSETDIM].Value.l = MAPI_TO;

}

pRouteData->palAddrListOld = lpalOld;
pRouteData->palAddrListActive = lpalActive;

ret:
MAPIFreeBuffer(pvalAllProps);
return TRUE;
err:
FreePadrlist(lpalOld);
FreePadrlist(lpalActive);
MAPIFreeBuffer(pvalAllProps);

return FALSE;
}


/*deletes all the props corresponding to the entries in palAddrListActive*/
BOOL DelRouteProps(LPROUTEDATA pRouteData)
{
UINT ulidx;
LPSPropTagArray lpsptaRouteTags = NULL;
LPSPropProblemArray lpsppaProblems = NULL;
HRESULT hr = hrSuccess;

if(!pRouteData->palAddrListActive) return TRUE;


Assert(pRouteData);
Assert(pRouteData->pmsgRouteMsg);

for(ulidx = pRouteData->nCurrentRouteRecip; ulidx < pRouteData->nTotalRouteRecip; ++ulidx)
{
if(!GetRoutePropTagArray(ulidx, pRouteData->pmsgRouteMsg, &lpsptaRouteTags))
{
/*
//$ even if it gives an error still have to go through all of them*/
continue;
}
hr = pRouteData->pmsgRouteMsg->lpVtbl->DeleteProps(pRouteData->pmsgRouteMsg, lpsptaRouteTags, &lpsppaProblems);
/* even if it gives an error still have to go through all of them*/
if(lpsppaProblems)
{
DebugTraceProblems("Client:", lpsppaProblems);
MAPIFreeBuffer(lpsppaProblems);
lpsppaProblems = NULL;
}

MAPIFreeBuffer(lpsptaRouteTags);

lpsptaRouteTags = NULL;

}

return (S_OK == GetScode(hr));
}

BOOL SetRouteProps(LPROUTEDATA pRouteData)
{
//SPropValue pspvRouteProps[ROUTEPROPSETDIM];
LPSPropTagArray lpsptaRouteTags = NULL;
ULONG cRouteRecip;
LPADRENTRY lpae = NULL;
LPSPropValue lpspv = NULL;
UINT ulidx;
LPSPropProblemArray lpsppaProblems = NULL;
HRESULT hr;
UINT cNewRecips = 0;
LPSPropValue pvalAll = NULL;
LPSPropValue pval = NULL;

Assert(pRouteData);
if(!pRouteData->palAddrListActive) return TRUE;

cRouteRecip = pRouteData->nCurrentRouteRecip;

if(!GetRoutePropTagArrayFast(
cRouteRecip + pRouteData->palAddrListActive->cEntries,
pRouteData->pmsgRouteMsg,
TRUE,
&lpsptaRouteTags))
{
hr = ResultFromScode(E_FAIL);
goto err;
}

if(hr = MAPIAllocateBuffer(
pRouteData->palAddrListActive->cEntries * ROUTEPROPSETDIM *
sizeof(SPropValue), (LPVOID *) &pvalAll))
goto err;

ZeroMemory(pvalAll, pRouteData->palAddrListActive->cEntries *
ROUTEPROPSETDIM * sizeof(SPropValue));

/* get required properties missing in AddrlistActive*/
hr = pabAddrB->lpVtbl->PrepareRecips(pabAddrB, 0, (LPSPropTagArray)&sptRouteProps,
pRouteData->palAddrListActive);
if(S_OK != GetScode(hr))
{
DebugTrace("Client: PrepareRecips failed");
goto err;
}

for(lpae = pRouteData->palAddrListActive->aEntries,
pval = pvalAll, cNewRecips = 0;
lpae < pRouteData->palAddrListActive->aEntries +
pRouteData->palAddrListActive->cEntries;
++lpae )
{
if(!lpae->rgPropVals)
{
continue;
}

++cNewRecips;

Assert(lpae->rgPropVals[ROUTEPROPSET_EMAIL_ADDRESS].ulPropTag == PR_EMAIL_ADDRESS);
pval[ROUTEPROPSET_EMAIL_ADDRESS].Value.LPSZ =
lpae->rgPropVals[ROUTEPROPSET_EMAIL_ADDRESS].Value.LPSZ;

Assert(lpae->rgPropVals[ROUTEPROPSET_ADDRTYPE].ulPropTag == PR_ADDRTYPE);
pval[ROUTEPROPSET_ADDRTYPE].Value.LPSZ =
lpae->rgPropVals[ROUTEPROPSET_ADDRTYPE].Value.LPSZ;

Assert(lpae->rgPropVals[ROUTEPROPSET_DISPLAY_NAME].ulPropTag == PR_DISPLAY_NAME);
pval[ROUTEPROPSET_DISPLAY_NAME].Value.LPSZ =
lpae->rgPropVals[ROUTEPROPSET_DISPLAY_NAME].Value.LPSZ;

Assert(lpae->rgPropVals[ROUTEPROPSET_ENTRYID].ulPropTag == PR_ENTRYID);
pval[ROUTEPROPSET_ENTRYID].Value =
lpae->rgPropVals[ROUTEPROPSET_ENTRYID].Value;

Assert(lpae->rgPropVals[ROUTEPROPSET_SEARCH_KEY].ulPropTag == PR_SEARCH_KEY);
pval[ROUTEPROPSET_SEARCH_KEY].Value =
lpae->rgPropVals[ROUTEPROPSET_SEARCH_KEY].Value;

pval += ROUTEPROPSETDIM;
}


for(ulidx = cRouteRecip * ROUTEPROPSETDIM, pval = pvalAll;
ulidx < (cRouteRecip + cNewRecips)*ROUTEPROPSETDIM;
++pval, ++ulidx)
{
pval->ulPropTag = lpsptaRouteTags->aulPropTag[ulidx];
}

hr = pRouteData->pmsgRouteMsg->lpVtbl->SetProps(
pRouteData->pmsgRouteMsg,
cNewRecips * ROUTEPROPSETDIM,
pvalAll,
&lpsppaProblems);



if(S_OK != GetScode(hr))
{

DebugTraceResult(SetProps, hr);
goto err;
}

if(lpsppaProblems)
{
DebugTraceProblems("Client: SetProps", lpsppaProblems);
MAPIFreeBuffer(lpsppaProblems);
lpsppaProblems = NULL;
}


MAPIFreeBuffer(lpsptaRouteTags);
lpsptaRouteTags = NULL;


pRouteData->nTotalRouteRecip = pRouteData->nCurrentRouteRecip + cNewRecips;

++(pRouteData->nCurrentRouteRecip);
if(!SetRouteIndices(pRouteData))
{
hr = ResultFromScode(E_FAIL);
goto err;
}


err:
MAPIFreeBuffer(lpsptaRouteTags);
MAPIFreeBuffer(lpsppaProblems);
MAPIFreeBuffer(pvalAll);
return ( hr ? FALSE : TRUE);
}

BOOL SetMessageClass(LPMESSAGE lpM, LPSTR lpszClass)
{
HRESULT hr;
SPropValue spvProp;

spvProp.ulPropTag = PR_MESSAGE_CLASS;
spvProp.dwAlignPad = 0;
spvProp.Value.LPSZ = lpszClass;

Assert(lpM);

hr = HrSetOneProp((LPMAPIPROP)lpM, &spvProp);
if(hr)
{
DebugTrace("Client: HrSetOneProp failed (SetMessageClass)");
return FALSE;
}

return TRUE;
}

/* this functions works correctly only for received (saved) msgs*/
/*BOOL HasAttachment(LPMESSAGE lpM)
{
HRESULT hr;
LPSPropValue lpspvProp = NULL;
LONG lflags = 0;

Assert(lpM);

hr = HrGetOneProp((LPMAPIPROP) lpM, PR_MESSAGE_FLAGS, &lpspvProp);
if(hr)
{
DebugTrace("Client: HrGetOneProp failed (hasattachment)");
goto err;
}

lflags = lpspvProp->Value.l;

err:
MAPIFreeBuffer(lpspvProp);

return lflags & MSGFLAG_HASATTACH;
}*/

BOOL PopulateAttachList(HWND hDlg, LPROUTEDATA pRouteData)
{
SizedSPropTagArray (1, sptAttachNum) = { 1, {PR_ATTACH_NUM} };
LPSRowSet lprsRows = NULL;
LPATTACH lpAttach = NULL;
HRESULT hr;
UINT idx, lbidx;
LPSPropValue lpProp = NULL;
SPropValue spvPosProp = {0};
spvPosProp.ulPropTag = PR_RENDERING_POSITION;
spvPosProp.Value.l = -1;

Assert(pRouteData);
Assert(pRouteData->pmsgRouteMsg);

hr = pRouteData->pmsgRouteMsg->lpVtbl->
GetAttachmentTable(pRouteData->pmsgRouteMsg, 0,
&pRouteData->ptblAttach);
if(S_OK != hr)
{
DebugTrace("Client: GetAttachmentTable failed (PopulateAttachList)");
goto err;
}

hr = HrQueryAllRows(pRouteData->ptblAttach, (LPSPropTagArray)&sptAttachNum, NULL,
NULL, 0, &lprsRows);
if(S_OK != hr)
{
DebugTrace("Client: QueryAllRows failed (PopulateAttachList)");
goto err;
}

if(!lprsRows || !(lprsRows->cRows)) goto err;

for(idx = 0; idx < lprsRows->cRows; ++idx)
{
Assert(lprsRows->aRow[idx].cValues == 1);
Assert(lprsRows->aRow[idx].lpProps->ulPropTag == PR_ATTACH_NUM);

hr = pRouteData->pmsgRouteMsg->lpVtbl->
OpenAttach(pRouteData->pmsgRouteMsg,
lprsRows->aRow[idx].lpProps->Value.l,
NULL, MAPI_MODIFY | MAPI_DEFERRED_ERRORS, &lpAttach);
if(S_OK != hr)
{
DebugTrace("Client: openattach failed (PopulateAttachList)");
goto err;
}

hr = HrGetOneProp((LPMAPIPROP)lpAttach, PR_DISPLAY_NAME, &lpProp);
if(hr)
{
DebugTrace("Client: HrGetOneProp failed (PopulateAttachList)");
goto err;
}
lbidx = ListBox_AddString(GetDlgItem(hDlg, IDC_RTATTACHLIST), lpProp->Value.LPSZ);
if(LB_ERR != lbidx)
{
ListBox_SetItemData(GetDlgItem(hDlg, IDC_RTATTACHLIST), lbidx,
lprsRows->aRow[idx].lpProps->Value.l);
}

MAPIFreeBuffer(lpProp);
lpProp = NULL;

/* in received msgs attachment positions are not -1 any more.
Set them to -1 again*/
hr = HrSetOneProp((LPMAPIPROP)lpAttach, &spvPosProp);
if(!hr)
{
hr = lpAttach->lpVtbl->SaveChanges(lpAttach, 0);
}
lpAttach->lpVtbl->Release(lpAttach);
lpAttach = NULL;
}

err:

FreeProws(lprsRows);
lprsRows = NULL;

UlRelease(lpAttach);

MAPIFreeBuffer(lpProp);

return (S_OK == GetScode(hr));
}

BOOL CreateNewAttachment(HWND hDlg)
{
OPENFILENAME ofn;
char szFileName[MAX_PATH] = "";
char szFilter[256];
static char szFileTitle[16] = "";
static char szDirName[256] = "";
LPSTR lpszEndPath;
UINT idx, lbidx;
HRESULT hr;
LPROUTEDATA pRouteData = NULL;

enum { REND_POS, PATH_NAME, ATT_METHOD, DISP_NAME, ATT_FILENAME, ATT_DIM};

SizedSPropTagArray(ATT_DIM , sptAttachTags) =
{ ATT_DIM,
{ PR_RENDERING_POSITION, PR_ATTACH_PATHNAME,
PR_ATTACH_METHOD, PR_DISPLAY_NAME, PR_ATTACH_FILENAME }
};
SPropValue spvAttachProps[ATT_DIM];

LPATTACH lpAttach = NULL;
ULONG ulAttachNum = 0;

pRouteData = (LPROUTEDATA)GetWindowLong(hDlg, GWL_USERDATA);
Assert(pRouteData);

#ifndef WIN16
if (!szDirName[0])
GetCurrentDirectory (255, (LPSTR) szDirName);
else
#endif
lstrcpy (szFileName, szFileTitle);

if(!LoadString(hInst, IDS_FILTER, szFilter, sizeof(szFilter)))
{
DebugTrace("Client: failed to load a string");
return FALSE;
}

for (idx = 0; szFilter[idx] != '\0'; idx++)
if (szFilter[idx] == '|')
szFilter[idx] = '\0';

ofn.lStructSize = sizeof (OPENFILENAME);
ofn.hwndOwner = 0;
ofn.hInstance = 0;
ofn.lpstrFilter = szFilter;
ofn.lpstrCustomFilter = NULL;
ofn.nMaxCustFilter = 0L;
ofn.nFilterIndex = 1L;
ofn.lpstrFile = szFileName;
ofn.nMaxFile = 256;
ofn.lpstrFileTitle = szFileTitle;
ofn.nMaxFileTitle = 16;
ofn.lpstrInitialDir = szDirName;
ofn.lpstrTitle = "Attach";
ofn.nFileOffset = 0;
ofn.nFileExtension = 0;
ofn.lpstrDefExt = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

if (!GetOpenFileName (&ofn))
return FALSE;

/* Save the directory for the next time we call this */

lstrcpy (szDirName, szFileName);
if (lpszEndPath = SzFindLastCh(szDirName, '\\'))
*(lpszEndPath) = '\0';

hr = pRouteData->pmsgRouteMsg->lpVtbl->CreateAttach(pRouteData->pmsgRouteMsg,
NULL, MAPI_DEFERRED_ERRORS, &ulAttachNum, &lpAttach);
if(S_OK != GetScode(hr))
{
DebugTrace("Client: CreateAttach failed (CreateNewAttach)");
return FALSE;
}

for(idx = 0; idx < ATT_DIM; ++idx)
{
spvAttachProps[idx].ulPropTag = sptAttachTags.aulPropTag[idx];
spvAttachProps[idx].dwAlignPad = 0;
}

spvAttachProps[REND_POS].Value.l = -1;
spvAttachProps[PATH_NAME].Value.LPSZ = szFileName;
spvAttachProps[ATT_METHOD].Value.l = ATTACH_BY_REF_RESOLVE;
/* ATTACH_BY_REF_RESOLVE means the spooler wiil reslove the attachment into ATTACH_BY_VALUE
and place the attachment data in PR_ATTACH_DATA_BIN */
spvAttachProps[DISP_NAME].Value.LPSZ = szFileTitle;
spvAttachProps[ATT_FILENAME].Value.LPSZ = szFileTitle;

hr = lpAttach->lpVtbl->SetProps(lpAttach, ATT_DIM, spvAttachProps, NULL);
if(S_OK != GetScode(hr))
{
DebugTrace("Client: SetProp failed (createnewattachment)");
goto err;
}

hr = lpAttach->lpVtbl->SaveChanges(lpAttach, MAPI_DEFERRED_ERRORS);
if(S_OK != GetScode(hr))
{
DebugTrace("Client: SaveChanges failed (createnewattachment)");
goto err;
}

lbidx = ListBox_AddString(GetDlgItem(hDlg, IDC_RTATTACHLIST), szFileTitle);
if(LB_ERR != lbidx)
{
/*Store the attachment # with its name in the listbox*/
ListBox_SetItemData(GetDlgItem(hDlg, IDC_RTATTACHLIST), lbidx,
ulAttachNum);
}

err:
UlRelease(lpAttach);

return (S_OK == GetScode(hr));
}

void SaveAttachment( HWND hDlg, UINT itemindx)
{
OPENFILENAME ofn;
char szFileName[256] = "";
char szFilter[256];
static char szFileTitle[16];
static char szDirName[256] = "";
LPSTR lpszEndPath;
ULONG idx;
HRESULT hr;
LPSPropValue lpProp = NULL;
LPATTACH lpAttach = NULL;
LPSTREAM lpstrAtt=NULL, lpstrFile=NULL;
STATSTG statstg = {0};
ULONG ulAttachNum;
LPROUTEDATA pRouteData = NULL;

pRouteData = (LPROUTEDATA)GetWindowLong(hDlg, GWL_USERDATA);
if(!pRouteData)
{
DebugTrace("Client: pRouteData == 0 (saveattachment)");
goto err;
}

if (!szDirName[0])
GetTempPath (sizeof(szDirName), szDirName);

if(!LoadString(hInst, IDS_FILTER, szFilter, sizeof(szFilter)))
{
DebugTrace("Client: failed to load a string");
goto err;
}

for (idx = 0; szFilter[idx] != '\0'; idx++)
if (szFilter[idx] == '|')
szFilter[idx] = '\0';

ulAttachNum = ListBox_GetItemData(GetDlgItem(hDlg, IDC_RTATTACHLIST), itemindx);
if(LB_ERR == ulAttachNum)
{
DebugTrace("Client: can't get attach num (saveattachment)");
return;
}

hr = pRouteData->pmsgRouteMsg->lpVtbl->
OpenAttach(pRouteData->pmsgRouteMsg, ulAttachNum, NULL,
MAPI_BEST_ACCESS | MAPI_DEFERRED_ERRORS, &lpAttach);
if(S_OK != GetScode(hr))
{
DebugTrace("Client: OpenAttach failed (saveattachment)");
goto err;
}

hr = HrGetOneProp((LPMAPIPROP)lpAttach, PR_ATTACH_FILENAME, &lpProp);
if(hr)
{
DebugTrace("Client: OpenAttach failed (saveattachment)");
goto err;
}

lstrcpy (szFileName, lpProp->Value.LPSZ);
MAPIFreeBuffer(lpProp);
lpProp = NULL;

ofn.lStructSize = sizeof (OPENFILENAME);
ofn.hwndOwner = hDlg;
ofn.hInstance = 0;
ofn.lpstrFilter = szFilter;
ofn.lpstrCustomFilter = NULL;
ofn.nMaxCustFilter = 0L;
ofn.nFilterIndex = 1L;
ofn.lpstrFile = szFileName;
ofn.nMaxFile = sizeof(szFileName);
ofn.lpstrFileTitle = szFileTitle;
ofn.nMaxFileTitle = sizeof(szFileTitle);
ofn.lpstrInitialDir = szDirName;
ofn.lpstrTitle = "Save Attachment";
ofn.nFileOffset = 0;
ofn.nFileExtension = 0;
ofn.lpstrDefExt = NULL;
ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;

if (!GetSaveFileName (&ofn))
goto err;

/* Save the directory for the next time we call this */

lstrcpy (szDirName, szFileName);
if (lpszEndPath = SzFindLastCh(szDirName, '\\'))
*(lpszEndPath) = '\0';


hr = lpAttach->lpVtbl->OpenProperty (lpAttach, PR_ATTACH_DATA_BIN,
(LPIID)&IID_IStream,
0, MAPI_DEFERRED_ERRORS,
(LPUNKNOWN *)&lpstrAtt);
if(S_OK != GetScode(hr))
{
/*must be a new attachment*/
hr = HrGetOneProp((LPMAPIPROP)lpAttach, PR_ATTACH_PATHNAME, &lpProp);
if(hr)
{
DebugTrace("Client: PR_ATTACH_PATHNAME (saveattach)");
goto err;
}
CopyFile(lpProp->Value.LPSZ, szFileName, TRUE);
goto err;
}

hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
STGM_CREATE | STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
szFileName, NULL, &lpstrFile);
if(S_OK != GetScode(hr))
{
DebugTrace("Client: OpenStreamOnFile failed (saveattachment)");
goto err;
}


lpstrAtt->lpVtbl->Stat(lpstrAtt, &statstg, STATFLAG_NONAME);

hr = lpstrAtt->lpVtbl->CopyTo(lpstrAtt, lpstrFile, statstg.cbSize, NULL, NULL);
if(S_OK != GetScode(hr))
{
DebugTrace("Client: CopyTo failed (saveattachment)");
goto err;
}

err:
UlRelease(lpstrAtt);
UlRelease(lpstrFile);
UlRelease(lpAttach);
MAPIFreeBuffer(lpProp);
}