/*      SCCS Id: @(#)mapimail.c 3.4     $Date: 2003/03/12 11:15:16 $        */
/* Copyright (c) Michael Allison, 1997                  */
/* NetHack may be freely redistributed.  See license for details. */

#ifdef MAX_BODY_SIZE
#undef MAX_BODY_SIZE
#define MAX_BODY_SIZE           2048    /* largest body held in ram in bytes */
#endif

#include "hack.h"
# ifdef LAN_MAIL
#include "win32api.h"
#include <mapi.h>

#define MAPI_MSGID_SIZE 512     /* as recommended */
#define MAPI_LIB_FAIL           1
#define MAPI_FUNC_FAIL          2

HANDLE hLibrary;                        /* Handle for MAPI32.DLL     */
LHANDLE MAPISession;                    /* Handle for MAPI session   */
char MAPIMessageID[MAPI_MSGID_SIZE];    /* Msg ID from provider      */
lpMapiMessage MAPIMessage;              /* Ptr to message from prov  */
struct lan_mail_struct received_msg;    /* store received msg here   */
#ifdef MAPI_VERBOSE
FILE *dbgfile;
#define MAPIDEBUGFILENAME	"mapidebug.log"
#endif

/*
 * Prototypes for functions in this file
 * (Not in normal NetHack style, but its doubtful that any
 *  of the code in here is at all portable between platforms)
 */
static long __stdcall start_mailthread(LPVOID ThreadParam);
static boolean MAPI_mail_check(char *);
static boolean MAPI_mail_fetch(char *);
static void MAPI_mail_finish(void);
static int MAPI_mail_context(int *);
static boolean MAPI_mail_init(char *);
static int InitMAPI(void);
static int DeInitMAPI(void);
static void MAPI_mail_abort(unsigned long);

/*
 * Declare the function pointers used to access MAPI routines
 * from the MAPI32.DLL
 */
LPMAPILOGON     fpMAPILogon;
LPMAPILOGOFF    fpMAPILogoff;
LPMAPIFINDNEXT  fpMAPIFindNext;
LPMAPIREADMAIL  fpMAPIReadMail;
LPMAPIFREEBUFFER fpMAPIFreeBuffer;


/*
 * Data requiring synchronized access between the
 * two thread contexts contained in this file
 * (nethack thread and the MAPI-mail thread)
 */
HANDLE mailthread = 0;          /* handle for the mail-thread       */
unsigned nhmailthread_ID;       /* thread ID for mail-thread        */
unsigned long nhmail_param;     /* value passed to mail-thread      */
long mailthread_continue = 0;   /* nh -> mapi thread: shut down!    */
long mailthread_stopping = 0;   /* mapi -> nh thread: MAPI is ill!  */

/*
 * Data updated by the NetHack thread only
 * but read by the MAPI-mail thread.
 *
 */
char MAPI_username[120];
boolean debugmapi;

/*
 * Data updated by the MAPI-mail thread only
 * but read by the NetHack thread.
 */
long mail_fetched = 0;

/*
 * Data used only by the MAPI-mail thread.
 */
long mailpasses;        /* counts the FindNext calls issued to MAPI */
char msgID[80];         /* message ID of msg under manipulation     */

/*===============================================================
 * NetHack thread routines
 *
 * These routines run in the context of the main NetHack thread.
 * They are used to provide an interface between the NetHack
 * LAN_MAIL code and the MAPI-thread code.
 *
 * These routines are referenced by shared code in
 * sys/share/nhlan.c and they are prototyped in include/extern.h
 *===============================================================
 */
boolean mail_check()
{
	if (!mailthread_stopping) {
		if (mail_fetched > 0)
			return TRUE;
	} else lan_mail_terminate();
	return FALSE;
}


boolean mail_fetch(msg)
struct lan_mail_struct *msg;
{
	/* shouldn't need to check mailthread_stopping here
	   as the data should be valid anyway */
	*msg = received_msg;
	iflags.lan_mail_fetched = TRUE;
	/* clear the marker now so new messages can arrive */
	InterlockedDecrement(&mail_fetched);
	/* check to see if the MAPI-mail thread is saying "stop" */
	if (mailthread_stopping) lan_mail_terminate();
	return TRUE;
}

void mail_finish()
{
	InterlockedDecrement(&mailthread_continue);
}

void mail_init(uname)
char *uname;
{
  /* This routine invokes the _beginthreadex()
   * run-time library call to start the execution
   * of the MAPI-mail thread.  It also performs
   * few other preparatory tasks.
   */
	if (mailthread_continue) {
		/* Impossible - something is really messed up */
		/* We better do some sanity checking */
		/* Is there a valid thread handle and ID? */
		if (mailthread && nhmailthread_ID) {
			/* TODO: check 'em via WIN32 call */
			/* if valid, do something about them */
			/* if not, clear them */
		}
	}
	mailthread = 0;
	nhmailthread_ID = 0;
	mailthread_continue = 0;  /* no interlock needed yet */
	mailthread_stopping = 0;  /* no interlock needed yet */
#ifdef MAPI_VERBOSE
	if (getenv("DEBUG_MAPI")) debugmapi = TRUE; 
#endif
	if (uname)
		strcpy(MAPI_username, uname);
	else {
#ifdef MAPI_VERBOSE
		if (debugmapi) dbgfile = fopen(MAPIDEBUGFILENAME,"w");
		if (dbgfile) {
		       fprintf(dbgfile,
			      "mapi initialization bypassed because uname not available\n");
		       fclose(dbgfile);
		}
#endif
		return;
	}
	mailthread = (HANDLE)_beginthreadex(
			(void *)0,
			(unsigned)0,
			start_mailthread,
			(void *)&nhmail_param,
			(unsigned)0, (unsigned *)&nhmailthread_ID);
#if 0
	/* TODO: check for failure of the thread. For now nethack
	 *       doesn't care
	 */
	if (!mailthread) {

	}
#endif
}

/*===============================================================
 * MAPI-mail thread routines
 *
 * These routines run in the context of their own
 * MAPI-mail thread.  They were placed into their own
 * thread, because MAPI calls can sometimes (often?) take
 * a horribly long time to return (minutes even).
 *
 * Each of the routines below are referenced only by other
 * routines below, with the exception of start_mailthread(),
 * of course, which is started by a run-time library call
 * issued from the main NetHack thread.
 *===============================================================
 */

/*
 * start_mailthread() is the entry point of the MAPI-mail thread.
 *
 */

static long __stdcall start_mailthread(LPVOID ThreadParam)
{
    char *lu = MAPI_username;
    if(MAPI_mail_init(lu)) {
	InterlockedIncrement(&mailthread_continue);
	while (mailthread_continue) {
		mailpasses++;
		if (MAPI_mail_check(msgID)) {
			if (MAPI_mail_fetch(msgID)) {
				/* getting here means success */
			}
		}
		Sleep(MAILTHREADFREQ);
	}
#ifdef MAPI_VERBOSE
	if (debugmapi && !mailthread_continue) {
	  fprintf(dbgfile,
		"MAPI-thread detected mailthread_continue change.\n");
	  fprintf(dbgfile,
		"NetHack has requested that the MAPI-thread cease.\n");
	}
#endif
    }
    return 0;
}

static int
MAPI_mail_context(mcount)
int *mcount;
{
	unsigned long status;
	char tmpID[80];
	int count = 0;

	tmpID[0] = '\0';
	MAPIMessageID[0] = '\0';

	/* Get the ID of the first unread message */
	status = fpMAPIFindNext(MAPISession, 0, 0, 0,
		MAPI_UNREAD_ONLY | MAPI_GUARANTEE_FIFO,
		0, tmpID);
	/* Now loop through them all until we have no more */
	while (status == SUCCESS_SUCCESS) {
		strcpy(MAPIMessageID, tmpID);
		count++;
		status = fpMAPIFindNext(MAPISession, 0,
			0, MAPIMessageID,
			MAPI_UNREAD_ONLY | MAPI_GUARANTEE_FIFO,
			0, tmpID);
	}
	if (status == MAPI_E_NO_MESSAGES) {
		/* context is now at last message */
		if (mcount) *mcount = count;
		return SUCCESS_SUCCESS;
	}
	return status;
}

static boolean
MAPI_mail_check(mID)
char *mID;
{
	unsigned long status;
	char tmpID[80];

	tmpID[0] = '\0';

#ifdef MAPI_VERBOSE
	if (debugmapi) fprintf(dbgfile, "MAPI_mail_check() ");
#endif
	if (mail_fetched) {
#ifdef MAPI_VERBOSE
	if (debugmapi) fprintf(dbgfile, "returning FALSE (buffer occupied)\n");
#endif
		return FALSE; /* buffer occupied, don't bother */
	}
	/* Get the ID of the next unread message if there is one */
	status = fpMAPIFindNext(MAPISession, 0, 0, MAPIMessageID,
		MAPI_UNREAD_ONLY | MAPI_GUARANTEE_FIFO,
		0, tmpID);
	if (status == SUCCESS_SUCCESS) {
		strcpy(mID, tmpID);
#ifdef MAPI_VERBOSE
	if (debugmapi) fprintf(dbgfile, "returning TRUE\n");
#endif
		return TRUE;
	}
	if (status == MAPI_E_NO_MESSAGES) {
#ifdef MAPI_VERBOSE
		if (debugmapi) fprintf(dbgfile, "returning FALSE\n");
#endif
		return FALSE;
	}
#ifdef MAPI_VERBOSE
	if (debugmapi) fprintf(dbgfile,"Error, check_newmail() status: %d\n", status);
	MAPI_mail_abort(status);
#endif
	return FALSE;
}

static boolean
MAPI_mail_fetch(mID)
char *mID;
{
	unsigned long status;

#ifdef MAPI_VERBOSE
	if (debugmapi) fprintf(dbgfile, "MAPI_mail_fetch() ");
#endif
	/*
	 *  Update context right away so we don't loop if there
	 *  was a problem getting the message
	 */
	strcpy(MAPIMessageID, mID);

	if (mail_fetched) {
#ifdef MAPI_VERBOSE
	if (debugmapi) fprintf(dbgfile, "returning FALSE (buffer occupied)\n");
#endif
		 return FALSE;  /* buffer occupied */
	}

	status = fpMAPIReadMail(MAPISession, 0, mID,
		 MAPI_SUPPRESS_ATTACH | MAPI_PEEK,
		 0, &MAPIMessage);
	if (status == SUCCESS_SUCCESS) {
		strncpy(received_msg.subject,
			MAPIMessage->lpszSubject,
			sizeof(received_msg.subject) - 1);
		if((MAPIMessage->lpOriginator->lpszName != (char *)0)
		 && MAPIMessage->lpOriginator->lpszName[0] != '\0')
			strncpy(received_msg.sender,
				MAPIMessage->lpOriginator->lpszName,
				sizeof(received_msg.sender) - 1);
		else
			strncpy(received_msg.sender,
				MAPIMessage->lpOriginator->lpszAddress,
				sizeof(received_msg.sender) - 1);
		strncpy(received_msg.body,
			MAPIMessage->lpszNoteText,MAX_BODY_SIZE - 1);
		received_msg.body[MAX_BODY_SIZE - 1] = '\0';
		received_msg.body_in_ram = TRUE;
		status = fpMAPIFreeBuffer(MAPIMessage);
		InterlockedIncrement(&mail_fetched);
#ifdef MAPI_VERBOSE
		if (debugmapi) fprintf(dbgfile, "returning TRUE\n");
#endif
		return TRUE;
	}
#ifdef MAPI_VERBOSE
	else
		if (debugmapi) fprintf(dbgfile,"MAPIRead failed, status = %d\n", status);
	if (debugmapi) fprintf(dbgfile, "returning FALSE (failed)\n");
#endif
	return FALSE;
}

static void
MAPI_mail_finish()
{
	InterlockedIncrement(&mailthread_stopping);
	(void) fpMAPILogoff(MAPISession,0,0,0);
	(void) DeInitMAPI();
#ifdef MAPI_VERBOSE
	if (debugmapi) fclose(dbgfile);
#endif
	(void) _endthreadex(0);
}

static void
MAPI_mail_abort(reason)
unsigned long reason;
{
#ifdef MAPI_VERBOSE
	if (debugmapi) fprintf(dbgfile,
		"Terminating MAPI-thread due to error %d.\n", reason);
#endif
	MAPI_mail_finish();
}

static boolean
MAPI_mail_init(uname)
char *uname;
{
	unsigned long status;
	int count = 0;

#ifdef MAPI_VERBOSE
	if (debugmapi) dbgfile = fopen(MAPIDEBUGFILENAME,"w");
	if (debugmapi) fprintf(dbgfile,"Hello %s, NetHack is initializing MAPI.\n",
		uname);
#endif
	status = InitMAPI();
	if (status) {
#ifdef MAPI_VERBOSE
	    if (debugmapi) fprintf(dbgfile,"Error initializing MAPI %d\n", status);
#endif
	    return FALSE;
	}
	status = fpMAPILogon(0,uname,0L,0L,0L,(LPLHANDLE)&MAPISession);
	if (status != SUCCESS_SUCCESS) {
#ifdef MAPI_VERBOSE
	    if (debugmapi) fprintf(dbgfile,"Status of MAPI logon is %d\n", status);
#endif
	    return FALSE;
	}
#ifdef MAPI_VERBOSE
	if (debugmapi) fprintf(dbgfile,
		"Stage 1 of MAPI initialization successful.\n");
	if (debugmapi) fprintf(dbgfile,"MAPI Session handle: %d\n", MAPISession);
#endif
	status = MAPI_mail_context(&count);
	if (status == SUCCESS_SUCCESS) {
#ifdef MAPI_VERBOSE
	    if (debugmapi) fprintf(dbgfile,
		    "Stage 2 of MAPI initialization successful.\n");
	    if (debugmapi) fprintf(dbgfile,"Detected %d old unread messages.\n",
		    count);
#endif
	    return TRUE;
	}
#ifdef MAPI_VERBOSE
	if (debugmapi) fprintf(dbgfile,
		"Error, status of MAPI_mail_context() is %d.\n",
		status);
	if (debugmapi) fprintf(dbgfile,
		"Dismantling MAPI interface and cleaning up.\n");
#endif
	MAPI_mail_finish();
	return FALSE;
}

int InitMAPI()
{

    if (!(hLibrary = LoadLibrary("MAPI32.DLL")))
      return MAPI_LIB_FAIL;

    if ((fpMAPILogon = (LPMAPILOGON)GetProcAddress(hLibrary,"MAPILogon")) == NULL)
      return MAPI_FUNC_FAIL;

    if ((fpMAPILogoff = (LPMAPILOGOFF)GetProcAddress(hLibrary,"MAPILogoff")) == NULL)
      return MAPI_FUNC_FAIL;

    if ((fpMAPIFindNext= (LPMAPIFINDNEXT)GetProcAddress(hLibrary,"MAPIFindNext")) == NULL)
      return MAPI_FUNC_FAIL;

    if ((fpMAPIReadMail= (LPMAPIREADMAIL)GetProcAddress(hLibrary,"MAPIReadMail")) == NULL)
      return MAPI_FUNC_FAIL;

    if ((fpMAPIFindNext= (LPMAPIFINDNEXT)GetProcAddress(hLibrary,"MAPIFindNext")) == NULL)
      return MAPI_FUNC_FAIL;

    if ((fpMAPIFreeBuffer= (LPMAPIFREEBUFFER)GetProcAddress(hLibrary,"MAPIFreeBuffer")) == NULL)
      return MAPI_FUNC_FAIL;

#ifdef MAPI_VERBOSE
    if (debugmapi) {
	fprintf(dbgfile,"Entry Points:\n");
    	fprintf(dbgfile,"MAPILogon      = %p\n",fpMAPILogon);
    	fprintf(dbgfile,"MAPILogoff     = %p\n",fpMAPILogoff);
    	fprintf(dbgfile,"MAPIFindNext   = %p\n",fpMAPIFindNext);
   	fprintf(dbgfile,"MAPIFreeBuffer = %p\n",fpMAPIReadMail);
    	fprintf(dbgfile,"MAPIReadMail   = %p\n",fpMAPIFreeBuffer);
    }
#endif
    return 0;
}

int DeInitMAPI()
{

    fpMAPILogon = NULL;
    fpMAPILogoff= NULL;
    fpMAPIFindNext= NULL;
    fpMAPIReadMail= NULL;
    fpMAPIFreeBuffer = NULL;
    FreeLibrary(hLibrary);
    return 0;
}

#endif /*LAN_MAIL*/

