CLIENT.C

/* 
- C L I E N T . C
-
* Purpose:
* Sample mail client for the MAPI 1.0 PDK.
* Exclusively uses the Simple MAPI interface.
*
* Copyright 1993-1995 Microsoft Corporation. All Rights Reserved.
*/

#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <commdlg.h>
#include <mapiwin.h>
#include <mapidbg.h>
#include "client.h"
#include "bitmap.h"
#include "pvalloc.h"

HANDLE hInst;
HINSTANCE hlibMAPI = 0;

LPMAPILOGON lpfnMAPILogon = NULL;
LPMAPILOGOFF lpfnMAPILogoff = NULL;
LPMAPISENDMAIL lpfnMAPISendMail = NULL;
LPMAPISENDDOCUMENTS lpfnMAPISendDocuments = NULL;
LPMAPIFINDNEXT lpfnMAPIFindNext = NULL;
LPMAPIREADMAIL lpfnMAPIReadMail = NULL;
LPMAPISAVEMAIL lpfnMAPISaveMail = NULL;
LPMAPIDELETEMAIL lpfnMAPIDeleteMail = NULL;
LPMAPIFREEBUFFER lpfnMAPIFreeBuffer = NULL;
LPMAPIADDRESS lpfnMAPIAddress = NULL;
LPMAPIDETAILS lpfnMAPIDetails = NULL;
LPMAPIRESOLVENAME lpfnMAPIResolveName = NULL;

/* Static Data */

static BOOL fDialogIsActive = FALSE;
static DWORD cUsers = 0;
static ULONG flSendMsgFlags = 0;
static LHANDLE lhSession = 0L;
static HBITMAP hReadBmp = 0;
static HBITMAP hReadABmp = 0;
static HBITMAP hUnReadBmp = 0;
static HBITMAP hUnReadABmp = 0;
static HCURSOR hWaitCur;
static LPMSGID lpReadMsgNode;
static lpMapiMessage lpmsg = NULL;

#ifdef _WIN32
#define szMAPIDLL "MAPI32.DLL"
#else
#define szMAPIDLL "MAPI.DLL"
#endif

int WINAPI
WinMain (HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpszCmd, int nCmdShow)
{
MSG msg;

if (!hPrevInst)
if (!InitApplication (hInstance))
return (FALSE);

if (!InitInstance (hInstance, nCmdShow))
return (FALSE);

while (GetMessage (&msg, 0, 0, 0))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}

DeinitApplication ();

return (msg.wParam);
}

/*
- InitApplication
-
* Purpose:
* Initialize the application.
*
* Parameters:
* hInstance - Instance handle
*
* Returns:
* True/False
*
*/

BOOL
InitApplication (HANDLE hInstance)
{
WNDCLASS wc;

wc.style = 0;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon (hInstance, "NoMail");
wc.hCursor = LoadCursor (0, IDC_ARROW);
wc.hbrBackground = GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = "MailMenu";
wc.lpszClassName = "Client";

return (RegisterClass (&wc));
}

/*
- InitInstance
-
* Purpose:
* Initialize this instance.
*
* Parameters:
* hInstance - Instance handle
* nCmdShow - Do we show the window?
*
* Returns:
* True/False
*
*/

BOOL
InitInstance (HANDLE hInstance, int nCmdShow)
{
HWND hWnd;
BOOL fInit;
ULONG ulResult;

hInst = hInstance;

hWnd = CreateWindow ("Client", "MAPI Sample Mail Client",
WS_OVERLAPPEDWINDOW, 5, 5, 300, 75, 0, 0, hInst, NULL);

if (!hWnd)
return (FALSE);

ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);

hWaitCur = LoadCursor(0, IDC_WAIT);

if (fInit = InitSimpleMAPI ())
{

/* MAPILogon might yield control to Windows. So to prevent the user
from clicking "logon" while we are in the process of loggin on we
have to disable it*/
SecureMenu(hWnd, TRUE);

if ((ulResult = MAPILogon ((ULONG) hWnd, NULL, NULL,
MAPI_LOGON_UI | MAPI_NEW_SESSION,
0, &lhSession)) == SUCCESS_SUCCESS)
{
ToggleMenuState (hWnd, TRUE);
}
else
{
SecureMenu(hWnd, FALSE);
lhSession = 0;
MakeMessageBox (hWnd, ulResult, IDS_LOGONFAIL, MBS_ERROR);
}
}

return (fInit);
}

/*
- InitSimpleMAPI
-
* Purpose:
* Loads the DLL containing the simple MAPI functions and sets
* up a pointer to each. Wrappers for the function pointers
* are declared in SMAPI.H.
*
* Returns:
* TRUE if sucessful, else FALSE
*
* Side effects:
* Loads a DLL and sets up function pointers
*/
BOOL
InitSimpleMAPI (void)
{
UINT fuError;

/*
*Check if MAPI is installed on the system
*/
if(!fSMAPIInstalled())
return FALSE;

fuError = SetErrorMode(SEM_NOOPENFILEERRORBOX);
hlibMAPI = LoadLibrary(szMAPIDLL);
SetErrorMode(fuError);

#ifdef _WIN32
if (!hlibMAPI)
#else
if (hlibMAPI < 32)
#endif
return (FALSE);

if (!(lpfnMAPILogon = (LPMAPILOGON) GetProcAddress (hlibMAPI, "MAPILogon")))
return (FALSE);
if (!(lpfnMAPILogoff = (LPMAPILOGOFF) GetProcAddress (hlibMAPI, "MAPILogoff")))
return (FALSE);
if (!(lpfnMAPISendMail = (LPMAPISENDMAIL) GetProcAddress (hlibMAPI, "MAPISendMail")))
return (FALSE);
if (!(lpfnMAPISendDocuments = (LPMAPISENDDOCUMENTS) GetProcAddress (hlibMAPI, "MAPISendDocuments")))
return (FALSE);
if (!(lpfnMAPIFindNext = (LPMAPIFINDNEXT) GetProcAddress (hlibMAPI, "MAPIFindNext")))
return (FALSE);
if (!(lpfnMAPIReadMail = (LPMAPIREADMAIL) GetProcAddress (hlibMAPI, "MAPIReadMail")))
return (FALSE);
if (!(lpfnMAPISaveMail = (LPMAPISAVEMAIL) GetProcAddress (hlibMAPI, "MAPISaveMail")))
return (FALSE);
if (!(lpfnMAPIDeleteMail = (LPMAPIDELETEMAIL) GetProcAddress (hlibMAPI, "MAPIDeleteMail")))
return (FALSE);
if (!(lpfnMAPIFreeBuffer = (LPMAPIFREEBUFFER) GetProcAddress (hlibMAPI, "MAPIFreeBuffer")))
return (FALSE);
if (!(lpfnMAPIAddress = (LPMAPIADDRESS) GetProcAddress (hlibMAPI, "MAPIAddress")))
return (FALSE);
if (!(lpfnMAPIDetails = (LPMAPIDETAILS) GetProcAddress (hlibMAPI, "MAPIDetails")))
return (FALSE);
if (!(lpfnMAPIResolveName = (LPMAPIRESOLVENAME) GetProcAddress (hlibMAPI, "MAPIResolveName")))
return (FALSE);

return (TRUE);
}

/*
- fSMAPIInstalled
-
* Purpose:
* Checks the appropriate win.ini/registry value to see if Simple MAPI is
* installed in the system.
*
* Returns:
* TRUE if Simple MAPI is installed, else FALSE
*
*/
BOOL
fSMAPIInstalled(void)
{
#ifdef _WIN32
/* on win32, if it's NT 3.51 or lower the value to check is
win.ini \ [Mail] \ MAPI, otherwise it's a registry value
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Messaging Subsystem\MAPI
*/

OSVERSIONINFO osvinfo;
LONG lr;
HKEY hkWMS;

#define MAPIVSize 8
char szMAPIValue[MAPIVSize];
DWORD dwType;
DWORD cbMAPIValue = MAPIVSize;

osvinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

if(!GetVersionEx(&osvinfo))
return FALSE;

if( osvinfo.dwMajorVersion > 3 ||
(osvinfo.dwMajorVersion == 3 && osvinfo.dwMinorVersion > 51))
{ //check the registry value
lr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows Messaging Subsystem",
0, KEY_READ, &hkWMS);
if(ERROR_SUCCESS == lr)
{
lr = RegQueryValueEx(hkWMS, "MAPI", 0, &dwType, szMAPIValue, &cbMAPIValue);
RegCloseKey(hkWMS);
if(ERROR_SUCCESS == lr)
{
Assert(dwType == REG_SZ);
if(lstrcmp(szMAPIValue, "1") == 0)
return TRUE;
}
}

return FALSE;
}

/* fall through*/
#endif /*_WIN32*/

/*check the win.ini value*/
return GetProfileInt("Mail", "MAPI", 0);

}


void
DeinitApplication ()
{
DeinitSimpleMAPI ();
}

void
DeinitSimpleMAPI ()
{
if (hlibMAPI)
{
FreeLibrary (hlibMAPI);
hlibMAPI = 0;
}
}

/*
- MainWndProc
-
* Purpose:
* Main Window Procedure for test program.
*
* Parameters:
* hWnd
* message
* wParam
* lParam
*
* Returns:
*
*
*/

LONG FAR PASCAL
MainWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
ULONG ulResult;

switch (msg)
{
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDM_LOGON:
if (!lhSession)
{
/* MAPILogon might yield control to Windows. So to prevent the user
from clicking "logon" while we are in the process of loggin on we
have to disable it*/
SecureMenu(hWnd, TRUE);

if ((ulResult = MAPILogon ((ULONG) hWnd, NULL, NULL,
MAPI_LOGON_UI | MAPI_NEW_SESSION,
0, &lhSession)) == SUCCESS_SUCCESS)
{
ToggleMenuState (hWnd, TRUE);
}
else
{
SecureMenu(hWnd, FALSE);
lhSession = 0;
MakeMessageBox (hWnd, ulResult, IDS_LOGONFAIL, MBS_ERROR);
}
}
break;

case IDM_LOGOFF:
if (lhSession)
{
MAPILogoff (lhSession, (ULONG) hWnd, 0, 0);
ToggleMenuState (hWnd, FALSE);
lhSession = 0;
}
break;

case IDM_COMPOSE:
fDialogIsActive = TRUE;
DialogBox (hInst, "ComposeNote", hWnd, ComposeDlgProc);
fDialogIsActive = FALSE;
break;

case IDM_READ:
fDialogIsActive = TRUE;
DialogBox (hInst, "InBox", hWnd, InBoxDlgProc);
fDialogIsActive = FALSE;
break;

case IDM_SEND:
if(lhSession)
{
MapiMessage msgSend;

memset(&msgSend, 0, sizeof(MapiMessage));
fDialogIsActive = TRUE;
MAPISendMail(lhSession, (ULONG)hWnd, &msgSend, MAPI_DIALOG, 0L);
fDialogIsActive = FALSE;
}
break;

case IDM_ADDRBOOK:
if (lhSession)
{
fDialogIsActive = TRUE;
if ((ulResult = MAPIAddress (lhSession, (ULONG) hWnd,
NULL, 0, NULL, 0, NULL, 0, 0, NULL, NULL)))
{
if (ulResult != MAPI_E_USER_ABORT)
MakeMessageBox (hWnd, ulResult, IDS_ADDRBOOKFAIL, MBS_ERROR);
}
fDialogIsActive = FALSE;
}
break;

case IDM_DETAILS:
if (lhSession)
{
fDialogIsActive = TRUE;
DialogBox(hInst, "Details", hWnd, DetailsDlgProc);
fDialogIsActive = FALSE;
}
break;

case IDM_ABOUT:
fDialogIsActive = TRUE;
DialogBox (hInst, "AboutBox", hWnd, AboutDlgProc);
fDialogIsActive = FALSE;
break;

case IDM_EXIT:
if (lhSession)
MAPILogoff (lhSession, (ULONG) hWnd, 0, 0);

PostQuitMessage (0);
break;

default:
return (DefWindowProc (hWnd, msg, wParam, lParam));
}
break;

case WM_QUERYENDSESSION:
{

/*
* If we have a modal dialog open (all our dialogs are modal, so
* just see if we have a dialog open), veto the shutdown.
*/

if (fDialogIsActive)
{
LPCSTR szTitle = "MAPI Sample Mail Client";
char szText[256];

LoadString (hInst, IDS_DIALOGACTIVE, szText, 255);

#ifdef WIN16
MessageBox((HWND)NULL, szText, szTitle, MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
#else
MessageBoxA(NULL, szText, szTitle, MB_OK | MB_ICONSTOP | MB_TASKMODAL | MB_SETFOREGROUND);
#endif
return FALSE;
}

else
{
return TRUE;
}
}

case WM_ENDSESSION:

if (wParam)
{
DestroyWindow (hWnd);
}

break;

case WM_CLOSE:
case WM_DESTROY:
if (lhSession)
MAPILogoff (lhSession, (ULONG) hWnd, 0, 0);

PostQuitMessage (0);
break;

default:
return (DefWindowProc (hWnd, msg, wParam, lParam));
}
return FALSE;
}

/*
- AboutDlgProc
-
* Purpose:
* About box dialog procedure
*
* Parameters:
* hDlg
* message
* wParam
* lParam
*
* Returns:
* True/False
*
*/

BOOL FAR PASCAL
AboutDlgProc (HWND hDlg, UINT msg, UINT wParam, LONG lParam)
{

#include <pdkver.h>

char rgchVersion[80];

switch (msg)
{
case WM_INITDIALOG:
wsprintf(rgchVersion, "Version %d.%d.%d (%s)", rmj, rmm, rup,
szVerName && *szVerName ? szVerName : "BUDDY");
SetDlgItemText(hDlg, IDC_VERSION, rgchVersion);
return TRUE;

case WM_COMMAND:
if (wParam == IDOK || wParam == IDCANCEL)
{
EndDialog (hDlg, TRUE);
return TRUE;
}
break;
}
return FALSE;
}

/*
- OptionsDlgProc
-
* Purpose:
* Message Options dialog procedure
*
* Parameters:
* hDlg
* message
* wParam
* lParam
*
* Returns:
* True/False
*
*/

BOOL FAR PASCAL
OptionsDlgProc (HWND hDlg, UINT msg, UINT wParam, LONG lParam)
{
switch (msg)
{
case WM_INITDIALOG:
CheckDlgButton (hDlg, IDC_RETURN,
!!(flSendMsgFlags & MAPI_RECEIPT_REQUESTED));
return TRUE;

case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDOK:
if (IsDlgButtonChecked (hDlg, IDC_RETURN))
flSendMsgFlags |= MAPI_RECEIPT_REQUESTED;
else
flSendMsgFlags &= ~MAPI_RECEIPT_REQUESTED;

case IDCANCEL:
EndDialog (hDlg, TRUE);
return TRUE;
}
break;
}
return FALSE;
}

/*
- DetailsDlgProc
-
* Purpose:
* User Details dialog procedure
*
* Parameters:
* hDlg
* message
* wParam
* lParam
*
* Returns:
* True/False
*
*/

BOOL FAR PASCAL
DetailsDlgProc (HWND hDlg, UINT msg, UINT wParam, LONG lParam)
{
LPSTR lpszType = NULL;
LPSTR lpszAddr = NULL;
LPSTR lpszName;
ULONG cRecips;
ULONG ulResult;
lpMapiRecipDesc lpRecip = NULL;

switch (msg)
{
case WM_INITDIALOG:
while(!lpRecip)
{
if ((ulResult = MAPIAddress (lhSession, (ULONG) hDlg,
"Select One User", 1, "User:", 0, NULL, 0, 0,
&cRecips, &lpRecip)))
{
if (ulResult != MAPI_E_USER_ABORT)
MakeMessageBox (hDlg, ulResult, IDS_ADDRBOOKFAIL, MBS_ERROR);

EndDialog (hDlg, TRUE);
return TRUE;
}

if (cRecips == 0)
{
EndDialog (hDlg, TRUE);
return TRUE;
}

if (cRecips > 1)
{
cRecips = 0;
MAPIFreeBuffer (lpRecip);
lpRecip = NULL;
MakeMessageBox (hDlg, 0, IDS_DETAILS_TOO_MANY, MBS_OOPS);
}
}
lpszName = lpRecip->lpszName;
if(lpRecip->lpszAddress)
{
lpszType = strtok(lpRecip->lpszAddress, ":");
lpszAddr = strtok(NULL, "\n");
}

SetDlgItemText(hDlg, IDC_NAME, lpszName);
SetDlgItemText(hDlg, IDC_TYPE, (lpszType ? lpszType : "MSPEER"));
SetDlgItemText(hDlg, IDC_ADDR, (lpszAddr ? lpszAddr : ""));

MAPIFreeBuffer (lpRecip);
return TRUE;

case WM_COMMAND:
if(LOWORD(wParam) == IDC_CLOSE || LOWORD(wParam) ==IDCANCEL)
{
EndDialog (hDlg, TRUE);
return TRUE;
}
break;
}
return FALSE;
}

/*
- ComposeDlgProc
-
* Purpose:
* Dialog procedure for the ComposeNote dialog.
*
* Parameters:
* hDlg
* message
* wParam
* lParam
*
* Returns:
* True/False
*
*/

BOOL FAR PASCAL
ComposeDlgProc (HWND hDlg, UINT msg, UINT wParam, LONG lParam)
{
char szUnResNames[TO_EDIT_MAX];
char szDisplayNames[TO_EDIT_MAX];
/* char szAttach[FILE_ATTACH_MAX];*/
BOOL fUnResTo, fUnResCc;
LONG cb, cLines;
ULONG ulResult;
HCURSOR hOldCur;
static LPSTR lpszSubject;
static LPSTR lpszNoteText;
static ULONG cRecips;
static ULONG cNewRecips;
static ULONG cAttach;
static lpMapiRecipDesc lpRecips;
static lpMapiRecipDesc lpNewRecips;
static lpMapiFileDesc lpAttach;
ULONG idx;

switch (msg)
{
case WM_INITDIALOG:
if (lpmsg)
{
/* ComposeNote is being called to either forward or reply */
/* to a message in the Inbox. So, we'll initialize the */
/* ComposeNote form with data from the global MapiMessage */

lpszSubject = lpmsg->lpszSubject;
lpszNoteText = lpmsg->lpszNoteText;
cRecips = lpmsg->nRecipCount;
cAttach = lpmsg->nFileCount;
lpRecips = lpmsg->lpRecips;
lpAttach = lpmsg->lpFiles;

if (cRecips)
{
MakeDisplayNameStr (szDisplayNames, MAPI_TO,
cRecips, lpRecips);
if (*szDisplayNames)
SetDlgItemText (hDlg, IDC_TO, szDisplayNames);

MakeDisplayNameStr (szDisplayNames, MAPI_CC,
cRecips, lpRecips);
if (*szDisplayNames)
SetDlgItemText (hDlg, IDC_CC, szDisplayNames);
}
SetDlgItemText (hDlg, IDC_SUBJECT, lpmsg->lpszSubject);
SetDlgItemText (hDlg, IDC_NOTE, lpmsg->lpszNoteText);
if (!cAttach)
{
EnableWindow (GetDlgItem (hDlg, IDC_CATTACHMENT), FALSE);
EnableWindow (GetDlgItem (hDlg, IDT_CATTACHMENT), FALSE);
}
else
{
for(idx = 0; idx < cAttach; idx++)
if (lpAttach[idx].lpszFileName)
SendDlgItemMessage(hDlg, IDC_CATTACHMENT, LB_ADDSTRING, 0,
(LPARAM)lpAttach[idx].lpszFileName);

/*SendDlgItemMessage(hDlg, IDC_CATTACHMENT, LB_SETCURSEL, 0, 0L);*/
}

SendDlgItemMessage (hDlg, IDC_TO, EM_SETMODIFY, FALSE, 0);
SendDlgItemMessage (hDlg, IDC_CC, EM_SETMODIFY, FALSE, 0);
SendDlgItemMessage (hDlg, IDC_SUBJECT, EM_SETMODIFY, FALSE, 0);
SendDlgItemMessage (hDlg, IDC_NOTE, EM_SETMODIFY, FALSE, 0);
if(cRecips)
SetFocus (GetDlgItem (hDlg, IDC_NOTE));
else
SetFocus (GetDlgItem (hDlg, IDC_TO));
}
else
{
lpmsg = (lpMapiMessage)PvAlloc(sizeof(MapiMessage));

if (!lpmsg)
goto cleanup;

memset (lpmsg, 0, sizeof (MapiMessage));

lpszSubject = NULL;
lpszNoteText = NULL;
cRecips = 0;
cAttach = 0;
lpRecips = NULL;
lpNewRecips = NULL;
lpAttach = NULL;

lpmsg->flFlags = flSendMsgFlags;
SetFocus (GetDlgItem (hDlg, IDC_TO));
}
return FALSE;

case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDC_ATTACH:
if (GetNextFile (hDlg, (ULONG) -1, &cAttach, &lpAttach) == SUCCESS_SUCCESS)
{
/* if the first attachment */
if (cAttach == 1)
{
EnableWindow (GetDlgItem (hDlg, IDC_CATTACHMENT), TRUE);
EnableWindow (GetDlgItem (hDlg, IDT_CATTACHMENT), TRUE);
}

if (lpAttach[cAttach - 1].lpszFileName)
SendDlgItemMessage(hDlg, IDC_CATTACHMENT, LB_ADDSTRING, 0,
(LPARAM)lpAttach[cAttach -1].lpszFileName);

/* Now, send a little render message to the NoteText edit */

/*wsprintf (szAttach, "<<File: %s>>",
lpAttach[cAttach - 1].lpszFileName);

SendDlgItemMessage (hDlg, IDC_NOTE, EM_REPLACESEL, 0,
(LPARAM) ((LPSTR) szAttach));*/
}
break;

case IDC_ADDRBOOK:
SendMessage(hDlg, WM_COMMAND, MAKELONG(IDC_RESOLVE,0), 0);
ulResult = MAPIAddress (lhSession, (ULONG) hDlg, NULL,
2, NULL, cRecips, lpRecips, 0, 0,
&cNewRecips, &lpNewRecips);
if (ulResult)
{
if (ulResult != MAPI_E_USER_ABORT)
MakeMessageBox (hDlg, ulResult, IDS_ADDRBOOKFAIL, MBS_ERROR);
}
else
{
if (cNewRecips)
{
PvFree(lpRecips);
lpRecips = (lpMapiRecipDesc)PvAlloc(cNewRecips*sizeof(MapiRecipDesc));
cRecips = cNewRecips;

while(cNewRecips--)
CopyRecipient(lpRecips, &lpRecips[cNewRecips],
&lpNewRecips[cNewRecips]);

MAPIFreeBuffer(lpNewRecips);
lpNewRecips = NULL;
cNewRecips = 0;

MakeDisplayNameStr (szDisplayNames, MAPI_TO,
cRecips, lpRecips);
if (*szDisplayNames)
SetDlgItemText (hDlg, IDC_TO, szDisplayNames);

MakeDisplayNameStr (szDisplayNames, MAPI_CC,
cRecips, lpRecips);
if (*szDisplayNames)
SetDlgItemText (hDlg, IDC_CC, szDisplayNames);

SendDlgItemMessage (hDlg, IDC_TO, EM_SETMODIFY, FALSE, 0);
SendDlgItemMessage (hDlg, IDC_CC, EM_SETMODIFY, FALSE, 0);
}
}
break;

case IDC_OPTIONS:
DialogBox (hInst, "Options", hDlg, OptionsDlgProc);
break;

case IDC_SEND:
case IDC_RESOLVE:
fUnResTo = FALSE;
fUnResCc = FALSE;

hOldCur = SetCursor(hWaitCur);


/* Get the names from the To: field and resolve them first */

/*if (SendDlgItemMessage (hDlg, IDC_TO, EM_GETMODIFY, 0, 0) && */
if (cb = SendDlgItemMessage (hDlg, IDC_TO, WM_GETTEXT,
(WPARAM)sizeof(szUnResNames), (LPARAM)szUnResNames))
{
if (!ResolveFriendlyNames (hDlg, szUnResNames, MAPI_TO,
&cRecips, &lpRecips))
{
MakeDisplayNameStr (szDisplayNames, MAPI_TO,
cRecips, lpRecips);
if (*szDisplayNames)
{
if (*szUnResNames)
{
lstrcat (szDisplayNames, "; ");
lstrcat (szDisplayNames, szUnResNames);
fUnResTo = TRUE;
}

SetDlgItemText (hDlg, IDC_TO, szDisplayNames);
}
else
{
if (*szUnResNames)
{
SetDlgItemText (hDlg, IDC_TO, szUnResNames);
fUnResTo = TRUE;
}
}
}
/*SendDlgItemMessage (hDlg, IDC_TO, EM_SETMODIFY, FALSE, 0);*/
}

/* Now, get the names from the Cc: field and resolve them */

/*if (SendDlgItemMessage (hDlg, IDC_CC, EM_GETMODIFY, 0, 0) &&*/
if (cb = SendDlgItemMessage (hDlg, IDC_CC, WM_GETTEXT,
(WPARAM)sizeof(szUnResNames), (LPARAM)szUnResNames))
{
if (!ResolveFriendlyNames (hDlg, szUnResNames, MAPI_CC,
&cRecips, &lpRecips))
{
MakeDisplayNameStr (szDisplayNames, MAPI_CC,
cRecips, lpRecips);
if (*szDisplayNames)
{
if (*szUnResNames)
{
lstrcat (szDisplayNames, "; ");
lstrcat (szDisplayNames, szUnResNames);
fUnResCc = TRUE;
}

SetDlgItemText (hDlg, IDC_CC, szDisplayNames);
}
else
{
if (*szUnResNames)
{
SetDlgItemText (hDlg, IDC_CC, szUnResNames);
fUnResCc = TRUE;
}
}
}
/*SendDlgItemMessage (hDlg, IDC_CC, EM_SETMODIFY, FALSE, 0);*/
}

/* If we were just Resolving Names then we can leave now */

if (LOWORD (wParam) == IDC_RESOLVE)
{
SetCursor(hOldCur);
break;
}

if (cRecips == 0 || fUnResTo || fUnResCc)
{
if (!cRecips)
MakeMessageBox (hDlg, 0, IDS_NORECIPS, MBS_OOPS);

if (fUnResTo)
SetFocus (GetDlgItem (hDlg, IDC_TO));
else if (fUnResCc)
SetFocus (GetDlgItem (hDlg, IDC_CC));
else
SetFocus (GetDlgItem (hDlg, IDC_TO));

SetCursor(hOldCur);
break;
}

/* Everything is OK so far, lets get the Subject */
/* and the NoteText and try to send the message. */

/* Get Subject from Edit */

if (SendDlgItemMessage (hDlg, IDC_SUBJECT, EM_GETMODIFY, 0, 0))
{
cb = SendDlgItemMessage (hDlg, IDC_SUBJECT, EM_LINELENGTH, 0, 0L);

PvFree(lpszSubject);
lpszSubject = (LPTSTR)PvAlloc(cb + 1);

if (!lpszSubject)
goto cleanup;

GetDlgItemText (hDlg, IDC_SUBJECT, lpszSubject, (int)cb+1);
}

/* Get the NoteText from Edit */

if (SendDlgItemMessage (hDlg, IDC_NOTE, EM_GETMODIFY, 0, 0))
{
cLines = SendDlgItemMessage (hDlg, IDC_NOTE,
EM_GETLINECOUNT, 0, 0L);

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

cb = SendDlgItemMessage (hDlg, IDC_NOTE, EM_LINEINDEX,
(UINT)cLines - 1, 0L);
cb += SendDlgItemMessage (hDlg, IDC_NOTE, EM_LINELENGTH,
(UINT)cb, 0L);

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

cb += cLines * 2;

PvFree(lpszNoteText);
lpszNoteText = (LPTSTR)PvAlloc(cb + 1);

if (!lpszNoteText)
goto cleanup;

/* Get the Note Text from the edit */

GetDlgItemText (hDlg, IDC_NOTE, lpszNoteText, (int)cb);
}
else
{
/* Make an empty string for NoteText */

lpszNoteText = (LPTSTR)PvAlloc(1);
if (!lpszNoteText)
goto cleanup;
*lpszNoteText = '\0';
}
}

lpmsg->lpszSubject = lpszSubject;
lpmsg->lpszNoteText = lpszNoteText;
lpmsg->nRecipCount = cRecips;
lpmsg->lpRecips = lpRecips;
lpmsg->nFileCount = cAttach;
lpmsg->lpFiles = lpAttach;
lpmsg->flFlags = flSendMsgFlags;

ulResult = MAPISendMail (lhSession, (ULONG) hDlg, lpmsg, 0, 0);

LogSendMail(ulResult);

if (ulResult)
{
MakeMessageBox (hDlg, ulResult, IDS_SENDERROR, MBS_ERROR);
SetCursor(hOldCur);
break;
}
cleanup:
SetCursor(hOldCur);

case IDCANCEL:
PvFree(lpmsg->lpszMessageType);
PvFree(lpmsg->lpszConversationID);
PvFree(lpmsg);
PvFree(lpRecips);
PvFree(lpAttach);
PvFree(lpszSubject);
PvFree(lpszNoteText);
lpmsg = NULL;

EndDialog (hDlg, TRUE);
return TRUE;
break;

default:
break;
}
break;
}
return FALSE;
}

/*
- InBoxDlgProc
-
* Purpose:
* Dialog procedure for the InBox dialog.
*
* Parameters:
* hDlg
* message
* wParam
* lParam
*
* Returns:
* True/False
*
*/

BOOL FAR PASCAL
InBoxDlgProc (HWND hDlg, UINT msg, UINT wParam, LONG lParam)
{
char szMsgID[512];
char szSeedMsgID[512];
LPMSGID lpMsgNode;
static LPMSGID lpMsgIdList = NULL;
lpMapiMessage lpMessage;
ULONG ulResult;
DWORD nIndex;
RECT Rect;
HCURSOR hOldCur;

switch (msg)
{
case WM_INITDIALOG:
hOldCur = SetCursor(hWaitCur);

InitBmps(hDlg, IDC_MSG);

/* Populate List Box with all messages in InBox. */
/* This is a painfully slow process for now. */

ulResult = MAPIFindNext (lhSession, (ULONG) hDlg, NULL, NULL,
MAPI_GUARANTEE_FIFO | MAPI_LONG_MSGID, 0, szMsgID);

while (ulResult == SUCCESS_SUCCESS)
{
ulResult = MAPIReadMail (lhSession, (ULONG) hDlg, szMsgID,
MAPI_PEEK | MAPI_ENVELOPE_ONLY,
0, &lpMessage);

if (!ulResult)
{
lpMsgNode = MakeMsgNode (lpMessage, szMsgID);

if (lpMsgNode)
{
InsertMsgNode (lpMsgNode, &lpMsgIdList);

SendDlgItemMessage (hDlg, IDC_MSG, LB_ADDSTRING,
0, (LONG) lpMsgNode);

} 
MAPIFreeBuffer (lpMessage);
}

lstrcpy (szSeedMsgID, szMsgID);
ulResult = MAPIFindNext (lhSession, (ULONG) hDlg, NULL, szSeedMsgID,
MAPI_GUARANTEE_FIFO | MAPI_LONG_MSGID, 0, szMsgID);
}

SetCursor(hOldCur);
SetFocus (GetDlgItem (hDlg, IDC_MSG));
return TRUE;
break;

case WM_SETFOCUS:
SetFocus (GetDlgItem (hDlg, IDC_MSG));
break;

case WM_MEASUREITEM:
/* Sets the height of the owner-drawn List-Box */
MeasureItem(hDlg, (MEASUREITEMSTRUCT *)lParam);
break;

case WM_DRAWITEM:
DrawItem((DRAWITEMSTRUCT *)lParam);
break;

case WM_DELETEITEM:
/* This message is handled by the IDC_DELETE message */
return TRUE;
break;

case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDC_NEW:
hOldCur = SetCursor(hWaitCur);

ulResult = MAPIFindNext (lhSession, (ULONG) hDlg, NULL, NULL,
MAPI_UNREAD_ONLY | MAPI_LONG_MSGID, 0, szMsgID);

while (ulResult == SUCCESS_SUCCESS)
{
if (!FindNode (lpMsgIdList, szMsgID))
{
ulResult = MAPIReadMail (lhSession, (ULONG) hDlg, szMsgID,
MAPI_PEEK | MAPI_ENVELOPE_ONLY, 0, &lpMessage);

if (!ulResult)
{
lpMsgNode = MakeMsgNode (lpMessage, szMsgID);
InsertMsgNode (lpMsgNode, &lpMsgIdList);

SendDlgItemMessage (hDlg, IDC_MSG, LB_ADDSTRING,
0, (LONG) lpMsgNode);

MAPIFreeBuffer (lpMessage);
}
}

lstrcpy (szSeedMsgID, szMsgID);
ulResult = MAPIFindNext (lhSession, (ULONG) hDlg, NULL, szSeedMsgID,
MAPI_UNREAD_ONLY | MAPI_LONG_MSGID, 0, szMsgID);
}
SetCursor(hOldCur);
break;

case IDC_MSG:
if(HIWORD(wParam) != LBN_DBLCLK)
break;

case IDC_READ:
nIndex = SendDlgItemMessage (hDlg, IDC_MSG, LB_GETCURSEL, 0, 0);

if (nIndex == LB_ERR)
break;

lpReadMsgNode = (LPMSGID) SendDlgItemMessage (hDlg, IDC_MSG,
LB_GETITEMDATA, (UINT)nIndex, 0L);

if (lpReadMsgNode)
DialogBox (hInst, "ReadNote", hDlg, ReadMailDlgProc);

/* Update the Messages List-Box with new icon */

SendDlgItemMessage (hDlg, IDC_MSG, LB_GETITEMRECT, (UINT)nIndex, (LPARAM) &Rect);
InvalidateRect(GetDlgItem(hDlg, IDC_MSG), &Rect, FALSE);
break;

case IDC_DELETE:
nIndex = SendDlgItemMessage (hDlg, IDC_MSG, LB_GETCURSEL, 0, 0);

if (nIndex == LB_ERR)
break;

lpMsgNode = (LPMSGID) SendDlgItemMessage (hDlg, IDC_MSG,
LB_GETITEMDATA, (UINT)nIndex, 0);

if (lpMsgNode)
{
MAPIDeleteMail (lhSession, (ULONG) hDlg, lpMsgNode->lpszMsgID, 0, 0);
DeleteMsgNode (lpMsgNode, &lpMsgIdList);
}

SendDlgItemMessage (hDlg, IDC_MSG, LB_DELETESTRING, (UINT)nIndex, 0);
break;

case IDC_CLOSE:
case IDCANCEL:
FreeMsgList (lpMsgIdList);
lpMsgIdList = NULL;

DeInitBmps();

EndDialog (hDlg, TRUE);
return TRUE;
break;

default:
break;
}
break;
}

return FALSE;
}

/*
- ReadMailDlgProc
-
* Purpose:
* Dialog procedure for the ReadMail dilaog.
*
* Parameters:
* hDlg
* message
* wParam
* lParam
*
* Returns:
* True/False
*
*/

BOOL FAR PASCAL
ReadMailDlgProc (HWND hDlg, UINT msg, UINT wParam, LONG lParam)
{
ULONG ulResult;
char szTo[TO_EDIT_MAX];
char szCc[TO_EDIT_MAX];
char szChangeMsg[512];
ULONG idx;
static lpMapiMessage lpReadMsg;

switch (msg)
{
case WM_INITDIALOG:
if (ulResult = MAPIReadMail (lhSession, (LONG) hDlg, lpReadMsgNode->lpszMsgID,
0, 0, &lpReadMsg))
{
MakeMessageBox(hDlg, ulResult, IDS_READFAIL, MBS_ERROR);
EndDialog (hDlg, TRUE);
return TRUE;
}

lpReadMsgNode->fUnRead = FALSE;

szTo[0] = '\0';
szCc[0] = '\0';

for (idx = 0; idx < lpReadMsg->nRecipCount; idx++)
{
if (lpReadMsg->lpRecips[idx].ulRecipClass == MAPI_TO)
{
lstrcat (szTo, lpReadMsg->lpRecips[idx].lpszName);
lstrcat (szTo, "; ");
}
else if (lpReadMsg->lpRecips[idx].ulRecipClass == MAPI_CC)
{
lstrcat (szCc, lpReadMsg->lpRecips[idx].lpszName);
lstrcat (szCc, "; ");
}
else
{
/* Must be Bcc, lets ignore it! */
}
}

if(*szTo)
szTo[lstrlen (szTo) - 2] = '\0';
if(*szCc)
szCc[lstrlen (szCc) - 2] = '\0';

SetDlgItemText (hDlg, IDC_RFROM,
(lpReadMsg->lpOriginator && lpReadMsg->lpOriginator->lpszName ?
lpReadMsg->lpOriginator->lpszName : ""));
SetDlgItemText (hDlg, IDC_RDATE,
(lpReadMsg->lpszDateReceived ? lpReadMsg->lpszDateReceived : ""));
SetDlgItemText (hDlg, IDC_RTO, szTo);
SetDlgItemText (hDlg, IDC_RCC, szCc);
SetDlgItemText (hDlg, IDC_RSUBJECT,
(lpReadMsg->lpszSubject ? lpReadMsg->lpszSubject : ""));
SetDlgItemText (hDlg, IDC_READNOTE,
(lpReadMsg->lpszNoteText ? lpReadMsg->lpszNoteText : ""));

if (!lpReadMsg->nFileCount)
{
EnableWindow (GetDlgItem (hDlg, IDC_SAVEATTACH), FALSE);
EnableWindow (GetDlgItem (hDlg, IDC_ATTACHMENT), FALSE);
EnableWindow (GetDlgItem (hDlg, IDT_ATTACHMENT), FALSE);
}
else
{
for(idx = 0; idx < lpReadMsg->nFileCount; idx++)
if (lpReadMsg->lpFiles[idx].lpszFileName)
SendDlgItemMessage(hDlg, IDC_ATTACHMENT, LB_ADDSTRING, 0,
(LPARAM)lpReadMsg->lpFiles[idx].lpszFileName);

SendDlgItemMessage(hDlg, IDC_ATTACHMENT, LB_SETCURSEL, 0, 0L);
}

SetFocus (GetDlgItem (hDlg, IDC_READNOTE));
return FALSE;

case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDC_SAVECHANGES:
if (SendDlgItemMessage (hDlg, IDC_READNOTE, EM_GETMODIFY, 0, 0))
ulResult = SaveMsgChanges (hDlg, lpReadMsg, lpReadMsgNode->lpszMsgID);
SendDlgItemMessage (hDlg, IDC_READNOTE, EM_SETMODIFY, 0, 0);
break;

case IDC_ATTACHMENT:
if(HIWORD(wParam) != LBN_DBLCLK)
break;

case IDC_SAVEATTACH:
idx = SendDlgItemMessage(hDlg, IDC_ATTACHMENT, LB_GETCURSEL, 0, 0L);

if(idx != LB_ERR)
{
SaveFileAttachments(hDlg, &lpReadMsg->lpFiles[idx]);
SetFocus(GetDlgItem (hDlg, IDC_ATTACHMENT));
return FALSE;

}
break;

case IDC_REPLY:
case IDC_REPLYALL:
case IDC_FORWARD:
MakeNewMessage (lpReadMsg, LOWORD (wParam));
DialogBox (hInst, "ComposeNote", hDlg, ComposeDlgProc);
break;

case IDCANCEL:
if (SendDlgItemMessage (hDlg, IDC_READNOTE, EM_GETMODIFY, 0, 0))
{
wsprintf (szChangeMsg, "Save changes to: '%s' in Inbox?",
(lpReadMsg->lpszSubject ? lpReadMsg->lpszSubject : ""));

if (MessageBox (hDlg, szChangeMsg, "Mail", MB_YESNO) == IDYES)
{
ulResult = SaveMsgChanges (hDlg, lpReadMsg, lpReadMsgNode->lpszMsgID);
}
}

/* If there were file attachments, then delete the temps */

for(idx = 0; idx < lpReadMsg->nFileCount; idx++)
if (lpReadMsg->lpFiles[idx].lpszPathName)
DeleteFile(lpReadMsg->lpFiles[idx].lpszPathName);

MAPIFreeBuffer (lpReadMsg);
lpReadMsg = NULL;
EndDialog (hDlg, TRUE);
return TRUE;
}
break;
}
return FALSE;
}

/*
- MakeMessageBox
-
* Purpose:
* Gets resource string and displays an error message box.
*
* Parameters:
* hWnd - Handle to parent window
* idString - Resource ID of message in StringTable
*
* Returns:
* Void
*
*/

void
MakeMessageBox (HWND hWnd, ULONG ulResult, UINT idString, UINT fStyle)
{
char szMessage[256];
char szMapiReturn[64];

LoadString (hInst, idString, szMessage, 255);

if (ulResult)
{
LoadString (hInst, (UINT)ulResult, szMapiReturn, 64);
lstrcat (szMessage, "\nReturn Code: ");
lstrcat (szMessage, szMapiReturn);
}

MessageBox (hWnd, szMessage, "Problem", fStyle);
}

/*
- ResolveFriendlyNames
-
* Purpose:
* Helper function to convert a string of ';' delimited friendly
* names into an array of MapiRecipDescs.
*
* Side Effects:
* The display string passed in is modified to contain the
* friendly names of the mail users as found in the sample
* address book.
*
* Note:
* Duplicate names in the address book will result in undefined
* behavior.
*
* Parameters:
* hWnd - Handle to parent window
* lpszDisplayNames - string of ';' delimited user names
* ulRecipClass - either MAPI_TO, MAPI_CC, or MAPI_BCC
* lpcRecips - Address of recipient count to be returned
* lppRecips - Address of recipient array to be returned
*
* Return:
* ulResult
*/

ULONG
ResolveFriendlyNames (HWND hWnd, LPSTR lpszDisplayNames, ULONG ulRecipClass,
ULONG * lpcRecips, lpMapiRecipDesc * lppRecips)
{
char szResolve[TO_EDIT_MAX];
LPSTR lpszNameToken;
ULONG cRecips = 0;
ULONG cFails = 0;
ULONG ulResult;
lpMapiRecipDesc lpRecip;
lpMapiRecipDesc lpRecipList;

*szResolve = '\0';
lpszNameToken = strtok (lpszDisplayNames, ";\n");

while (lpszNameToken)
{
/* Strip leading blanks from name */

while (*lpszNameToken == ' ')
lpszNameToken++;

/* Check if name has already been resolved */

if (!FNameInList (lpszNameToken, *lpcRecips, *lppRecips))
{
lstrcat (szResolve, lpszNameToken);
lstrcat (szResolve, "; ");
cRecips++;
}

/* Get Next Token */

lpszNameToken = strtok (NULL, ";\n");
}

*lpszDisplayNames = '\0';

if (!szResolve[0])
{
ulResult = SUCCESS_SUCCESS;
goto err;
}

szResolve[lstrlen (szResolve) - 2] = '\0';

lpRecipList = (lpMapiRecipDesc)PvAlloc((cRecips + *lpcRecips) * sizeof (MapiRecipDesc));

if (!lpRecipList)
{
ulResult = MAPI_E_INSUFFICIENT_MEMORY;
goto err;
}
memset (lpRecipList, 0, (size_t)(cRecips+*lpcRecips)*sizeof(MapiRecipDesc));

cRecips = 0;

while (cRecips < *lpcRecips)
{
ulResult = CopyRecipient (lpRecipList, &lpRecipList[cRecips],
*lppRecips + cRecips);

if (ulResult)
{
PvFree(lpRecipList);
goto err;
}

cRecips++;
}

PvFree(*lppRecips);

lpszNameToken = strtok (szResolve, ";\n");

while (lpszNameToken)
{
/* Strip leading blanks (again) */

while (*lpszNameToken == ' ')
lpszNameToken++;

ulResult = MAPIResolveName (lhSession, (ULONG) hWnd, lpszNameToken,
MAPI_DIALOG, 0, &lpRecip);

if (ulResult == SUCCESS_SUCCESS)
{
lpRecip->ulRecipClass = ulRecipClass;
ulResult = CopyRecipient (lpRecipList, &lpRecipList[cRecips], lpRecip);

MAPIFreeBuffer (lpRecip);

if (ulResult)
goto cleanup;

cRecips++;
}
else
{
lstrcat (lpszDisplayNames, lpszNameToken);
lstrcat (lpszDisplayNames, "; ");
cFails++;
}
lpszNameToken = strtok (NULL, ";\n");
}

/* if cFails > 0 then we have partial success */

ulResult = SUCCESS_SUCCESS;

if (cFails)
MakeMessageBox (hWnd, 0, IDS_UNRESOLVEDNAMES, MBS_INFO);

cleanup:
*lpcRecips = cRecips;
*lppRecips = lpRecipList;
err:
if (*lpszDisplayNames)
lpszDisplayNames[lstrlen (lpszDisplayNames) - 2] = '\0';

return ulResult;
}

/*
- CopyRecipient
-
* Purpose:
* Called in support of ResolveFriendlyNames() to build an array
* of chained MapiRecipDescs.
*
* Parameters:
* lpParent - Parent memory that allocations get chained to
* lpDest - Destination Recipient
* lpSrc - Source Recipient
*
* Return:
* ulResult
*/

ULONG
CopyRecipient (lpMapiRecipDesc lpParent,
lpMapiRecipDesc lpDest,
lpMapiRecipDesc lpSrc)
{
lpDest->ulReserved = lpSrc->ulReserved;
lpDest->ulRecipClass = lpSrc->ulRecipClass;
lpDest->ulEIDSize = lpSrc->ulEIDSize;

if (lpSrc->lpszName)
{
lpDest->lpszName = (LPTSTR)PvAllocMore(lstrlen(lpSrc->lpszName) + 1,
(LPVOID)lpParent);

if (!lpDest->lpszName)
return MAPI_E_INSUFFICIENT_MEMORY;

lstrcpy (lpDest->lpszName, lpSrc->lpszName);
}
else
lpDest->lpszName = NULL;

if (lpSrc->lpszAddress)
{
lpDest->lpszAddress = (LPTSTR)PvAllocMore(lstrlen (lpSrc->lpszAddress) + 1,
(LPVOID)lpParent);

if (!lpDest->lpszAddress)
return MAPI_E_INSUFFICIENT_MEMORY;

lstrcpy (lpDest->lpszAddress, lpSrc->lpszAddress);
}
else
lpDest->lpszAddress = NULL;

if (lpSrc->lpEntryID)
{
lpDest->lpEntryID = (LPBYTE)PvAllocMore(lpSrc->ulEIDSize,
(LPVOID)lpParent);

if (!lpDest->lpEntryID)
return MAPI_E_INSUFFICIENT_MEMORY;

if (lpSrc->ulEIDSize)
memcpy (lpDest->lpEntryID, lpSrc->lpEntryID, (size_t)lpSrc->ulEIDSize);
}
else
lpDest->lpEntryID = NULL;

return SUCCESS_SUCCESS;

}

/*
- GetNextFile
-
* Purpose:
* Called when user clicks 'Attach' button in Compose Note form.
* We will build a chained memory chunk for mmore than one file
* attachment so the memory can be freed with a single call to
* PvFree.
*
* Parameters:
* hWnd - Window handle of Compose Note dialog
* nPos - Render position of attachment in Notetext.
* lpcAttach - Pointer to the count of attachments.
* lppAttach - Pointer to the MapiFileDesc array.
*
* Return:
* ulResult.
*/

ULONG
GetNextFile (HWND hWnd, ULONG nPos, ULONG * lpcAttach,
lpMapiFileDesc * lppAttach)
{
lpMapiFileDesc lpAttach;
lpMapiFileDesc lpAttachT;
OPENFILENAME ofn;
char szFileName[256] = "";
char szFilter[256];
static char szFileTitle[16];
static char szDirName[256] = "";
LPSTR lpszEndPath;
ULONG idx;
ULONG ulResult = SUCCESS_SUCCESS;

if (!szDirName[0])
GetSystemDirectory ((LPSTR) szDirName, 255);
else
lstrcpy (szFileName, szFileTitle);

LoadString(hInst, IDS_FILTER, szFilter, sizeof(szFilter));

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 | OFN_HIDEREADONLY;

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

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

lstrcpy (szDirName, szFileName);
if (lpszEndPath = strstr (szDirName, szFileTitle))
*(--lpszEndPath) = '\0';

lpAttach = (lpMapiFileDesc)PvAlloc(((*lpcAttach) + 1) * sizeof (MapiFileDesc));

if(!lpAttach)
goto err;

memset (lpAttach, 0, (size_t)(*lpcAttach + 1) * sizeof (MapiFileDesc));

lpAttachT = *lppAttach;

for (idx = 0; idx < *lpcAttach; idx++)
if(ulResult = CopyAttachment (lpAttach, &lpAttach[idx], &lpAttachT[idx]))
goto err;

lpAttach[idx].ulReserved = 0;
lpAttach[idx].flFlags = 0;
lpAttach[idx].nPosition = (ULONG)(-1);
lpAttach[idx].lpFileType = NULL;

lpAttach[idx].lpszPathName = (LPTSTR)PvAllocMore(lstrlen (szFileName) + 1,
(LPVOID)lpAttach);

if(!lpAttach[idx].lpszPathName)
goto err;

lpAttach[idx].lpszFileName = (LPTSTR)PvAllocMore(lstrlen (szFileTitle) + 1,
(LPVOID)lpAttach);

if(!lpAttach[idx].lpszFileName)
goto err;

lstrcpy (lpAttach[idx].lpszPathName, szFileName);
lstrcpy (lpAttach[idx].lpszFileName, szFileTitle);

PvFree(lpAttachT);

*lppAttach = lpAttach;
(*lpcAttach)++;

err:
if(ulResult)
PvFree(lpAttach);

return ulResult;
}

/*
- CopyAttachment
-
* Purpose:
* Called in support of GetNextFile() to re-build an array
* of chained MapiFileDescs.
*
* Parameters:
* lpParent - Parent memory that allocations get chained to
* lpDest - Destination Recipient
* lpSrc - Source Recipient
*
* Return:
* Void.
*/

ULONG
CopyAttachment (lpMapiFileDesc lpParent,
lpMapiFileDesc lpDest,
lpMapiFileDesc lpSrc)
{
lpDest->ulReserved = lpSrc->ulReserved;
lpDest->flFlags = lpSrc->flFlags;
lpDest->nPosition = lpSrc->nPosition;
lpDest->lpFileType = lpSrc->lpFileType;

if (lpSrc->lpszPathName)
{
lpDest->lpszPathName = (LPTSTR)PvAllocMore(lstrlen (lpSrc->lpszPathName) + 1,
(LPVOID)lpParent);

if (!lpDest->lpszPathName)
return MAPI_E_INSUFFICIENT_MEMORY;

lstrcpy (lpDest->lpszPathName, lpSrc->lpszPathName);
}
else
lpDest->lpszPathName = NULL;

if (lpSrc->lpszFileName)
{
lpDest->lpszFileName = (LPTSTR)PvAllocMore(lstrlen (lpSrc->lpszFileName) + 1,
(LPVOID)lpParent);

if (!lpDest->lpszFileName)
return MAPI_E_INSUFFICIENT_MEMORY;

lstrcpy (lpDest->lpszFileName, lpSrc->lpszFileName);
}
else
lpDest->lpszFileName = NULL;

return SUCCESS_SUCCESS;

}

/*
- FNameInList
-
* Purpose:
* To find lpszName in an array of recipients. Used to determine
* if user name has already been resolved.
*
* Parameters:
* lpszName - Friendly name to search for
* cRecips - Count of recipients in lpRecips
* lpRecips - Array of MapiRecipDescs
*
* Return:
* TRUE/FALSE
*/

BOOL
FNameInList (LPSTR lpszName, ULONG cRecips, lpMapiRecipDesc lpRecips)
{
/* Case sensitive compare of each friendly name in list. */

if (!cRecips || !lpRecips)
return FALSE;

while (cRecips--)
if (!lstrcmp (lpszName, lpRecips[cRecips].lpszName))
return TRUE;

return FALSE;
}


/*
- MakeMsgNode
-
* Purpose:
* Allocate memory for a new MSGID node and initialize its
* data members to the values passed in.
*
* Parameters:
* lpMsg - Pointer to a MapiMessage
* lpszMsgID - Opaque message identifier
*
* Return:
* lpMsgNode - Pointer to new node
*/

LPMSGID
MakeMsgNode (lpMapiMessage lpMsg, LPSTR lpszMsgID)
{
LPMSGID lpMsgNode = NULL;

if (!lpMsg || !lpszMsgID)
goto err;

lpMsgNode = (LPMSGID)PvAlloc(sizeof (MSGID));

if (!lpMsgNode)
goto err;

memset (lpMsgNode, 0, sizeof (MSGID));

if (lpMsg->nFileCount)
lpMsgNode->fHasAttach = TRUE;

if (lpMsg->flFlags & MAPI_UNREAD)
lpMsgNode->fUnRead = TRUE;

lpMsgNode->lpszMsgID = (LPTSTR)PvAllocMore(lstrlen (lpszMsgID) + 1,
(LPVOID)lpMsgNode);

if (!lpMsgNode->lpszMsgID)
goto err;

lstrcpy (lpMsgNode->lpszMsgID, lpszMsgID);

if (lpMsg->lpOriginator && lpMsg->lpOriginator->lpszName)
{
lpMsgNode->lpszFrom = (LPTSTR)PvAllocMore(lstrlen(lpMsg->lpOriginator->lpszName) + 1,
(LPVOID)lpMsgNode);

if (!lpMsgNode->lpszFrom)
goto err;

lstrcpy (lpMsgNode->lpszFrom, lpMsg->lpOriginator->lpszName);
}

if (lpMsg->lpszSubject)
{
lpMsgNode->lpszSubject = (LPTSTR)PvAllocMore(lstrlen (lpMsg->lpszSubject) + 1,
(LPVOID)lpMsgNode);

if (!lpMsgNode->lpszSubject)
goto err;

lstrcpy (lpMsgNode->lpszSubject, lpMsg->lpszSubject);
}

if (lpMsg->lpszDateReceived)
{
lpMsgNode->lpszDateRec = (LPTSTR)PvAllocMore(lstrlen (lpMsg->lpszDateReceived) + 1,
(LPVOID)lpMsgNode);

if (!lpMsgNode->lpszDateRec)
goto err;

lstrcpy (lpMsgNode->lpszDateRec, lpMsg->lpszDateReceived);
}

return lpMsgNode;

err:
PvFree(lpMsgNode);
return NULL;
}

/*
- InsertMsgNode
-
* Purpose:
* Currently (for simplicity) we will insert the nodes
* at the beginning of the list. This can later be
* replaced with a routine that can insert sorted on
* different criteria, like DateReceived, From, or
* Subject. But for now...
*
* Parameters:
* lpMsgNode - Pointer to a MSGID node
* lppMsgHead - Pointer to the head of the list
*
* Return:
* Void.
*/

void
InsertMsgNode (LPMSGID lpMsgNode, LPMSGID * lppMsgHead)
{
if (*lppMsgHead)
{
lpMsgNode->lpNext = *lppMsgHead;
(*lppMsgHead)->lpPrev = lpMsgNode;
}
else
lpMsgNode->lpNext = NULL;

/* The next 2 assignments are here in case the node came from somewhere */
/* other than a call to MakeMsgNode () in which case we aren't sure */
/* they're already NULL. */

lpMsgNode->lpPrev = NULL;
*lppMsgHead = lpMsgNode;
}

/*
- DeleteMsgNode
-
* Purpose:
* Removes the node passed in from the list. This
* may seem like a strange way to do this but it's
* not, because the Owner-Drawn List Box gives us
* direct access to elements in the list that makes
* it easier to do things this way.
*
* Parameters:
* lpMsgNode - Pointer to the MSGID node to delete
* lppMsgHead - Pointer to the head of the list
*
* Return:
* Void.
*/

void
DeleteMsgNode (LPMSGID lpMsgNode, LPMSGID * lppMsgHead)
{
if (!lpMsgNode)
return;

/* Check if we are the first node */

if (lpMsgNode->lpPrev)
lpMsgNode->lpPrev->lpNext = lpMsgNode->lpNext;

/* Check if we are the last node */

if (lpMsgNode->lpNext)
lpMsgNode->lpNext->lpPrev = lpMsgNode->lpPrev;

/* check if we are the only node */

if(lpMsgNode == *lppMsgHead)
*lppMsgHead = NULL;

PvFree(lpMsgNode);
return;
}



/*
- FindNode
-
* Purpose:
* Returns a pointer to the node containing lpszMsgID.
* Returns NULL if node doesn't exist or lpszMsgID is NULL.
*
* Parameters:
* lpMsgHead - Pointer to the head of the list
* lpszMsgID - Message ID to search for
*
* Return:
* lpMsgNode - Pointer to the node returned
*/

LPMSGID
FindNode (LPMSGID lpMsgHead, LPSTR lpszMsgID)
{
if (!lpszMsgID)
return NULL;

while (lpMsgHead)
{
if (!lstrcmp (lpMsgHead->lpszMsgID, lpszMsgID))
break;

lpMsgHead = lpMsgHead->lpNext;
}

return lpMsgHead;
}



/*
- FreeMsgList
-
* Purpose:
* Walks down the MsgList and frees each node.
*
* Parameters:
* lpMsgHead - Pointer to the head of the list
*
* Return:
* Void.
*/

void
FreeMsgList (LPMSGID lpMsgHead)
{
LPMSGID lpT;

while (lpMsgHead)
{
lpT = lpMsgHead;
lpMsgHead = lpMsgHead->lpNext;
PvFree(lpT);
}
}

/*
- MakeDisplayNameStr
-
* Purpose:
* Finds all recipients of type ulRecipClass in lpRecips and adds
* their friendly name to the display string.
*
* Parameters:
* lpszDisplay - Destination string for names
* ulRecipClass - Recipient types to search for
* cRecips - Count of recipients in lpRecips
* lpRecips - Pointer to array of MapiRecipDescs
*
* Return:
* Void.
*/

void
MakeDisplayNameStr (LPSTR lpszDisplay, ULONG ulRecipClass,
ULONG cRecips, lpMapiRecipDesc lpRecips)
{
ULONG idx;

*lpszDisplay = '\0';

for (idx = 0; idx < cRecips; idx++)
{
if (lpRecips[idx].ulRecipClass == ulRecipClass)
{
lstrcat (lpszDisplay, lpRecips[idx].lpszName);
lstrcat (lpszDisplay, "; ");
}
}

if (*lpszDisplay)
lpszDisplay[lstrlen (lpszDisplay) - 2] = '\0';
}



/*
- SaveMsgChanges
-
* Purpose:
* If while reading a message the user changes the notetext at all
* then this function is called to save those changes in the Inbox.
*
* Parameters:
* hWnd - handle to the window/dialog who called us
* lpMsg - pointer to the MAPI message to be saved
* lpszMsgID - ID of the message to save
*
* Return:
* ulResult - Indicating success/failure
*/

ULONG
SaveMsgChanges (HWND hWnd, lpMapiMessage lpMsg, LPSTR lpszMsgID)
{
LPSTR lpszT;
LPSTR lpszNoteText = NULL;
LONG cLines, cb;
ULONG ulResult = MAPI_E_INSUFFICIENT_MEMORY;

lpszT = lpMsg->lpszNoteText;

cLines = SendDlgItemMessage (hWnd, IDC_READNOTE, EM_GETLINECOUNT, 0, 0L);
cb = SendDlgItemMessage (hWnd, IDC_READNOTE, EM_LINEINDEX, (UINT)cLines - 1, 0L);
cb += SendDlgItemMessage (hWnd, IDC_READNOTE, EM_LINELENGTH, (UINT)cb, 0L);
cb += cLines * 2;

lpszNoteText = (LPTSTR)PvAlloc(cb + 1);

if (!lpszNoteText)
goto err;

SendDlgItemMessage (hWnd, IDC_READNOTE, WM_GETTEXT,
(WPARAM) cb, (LPARAM) lpszNoteText);

lpMsg->lpszNoteText = lpszNoteText;
ulResult = MAPISaveMail (lhSession, (ULONG) hWnd, lpMsg, MAPI_LONG_MSGID,
0, lpReadMsgNode->lpszMsgID);

PvFree(lpszNoteText);

err:
lpMsg->lpszNoteText = lpszT;
return ulResult;
}



/*
- MakeNewMessage
-
* Purpose:
* This function is used to construct a new message for the
* ComposeNote UI. This gets called as a result of a Reply,
* ReplyAll, or a Forward action on a message being read.
* The destination for the new message is lpmsg, the global
* MapiMessage struct pointer used by ComposeNoteDlgProc.
* ComposeNoteDlgProc always frees the memory consumed by
* this object whether it allocated it or not.
*
* Parameters:
* lpSrcMsg - MapiMessage to be copied
* flType - Specifies the action that caused this call
* either: IDC_REPLY, IDC_REPLYALL, or IDC_FORWARD
*
* Return:
* ulResult - Indicates success/failure
*/

ULONG
MakeNewMessage (lpMapiMessage lpSrcMsg, UINT flType)
{
ULONG idx;
ULONG ulResult = SUCCESS_SUCCESS;

if (!lpSrcMsg)
return MAPI_E_FAILURE;

lpmsg = (lpMapiMessage)PvAlloc(sizeof (MapiMessage));

if (!lpmsg)
goto err;

memset (lpmsg, 0, sizeof (MapiMessage));

lpmsg->flFlags = flSendMsgFlags;

if (lpSrcMsg->lpszSubject)
{
lpmsg->lpszSubject = (LPTSTR)PvAlloc(lstrlen(lpSrcMsg->lpszSubject) + 5);

if (!lpmsg->lpszSubject)
goto err;

if (flType == IDC_FORWARD)
lstrcpy (lpmsg->lpszSubject, "FW: ");
else
lstrcpy (lpmsg->lpszSubject, "RE: ");

lstrcat (lpmsg->lpszSubject, lpSrcMsg->lpszSubject);
}

if (lpSrcMsg->lpszNoteText)
{
lpmsg->lpszNoteText = (LPTSTR)PvAlloc(lstrlen(lpSrcMsg->lpszNoteText) + 32);

if (!lpmsg->lpszNoteText)
goto err;

lstrcpy (lpmsg->lpszNoteText, "\r\n--------------------------\r\n");
lstrcat (lpmsg->lpszNoteText, lpSrcMsg->lpszNoteText);
}

if (lpSrcMsg->lpszMessageType)
{
lpmsg->lpszMessageType = (LPTSTR)PvAlloc(lstrlen (lpSrcMsg->lpszMessageType) + 1);


if (!lpmsg->lpszMessageType)
goto err;

lstrcpy (lpmsg->lpszMessageType, lpSrcMsg->lpszMessageType);
}

if (lpSrcMsg->lpszConversationID)
{
lpmsg->lpszConversationID = (LPTSTR)PvAlloc(lstrlen(lpSrcMsg->lpszConversationID) + 1);

if (!lpmsg->lpszConversationID)
goto err;

lstrcpy (lpmsg->lpszConversationID, lpSrcMsg->lpszConversationID);
}

if (lpSrcMsg->nFileCount && flType == IDC_FORWARD )
{
lpmsg->nFileCount = lpSrcMsg->nFileCount;

lpmsg->lpFiles = (lpMapiFileDesc)PvAlloc(lpmsg->nFileCount * sizeof (MapiFileDesc));

if (!lpmsg->lpFiles)
goto err;
memset (lpmsg->lpFiles, 0, (size_t)lpmsg->nFileCount * sizeof (MapiFileDesc));

for (idx = 0; idx < lpmsg->nFileCount; idx++)
{
CopyAttachment (lpmsg->lpFiles, &lpmsg->lpFiles[idx],
&lpSrcMsg->lpFiles[idx]);

if ((&lpmsg->lpFiles[idx])->nPosition != (ULONG) -1)
{
/*lpmsg->lpszNoteText[(&lpmsg->lpFiles[idx])->nPosition
+ lstrlen("\r\n--------------------------\r\n")] = '+';*/
(&lpmsg->lpFiles[idx])->nPosition = (ULONG) -1;

}


}
}

if (flType == IDC_REPLY || flType == IDC_REPLYALL)
{
ULONG idxSrc;

if(lpSrcMsg->lpOriginator)
lpmsg->nRecipCount = 1;

if (flType == IDC_REPLYALL)
lpmsg->nRecipCount += lpSrcMsg->nRecipCount;

if(!lpmsg->nRecipCount)
return ulResult;

lpmsg->lpRecips = (lpMapiRecipDesc)PvAlloc(lpmsg->nRecipCount * sizeof (MapiRecipDesc));

if (!lpmsg->lpRecips)
goto err;

memset (lpmsg->lpRecips, 0, (size_t)lpmsg->nRecipCount * sizeof (MapiRecipDesc));
idx = 0;

if(lpSrcMsg->lpOriginator)
{
lpSrcMsg->lpOriginator->ulRecipClass = MAPI_TO;
CopyRecipient (lpmsg->lpRecips, lpmsg->lpRecips,
lpSrcMsg->lpOriginator);
lpSrcMsg->lpOriginator->ulRecipClass = MAPI_ORIG;
idx = 1;
}

for (idxSrc = 0; idx < lpmsg->nRecipCount; idxSrc++, idx++)
CopyRecipient (lpmsg->lpRecips, &lpmsg->lpRecips[idx],
&lpSrcMsg->lpRecips[idxSrc]);
}

return ulResult;

err:
if(lpmsg)
{
PvFree(lpmsg->lpszSubject);
PvFree(lpmsg->lpszNoteText);
PvFree(lpmsg->lpszMessageType);
PvFree(lpmsg->lpszConversationID);
PvFree(lpmsg->lpRecips);
PvFree(lpmsg->lpFiles);
PvFree(lpmsg);
lpmsg = NULL;
}
return ulResult;
}



/*
- LogSendMail
-
* Purpose:
* Used to track how many messages were sent with this client.
* This information is used strictly for gathering stats on
* how many messages were pumped through the spooler/transport.
*
* Usage:
* Add the following to the win.ini file:
* [MAPI Client]
* LogFile=filepath
*
* where: filepath can be a full UNC path or some local path & file
*
* Parameters:
* ulResult - Currently unused; should be used to count errors
*
* Result:
* Void.
*/

void LogSendMail(ULONG ulResult)
{
char szLogFile[128];
char szCount[32];
OFSTRUCT ofs;
HFILE hf = HFILE_ERROR;
int cSent = 1;

if(!GetProfileString("MAPI Client", "LogFile", "mapicli.log",
szLogFile, sizeof(szLogFile)))
return;

if((hf = OpenFile(szLogFile, &ofs, OF_READWRITE)) == HFILE_ERROR)
{
if((hf = OpenFile(szLogFile, &ofs, OF_CREATE|OF_READWRITE)) == HFILE_ERROR)
return;
}
else
{
if(!_lread(hf, szCount, sizeof(szCount)))
{
_lclose(hf);
return;
}

cSent = atoi(szCount) + 1;
}

wsprintf(szCount, "%d", cSent);

_llseek(hf, 0, 0);

_lwrite(hf, szCount, lstrlen(szCount));
_lclose(hf);

return;
}



/*
- SaveFileAttachments
-
* Purpose:
* Displays a 'Save As' common dialog to allow the user to save
* file attachments contained in the current message.
*
* Parameters:
* hWnd - Window handle of calling WndProc
* cFiles - Count of the files in the file array
* lpFiles - Array of MapiFileDescs
*
* Return:
* Void.
*/

void SaveFileAttachments(HWND hWnd, lpMapiFileDesc lpFile)
{
OPENFILENAME ofn;
char szFileName[256] = "";
char szFilter[256];
static char szFileTitle[16];
static char szDirName[256] = "";
LPSTR lpszEndPath;
ULONG idx;

if (!lpFile)
return;

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

LoadString(hInst, IDS_FILTER, szFilter, sizeof(szFilter));

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

lstrcpy (szFileName, lpFile->lpszFileName);

ofn.lStructSize = sizeof (OPENFILENAME);
ofn.hwndOwner = hWnd;
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))
return;

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

lstrcpy (szDirName, szFileName);
if (lpszEndPath = strstr (szDirName, szFileTitle))
*(--lpszEndPath) = '\0';

/* Use CopyFile to carry out the operation. */

if(!CopyFile(lpFile->lpszPathName, szFileName, FALSE))
MakeMessageBox (hWnd, 0, IDS_SAVEATTACHERROR, MBS_ERROR);
}



/*
- ToggleMenuState
-
* Purpose:
* Enables/Disables menu items depending on the session state.
*
* Parameters:
* hWnd - handle to the window/dialog who called us
* fLoggedOn - TRUE if logged on, FALSE if logged off
*
* Return:
* Void.
*/

void ToggleMenuState(HWND hWnd, BOOL fLoggedOn)
{
EnableMenuItem (GetMenu (hWnd), IDM_LOGOFF, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_COMPOSE, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_READ, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_SEND, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_ADDRBOOK, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_DETAILS, !fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_LOGON, fLoggedOn);
EnableMenuItem (GetMenu (hWnd), IDM_EXIT, FALSE);
}

//
// SecureMenu
//
// Purpose:
// Enables/Disables Logon and Exit menu items.
// CMCLogon might yield control to Windows, so the user might be able to
// access the window menu (for example click Logon) after we call
// MAPILogon, but before it returns.
//
// Parameters:
// hWnd - handle to the window/dialog who called us
// fBeforeLogon - TRUE when this function is called when we are about
// to call MAPILogon, FALSE if called after logon (failed)
// if Logon succeddes ToggleMenuState is called instead of
// this function.
//
// Return:
// Void.
//


void SecureMenu(HWND hWnd, BOOL fBeforeLogon)
{
EnableMenuItem (GetMenu (hWnd), IDM_LOGON, fBeforeLogon);
EnableMenuItem (GetMenu (hWnd), IDM_EXIT, fBeforeLogon);
}