/*
 * Skype cmd.exe plugin v0.1 - "listening master" module (c) 2006 Kostya Kortchinsky - EADS/DCR/STI/C
 * Compile with "cl /W3 plugin_master.c /MT"
 */

#include <windows.h>
#include <stdio.h>
#include <process.h>

#pragma comment(lib, "rpcrt4.lib")
#pragma comment(lib, "user32.lib")

HWND hInit_MainWindowHandle;
HINSTANCE hInit_ProcessHandle;
char acInit_WindowClassName[128];
HANDLE hGlobal_ThreadShutdownEvent;
BOOL volatile fGlobal_ThreadRunning = TRUE;
UINT uiGlobal_MsgID_SkypeControlAPIAttach;
UINT uiGlobal_MsgID_SkypeControlAPIDiscover;
HWND hGlobal_SkypeAPIWindowHandle = NULL;
DWORD ulGlobal_PromptConsoleMode = 0;
HANDLE volatile hGlobal_PromptConsoleHandle = NULL;

enum {
	SKYPECONTROLAPI_ATTACH_SUCCESS = 0,
	SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION = 1,
	SKYPECONTROLAPI_ATTACH_REFUSED = 2,
	SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE = 3,
	SKYPECONTROLAPI_ATTACH_API_AVAILABLE = 0x8001
};

char szMessageCreate[] = "CREATE APPLICATION minishell";
char szMessageStreams[] = "APPLICATION minishell STREAMS ";
char szMessageDelete[] = "DELETE APPLICATION minishell";
char szMessageWrite[] = "ALTER APPLICATION minishell WRITE ";
char szMessageSending[] = "APPLICATION minishell SENDING ";
char szMessageReceived[] = "APPLICATION minishell RECEIVED ";
char szMessageRead[] = "ALTER APPLICATION minishell READ ";

static BYTE szStream[64];

void ProcessMessage(PCOPYDATASTRUCT poCopyData)
{
	COPYDATASTRUCT oCopyData;
	static BYTE szBuffer[1152];

	oCopyData.dwData = 0;
	oCopyData.lpData = NULL;
	memset(szBuffer, 0, sizeof(szBuffer));
	if (stricmp(poCopyData->lpData, szMessageCreate) == 0) {
		OutputDebugString("[DEBUG] Application registration successful.\n");
	}
	else if (strnicmp(poCopyData->lpData, szMessageStreams, strlen(szMessageStreams)) == 0) {
		memset(szStream, 0, sizeof(szStream));
		strncpy(szStream, &(((char *)(poCopyData->lpData))[strlen(szMessageStreams)]), sizeof(szStream) - 1);
		if (strlen(szStream) == 0)
			OutputDebugString("[DEBUG] Stream closed.\n");
		else
			OutputDebugString(szStream);
	}
	else if (stricmp(poCopyData->lpData, szMessageSending) == 0) {
		OutputDebugString("[DEBUG] Message successfully sent.\n");
	}
	else if (strnicmp(poCopyData->lpData, szMessageReceived, strlen(szMessageReceived)) == 0) {
		if (strnicmp(szStream, &(((char *)(poCopyData->lpData))[strlen(szMessageReceived)]), strlen(szStream)) == 0) {
			OutputDebugString("[DEBUG] Incoming message.\n");
			_snprintf(szBuffer, sizeof(szBuffer) - 1, "%s%s", szMessageRead, szStream);
			oCopyData.lpData = szBuffer;
		}
	}
	else if (strnicmp(poCopyData->lpData, szMessageRead, strlen(szMessageRead)) == 0) {
		if (strnicmp(szStream, &(((char *)(poCopyData->lpData))[strlen(szMessageRead)]), strlen(szStream)) == 0) {
			OutputDebugString("[DEBUG] Message received.\n");
			strncpy(szBuffer, &(((char *)(poCopyData->lpData))[strlen(szMessageRead) + strlen(szStream) + 1]), sizeof(szBuffer) - 1);
			printf("%s", szBuffer);
		}
	}
	if (oCopyData.lpData != NULL) {
		oCopyData.cbData = strlen(oCopyData.lpData) + 1;
		if (SendMessage(hGlobal_SkypeAPIWindowHandle, WM_COPYDATA, (WPARAM)hInit_MainWindowHandle, (LPARAM)&oCopyData) == FALSE)
			hGlobal_SkypeAPIWindowHandle = NULL;
	}
	// printf( "Message from Skype(%u): %.*s\n", poCopyData->dwData, poCopyData->cbData, poCopyData->lpData);
}

BOOL Global_Console_ReadRow(char *pacPromptBuffer, unsigned int uiMaxLength)
{
	HANDLE hConsoleHandle, hDuplicatedConsoleHandle;
	DWORD ulCharactersRead, ulConsoleMode;
	unsigned int uiNewLength;
	BOOL fReadConsoleResult;
	BOOL fReturnStatus;
	char cCharacter;

	fReturnStatus = FALSE;
	while ((hConsoleHandle = GetStdHandle(STD_INPUT_HANDLE)) != INVALID_HANDLE_VALUE) {
		if (DuplicateHandle(GetCurrentProcess(), hConsoleHandle,
			GetCurrentProcess(), &hDuplicatedConsoleHandle, 0, FALSE,
			DUPLICATE_SAME_ACCESS) == FALSE)
			break;
		GetConsoleMode(hDuplicatedConsoleHandle, &ulConsoleMode);
		SetConsoleMode(hDuplicatedConsoleHandle, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT);
		hGlobal_PromptConsoleHandle = hDuplicatedConsoleHandle;
		ulGlobal_PromptConsoleMode = ulConsoleMode;
		fReadConsoleResult = ReadConsole(hGlobal_PromptConsoleHandle,
			(LPVOID)pacPromptBuffer, uiMaxLength, &ulCharactersRead, (LPVOID)0);
		if (hGlobal_PromptConsoleHandle == (HANDLE)0)
			break;
		hGlobal_PromptConsoleHandle = (HANDLE)0;
		SetConsoleMode(hDuplicatedConsoleHandle, ulConsoleMode);
		CloseHandle(hDuplicatedConsoleHandle);
		if (fReadConsoleResult == FALSE || ulCharactersRead > uiMaxLength)
			break;
		pacPromptBuffer[ulCharactersRead] = 0;
		uiNewLength = ulCharactersRead;
		while (uiNewLength != 0) {
			cCharacter = pacPromptBuffer[uiNewLength - 1];
			if (cCharacter != '\r' && cCharacter != '\n')
				break;
			uiNewLength--;
		}
		pacPromptBuffer[uiNewLength] = 0;
		fReturnStatus = TRUE;
		break;
	}
	if (fReturnStatus == FALSE)
		pacPromptBuffer[0] = 0;
	return(fReturnStatus);
}

void Global_Console_CancelReadRow(void)
{
	if (hGlobal_PromptConsoleHandle != (HANDLE)0) {
		SetConsoleMode(hGlobal_PromptConsoleHandle, ulGlobal_PromptConsoleMode);
		CloseHandle(hGlobal_PromptConsoleHandle);
		hGlobal_PromptConsoleHandle = (HANDLE)0;
	}
}

static LRESULT APIENTRY SkypeAPITest_Windows_WindowProc(HWND hWindow, UINT uiMessage, WPARAM uiParam, LPARAM ulParam)
{
	LRESULT lReturnCode;
	BOOL fIssueDefProc;
	COPYDATASTRUCT oCopyData;

	lReturnCode = 0;
	fIssueDefProc = FALSE;
	oCopyData.dwData = 0;
	switch(uiMessage) {
		case WM_DESTROY:
			hInit_MainWindowHandle = NULL;
			PostQuitMessage(0);
			break;
		case WM_COPYDATA:
			if (hGlobal_SkypeAPIWindowHandle == (HWND)uiParam) {
				ProcessMessage((PCOPYDATASTRUCT)ulParam);
				lReturnCode = 1;
			}
			break;
		default:
			if(uiMessage == uiGlobal_MsgID_SkypeControlAPIAttach) {
				switch(ulParam) {
					case SKYPECONTROLAPI_ATTACH_SUCCESS:
						hGlobal_SkypeAPIWindowHandle = (HWND)uiParam;
						oCopyData.lpData = szMessageCreate;
						oCopyData.cbData = strlen(oCopyData.lpData) + 1;
						if (SendMessage(hGlobal_SkypeAPIWindowHandle, WM_COPYDATA, (WPARAM)hInit_MainWindowHandle, (LPARAM)&oCopyData) == FALSE)
							hGlobal_SkypeAPIWindowHandle = NULL;
						break;
					case SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION:
					case SKYPECONTROLAPI_ATTACH_REFUSED:
					case SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE:
					case SKYPECONTROLAPI_ATTACH_API_AVAILABLE:
						break;
				}
				lReturnCode = 1;
				break;
			}
			fIssueDefProc = TRUE;
			break;
	}
	if (fIssueDefProc)
		lReturnCode = DefWindowProc(hWindow, uiMessage, uiParam, ulParam);
	return (lReturnCode);
}

BOOL Initialize_CreateWindowClass(void)
{
	unsigned char *paucUUIDString;
	RPC_STATUS lUUIDResult;
	BOOL fReturnStatus;
	UUID oUUID;

	fReturnStatus = FALSE;
	lUUIDResult = UuidCreate(&oUUID);
	hInit_ProcessHandle = (HINSTANCE)OpenProcess(PROCESS_DUP_HANDLE, FALSE, GetCurrentProcessId());
	if (hInit_ProcessHandle != NULL && (lUUIDResult == RPC_S_OK || lUUIDResult == RPC_S_UUID_LOCAL_ONLY)) {
		if(UuidToString(&oUUID, &paucUUIDString) == RPC_S_OK) {
			WNDCLASS oWindowClass;
			strcpy(acInit_WindowClassName, "Skype-API-Test-");
			strcat(acInit_WindowClassName, (char *)paucUUIDString);

			oWindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
			oWindowClass.lpfnWndProc = (WNDPROC)&SkypeAPITest_Windows_WindowProc;
			oWindowClass.cbClsExtra = 0;
			oWindowClass.cbWndExtra = 0;
			oWindowClass.hInstance = hInit_ProcessHandle;
			oWindowClass.hIcon = NULL;
			oWindowClass.hCursor = NULL;
			oWindowClass.hbrBackground = NULL;
			oWindowClass.lpszMenuName = NULL;
			oWindowClass.lpszClassName = acInit_WindowClassName;

			if (RegisterClass(&oWindowClass) != 0)
				fReturnStatus = TRUE;
			RpcStringFree(&paucUUIDString);
		}
	}
	if (fReturnStatus == FALSE)
		CloseHandle(hInit_ProcessHandle), hInit_ProcessHandle = NULL;
	return (fReturnStatus);
}

void DeInitialize_DestroyWindowClass(void)
{
	UnregisterClass(acInit_WindowClassName, hInit_ProcessHandle);
	CloseHandle(hInit_ProcessHandle), hInit_ProcessHandle = NULL;
}

BOOL Initialize_CreateMainWindow(void)
{
	hInit_MainWindowHandle = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,
		acInit_WindowClassName, "", WS_BORDER | WS_SYSMENU | WS_MINIMIZEBOX,
		CW_USEDEFAULT, CW_USEDEFAULT, 128, 128, NULL, 0, hInit_ProcessHandle, 0);
	return (hInit_MainWindowHandle != NULL ? TRUE : FALSE);
}

void DeInitialize_DestroyMainWindow(void)
{
	if (hInit_MainWindowHandle != NULL)
		DestroyWindow(hInit_MainWindowHandle), hInit_MainWindowHandle = NULL;
}

void Global_MessageLoop(void)
{
	MSG oMessage;

	while (GetMessage(&oMessage, 0, 0, 0) != FALSE) {
		TranslateMessage(&oMessage);
		DispatchMessage(&oMessage);
	}
}

void __cdecl Global_InputProcessingThread(void *pUnused)
{
	static char acInputRow[1024];
	static char szAP2APMessage[1024 + 128];
	COPYDATASTRUCT oCopyData;

	oCopyData.dwData = 0;
	if (SendMessage(HWND_BROADCAST, uiGlobal_MsgID_SkypeControlAPIDiscover, (WPARAM)hInit_MainWindowHandle, 0) != 0) {
		while (Global_Console_ReadRow(acInputRow, sizeof(acInputRow) - 1)) {
			if (strlen(szStream) == 0)
				continue;
			_snprintf(szAP2APMessage, sizeof(szAP2APMessage) - 1, "%s%s %s", szMessageWrite, szStream, acInputRow);
			oCopyData.lpData = szAP2APMessage;
			oCopyData.cbData = strlen(oCopyData.lpData) + 1;
			if (oCopyData.cbData != 1) {
				if (SendMessage(hGlobal_SkypeAPIWindowHandle, WM_COPYDATA, (WPARAM)hInit_MainWindowHandle, (LPARAM)&oCopyData) == FALSE) {
					hGlobal_SkypeAPIWindowHandle = NULL;
				}
			}
		}
	}
	PostMessage(hInit_MainWindowHandle, WM_CLOSE, 0, 0);
	SetEvent(hGlobal_ThreadShutdownEvent);
	fGlobal_ThreadRunning = FALSE;
}

int main(int argc, char *argv[])
{
	memset(szStream, 0, sizeof(szStream));
	uiGlobal_MsgID_SkypeControlAPIAttach = RegisterWindowMessage("SkypeControlAPIAttach");
	uiGlobal_MsgID_SkypeControlAPIDiscover = RegisterWindowMessage("SkypeControlAPIDiscover");
	if (uiGlobal_MsgID_SkypeControlAPIAttach != 0 && uiGlobal_MsgID_SkypeControlAPIDiscover != 0) {
		if (Initialize_CreateWindowClass()) {
			if (Initialize_CreateMainWindow()) {
				hGlobal_ThreadShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
				if (hGlobal_ThreadShutdownEvent != NULL) {
					if (_beginthread(Global_InputProcessingThread, 64 * 1024, NULL) != (unsigned long)-1) {
						Global_MessageLoop();
						Global_Console_CancelReadRow();
						WaitForSingleObject(hGlobal_ThreadShutdownEvent, INFINITE);
					}
					CloseHandle(hGlobal_ThreadShutdownEvent);
				}
				DeInitialize_DestroyMainWindow();
			}
			DeInitialize_DestroyWindowClass();
		}
	}

	return 0;
}