/*
 * Xailer source code:
 *
 * Mapi.prg
 * Soporte para simple MAPI
 *
 * Copyright 2003, 2007 Jose Lalin
 * Copyright 2003, 2007 Xailer.com
 * All rights reserved
 *
 */

#include "Xailer.ch"
#include "WinReg.api"

#define MAPI_LOGON_UI            0x00000001
#define MAPI_NEW_SESSION         0x00000002
#define MAPI_PASSWORD_UI         0x00020000
#define MAPI_DIALOG              0x00000008
#define MAPI_RECEIPT_REQUESTED   0x00000002


//--------------------------------------------------------------------------

CLASS XMapi FROM TComponent

PUBLISHED:
   PROPERTY cName             INIT ""
   PROPERTY cPassword         INIT ""

   PROPERTY cFromName         INIT ""
   PROPERTY cFromAddress      INIT ""

   PROPERTY cSubject          INIT ""
   PROPERTY cMessage          INIT ""

   PROPERTY cMsgType          INIT ""
   PROPERTY cConversationID   INIT ""

   PROPERTY dDate             INIT Date()
   PROPERTY cTime             INIT Time()

   PROPERTY aReceipts         INIT {}
   PROPERTY aAttachments      INIT {}

   PROPERTY lUseUI            INIT .F. WRITE INLINE ::FlUseUI := ::SetLogonFlag( MAPI_LOGON_UI, Value )
   PROPERTY lAskPassword      INIT .F. WRITE INLINE ::FlAskPassword := ::SetLogonFlag( MAPI_PASSWORD_UI, Value )
   PROPERTY lNewSession       INIT .F. WRITE INLINE ::FlNewSession := ::SetLogonFlag( MAPI_NEW_SESSION, Value )

   PROPERTY lAskRecipients    INIT .F. WRITE INLINE ::FlAskRecipients := ::SetSendFlag( MAPI_DIALOG, Value )
   PROPERTY lReceipt          INIT .F. WRITE INLINE ::FlReceipt := ::SetSendFlag( MAPI_RECEIPT_REQUESTED, Value )

   PROPERTY cAccount          INIT "" READ METHOD GetAccount WRITE METHOD SetAccount

PUBLIC:
   DATA nLastError   INIT 0   READONLY
   DATA hSession     INIT 0   READONLY
   DATA lLoggedOn    INIT .F. READONLY

   METHOD Create( oParent ) CONSTRUCTOR
   METHOD Destroy() // --> Nil

   METHOD Installed() // --> lResult

   METHOD Logon() // --> lSuccess
   METHOD Logoff() // --> lSuccess

   METHOD Send()  // --> lSuccess

   METHOD AddReceipt( cName, cAddress, nType ) // --> Nil

   METHOD GetAccountNames( lSorted )   // --> aAccountNames

RESERVED:
   METHOD GetAccount()
   METHOD SetAccount( cAccount )

PROTECTED:
   DATA hLib         INIT 0
   DATA nLogonFlags  INIT 0
   DATA nSendFlags   INIT 0

   METHOD SetLogonFlag( nFlag, Value )
   METHOD SetSendFlag( nFlag, Value )

   METHOD Initialize()
   METHOD Uninitialize()

ENDCLASS

//--------------------------------------------------------------------------

METHOD Create( oParent ) CLASS XMapi

   ::Super:Create( oParent )

   ::Initialize()

RETURN Self

//--------------------------------------------------------------------------

METHOD Destroy() CLASS XMapi

   ::Uninitialize()

RETURN Nil

//--------------------------------------------------------------------------

METHOD SetLogonFlag( nFlag, Value ) CLASS XMapi

   IF Value
      ::nLogonFlags := nOr( ::nLogonFlags, nFlag )
   ELSE
      ::nLogonFlags := nExclude( ::nLogonFlags, nFlag )
   ENDIF

RETURN Value

//--------------------------------------------------------------------------

METHOD SetSendFlag( nFlag, Value ) CLASS XMapi

   IF Value
      ::nSendFlags := nOr( ::nSendFlags, nFlag )
   ELSE
      ::nSendFlags := nExclude( ::nSendFlags, nFlag )
   ENDIF

RETURN Value

//--------------------------------------------------------------------------

METHOD AddReceipt( cName, cAddress, nType ) CLASS XMapi

   DEFAULT cName  TO "", ;
           nType  TO mapiTO

   IF Empty( cAddress ) .AND. ! Empty( cName ) .AND. "@" $ cName
		cAddress := cName
   ENDIF

   IF Empty( cName ) .AND. ! Empty( cAddress )
		cName := cAddress
   ENDIF

   AAdd( ::aReceipts, { cName, cAddress, nType } )

RETURN Nil

//--------------------------------------------------------------------------

#pragma BEGINDUMP

#include <windows.h>
#include <mapi.h>
#ifndef __GNUC__
   #include <mapix.h>
#endif
#include <xailer.h>

#define MAPI_DLL  "MAPI32.DLL"

typedef HRESULT FAR PASCAL (* PMAPIInit)( LPVOID );
typedef VOID FAR PASCAL (* PMAPIUninit)( void );

typedef ULONG FAR PASCAL (* PMAPILogon)( ULONG ulUIParam,
                              LPSTR lpszName, LPSTR lpszPassword,
                              FLAGS flFlags, ULONG ulReserved,
                              LPLHANDLE lplhSession );

typedef ULONG FAR PASCAL (* PMAPILogoff)( LHANDLE lhSession,
                              ULONG ulUIParam, FLAGS flFlags,
                              ULONG ulReserved );

typedef ULONG FAR PASCAL (* PMAPISendMail)( LHANDLE lhSession,
                           ULONG ulUIParam, lpMapiMessage lpMessage,
                           FLAGS flFlags, ULONG ulReserved );

//--------------------------------------------------------------------------

HB_FUNC_STATIC( XMAPI_INITIALIZE )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HINSTANCE hLib = LoadLibrary( MAPI_DLL );

   if( hLib >= (HINSTANCE) 32 )
   {
      PMAPIInit fnMAPIInit = (PMAPIInit) GetProcAddress( hLib, "MAPIInitialize" );

      if( fnMAPIInit )
      {
         HRESULT hr;
         MAPIINIT_0 mapiinit;

         memset( &mapiinit, 0, sizeof( MAPIINIT_0 ) );
         hr = fnMAPIInit( &mapiinit );

         if( FAILED( hr ) )
            XA_ObjSendNL( Self, "_nLastError", HRESULT_CODE( hr ) );
      }

      XA_ObjSendNL( Self, "_hLib", (LONG) hLib );
   }
}

//--------------------------------------------------------------------------

HB_FUNC_STATIC( XMAPI_UNINITIALIZE )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HINSTANCE hLib = (HINSTANCE) XA_ObjGetNL( Self, "hLib" );

   if( hLib >= (HINSTANCE) 32 )
   {
      PMAPIUninit fnMAPIUninit = (PMAPIUninit) GetProcAddress( hLib, "MAPIUninitialize" );

      if( fnMAPIUninit )
         fnMAPIUninit();

      FreeLibrary( hLib );
      XA_ObjSendNL( Self, "_hLib", 0 );
   }
}

//--------------------------------------------------------------------------

HB_FUNC_STATIC( XMAPI_INSTALLED )
{
   hb_retl( SearchPath( NULL, "mapi32.dll", NULL, 0, NULL, NULL ) != 0 );
}

//--------------------------------------------------------------------------

HB_FUNC_STATIC( XMAPI_LOGON )
{
   PHB_ITEM Self = hb_stackSelfItem();
   BOOL bLogged = XA_ObjGetL( Self, "lLoggedOn" );
   BOOL bSuccess = FALSE;

   if( ! bLogged )
   {
      HINSTANCE hLib = (HINSTANCE) XA_ObjGetNL( Self, "hLib" );
      PMAPILogon fnMAPILogon = (PMAPILogon) GetProcAddress( hLib, "MAPILogon" );

      if( fnMAPILogon )
      {
         PHB_ITEM oParent = XA_ObjGetItemCopy( Self, "oParent" );
         LPSTR szUser = XA_ObjGetLen( Self, "cName" ) ? XA_ObjGetCBuf( Self, "cName" ) : NULL;
         LPSTR szPass = XA_ObjGetLen( Self, "cPassword" ) ? XA_ObjGetCBuf( Self, "cPassword" ) : NULL;

         HWND hwndOwner;
         ULONG ulResult;
         LHANDLE hSession;

         if( HB_IS_OBJECT( oParent ) )
         {
            hwndOwner = GetHandleOf( oParent );
            hb_itemRelease( oParent );
         }
         else
            hwndOwner = GetActiveWindow();

         if( ! hwndOwner )
            hwndOwner = GetDesktopWindow();

         ulResult = (* fnMAPILogon)(   (ULONG) hwndOwner,
                                       szUser, szPass,
                                       (FLAGS) XA_ObjGetNL( Self, "nLogonFlags" ),
                                       0, (LPLHANDLE) &hSession );

         bSuccess = ( ulResult == SUCCESS_SUCCESS );

         XA_ObjSendL( Self, "_lLoggedOn", bSuccess );
         XA_ObjSendNL( Self, "_hSession", hSession );
         XA_ObjSendNL( Self, "_nLastError", ulResult );

         XA_FreeCBuf( szUser );
         XA_FreeCBuf( szPass );
      }
   }

   hb_retl( bSuccess );
}

//--------------------------------------------------------------------------

HB_FUNC_STATIC( XMAPI_LOGOFF )
{
   PHB_ITEM Self = hb_stackSelfItem();
   BOOL bLogged = XA_ObjGetL( Self, "lLoggedOn" );
   LHANDLE hSession = (LHANDLE) XA_ObjGetNL( Self, "hSession" );
   BOOL bSuccess = FALSE;

   if( bLogged && hSession )
   {
      HINSTANCE hLib = (HINSTANCE) XA_ObjGetNL( Self, "hLib" );

      PMAPILogoff fnMAPILogoff = (PMAPILogoff) GetProcAddress( hLib, "MAPILogoff" );

      if( fnMAPILogoff )
      {
         ULONG ulResult;

         ulResult = (* fnMAPILogoff)( hSession, 0L, 0L, 0L );

         bSuccess = ( ulResult == SUCCESS_SUCCESS );

         XA_ObjSendL( Self, "_lLoggedOn", ! bSuccess );
         XA_ObjSendNL( Self, "_nLastError", ulResult );
         XA_ObjSendNL( Self, "_hSession", 0 );
      }
   }

   hb_retl( bSuccess );
}

//--------------------------------------------------------------------------

HB_FUNC_STATIC( XMAPI_SEND )
{
   PHB_ITEM Self = hb_stackSelfItem();
   BOOL bSuccess = FALSE;

   if( XA_ObjGetL( Self, "lLoggedOn" ) )
   {
      HINSTANCE hLib = (HINSTANCE) XA_ObjGetNL( Self, "hLib" );
      PMAPISendMail fnMAPISendMail = (PMAPISendMail) GetProcAddress( hLib, "MAPISendMail" );

      if( fnMAPISendMail )
      {
         PHB_ITEM oParent = XA_ObjGetItemCopy( Self, "oParent" );
         LHANDLE hSession = (LHANDLE) XA_ObjGetNL( Self, "hSession" );
         HWND hwndOwner;
         ULONG ulResult;
         ULONG ulLen;

         MapiMessage msg;
         MapiRecipDesc lpOriginator;
         MapiRecipDesc lpRecip[ 10 ];
         MapiFileDesc  lpFiles[ 10 ];

         PHB_ITEM pArray;
         char cDatetime[20];

         if( HB_IS_OBJECT( oParent ) )
         {
            hwndOwner = GetHandleOf( oParent );
            hb_itemRelease( oParent );
         }
         else
            hwndOwner = GetActiveWindow();

         if( ! hwndOwner )
            hwndOwner = GetDesktopWindow();

         {
            int lYear, lMonth, lDay;
            PHB_ITEM pDate = XA_ObjGetItem( Self, "dDate" );

            hb_dateDecode( hb_itemGetDL( pDate ), &lYear, &lMonth, &lDay );

            sprintf( cDatetime, "%i/%i/%i %s", lYear, lMonth, lDay, XA_ObjGetC( Self, "cTime" ) );
         }

         memset( &msg, 0, sizeof( MapiMessage ) );

         msg.ulReserved         = 0;
         msg.lpszSubject        = XA_ObjGetLen( Self, "cSubject" ) ? XA_ObjGetC( Self, "cSubject" ) : NULL;
         msg.lpszNoteText       = XA_ObjGetLen( Self, "cMessage" ) ? XA_ObjGetC( Self, "cMessage" ) : NULL;
         msg.lpszMessageType    = NULL; //XA_ObjGetLen( Self, "cMsgType" ) ? XA_ObjGetC( Self, "cMsgType" ) : NULL;
         msg.lpszDateReceived   = cDatetime;
         msg.lpszConversationID = XA_ObjGetLen( Self, "cConversationID" ) ? XA_ObjGetC( Self, "cConversationID" ) : NULL;
         msg.flFlags            = XA_ObjGetNL( Self, "nSendFlags" );

         memset( &lpOriginator, 0, sizeof( MapiRecipDesc ) );

         lpOriginator.lpszName = XA_ObjGetLen( Self, "cFromName" ) ? XA_ObjGetC( Self, "cFromName" ) : NULL;
         lpOriginator.lpszAddress = XA_ObjGetLen( Self, "cFromAddress" ) ? XA_ObjGetC( Self, "cFromAddress" ) : NULL;
         lpOriginator.ulRecipClass = MAPI_ORIG;
         msg.lpOriginator = &lpOriginator;

         pArray = XA_ObjGetItemCopy( Self, "aReceipts" );
         ulLen = hb_arrayLen( pArray );

         if( ulLen )
         {
            ULONG ulCount;

            memset( &lpRecip, 0, sizeof( lpRecip ) );

            for( ulCount = 0; ulCount < ulLen; ulCount++ )
            {
               PHB_ITEM pSub;

               pSub = hb_itemArrayGet( pArray, ulCount + 1 );
               lpRecip[ ulCount ].lpszName     = hb_arrayGetC( pSub, 1 );
               lpRecip[ ulCount ].lpszAddress  = hb_arrayGetC( pSub, 2 );
               lpRecip[ ulCount ].ulRecipClass = hb_arrayGetNL( pSub, 3 ); //MAPI_TO, _CC, _BCC
               hb_itemRelease( pSub );
            }

            msg.nRecipCount = ulLen;
            msg.lpRecips    = lpRecip;

         }
         hb_itemRelease( pArray );

         pArray = XA_ObjGetItemCopy( Self, "aAttachments" );
         ulLen = hb_arrayLen( pArray );

         if( ulLen )
         {
            ULONG ulCount;

            memset( &lpFiles, 0, sizeof( lpFiles ) );

            for( ulCount = 0; ulCount < ulLen; ulCount++ )
            {
               PHB_ITEM pSub;

               pSub = hb_itemArrayGet( pArray, ulCount + 1 );
               lpFiles[ ulCount ].lpszFileName = hb_arrayGetC( pSub, 1 );
               lpFiles[ ulCount ].lpszPathName = hb_arrayGetC( pSub, 2 );
               lpFiles[ ulCount ].ulReserved   = 0 ;
               lpFiles[ ulCount ].nPosition    = -1;
               hb_itemRelease( pSub );
            }

            msg.nFileCount = ulLen;
            msg.lpFiles    = lpFiles;

         }
         hb_itemRelease( pArray );

         ulResult = (* fnMAPISendMail)( hSession, (ULONG) hwndOwner,
                                          &msg,
                                          XA_ObjGetNL( Self, "nSendFlags" ),
                                          0 );
         bSuccess = ( ulResult == SUCCESS_SUCCESS );
         XA_ObjSendNL( Self, "_nLastError", ulResult );

         // Liberar memoria
         for( ulLen = 0; ulLen < msg.nRecipCount; ulLen++ )
            {
            if( msg.lpRecips[ ulLen ].lpszName )
               hb_xfree( msg.lpRecips[ ulLen ].lpszName );
            if( msg.lpRecips[ ulLen ].lpszAddress )
               hb_xfree( msg.lpRecips[ ulLen ].lpszAddress );
            }
         for( ulLen = 0; ulLen < msg.nFileCount; ulLen++ )
            {
            if( msg.lpFiles[ ulLen ].lpszFileName )
               hb_xfree( msg.lpFiles[ ulLen ].lpszFileName );
            if( msg.lpFiles[ ulLen ].lpszPathName )
               hb_xfree( msg.lpFiles[ ulLen ].lpszPathName );
            }
      }
   }

   hb_retl( bSuccess );
}

#pragma ENDDUMP

//--------------------------------------------------------------------------

METHOD GetAccount() CLASS XMapi

   LOCAL cKey := ""

   WITH OBJECT TRegistry():New()
      :Root := HKEY_CURRENT_USER
      IF :OpenKey( "Software\Microsoft\Internet Account Manager" ) == 0
         cKey := :GetString( "Default Mail Account" )
         :CloseKey()
         IF :OpenKey( "Software\Microsoft\Internet Account Manager\Accounts\" + cKey ) == 0
            ::FcAccount := :GetString( "Account Name" )
            :CloseKey()
         ENDIF
      ENDIF
   END

RETURN ::FcAccount

//--------------------------------------------------------------------------

METHOD SetAccount( cAccount ) CLASS XMapi

   LOCAL aKeys := {}
   LOCAL cKey
   LOCAL i

   WITH OBJECT TRegistry():New()
      :Root := HKEY_CURRENT_USER
      IF :OpenKey( "Software\Microsoft\Internet Account Manager\Accounts" ) == 0
         aKeys := :EnumKeys()
         :CloseKey()

         FOR i := 1 TO Len( aKeys )
            IF :OpenKey( "Software\Microsoft\Internet Account Manager\Accounts\" + aKeys[ i ] ) == 0
               IF Upper( :GetString( "Account Name" ) ) == Upper( AllTrim( cAccount ) )
                  cKey := aKeys[ i ]
                  EXIT
               ENDIF
               :CloseKey()
            ENDIF
         NEXT

         IF ! Empty( cKey )
            :CloseKey()
            IF :OpenKey( "Software\Microsoft\Internet Account Manager" ) == 0
               :SetString( "Default Mail Account", cKey )
               ::FcAccount := cKey
               :CloseKey()
            ENDIF
         ENDIF
      ENDIF
   END

RETURN ::FcAccount

//--------------------------------------------------------------------------

METHOD GetAccountNames( lSorted ) CLASS XMapi

   LOCAL aKeys := {}
   LOCAL aNames := {}
   LOCAL i

   DEFAULT lSorted   TO .F.

   WITH OBJECT TRegistry():New()
      :Root := HKEY_CURRENT_USER
      IF :OpenKey( "Software\Microsoft\Internet Account Manager\Accounts" ) == 0
         aKeys := :EnumKeys()
         :CloseKey()
         FOR i := 1 TO Len( aKeys )
            IF :OpenKey( "Software\Microsoft\Internet Account Manager\Accounts\" + aKeys[ i ] ) == 0
               AAdd( aNames, :GetString( "Account Name" ) )
               :CloseKey()
               IF lSorted
                  ASort( aNames )
               ENDIF
            ENDIF
         NEXT
      ENDIF
   END

RETURN aNames

//--------------------------------------------------------------------------
