diff src/perl.c @ 4617:858979ab3867

[gaim-migrate @ 4908] Big Changes: -Rewrote some of the perl stuff so perl scripts can change a few of their parameters -Receiving a file with AIM over oscar works pretty well Now, the "nitty gritty": Very minor change to prefs.c: In the plugins details tab, I changed "URL" to "Web Site." I was just going to fix the tabbing, but silvestrij suggested changing it to "Web site," and I thought that sounded good. I think it fits better, too. I dunno, maybe that's just me. "Get Capabilities" has stopped working for some reason. I'm just going to blame AOL. It's really not important anyway, and some people wanted it taken off. It is now #ifdef 0'ed out. I'll remove it completely if it continues to no longer function. I took out a few plugin_event calls from oscar.c and put them in core code. "event_error" should be, uh, "evented" when there is an error signing on. Hopefully no one was using this. It's really pretty useless. The parameter is now the reason for not being able to connect rather than the archaic toc error code. I screwed around with how perl functions are called some. There was way the hell too much malloc'ing going on here. I think all in all it's an improvement, though I'm still not a big fan of how changes to parameters propagate to the actual memory. I really think it would be nice if the perl stuff was made into a C plugin. It's just so much cleaner. Especially if someone wanted to write, say, a python or tcl interpreter. That's how xchat2 does it. I just think that would be really slick. Like butter. Or ice. Very unlike Velcro. I added a "Change Password" Protocol Action for ICQ over oscar. This was really pretty easy. I'd like to thank my housemate Andrew for complaining a lot that having to use Windows ICQ to change his password was a pain. I rewrote a lot of the oscar file transfer stuff to use Christian's new xfer interface. This involved moving a few functions from ft.c to im.c, where they belong. I also removed all the #if 0'ed getfile functions. I'll be rewritting them soonish. Receiving a file should work perfectly, aside from maybe a small memleak when stuff is canceled. Sending a file is currently disabled. No ETA on when I'll have that working. I renamed pretty much all of the functions in im.c so they have kind of a scheme now. They should all be aim_im_bleh, since "im" is the family name. There comes a time when you must break the crap out of any clients that might be using libfaim in order to make stuff cleaner. Maybe. I got rid of the snac destructor stuff for now. I'll probably add it back later. I wasn't entirely comfortable with how it was done. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Wed, 26 Feb 2003 05:01:37 +0000
parents 9df99116840a
children 22215f5129b2
line wrap: on
line diff
--- a/src/perl.c	Wed Feb 26 03:39:07 2003 +0000
+++ b/src/perl.c	Wed Feb 26 05:01:37 2003 +0000
@@ -132,7 +132,7 @@
 XS(XS_GAIM_add_timeout_handler); /* figure it out */
 
 /* play sound */
-XS(XS_GAIM_play_sound); /*play a sound*/
+XS(XS_GAIM_play_sound); /*play a sound */
 
 static void
 #ifdef OLD_PERL
@@ -188,76 +188,106 @@
 }
 
 /*
+  2003/02/06: execute_perl modified by Mark Doliner <mark@kingant.net>
+		Pass parameters by pushing them onto the stack rather than 
+		passing an array of strings.  This way, perl scripts can 
+		modify the parameters and we can get the changed values 
+		and then shoot ourselves.  I mean, uh, use them.
+
   2001/06/14: execute_perl replaced by Martin Persson <mep@passagen.se>
-              previous use of perl_eval leaked memory, replaced with
-              a version that uses perl_call instead
-  30/11/2002: execute_perl modified by Eric Timme <timothy@voidnet.com>
-			  args changed to char** so that we can have preparsed
-  			  arguments again, and many headaches ensued! This essentially 
-			  means we replaced one hacked method with a messier hacked 
-			  method out of perceived necessity. Formerly execute_perl 
-			  required a single char_ptr, and it would insert it into an 
-			  array of character pointers and NULL terminate the new array.
-			  Now we have to pass in pre-terminated character pointer arrays
-			  to accomodate functions that want to pass in multiple arguments.
+		previous use of perl_eval leaked memory, replaced with
+		a version that uses perl_call instead
 
-			  Previously arguments were preparsed because an argument list
-			  was constructed in the form 'arg one','arg two' and was
-			  executed via a call like &funcname(arglist) (see .59.x), so
-			  the arglist was magically pre-parsed because of the method. 
-			  With Martin Persson's change to perl_call we now need to
-			  use a null terminated list of character pointers for arguments
-			  if we wish them to be parsed. Lacking a better way to allow
-			  for both single arguments and many I created a NULL terminated
-			  array in every function that called execute_perl and passed
-			  that list into the function.  In the former version a single
-			  character pointer was passed in, and was placed into an array
-			  of character pointers with two elements, with a NULL element
-			  tacked onto the back, but this method no longer seemed prudent.
+  30/11/2002: execute_perl modified by Eric Timme <timothy@voidnet.com>
+		args changed to char** so that we can have preparsed
+  		arguments again, and many headaches ensued! This essentially 
+		means we replaced one hacked method with a messier hacked 
+		method out of perceived necessity. Formerly execute_perl 
+		required a single char_ptr, and it would insert it into an 
+		array of character pointers and NULL terminate the new array.
+		Now we have to pass in pre-terminated character pointer arrays
+		to accomodate functions that want to pass in multiple arguments.
 
-			  Enhancements in the future might be to get rid of pre-declaring
-			  the array sizes?  I am not comfortable enough with this
-			  subject to attempt it myself and hope it to stand the test
-			  of time.
+		Previously arguments were preparsed because an argument list
+		was constructed in the form 'arg one','arg two' and was
+		executed via a call like &funcname(arglist) (see .59.x), so
+		the arglist was magically pre-parsed because of the method. 
+		With Martin Persson's change to perl_call we now need to
+		use a null terminated list of character pointers for arguments
+		if we wish them to be parsed. Lacking a better way to allow
+		for both single arguments and many I created a NULL terminated
+		array in every function that called execute_perl and passed
+		that list into the function.  In the former version a single
+		character pointer was passed in, and was placed into an array
+		of character pointers with two elements, with a NULL element
+		tacked onto the back, but this method no longer seemed prudent.
+
+		Enhancements in the future might be to get rid of pre-declaring
+		the array sizes?  I am not comfortable enough with this
+		subject to attempt it myself and hope it to stand the test
+		of time.
 */
 
 static int 
 execute_perl(char *function, char **args)
 {
-	char buf[512];
-	int count, ret_value = 1;
-	SV *sv;
+	int count, i, ret_value = 1;
+	SV *sv_args[5];
+	STRLEN na;
 
+	/*
+	 * Set up the perl environment, push arguments onto the 
+	 * perl stack, then call the given function
+	 */
 	dSP;
-        ENTER;
-        SAVETMPS;
-        PUSHMARK(sp);
-        count = perl_call_argv(function, G_EVAL | G_SCALAR, args);
-        SPAGAIN;
+	ENTER;
+	SAVETMPS;
+	PUSHMARK(sp);
+	for (i=0; i<5; i++)
+		if (args[i]) {
+			sv_args[i] = sv_2mortal(newSVpv(args[i], 0));
+			XPUSHs(sv_args[i]);
+		}
+	PUTBACK;
+	count = call_pv(function, G_EVAL | G_SCALAR);
+	SPAGAIN;
 
-        sv = GvSV(gv_fetchpv("@", TRUE, SVt_PV));
-        if (SvTRUE(sv)) {
-                g_snprintf(buf, 512, "Perl error: %s\n", SvPV(sv, count));
-                debug_printf(buf);
-                POPs;
-        } else if (count != 1) {
-                g_snprintf(buf, 512, "Perl error: expected 1 value from %s, "
-                        "got: %d\n", function, count);
-                debug_printf(buf);
-        } else {
-                ret_value = POPi;
-        }
+	/* Check for "die," make sure we have 1 argument, and set our return value */
+	if (SvTRUE(ERRSV)) {
+		debug_printf("Perl function %s exited abnormally: %s\n", function, SvPV(ERRSV, na));
+		POPs;
+	} else if (count != 1) {
+		/* This should NEVER happen.  G_SCALAR ensures that we WILL have 1 parameter */
+		debug_printf("Perl error from %s: expected 1 return value, but got %d\n", function, count);
+	} else {
+		ret_value = POPi;
+	}
 
-        PUTBACK;
-        FREETMPS;
-        LEAVE;
+	/* Check for changed arguments */
+	for (i=0; i<5; i++)
+		if (args[i] && strcmp(args[i], SvPVX(sv_args[i]))) {
+			/*
+			 * Shizzel.  So the perl script changed one of the parameters, and we want 
+			 * this change to affect the original parameters.  args[i] is just a tempory 
+			 * little list of pointers.  We don't want to free args[i] here because the 
+			 * new parameter doesn't overwrite the data that args[i] points to.  That is 
+			 * done by the function that called execute_perl.  I'm not explaining this 
+			 * very well.  See, it's aggregate...  Oh, but if 2 perl scripts both modify 
+			 * the data, _that's_ a memleak.  This is really kind of hackish.  I should 
+			 * fix it.  Look how long this comment is.  Holy crap.
+			 */
+			args[i] = g_strdup(SvPV(sv_args[i], na));
+		}
 
-        return ret_value;
+	PUTBACK;
+	FREETMPS;
+	LEAVE;
 
+	return ret_value;
 }
 
 void perl_unload_file(struct gaim_plugin *plug) {
-	char *atmp[2] = { "", NULL }; /* see execute_perl --et */
+	char *atmp[1] = { "" };
 	struct perlscript *scp = NULL;
 	struct _perl_timeout_handlers *thn;
 	struct _perl_event_handlers *ehn;
@@ -305,14 +335,14 @@
 		pl = pl->next;
 	}
 
-	plug->handle=NULL;
+	plug->handle = NULL;
 	plugins = g_list_remove(plugins, plug);
 	save_prefs();
 }
 
 int perl_load_file(char *script_name)
 {
-	char *atmp[2] = { script_name, NULL }; /* see execute_perl --et */
+	char *atmp[1] = { script_name };
 	struct gaim_plugin *plug = NULL;
 	GList *p = probed_plugins;
 	GList *s;
@@ -399,13 +429,13 @@
 }
 
 static void perl_init()
-{        /*changed the name of the variable from load_file to
+{	/* changed the name of the variable from load_file to
 	   perl_definitions since now it does much more than defining
 	   the load_file sub. Moreover, deplaced the initialisation to
 	   the xs_init function. (TheHobbit)*/
-        char *perl_args[] = { "", "-e", "0", "-w" };
-        char perl_definitions[] =
-        {
+	char *perl_args[] = { "", "-e", "0", "-w" };
+	char perl_definitions[] =
+	{
 		/* We use to function one to load a file the other to
 		   execute the string obtained from the first and holding
 		   the file conents. This allows to have a realy local $/
@@ -451,7 +481,7 @@
 
 void perl_end()
 {
-	char *atmp[2] = { "", NULL }; /* see execute_perl --et */
+	char *atmp[1] = { "" };
 	struct perlscript *scp;
 	struct _perl_timeout_handlers *thn;
 	struct _perl_event_handlers *ehn;
@@ -693,7 +723,7 @@
 
 	command = SvPV(ST(0), junk);
 	if (!command) XSRETURN(0);
-	if        (!strncasecmp(command, "signon", 6)) {
+	if (!strncasecmp(command, "signon", 6)) {
 		int index = SvIV(ST(1));
 		if (g_slist_nth_data(gaim_accounts, index))
 		serv_login(g_slist_nth_data(gaim_accounts, index));
@@ -840,9 +870,8 @@
 	else
 		gaim_conversation_set_account(c, gc->account);
 
-	gaim_conversation_write(c, NULL, what, -1,
-							(WFLAG_SEND | (isauto ? WFLAG_AUTO : 0)),
-							time(NULL));
+	gaim_conversation_write(c, NULL, what, -1, 
+				(WFLAG_SEND | (isauto ? WFLAG_AUTO : 0)), time(NULL));
 	serv_send_im(gc, nick, what, -1, isauto ? IM_FLAG_AWAY : 0);
 	XSRETURN(0);
 }
@@ -884,50 +913,40 @@
 
 int perl_event(enum gaim_event event, void *arg1, void *arg2, void *arg3, void *arg4, void *arg5)
 {
-	/* changing buf to an array of char pointers so we get @_ back in	
-	 * order; basically scrapping legacy code in favor of blocky		 
-	 * unimaginative code. Additionally, assigned values one at a time 
-	 * for the sake of clarity. --Eric Timme (30 November 2002)	*/ 
-
-	char *buf[5] = { NULL, NULL, NULL, NULL, NULL }; 
-	/* 5 for max 4 arguments =\  Note, if you change the above,
-	 * change the two loops at the bottom that free up memory */
-
+	char *buf[5] = { NULL, NULL, NULL, NULL, NULL }; /* Maximum of 5 args */
+	char tmpbuf1[16], tmpbuf2[16], tmpbuf3[1];
 	GList *handler;
 	struct _perl_event_handlers *data;
 	int handler_return;
-	int i=0; /* necessary counter variable? --et */
 
+	tmpbuf1[0] = '\0';
+	tmpbuf2[0] = '\0';
+	tmpbuf3[0] = '\0';
+
+	/* Make a pretty array of char*'s with which to call perl functions */
 	switch (event) {
 	case event_signon:
 	case event_signoff:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
 		break;
 	case event_away:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", 
-				((struct gaim_connection *)arg1)->away ?
-					escape_quotes(((struct gaim_connection *)arg1)->away) : "");
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = ((struct gaim_connection *)arg1)->away ?
+				((struct gaim_connection *)arg1)->away : tmpbuf2;
 		break;
 	case event_im_recv:
-		{
-		char *tmp = *(char **)arg2 ? g_strdup(escape_quotes(*(char **)arg2)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", tmp);
-		buf[2] = g_strdup_printf("%s",
-				*(char **)arg3 ? escape_quotes(*(char **)arg3) : "");
-		g_free(tmp);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = *(char **)arg2 ? *(char **)arg2 : tmpbuf3;
+		buf[2] = *(char **)arg3 ? *(char **)arg3 : tmpbuf3;
 		break;
 	case event_im_send:
-		{
-		char *tmp = arg2 ? g_strdup(escape_quotes(arg2)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", tmp);
-		buf[2] = g_strdup_printf("%s", 
-			   *(char **)arg3 ? escape_quotes(*(char **)arg3) : "");
-		g_free(tmp);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2 ? arg2 : tmpbuf3;
+		buf[2] = *(char **)arg3 ? *(char **)arg3 : tmpbuf3;
 		break;
 	case event_buddy_signon:
 	case event_buddy_signoff:
@@ -937,103 +956,81 @@
 	case event_buddy_idle:
 	case event_buddy_unidle:
 	case event_got_typing:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", escape_quotes(arg2));
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2;
 		break;
 	case event_chat_invited:
-		{
-		char *tmp2, *tmp3, *tmp4;
-		tmp2 = g_strdup(escape_quotes(arg2));
-		tmp3 = g_strdup(escape_quotes(arg3));
-		tmp4 = arg4 ? g_strdup(escape_quotes(arg4)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", tmp2);
-		buf[2] = g_strdup_printf("%s", tmp3);
-		buf[3] = g_strdup_printf("%s", tmp4);
-		g_free(tmp2);
-		g_free(tmp3);
-		g_free(tmp4);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2;
+		buf[2] = arg3;
+		buf[3] = arg4;
 		break;
 	case event_chat_join:
 	case event_chat_buddy_join:
 	case event_chat_buddy_leave:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%d", (int)arg2);
-		buf[2] = g_strdup_printf("%s", escape_quotes(arg3));
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		buf[2] = arg3;
 		break;
 	case event_chat_leave:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%d", (int)arg2);
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
 		break;
 	case event_chat_recv:
-		{
-		char *t3, *t4;
-		t3 = g_strdup(escape_quotes(*(char **)arg3));
-		t4 = *(char **)arg4 ? g_strdup(escape_quotes(*(char **)arg4)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%d", (int)arg2);
-		buf[2] = g_strdup_printf("%s", t3);
-		buf[3] = g_strdup_printf("%s", t4);
-		g_free(t3);
-		g_free(t4);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		buf[2] = *(char **)arg3;
+		buf[3] = *(char **)arg4 ? *(char **)arg4 : tmpbuf3;
 		break;
 	case event_chat_send_invite:
-		{
-		char *t3, *t4;
-		t3 = g_strdup(escape_quotes(arg3));
-		t4 = *(char **)arg4 ? g_strdup(escape_quotes(*(char **)arg4)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%d", (int)arg2);
-		buf[2] = g_strdup_printf("%s", t3);
-		buf[3] = g_strdup_printf("%s", t4);
-		g_free(t3);
-		g_free(t4);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		buf[2] = arg3;
+		buf[3] = *(char **)arg4 ? *(char **)arg4 : tmpbuf3;
 		break;
 	case event_chat_send:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%d", (int)arg2);
-		buf[2] = g_strdup_printf("%s", 
-				*(char **)arg3 ? escape_quotes(*(char **)arg3) : "");
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		buf[2] = *(char **)arg3 ? *(char **)arg3 : tmpbuf3;
 		break;
 	case event_warned:
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", arg2 ? escape_quotes(arg2) : "");
-		buf[2] = g_strdup_printf("%d", (int)arg3);
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2 ? arg2 : tmpbuf3;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg3);
+		buf[2] = tmpbuf2;
 		break;
 	case event_quit:
 	case event_blist_update:
-		buf[0] = g_malloc0(1);
+		buf[0] = tmpbuf3;
 		break;
 	case event_new_conversation:
 	case event_del_conversation:
-		buf[0] = g_strdup_printf("%s", escape_quotes(arg1));
+		buf[0] = arg1;
 		break;
 	case event_im_displayed_sent:
-		{
-		char *tmp2, *tmp3;
-		tmp2 = g_strdup(escape_quotes(arg2));
-		tmp3 = *(char **)arg3 ? g_strdup(escape_quotes(*(char **)arg3)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", tmp2);
-		buf[2] = g_strdup_printf("%s", tmp3);
-		g_free(tmp2);
-		g_free(tmp3);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2;
+		buf[2] = *(char **)arg3 ? *(char **)arg3 : tmpbuf3;
 		break;
 	case event_im_displayed_rcvd:
-		{
-		char *tmp2, *tmp3;
-		tmp2 = g_strdup(escape_quotes(arg2));
-		tmp3 = arg3 ? g_strdup(escape_quotes(arg3)) : g_malloc0(1);
-		buf[0] = g_strdup_printf("%lu", (unsigned long)arg1);
-		buf[1] = g_strdup_printf("%s", tmp2);
-		buf[2] = g_strdup_printf("%s", tmp3);
-		g_free(tmp2);
-		g_free(tmp3);
-		}
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2;
+		buf[2] = arg3 ? arg3 : tmpbuf3;
 		break;
 	case event_draw_menu:
 		/* we can't handle this usefully without gtk/perl bindings */
@@ -1043,26 +1040,66 @@
 		return 0;
 	}
 
+	/* Call any applicable functions */
 	for (handler = perl_event_handlers; handler != NULL; handler = handler->next) {
 		data = handler->data;
 		if (!strcmp(event_name(event), data->event_type)) {
 			handler_return = execute_perl(data->handler_name, buf);
 			if (handler_return) {
-				/* loop to try and free up memory from the 
-				 * char pointer array? --et */
-				for (i=0;i<5;i++)
-					if (buf[i])
-						g_free(buf[i]);
 				return handler_return;
 			}
 		}
 	}
 
-	/* loop to try and free up memory from the 
-	 * char pointer array? --et */
-	for (i=0;i<5;i++)
-		if (buf[i])
-			g_free(buf[i]);
+	/* Now make changes from perl scripts affect the real data */
+	switch (event) {
+	case event_im_recv:
+		if (buf[1] != *(char **)arg2) {
+			free(*(char **)arg2);
+			*(char **)arg2 = buf[1];
+		}
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		break;
+	case event_im_send:
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		break;
+	case event_chat_recv:
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		if (buf[3] != *(char **)arg4) {
+			free(*(char **)arg4);
+			*(char **)arg4 = buf[3];
+		}
+		break;
+	case event_chat_send_invite:
+		if (buf[3] != *(char **)arg4) {
+			free(*(char **)arg4);
+			*(char **)arg4 = buf[3];
+		}
+		break;
+	case event_chat_send:
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		break;
+	case event_im_displayed_sent:
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		break;
+	default:
+		break;
+	}
 
 	return 0;
 }
@@ -1114,19 +1151,19 @@
 		if (!strcmp(ehn->event_type, SvPV(ST(0), junk)) &&
 			!strcmp(ehn->handler_name, SvPV(ST(1), junk)))
 		{
-	        perl_event_handlers = g_list_remove(perl_event_handlers, ehn);
+			perl_event_handlers = g_list_remove(perl_event_handlers, ehn);
 			g_free(ehn->event_type);
 			g_free(ehn->handler_name);
 			g_free(ehn);
 		}
 
 		cur = next;
-    }
+	}
 }
 
 static int perl_timeout(gpointer data)
 {
-	char *atmp[2] = { NULL, NULL }; /* see execute_perl --et */
+	char *atmp[1] = { NULL };
 	struct _perl_timeout_handlers *handler = data;
 
 	atmp[0] = escape_quotes(handler->handler_args);