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();