//---------------------------------------------------------------------------
// Based on www.xchat.org provided plugin header - works ONLY with new plugin
//  system for xchat versions >= 2.x
//
// http://mircryption.sourceforge.net
// By Dark Raichu and Xro, 1/03 ...
//  4/27/03 fixed lines with / not being encrypted
//  4/28/03 added \017 characters to format, which allows nicks to be clicked.
//  5/09/03 changed default keyfile location to ~/.xchat/MircryptionKeys.txt
//  5/13/03 added windows compatibility (tested with xygwin and xchat2 for win32)
//  9/08/03 added support for ` prefix toggle
// 12/27/03 added color stripping [hadez@darklab.org]
// 03/21/04 help message was saying to call '/setkey #chan key' instead of '/setkey key'
// 03/30/04 changed default outgoing tag to +OK for better default compatibility with other scripts
// 05/18/04 added meow support
// 12/26/04 fixing for xchat umlaout bug (utf8 stuff)
// 01/08/04 added new cbc blowfish modes
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// xchat plugin version
#define XMC_VERSIONSTRING "0.1.0xmc"
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// cygwin may not have WIN32 defined (which we use AND more imp xchat.h uses)
#ifndef WIN32
 #ifdef _MSC_VER
 #define WIN32
 #endif
 #ifdef __CYGWIN__
 #define WIN32
 #endif
 #ifdef __MINGW32__
 #define WIN32
 #endif
#endif
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// default key file name and location
#ifdef WIN32
 #define DEFAULTKEYFILE "MircryptionKeys.txt"
#else
 // note if no / at front then this is relative filename off of user's home dir
 // remember that the . at start of name makes the file visible only w/ ls -a
 #define DEFAULTKEYFILE ".MircryptionKeys.txt"
#endif
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// xchat function and enum definitions
#include "xchat-plugin.h"
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Application includes
#include "mirc_codes.h"
#include "mircryption.h"
#include <stdlib.h>
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// Uncomment one of these pairs to choose how encrypted messages are formatted
// Default mircryption method, use [] around encrypted text
#define EMESSAGE_RECV_FORMAT "\00302[\017\00300%s\017\00302]\017\t%s\n"
#define EMESSAGE_SEND_FORMAT "\00306[\017\00300%s\017\00306]\017\t%s\n"
#define ENOTICE_RECV_FORMAT "\00302-[\017\00313%s\017\00302]-\017\t%s\n"
#define ENOTICE_SEND_FORMAT "\00303]\017\00300%s\017\00303[\017\t%s\n"
#define EMSG_SEND_FORMAT ENOTICE_SEND_FORMAT
#define EACTIONFORMAT "[\00313*\017]\t%s %s\n"
// Xro prefers a prefix of mc before <>
//#define EMESSAGEFORMAT "mc \00303>\00300%s\00303<\00300\t%s"
//#define EACTIONFORMAT "mc \00313*\00300\t%s %s"
// house uses the following:
//#define EMESSAGE_SEND_FORMAT "\00306[\017%s\00306]\017\t%s\n"
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Here you can choose what outgoing tag to use; the defaul is mcps
//  but you can set it to +OK if you want to be compatible with others apps.
//  just comment out the one you *dont* want to use with a //
//#define OUTGOINGETAG "mcps"
#define OUTGOINGETAG "+OK"
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// If you want to force your xchat plugin to ignore meow broadcasts, you
//  can set this to true.  Meows are just fun ways of saying hello to fello
//  mircryption users.
#define IGNOREMEOWS false
//---------------------------------------------------------------------------







//---------------------------------------------------------------------------
// global plugin handle
static xchat_plugin *ph;
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// pointer to global mircryptor which does the bulk of the work
MircryptionClass_xchat *mircryptor=NULL;
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Forward declarations
static int setdefault_keyfilelocation();
//
static int mc_event_privdialog(char *word[], void *userdata);
static int mc_event_channelmessage(char *word[], void *userdata);
static int mc_event_action(char *word[], void *userdata);
static int mc_event_channelnotice(char *word[], void *userdata);
static int mc_event_notice(char *word[], void *userdata);
static int mc_event_topic(char *word[], void *userdata);
static int mc_event_topicchange(char *word[], void *userdata);
static int mc_event_yourmessage(char *word[], void *userdata);
static int mc_event_noticesend(char *word[], void *userdata);
//
static int mc_help(char *word[], char *word_eol[], void *userdata);
static int mc_setkey(char *word[], char *word_eol[], void *userdata);
static int mc_delkey(char *word[], char *word_eol[], void *userdata);
static int mc_disablekey(char *word[], char *word_eol[], void *userdata);
static int mc_enablekey(char *word[], char *word_eol[], void *userdata);
static int mc_displaykey(char *word[], char *word_eol[], void *userdata);
static int mc_listkeys(char *word[], char *word_eol[], void *userdata);
static int mc_keypassphrase(char *word[], char *word_eol[], void *userdata);
static int mc_setkeyfile(char *word[], char *word_eol[], void *userdata);
static int mc_etopic(char *word[], char *word_eol[], void *userdata);
static int mc_alltext(char *word[], char *word_eol[], void *userdata);
static int mc_action(char *word[], char *word_eol[], void *userdata);
static int mc_notice(char *word[], char *word_eol[], void *userdata);

static void mylowercasify(char *str);

/*
  Author: hadez@darklab.org
  Description: see function code at bottom of file for details
*/
void strip_mirc_colors(xchat_plugin*,char*);

// meow handling
bool HandleMeow(char *channelname,char *nick,char *text);

// xchat umlaout issue (utf8 stuff)
void Utf8DirtyFix(char *intext,char *outtext);
//---------------------------------------------------------------------------



//---------------------------------------------------------------------------
// new extra safe buffer overflow checks
// thanks to www.rainbowcrack-online.com and ircfuz for finding this dangeroud possibility
//  more serious buffer overflow checks are also now in the dll as well
void mcnicksafe_strcpy(char *dest,const char*src) {mcsafe_strcpy(dest,src,MAXCHANNELNAMESIZE);};
void mclinesafe_strcpy(char *dest,const char*src) {mcsafe_strcpy(dest,src,MAXLINELEN);};
void mckeysafe_strcpy(char *dest,const char*src) {mcsafe_strcpy(dest,src,MAXSAFEKEYSIZE);};
void mcsafe_strcpy(char *dest,const char *src, int maxlen) {if (strlen(src)<maxlen-1) strcpy(dest,src); else {strncpy(dest,src,maxlen-1);dest[maxlen-1]='\0';};};
//---------------------------------------------------------------------------






//---------------------------------------------------------------------------
// do we need a dummy main for some compilers
// ATTN: new 07/10/03
// int main(int argc,void **argv) {return 0;}
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
extern "C" int xchat_plugin_init(xchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg)
{
	// plugin is being loaded

	// save this in order to call xchat_* functions
	ph = plugin_handle;

	// xchat plugin info
	*plugin_name = "mircryption";
	*plugin_desc = "Mircryption - cryptographic addon for mirc/xchat (http://mircryption.sourceforge.net)";
	*plugin_version = XMC_VERSIONSTRING;

	// hook the commands we provide
	xchat_hook_command(ph, "mircryption", PRI_NORM, mc_help, "Usage: MIRCRYPTION, shows help for mircryption", 0);
	xchat_hook_command(ph, "setkey", PRI_NORM, mc_setkey, "Usage: SETKEY keyphrase..,  enables encryption/decryption on current channel, using key specified; can be used to add or modify keys.", 0);
	xchat_hook_command(ph, "delkey", PRI_NORM, mc_delkey, "Usage: DELKEY, removes encryption key from current channel.", 0);
	xchat_hook_command(ph, "disablekey", PRI_NORM, mc_disablekey, "Usage: DISABLEKEY, temporarily disables encryption for current channel", 0);
	xchat_hook_command(ph, "enablekey", PRI_NORM, mc_enablekey, "Usage: ENABLEKEY, re-enables encryption for current channel", 0);
	xchat_hook_command(ph, "displaykey", PRI_NORM, mc_displaykey, "Usage: DISPLAYKEY, shows you (and only you) the key for the current channel", 0);
	xchat_hook_command(ph, "listkeys", PRI_NORM, mc_listkeys, "Usage: LISTKEYS, lists all channel encryption keys currently stored", 0);
	xchat_hook_command(ph, "keypassphrase", PRI_NORM, mc_keypassphrase, "", 0); // legacy
	xchat_hook_command(ph, "masterkey", PRI_NORM, mc_keypassphrase, "Usage: MASTERKEY phrase.., set or change current master keyfile passphrase to 'phrase'", 0);
	xchat_hook_command(ph, "setkeyfile", PRI_NORM, mc_setkeyfile, "Usage: SETKEYFILE filename, set the name of the file to be used for storing/retrieving keys", 0);
	xchat_hook_command(ph, "etopic", PRI_NORM, mc_etopic, "Usage: ETOPIC text.., encrypt the topic for the current channel to text", 0);
	xchat_hook_command(ph, "me", PRI_NORM, mc_action, "Usage: ME <action>", 0);
	xchat_hook_command(ph, "notice", PRI_NORM, mc_notice, "Usage: NOTICE <nick/channel> <message>, sends a notice. Notices are a type of message that should be auto reacted to", 0);
	xchat_hook_command(ph, "", PRI_NORM, mc_alltext, "trap all input for encryption", 0);

	// hook the events we want to respond to
	xchat_hook_print(ph, "Channel Message", PRI_NORM, mc_event_channelmessage, 0);
	xchat_hook_print(ph, "Channel Notice", PRI_NORM, mc_event_channelnotice, 0);
	xchat_hook_print(ph, "Channel Action", PRI_NORM, mc_event_action, 0);
	xchat_hook_print(ph, "Notice", PRI_NORM, mc_event_notice, 0);
	xchat_hook_print(ph, "Topic", PRI_NORM, mc_event_topic, 0);
	xchat_hook_print(ph, "Topic Change", PRI_NORM, mc_event_topicchange, 0);
	xchat_hook_print(ph, "Your Message", PRI_NORM, mc_event_yourmessage, 0);
	xchat_hook_print(ph, "Notice Send", PRI_NORM, mc_event_noticesend, 0);
	xchat_hook_print(ph, "Private Message to Dialog", PRI_NORM, mc_event_privdialog, 0);

	// create our mircryptor class which does the bulk of the work
	mircryptor=new MircryptionClass_xchat;
	// set keyfile to ~/.MircryptionKeys.txt
	setdefault_keyfilelocation();
	// if keyfile is not yet unlocked, then unlock them
	mircryptor->load_keys();

	// inform user that the addon is loaded
	xchat_printf(ph, "Mircryption ver %s loaded - encryption currently *disabled*\n",XMC_VERSIONSTRING);
	xchat_printf(ph, " type /masterkey PASSPHRASE to activate, or /mircryption for help.\n");
	
	// return 1 for success
	return 1;
}


extern "C" int xchat_plugin_deinit (xchat_plugin *plugin_handle)
{
	// plugin needs to shut down

	// free mircryptor object
	if (mircryptor!=NULL)
		delete mircryptor;
	mircryptor=NULL;

	// return 1 for success
	return 1;
}
//---------------------------------------------------------------------------



//---------------------------------------------------------------------------
static int setdefault_keyfilelocation()
{
	// set default key file location
	char returndata[MAXRETURNSTRINGLEN];
	char keyfile[1000];

	// form default keyfile name
	strcpy(keyfile,DEFAULTKEYFILE);

    #ifndef WIN32
	if (keyfile[0]!='/')
		{
		// we need to grab users home explicitly, using ~/ didnt work
		strcpy(keyfile,getenv("HOME"));
		if (strlen(keyfile)>0)
			strcat(keyfile,"/");
		strcat(keyfile,DEFAULTKEYFILE);
		}
	#endif
	
	mircryptor->mc_setkeyfilename(keyfile,returndata);
}
//---------------------------------------------------------------------------



//---------------------------------------------------------------------------
static int mc_event_privdialog(char *word[], void *userdata)
{
	// handle event
	char returndata[MAXRETURNSTRINGLEN];
	bool bretv;

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel"));
	channelname[MAXCHANNELNAMESIZE-1]='\0';

	// new force lowercase
	mylowercasify(channelname);

	char nick[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(nick,word[1]);
	nick[MAXCHANNELNAMESIZE-1]='\0';

	char text[MAXLINELEN];
	mclinesafe_strcpy(text,word[2]);

	// decrypt it
	bretv=mircryptor->mc_decrypt2(channelname,text,returndata);
	if (!bretv)
		{
		if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
			xchat_print(ph,returndata);
		return EAT_NONE;
		}
	else if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
		{
		// it was decrypted so we handle it
		//xchat_printf(ph,"[%s] %s",nick,returndata);
		//xchat_commandf(ph,"recv :%s!%s@mircryption PRIVMSG %s :(e) %s",nick,nick,channelname,returndata);
	    strip_mirc_colors(ph,returndata);

		//check if it's a fish stype crypted action [hadez]
		if(strncmp(returndata,"\001ACTION ",8) == 0)
			{
			// something smells fishy here
			// get rid of the trailing \001
			returndata[strlen(returndata)-1] = '\0';
			// ignore the "\001ACTION " part and print it in eaction format
			xchat_printf(ph,EACTIONFORMAT,nick,returndata+8);
			}
		else
			{
			xchat_printf(ph,EMESSAGE_RECV_FORMAT,nick,returndata);
			}
		//xchat_printf(ph,EMESSAGE_RECV_FORMAT,nick,returndata);

		return EAT_ALL;
		}

	// don't eat this event, let xchat handle it also
	return EAT_NONE;
}

static int mc_event_channelmessage(char *word[], void *userdata)
{
	// handle event
	char returndata[MAXRETURNSTRINGLEN];
	bool bretv;

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	char nick[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(nick,word[1]);
	char text[MAXLINELEN];
	mclinesafe_strcpy(text,word[2]);

	// is this a meow?
	if (strncmp(text,"mcps meow meow",14)==0)
		{
		if (HandleMeow(channelname,nick,text))
			return EAT_ALL;
		}

	// decrypt it
	bretv=mircryptor->mc_decrypt2(channelname,text,returndata);
	if (!bretv)
		{
		if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
			xchat_print(ph,returndata);
		return EAT_NONE;
		}
	else if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
		{
		// it was decrypted so we handle it
		//xchat_printf(ph,"[%s] %s",nick,returndata);
		//xchat_commandf(ph,"recv :%s!%s@mircryption PRIVMSG %s :(e) %s",nick,nick,channelname,returndata);
		strip_mirc_colors(ph,returndata);

		//check if it's a fish stype crypted action [hadez]
		if(strncmp(returndata,"\001ACTION ",8) == 0)
			{
			// something smells fishy here
			// get rid of the trailing \001
			returndata[strlen(returndata)-1]='\0';
			// ignore the "\001ACTION " part and print it in eaction format
			xchat_printf(ph,EACTIONFORMAT,nick,returndata+8);
			}
		else
			{
			xchat_printf(ph,EMESSAGE_RECV_FORMAT,nick,returndata);
			}
		//xchat_printf(ph,EMESSAGE_RECV_FORMAT,nick,returndata);

		return EAT_ALL;
		}

	// don't eat this event, let xchat handle it also
	return EAT_NONE;
}

static int mc_event_action(char *word[], void *userdata)
{
	// handle event
	char returndata[MAXRETURNSTRINGLEN];
	bool bretv;

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	char nick[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(nick,word[1]);
	char text[MAXLINELEN];
	mclinesafe_strcpy(text,word[2]);

	// decrypt it
	bretv=mircryptor->mc_decrypt2(channelname,text,returndata);
	if (!bretv)
		{
		if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
			xchat_print(ph,returndata);
		return EAT_NONE;
		}
	else if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
		{
		// it was decrypted so we handle it
		//xchat_printf(ph,"[%s] %s",nick,returndata);
		//xchat_commandf(ph,"recv :%s!%s@mircryption PRIVMSG %s :[mc] %s",nick,nick,channelname,returndata);
		strip_mirc_colors(ph,returndata);
		xchat_printf(ph,EACTIONFORMAT,nick,returndata);
		return EAT_ALL;
		}

	// don't eat this event, let xchat handle it also
	return EAT_NONE;
}


static int mc_event_channelnotice(char *word[], void *userdata)
{
	// handle event
	bool bretv;
	char returndata[MAXRETURNSTRINGLEN];

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	char nick[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(nick,word[1]);
	char text[MAXLINELEN];
	mclinesafe_strcpy(text,word[3]);

	// decrypt it
	bretv=mircryptor->mc_decrypt2(channelname,text,returndata);
	if (!bretv)
		{
		if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
			xchat_print(ph,returndata);
		return EAT_NONE;
		}
	if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
		{
		// it was decrypted so we handle it
		//xchat_printf(ph,"[%s] -> %s",nick,returndata);
		//xchat_commandf(ph,"recv :%s!%s@mircryption NOTICE %s :(e) %s",nick,nick,channelname,returndata);
		strip_mirc_colors(ph,returndata);
		xchat_printf(ph,ENOTICE_RECV_FORMAT,channelname,returndata);
		return EAT_ALL;
		}

	// don't eat this event, let xchat handle it also
	return EAT_NONE;
}

static int mc_event_notice(char *word[], void *userdata)
{
	// handle event
	char returndata[MAXRETURNSTRINGLEN];
	bool bretv;

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	char nick[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(nick,word[1]);
	char mynick[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(mynick,xchat_get_info(ph, "nick"));
	char text[MAXLINELEN];
	mclinesafe_strcpy(text,word[2]);

	// decrypt it
	bretv=mircryptor->mc_decrypt2(channelname,text,returndata);
	if (!bretv)
		{
		if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
			xchat_print(ph,returndata);
		return EAT_NONE;
		}
	if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
		{
		// it was decrypted so we handle it
		//xchat_printf(ph,"[%s] -> %s",nick,returndata);
		//xchat_commandf(ph,"recv :%s!%s@mircryption NOTICE %s :(e) %s",nick,nick,mynick,returndata);
		strip_mirc_colors(ph,returndata);
		xchat_printf(ph,ENOTICE_RECV_FORMAT,nick,returndata);
		return EAT_ALL;
		}

	// don't eat this event, let xchat handle it also
	return EAT_NONE;
}

static int mc_event_topic(char *word[], void *userdata)
{
	// handle event
	char returndata[MAXRETURNSTRINGLEN];
	bool bretv;

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,word[1]);
	// new force lowercase
	mylowercasify(channelname);

	char text[MAXLINELEN];
	mclinesafe_strcpy(text,word[2]);
	char mynick[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(mynick,xchat_get_info(ph, "nick"));
	char server[255];
	mcsafe_strcpy(server,xchat_get_info(ph, "server"),255);

	// decrypt it
	bretv=mircryptor->mc_decrypt2(channelname,text,returndata);
	if (!bretv)
		{
		if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
			xchat_print(ph,returndata);
		return EAT_NONE;
		}
	if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
		{
		// it was decrypted so we handle it
		// we dont need to print it since it will be displayed automatically
		// xchat_printf(ph,"Topic for %s is decrypted as: %s",channelname,returndata);
		strip_mirc_colors(ph,returndata);
		xchat_commandf(ph,"recv :%s 332 %s %s :(e) %s",server,mynick,channelname,returndata);
		return EAT_ALL;
		}

	// don't eat this event, let xchat handle it also
	return EAT_NONE;
}

static int mc_event_topicchange(char *word[], void *userdata)
{
	// handle event
	char returndata[MAXRETURNSTRINGLEN];
	bool bretv;

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	char nick[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(nick,word[1]);
	char text[MAXLINELEN];
	mclinesafe_strcpy(text,word[2]);

	// decrypt it
	bretv=mircryptor->mc_decrypt2(channelname,text,returndata);
	if (!bretv)
		{
		if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
			xchat_print(ph,returndata);
		return EAT_NONE;
		}
	if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
		{
		// it was decrypted so we handle it
		// no need to annnounce it, it will get announced automatically by xchat
		// xchat_printf(ph,"%s has encrypted the topic for %s to: %s",nick,channelname,returndata);
		strip_mirc_colors(ph,returndata);
		xchat_commandf(ph,"recv :%s!%s@mircryption TOPIC %s :(e) %s",nick,nick,channelname,returndata);
		return EAT_ALL;
		}

	//xchat_printf(ph,"TOPICCHANGE nick='%s' chjannel='%s' text='%s' return='%s'",nick,channelname,text,returndata);

	// don't eat this event, let xchat handle it also
	return EAT_NONE;
}

static int mc_event_yourmessage(char *word[], void *userdata)
{
	// issue a command to xchat
	// this is kind of wierd because SOMETIMES encryption is handled prior to this in mc_alltext()
	char returndata[MAXRETURNSTRINGLEN];
	bool bretv;

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	char nick[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(nick,word[1]);
	char text[MAXLINELEN];
	mclinesafe_strcpy(text,word[2]);

	if (strncmp(text,"mcps ",5)==0)
		return EAT_ALL;
	if (strncmp(text,OUTGOINGETAG,strlen(OUTGOINGETAG))==0)
		return EAT_ALL;

	// don't eat this event, let xchat handle it also
	return EAT_NONE;
}

static int mc_event_noticesend(char *word[], void *userdata)
{
	char returndata[MAXRETURNSTRINGLEN];
	bool bretv;
	
	// get info
	char dest[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(dest,word[1]);
	char text[MAXLINELEN];
	mclinesafe_strcpy(text,word[2]);

	// if its a meow reply then its already displayed by us so we do nothing here
	if (strncmp(text,"mcps meow meowreply",19)==0)
		return EAT_ALL;

	//lets see if we can decrypt the notice we just sent
	bretv=mircryptor->mc_decrypt2(dest,text,returndata);
	if (!bretv)
		{
		if (strcmp(returndata,"")!=0)
			{
			xchat_print(ph,returndata);
			return EAT_ALL;
			}
		else
			return EAT_NONE;
		}
	else if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
		{
		xchat_printf(ph,ENOTICE_SEND_FORMAT,dest,returndata);
		return EAT_ALL;
		}

	// don't eat this event, let xchat handle it also
	return EAT_NONE;

}
//---------------------------------------------------------------------------





//---------------------------------------------------------------------------
static int mc_help(char *word[], char *word_eol[], void *userdata)
{
	xchat_print(ph,"-\n");
	xchat_print(ph,". Mircryption - xchat version - Dark Raichu & Xro\n");
	xchat_print(ph,". blowfish encryption algorithms are by bruce schneier and jim conger.\n");
	xchat_print(ph,".... /setkey [keyphrase]'             enables encryption/decryption on specified channel (defaults to current channel), using key specified.  can be used to add or modify keys.\n");
	xchat_print(ph,".... /delkey [channel]'               removes encryption key for specified channel (defaults to current channel).\n");
	xchat_print(ph,".... /disablekey'                     temporarily disables encryption for current channel\n");
	xchat_print(ph,".... /enablekey'                      re-enables encryption for current channel\n");
	xchat_print(ph,".... /displaykey'                     shows you (and only you) the key for the current channel\n");
	//      xchat_print(ph,".... /plain ...'                      send ... text without encryption\n");
	xchat_print(ph,".... /listkeys'                       lists all channel encryption keys currently stored\n");
	xchat_print(ph,"... /keypassphrase [phrase]'        set or change current master keyfile passphrase to keyword\n");
	//      xchat_print(ph,".... /mcscheme X'                     set color/identification scheme, where X is the number 1-3\n");
	xchat_print(ph,".... /etopic [text]'                  set an encrypted topic for the channel.\n");
	//      xchat_print(ph,".... /encryptecho channelname text'   echoes encrypted version of text IF channel is set to encrypt, otherwise plaintext\n");
	//      xchat_print(ph,".... /decryptecho channelname text'   echoes decrypted version of text IF channel has a key, otherwise plaintext\n");
	xchat_print(ph,".... /setkeyfile filename'            set the name of the file to be used for storing/retrieving keys\n");
	//      xchat_print(ph,".... /emsg channelname test...        replacement for /msg - encrypts text if appropriate (good for bots, etc.)\n");
	//      xchat_print(ph,".... /mcmeow [channelname]            broadcast handshake query to channel\n");
	//      xchat_print(ph,".... /etext text...                   same as typing text... in encrypted channel, BUT igores disabling, and wont send to channel without encryption\n");
	//      xchat_print(ph,".... /textpad                         launches the textpad dialog for copy and paste of big text\n");
	xchat_print(ph,"-\n");

	/// eat this command so xchat and other plugins can't process it
	return EAT_ALL;
}
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
static int mc_setkey(char *word[], char *word_eol[], void *userdata)
{
	// handle this command
	char returndata[MAXRETURNSTRINGLEN];

	// get info
	char keyphrase[MAXKEYSIZE];
	mckeysafe_strcpy(keyphrase,word_eol[2]); 
	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	// execute command
	mircryptor->mc_setkey(channelname,keyphrase,returndata);

	// display result
	xchat_printf(ph, "%s",returndata);

	/// eat this command so xchat and other plugins can't process it
	return EAT_ALL;
}


static int mc_delkey(char *word[], char *word_eol[], void *userdata)
{
	// handle this command
	char returndata[MAXRETURNSTRINGLEN];

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	// execute command
	mircryptor->mc_delkey(channelname,returndata);

	// display result
	xchat_printf(ph, "%s",returndata);

	/// eat this command so xchat and other plugins can't process it
	return EAT_ALL;
}


static int mc_disablekey(char *word[], char *word_eol[], void *userdata)
{
	// handle this command
	char returndata[MAXRETURNSTRINGLEN];

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	// execute command
	mircryptor->mc_disablekey(channelname,returndata);

	// display result
	xchat_printf(ph, "%s",returndata);

	/// eat this command so xchat and other plugins can't process it
	return EAT_ALL;
}


static int mc_enablekey(char *word[], char *word_eol[], void *userdata)
{
	// handle this command
	char returndata[MAXRETURNSTRINGLEN];

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	// execute command
	mircryptor->mc_enablekey(channelname,returndata);

	// display result
	xchat_printf(ph, "%s",returndata);

	/// eat this command so xchat and other plugins can't process it
	return EAT_ALL;
}


static int mc_displaykey(char *word[], char *word_eol[], void *userdata)
{
	// handle this command
	char returndata[MAXRETURNSTRINGLEN];

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	// execute command
	mircryptor->mc_displaykey(channelname,returndata);

	// display result
	xchat_printf(ph, "%s",returndata);

	/// eat this command so xchat and other plugins can't process it
	return EAT_ALL;
}


static int mc_listkeys(char *word[], char *word_eol[], void *userdata)
{
	// handle this command
	char returndata[MAXRETURNSTRINGLEN];

	// execute command
	mircryptor->mc_listkeys(returndata);

	/// eat this command so xchat and other plugins can't process it
	return EAT_ALL;
}


static int mc_keypassphrase(char *word[], char *word_eol[], void *userdata)
{
	// handle this command
	char returndata[MAXRETURNSTRINGLEN];

	// get info
	char keyphrase[MAXKEYSIZE];
	strcpy(keyphrase,word_eol[2]); 

	// execute command
	mircryptor->mc_setunlockpassphrase(keyphrase,returndata);

	// display result
	xchat_printf(ph, "%s",returndata);

	/// eat this command so xchat and other plugins can't process it
	return EAT_ALL;
}


static int mc_setkeyfile(char *word[], char *word_eol[], void *userdata)
{
	// handle this command
	char returndata[MAXRETURNSTRINGLEN];

	// get info
	char keyfile[MAXKEYSIZE];
	strcpy(keyfile,word_eol[2]); 

	// execute command
	mircryptor->mc_setkeyfilename(keyfile,returndata);

	// display result
	xchat_printf(ph, "%s",returndata);

	/// eat this command so xchat and other plugins can't process it
	return EAT_ALL;
}


static int mc_etopic(char *word[], char *word_eol[], void *userdata)
{
	// handle this command
	bool bretv;
	char returndata[MAXRETURNSTRINGLEN];

	// get info
	char text[MAXLINELEN];
	char text2[MAXLINELEN];
	strcpy(text,word_eol[2]); 
	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	// utf8 fix for text
	Utf8DirtyFix(text,text2);

	// encrypt it
	bretv=mircryptor->mc_encrypt2(channelname,text2,returndata);
	//	xchat_printf(ph,"debug: topic for %s:tried to encrypt '%s' to '%s'",channelname,text,returndata);
	if (!bretv)
		{
		if (strcmp(returndata,"")!=0)
			{
			xchat_print(ph,returndata);
			return EAT_ALL;
			}
		else
			return EAT_NONE;
		}
	else if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
		{
		// it was encrypted so we handle it
		// send the encrypted text
		xchat_commandf(ph,"TOPIC %s %s",channelname,returndata);
		// we dont actually display it here, as it will be displayed on the event we trigger when we set the topic
		// xchat_printf(ph,"Encrypted topic for [%s] set to: %s",channelname,text);
		return EAT_ALL;
		}
	else
		{
		// error
		xchat_printf(ph,"topic could not be encrypted, so it wasnt set.");
		}

	/// eat this command so xchat and other plugins can't process it
	return EAT_ALL;
}


static int mc_alltext(char *word[], char *word_eol[], void *userdata)
{
	// handle this command
	bool bretv;
	char returndata[MAXRETURNSTRINGLEN];

	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	char text[MAXLINELEN];
	char text2[MAXLINELEN];
	mclinesafe_strcpy(text,word_eol[1]);
	char nick[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(nick,xchat_get_info(ph, "nick")); 

	// note we dont need this (in fact it causes problems) because xchat, unlike mirc
	//  will not invoke mc_alltext on commands
	/*
	if (word_eol[1][0]=='/')
		{
		// a command, so not for us
		return EAT_NONE;
		}
	*/

	// are they using the reverse-encryption prefix?
	bool isencrypting=mircryptor->mc_isencrypting(channelname,returndata);
	if (text[0]=='`')
		{
		// yes they are using the prefix.  remove it.
		strcpy(text,&text[1]);
		if (isencrypting)
			{
			// they are normally set to encrypt, so we disable it and send plain text
			xchat_commandf(ph,"MSG %s %s",channelname,text);
			// we dont show the user what they typed here, it will be shown in event_ourmessage
			//	xchat_printf(ph,MESSAGE_SEND_FORMAT,nick,text);
			return EAT_ALL;
			}
		// encryption is disabled normally (or doesnt exist at all)
		// we just drop through in this case.
		}
	else if (!isencrypting)
		return EAT_NONE;

	// utf8 fix for text
	Utf8DirtyFix(text,text2);

	// encrypt it
	bretv=mircryptor->mc_forceencrypt(channelname,text2,returndata);
	if (!bretv)
		{
		if (strcmp(returndata,"")!=0)
			{
			xchat_print(ph,returndata);
			return EAT_ALL;
			}
		else
			return EAT_NONE;
		}
	else if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
		{
		// it was encrypted so we handle it
		// send the encrypted text
		xchat_commandf(ph,"MSG %s %s %s",channelname,OUTGOINGETAG,returndata);
		// show user their own text
		//xchat_printf(ph,"[%s] %s",nick,text);
		//xchat_commandf(ph,"recv :%s!%s@mircryption PRIVMSG %s :(e) %s",nick,nick,channelname,text);
		xchat_printf(ph,EMESSAGE_SEND_FORMAT,nick,text);
		return EAT_ALL;
		}

	// let xchat handle this normally
	return EAT_NONE;
}



static int mc_action(char *word[], char *word_eol[], void *userdata)
{
	// handle this command
	bool bretv;
	char returndata[MAXRETURNSTRINGLEN];

	// get info
	char text[MAXLINELEN];
	char text2[MAXLINELEN];
	strcpy(text,word_eol[2]); 
	// get info
	char channelname[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(channelname,xchat_get_info(ph, "channel")); 
	// new force lowercase
	mylowercasify(channelname);

	char nick[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(nick,xchat_get_info(ph, "nick")); 
	
	if (strcmp(word[2],"mcps")==0 || strcmp(word[2],OUTGOINGETAG)==0)
		{
		//we already encrypted it
		//even though this is a not-nice hack
		//it is necessary to avoid recursive calls
		return EAT_NONE;
		}

	// utf8 fix for text
	Utf8DirtyFix(text,text2);

	// encrypt it
	bretv=mircryptor->mc_encrypt(channelname,text2,returndata);
	if (!bretv)
		{
		if (strcmp(returndata,"")!=0)
			{
			xchat_print(ph,returndata);
			return EAT_ALL;
			}
		else
			return EAT_NONE;
		}
	else if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
		{
		// it was encrypted so we handle it
		// send the encrypted text
		xchat_commandf(ph,"me %s %s",OUTGOINGETAG,returndata);
		//xchat_printf(ph,"mc \00313*\00300\t%s %s",nick,text); //xchat already does that when the action comes back from the server
		return EAT_ALL;
		}
	else
		{
		// error
		xchat_printf(ph,"action could not be encrypted, so it wasnt set.");
		}

	/// eat this command so xchat and other plugins can't process it
	return EAT_ALL;
}

static int mc_notice(char *word[], char *word_eol[], void *userdata)
{
	// handle this command
	bool bretv;
	char returndata[MAXRETURNSTRINGLEN];

	// get info
	char text[MAXLINELEN];
	char text2[MAXLINELEN];
	mclinesafe_strcpy(text,word_eol[3]); 
	// get destination (nick/channel)
	char dest[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(dest,word[2]); 
	// new force lowercase
	mylowercasify(dest);

	if (strcmp(word[3],"mcps")==0 || strcmp(word[3],OUTGOINGETAG)==0)
		{
		//we already encrypted it
		//even though this is a not-nice hack
		//it is necessary to avoid recursive calls
		return EAT_NONE;
		}

	// utf8 fix for text
	Utf8DirtyFix(text,text2);

	// encrypt it
	bretv=mircryptor->mc_encrypt(dest,text2,returndata);
	if (!bretv)
		{
		if (strcmp(returndata,"")!=0)
			{
			xchat_print(ph,returndata);
			return EAT_ALL;
			}
		else
			return EAT_NONE;
		}
	else if (strcmp(returndata,text)!=0 && returndata[0]!='\0')
		{
		// it was encrypted so we handle it
		// send the encrypted text
		xchat_commandf(ph,"notice %s %s %s",dest,OUTGOINGETAG,returndata);
		//xchat_printf(ph,"mc \00313*\00300\t%s %s",nick,text); //xchat already does that when the action comes back from the server
		return EAT_ALL;
		}
	else
		{
		// error
		xchat_printf(ph,"notice could not be encrypted, so it wasnt sent.");
		}

	/// eat this command so xchat and other plugins can't process it
	return EAT_ALL;
}

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









//---------------------------------------------------------------------------
char *MircryptionClass_xchat::get_classversionstring()
{
	// return some version information - you dont have to override this one
	return "Mircryption class for xchat";
}


bool MircryptionClass_xchat::present_messsagebox(char *messagetext,char *windowtitle)
{
	// present some info to the user, preferably in a pop-up modal dialog
	// returns true on success

	display_statustext(messagetext);
	return false;
}


// we need the user to give us a valid master passphrase
bool MircryptionClass_xchat::request_unlockpassphrase()
{
	// return true if user gives us a valid passphrase, and unlock the keys, false if not
	// I dont know how to ask xchat client for info yet, so instead we just return false and expect uuser to set it with /keypassphrase
	return false;
}


bool MircryptionClass_xchat::send_irccommand(char *irccommand,char *text)
{
	// send an irc command to server
	// returns true on success

	xchat_commandf(ph,"%s %s",irccommand,text);
	return false;
}


bool MircryptionClass_xchat::display_statustext(char *messagetext)
{
	// display some information for the user in some default text area (like status window in mirc)
	// returns true on success

	xchat_print(ph, messagetext);
	return true;
}
//---------------------------------------------------------------------------




//---------------------------------------------------------------------------
void mylowercasify(char *str)
{
	if (str==NULL)
		return;

	int len=strlen(str);
	char c;

	for (int count=0;count<len;++count)
		{
		c=str[count];
		if (c>='A' && c<='Z')
			str[count]=c+32;
		}
}
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
/*
  Author: hadez@darklab.org
  Date: 27.12.03 22:09
  Description: frontend to mirc_codes.h
*/
void strip_mirc_colors(xchat_plugin* ph, char* textline)
{
	int i;
	const char *str;
	if (xchat_get_prefs(ph,"text_stripcolor",&str, &i) == 3) // successfully got boolan value
		{
		if(i == 1) // stripping enabled
			{
			mirc_codes::clean(textline);
			}
		}
}
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
bool HandleMeow(char *channelname,char *nick,char *text)
{
	// handle a meow and return true on success
	char returndata[MAXRETURNSTRINGLEN],estatus[MAXRETURNSTRINGLEN];
	bool bretv;

	// grab sender string
	char *senderstring,*inteststring;
	senderstring=strtok(text," ");
	senderstring=strtok(NULL," "); senderstring=strtok(NULL," "); senderstring=strtok(NULL," ");senderstring=strtok(NULL," ");
	inteststring=strtok(NULL," ");
		
	// if they want to ignore meows
	if (IGNOREMEOWS || senderstring==NULL || inteststring==NULL)
		{
		xchat_printf(ph,"ignoring meow broadcast from %s on channel %s (set IGNOREMEOWS to false in mircryption.cpp to stop ignoring).",nick,channelname);
		return true;
		}

	// decrypt the test string
	char teststring[MAXRETURNSTRINGLEN];
	sprintf(teststring,"mcps %s",inteststring);
	bretv=mircryptor->mc_decrypt2(channelname,teststring,returndata);
	if (!bretv || strcmp(returndata,teststring)==0 || returndata[0]=='\0')
		{
		// no key for chan
		strcpy(estatus,"no encryption key for this channel");
		}
	else
		{
		// does it match meow senders key
		if (strcmp(returndata,"meow")==0)
			strcpy(estatus,"crypting (key match)");		
		else
			strcpy(estatus,"crypting (key mismatch)");
		}

	// get our nick
	char mynick[MAXCHANNELNAMESIZE];
	mcnicksafe_strcpy(mynick,xchat_get_info(ph, "nick")); 
	
	// show user what we are replying
	xchat_printf(ph,"[=^.^=] [%s] %s -> meow %s %s",senderstring,nick,channelname,estatus);

	// send reply as notice
	sprintf(returndata,"mcps meow meowreply %s %s [%s] %s -> %s",nick,channelname,XMC_VERSIONSTRING,mynick,estatus);
	xchat_commandf(ph,"notice %s %s",nick,returndata);
	
	// return success
	return true;
}
//---------------------------------------------------------------------------





//---------------------------------------------------------------------------
void Utf8DirtyFix(char *intext,char *outtext)
{
	// walk through the string and change any xchat encoded utf8 (glib) special characters
	// really the ideal way to fix this is for xchat developers to expose a function to conver the plugin-passed utf8 text to local text
	// but for now we use this quick fix
	unsigned char c,c2;
	int len=strlen(intext);
	int desti=0;
	
	for (int count=0;count<len;++count)
	    {
		c=intext[count];
		if (c==195)
		    {
			// ok special two-byte character prefix (skip over it)
			++count;
			c2=intext[count];
			// now set c to actual character to replace
			if (c2==0)
				c=0;
			else
			    {
			    // apparent dirty fix for utf8
				c=c2+64;
				}
			}
		// write it and increment index
		outtext[desti]=c;
		++desti;
		}
	// '\0' terminate
	outtext[desti]='\0';
}
//---------------------------------------------------------------------------


syntax highlighted by Code2HTML, v. 0.9.1