/*
 * $Header$
 *
 * Copyright 2008 Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * Export of this software from the United States of America may
 * require a specific license from the United States Government.
 * It is the responsibility of any person or organization contemplating
 * export to obtain such a license before exporting.
 *
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of M.I.T. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  Furthermore if you modify this software you must label
 * your software as modified software and not distribute it in such a
 * fashion that it might be confused with the original M.I.T. software.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 */

extern "C" {
#include <windows.h>
#include <LMCons.h>

#include "dllmain.h"
#include "tls.h"
#include "cci_debugging.h"
#include "ccapi_context.h"
#include "ccapi_ipc.h"
#include "client.h"

void cci_process_init__auxinit();
    }


#define CCAPI_V2_MUTEX_NAME     TEXT("MIT_CCAPI_V4_MUTEX")

// Process-specific data:
static DWORD    dwTlsIndex;
static char     _user[UNLEN+1];     // Username is used as part of the server and client endpoints.
static  HANDLE  sessionToken;
static char*    ep_prefices[]   = {"CCS", "CCAPI"};
HANDLE          hCCAPIv2Mutex   = NULL;
DWORD           firstThreadID   = 0;

// These data structures are used by the old CCAPI implementation 
//  to keep track of the state of the RPC connection.  All data is static.
static Init     init;
static Client   client;

DWORD    GetTlsIndex()  {return dwTlsIndex;}

// DllMain() is the entry-point function for this DLL. 
BOOL WINAPI DllMain(HINSTANCE hinstDLL,     // DLL module handle
                    DWORD fdwReason,        // reason called
                    LPVOID lpvReserved) {   // reserved 

    struct tspdata* ptspdata;
    BOOL            fIgnore;
    BOOL            bStatus;
    DWORD           status      = 0;        // 0 is success.
    DWORD           maxUN       = sizeof(_user);
    unsigned int    i           = 0;
    unsigned int    j           = 0;
 
    switch (fdwReason) { 
        // The DLL is loading due to process initialization or a call to LoadLibrary:
        case DLL_PROCESS_ATTACH: 
            cci_debug_printf("%s DLL_PROCESS_ATTACH", __FUNCTION__);
            // Process-wide mutex used to allow only one thread at a time into the RPC code:
            hCCAPIv2Mutex = CreateMutex(NULL, FALSE, CCAPI_V2_MUTEX_NAME);

            // Figure out our username; it's process-wide:
            bStatus = GetUserName(_user, &maxUN);
            if (!bStatus) return bStatus;

            // Remove any characters that aren't valid endpoint characters:
            while (_user[j] != 0) {
                if (isalnum(_user[j])) _user[i++] = _user[j];
                j++;
                }
            _user[i]    = '\0';

            // Our logon session is determined in client.cxx, old CCAPI code carried
            //  over to this implementation.

            // Allocate a TLS index:
            if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES) return FALSE; 

            cci_process_init__auxinit();
            // Don't break; fallthrough: Initialize the TLS index for first thread.
 
        // The attached process creates a new thread:
        case DLL_THREAD_ATTACH:
            cci_debug_printf("%s DLL_THREAD_ATTACH", __FUNCTION__);
            // Don't actually rely on this case for allocation of resources.
            // Applications (like SecureCRT) may have threads already
            // created (say 'A' and 'B') before the dll is loaded. If the dll
            // is loaded in thread 'A' but then used in thread 'B', thread 'B'
            // will never execute this code.
            fIgnore     = TlsSetValue(dwTlsIndex, NULL);

            // Do not call cci_ipc_thread_init() yet; defer until we actually
            // need it.  On XP, cci_ipc_thread_init() will cause additional
            // threads to be immediately spawned, which will bring us right
            // back here again ad infinitum, until windows
            // resources are exhausted.
            break;
 
        // The thread of the attached process terminates:
        case DLL_THREAD_DETACH: 
            cci_debug_printf("%s DLL_THREAD_DETACH", __FUNCTION__);
            // Release the allocated memory for this thread
            ptspdata = (struct tspdata*)TlsGetValue(dwTlsIndex); 
            if (ptspdata != NULL) {
                free(ptspdata);
                TlsSetValue(dwTlsIndex, NULL); 
                }
            break; 
 
        // DLL unload due to process termination or FreeLibrary:
        case DLL_PROCESS_DETACH: 
            cci_debug_printf("%s DLL_PROCESS_DETACH", __FUNCTION__);
            //++ Copied from previous implementation:
            // Process Teardown "Problem"
            //
            // There are two problems that occur during process teardown:
            //
            // 1) Windows (NT/9x/2000) does not keep track of load/unload
            //    ordering dependencies for use in process teardown.
            //
            // 2) The RPC exception handling in the RPC calls do not work
            //    during process shutdown in Win9x.
            //
            // When a process is being torn down in Windows, the krbcc DLL
            // may get a DLL_PROCESS_DETACH before other DLLs are done
            // with it.  Thus, it may disconnect from the RPC server
            // before the last shutdown RPC call.
            //
            // On NT/2000, this is ok because the RPC call will fail and just
            // return an error.
            //
            // On Win9x/Me, the RPC exception will not be caught.
            // However, Win9x ignores exceptions during process shutdown,
            // so the exception will never be seen unless a debugger is
            // attached to the proccess.
            //
            // A good potential woraround would be to have a global
            // variable that denotes whether the DLL is attached to the
            // process.  If it is not, all entrypoints into the DLL should
            // return failure.
            //
            // A not as good workaround is below but ifdefed out.
            //
            // However, we can safely ignore this problem since it can
            // only affects people running debuggers under 9x/Me who are
            // using multiple DLLs that use this DLL.
            //
            WaitForSingleObject( hCCAPIv2Mutex, INFINITE );
#if 0
            bool process_teardown_workaround = false;
            if (lpvReserved) {
                Init::InitInfo info;
                status = Init::Info(info);
                if (status) break;
                if (!info.isNT) process_teardown_workaround = true;
            }
            if (process_teardown_workaround)
                break;
#endif
            // return value is ignored, so we set status for debugging purposes
            status = Client::Cleanup();
            status = Init::Cleanup();
            ReleaseMutex( hCCAPIv2Mutex );
            CloseHandle( hCCAPIv2Mutex );
            //-- Copied from previous implementation.

            // Release the allocated memory for this thread:
            ptspdata = (struct tspdata*)TlsGetValue(dwTlsIndex); 
            if (ptspdata != NULL)
                free(ptspdata);
            TlsFree(dwTlsIndex);    // Release the TLS index.
            // Ideally, we would enumerate all other threads here and
            // release their thread local storage as well.
            break; 
 
        default:
            cci_debug_printf("%s unexpected reason %d", __FUNCTION__, fdwReason);
            break;
        } 
 
    UNREFERENCED_PARAMETER(hinstDLL);       // no whining!
    UNREFERENCED_PARAMETER(lpvReserved); 
    return status ? FALSE : TRUE;
}


#ifdef __cplusplus    // If used by C++ code, 
extern "C" {          // we need to export the C interface
#endif

#ifdef __cplusplus
}
#endif

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

extern "C" void  __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t len) {
    return(malloc(len));
    }

extern "C" void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr) {
    free(ptr);
    }
