/*

'File Association Manager'-Plugin for Miranda IM

Copyright (C) 2005-2007 H. Herkenrath

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program (AssocMgr-License.txt); if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

// -- Includes
#include "stdafx.h"

/*
#define AIM_SUPPORT_TEST
#define ICQ_SUPPORT_TEST
#define YAHOO_SUPPORT_TEST
#define MSN_SUPPORT_TEST
#define GG_SUPPORT_TEST
#define JABBER_SUPPORT_TEST
*/

// -----------------------------------------

extern HINSTANCE hInst;

/* a copy of those in m_assocmgr.h, as we did not #include those helpers */
__inline static int AssocMgr_AddNewFileTypeT(const char *ext, const char *mime, const TCHAR *desc, const TCHAR *verb, HINSTANCE hinst, UINT iconid, const char *service, DWORD flags)
{
	FILETYPEDESC ftd;
	ftd.cbSize = sizeof(FILETYPEDESC);
	ftd.pszFileExt = ext;
	ftd.pszMimeType = mime;
	ftd.ptszDescription = desc;
	ftd.hInstance = hinst;
	ftd.nIconResID = iconid;
	ftd.ptszVerbDesc = verb;
	ftd.pszService = service;
	ftd.flags = flags | FTDF_TCHAR;
	return CallService(MS_ASSOCMGR_ADDNEWFILETYPE, 0, (LPARAM)&ftd);
}

static int __inline AssocMgr_AddNewUrlType(const char *prefix, const char *desc, HINSTANCE hinst, UINT iconid, const char *service, DWORD flags)
{
	URLTYPEDESC utd;
	utd.cbSize = sizeof(URLTYPEDESC);
	utd.pszProtoPrefix = prefix;
	utd.pszDescription = desc;
	utd.hInstance = hinst;
	utd.nIconResID = iconid;
	utd.pszService = service;
	utd.flags = flags&~UTDF_UNICODE;
	return CallService(MS_ASSOCMGR_ADDNEWURLTYPE, 0, (LPARAM)&utd);
}

// -----------------------------------------

#ifdef AIM_SUPPORT_TEST

#define AIM_PROTOCOL_NAME  "AIM"
#define IDI_AOL  28
#define MOD_KEY_CL  "CList"
#define AIM_KEY_NL  "NotOnList"
struct oscar_data { HINSTANCE hInstance; } static conn;
static __inline HANDLE find_contact(const char *nick) { nick; return NULL; }
static __inline HANDLE add_contact(const char *nick) { nick; return db_find_first(); }
static __inline void aim_gchat_joinrequest(const char *room, int exchange) { room; exchange; MessageBoxA(NULL, "Join group chat!", room, MB_OK); return; }
#include <m_protosvc.h>
#include <m_message.h>

#include "m_assocmgr.h"
static HANDLE hHookModulesLoaded;
static HANDLE hServiceParseLink;

static int ServiceParseAimLink(WPARAM, LPARAM lParam)
{
	char *arg = (char*)lParam;
	if (arg == NULL) return 1; /* sanity check */
  /* skip leading prefix */
	arg = strchr(arg, ':');
	if (arg == NULL) return 1; /* parse failed */
	for (++arg; *arg == '/'; ++arg);
	/*
		add user:      aim:addbuddy?screenname=NICK&groupname=GROUP
		send message:  aim:goim?screenname=NICK&message=MSG
		open chatroom: aim:gochat?roomname=ROOM&exchange=NUM
	*/
	/* add a contact to the list */
	if (!_strnicmp(arg, "addbuddy?", 9)) {
		char *tok, *sn = NULL, *group = NULL;
		ADDCONTACTSTRUCT acs;
		PROTOSEARCHRESULT psr;
		if (*(arg += 9) == 0) return 1; /* parse failed */
		tok = strtok(arg, "&"); /* first token */
		while (tok != NULL) {
			if (!_strnicmp(tok, "screenname=", 11) && *(tok + 11) != 0)
				sn = Netlib_UrlDecode(tok + 11);
			if (!_strnicmp(tok, "groupname=", 10) && *(tok + 10) != 0)
				group = Netlib_UrlDecode(tok + 10);  /* group is currently ignored */
			tok = strtok(NULL, "&"); /* next token */
		}
		if (sn == NULL) return 1; /* parse failed */
		if (find_contact(sn) == NULL) { /* does not yet check if sn is current user */
			acs.handleType = HANDLE_SEARCHRESULT;
			acs.szProto = AIM_PROTOCOL_NAME;
			acs.psr = &psr;
			memset(&psr, 0, sizeof(PROTOSEARCHRESULT));
			psr.cbSize = sizeof(PROTOSEARCHRESULT);
			psr.nick.t = sn;
			CallService(MS_ADDCONTACT_SHOW, 0, (LPARAM)&acs);
		}
		return 0;
	}
	/* send a message to a contact */
	else if (!_strnicmp(arg, "goim?", 5)) {
		char *tok, *sn = NULL, *msg = NULL;
		MCONTACT hContact;
		if (*(arg += 5) == 0) return 1; /* parse failed */
		tok = strtok(arg, "&"); /* first token */
		while (tok != NULL) {
			if (!_strnicmp(tok, "screenname=", 11) && *(tok + 11) != 0)
				sn = Netlib_UrlDecode(tok + 11);
			if (!_strnicmp(tok, "message=", 8) && *(tok + 8) != 0)
				msg = Netlib_UrlDecode(tok + 8);
			tok = strtok(NULL, "&"); /* next token */
		}
		if (sn == NULL) return 1; /* parse failed */
		if (ServiceExists(MS_MSG_SENDMESSAGE)) {
			hContact = find_contact(sn);
			if (hContact == NULL) {
				hContact = add_contact(sn); /* does not yet check if sn is current user */
				if (hContact != NULL)
					db_set_b(hContact, MOD_KEY_CL, AIM_KEY_NL, 1);
			}
			if (hContact != NULL)
				CallService(MS_MSG_SENDMESSAGE, hContact, (LPARAM)msg);
		}
		return 0;
	}
	/* open a chatroom */
	else if (!_strnicmp(arg, "gochat?", 7)) {
		char *tok, *rm = NULL;
		int exchange = 0;
		if (*(arg += 7) == 0) return 1; /* parse failed */
		tok = strtok(arg, "&"); /* first token */
		while (tok != NULL) {
			if (!_strnicmp(tok, "roomname=", 9) && *(tok + 9) != 0)
				rm = Netlib_UrlDecode(tok + 9);
			if (!_strnicmp(tok, "exchange=", 9))
				exchange = atoi(Netlib_UrlDecode(tok + 9));
			tok = strtok(NULL, "&"); /* next token */
		}
		if (rm == NULL || exchange <= 0) return 1; /* parse failed */
		aim_gchat_joinrequest(rm, exchange);
		return 0;
	}
	return 1; /* parse failed */
}

static int AimLinksModulesLoaded(WPARAM wParam, LPARAM lParam)
{
	char service_name[MAXMODULELABELLENGTH];
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);
	mir_snprintf(service_name, _countof(service_name), "%s%s", AIM_PROTOCOL_NAME, "ParseAimLink");
	/* or "AOL Instant Messenger Links" */
	AssocMgr_AddNewUrlType("aim:", Translate("AIM link protocol"), conn.hInstance, IDI_AOL, service_name, 0);
	return 0;
}

void aim_links_init()
{
	char service_name[MAXMODULELABELLENGTH];
	//LOG(LOG_DEBUG,"Links: init");
	mir_snprintf(service_name, _countof(service_name), "%s%s", AIM_PROTOCOL_NAME, "ParseAimLink");
	hServiceParseLink = CreateServiceFunction(service_name, ServiceParseAimLink);
	hHookModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, AimLinksModulesLoaded);
}

void aim_links_destroy()
{
	//LOG(LOG_DEBUG,"Links: destroy");
	UnhookEvent(hHookModulesLoaded);
	DestroyServiceFunction(hServiceParseLink);
}
#endif

// -----------------------------------------

#ifdef ICQ_SUPPORT_TEST

#define gpszICQProtoName  "ICQ"
#define IDI_ICQ  101
#define hInst  GetModuleHandleA("ICQ")
static __inline HANDLE HContactFromUIN(DWORD dwUIN, int *Added) { dwUIN; Added; return db_find_first(); }
static __inline HANDLE ICQFindFirstContact(void) { return NULL; }
static __inline HANDLE ICQFindNextContact(MCONTACT hContact) { hContact; return NULL; }
static __inline void AddToCache(MCONTACT hContact, DWORD dwUin) { hContact; dwUin; }
static __inline DWORD ICQGetContactSettingUIN(MCONTACT hContact) { hContact; return 0; }
static __inline HANDLE HandleFromCacheByUin(DWORD dwUin) { dwUin; return NULL; }
#include <m_protosvc.h>
#include <m_message.h>
typedef struct
{
	PROTOSEARCHRESULT hdr;
	DWORD uin;
	BYTE auth;
	char* uid;
} ICQSEARCHRESULT;

#include "m_assocmgr.h"
static HANDLE hHookModulesLoaded;
static HANDLE hServiceOpenFile;

static void TrimString(char *str)
{
	int len, start;
	len = mir_strlen(str);
	while (str[0] != '\0' && (unsigned char)str[len - 1] <= ' ') str[--len] = 0;
	for (start = 0; str[start] && (unsigned char)str[start] <= ' '; ++start);
	memmove(str, str + start, len - start + 1);
}

static BOOL IsEmpty(const char *str)
{
	int i;
	for (i = 0; str[i] != '\0'; i++)
		if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n')
			return FALSE;
	return TRUE;
}

#define ICQFILE_MESSAGEUSER  1
#define ICQFILE_ADDUSER      2
typedef struct
{
	int type;
	char uin[64];
	char email[128];
	char nick[128];
	char firstName[64];
	char lastName[64];
} ICQFILEINFO;

static HANDLE IsHContactFromUIN(DWORD uin)
{
	MCONTACT hContact;
	DWORD dwUin;
	hContact = HandleFromCacheByUin(uin);
	if (hContact != NULL) return hContact;
	hContact = ICQFindFirstContact();
	while (hContact != NULL) {
		dwUin = ICQGetContactSettingUIN(hContact);
		if (dwUin == uin) {
			AddToCache(hContact, dwUin);
			return hContact;
		}
		hContact = ICQFindNextContact(hContact);
	}
	return NULL;
}

static void AddIcqUser(ICQFILEINFO *info)
{
	ADDCONTACTSTRUCT acs;
	ICQSEARCHRESULT psr;
	DWORD uin;
	/* check that uin does not belong to current user */
	uin = atoi(info->uin);
	if (uin != 0 && uin != ICQGetContactSettingUIN(NULL)) {
		/* not yet present? */
		if (IsHContactFromUIN(uin) == NULL) {
			acs.handleType = HANDLE_SEARCHRESULT;
			acs.szProto = gpszICQProtoName;
			acs.psr = (PROTOSEARCHRESULT*)&psr;
			memset(&psr, 0, sizeof(ICQSEARCHRESULT));
			psr.hdr.cbSize = sizeof(ICQSEARCHRESULT);
			if (info->nick[0]) psr.hdr.nick = info->nick;
			else psr.hdr.nick = info->uin;
			if (info->email[0]) psr.hdr.email = info->email;
			if (info->firstName[0]) psr.hdr.firstName = info->firstName;
			if (info->lastName[0]) psr.hdr.lastName = info->lastName;
			psr.uin = uin;
			psr.auth = 1; /* authentication needed flag */
			psr.uid = NULL; /* icq contact */
			CallService(MS_ADDCONTACT_SHOW, 0, (LPARAM)&acs);
		}
	}
}

static void MessageIcqUser(ICQFILEINFO *info)
{
	MCONTACT hContact;
	if (ServiceExists(MS_MSG_SENDMESSAGE)) {
		hContact = HContactFromUIN(atoi(info->uin), NULL); /* adds the contact if needed */
		if (hContact != NULL)
			CallService(MS_MSG_SENDMESSAGE, hContact, 0);
	}
}

static int IcqOpenFile(WPARAM wParam, LPARAM lParam)
{
	TCHAR *pszFile = (TCHAR*)lParam; /* TCHAR is specified on AssocMgr_AddNewFileTypeT() */
	FILE *fp;
	ICQFILEINFO info;
	char line[4096], *sep;
	UNREFERENCED_PARAMETER(wParam);
	/*
		send message:
		[ICQ Message User]
		UIN=1234567

		add user:
		(all the .uin files I've seen only have UIN= in them)
		[ICQ User]
		UIN=1234567
		Email=
		NickName=
		FirstName=
		LastName=
	*/
	if (pszFile == NULL) return 1; /* sanity check */
	fp = _tfopen(pszFile, _T("rt"));
	if (fp == NULL) return 1; /* open failed */
	info.type = 0;
	while (!feof(fp)) {
		if (fgets(line, sizeof(line), fp) == NULL) break;
		TrimString(line);
		if (IsEmpty(line)) continue;
		if (line[0] == '[') {
			memset(&info, 0, sizeof(info));
			if (!mir_strcmpi(line, "[ICQ Message User]"))
				info.type = ICQFILE_MESSAGEUSER;
			else if (!mir_strcmpi(line, "[ICQ User]"))
				info.type = ICQFILE_ADDUSER;
			continue;
		}
		if (info.type == 0) continue;
		sep = strchr(line, '=');
		if (sep == NULL) { info.type = 0; break; } /* format error */
		*(sep++) = '\0';
		if (!mir_strcmp("UIN", line)) mir_strncpy(info.uin, sep, sizeof(info.uin)); /* buffer safe */
		else if (!mir_strcmp("Email", line)) mir_strncpy(info.email, sep, sizeof(info.email)); /* buffer safe */
		else if (!mir_strcmp("NickName", line)) mir_strncpy(info.nick, sep, sizeof(info.nick)); /* buffer safe */
		else if (!mir_strcmp("FirstName", line)) mir_strncpy(info.firstName, sep, sizeof(info.firstName)); /* buffer safe */
		else if (!mir_strcmp("LastName", line)) mir_strncpy(info.lastName, sep, sizeof(info.lastName)); /* buffer safe */
	}
	fclose(fp);
	switch (info.type) {
	case ICQFILE_MESSAGEUSER: MessageIcqUser(&info); return 0;
	case ICQFILE_ADDUSER:     AddIcqUser(&info); return 0;
	default: return 1; /* open failed */
	}
}

static int IcqFilesModulesLoaded(WPARAM wParam, LPARAM lParam)
{
	char szService[MAX_PATH + 32];
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);
	mir_strcat(mir_strcpy(szService, gpszICQProtoName), "OpenFile");
	/* .icq files are not used, just by the ProtoLink plugin */
	 //AssocMgr_AddNewFileTypeT(".icq","application/x-icq",TranslateT("ICQ link shortcut"),TranslateT("&Add to contact list..."),hInst,IDI_ICQ,szService,FTDF_BROWSERAUTOOPEN|FTDF_ISTEXT|FTDF_ISSHORTCUT|FTDF_DEFAULTDISABLED);
	AssocMgr_AddNewFileTypeT(".uin", "application/x-icq", TranslateT("ICQ link shortcut"), TranslateT("&Add to contact list..."), hInst, IDI_ICQ, szService, FTDF_BROWSERAUTOOPEN | FTDF_ISTEXT | FTDF_ISSHORTCUT);
	return 0;
}

void InitIcqFiles(void)
{
	char szService[MAX_PATH + 32];
	mir_strcat(mir_strcpy(szService, gpszICQProtoName), "OpenFile");
	hServiceOpenFile = CreateServiceFunction(szService, IcqOpenFile);
	hHookModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, IcqFilesModulesLoaded);
}

void UninitIcqFiles(void)
{
	UnhookEvent(hHookModulesLoaded);
	DestroyServiceFunction(hServiceOpenFile);
}

#undef hInst
#endif

// -----------------------------------------

#ifdef YAHOO_SUPPORT_TEST

#define yahooProtocolName "YAHOO"
#define IDI_YAHOO  10101
#define hinstance  GetModuleHandleA("YAHOO")
static __inline HANDLE getbuddyH(const char *yahoo_id) { yahoo_id; return NULL; }
static __inline HANDLE add_buddy(const char *yahoo_id, const char *yahoo_name, DWORD flags) { yahoo_id; yahoo_name; flags; return db_find_first(); }
#include <m_protosvc.h>
#include <m_message.h>

#include "m_assocmgr.h"
static HANDLE hHookModulesLoaded;
static HANDLE hServiceParseYmsgrLink;

static int ServiceParseYmsgrLink(WPARAM wParam, LPARAM lParam)
{
	char *arg = (char*)lParam;
	UNREFERENCED_PARAMETER(wParam);
	if (arg == NULL) return 1; /* sanity check */
  /*
	  add user:       ymsgr:addfriend?ID
	  send message:   ymsgr:sendim?ID&m=MESSAGE
	  add chatroom:   ymsgr:chat?ROOM
  */
  /* skip leading prefix */
	arg = strchr(arg, ':');
	if (arg == NULL) return 1; /* parse failed */
	for (++arg; *arg == '/'; ++arg);
	/* add a contact to the list */
	if (!_strnicmp(arg, "addfriend?", 10)) {
		char *tok, *id = NULL;
		ADDCONTACTSTRUCT acs;
		PROTOSEARCHRESULT psr;
		if (*(arg += 10) == 0) return 1; /* parse failed */
		tok = strtok(arg, "&"); /* first token */
		if (tok != NULL) id = Netlib_UrlDecode(tok);
		if (id == NULL || *id == 0) return 1; /* parse failed */
		if (getbuddyH(id) == NULL) { /* does not yet check if id is current user */
			acs.handleType = HANDLE_SEARCHRESULT;
			acs.szProto = yahooProtocolName;
			acs.psr = &psr;
			memset(&psr, 0, sizeof(PROTOSEARCHRESULT));
			psr.cbSize = sizeof(PROTOSEARCHRESULT);
			psr.nick.t = id;
			CallService(MS_ADDCONTACT_SHOW, 0, (LPARAM)&acs);
		}
		return 0;
	}
	/* send a message to a contact */
	else if (!_strnicmp(arg, "sendim?", 7)) {
		char *tok, *id = NULL, *msg = NULL;
		MCONTACT hContact;
		if (*(arg += 7) == 0) return 1; /* parse failed */
		tok = strtok(arg, "&"); /* first token */
		if (tok != NULL) id = tok;
		while (tok != NULL) {
			if (!_strnicmp(tok, "m=", 2) && *(tok + 2) != 0)
				msg = Netlib_UrlDecode(tok + 2);
			tok = strtok(NULL, "&"); /* next token */
		}
		if (id == NULL || *id == 0) return 1; /* parse failed */
		if (ServiceExists(MS_MSG_SENDMESSAGE)) { /* does not yet check if sn is current user */
			hContact = add_buddy(id, id, PALF_TEMPORARY); /* ensure contact is on list */
			if (hContact != NULL)
				CallService(MS_MSG_SENDMESSAGE, hContact, (LPARAM)msg);
		}
		return 0;
	}
	/* open a chatroom */
	else if (!_strnicmp(arg, "chat?", 5)) {
		char *tok, *rm = NULL;
		if (*(arg += 5) == 0) return 1; /* parse failed */
		tok = strtok(arg, "&"); /* first token */
		if (tok != NULL) rm = Netlib_UrlDecode(tok);
		if (rm == NULL) return 1; /* parse failed */
		/* not yet implemented (rm contains name of chatroom)*/
		return 0;
	}
	return 1; /* parse failed */
}

static int YmsgrLinksModulesLoaded(WPARAM wParam, LPARAM lParam)
{
	char szService[MAXMODULELABELLENGTH];
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);
	mir_snprintf(szService, _countof(szService), "%s%s", yahooProtocolName, "ParseYmsgrLink");
	AssocMgr_AddNewUrlType("ymsgr:", Translate("Yahoo link protocol"), hinstance, IDI_YAHOO, szService, 0);
	return 0;
}

void YmsgrLinksInit(void)
{
	char szService[MAXMODULELABELLENGTH];
	mir_snprintf(szService, _countof(szService), "%s%s", yahooProtocolName, "ParseYmsgrLink");
	hServiceParseYmsgrLink = CreateServiceFunction(szService, ServiceParseYmsgrLink);
	hHookModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, YmsgrLinksModulesLoaded);
}

void YmsgrLinksUninit(void)
{
	UnhookEvent(hHookModulesLoaded);
	DestroyServiceFunction(hServiceParseYmsgrLink);
}

#undef hinstance
#endif

// -----------------------------------------

#ifdef MSN_SUPPORT_TEST

#define msnProtocolName "MSN"
#define IDI_MSN  101
#define hInst  GetModuleHandleA("MSN")
static __inline HANDLE MSN_HContactFromEmailT(const char *msnEmail) { msnEmail; return NULL; }
static __inline HANDLE MSN_HContactFromEmail(const char *msnEmail, const char *msnNick, int addIfNeeded, int temporary) { msnEmail; msnNick; addIfNeeded; temporary; return db_find_first(); }
#include <m_protosvc.h>
#include <m_message.h>

#include "m_assocmgr.h"
static HANDLE hHookModulesLoaded;
static HANDLE hServiceParseMsnimLink;

static int ServiceParseMsnimLink(WPARAM wParam, LPARAM lParam)
{
	char *arg = (char*)lParam;
	UNREFERENCED_PARAMETER(wParam);
	if (arg == NULL) return 1; /* sanity check */
  /*
	  add user:      msnim:add?contact=netpassport@emailaddress.com
	  send message:  msnim:chat?contact=netpassport@emailaddress.com
	  voice chat:    msnim:voice?contact=netpassport@emailaddress.com
	  video chat:    msnim:video?contact=netpassport@emailaddress.com
  */
  /* skip leading prefix */
	arg = strchr(arg, ':');
	if (arg == NULL) return 1; /* parse failed */
	for (++arg; *arg == '/'; ++arg);
	/* add a contact to the list */
	if (!_strnicmp(arg, "add?", 4)) {
		char *tok, *email = NULL;
		ADDCONTACTSTRUCT acs;
		PROTOSEARCHRESULT psr;
		if (*(arg += 4) == 0) return 1; /* parse failed */
		tok = strtok(arg, "&"); /* first token */
		while (tok != NULL) {
			if (!_strnicmp(tok, "contact=", 8) && *(tok + 11) != 0)
				email = Netlib_UrlDecode(tok + 11);
			tok = strtok(NULL, "&"); /* next token */
		}
		if (email == NULL || *email == 0) return 1; /* parse failed */
		if (MSN_HContactFromEmailT(email) == NULL) { /* does not yet check if email is current user */
			acs.handleType = HANDLE_SEARCHRESULT;
			acs.szProto = AIM_PROTOCOL_NAME;
			acs.psr = &psr;
			memset(&psr, 0, sizeof(PROTOSEARCHRESULT));
			psr.cbSize = sizeof(PROTOSEARCHRESULT);
			psr.nick.t = email;
			psr.email.t = email;
			CallService(MS_ADDCONTACT_SHOW, 0, (LPARAM)&acs);
		}
		return 0;
	}
	/* send a message to a contact */
  /* "voice" and "video" not yet implemented, perform same action as "chat" */
	else if (!_strnicmp(arg, "chat?", 5) || !_strnicmp(arg, "voice?", 6) || !_strnicmp(arg, "video?", 6)) {
		char *tok, *email = NULL;
		MCONTACT hContact;
		if (*(arg += 5) == 0) return 1; /* parse failed */
		if (*arg == '?' && *(++arg) == 0) return 1; /* for "voice?" and "video?" */
		tok = strtok(arg, "&"); /* first token */
		while (tok != NULL) {
			if (!_strnicmp(tok, "contact=", 8) && *(tok + 11) != 0)
				email = Netlib_UrlDecode(tok + 11);
			tok = strtok(NULL, "&"); /* next token */
		}
		if (email == NULL || *email == 0) return 1; /* parse failed */
		if (ServiceExists(MS_MSG_SENDMESSAGE)) {
			hContact = MSN_HContactFromEmail(email, email, TRUE, TRUE); /* does not yet check if email is current user */
			if (hContact != NULL)
				CallService(MS_MSG_SENDMESSAGE, hContact, 0);
		}
		return 0;
	}
	return 1; /* parse failed */
}

static int MsnLinksModulesLoaded(WPARAM wParam, LPARAM lParam)
{
	char szService[MAXMODULELABELLENGTH];
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);
	mir_snprintf(szService, _countof(szService), "%s%s", msnProtocolName, "ParseMsnimLink");
	AssocMgr_AddNewUrlType("msnim:", Translate("MSN link protocol"), hInst, IDI_MSN, szService, 0);
	return 0;
}

int LoadMsnLinks(void)
{
	char szService[MAXMODULELABELLENGTH];
	mir_snprintf(szService, _countof(szService), "%s%s", msnProtocolName, "ParseMsnimLink");
	hServiceParseMsnimLink = CreateServiceFunction(szService, ServiceParseMsnimLink);
	hHookModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, MsnLinksModulesLoaded);
	return 0;
}

void UnloadMsnLinks(void)
{
	UnhookEvent(hHookModulesLoaded);
	DestroyServiceFunction(hServiceParseMsnimLink);
}

#undef hInst
#endif

// -----------------------------------------

#ifdef GG_SUPPORT_TEST

#define GG_PROTO  "GG"
#define IDI_GG  251
#define hInstance  GetModuleHandleA("GG")
typedef DWORD uin_t;
static __inline HANDLE gg_getcontact(uin_t uin, int create, int inlist, char *szNick) { uin; create; inlist; szNick; return db_find_first(); }
#include <m_protosvc.h>
#include <m_message.h>

#include "m_assocmgr.h"
static HANDLE hHookModulesLoaded;
static HANDLE hServiceParseLink;

static int ServiceParseLink(WPARAM wParam, LPARAM lParam)
{
	char *arg = (char*)lParam;
	UNREFERENCED_PARAMETER(wParam);
	if (arg == NULL) return 1; /* sanity check */
  /* send message:  gg:UID */
  /* skip leading prefix */
	arg = strchr(arg, ':');
	if (arg == NULL) return 1; /* parse failed */
	for (++arg; *arg == '/'; ++arg);
	/* send a message to a contact */
	{	MCONTACT hContact;
	if (ServiceExists(MS_MSG_SENDMESSAGE)) {
		hContact = gg_getcontact(atoi(arg), TRUE, FALSE, arg);
		if (hContact != NULL)
			CallService(MS_MSG_SENDMESSAGE, hContact, 0);
	}
	}
	return 0;
}

static int LinksModulesLoaded(WPARAM wParam, LPARAM lParam)
{
	char szService[MAXMODULELABELLENGTH];
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);
	mir_snprintf(szService, _countof(szService), "%s%s", GG_PROTO, "ParseMsnimLink");
	AssocMgr_AddNewUrlType("gg:", Translate("Gadu-Gadu link protocol"), hInstance, IDI_GG, szService, 0);
	return 0;
}

void gg_registerlinks(void)
{
	char szService[MAXMODULELABELLENGTH];
	mir_snprintf(szService, _countof(szService), "%s%s", GG_PROTO, "ParseLink");
	hServiceParseLink = CreateServiceFunction(szService, ServiceParseLink);
	hHookModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, LinksModulesLoaded);
}

void gg_unregisterlinks(void)
{
	UnhookEvent(hHookModulesLoaded);
	DestroyServiceFunction(hServiceParseLink);
}

#undef hInstance
#endif

// -----------------------------------------

#ifdef JABBER_SUPPORT_TEST

#define jabberProtoName  "JABBER"
#define IDI_JABBER  102
#define hInst  GetModuleHandleA("JABBER")
static __inline HANDLE JabberHContactFromJID(const char *jid) { jid; return NULL; }
static __inline HANDLE JabberDBCreateContact(char *jid, char *nick, BOOL temporary, BOOL stripResource) { jid; nick; temporary; stripResource; return db_find_first(); }
#include <m_protosvc.h>
#include <m_message.h>

#include "m_assocmgr.h"
static HANDLE hHookModulesLoaded;
static HANDLE hServiceParseXmppURI;

static int ServiceParseXmppURI(WPARAM wParam, LPARAM lParam)
{
	char *arg = (char*)lParam;
	char *jid;
	UNREFERENCED_PARAMETER(wParam);
	if (arg == NULL) return 1; /* sanity check */
  /* skip leading prefix */
	arg = strchr(arg, ':');
	if (arg == NULL) return 1; /* parse failed */
	for (++arg; *arg == '/'; ++arg);
	/*
		complete specification: http://www.xmpp.org/extensions/xep-0147.html
		send message:           xmpp:JID?message;subject=TEXT&body=TEXT
		add user:               xmpp:JID?roster
		remove user:            xmpp:JID?remove
	*/
	/* user id */
	arg = strchr(jid = arg, '?');
	if (arg == NULL) arg += mir_strlen(arg); /* points to terminating nul */
	else *(arg++) = 0;
	if (*jid == 0) return 1; /* parse failed */
	 /* send a message to a contact */
	else if (*arg == 0 || (!_strnicmp(arg, "message", 7) && (*(arg + 7) == ';' || *(arg + 7) == 0))) {
		char *tok, *subj = NULL, *body = NULL;
		MCONTACT hContact;
		char msg[1024];
		arg += 7;
		while (*arg == ';') ++arg;
		tok = strtok(arg, "&"); /* first token */
		while (tok != NULL) {
			if (!_strnicmp(tok, "subject=", 8) && *(tok + 8) != 0)
				subj = Netlib_UrlDecode(tok + 8);
			if (!_strnicmp(tok, "body=", 5) && *(tok + 5) != 0)
				body = Netlib_UrlDecode(tok + 5);
			tok = strtok(NULL, "&"); /* next token */
		}
		if (ServiceExists(MS_MSG_SENDMESSAGE)) {
			hContact = JabberDBCreateContact(jid, jid, TRUE, FALSE);
			if (subj != NULL && body != NULL) {
				mir_snprintf(msg, _countof(msg), "%.128s %s", subj, body);
				body = msg;
			}
			else if (body == NULL) body = subj;
			if (hContact != NULL)
				CallService(MS_MSG_SENDMESSAGE, hContact, (LPARAM)body);
		}
		return 0;
	}
	/* add user to contact list */
	else if (!_strnicmp(arg, "roster", 6) && (*(arg + 6) == ';' || *(arg + 6) == 0)) {
		ADDCONTACTSTRUCT acs;
		PROTOSEARCHRESULT psr;
		if (JabberHContactFromJID(jid) == NULL) { /* does not yet check if jid belongs to current user */
			acs.handleType = HANDLE_SEARCHRESULT;
			acs.szProto = jabberProtoName;
			acs.psr = &psr;
			memset(&psr, 0, sizeof(PROTOSEARCHRESULT));
			psr.cbSize = sizeof(PROTOSEARCHRESULT);
			psr.nick.t = jid;
			CallService(MS_ADDCONTACT_SHOW, 0, (LPARAM)&acs);
		}
		return 0;
	}
	/* remove user from contact list */
	else if (!_strnicmp(arg, "remove", 6) && (*(arg + 6) == ';' || *(arg + 6) == 0)) {
		MCONTACT hContact;
		hContact = JabberHContactFromJID(jid);
		if (hContact == NULL) /* not yet implemented: show standard miranda dialog here */
			CallService(MS_DB_CONTACT_DELETE, hContact, 0);
		return 0;
	}
	/* add user subscription */
	else if (!_strnicmp(arg, "subscribe", 9) && (*(arg + 9) == ';' || *(arg + 9) == 0)) {
		/* not yet implemented */
		return 0;
	}
	/* remove user subscription */
	else if (!_strnicmp(arg, "unsubscribe", 11) && (*(arg + 11) == ';' || *(arg + 11) == 0)) {
		/* not yet implemented */
		return 0;
	}
	return 1; /* parse failed */
}

static int JabberLinksModulesLoaded(WPARAM wParam, LPARAM lParam)
{
	char szService[MAXMODULELABELLENGTH];
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);
	mir_snprintf(szService, _countof(szService), "%s%s", jabberProtoName, "ParseXmppURI");
	AssocMgr_AddNewUrlType("xmpp:", Translate("Jabber link protocol"), hInst, IDI_JABBER, szService, 0);
	return 0;
}

int JabberLinksInit()
{
	char szService[MAXMODULELABELLENGTH];
	mir_snprintf(szService, _countof(szService), "%s%s", jabberProtoName, "ParseXmppURI");
	hServiceParseXmppURI = CreateServiceFunction(szService, ServiceParseXmppURI);
	hHookModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, JabberLinksModulesLoaded);
	return 0;
}

int JabberLinksUninit()
{
	UnhookEvent(hHookModulesLoaded);
	DestroyServiceFunction(hServiceParseXmppURI);
	return 0;
}

#undef hInst
#endif

// -----------------------------------------

void InitTest(void)
{
	#ifdef AIM_SUPPORT_TEST
	conn.hInstance = GetModuleHandleA("AIM");
	aim_links_init();
	#endif
	#ifdef ICQ_SUPPORT_TEST
	InitIcqFiles();
	#endif
	#ifdef YAHOO_SUPPORT_TEST
	YmsgrLinksInit();
	#endif
	#ifdef MSN_SUPPORT_TEST
	LoadMsnLinks();
	#endif
	#ifdef GG_SUPPORT_TEST
	gg_registerlinks();
	#endif
	#ifdef JABBER_SUPPORT_TEST
	JabberLinksInit();
	#endif
	//hServiceTest=CreateServiceFunction("AssocMgr/TestingService",TestingService);
	//AssocMgr_AddNewFileTypeT(".mir",NULL,TranslateT("Miranda Installer Package (demo purpose)"),TranslateT("&Install"),hInst,IDI_MIRANDAFILE,"AssocMgr/TestingService",0);
}

void UninitTest(void)
{
	#ifdef AIM_SUPPORT_TEST
	aim_links_destroy();
	#endif
	#ifdef ICQ_SUPPORT_TEST
	UninitIcqFiles();
	#endif
	#ifdef YAHOO_SUPPORT_TEST
	YmsgrLinksUninit();
	#endif
	#ifdef MSN_SUPPORT_TEST
	UnloadMsnLinks();
	#endif
	#ifdef GG_SUPPORT_TEST
	gg_unregisterlinks();
	#endif
	#ifdef JABBER_SUPPORT_TEST
	JabberLinksUninit();
	#endif
	//DestroyServiceFunction(hServiceTest);
}