Package: xchat
Version: 2.8.8-3
Severity: normal
Tags: patch

--- Please enter the report below this line. ---
The xchat python plugin incorrectly terminates the data it passes to
scripts if they contain intermediate NULL values. 

To reproduce you can have a python script to call xchat.hook_print for
"Quit" with a callback to print the whole list it receives.  Then, join
a test channel and you can use the 'recv' command to simulate a
join/quit:

/recv :[email protected] JOIN #testchannel
/recv :[email protected] QUIT

This will not display the host portion of the list, because the quit
message was a NULL and the host is the third item.

In contrast, using:

/recv :[email protected] JOIN #testchannel
/recv :[email protected] QUIT :message

This will display the host portion of the list.

The upstream report, with patch:
https://sourceforge.net/tracker/index.php?func=detail&aid=2947504&group_id=239&atid=100239

Attaching the patch here as well.

--- System information. ---
Architecture: i386
Kernel:       Linux 2.6.38-2-686-bigmem

Debian Release: wheezy/sid
  500 unstable        ftp.us.debian.org 
    1 experimental    ftp.us.debian.org 

--- Package information. ---
Depends                  (Version) | Installed
==================================-+-============
libatk1.0-0            (>= 1.12.4) | 2.0.0-1
libc6                     (>= 2.7) | 2.13-2
libcairo2               (>= 1.2.4) | 1.10.2-6
libdbus-1-3             (>= 1.0.2) | 1.4.8-3
libdbus-glib-1-2         (>= 0.88) | 0.92-1
libfontconfig1          (>= 2.8.0) | 2.8.0-2.2
libfreetype6            (>= 2.2.1) | 2.4.4-1
libgdk-pixbuf2.0-0     (>= 2.22.0) | 2.23.3-3
libglib2.0-0           (>= 2.24.0) | 2.28.6-1
libgtk2.0-0            (>= 2.24.0) | 2.24.4-3
libpango1.0-0          (>= 1.14.0) | 1.28.3-6
libperl5.12            (>= 5.12.3) | 5.12.3-6
libsexy2                (>= 0.1.8) | 0.1.11-2+b1
libssl1.0.0             (>= 1.0.0) | 1.0.0d-2
libx11-6                           | 2:1.4.3-1
libxml2                (>= 2.6.27) | 2.7.8.dfsg-2+b1
xchat-common           (= 2.8.8-3) | 2.8.8-3


Recommends          (Version) | Installed
=============================-+-===========
libpython2.6         (>= 2.6) | 2.6.6-10
tcl8.5             (>= 8.5.0) | 8.5.9-2
esound-clients                | 
 OR alsa-utils                | 1.0.23-5+b1
libnotify1                    | 0.5.0-2
 OR libnotify-bin             | 0.5.0-2
xdg-utils                     | 1.1.0~rc1-2


Package's Suggests field is empty.




--- a/plugins/python/python.c	2010-05-15 23:28:18.000000000 -0500
+++ b/plugins/python/python.c	2011-05-12 16:34:32.000000000 -0500
@@ -217,16 +217,17 @@ typedef struct {
 	void *data; /* A handle, when type == HOOK_XCHAT */
 } Hook;
 
 
 /* ===================================================================== */
 /* Function declarations */
 
 static PyObject *Util_BuildList(char *word[]);
+static PyObject *Util_BuildEOLList(char *word[]);
 static void Util_Autoload();
 static char *Util_Expand(char *filename);
 
 static int Callback_Command(char *word[], char *word_eol[], void *userdata);
 static int Callback_Print(char *word[], void *userdata);
 static int Callback_Timer(void *userdata);
 static int Callback_ThreadTimer(void *userdata);
 
@@ -325,33 +326,82 @@ Copyright (c) 2002-2003  Gustavo Niemeye
 
 /* ===================================================================== */
 /* Utility functions */
 
 static PyObject *
 Util_BuildList(char *word[])
 {
 	PyObject *list;
-	int listsize = 0;
+	int listsize = 31;
 	int i;
-	while (word[listsize] && word[listsize][0])
-		listsize++;
+	/* Find the last valid array member; there may be intermediate NULL
+	 * members that would otherwise cause us to drop some members. */
+	while (listsize > 0 &&
+	      (word[listsize] == NULL || word[listsize][0] == 0))
+		listsize--;
+
+	list = PyList_New(listsize);
+	if (list == NULL) {
+                PyErr_Print();
+		return NULL;
+	}
+	for (i = 1; i <= listsize; i++) {
+		PyObject *o;
+		if (word[i] == NULL) {
+			o = Py_None;
+		} else {
+			o = PyString_FromString(word[i]);
+		}
+		if (o == NULL) {
+			Py_DECREF(list);
+			PyErr_Print();
+			return NULL;
+		}
+		PyList_SetItem(list, i - 1, o);
+	}
+	return list;
+}
+
+static PyObject *
+Util_BuildEOLList(char *word[])
+{
+	PyObject *list;
+	int listsize = 31;
+	int i;
+
+	/* Find the last valid array member; there may be intermediate NULL
+	 * members that would otherwise cause us to drop some members. */
+	while (listsize > 0 && (word[listsize] == NULL || word[listsize][0] == 0))
+		listsize--;
+
 	list = PyList_New(listsize);
 	if (list == NULL) {
                 PyErr_Print();
 		return NULL;
 	}
-	for (i = 0; i != listsize; i++) {
-		PyObject *o = PyString_FromString(word[i]);
+
+	PyObject *accum = PyString_FromFormat("%s", word[listsize]);
+	PyList_SetItem(list, listsize - 1, accum);
+	for (i = listsize - 1; i > 0; i--) {
+		PyObject *o;
+		if (word[i] == NULL) {
+			o = accum;
+		} else {
+			o = PyString_FromFormat("%s %s",
+			   			word[i],
+			   			PyString_AsString(accum));
+		}
 		if (o == NULL) {
 			Py_DECREF(list);
 			PyErr_Print();
 			return NULL;
 		}
-		PyList_SetItem(list, i, o);
+		accum = o;
+		PyList_SetItem(list, i - 1, o);
 	}
 	return list;
 }
 
 static void
 Util_Autoload_from (const char *dir_name)
 {
 #ifndef PATH_MAX
@@ -453,22 +503,22 @@ Callback_Command(char *word[], char *wor
 {
 	Hook *hook = (Hook *) userdata;
 	PyObject *retobj;
 	PyObject *word_list, *word_eol_list;
 	int ret = 0;
 
 	BEGIN_PLUGIN(hook->plugin);
 
-	word_list = Util_BuildList(word+1);
+	word_list = Util_BuildList(word);
 	if (word_list == NULL) {
 		END_PLUGIN(hook->plugin);
 		return 0;
 	}
-	word_eol_list = Util_BuildList(word_eol+1);
+	word_eol_list = Util_BuildList(word_eol);
 	if (word_eol_list == NULL) {
 		Py_DECREF(word_list);
 		END_PLUGIN(hook->plugin);
 		return 0;
 	}
 
 	retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
 				       word_eol_list, hook->userdata);
@@ -494,78 +544,38 @@ Callback_Command(char *word[], char *wor
 
 static int
 Callback_Print(char *word[], void *userdata)
 {
 	Hook *hook = (Hook *) userdata;
 	PyObject *retobj;
 	PyObject *word_list;
 	PyObject *word_eol_list;
-	char **word_eol;
-	char *word_eol_raw;
-	int listsize = 0;
-	int next = 0;
-	int i;
 	int ret = 0;
 
-	/* Cut off the message identifier. */
-	word += 1;
-
-	/* XChat doesn't provide a word_eol for print events, so we
-	 * build our own here. */
-	while (word[listsize] && word[listsize][0])
-		listsize++;
-	word_eol = (char **) g_malloc(sizeof(char*)*(listsize+1));
-	if (word_eol == NULL) {
-		xchat_print(ph, "Not enough memory to alloc word_eol "
-				"for python plugin callback.");
-		return 0;
-	}
-	/* First build a word clone, but NULL terminated. */
-	memcpy(word_eol, word, listsize*sizeof(char*));
-	word_eol[listsize] = NULL;
-	/* Then join it. */
-	word_eol_raw = g_strjoinv(" ", word_eol);
-	if (word_eol_raw == NULL) {
-		xchat_print(ph, "Not enough memory to alloc word_eol_raw "
-				"for python plugin callback.");
-		return 0;
-	}
-	/* And rebuild the real word_eol. */
-	for (i = 0; i != listsize; i++) {
-		word_eol[i] = word_eol_raw+next;
-		next += strlen(word[i])+1;
-	}
-	word_eol[i] = "";
-
 	BEGIN_PLUGIN(hook->plugin);
 
 	word_list = Util_BuildList(word);
 	if (word_list == NULL) {
-		g_free(word_eol_raw);
-		g_free(word_eol);
 		END_PLUGIN(hook->plugin);
 		return 0;
 	}
-	word_eol_list = Util_BuildList(word_eol);
+
+	word_eol_list = Util_BuildEOLList(word);
 	if (word_eol_list == NULL) {
-		g_free(word_eol_raw);
-		g_free(word_eol);
 		Py_DECREF(word_list);
 		END_PLUGIN(hook->plugin);
 		return 0;
 	}
 
 	retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
 				       word_eol_list, hook->userdata);
 	Py_DECREF(word_list);
 	Py_DECREF(word_eol_list);
 
-	g_free(word_eol_raw);
-	g_free(word_eol);
 	if (retobj == Py_None) {
 		ret = XCHAT_EAT_NONE;
 		Py_DECREF(retobj);
 	} else if (retobj) {
 		ret = PyInt_AsLong(retobj);
 		Py_DECREF(retobj);
 	} else {
 		PyErr_Print();

Reply via email to