summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorChristopher Davis <loafier@gmail.com>2006-08-12 22:16:53 +0000
committerChristopher Davis <loafier@gmail.com>2006-08-12 22:16:53 +0000
commit3a028090359e5d5d24ccbfc11d9b6ff5681aab4f (patch)
tree0cfb8ec1eb8a49366fc663bef00bf4dfb1f7c307 /src
parentf13ea25509e932d426ebd69d90368fe9b1d4c1ab (diff)
downloadirssi-python-3a028090359e5d5d24ccbfc11d9b6ff5681aab4f.tar.gz
irssi-python-3a028090359e5d5d24ccbfc11d9b6ff5681aab4f.tar.xz
irssi-python-3a028090359e5d5d24ccbfc11d9b6ff5681aab4f.zip
directory structure change
git-svn-id: http://svn.irssi.org/repos/irssi-python@4312 dbcabf3a-b0e7-0310-adc4-f8d773084564
Diffstat (limited to 'src')
-rw-r--r--src/Makefile35
-rw-r--r--src/constants.awk20
-rw-r--r--src/constants.txt45
-rw-r--r--src/irssi.py31
-rw-r--r--src/irssi_startup.py202
-rw-r--r--src/objects/Makefile28
-rw-r--r--src/objects/ban-object.c155
-rw-r--r--src/objects/ban-object.h18
-rw-r--r--src/objects/base-objects.c235
-rw-r--r--src/objects/base-objects.h105
-rw-r--r--src/objects/channel-object.c379
-rw-r--r--src/objects/channel-object.h22
-rw-r--r--src/objects/chatnet-object.c182
-rw-r--r--src/objects/chatnet-object.h22
-rw-r--r--src/objects/command-object.c145
-rw-r--r--src/objects/command-object.h18
-rw-r--r--src/objects/connect-object.c198
-rw-r--r--src/objects/connect-object.h22
-rw-r--r--src/objects/dcc-chat-object.c137
-rw-r--r--src/objects/dcc-chat-object.h21
-rw-r--r--src/objects/dcc-get-object.c138
-rw-r--r--src/objects/dcc-get-object.h18
-rw-r--r--src/objects/dcc-object.c347
-rw-r--r--src/objects/dcc-object.h24
-rw-r--r--src/objects/dcc-send-object.c138
-rw-r--r--src/objects/dcc-send-object.h18
-rw-r--r--src/objects/factory.c326
-rw-r--r--src/objects/factory.h58
-rw-r--r--src/objects/ignore-object.c284
-rw-r--r--src/objects/ignore-object.h21
-rw-r--r--src/objects/irc-channel-object.c173
-rw-r--r--src/objects/irc-channel-object.h21
-rw-r--r--src/objects/irc-connect-object.c86
-rw-r--r--src/objects/irc-connect-object.h21
-rw-r--r--src/objects/irc-server-object.c491
-rw-r--r--src/objects/irc-server-object.h21
-rw-r--r--src/objects/log-object.c477
-rw-r--r--src/objects/log-object.h21
-rw-r--r--src/objects/logitem-object.c156
-rw-r--r--src/objects/logitem-object.h21
-rw-r--r--src/objects/main-window-object.c199
-rw-r--r--src/objects/main-window-object.h20
-rw-r--r--src/objects/netsplit-channel-object.c170
-rw-r--r--src/objects/netsplit-channel-object.h21
-rw-r--r--src/objects/netsplit-object.c184
-rw-r--r--src/objects/netsplit-object.h19
-rw-r--r--src/objects/netsplit-server-object.c157
-rw-r--r--src/objects/netsplit-server-object.h18
-rw-r--r--src/objects/nick-object.c237
-rw-r--r--src/objects/nick-object.h22
-rw-r--r--src/objects/notifylist-object.c221
-rw-r--r--src/objects/notifylist-object.h18
-rw-r--r--src/objects/process-object.c222
-rw-r--r--src/objects/process-object.h22
-rw-r--r--src/objects/pyscript-object.c801
-rw-r--r--src/objects/pyscript-object.h33
-rw-r--r--src/objects/query-object.c172
-rw-r--r--src/objects/query-object.h21
-rw-r--r--src/objects/rawlog-object.c323
-rw-r--r--src/objects/rawlog-object.h22
-rw-r--r--src/objects/reconnect-object.c151
-rw-r--r--src/objects/reconnect-object.h20
-rw-r--r--src/objects/server-object.c807
-rw-r--r--src/objects/server-object.h28
-rw-r--r--src/objects/statusbar-item-object.c253
-rw-r--r--src/objects/statusbar-item-object.h22
-rw-r--r--src/objects/textdest-object.c285
-rw-r--r--src/objects/textdest-object.h24
-rw-r--r--src/objects/theme-object.c195
-rw-r--r--src/objects/theme-object.h18
-rw-r--r--src/objects/window-item-object.c334
-rw-r--r--src/objects/window-item-object.h26
-rw-r--r--src/objects/window-object.c678
-rw-r--r--src/objects/window-object.h21
-rw-r--r--src/pyconstants.h7
-rw-r--r--src/pycore.c160
-rw-r--r--src/pycore.h7
-rw-r--r--src/pyirssi.h32
-rw-r--r--src/pyirssi_irc.h20
-rw-r--r--src/pyloader.c358
-rw-r--r--src/pyloader.h23
-rw-r--r--src/pymodule.c1909
-rw-r--r--src/pymodule.h11
-rw-r--r--src/pysigmap.h188
-rw-r--r--src/pysignals.c805
-rw-r--r--src/pysignals.h44
-rw-r--r--src/pysource.c130
-rw-r--r--src/pysource.h10
-rw-r--r--src/pystatusbar.c132
-rw-r--r--src/pystatusbar.h13
-rw-r--r--src/pythemes.c287
-rw-r--r--src/pythemes.h13
-rw-r--r--src/pyutils.c81
-rw-r--r--src/pyutils.h12
-rw-r--r--src/sig2code.awk61
-rw-r--r--src/sig2code.txt44
96 files changed, 14991 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..123e0ed
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,35 @@
+CC = gcc
+
+PYTHON = /usr/include/python2.4
+IRSSI = /home/chrisd/irssi-0.8.10
+
+CFLAGS = -fpic -ggdb -Wall -I$(PYTHON) -I$(IRSSI) -I$(IRSSI)/src \
+-I$(IRSSI)/src/fe-common/core -I$(IRSSI)/src/core -I$(IRSSI)/src/fe-text \
+-I$(IRSSI)/src/irc -I$(IRSSI)/src/irc/core -I$(IRSSI)/src/irc/dcc \
+-I$(IRSSI)/src/irc/notifylist -I.. -I. -Iobjects \
+`pkg-config glib-2.0 --cflags`
+
+LDFLAGS = -fpic /usr/lib/libpython2.4.so
+
+OBJ = pycore.o pyutils.o pymodule.o pyloader.o pysignals.o pysource.o \
+pythemes.o pystatusbar.o pyconstants.o
+
+pyirssi: pyobjects.a $(OBJ)
+ $(CC) -shared -o libirssi_python.so $(OBJ) objects/pyobjects.a $(LDFLAGS)
+
+pyobjects.a:
+ cd objects/ && make
+
+%.o: %.c
+ $(CC) -c $< $(CFLAGS)
+
+signalmap:
+ awk -f sig2code.awk ~/irssi-0.8.10/docs/signals.txt > pysigmap.h
+
+constants:
+ awk -f constants.awk constants.txt > pyconstants.c
+
+clean:
+ rm -f *.o *.so
+ cd objects/ && make clean
+
diff --git a/src/constants.awk b/src/constants.awk
new file mode 100644
index 0000000..3aa5034
--- /dev/null
+++ b/src/constants.awk
@@ -0,0 +1,20 @@
+BEGIN {
+ print "#include \"pymodule.h\"";
+ print "#include \"pyirssi_irc.h\"";
+ print
+ print "void pyconstants_init(void)"
+ print "{"
+}
+
+{
+ if (NF >= 2)
+ constant = $2;
+ else
+ constant = $1;
+
+ printf(" PyModule_AddIntConstant(py_module, \"%s\", %s);\n", $1, constant);
+}
+
+END {
+ print "}"
+}
diff --git a/src/constants.txt b/src/constants.txt
new file mode 100644
index 0000000..a3b8917
--- /dev/null
+++ b/src/constants.txt
@@ -0,0 +1,45 @@
+IO_IN G_IO_IN
+IO_OUT G_IO_OUT
+IO_PRI G_IO_PRI
+IO_ERR G_IO_ERR
+IO_HUP G_IO_HUP
+IRSSI_GUI_GNOME
+IRSSI_GUI_GTK
+IRSSI_GUI_KDE
+IRSSI_GUI_NONE
+IRSSI_GUI_QT
+IRSSI_GUI_TEXT
+IRC_MASK_DOMAIN
+IRC_MASK_HOST
+IRC_MASK_NICK
+IRC_MASK_USER
+MSGLEVEL_ACTIONS
+MSGLEVEL_ALL
+MSGLEVEL_CLIENTCRAP
+MSGLEVEL_CLIENTERROR
+MSGLEVEL_CLIENTNOTICE
+MSGLEVEL_CRAP
+MSGLEVEL_CTCPS
+MSGLEVEL_DCC
+MSGLEVEL_DCCMSGS
+MSGLEVEL_HILIGHT
+MSGLEVEL_INVITES
+MSGLEVEL_JOINS
+MSGLEVEL_KICKS
+MSGLEVEL_LASTLOG
+MSGLEVEL_MODES
+MSGLEVEL_MSGS
+MSGLEVEL_NEVER
+MSGLEVEL_NICKS
+MSGLEVEL_NO_ACT
+MSGLEVEL_NOHILIGHT
+MSGLEVEL_NOTICES
+MSGLEVEL_PARTS
+MSGLEVEL_PUBLIC
+MSGLEVEL_QUITS
+MSGLEVEL_SNOTES
+MSGLEVEL_TOPICS
+MSGLEVEL_WALLOPS
+SIGNAL_PRIORITY_DEFAULT
+SIGNAL_PRIORITY_HIGH
+SIGNAL_PRIORITY_LOW
diff --git a/src/irssi.py b/src/irssi.py
new file mode 100644
index 0000000..3f193ad
--- /dev/null
+++ b/src/irssi.py
@@ -0,0 +1,31 @@
+"""
+"""
+
+import sys
+import _irssi
+from _irssi import *
+
+def command_bind(*args, **kwargs):
+ """ see Script.command_bind """
+ get_script().command_bind(*args, **kwargs)
+
+def command_unbind(*args, **kwargs):
+ """ see Script.command_unbind """
+ get_script().command_unbind(*args, **kwargs)
+
+def signal_add(*args, **kwargs):
+ """ see Script.signal_add """
+ get_script().signal_add(*args, **kwargs)
+
+def signal_remove(*args, **kwargs):
+ """ see Script.signal_remove """
+ get_script().signal_remove(*args, **kwargs)
+
+def timeout_add(*args, **kwargs):
+ """ see Script.timeout_add """
+ get_script().timeout_add(*args, **kwargs)
+
+def io_add_watch(*args, **kwargs):
+ """ see Script.io_add_watch """
+ get_script().io_add_watch(*args, **kwargs)
+
diff --git a/src/irssi_startup.py b/src/irssi_startup.py
new file mode 100644
index 0000000..ee8dbdd
--- /dev/null
+++ b/src/irssi_startup.py
@@ -0,0 +1,202 @@
+import sys, imp, __builtin__
+import _irssi
+
+class Output:
+ def __init__(self, level):
+ self.level = level
+ self.buf = []
+ def write(self, text):
+ if not text:
+ return
+ self.buf.append(text)
+ if '\n' == text[-1]:
+ text = ''.join(self.buf)[:-1]
+ for line in text.split('\\n'):
+ _irssi.active_win().prnt(line, self.level)
+ self.buf = []
+
+#XXX: hardcode
+#try:
+sys.stdout = Output(level = 0x0080000)
+sys.stderr = Output(level = 0x0100000)
+#except Exception, e:
+# print 'Cant set output', e
+# sys.stdout = sys.__stdout__
+# sys.stderr = sys.__stderr__
+
+"""
+#Import stuff modified from <Python>/Demo/imputil/knee.py
+#If a script instance is available, it's module dictionary
+#is used in place of sys.modules
+#XXX: needs testing
+#XXX: copyright?
+
+# Save the original hooks
+original_import = __builtin__.__import__
+original_reload = __builtin__.reload
+
+# Replacement for __import__()
+def import_hook(name, globals=None, locals=None, fromlist=None):
+ print 'LOADING', name
+ if name == 'sys':
+ return sys
+ #if name == '_irssi':
+ # return _irssi
+
+ try:
+ script = _irssi.get_script()
+ except RuntimeError:
+ print 'ORIG IMPORT', name
+ return original_import(name, globals, locals, fromlist)
+
+ parent = determine_parent(globals, script)
+ q, tail = find_head_package(parent, name, script)
+ m = load_tail(q, tail, script)
+ if not fromlist:
+ return q
+
+ if hasattr(m, "__path__"):
+ ensure_fromlist(m, fromlist, script)
+
+ recur -= 1
+ return m
+
+def determine_parent(globals, script):
+ if not globals or not globals.has_key("__name__"):
+ print 'DP no __name__ in globals'
+ return None
+ pname = globals['__name__']
+ print 'DP pname', pname
+ if globals.has_key("__path__"):
+ parent = script.modules[pname]
+ assert globals is parent.__dict__
+ return parent
+ if '.' in pname:
+ i = pname.rfind('.')
+ pname = pname[:i]
+ parent = script.modules[pname]
+ assert parent.__name__ == pname
+ return parent
+ return None
+
+def find_head_package(parent, name, script):
+ if '.' in name:
+ i = name.find('.')
+ head = name[:i]
+ tail = name[i+1:]
+ else:
+ head = name
+ tail = ""
+ if parent:
+ qname = "%s.%s" % (parent.__name__, head)
+ else:
+ qname = head
+ q = import_module(head, qname, parent, script)
+ if q:
+ return q, tail
+ else:
+ print 'FHP (1) no q module for', name
+
+ if parent:
+ qname = head
+ parent = None
+ q = import_module(head, qname, parent, script)
+ if q:
+ return q, tail
+ else:
+ print 'FHP (2) no q module for', name
+
+ raise ImportError, "No module named " + qname
+
+def load_tail(q, tail, script):
+ m = q
+ while tail:
+ i = tail.find('.')
+ if i < 0: i = len(tail)
+ head, tail = tail[:i], tail[i+1:]
+ mname = "%s.%s" % (m.__name__, head)
+ m = import_module(head, mname, m, script)
+ if not m:
+ raise ImportError, "No module named " + mname
+ return m
+
+def ensure_fromlist(m, fromlist, script, recursive=0):
+ for sub in fromlist:
+ if sub == "*":
+ if not recursive:
+ try:
+ all = m.__all__
+ except AttributeError:
+ pass
+ else:
+ assert recursive == 0
+ ensure_fromlist(m, all, script, 1)
+ continue
+ if sub != "*" and not hasattr(m, sub):
+ subname = "%s.%s" % (m.__name__, sub)
+ submod = import_module(sub, subname, m, script)
+ if not submod:
+ raise ImportError, "No module named " + subname
+
+def import_module(partname, fqname, parent, script):
+ try:
+ m = script.modules[fqname]
+ #if hasattr(m, '_script'):
+ # assert m._script == script
+
+ _irssi.prnt('LOADING CACHED %s script -> %s' % (fqname, repr(script)))
+
+ return m
+ except KeyError:
+ print 'IM no cached moddule for', fqname
+ pass
+
+ try:
+ fp, pathname, stuff = imp.find_module(partname,
+ parent and parent.__path__)
+ except ImportError, e:
+ print 'IM import error', e
+ return None
+
+ try:
+ m = imp.load_module(fqname, fp, pathname, stuff)
+ finally:
+ if fp: fp.close()
+ if parent:
+ setattr(parent, partname, m)
+
+ #don't load script into builtins, extensions, or this wrapper
+ if hasattr(m, '__file__') and fqname != 'irssi':
+ m._script = script
+ script.modules[fqname] = m
+
+ #if hasattr(m, '__file__') and fqname not in ('__builtin__', 'sys', '__main__', '_irssi'):
+ # del sys.modules[fqname]
+
+ _irssi.prnt('GOT -> %s, SCRIPT -> %s' % (m, repr(script)))
+
+ return m
+
+
+# Replacement for reload()
+def reload_hook(module):
+ _irssi.prnt('reloading ' + repr(module))
+ try:
+ script = _irssi.get_script()
+ except RuntimeError:
+ return original_reload(module)
+
+ name = module.__name__
+ if '.' not in name:
+ return import_module(name, name, None, script)
+ i = name.rfind('.')
+ pname = name[:i]
+ parent = script.modules[pname]
+ return import_module(name[i+1:], name, parent, script)
+
+
+# Now install our hooks
+__builtin__.__import__ = import_hook
+__builtin__.reload = reload_hook
+"""
+
diff --git a/src/objects/Makefile b/src/objects/Makefile
new file mode 100644
index 0000000..6cc56fb
--- /dev/null
+++ b/src/objects/Makefile
@@ -0,0 +1,28 @@
+CC = gcc
+
+PYTHON = /usr/include/python2.4
+IRSSI = /home/chrisd/irssi-0.8.10
+CFLAGS = -fpic -ggdb -Wall -I$(PYTHON) -I$(IRSSI) -I$(IRSSI)/src \
+-I$(IRSSI)/src/fe-common/core -I$(IRSSI)/src/core -I$(IRSSI)/src/fe-text \
+-I$(IRSSI)/src/irc -I$(IRSSI)/src/irc/core -I$(IRSSI)/src/irc/dcc \
+-I$(IRSSI)/src/irc/notifylist -I.. \
+`pkg-config glib-2.0 --cflags`
+
+OBJ = pyscript-object.o base-objects.o window-item-object.o channel-object.o \
+query-object.o server-object.o connect-object.o irc-server-object.o \
+irc-connect-object.o irc-channel-object.o ban-object.o nick-object.o \
+chatnet-object.o reconnect-object.o window-object.o textdest-object.o \
+rawlog-object.o log-object.o logitem-object.o ignore-object.o \
+dcc-object.o dcc-chat-object.o dcc-get-object.o dcc-send-object.o \
+netsplit-object.o netsplit-server-object.o netsplit-channel-object.o \
+notifylist-object.o process-object.o command-object.o theme-object.o \
+statusbar-item-object.o main-window-object.o factory.o
+
+pyobjects.a: $(OBJ)
+ ar r pyobjects.a $(OBJ)
+
+%.o: %.c
+ $(CC) -c $< $(CFLAGS)
+
+clean:
+ rm -f *.o *.so *.a
diff --git a/src/objects/ban-object.c b/src/objects/ban-object.c
new file mode 100644
index 0000000..908bae2
--- /dev/null
+++ b/src/objects/ban-object.c
@@ -0,0 +1,155 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "ban-object.h"
+#include "pycore.h"
+
+/* monitor "ban remove" signal */
+static void ban_cleanup(CHANNEL_REC *chan, BAN_REC *ban)
+{
+ PyBan *pyban = signal_get_user_data();
+
+ if (ban == pyban->data)
+ {
+ pyban->data = NULL;
+ pyban->cleanup_installed = 0;
+ signal_remove_data("ban remove", ban_cleanup, pyban);
+ }
+}
+
+static void PyBan_dealloc(PyBan *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("ban remove", ban_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyBan_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyBan *self;
+
+ self = (PyBan *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+PyDoc_STRVAR(PyBan_ban_doc,
+ "The ban"
+);
+static PyObject *PyBan_ban_get(PyBan *self, void *closure)
+{
+ BAN_REC *data = self->data;
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(data->ban);
+}
+
+PyDoc_STRVAR(PyBan_setby_doc,
+ "Nick of who set the ban"
+);
+static PyObject *PyBan_setby_get(PyBan *self, void *closure)
+{
+ BAN_REC *data = self->data;
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(data->setby);
+}
+
+PyDoc_STRVAR(PyBan_time_doc,
+ "Timestamp when ban was set"
+);
+static PyObject *PyBan_time_get(PyBan *self, void *closure)
+{
+ BAN_REC *data = self->data;
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(data->time);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyBan_getseters[] = {
+ {"ban", (getter)PyBan_ban_get, NULL,
+ PyBan_ban_doc, NULL},
+ {"setby", (getter)PyBan_setby_get, NULL,
+ PyBan_setby_doc, NULL},
+ {"time", (getter)PyBan_time_get, NULL,
+ PyBan_time_doc, NULL},
+ {NULL}
+};
+
+/* Methods for object */
+static PyMethodDef PyBan_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyBanType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Ban", /*tp_name*/
+ sizeof(PyBan), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyBan_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyBan objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyBan_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyBan_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyBan_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pyban_new(void *ban)
+{
+ PyBan *pyban;
+
+ pyban = py_inst(PyBan, PyBanType);
+ if (!pyban)
+ return NULL;
+
+ pyban->data = ban;
+ pyban->cleanup_installed = 1;
+ signal_add_last_data("ban remove", ban_cleanup, pyban);
+
+ return (PyObject *)pyban;
+}
+
+int ban_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyBanType) < 0)
+ return 0;
+
+ Py_INCREF(&PyBanType);
+ PyModule_AddObject(py_module, "Ban", (PyObject *)&PyBanType);
+
+ return 1;
+}
diff --git a/src/objects/ban-object.h b/src/objects/ban-object.h
new file mode 100644
index 0000000..59ec9e7
--- /dev/null
+++ b/src/objects/ban-object.h
@@ -0,0 +1,18 @@
+#ifndef _BAN_OBJECT_H_
+#define _BAN_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+} PyBan;
+
+extern PyTypeObject PyBanType;
+
+int ban_object_init(void);
+PyObject *pyban_new(void *ban);
+#define pyban_check(op) PyObject_TypeCheck(op, &PyBanType)
+
+#endif
diff --git a/src/objects/base-objects.c b/src/objects/base-objects.c
new file mode 100644
index 0000000..42f5cac
--- /dev/null
+++ b/src/objects/base-objects.c
@@ -0,0 +1,235 @@
+#include <Python.h>
+#include "structmember.h"
+#include "pymodule.h"
+#include "base-objects.h"
+#include "pyirssi.h"
+
+/* This is the base type for Irssi objects with a type id. The user can find
+ * the type name, type id, and check if the object is wrapping a valid Irssi
+ * record.
+ */
+
+static void PyIrssiBase_dealloc(PyIrssiBase *self)
+{
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyIrssiBase_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyIrssiBase *self;
+
+ self = (PyIrssiBase *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyIrssiBase_type_id_doc,
+ "Irssi's type id for object"
+);
+static PyObject *PyIrssiBase_type_id_get(PyIrssiBase *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->type);
+}
+
+PyDoc_STRVAR(PyIrssiBase_type_doc,
+ "Irssi's name for object"
+);
+static PyObject *PyIrssiBase_type_get(PyIrssiBase *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->base_name);
+}
+
+PyDoc_STRVAR(PyIrssiBase_valid_doc,
+ "True if the object is valid"
+);
+static PyObject *PyIrssiBase_valid_get(PyIrssiBase *self, void *closure)
+{
+ if (self->data != NULL)
+ Py_RETURN_TRUE;
+
+ Py_RETURN_FALSE;
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyIrssiBase_getseters[] = {
+ {"type_id", (getter)PyIrssiBase_type_id_get, NULL,
+ PyIrssiBase_type_id_doc, NULL},
+ {"type", (getter)PyIrssiBase_type_get, NULL,
+ PyIrssiBase_type_doc, NULL},
+ {"valid", (getter)PyIrssiBase_valid_get, NULL,
+ PyIrssiBase_valid_doc, NULL},
+ {NULL}
+};
+
+/* Methods for object */
+static PyMethodDef PyIrssiBase_methods[] = {
+ {NULL}
+};
+
+PyTypeObject PyIrssiBaseType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "IrssiBase", /*tp_name*/
+ sizeof(PyIrssiBase), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyIrssiBase_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyIrssiBase objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyIrssiBase_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyIrssiBase_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyIrssiBase_new, /* tp_new */
+};
+
+
+/* IrssiChatBase is a base type for any object with a chat type. The user
+ can find the chat type string name with the chat_type member or
+ the type id with the chat_type_id member. It inherits from IrssiBase
+ so the type, valid, and type_id members are visible to the user, too */
+
+static void PyIrssiChatBase_dealloc(PyIrssiChatBase *self)
+{
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyIrssiChatBase_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyIrssiChatBase *self;
+
+ self = (PyIrssiChatBase *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyIrssiChatBase_chat_type_id_doc,
+ "Chat Type id (int)"
+);
+static PyObject *PyIrssiChatBase_chat_type_id_get(PyIrssiChatBase *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->chat_type);
+}
+
+PyDoc_STRVAR(PyIrssiChatBase_chat_type_doc,
+ "Chat name (str)"
+);
+static PyObject *PyIrssiChatBase_chat_type_get(PyIrssiChatBase *self, void *closure)
+{
+ CHAT_PROTOCOL_REC *rec;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ rec = chat_protocol_find_id(self->data->chat_type);
+ if (rec)
+ RET_AS_STRING_OR_NONE(rec->name);
+ else
+ Py_RETURN_NONE;
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyIrssiChatBase_getseters[] = {
+ {"chat_type_id", (getter)PyIrssiChatBase_chat_type_id_get, NULL,
+ PyIrssiChatBase_chat_type_id_doc, NULL},
+ {"chat_type", (getter)PyIrssiChatBase_chat_type_get, NULL,
+ PyIrssiChatBase_chat_type_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+static PyMethodDef PyIrssiChatBase_methods[] = {
+ {NULL}
+};
+
+PyTypeObject PyIrssiChatBaseType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "IrssiChatBase", /*tp_name*/
+ sizeof(PyIrssiChatBase), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyIrssiChatBase_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyIrssiChatBase objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyIrssiChatBase_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyIrssiChatBase_getseters, /* tp_getset */
+ &PyIrssiBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyIrssiChatBase_new, /* tp_new */
+};
+
+int base_objects_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyIrssiBaseType) < 0)
+ return 0;
+ if (PyType_Ready(&PyIrssiChatBaseType) < 0)
+ return 0;
+
+ Py_INCREF(&PyIrssiBaseType);
+ Py_INCREF(&PyIrssiChatBaseType);
+ PyModule_AddObject(py_module, "IrssiBase", (PyObject *)&PyIrssiBaseType);
+ PyModule_AddObject(py_module, "IrssiChatBase", (PyObject *)&PyIrssiChatBaseType);
+
+ return 1;
+}
diff --git a/src/objects/base-objects.h b/src/objects/base-objects.h
new file mode 100644
index 0000000..5351ead
--- /dev/null
+++ b/src/objects/base-objects.h
@@ -0,0 +1,105 @@
+#ifndef _BASE_OBJECTS_H_
+#define _BASE_OBJECTS_H_
+
+#include <Python.h>
+
+/* data is a pointer to the underlying Irssi record */
+/* base_name is the type name of the object returned from the type member
+ it can be SERVER, CHANNEL, QUERY, etc. Note: base_name isn't freed, so
+ it cannot point to heap memory */
+/* cleanup_installed: 1 = a cleanup signal handler is installed, 0 = not installed */
+#define PyIrssi_HEAD(type) \
+ PyObject_HEAD \
+ type *data; \
+ const char *base_name; \
+ int cleanup_installed;
+
+/* for uninheritable objects without a type id (Ban, Log, etc) */
+#define PyIrssiFinal_HEAD(type) \
+ PyObject_HEAD \
+ type *data; \
+ int cleanup_installed;
+
+/* to access data from any irssi object */
+typedef struct
+{
+ PyObject_HEAD
+ void *data;
+} PyIrssiObject;
+
+#define DATA(obj) (obj? ((PyIrssiObject *)obj)->data : NULL)
+
+/* base for classes with a type */
+typedef struct
+{
+ int type;
+} IRSSI_BASE_REC;
+
+typedef struct
+{
+ PyIrssi_HEAD(IRSSI_BASE_REC)
+} PyIrssiBase;
+
+
+/* base for classes with type and a chat type */
+typedef struct
+{
+ int type;
+ int chat_type;
+} IRSSI_CHAT_REC;
+
+typedef struct
+{
+ PyIrssi_HEAD(IRSSI_CHAT_REC)
+} PyIrssiChatBase;
+
+extern PyTypeObject PyIrssiBaseType;
+extern PyTypeObject PyIrssiChatBaseType;
+
+#define pybase_check(op) PyObject_TypeCheck(op, &PyIrssiBaseType)
+#define pychatbase_check(op) PyObject_TypeCheck(op, &PyIrssiChatBaseType)
+#define py_instp(tp, to) ((tp *) (to)->tp_alloc(to, 0))
+#define py_inst(tp, to) py_instp(tp, &to)
+
+int base_objects_init(void);
+
+#define RET_NULL_IF_INVALID(data) \
+ if (data == NULL) \
+ return PyErr_Format(PyExc_RuntimeError, "wrapped object is invalid")
+
+#define RET_N1_IF_INVALID(data) \
+do { \
+ if (data == NULL) \
+ { \
+ PyErr_Format(PyExc_RuntimeError, "wrapped object is invalid"); \
+ return -1; \
+ } \
+} while (0)
+
+#define RET_AS_STRING_OR_NONE(str) \
+do { \
+ if (str) \
+ return PyString_FromString(str); \
+ else \
+ { \
+ Py_INCREF(Py_None); \
+ return Py_None; \
+ } \
+} while (0)
+
+
+#define RET_AS_STRING_OR_EMPTY(str) return PyString_FromString(str? str : "")
+
+#define RET_AS_OBJ_OR_NONE(obj) \
+do { \
+ PyObject *tmp = obj; \
+ if (!tmp) \
+ tmp = Py_None; \
+ Py_INCREF(tmp); \
+ return tmp; \
+} while (0)
+
+#define INVALID_MEMBER(member) \
+ return PyErr_Format(PyExc_RuntimeError, "invalid member id, %d", member)
+
+#endif
diff --git a/src/objects/channel-object.c b/src/objects/channel-object.c
new file mode 100644
index 0000000..fa4edd4
--- /dev/null
+++ b/src/objects/channel-object.c
@@ -0,0 +1,379 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "factory.h"
+#include "channel-object.h"
+#include "pycore.h"
+
+/* monitor "channel destroyed" signal */
+static void chan_cleanup(CHANNEL_REC *chan)
+{
+ PyChannel *pychan = signal_get_user_data();
+
+ if (chan == pychan->data)
+ {
+ pychan->data = NULL;
+ pychan->cleanup_installed = 0;
+ signal_remove_data("channel destroyed", chan_cleanup, pychan);
+ }
+}
+
+static void PyChannel_dealloc(PyChannel *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("channel destroyed", chan_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyChannel_topic_doc,
+ "Channel topic"
+);
+static PyObject *PyChannel_topic_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->topic);
+}
+
+PyDoc_STRVAR(PyChannel_topic_by_doc,
+ "Nick who set the topic"
+);
+static PyObject *PyChannel_topic_by_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->topic_by);
+}
+
+PyDoc_STRVAR(PyChannel_topic_time_doc,
+ "Timestamp when the topic was set"
+);
+static PyObject *PyChannel_topic_time_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromLong(self->data->topic_time);
+}
+
+PyDoc_STRVAR(PyChannel_no_modes_doc,
+ "Channel is modeless"
+);
+static PyObject *PyChannel_no_modes_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->no_modes);
+}
+
+PyDoc_STRVAR(PyChannel_mode_doc,
+ "Channel mode"
+);
+static PyObject *PyChannel_mode_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->mode);
+}
+
+PyDoc_STRVAR(PyChannel_limit_doc,
+ "Max. users in channel (+l mode)"
+);
+static PyObject *PyChannel_limit_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->limit);
+}
+
+PyDoc_STRVAR(PyChannel_key_doc,
+ "Channel key (password)"
+);
+static PyObject *PyChannel_key_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->key);
+}
+
+PyDoc_STRVAR(PyChannel_chanop_doc,
+ "You are channel operator"
+);
+static PyObject *PyChannel_chanop_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->chanop);
+}
+
+PyDoc_STRVAR(PyChannel_names_got_doc,
+ "/NAMES list has been received"
+);
+static PyObject *PyChannel_names_got_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->names_got);
+}
+
+PyDoc_STRVAR(PyChannel_wholist_doc,
+ "/WHO list has been received"
+);
+static PyObject *PyChannel_wholist_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->wholist);
+}
+
+PyDoc_STRVAR(PyChannel_synced_doc,
+ "Channel is fully synchronized"
+);
+static PyObject *PyChannel_synced_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->synced);
+}
+
+PyDoc_STRVAR(PyChannel_joined_doc,
+ "JOIN event for this channel has been received"
+);
+static PyObject *PyChannel_joined_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->joined);
+}
+
+PyDoc_STRVAR(PyChannel_left_doc,
+ "You just left the channel (for 'channel destroyed' event)"
+);
+static PyObject *PyChannel_left_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->left);
+}
+
+PyDoc_STRVAR(PyChannel_kicked_doc,
+ "You were just kicked out of the channel (for 'channel destroyed' event)"
+);
+static PyObject *PyChannel_kicked_get(PyChannel *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->kicked);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyChannel_getseters[] = {
+ {"topic", (getter)PyChannel_topic_get, NULL,
+ PyChannel_topic_doc, NULL},
+ {"topic_by", (getter)PyChannel_topic_by_get, NULL,
+ PyChannel_topic_by_doc, NULL},
+ {"topic_time", (getter)PyChannel_topic_time_get, NULL,
+ PyChannel_topic_time_doc, NULL},
+ {"no_modes", (getter)PyChannel_no_modes_get, NULL,
+ PyChannel_no_modes_doc, NULL},
+ {"mode", (getter)PyChannel_mode_get, NULL,
+ PyChannel_mode_doc, NULL},
+ {"limit", (getter)PyChannel_limit_get, NULL,
+ PyChannel_limit_doc, NULL},
+ {"key", (getter)PyChannel_key_get, NULL,
+ PyChannel_key_doc, NULL},
+ {"chanop", (getter)PyChannel_chanop_get, NULL,
+ PyChannel_chanop_doc, NULL},
+ {"names_got", (getter)PyChannel_names_got_get, NULL,
+ PyChannel_names_got_doc, NULL},
+ {"wholist", (getter)PyChannel_wholist_get, NULL,
+ PyChannel_wholist_doc, NULL},
+ {"synced", (getter)PyChannel_synced_get, NULL,
+ PyChannel_synced_doc, NULL},
+ {"joined", (getter)PyChannel_joined_get, NULL,
+ PyChannel_joined_doc, NULL},
+ {"left", (getter)PyChannel_left_get, NULL,
+ PyChannel_left_doc, NULL},
+ {"kicked", (getter)PyChannel_kicked_get, NULL,
+ PyChannel_kicked_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyChannel_nicks_doc,
+ "nicks() -> list of Nick objects\n"
+ "\n"
+ "Return a list of nicks in the channel.\n"
+);
+static PyObject *PyChannel_nicks(PyChannel *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ return py_irssi_chatlist_new(nicklist_getnicks(self->data), 1);
+}
+
+PyDoc_STRVAR(PyChannel_nicks_find_mask_doc,
+ "nicks_find_mask(mask) -> Nick object or None\n"
+ "\n"
+ "Find nick mask from nicklist, wildcards allowed.\n"
+);
+static PyObject *PyChannel_nicks_find_mask(PyChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"mask", NULL};
+ char *mask = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &mask))
+ return NULL;
+
+ return py_irssi_chat_new(nicklist_find_mask(self->data, mask), 1);
+}
+
+PyDoc_STRVAR(PyChannel_nick_find_doc,
+ "nick_find(nick) -> Nick object or None\n"
+ "\n"
+ "Find nick from nicklist.\n"
+);
+static PyObject *PyChannel_nick_find(PyChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ char *nick = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &nick))
+ return NULL;
+
+ return py_irssi_chat_new(nicklist_find(self->data, nick), 1);
+}
+
+PyDoc_STRVAR(PyChannel_nick_remove_doc,
+ "nick_remove(nick) -> None\n"
+ "\n"
+ "Remove nick from nicklist.\n"
+);
+static PyObject *PyChannel_nick_remove(PyChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ PyObject *nick = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &nick))
+ return NULL;
+
+ if (!pynick_check(nick))
+ return PyErr_Format(PyExc_TypeError, "arg must be nick");
+
+ nicklist_remove(self->data, ((PyNick*)nick)->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyChannel_nick_insert_obj_doc,
+ "nick_insert(nick) -> None\n"
+ "\n"
+ "Insert nick object into nicklist.\n"
+);
+static PyObject *PyChannel_nick_insert_obj(PyChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ PyObject *nick = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &nick))
+ return NULL;
+
+ if (!pynick_check(nick))
+ return PyErr_Format(PyExc_TypeError, "arg must be nick");
+
+ nicklist_insert(self->data, ((PyNick*)nick)->data);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyChannel_methods[] = {
+ {"nicks", (PyCFunction)PyChannel_nicks, METH_NOARGS,
+ PyChannel_nicks_doc},
+ {"nicks_find_mask", (PyCFunction)PyChannel_nicks_find_mask, METH_VARARGS | METH_KEYWORDS,
+ PyChannel_nicks_find_mask_doc},
+ {"nick_find", (PyCFunction)PyChannel_nick_find, METH_VARARGS | METH_KEYWORDS,
+ PyChannel_nick_find_doc},
+ {"nick_remove", (PyCFunction)PyChannel_nick_remove, METH_VARARGS | METH_KEYWORDS,
+ PyChannel_nick_remove_doc},
+ {"nick_insert_obj", (PyCFunction)PyChannel_nick_insert_obj, METH_VARARGS | METH_KEYWORDS,
+ PyChannel_nick_insert_obj_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyChannelType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Channel", /*tp_name*/
+ sizeof(PyChannel), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyChannel_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyChannel objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyChannel_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyChannel_getseters, /* tp_getset */
+ &PyWindowItemType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pychannel_sub_new(void *chan, const char *name, PyTypeObject *type)
+{
+ PyObject *pychan;
+
+ pychan = pywindow_item_sub_new(chan, name, type);
+ if (pychan)
+ {
+ PyChannel *pych = (PyChannel *)pychan;
+ signal_add_last_data("channel destroyed", chan_cleanup, pych);
+ pych->cleanup_installed = 1;
+ }
+
+ return pychan;
+}
+
+PyObject *pychannel_new(void *chan)
+{
+ static const char *name = "CHANNEL";
+ return pychannel_sub_new(chan, name, &PyChannelType);
+}
+
+int channel_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyChannelType) < 0)
+ return 0;
+
+ Py_INCREF(&PyChannelType);
+ PyModule_AddObject(py_module, "Channel", (PyObject *)&PyChannelType);
+
+ return 1;
+}
diff --git a/src/objects/channel-object.h b/src/objects/channel-object.h
new file mode 100644
index 0000000..0858e7b
--- /dev/null
+++ b/src/objects/channel-object.h
@@ -0,0 +1,22 @@
+#ifndef _CHANNEL_OBJECT_H_
+#define _CHANNEL_OBJECT_H_
+
+#include <Python.h>
+#include "window-item-object.h"
+
+/* forward */
+struct _CHANNEL_REC;
+
+typedef struct
+{
+ PyWindowItem_HEAD(struct _CHANNEL_REC)
+} PyChannel;
+
+extern PyTypeObject PyChannelType;
+
+int channel_object_init(void);
+PyObject *pychannel_sub_new(void *chan, const char *name, PyTypeObject *type);
+PyObject *pychannel_new(void *chan);
+#define pychannel_check(op) PyObject_TypeCheck(op, &PyChannelType)
+
+#endif
diff --git a/src/objects/chatnet-object.c b/src/objects/chatnet-object.c
new file mode 100644
index 0000000..c22f0cb
--- /dev/null
+++ b/src/objects/chatnet-object.c
@@ -0,0 +1,182 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "chatnet-object.h"
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+static void chatnet_cleanup(CHATNET_REC *cn)
+{
+ PyChatnet *pycn = signal_get_user_data();
+
+ if (cn == pycn->data)
+ {
+ pycn->data = NULL;
+ pycn->cleanup_installed = 0;
+ signal_remove_data("chatnet destroyed", chatnet_cleanup, pycn);
+ }
+}
+
+static void PyChatnet_dealloc(PyChatnet *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("chatnet destroyed", chatnet_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyChatnet_name_doc,
+ "name of chat network"
+);
+static PyObject *PyChatnet_name_get(PyChatnet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->name);
+}
+
+PyDoc_STRVAR(PyChatnet_nick_doc,
+ "if not empty, nick preferred in this network"
+);
+static PyObject *PyChatnet_nick_get(PyChatnet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->nick);
+}
+
+PyDoc_STRVAR(PyChatnet_username_doc,
+ "if not empty, username preferred in this network"
+);
+static PyObject *PyChatnet_username_get(PyChatnet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->username);
+}
+
+PyDoc_STRVAR(PyChatnet_realname_doc,
+ "if not empty, realname preferred in this network"
+);
+static PyObject *PyChatnet_realname_get(PyChatnet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->realname);
+}
+
+PyDoc_STRVAR(PyChatnet_own_host_doc,
+ "address to use when connecting to this network"
+);
+static PyObject *PyChatnet_own_host_get(PyChatnet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->own_host);
+}
+
+PyDoc_STRVAR(PyChatnet_autosendcmd_doc,
+ "command to send after connecting to this network"
+);
+static PyObject *PyChatnet_autosendcmd_get(PyChatnet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->autosendcmd);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyChatnet_getseters[] = {
+ {"name", (getter)PyChatnet_name_get, NULL,
+ PyChatnet_name_doc, NULL},
+ {"nick", (getter)PyChatnet_nick_get, NULL,
+ PyChatnet_nick_doc, NULL},
+ {"username", (getter)PyChatnet_username_get, NULL,
+ PyChatnet_username_doc, NULL},
+ {"realname", (getter)PyChatnet_realname_get, NULL,
+ PyChatnet_realname_doc, NULL},
+ {"own_host", (getter)PyChatnet_own_host_get, NULL,
+ PyChatnet_own_host_doc, NULL},
+ {"autosendcmd", (getter)PyChatnet_autosendcmd_get, NULL,
+ PyChatnet_autosendcmd_doc, NULL},
+ {NULL}
+};
+
+static PyMethodDef PyChatnet_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyChatnetType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Chatnet", /*tp_name*/
+ sizeof(PyChatnet), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyChatnet_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyChatnet objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyChatnet_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyChatnet_getseters, /* tp_getset */
+ &PyIrssiChatBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* chatnet factory function */
+PyObject *pychatnet_sub_new(void *cn, PyTypeObject *subclass)
+{
+ static const char *name = "CHATNET";
+ PyChatnet *pycn = NULL;
+
+ pycn = py_instp(PyChatnet, subclass);
+ if (!pycn)
+ return NULL;
+
+ pycn->data = cn;
+ pycn->base_name = name;
+ signal_add_last_data("chatnet destroyed", chatnet_cleanup, pycn);
+ pycn->cleanup_installed = 1;
+
+ return (PyObject *)pycn;
+}
+
+PyObject *pychatnet_new(void *cn)
+{
+ return pychatnet_sub_new(cn, &PyChatnetType);
+}
+
+int chatnet_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyChatnetType) < 0)
+ return 0;
+
+ Py_INCREF(&PyChatnetType);
+ PyModule_AddObject(py_module, "Chatnet", (PyObject *)&PyChatnetType);
+
+ return 1;
+}
diff --git a/src/objects/chatnet-object.h b/src/objects/chatnet-object.h
new file mode 100644
index 0000000..8a6d1fc
--- /dev/null
+++ b/src/objects/chatnet-object.h
@@ -0,0 +1,22 @@
+#ifndef _CHATNET_OBJECT_H_
+#define _CHATNET_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _CHATNET_REC;
+
+typedef struct
+{
+ PyIrssi_HEAD(struct _CHATNET_REC)
+} PyChatnet;
+
+extern PyTypeObject PyChatnetType;
+
+int chatnet_object_init(void);
+PyObject *pychatnet_sub_new(void *cn, PyTypeObject *subclass);
+PyObject *pychatnet_new(void *cn);
+#define pychatnet_check(op) PyObject_TypeCheck(op, &PyChatnetType)
+
+#endif
diff --git a/src/objects/command-object.c b/src/objects/command-object.c
new file mode 100644
index 0000000..ad60012
--- /dev/null
+++ b/src/objects/command-object.c
@@ -0,0 +1,145 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "command-object.h"
+#include "pycore.h"
+
+#define COMMAND(cmd) ((COMMAND_REC *)cmd)
+
+/* monitor "commandlist remove" signal */
+static void command_cleanup(COMMAND_REC *command)
+{
+ PyCommand *pycommand = signal_get_user_data();
+
+ if (command == pycommand->data)
+ {
+ pycommand->data = NULL;
+ pycommand->cleanup_installed = 0;
+ signal_remove_data("commandlist remove", command_cleanup, pycommand);
+ }
+}
+
+static void PyCommand_dealloc(PyCommand *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("commandlist remove", command_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyCommand_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyCommand *self;
+
+ self = (PyCommand *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyCommand_cmd_doc,
+ "Command name"
+);
+static PyObject *PyCommand_cmd_get(PyCommand *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(COMMAND(self->data)->cmd);
+}
+
+PyDoc_STRVAR(PyCommand_category_doc,
+ "Category"
+);
+static PyObject *PyCommand_category_get(PyCommand *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(COMMAND(self->data)->category);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyCommand_getseters[] = {
+ {"cmd", (getter)PyCommand_cmd_get, NULL,
+ PyCommand_cmd_doc, NULL},
+ {"category", (getter)PyCommand_category_get, NULL,
+ PyCommand_category_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+/* Methods for object */
+static PyMethodDef PyCommand_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyCommandType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Command", /*tp_name*/
+ sizeof(PyCommand), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyCommand_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyCommand objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyCommand_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyCommand_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyCommand_new, /* tp_new */
+};
+
+
+/* command factory function */
+PyObject *pycommand_new(void *command)
+{
+ PyCommand *pycommand;
+
+ pycommand = py_inst(PyCommand, PyCommandType);
+ if (!pycommand)
+ return NULL;
+
+ pycommand->data = command;
+ pycommand->cleanup_installed = 1;
+ signal_add_last_data("commandlist remove", command_cleanup, pycommand);
+
+ return (PyObject *)pycommand;
+}
+
+int command_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyCommandType) < 0)
+ return 0;
+
+ Py_INCREF(&PyCommandType);
+ PyModule_AddObject(py_module, "Command", (PyObject *)&PyCommandType);
+
+ return 1;
+}
diff --git a/src/objects/command-object.h b/src/objects/command-object.h
new file mode 100644
index 0000000..e6402cf
--- /dev/null
+++ b/src/objects/command-object.h
@@ -0,0 +1,18 @@
+#ifndef _COMMAND_OBJECT_H_
+#define _COMMAND_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+} PyCommand;
+
+extern PyTypeObject PyCommandType;
+
+int command_object_init(void);
+PyObject *pycommand_new(void *command);
+#define pycommand_check(op) PyObject_TypeCheck(op, &PyCommandType)
+
+#endif
diff --git a/src/objects/connect-object.c b/src/objects/connect-object.c
new file mode 100644
index 0000000..8ef9a29
--- /dev/null
+++ b/src/objects/connect-object.c
@@ -0,0 +1,198 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "connect-object.h"
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+static void connect_cleanup(SERVER_CONNECT_REC *connect)
+{
+ PyConnect *pyconn = signal_get_user_data();
+
+ /*
+ if (server == pyconn->data)
+ {
+ pyserver->data = NULL;
+ pyserver->cleanup_installed = 0;
+ signal_remove_data("server disconnected", connect_cleanup, pyserver);
+ }
+ */
+}
+
+static void PyConnect_dealloc(PyConnect *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("server disconnected", connect_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyConnect_address_doc,
+ "Address where we connected (irc.blah.org)"
+);
+static PyObject *PyConnect_address_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->address);
+}
+
+PyDoc_STRVAR(PyConnect_port_doc,
+ "Port where we're connected"
+);
+static PyObject *PyConnect_port_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->port);
+}
+
+PyDoc_STRVAR(PyConnect_chatnet_doc,
+ "Chat network"
+);
+static PyObject *PyConnect_chatnet_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->chatnet);
+}
+
+PyDoc_STRVAR(PyConnect_password_doc,
+ "Password we used in connection."
+);
+static PyObject *PyConnect_password_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->password);
+}
+
+PyDoc_STRVAR(PyConnect_wanted_nick_doc,
+ "Nick which we would prefer to use"
+);
+static PyObject *PyConnect_wanted_nick_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->nick);
+}
+
+PyDoc_STRVAR(PyConnect_username_doc,
+ "User name"
+);
+static PyObject *PyConnect_username_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->username);
+}
+
+PyDoc_STRVAR(PyConnect_realname_doc,
+ "Real name"
+);
+static PyObject *PyConnect_realname_get(PyConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->realname);
+}
+
+/* Get/Set */
+static PyGetSetDef PyConnect_getseters[] = {
+ {"address", (getter)PyConnect_address_get, NULL,
+ PyConnect_address_doc, NULL},
+ {"port", (getter)PyConnect_port_get, NULL,
+ PyConnect_port_doc, NULL},
+ {"chatnet", (getter)PyConnect_chatnet_get, NULL,
+ PyConnect_chatnet_doc, NULL},
+ {"password", (getter)PyConnect_password_get, NULL,
+ PyConnect_password_doc, NULL},
+ {"wanted_nick", (getter)PyConnect_wanted_nick_get, NULL,
+ PyConnect_wanted_nick_doc, NULL},
+ {"username", (getter)PyConnect_username_get, NULL,
+ PyConnect_username_doc, NULL},
+ {"realname", (getter)PyConnect_realname_get, NULL,
+ PyConnect_realname_doc, NULL},
+ {NULL}
+};
+
+PyTypeObject PyConnectType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Connect", /*tp_name*/
+ sizeof(PyConnect), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyConnect_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyConnect objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ PyConnect_getseters, /* tp_getset */
+ &PyIrssiChatBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* server connect factory function (managed == 0, don't do signal cleanup, 1 == do sig cleanup */
+PyObject *pyconnect_sub_new(void *connect, PyTypeObject *subclass, int managed)
+{
+ static const char *CONNECT_TYPE = "SERVER CONNECT";
+ PyConnect *pyconn = NULL;
+
+ g_return_val_if_fail(connect != NULL, NULL);
+
+ pyconn = py_instp(PyConnect, subclass);
+ if (!pyconn)
+ return NULL;
+
+ pyconn->base_name = CONNECT_TYPE;
+ pyconn->data = connect;
+
+ if (managed)
+ {
+ //XXX: how to handle cleanup?
+ //signal_add_last_data("server disconnected", connect_cleanup, pyconn);
+ //pyconn->cleanup_installed = 1;
+ }
+
+ return (PyObject *)pyconn;
+}
+
+PyObject *pyconnect_new(void *connect, int managed)
+{
+ return pyconnect_sub_new(connect, &PyConnectType, managed);
+}
+
+int connect_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyConnectType) < 0)
+ return 0;
+
+ Py_INCREF(&PyConnectType);
+ PyModule_AddObject(py_module, "Connect", (PyObject *)&PyConnectType);
+
+ return 1;
+}
diff --git a/src/objects/connect-object.h b/src/objects/connect-object.h
new file mode 100644
index 0000000..53bd509
--- /dev/null
+++ b/src/objects/connect-object.h
@@ -0,0 +1,22 @@
+#ifndef _CONNECT_OBJECT_H_
+#define _CONNECT_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _SERVER_CONNECT_REC;
+
+typedef struct
+{
+ PyIrssi_HEAD(struct _SERVER_CONNECT_REC)
+} PyConnect;
+
+extern PyTypeObject PyConnectType;
+
+int connect_object_init(void);
+PyObject *pyconnect_sub_new(void *connect, PyTypeObject *subtype, int managed);
+PyObject *pyconnect_new(void *connect, int managed);
+#define pyconnect_check(op) PyObject_TypeCheck(op, &PyConnectType)
+
+#endif
diff --git a/src/objects/dcc-chat-object.c b/src/objects/dcc-chat-object.c
new file mode 100644
index 0000000..5cfe796
--- /dev/null
+++ b/src/objects/dcc-chat-object.c
@@ -0,0 +1,137 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "dcc-chat-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+/* inherit destroy and cleanup from DccChat type */
+
+/* Getters */
+PyDoc_STRVAR(PyDccChat_id_doc,
+ "Unique identifier - usually same as nick"
+);
+static PyObject *PyDccChat_id_get(PyDccChat *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->id);
+}
+
+PyDoc_STRVAR(PyDccChat_mirc_ctcp_doc,
+ "Send CTCPs without the CTCP_MESSAGE prefix"
+);
+static PyObject *PyDccChat_mirc_ctcp_get(PyDccChat *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->mirc_ctcp);
+}
+
+PyDoc_STRVAR(PyDccChat_connection_lost_doc,
+ "Other side closed connection"
+);
+static PyObject *PyDccChat_connection_lost_get(PyDccChat *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->connection_lost);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyDccChat_getseters[] = {
+ {"id", (getter)PyDccChat_id_get, NULL,
+ PyDccChat_id_doc, NULL},
+ {"mirc_ctcp", (getter)PyDccChat_mirc_ctcp_get, NULL,
+ PyDccChat_mirc_ctcp_doc, NULL},
+ {"connection_lost", (getter)PyDccChat_connection_lost_get, NULL,
+ PyDccChat_connection_lost_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyDccChat_chat_send_doc,
+ "chat_send(data) -> None\n"
+ "\n"
+ "Send data to a dcc chat session.\n"
+);
+static PyObject *PyDccChat_chat_send(PyDccChat *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"data", NULL};
+ char *data = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &data))
+ return NULL;
+
+ dcc_chat_send(self->data, data);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyDccChat_methods[] = {
+ {"chat_send", (PyCFunction)PyDccChat_chat_send, METH_VARARGS | METH_KEYWORDS,
+ PyDccChat_chat_send_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyDccChatType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "DccChat", /*tp_name*/
+ sizeof(PyDccChat), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyDccChat objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyDccChat_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyDccChat_getseters, /* tp_getset */
+ &PyDccType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyObject *pydcc_chat_new(void *dcc)
+{
+ static const char *name = "DCC CHAT";
+ return pydcc_sub_new(dcc, name, &PyDccChatType);
+}
+
+int dcc_chat_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyDccChatType) < 0)
+ return 0;
+
+ Py_INCREF(&PyDccChatType);
+ PyModule_AddObject(py_module, "DccChat", (PyObject *)&PyDccChatType);
+
+ return 1;
+}
diff --git a/src/objects/dcc-chat-object.h b/src/objects/dcc-chat-object.h
new file mode 100644
index 0000000..955e6be
--- /dev/null
+++ b/src/objects/dcc-chat-object.h
@@ -0,0 +1,21 @@
+#ifndef _DCC_CHAT_OBJECT_H_
+#define _DCC_CHAT_OBJECT_H_
+
+#include <Python.h>
+#include "dcc-object.h"
+
+/* forward */
+struct CHAT_DCC_REC;
+
+typedef struct
+{
+ PyDcc_HEAD(struct CHAT_DCC_REC)
+} PyDccChat;
+
+extern PyTypeObject PyDccChatType;
+
+PyObject *pydcc_chat_new(void *dcc);
+#define pydcc_chat_check(op) PyObject_TypeCheck(op, &PyDccChatType)
+int dcc_chat_object_init(void);
+
+#endif
diff --git a/src/objects/dcc-get-object.c b/src/objects/dcc-get-object.c
new file mode 100644
index 0000000..fa7c13a
--- /dev/null
+++ b/src/objects/dcc-get-object.c
@@ -0,0 +1,138 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "dcc-get-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+#define DCC_GET_CAST(rec) ((GET_DCC_REC *)rec)
+
+/* inherit destroy and cleanup from Dcc type */
+
+/* Getters */
+PyDoc_STRVAR(PyDccGet_size_doc,
+ "File size"
+);
+static PyObject *PyDccGet_size_get(PyDccGet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC_GET_CAST(self->data)->size);
+}
+
+PyDoc_STRVAR(PyDccGet_skipped_doc,
+ "Bytes skipped from start (resuming file)"
+);
+static PyObject *PyDccGet_skipped_get(PyDccGet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC_GET_CAST(self->data)->skipped);
+}
+
+PyDoc_STRVAR(PyDccGet_get_type_doc,
+ "What to do if file exists? 0=default, 1=rename, 2=overwrite, 3=resume"
+);
+static PyObject *PyDccGet_get_type_get(PyDccGet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(DCC_GET_CAST(self->data)->get_type);
+}
+
+PyDoc_STRVAR(PyDccGet_file_doc,
+ "The real file name which we use."
+);
+static PyObject *PyDccGet_file_get(PyDccGet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC_GET_CAST(self->data)->file);
+}
+
+PyDoc_STRVAR(PyDccGet_file_quoted_doc,
+ "true if file name was received quoted (\"file name\")"
+);
+static PyObject *PyDccGet_file_quoted_get(PyDccGet *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(DCC_GET_CAST(self->data)->file_quoted);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyDccGet_getseters[] = {
+ {"size", (getter)PyDccGet_size_get, NULL,
+ PyDccGet_size_doc, NULL},
+ {"skipped", (getter)PyDccGet_skipped_get, NULL,
+ PyDccGet_skipped_doc, NULL},
+ {"get_type", (getter)PyDccGet_get_type_get, NULL,
+ PyDccGet_get_type_doc, NULL},
+ {"file", (getter)PyDccGet_file_get, NULL,
+ PyDccGet_file_doc, NULL},
+ {"file_quoted", (getter)PyDccGet_file_quoted_get, NULL,
+ PyDccGet_file_quoted_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+/* Methods for object */
+static PyMethodDef PyDccGet_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyDccGetType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "DccGet", /*tp_name*/
+ sizeof(PyDccGet), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyDccGet objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyDccGet_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyDccGet_getseters, /* tp_getset */
+ &PyDccType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyObject *pydcc_get_new(void *dcc)
+{
+ static const char *name = "DCC GET";
+ return pydcc_sub_new(dcc, name, &PyDccGetType);
+}
+
+int dcc_get_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyDccGetType) < 0)
+ return 0;
+
+ Py_INCREF(&PyDccGetType);
+ PyModule_AddObject(py_module, "DccGet", (PyObject *)&PyDccGetType);
+
+ return 1;
+}
diff --git a/src/objects/dcc-get-object.h b/src/objects/dcc-get-object.h
new file mode 100644
index 0000000..532b854
--- /dev/null
+++ b/src/objects/dcc-get-object.h
@@ -0,0 +1,18 @@
+#ifndef _DCC_GET_OBJECT_H_
+#define _DCC_GET_OBJECT_H_
+
+#include <Python.h>
+#include "dcc-object.h"
+
+typedef struct
+{
+ PyDcc_HEAD(void)
+} PyDccGet;
+
+extern PyTypeObject PyDccGetType;
+
+PyObject *pydcc_get_new(void *dcc);
+#define pydcc_get_check(op) PyObject_TypeCheck(op, &PyDccGetType)
+int dcc_get_object_init(void);
+
+#endif
diff --git a/src/objects/dcc-object.c b/src/objects/dcc-object.c
new file mode 100644
index 0000000..95a3a4a
--- /dev/null
+++ b/src/objects/dcc-object.c
@@ -0,0 +1,347 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "dcc-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+
+/* monitor "dcc destroyed signal" */
+static void dcc_cleanup(DCC_REC *dcc)
+{
+ PyDcc *pydcc = signal_get_user_data();
+
+ if (dcc == pydcc->data)
+ {
+ pydcc->data = NULL;
+ pydcc->cleanup_installed = 0;
+ signal_remove_data("dcc destroyed", dcc_cleanup, pydcc);
+ }
+}
+
+static void PyDcc_dealloc(PyDcc *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("dcc destroyed", dcc_cleanup, self);
+
+ Py_XDECREF(self->server);
+ Py_XDECREF(self->chat);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyDcc_orig_type_doc,
+ "Original DCC type that was sent to us - same as type except GET and SEND are swapped"
+);
+static PyObject *PyDcc_orig_type_get(PyDcc *self, void *closure)
+{
+ const char *type;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ type = module_find_id_str("DCC", DCC(self->data)->orig_type);
+ RET_AS_STRING_OR_NONE(type);
+}
+
+PyDoc_STRVAR(PyDcc_created_doc,
+ "Time stamp when the DCC record was created"
+);
+static PyObject *PyDcc_created_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC(self->data)->created);
+}
+
+PyDoc_STRVAR(PyDcc_server_doc,
+ "Server record where the DCC was initiated."
+);
+static PyObject *PyDcc_server_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->server);
+}
+
+PyDoc_STRVAR(PyDcc_servertag_doc,
+ "Tag of the server where the DCC was initiated."
+);
+static PyObject *PyDcc_servertag_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC(self->data)->servertag);
+}
+
+PyDoc_STRVAR(PyDcc_mynick_doc,
+ "Our nick to use in DCC chat."
+);
+static PyObject *PyDcc_mynick_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC(self->data)->mynick);
+}
+
+PyDoc_STRVAR(PyDcc_nick_doc,
+ "Other side's nick name."
+);
+static PyObject *PyDcc_nick_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC(self->data)->nick);
+}
+
+PyDoc_STRVAR(PyDcc_chat_doc,
+ "Dcc chat record if the request came through DCC chat"
+);
+static PyObject *PyDcc_chat_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->chat);
+}
+
+PyDoc_STRVAR(PyDcc_target_doc,
+ "Who the request was sent to - your nick, channel or empty if you sent the request"
+);
+static PyObject *PyDcc_target_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC(self->data)->target);
+}
+
+PyDoc_STRVAR(PyDcc_arg_doc,
+ "Given argument .. file name usually"
+);
+static PyObject *PyDcc_arg_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC(self->data)->arg);
+}
+
+PyDoc_STRVAR(PyDcc_addr_doc,
+ "Other side's IP address."
+);
+static PyObject *PyDcc_addr_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(DCC(self->data)->addrstr);
+}
+
+PyDoc_STRVAR(PyDcc_port_doc,
+ "Port we're connecting in."
+);
+static PyObject *PyDcc_port_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(DCC(self->data)->port);
+}
+
+PyDoc_STRVAR(PyDcc_starttime_doc,
+ "Unix time stamp when the DCC transfer was started"
+);
+static PyObject *PyDcc_starttime_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC(self->data)->starttime);
+}
+
+PyDoc_STRVAR(PyDcc_transfd_doc,
+ "Bytes transferred"
+);
+static PyObject *PyDcc_transfd_get(PyDcc *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC(self->data)->transfd);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyDcc_getseters[] = {
+ {"orig_type", (getter)PyDcc_orig_type_get, NULL,
+ PyDcc_orig_type_doc, NULL},
+ {"created", (getter)PyDcc_created_get, NULL,
+ PyDcc_created_doc, NULL},
+ {"server", (getter)PyDcc_server_get, NULL,
+ PyDcc_server_doc, NULL},
+ {"servertag", (getter)PyDcc_servertag_get, NULL,
+ PyDcc_servertag_doc, NULL},
+ {"mynick", (getter)PyDcc_mynick_get, NULL,
+ PyDcc_mynick_doc, NULL},
+ {"nick", (getter)PyDcc_nick_get, NULL,
+ PyDcc_nick_doc, NULL},
+ {"chat", (getter)PyDcc_chat_get, NULL,
+ PyDcc_chat_doc, NULL},
+ {"target", (getter)PyDcc_target_get, NULL,
+ PyDcc_target_doc, NULL},
+ {"arg", (getter)PyDcc_arg_get, NULL,
+ PyDcc_arg_doc, NULL},
+ {"addr", (getter)PyDcc_addr_get, NULL,
+ PyDcc_addr_doc, NULL},
+ {"port", (getter)PyDcc_port_get, NULL,
+ PyDcc_port_doc, NULL},
+ {"starttime", (getter)PyDcc_starttime_get, NULL,
+ PyDcc_starttime_doc, NULL},
+ {"transfd", (getter)PyDcc_transfd_get, NULL,
+ PyDcc_transfd_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyDcc_destroy_doc,
+ "destroy() -> None\n"
+ "\n"
+ "Destroy DCC connection\n"
+);
+static PyObject *PyDcc_destroy(PyDcc *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ dcc_destroy(DCC(self->data));
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyDcc_reject_doc,
+ "reject() -> None\n"
+ "\n"
+ "?\n"
+);
+static PyObject *PyDcc_reject(PyDcc *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"server", NULL};
+ PyObject *server = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &server))
+ return NULL;
+
+ if (!pyirc_server_check(server))
+ return PyErr_Format(PyExc_TypeError, "arg must be IRC server object");
+
+ dcc_reject(self->data, ((PyIrcServer*)server)->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyDcc_close_doc,
+ "close() -> None\n"
+ "\n"
+ "Close and destroy DCC connection.\n"
+);
+static PyObject *PyDcc_close(PyDcc *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ dcc_close(self->data);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyDcc_methods[] = {
+ {"destroy", (PyCFunction)PyDcc_destroy, METH_NOARGS,
+ PyDcc_destroy_doc},
+ {"reject", (PyCFunction)PyDcc_reject, METH_VARARGS | METH_KEYWORDS,
+ PyDcc_reject_doc},
+ {"close", (PyCFunction)PyDcc_close, METH_NOARGS,
+ PyDcc_close_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyDccType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Dcc", /*tp_name*/
+ sizeof(PyDcc), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyDcc_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyDcc objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyDcc_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyDcc_getseters, /* tp_getset */
+ &PyIrssiBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* Dcc factory function */
+PyObject *pydcc_sub_new(void *dcc, const char *name, PyTypeObject *subclass)
+{
+ PyObject *chat = NULL, *server = NULL;
+ PyDcc *pydcc;
+ DCC_REC *rec = dcc;
+
+ server = py_irssi_chat_new(rec->server, 1);
+ if (!server)
+ return NULL;
+
+ chat = py_irssi_chat_new(rec->chat, 1);
+ if (!chat)
+ {
+ Py_DECREF(server);
+ return NULL;
+ }
+
+ pydcc = py_instp(PyDcc, subclass);
+ if (!pydcc)
+ {
+ Py_DECREF(server);
+ Py_DECREF(chat);
+ return NULL;
+ }
+
+ pydcc->data = dcc;
+ pydcc->server = server;
+ pydcc->chat = chat;
+ pydcc->base_name = name;
+
+ pydcc->cleanup_installed = 1;
+ signal_add_last_data("dcc destroyed", dcc_cleanup, pydcc);
+
+ return (PyObject *)pydcc;
+}
+
+PyObject *pydcc_new(void *dcc)
+{
+ static const char *name = "DCC";
+ return pydcc_sub_new(dcc, name, &PyDccType);
+}
+
+int dcc_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyDccType) < 0)
+ return 0;
+
+ Py_INCREF(&PyDccType);
+ PyModule_AddObject(py_module, "Dcc", (PyObject *)&PyDccType);
+
+ return 1;
+}
diff --git a/src/objects/dcc-object.h b/src/objects/dcc-object.h
new file mode 100644
index 0000000..d44d3db
--- /dev/null
+++ b/src/objects/dcc-object.h
@@ -0,0 +1,24 @@
+#ifndef _DCC_OBJECT_H_
+#define _DCC_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+#define PyDcc_HEAD(type) \
+ PyIrssi_HEAD(type) \
+ PyObject *server; \
+ PyObject *chat;
+
+typedef struct
+{
+ PyDcc_HEAD(void)
+} PyDcc;
+
+extern PyTypeObject PyDccType;
+
+PyObject *pydcc_sub_new(void *dcc, const char *name, PyTypeObject *subclass);
+PyObject *pydcc_new(void *dcc);
+#define pydcc_check(op) PyObject_TypeCheck(op, &PyDccType)
+int dcc_object_init(void);
+
+#endif
diff --git a/src/objects/dcc-send-object.c b/src/objects/dcc-send-object.c
new file mode 100644
index 0000000..643f741
--- /dev/null
+++ b/src/objects/dcc-send-object.c
@@ -0,0 +1,138 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "dcc-send-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+#define DCC_SEND_CAST(rec) ((SEND_DCC_REC *)rec)
+
+/* inherit destroy and cleanup from Dcc type */
+
+/* Getters */
+PyDoc_STRVAR(PyDccSend_size_doc,
+ "File size"
+);
+static PyObject *PyDccSend_size_get(PyDccSend *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC_SEND_CAST(self->data)->size);
+}
+
+PyDoc_STRVAR(PyDccSend_skipped_doc,
+ "Bytes skipped from start (resuming file)"
+);
+static PyObject *PyDccSend_skipped_get(PyDccSend *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(DCC_SEND_CAST(self->data)->skipped);
+}
+
+PyDoc_STRVAR(PyDccSend_file_quoted_doc,
+ "True if file name was received quoted (\"file name\")"
+);
+static PyObject *PyDccSend_file_quoted_get(PyDccSend *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(DCC_SEND_CAST(self->data)->file_quoted);
+}
+
+PyDoc_STRVAR(PyDccSend_waitforend_doc,
+ "File is sent, just wait for the replies from the other side"
+);
+static PyObject *PyDccSend_waitforend_get(PyDccSend *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(DCC_SEND_CAST(self->data)->waitforend);
+}
+
+PyDoc_STRVAR(PyDccSend_gotalldata_doc,
+ "Got all acks from the other end"
+);
+static PyObject *PyDccSend_gotalldata_get(PyDccSend *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(DCC_SEND_CAST(self->data)->gotalldata);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyDccSend_getseters[] = {
+ {"size", (getter)PyDccSend_size_get, NULL,
+ PyDccSend_size_doc, NULL},
+ {"skipped", (getter)PyDccSend_skipped_get, NULL,
+ PyDccSend_skipped_doc, NULL},
+ {"file_quoted", (getter)PyDccSend_file_quoted_get, NULL,
+ PyDccSend_file_quoted_doc, NULL},
+ {"waitforend", (getter)PyDccSend_waitforend_get, NULL,
+ PyDccSend_waitforend_doc, NULL},
+ {"gotalldata", (getter)PyDccSend_gotalldata_get, NULL,
+ PyDccSend_gotalldata_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+/* Methods for object */
+static PyMethodDef PyDccSend_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyDccSendType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "DccSend", /*tp_name*/
+ sizeof(PyDccSend), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyDccSend objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyDccSend_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyDccSend_getseters, /* tp_getset */
+ &PyDccType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyObject *pydcc_send_new(void *dcc)
+{
+ static const char *name = "DCC SEND";
+ return pydcc_sub_new(dcc, name, &PyDccSendType);
+}
+
+int dcc_send_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyDccSendType) < 0)
+ return 0;
+
+ Py_INCREF(&PyDccSendType);
+ PyModule_AddObject(py_module, "DccSend", (PyObject *)&PyDccSendType);
+
+ return 1;
+}
diff --git a/src/objects/dcc-send-object.h b/src/objects/dcc-send-object.h
new file mode 100644
index 0000000..6303f86
--- /dev/null
+++ b/src/objects/dcc-send-object.h
@@ -0,0 +1,18 @@
+#ifndef _DCC_SEND_OBJECT_H_
+#define _DCC_SEND_OBJECT_H_
+
+#include <Python.h>
+#include "dcc-object.h"
+
+typedef struct
+{
+ PyDcc_HEAD(void)
+} PyDccSend;
+
+extern PyTypeObject PyDccSendType;
+
+PyObject *pydcc_send_new(void *dcc);
+#define pydcc_send_check(op) PyObject_TypeCheck(op, &PyDccSendType)
+int dcc_send_object_init(void);
+
+#endif
diff --git a/src/objects/factory.c b/src/objects/factory.c
new file mode 100644
index 0000000..49336e6
--- /dev/null
+++ b/src/objects/factory.c
@@ -0,0 +1,326 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "factory.h"
+
+/* Irssi object factory works for all items with at least a type member.
+ *
+ * Use py_irssi_new() or py_irssi_chat_new() to get a new wrapper for an
+ * IrssiObject or an IrssiChatObject, respectively.
+ *
+ * For objects not descending from IrssiObject or IrssiChatObject, you must
+ * use the object-specific init function directly.
+ */
+
+#define MAKEKEY(type, chat) ((chat << 16 ) | type)
+#define GETTYPE(key) (key & 0xffff)
+#define GETCHAT(key) ((key >> 16) & 0xffff)
+
+
+GHashTable *init_map = NULL;
+
+static int init_objects(void);
+static void register_chat(CHAT_PROTOCOL_REC *rec);
+static void unregister_chat(CHAT_PROTOCOL_REC *rec);
+static void insert_map(int type, int chat_type, InitFunc func);
+static int remove_chat(void *key, void *value, void *chat_typep);
+static void register_nonchat(void);
+static InitFunc find_map(int type, int chat_type);
+
+static int init_objects(void)
+{
+ if (!pyscript_init())
+ return 0;
+
+ /* order is somewhat important here */
+ if (!base_objects_init())
+ return 0;
+
+ if (!window_item_object_init())
+ return 0;
+
+ if (!channel_object_init())
+ return 0;
+
+ if (!query_object_init())
+ return 0;
+
+ if (!server_object_init())
+ return 0;
+
+ if (!connect_object_init())
+ return 0;
+
+ if (!irc_server_object_init())
+ return 0;
+
+ if (!irc_connect_object_init())
+ return 0;
+
+ if (!irc_channel_object_init())
+ return 0;
+
+ if (!ban_object_init())
+ return 0;
+
+ if (!nick_object_init())
+ return 0;
+
+ if (!chatnet_object_init())
+ return 0;
+
+ if (!reconnect_object_init())
+ return 0;
+
+ if (!window_object_init())
+ return 0;
+
+ if (!textdest_object_init())
+ return 0;
+
+ if (!rawlog_object_init())
+ return 0;
+
+ if (!log_object_init())
+ return 0;
+
+ if (!logitem_object_init())
+ return 0;
+
+ if (!ignore_object_init())
+ return 0;
+
+ if (!dcc_object_init())
+ return 0;
+
+ if (!dcc_chat_object_init())
+ return 0;
+
+ if (!dcc_get_object_init())
+ return 0;
+
+ if (!dcc_send_object_init())
+ return 0;
+
+ if (!netsplit_object_init())
+ return 0;
+
+ if (!netsplit_server_object_init())
+ return 0;
+
+ if (!netsplit_channel_object_init())
+ return 0;
+
+ if (!notifylist_object_init())
+ return 0;
+
+ if (!process_object_init())
+ return 0;
+
+ if (!command_object_init())
+ return 0;
+
+ if (!theme_object_init())
+ return 0;
+
+ if (!statusbar_item_object_init())
+ return 0;
+
+ if (!main_window_object_init())
+ return 0;
+
+ return 1;
+}
+
+static InitFunc find_map(int type, int chat_type)
+{
+ unsigned hash;
+
+ g_return_val_if_fail(type <= 0xffff, NULL);
+ g_return_val_if_fail(chat_type <= 0xffff, NULL);
+
+ hash = MAKEKEY(type, chat_type);
+ return g_hash_table_lookup(init_map, GUINT_TO_POINTER(hash));
+}
+
+static void insert_map(int type, int chat_type, InitFunc func)
+{
+ unsigned hash;
+
+ g_return_if_fail(type <= 0xffff);
+ g_return_if_fail(chat_type <= 0xffff);
+
+ hash = MAKEKEY(type, chat_type);
+ g_hash_table_insert(init_map, GUINT_TO_POINTER(hash), func);
+}
+
+static void register_chat(CHAT_PROTOCOL_REC *rec)
+{
+ int type;
+ int chat_type;
+ int is_irc = 0;
+
+ /* chat_type == rec->id ??? */
+ chat_type = chat_protocol_lookup(rec->name);
+ g_return_if_fail(chat_type >= 0 && chat_type < 0xffff);
+
+ if (!g_strcasecmp(rec->name, "IRC"))
+ is_irc = 1;
+
+ type = module_get_uniq_id("SERVER", 0);
+ if (is_irc)
+ insert_map(type, chat_type, (InitFunc)pyirc_server_new);
+ else
+ insert_map(type, chat_type, (InitFunc)pyserver_new);
+
+ type = module_get_uniq_id("SERVER CONNECT", 0);
+ if (is_irc)
+ insert_map(type, chat_type, (InitFunc)pyirc_connect_new);
+ else
+ insert_map(type, chat_type, (InitFunc)pyconnect_new);
+
+ type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL");
+ if (is_irc)
+ insert_map(type, chat_type, (InitFunc)pyirc_channel_new);
+ else
+ insert_map(type, chat_type, (InitFunc)pychannel_new);
+
+ type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY");
+ insert_map(type, chat_type, (InitFunc)pyquery_new);
+
+ type = module_get_uniq_id("CHATNET", 0);
+ insert_map(type, chat_type, (InitFunc)pychatnet_new);
+
+ type = module_get_uniq_id("NICK", 0);
+ insert_map(type, chat_type, (InitFunc)pynick_new);
+}
+
+/* register funcs for objects without a chat type */
+static void register_nonchat(void)
+{
+ int type;
+ int chat_type = 0xffff;
+
+ type = module_get_uniq_id_str("DCC", "CHAT");
+ insert_map(type, chat_type, (InitFunc)pydcc_chat_new);
+
+ type = module_get_uniq_id_str("DCC", "GET");
+ insert_map(type, chat_type, (InitFunc)pydcc_get_new);
+
+ type = module_get_uniq_id_str("DCC", "SEND");
+ insert_map(type, chat_type, (InitFunc)pydcc_send_new);
+
+ type = module_get_uniq_id_str("DCC", "SERVER");
+ insert_map(type, chat_type, (InitFunc)pydcc_new);
+}
+
+static int remove_chat(void *key, void *value, void *chat_typep)
+{
+ unsigned hash = GPOINTER_TO_UINT(key);
+ int chat_type = GPOINTER_TO_INT(chat_type);
+
+ if (GETCHAT(hash) == chat_type)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* remove all items matching chat_type */
+static void unregister_chat(CHAT_PROTOCOL_REC *rec)
+{
+ /*int chat_type = chat_protocol_lookup(rec->name);*/
+ g_hash_table_foreach_remove(init_map,
+ (GHRFunc)remove_chat,
+ GINT_TO_POINTER(rec->id));
+}
+
+PyObject *py_irssi_new(void *typeobj, int managed)
+{
+ IRSSI_BASE_REC *base = typeobj;
+ InitFunc ifunc;
+
+ if (!base)
+ Py_RETURN_NONE;
+
+ ifunc = find_map(base->type, 0xffff);
+
+ if (ifunc)
+ return ifunc(typeobj, managed);
+
+ return PyErr_Format(PyExc_RuntimeError, "no initfunc for object type %d", base->type);
+}
+
+PyObject *py_irssi_chat_new(void *typeobj, int managed)
+{
+ IRSSI_CHAT_REC *chat = typeobj;
+ InitFunc ifunc;
+
+ if (!chat)
+ Py_RETURN_NONE;
+
+ ifunc = find_map(chat->type, chat->chat_type);
+
+ if (ifunc)
+ return ifunc(typeobj, managed);
+
+ return PyErr_Format(PyExc_RuntimeError, "no initfunc for object type %d, chat_type %d",
+ chat->type, chat->chat_type);
+}
+
+PyObject *py_irssi_objlist_new(GSList *node, int managed, InitFunc init)
+{
+ PyObject *list = NULL;
+
+ list = PyList_New(0);
+ if (!list)
+ goto error;
+
+ for (; node != NULL; node = node->next)
+ {
+ int ret;
+ PyObject *obj = init(node->data, managed);
+
+ if (!obj)
+ goto error;
+
+ ret = PyList_Append(list, obj);
+ Py_DECREF(obj);
+
+ if (ret != 0)
+ goto error;
+ }
+
+ return list;
+
+error:
+ Py_XDECREF(list);
+ return NULL;
+}
+
+int factory_init(void)
+{
+ g_return_val_if_fail(init_map == NULL, 0);
+
+ if (!init_objects())
+ return 0;
+
+ init_map = g_hash_table_new(g_direct_hash, g_direct_equal);
+ g_slist_foreach(chat_protocols, (GFunc) register_chat, NULL);
+ register_nonchat();
+
+ signal_add("chat protocol created", (SIGNAL_FUNC) register_chat);
+ signal_add("chat protocol destroyed", (SIGNAL_FUNC) unregister_chat);
+
+ return 1;
+}
+
+void factory_deinit(void)
+{
+ g_return_if_fail(init_map != NULL);
+
+ g_hash_table_destroy(init_map);
+ init_map = NULL;
+
+ signal_remove("chat protocol created", (SIGNAL_FUNC) register_chat);
+ signal_remove("chat protocol destroyed", (SIGNAL_FUNC) unregister_chat);
+}
+
diff --git a/src/objects/factory.h b/src/objects/factory.h
new file mode 100644
index 0000000..9d4bd79
--- /dev/null
+++ b/src/objects/factory.h
@@ -0,0 +1,58 @@
+#ifndef _FACTORY_H_
+#define _FACTORY_H_
+
+#include <Python.h>
+#include "pyscript-object.h"
+#include "base-objects.h"
+#include "window-item-object.h"
+#include "channel-object.h"
+#include "query-object.h"
+#include "server-object.h"
+#include "connect-object.h"
+#include "irc-server-object.h"
+#include "irc-connect-object.h"
+#include "irc-channel-object.h"
+#include "ban-object.h"
+#include "nick-object.h"
+#include "chatnet-object.h"
+#include "reconnect-object.h"
+#include "window-object.h"
+#include "textdest-object.h"
+#include "rawlog-object.h"
+#include "log-object.h"
+#include "logitem-object.h"
+#include "ignore-object.h"
+#include "dcc-object.h"
+#include "dcc-chat-object.h"
+#include "dcc-get-object.h"
+#include "dcc-send-object.h"
+#include "netsplit-object.h"
+#include "netsplit-server-object.h"
+#include "netsplit-channel-object.h"
+#include "notifylist-object.h"
+#include "process-object.h"
+#include "command-object.h"
+#include "theme-object.h"
+#include "statusbar-item-object.h"
+#include "main-window-object.h"
+
+int factory_init(void);
+void factory_deinit(void);
+
+/*managed == 1: object invalidates itself
+ *managed == 0: caller responsible for invalidating object
+ *XXX: most objects invalidate themselves, ignoring "managed" switch,
+ * and some are never managed (Reconnect)
+ */
+
+/* For objects with a type member but no chat_type */
+PyObject *py_irssi_new(void *typeobj, int managed);
+/* For objects with both type and chat_type members */
+PyObject *py_irssi_chat_new(void *typeobj, int managed);
+
+typedef PyObject *(*InitFunc)(void *, int);
+PyObject *py_irssi_objlist_new(GSList *node, int managed, InitFunc init);
+#define py_irssi_chatlist_new(n, m) py_irssi_objlist_new(n, m, py_irssi_chat_new)
+#define py_irssi_list_new(n, m) py_irssi_objlist_new(n, m, py_irssi_new)
+
+#endif
diff --git a/src/objects/ignore-object.c b/src/objects/ignore-object.c
new file mode 100644
index 0000000..979c9b1
--- /dev/null
+++ b/src/objects/ignore-object.c
@@ -0,0 +1,284 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "ignore-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+/* monitor "ignore destroy" signal */
+static void ignore_cleanup(IGNORE_REC *ignore)
+{
+ PyIgnore *pyignore = signal_get_user_data();
+
+ if (ignore == pyignore->data)
+ {
+ pyignore->data = NULL;
+ pyignore->cleanup_installed = 0;
+ signal_remove_data("ignore destroy", ignore_cleanup, pyignore);
+ }
+}
+
+static void PyIgnore_dealloc(PyIgnore *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("ignore destroy", ignore_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyIgnore_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyIgnore *self;
+
+ self = (PyIgnore *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyIgnore_mask_doc,
+ "Ignore mask"
+);
+static PyObject *PyIgnore_mask_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->mask);
+}
+
+PyDoc_STRVAR(PyIgnore_servertag_doc,
+ "Ignore only in server"
+);
+static PyObject *PyIgnore_servertag_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->servertag);
+}
+
+PyDoc_STRVAR(PyIgnore_pattern_doc,
+ "Ignore text patern"
+);
+static PyObject *PyIgnore_pattern_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->pattern);
+}
+
+PyDoc_STRVAR(PyIgnore_level_doc,
+ "Ignore level"
+);
+static PyObject *PyIgnore_level_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->level);
+}
+
+PyDoc_STRVAR(PyIgnore_exception_doc,
+ "This is an exception ignore"
+);
+static PyObject *PyIgnore_exception_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->exception);
+}
+
+PyDoc_STRVAR(PyIgnore_regexp_doc,
+ "Regexp pattern matching"
+);
+static PyObject *PyIgnore_regexp_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->regexp);
+}
+
+PyDoc_STRVAR(PyIgnore_fullword_doc,
+ "Pattern matches only full words"
+);
+static PyObject *PyIgnore_fullword_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->fullword);
+}
+
+PyDoc_STRVAR(PyIgnore_replies_doc,
+ "Ignore replies to nick in channel"
+);
+static PyObject *PyIgnore_replies_get(PyIgnore *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->replies);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyIgnore_getseters[] = {
+ {"mask", (getter)PyIgnore_mask_get, NULL,
+ PyIgnore_mask_doc, NULL},
+ {"servertag", (getter)PyIgnore_servertag_get, NULL,
+ PyIgnore_servertag_doc, NULL},
+ {"pattern", (getter)PyIgnore_pattern_get, NULL,
+ PyIgnore_pattern_doc, NULL},
+ {"level", (getter)PyIgnore_level_get, NULL,
+ PyIgnore_level_doc, NULL},
+ {"exception", (getter)PyIgnore_exception_get, NULL,
+ PyIgnore_exception_doc, NULL},
+ {"regexp", (getter)PyIgnore_regexp_get, NULL,
+ PyIgnore_regexp_doc, NULL},
+ {"fullword", (getter)PyIgnore_fullword_get, NULL,
+ PyIgnore_fullword_doc, NULL},
+ {"replies", (getter)PyIgnore_replies_get, NULL,
+ PyIgnore_replies_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyIgnore_channels_doc,
+ "channels() -> list of str\n"
+ "\n"
+ "Ignore only in channels (list of names)\n"
+);
+static PyObject *PyIgnore_channels(PyIgnore *self, PyObject *args)
+{
+ char **p;
+ PyObject *list;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ list = PyList_New(0);
+ if (!list)
+ return NULL;
+
+ for (p = self->data->channels; *p; p++)
+ {
+ int ret;
+ PyObject *str;
+
+ str = PyString_FromString(*p);
+ if (!str)
+ {
+ Py_XDECREF(list);
+ return NULL;
+ }
+
+ ret = PyList_Append(list, str);
+ Py_DECREF(str);
+ if (ret != 0)
+ {
+ Py_XDECREF(list);
+ return NULL;
+ }
+ }
+
+ return list;
+}
+
+PyDoc_STRVAR(PyIgnore_add_rec_doc,
+ "add_rec() -> None\n"
+ "\n"
+ "Add ignore record"
+);
+static PyObject *PyIgnore_add_rec(PyIgnore *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ ignore_add_rec(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyIgnore_update_rec_doc,
+ "update_rec() -> None\n"
+ "\n"
+ "Update ignore record in configuration"
+);
+static PyObject *PyIgnore_update_rec(PyIgnore *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ ignore_update_rec(self->data);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyIgnore_methods[] = {
+ {"add_rec", (PyCFunction)PyIgnore_add_rec, METH_NOARGS,
+ PyIgnore_add_rec_doc},
+ {"update_rec", (PyCFunction)PyIgnore_update_rec, METH_NOARGS,
+ PyIgnore_update_rec_doc},
+ {"channels", (PyCFunction)PyIgnore_channels, METH_NOARGS,
+ PyIgnore_channels_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyIgnoreType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Ignore", /*tp_name*/
+ sizeof(PyIgnore), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyIgnore_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyIgnore objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyIgnore_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyIgnore_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyIgnore_new, /* tp_new */
+};
+
+
+/* ignore factory function */
+PyObject *pyignore_new(void *ignore)
+{
+ PyIgnore *pyignore;
+
+ pyignore = py_inst(PyIgnore, PyIgnoreType);
+ if (!pyignore)
+ return NULL;
+
+ pyignore->data = ignore;
+ pyignore->cleanup_installed = 1;
+ signal_add_last_data("ignore destroy", ignore_cleanup, pyignore);
+
+ return (PyObject *)pyignore;
+}
+
+int ignore_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyIgnoreType) < 0)
+ return 0;
+
+ Py_INCREF(&PyIgnoreType);
+ PyModule_AddObject(py_module, "Ignore", (PyObject *)&PyIgnoreType);
+
+ return 1;
+}
diff --git a/src/objects/ignore-object.h b/src/objects/ignore-object.h
new file mode 100644
index 0000000..39f7425
--- /dev/null
+++ b/src/objects/ignore-object.h
@@ -0,0 +1,21 @@
+#ifndef _IGNORE_OBJECT_H_
+#define _IGNORE_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _IGNORE_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct _IGNORE_REC)
+} PyIgnore;
+
+extern PyTypeObject PyIgnoreType;
+
+int ignore_object_init(void);
+PyObject *pyignore_new(void *ignore);
+#define pyignore_check(op) PyObject_TypeCheck(op, &PyIgnoreType)
+
+#endif
diff --git a/src/objects/irc-channel-object.c b/src/objects/irc-channel-object.c
new file mode 100644
index 0000000..5e9531d
--- /dev/null
+++ b/src/objects/irc-channel-object.c
@@ -0,0 +1,173 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "pycore.h"
+#include "irc-channel-object.h"
+#include "factory.h"
+
+/* PyIrcChannel destructor is inherited from PyChannel */
+
+/* specialized getters/setters */
+static PyGetSetDef PyIrcChannel_getseters[] = {
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(bans_doc,
+ "bans() -> list of Ban objects\n"
+ "\n"
+ "Returns a list of bans in the channel.\n"
+);
+static PyObject *PyIrcChannel_bans(PyIrcChannel *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ return py_irssi_objlist_new(self->data->banlist, 1, (InitFunc)pyban_new);
+}
+
+PyDoc_STRVAR(ban_get_mask_doc,
+ "ban_get_mask(nick, ban_type=0) -> str\n"
+ "\n"
+ "Get ban mask for 'nick'.\n"
+);
+static PyObject *PyIrcChannel_ban_get_mask(PyIrcChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", "ban_type", NULL};
+ char *nick, *str;
+ int ban_type = 0;
+ PyObject *ret;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &nick, &ban_type))
+ return NULL;
+
+ str = ban_get_mask(self->data, nick, ban_type);
+ if (!str)
+ Py_RETURN_NONE;
+
+ ret = PyString_FromString(str);
+ g_free(str);
+
+ return ret;
+}
+
+PyDoc_STRVAR(banlist_add_doc,
+ "banlist_add(ban, nick, time) -> Ban object or None\n"
+ "\n"
+ "Add a new ban to channel. Return None if duplicate."
+);
+static PyObject *PyIrcChannel_banlist_add(PyIrcChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"ban", "nick", "time", NULL};
+ char *ban, *nick;
+ time_t btime;
+ BAN_REC *newban;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssk", kwlist, &ban, &nick, &btime))
+ return NULL;
+
+ newban = banlist_add(self->data, ban, nick, btime);
+ /* XXX: return none or throw error? */
+ if (!newban)
+ Py_RETURN_NONE;
+
+ return pyban_new(newban);
+}
+
+PyDoc_STRVAR(banlist_remove_doc,
+ "banlist_remove(ban, nick) -> None\n"
+ "\n"
+ "Remove a new ban from channel.\n"
+);
+static PyObject *PyIrcChannel_banlist_remove(PyIrcChannel *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"ban", "nick", NULL};
+ char *ban, *nick;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, &ban, &nick))
+ return NULL;
+
+ banlist_remove(self->data, ban, nick);
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyIrcChannel_methods[] = {
+ {"bans", (PyCFunction)PyIrcChannel_bans, METH_NOARGS,
+ bans_doc},
+ {"ban_get_mask", (PyCFunction)PyIrcChannel_ban_get_mask, METH_VARARGS | METH_KEYWORDS,
+ ban_get_mask_doc},
+ {"banlist_add", (PyCFunction)PyIrcChannel_banlist_add, METH_VARARGS | METH_KEYWORDS,
+ banlist_add_doc},
+ {"banlist_remove", (PyCFunction)PyIrcChannel_banlist_remove, METH_VARARGS | METH_KEYWORDS,
+ banlist_remove_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyIrcChannelType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "IrcChannel", /*tp_name*/
+ sizeof(PyIrcChannel), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyIrcChannel objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyIrcChannel_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyIrcChannel_getseters, /* tp_getset */
+ &PyChannelType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* irc channel factory function */
+PyObject *pyirc_channel_new(void *chan)
+{
+ static const char *BASE_NAME = "CHANNEL";
+ return pychannel_sub_new(chan, BASE_NAME, &PyIrcChannelType);
+}
+
+int irc_channel_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyIrcChannelType) < 0)
+ return 0;
+
+ Py_INCREF(&PyIrcChannelType);
+ PyModule_AddObject(py_module, "IrcChannel", (PyObject *)&PyIrcChannelType);
+
+ return 1;
+}
diff --git a/src/objects/irc-channel-object.h b/src/objects/irc-channel-object.h
new file mode 100644
index 0000000..a1d845a
--- /dev/null
+++ b/src/objects/irc-channel-object.h
@@ -0,0 +1,21 @@
+#ifndef _IRC_CHANNEL_OBJECT_H_
+#define _IRC_CHANNEL_OBJECT_H_
+
+#include <Python.h>
+#include "window-item-object.h"
+
+/* forward */
+struct _IRC_CHANNEL_REC;
+
+typedef struct
+{
+ PyWindowItem_HEAD(struct _IRC_CHANNEL_REC)
+} PyIrcChannel;
+
+extern PyTypeObject PyIrcChannelType;
+
+int irc_channel_object_init(void);
+PyObject *pyirc_channel_new(void *chan);
+#define pyirc_channel_check(op) PyObject_TypeCheck(op, &PyIrcChannelType)
+
+#endif
diff --git a/src/objects/irc-connect-object.c b/src/objects/irc-connect-object.c
new file mode 100644
index 0000000..5068dea
--- /dev/null
+++ b/src/objects/irc-connect-object.c
@@ -0,0 +1,86 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "irc-connect-object.h"
+#include "pyirssi_irc.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+/* cleanup and deallocation handled by Connect base */
+
+/* Getters */
+PyDoc_STRVAR(PyIrcConnect_alternate_nick_doc,
+ "Alternate nick to use if default nick is taken"
+);
+static PyObject *PyIrcConnect_alternate_nick_get(PyIrcConnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->alternate_nick);
+}
+
+/* Get/Set */
+static PyGetSetDef PyIrcConnect_getseters[] = {
+ {"alternate_nick", (getter)PyIrcConnect_alternate_nick_get, NULL,
+ PyIrcConnect_alternate_nick_doc, NULL},
+ {NULL}
+};
+
+PyTypeObject PyIrcConnectType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "IrcConnect", /*tp_name*/
+ sizeof(PyIrcConnect), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyIrcConnect objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ PyIrcConnect_getseters, /* tp_getset */
+ &PyConnectType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyObject *pyirc_connect_new(void *connect, int managed)
+{
+ return pyconnect_sub_new(connect, &PyIrcConnectType, managed);
+}
+
+int irc_connect_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyIrcConnectType) < 0)
+ return 0;
+
+ Py_INCREF(&PyIrcConnectType);
+ PyModule_AddObject(py_module, "IrcConnect", (PyObject *)&PyIrcConnectType);
+
+ return 1;
+}
diff --git a/src/objects/irc-connect-object.h b/src/objects/irc-connect-object.h
new file mode 100644
index 0000000..3f6cad4
--- /dev/null
+++ b/src/objects/irc-connect-object.h
@@ -0,0 +1,21 @@
+#ifndef _IRC_CONNECT_OBJECT_H_
+#define _IRC_CONNECT_OBJECT_H_
+
+#include <Python.h>
+#include "connect-object.h"
+
+/* forward */
+struct _IRC_SERVER_CONNECT_REC;
+
+typedef struct
+{
+ PyIrssi_HEAD(struct _IRC_SERVER_CONNECT_REC)
+} PyIrcConnect;
+
+extern PyTypeObject PyIrcConnectType;
+
+int irc_connect_object_init(void);
+PyObject *pyirc_connect_new(void *connect, int managed);
+#define pyirc_connect_check(op) PyObject_TypeCheck(op, &PyIrcConnectType)
+
+#endif
diff --git a/src/objects/irc-server-object.c b/src/objects/irc-server-object.c
new file mode 100644
index 0000000..4e9a28e
--- /dev/null
+++ b/src/objects/irc-server-object.c
@@ -0,0 +1,491 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "irc-server-object.h"
+#include "factory.h"
+#include "pyirssi_irc.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+/* cleanup and dealloc inherited from base Server */
+
+/* Getters */
+PyDoc_STRVAR(PyIrcServer_real_address_doc,
+ "Address the IRC server gives"
+);
+static PyObject *PyIrcServer_real_address_get(PyIrcServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->real_address);
+}
+
+PyDoc_STRVAR(PyIrcServer_usermode_doc,
+ "User mode in server"
+);
+static PyObject *PyIrcServer_usermode_get(PyIrcServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->usermode);
+}
+
+PyDoc_STRVAR(PyIrcServer_userhost_doc,
+ "Your user host in server"
+);
+static PyObject *PyIrcServer_userhost_get(PyIrcServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->userhost);
+}
+
+static PyGetSetDef PyIrcServer_getseters[] = {
+ {"real_address", (getter)PyIrcServer_real_address_get, NULL,
+ PyIrcServer_real_address_doc, NULL},
+ {"usermode", (getter)PyIrcServer_usermode_get, NULL,
+ PyIrcServer_usermode_doc, NULL},
+ {"userhost", (getter)PyIrcServer_userhost_get, NULL,
+ PyIrcServer_userhost_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(get_channels_doc,
+ "get_channels() -> str\n"
+ "\n"
+ "Return a string of all channels (and keys, if any have them) in server,\n"
+ "like '#a,#b,#c,#d x,b_chan_key,x,x' or just '#e,#f,#g'\n"
+);
+static PyObject *PyIrcServer_get_channels(PyIrcServer *self, PyObject *args)
+{
+ char *list;
+ PyObject *ret;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ list = irc_server_get_channels(self->data);
+ ret = PyString_FromString(list);
+ g_free(list);
+
+ return ret;
+}
+
+PyDoc_STRVAR(send_raw_doc,
+ "send_raw(cmd) -> None\n"
+ "\n"
+ "Send raw message to server, it will be flood protected so you\n"
+ "don't need to worry about it.\n"
+);
+static PyObject *PyIrcServer_send_raw(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", NULL};
+ char *cmd;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd))
+ return NULL;
+
+ irc_send_cmd(self->data, cmd);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(send_raw_now_doc,
+ "send_raw_now(cmd) -> None\n"
+ "\n"
+ "Send raw message to server immediately without flood protection.\n"
+);
+static PyObject *PyIrcServer_send_raw_now(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", NULL};
+ char *cmd;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd))
+ return NULL;
+
+ irc_send_cmd_now(self->data, cmd);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(send_raw_split_doc,
+ "send_raw_split(cmd, nickarg, max_nicks) -> None\n"
+ "\n"
+ "Split the `cmd' into several commands so `nickarg' argument has only\n"
+ "`max_nicks' number of nicks.\n"
+ "\n"
+ "Example:\n"
+ "server.send_raw_split('KICK #channel nick1,nick2,nick3 :byebye', 3, 2)\n"
+ "\n"
+ "Irssi will send commands 'KICK #channel nick1,nick2 :byebye' and\n"
+ "'KICK #channel nick3 :byebye' to server.\n"
+);
+static PyObject *PyIrcServer_send_raw_split(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", "nickarg", "max_nicks", NULL};
+ char *cmd;
+ int nickarg;
+ int max_nicks;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sii", kwlist, &cmd, &nickarg, &max_nicks))
+ return NULL;
+
+ irc_send_cmd_split(self->data, cmd, nickarg, max_nicks);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(ctcp_send_reply_doc,
+ "ctcp_send_reply(data) -> None\n"
+ "\n"
+ "Send CTCP reply. This will be 'CTCP flood protected' so if there's too\n"
+ "many CTCP requests in buffer, this reply might not get sent. The data\n"
+ "is the full raw command to be sent to server, like\n"
+ "'NOTICE nick :\001VERSION irssi\001'\n"
+);
+static PyObject *PyIrcServer_ctcp_send_reply(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"data", NULL};
+ char *data;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &data))
+ return NULL;
+
+ ctcp_send_reply(self->data, data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(isupport_doc,
+ "isupport(name) -> str or None\n"
+ "\n"
+ "Returns the value of the named item in the ISUPPORT (005) numeric to the\n"
+ "script. If the item is not present returns undef, if the item has no value\n"
+ "then '' is returned use defined server.isupport('name') if you need to\n"
+ "check whether a property is present.\n"
+ "See http://www.ietf.org/internet-drafts/draft-brocklesby-irc-isupport-01.txt\n"
+ "for more information on the ISUPPORT numeric.\n"
+);
+static PyObject *PyIrcServer_isupport(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name;
+ char *found;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &name))
+ return NULL;
+
+ found = g_hash_table_lookup(self->data->isupport, name);
+
+ RET_AS_STRING_OR_NONE(found);
+}
+
+PyDoc_STRVAR(PyIrcServer_netsplit_find_doc,
+ "netsplit_find(nick, address) -> Netsplit object or None\n"
+ "\n"
+ "Check if nick!address is on the other side of netsplit. Netsplit records\n"
+ "are automatically removed after 30 minutes (current default)..\n"
+);
+static PyObject *PyIrcServer_netsplit_find(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", "address", NULL};
+ char *nick = "";
+ char *address = "";
+ NETSPLIT_REC *ns;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &nick, &address))
+ return NULL;
+
+ ns = netsplit_find(self->data, nick, address);
+ if (ns)
+ return pynetsplit_new(ns);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyIrcServer_netsplit_find_channel_doc,
+ "netsplit_find_channel(nick, address, channel) -> NetsplitChannel object or None\n"
+ "\n"
+ "Find nick record for nick!address in channel `channel'.\n"
+);
+static PyObject *PyIrcServer_netsplit_find_channel(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", "address", "channel", NULL};
+ char *nick = "";
+ char *address = "";
+ char *channel = "";
+ NETSPLIT_CHAN_REC *nsc;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &nick, &address, &channel))
+ return NULL;
+
+ nsc = netsplit_find_channel(self->data, nick, address, channel);
+ if (nsc)
+ return pynetsplit_channel_new(nsc);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyIrcServer_notifylist_ison_doc,
+ "notifylist_ison(nick) -> bool\n"
+ "\n"
+ "Check if nick is on server\n"
+);
+static PyObject *PyIrcServer_notifylist_ison(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ char *nick = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &nick))
+ return NULL;
+
+ return PyBool_FromLong(notifylist_ison_server(self->data, nick));
+}
+
+/* expect a list of tuples [('str', 'str'), ...] */
+static GSList *py_event_conv(PyObject *list)
+{
+ int i;
+ GSList *ret = NULL;
+
+ if (!PyList_Check(list))
+ {
+ PyErr_Format(PyExc_TypeError, "expect a list of tuples of two strings");
+ return NULL;
+ }
+
+ for (i = 0; i < PyList_Size(list); i++)
+ {
+ char *key;
+ char *val;
+ PyObject *tup = PyList_GET_ITEM(list, i);
+
+ if (!PyTuple_Check(tup) || !PyArg_ParseTuple(tup, "ss", &key, &val))
+ {
+ GSList *node;
+
+ for (node = ret; node; node = node->next)
+ g_free(node->data);
+
+ g_slist_free(ret);
+
+ if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_TypeError))
+ {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError, "expect a list of tuples of two strings");
+ }
+
+ return NULL;
+ }
+
+ ret = g_slist_append(ret, g_strdup(key));
+ ret = g_slist_append(ret, g_strdup(val));
+ }
+
+ return ret;
+}
+
+PyDoc_STRVAR(PyIrcServer_redirect_event_doc,
+ "redirect_event(command, signals, arg=None, count=1, remote=-1, failure_signal=None) -> None\n"
+ "\n"
+ "Specify that the next command sent to server will be redirected.\n"
+ "NOTE: This command MUST be called before sending the command to server.\n"
+ "\n"
+ "`command' - Name of the registered redirection that we're using.\n"
+ "\n"
+ "`count' - How many times to execute the redirection. Some commands may\n"
+ "send multiple stop events, like MODE #a,#b.\n"
+ "\n"
+ "`arg' - The argument to be compared in event strings. You can give multiple\n"
+ "arguments separated with space.\n"
+ "\n"
+ "`remote' - Specifies if the command is a remote command, -1 = use default.\n"
+ "\n"
+ "`failure_signal' - If irssi can't find the stop signal for the redirection,\n"
+ "this signal is called.\n"
+ "\n"
+ "`signals' - hash reference with \"event\" => \"redir signal\" entries.\n"
+ "If the event is "", all the events belonging to the redirection but not\n"
+ "specified here, will be sent there.\n"
+ "\n"
+ "Example:\n"
+ "\n"
+ "# ignore all events generated by whois query, except 311.\n"
+ "\n"
+ "server.redirect_event(\"whois\",\n"
+ " remote = 0,\n"
+ " arg = \"cras\",\n"
+ " signals = [\n"
+ " ('event 311', 'redir whois'),\n"
+ " ('', 'event empty') \n"
+ " ]\n"
+ ")\n"
+ "server.send_raw(\"WHOIS :cras\")\n"
+);
+static PyObject *PyIrcServer_redirect_event(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"command", "signals", "arg", "count", "remote", "failure_signal", NULL};
+ char *command = "";
+ int count = 1;
+ char *arg = NULL;
+ int remote = -1;
+ char *failure_signal = NULL;
+ PyObject *signals = NULL;
+ GSList *gsignals;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|ziiz", kwlist,
+ &command, &signals, &arg, &count, &remote, &failure_signal))
+ return NULL;
+
+ gsignals = py_event_conv(signals);
+ if (!gsignals)
+ return NULL;
+
+ server_redirect_event(self->data, command, count, arg, remote, failure_signal, gsignals);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyIrcServer_redirect_get_signal_doc,
+ "redirect_get_signal(event, args) -> str\n"
+);
+static PyObject *PyIrcServer_redirect_get_signal(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"event", "args", NULL};
+ char *event = "";
+ char *pargs = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &event, &pargs))
+ return NULL;
+
+ RET_AS_STRING_OR_NONE(server_redirect_get_signal(self->data, event, pargs));
+}
+
+PyDoc_STRVAR(PyIrcServer_redirect_peek_signal_doc,
+ "redirect_peek_signal(event, args) -> str\n"
+);
+static PyObject *PyIrcServer_redirect_peek_signal(PyIrcServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"event", "args", NULL};
+ char *event = "";
+ char *pargs = "";
+ int redirection;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &event, &pargs))
+ return NULL;
+
+ RET_AS_STRING_OR_NONE(server_redirect_peek_signal(self->data, event, pargs, &redirection));
+}
+
+/* Methods for object */
+static PyMethodDef PyIrcServer_methods[] = {
+ {"get_channels", (PyCFunction)PyIrcServer_get_channels, METH_NOARGS,
+ get_channels_doc},
+ {"send_raw", (PyCFunction)PyIrcServer_send_raw, METH_VARARGS | METH_KEYWORDS,
+ send_raw_doc},
+ {"send_raw_now", (PyCFunction)PyIrcServer_send_raw_now, METH_VARARGS | METH_KEYWORDS,
+ send_raw_now_doc},
+ {"send_raw_split", (PyCFunction)PyIrcServer_send_raw_split, METH_VARARGS | METH_KEYWORDS,
+ send_raw_split_doc},
+ {"ctcp_send_reply", (PyCFunction)PyIrcServer_ctcp_send_reply, METH_VARARGS | METH_KEYWORDS,
+ ctcp_send_reply_doc},
+ {"isupport", (PyCFunction)PyIrcServer_isupport, METH_VARARGS | METH_KEYWORDS,
+ isupport_doc},
+ {"netsplit_find", (PyCFunction)PyIrcServer_netsplit_find, METH_VARARGS | METH_KEYWORDS,
+ PyIrcServer_netsplit_find_doc},
+ {"netsplit_find_channel", (PyCFunction)PyIrcServer_netsplit_find_channel, METH_VARARGS | METH_KEYWORDS,
+ PyIrcServer_netsplit_find_channel_doc},
+ {"notifylist_ison", (PyCFunction)PyIrcServer_notifylist_ison, METH_VARARGS | METH_KEYWORDS,
+ PyIrcServer_notifylist_ison_doc},
+ {"redirect_event", (PyCFunction)PyIrcServer_redirect_event, METH_VARARGS | METH_KEYWORDS,
+ PyIrcServer_redirect_event_doc},
+ {"redirect_get_signal", (PyCFunction)PyIrcServer_redirect_get_signal, METH_VARARGS | METH_KEYWORDS,
+ PyIrcServer_redirect_get_signal_doc},
+ {"redirect_peek_signal", (PyCFunction)PyIrcServer_redirect_peek_signal, METH_VARARGS | METH_KEYWORDS,
+ PyIrcServer_redirect_peek_signal_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyIrcServerType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "IrcServer", /*tp_name*/
+ sizeof(PyIrcServer), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyIrcServer objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyIrcServer_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyIrcServer_getseters, /* tp_getset */
+ &PyServerType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyObject *pyirc_server_new(void *server)
+{
+ return pyserver_sub_new(server, &PyIrcServerType);
+}
+
+int irc_server_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyIrcServerType) < 0)
+ return 0;
+
+ Py_INCREF(&PyIrcServerType);
+ PyModule_AddObject(py_module, "IrcServer", (PyObject *)&PyIrcServerType);
+
+ return 1;
+}
diff --git a/src/objects/irc-server-object.h b/src/objects/irc-server-object.h
new file mode 100644
index 0000000..b3eb76a
--- /dev/null
+++ b/src/objects/irc-server-object.h
@@ -0,0 +1,21 @@
+#ifndef _IRC_SERVER_OBJECT_H_
+#define _IRC_SERVER_OBJECT_H_
+
+#include <Python.h>
+#include "server-object.h"
+
+/* forward */
+struct _IRC_SERVER_REC;
+
+typedef struct
+{
+ PyServer_HEAD(struct _IRC_SERVER_REC)
+} PyIrcServer;
+
+extern PyTypeObject PyIrcServerType;
+
+int irc_server_object_init(void);
+PyObject *pyirc_server_new(void *server);
+#define pyirc_server_check(op) PyObject_TypeCheck(op, &PyIrcServerType)
+
+#endif
diff --git a/src/objects/log-object.c b/src/objects/log-object.c
new file mode 100644
index 0000000..b18f826
--- /dev/null
+++ b/src/objects/log-object.c
@@ -0,0 +1,477 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "log-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+static LOG_ITEM_REC *find_item(LOG_REC *log, PyLogitem *item);
+static void log_cleanup(LOG_REC *log);
+static int logtype(int *type, int target, int window);
+
+/* find/convert a py log item */
+static LOG_ITEM_REC *find_item(LOG_REC *log, PyLogitem *item)
+{
+ int type;
+ char *name;
+ char *servertag = NULL;
+
+ if (!item->type || !item->name)
+ return NULL;
+
+ type = PyInt_AS_LONG(item->type);
+ name = PyString_AS_STRING(item->name);
+ if (item->servertag)
+ servertag = PyString_AS_STRING(item->servertag);
+
+ return log_item_find(log, type, name, servertag);
+}
+
+/* monitor "log remove" signal */
+static void log_cleanup(LOG_REC *log)
+{
+ PyLog *pylog = signal_get_user_data();
+
+ if (log == pylog->data)
+ {
+ pylog->data = NULL;
+ pylog->cleanup_installed = 0;
+ signal_remove_data("log remove", log_cleanup, pylog);
+ }
+}
+
+static void PyLog_dealloc(PyLog *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("log remove", log_cleanup, self);
+
+ if (self->data && !g_slist_find(logs, self->data))
+ {
+ printtext(NULL, NULL, MSGLEVEL_CRAP, "destroying orphan log %s", self->data->fname);
+ log_close(self->data);
+ }
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyLog_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyLog *self;
+
+ self = (PyLog *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* function to create the log */
+PyDoc_STRVAR(PyLog_doc,
+ "__init__(fname, level=MSGLEVEL_ALL)\n"
+ "\n"
+ "Create a log\n"
+);
+static int PyLog_init(PyLog *self, PyObject *args, PyObject *kwds)
+{
+ char *fname;
+ int level = MSGLEVEL_ALL;
+ LOG_REC *log;
+
+ static char *kwlist[] = {"fname", "level", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist,
+ &fname, &level))
+ return -1;
+
+ /*XXX: anything better than RuntimeError ? */
+ if (self->data || self->cleanup_installed)
+ {
+ PyErr_Format(PyExc_RuntimeError, "log already opened; close it first");
+ return -1;
+ }
+
+ log = log_create_rec(fname, level);
+ if (!log)
+ {
+ PyErr_Format(PyExc_RuntimeError, "failed to create log");
+ return -1;
+ }
+
+ self->data = log;
+ self->cleanup_installed = 1;
+ signal_add_last_data("log remove", log_cleanup, self);
+
+ return 0;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyLog_fname_doc,
+ "Log file name"
+);
+static PyObject *PyLog_fname_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->fname);
+}
+
+PyDoc_STRVAR(PyLog_real_fname_doc,
+ "The actual opened log file (after %d.%m.Y etc. are expanded)"
+);
+static PyObject *PyLog_real_fname_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->real_fname);
+}
+
+PyDoc_STRVAR(PyLog_opened_doc,
+ "Log file is open"
+);
+static PyObject *PyLog_opened_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(self->data->opened);
+}
+
+PyDoc_STRVAR(PyLog_level_doc,
+ "Log only these levels"
+);
+static PyObject *PyLog_level_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->level);
+}
+
+PyDoc_STRVAR(PyLog_last_doc,
+ "Timestamp when last message was written"
+);
+static PyObject *PyLog_last_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(self->data->last);
+}
+
+PyDoc_STRVAR(PyLog_autoopen_doc,
+ "Automatically open log at startup"
+);
+static PyObject *PyLog_autoopen_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->autoopen);
+}
+
+PyDoc_STRVAR(PyLog_failed_doc,
+ "Opening log failed last time"
+);
+static PyObject *PyLog_failed_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->failed);
+}
+
+PyDoc_STRVAR(PyLog_temp_doc,
+ "Log isn't saved to config file"
+);
+static PyObject *PyLog_temp_get(PyLog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->temp);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyLog_getseters[] = {
+ {"fname", (getter)PyLog_fname_get, NULL,
+ PyLog_fname_doc, NULL},
+ {"real_fname", (getter)PyLog_real_fname_get, NULL,
+ PyLog_real_fname_doc, NULL},
+ {"opened", (getter)PyLog_opened_get, NULL,
+ PyLog_opened_doc, NULL},
+ {"level", (getter)PyLog_level_get, NULL,
+ PyLog_level_doc, NULL},
+ {"last", (getter)PyLog_last_get, NULL,
+ PyLog_last_doc, NULL},
+ {"autoopen", (getter)PyLog_autoopen_get, NULL,
+ PyLog_autoopen_doc, NULL},
+ {"failed", (getter)PyLog_failed_get, NULL,
+ PyLog_failed_doc, NULL},
+ {"temp", (getter)PyLog_temp_get, NULL,
+ PyLog_temp_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyLog_items_doc,
+ "items() -> list of Log objects\n"
+ "\n"
+ "Return a list of log items\n"
+);
+static PyObject *PyLog_items(PyLog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return py_irssi_objlist_new(self->data->items, 1, (InitFunc)pylogitem_new);
+}
+
+PyDoc_STRVAR(PyLog_update_doc,
+ "update() -> None\n"
+ "\n"
+ "Add log to list of logs / save changes to config file.\n"
+);
+static PyObject *PyLog_update(PyLog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ log_update(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyLog_close_doc,
+ "destroy() -> None\n"
+ "\n"
+ "Destroy the log file\n"
+);
+static PyObject *PyLog_close(PyLog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ log_close(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyLog_start_logging_doc,
+ "start_logging() -> None\n"
+ "\n"
+ "Open log file and start logging.\n"
+);
+static PyObject *PyLog_start_logging(PyLog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ log_start_logging(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyLog_stop_logging_doc,
+ "stop_logging() -> None\n"
+ "\n"
+ "Stop and close the log file.\n"
+);
+static PyObject *PyLog_stop_logging(PyLog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ log_stop_logging(self->data);
+
+ Py_RETURN_NONE;
+}
+
+static int logtype(int *type, int target, int window)
+{
+ if (target || window)
+ {
+ if (target && window)
+ {
+ PyErr_SetString(PyExc_TypeError, "must specify target or window, not both");
+ return 0;
+ }
+
+ *type = target? 0 : 1;
+ }
+ else if (*type < 0)
+ {
+ PyErr_SetString(PyExc_TypeError, "must specify type, target, or window");
+ return 0;
+ }
+
+ return 1;
+}
+
+PyDoc_STRVAR(PyLog_item_add_doc,
+ "item_add(item, servertag=None, type=0, target=False, window=False) -> None\n"
+ "\n"
+ "Add a log item to log.\n"
+ "\n"
+ "Add a target item (nick, chan): \n"
+ " item_add('#linux', target=True)\n"
+ " item_add('#linux', type=0)\n"
+ "\n"
+ "Add a window ref: \n"
+ " item_add('2', window=True)\n"
+ " item_add('2', type=1)\n"
+);
+static PyObject *PyLog_item_add(PyLog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", "servertag", "type", "target", "window", NULL};
+ char *item = "";
+ char *servertag = NULL;
+ int type = 0;
+ int target = 0;
+ int window = 0;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ziii", kwlist,
+ &item, &servertag, &type, &target, &window))
+ return NULL;
+
+ if (!logtype(&type, target, window))
+ return NULL;
+
+ log_item_add(self->data, type, item, servertag);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyLog_item_destroy_doc,
+ "item_destroy(item) -> None\n"
+ "\n"
+ "Remove log item from log.\n"
+);
+static PyObject *PyLog_item_destroy(PyLog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", NULL};
+ PyObject *item = NULL;
+ LOG_ITEM_REC *li;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &item))
+ return NULL;
+
+ if (!pylogitem_check(item))
+ return PyErr_Format(PyExc_TypeError, "arg 1 should be log item");
+
+ li = find_item(self->data, (PyLogitem *)item);
+ if (!li)
+ return PyErr_Format(PyExc_TypeError, "log item invalid or not found");
+
+ log_item_destroy(self->data, li);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyLog_item_find_doc,
+ "item_find(item, servertag=None, type=-1, target=False, window=False) -> item or None\n"
+ "\n"
+ "Find item from log.\n"
+);
+static PyObject *PyLog_item_find(PyLog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", "servertag", "type", "target", "window", NULL};
+ char *item = "";
+ char *server = NULL;
+ int type = 0;
+ int target = 0;
+ int window = 0;
+ LOG_ITEM_REC *li;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ziii", kwlist,
+ &item, &server, &type, &target, &window))
+ return NULL;
+
+ if (!logtype(&type, target, window))
+ return NULL;
+
+ li = log_item_find(self->data, type, item, server);
+ if (li)
+ return pylogitem_new(li);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyLog_methods[] = {
+ {"items", (PyCFunction)PyLog_items, METH_NOARGS,
+ PyLog_items_doc},
+ {"update", (PyCFunction)PyLog_update, METH_NOARGS,
+ PyLog_update_doc},
+ {"close", (PyCFunction)PyLog_close, METH_NOARGS,
+ PyLog_close_doc},
+ {"start_logging", (PyCFunction)PyLog_start_logging, METH_NOARGS,
+ PyLog_start_logging_doc},
+ {"stop_logging", (PyCFunction)PyLog_stop_logging, METH_NOARGS,
+ PyLog_stop_logging_doc},
+ {"item_add", (PyCFunction)PyLog_item_add, METH_VARARGS | METH_KEYWORDS,
+ PyLog_item_add_doc},
+ {"item_destroy", (PyCFunction)PyLog_item_destroy, METH_VARARGS | METH_KEYWORDS,
+ PyLog_item_destroy_doc},
+ {"item_find", (PyCFunction)PyLog_item_find, METH_VARARGS | METH_KEYWORDS,
+ PyLog_item_find_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyLogType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Log", /*tp_name*/
+ sizeof(PyLog), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyLog_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ PyLog_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyLog_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyLog_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)PyLog_init, /* tp_init */
+ 0, /* tp_alloc */
+ PyLog_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pylog_new(void *log)
+{
+ PyLog *pylog;
+
+ pylog = (PyLog *)PyLogType.tp_alloc(&PyLogType, 0);
+ if (!pylog)
+ return NULL;
+
+ pylog->data = log;
+ pylog->cleanup_installed = 1;
+ signal_add_last_data("log remove", log_cleanup, pylog);
+
+ return (PyObject *)pylog;
+}
+
+int log_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyLogType) < 0)
+ return 0;
+
+ Py_INCREF(&PyLogType);
+ PyModule_AddObject(py_module, "Log", (PyObject *)&PyLogType);
+
+ return 1;
+}
diff --git a/src/objects/log-object.h b/src/objects/log-object.h
new file mode 100644
index 0000000..9893bc5
--- /dev/null
+++ b/src/objects/log-object.h
@@ -0,0 +1,21 @@
+#ifndef _LOG_OBJECT_H_
+#define _LOG_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _LOG_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct _LOG_REC)
+} PyLog;
+
+extern PyTypeObject PyLogType;
+
+int log_object_init(void);
+PyObject *pylog_new(void *log);
+#define pylog_check(op) PyObject_TypeCheck(op, &PyLogType)
+
+#endif
diff --git a/src/objects/logitem-object.c b/src/objects/logitem-object.c
new file mode 100644
index 0000000..4dc2275
--- /dev/null
+++ b/src/objects/logitem-object.c
@@ -0,0 +1,156 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "logitem-object.h"
+#include "pycore.h"
+
+/* no special cleanup -- value copy is made */
+
+static void PyLogitem_dealloc(PyLogitem *self)
+{
+ Py_XDECREF(self->type);
+ Py_XDECREF(self->name);
+ Py_XDECREF(self->servertag);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyLogitem_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyLogitem *self;
+
+ self = (PyLogitem *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyLogitem_type_doc,
+ "0=target, 1=window refnum"
+);
+static PyObject *PyLogitem_type_get(PyLogitem *self, void *closure)
+{
+ RET_AS_OBJ_OR_NONE(self->type);
+}
+
+PyDoc_STRVAR(PyLogitem_name_doc,
+ "Name"
+);
+static PyObject *PyLogitem_name_get(PyLogitem *self, void *closure)
+{
+ RET_AS_OBJ_OR_NONE(self->name);
+}
+
+PyDoc_STRVAR(PyLogitem_servertag_doc,
+ "Server tag"
+);
+static PyObject *PyLogitem_servertag_get(PyLogitem *self, void *closure)
+{
+ RET_AS_OBJ_OR_NONE(self->servertag);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyLogitem_getseters[] = {
+ {"type", (getter)PyLogitem_type_get, NULL,
+ PyLogitem_type_doc, NULL},
+ {"name", (getter)PyLogitem_name_get, NULL,
+ PyLogitem_name_doc, NULL},
+ {"servertag", (getter)PyLogitem_servertag_get, NULL,
+ PyLogitem_servertag_doc, NULL},
+ {NULL}
+};
+
+/* Methods for object */
+static PyMethodDef PyLogitem_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyLogitemType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Logitem", /*tp_name*/
+ sizeof(PyLogitem), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyLogitem_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyLogitem objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyLogitem_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyLogitem_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyLogitem_new, /* tp_new */
+};
+
+
+/* log item factory function */
+PyObject *pylogitem_new(void *log)
+{
+ LOG_ITEM_REC *li = log;
+ PyLogitem *pylog = NULL;
+
+ pylog = py_inst(PyLogitem, PyLogitemType);
+ if (!pylog)
+ return NULL;
+
+ pylog->type = PyInt_FromLong(li->type);
+ if (!pylog->type)
+ goto error;
+
+ pylog->name = PyString_FromString(li->name);
+ if (!pylog->name)
+ goto error;
+
+ if (li->servertag)
+ {
+ pylog->servertag = PyString_FromString(li->servertag);
+ if (!pylog->servertag)
+ goto error;
+ }
+
+ return (PyObject *)pylog;
+
+error:
+ Py_XDECREF(pylog);
+ return NULL;
+}
+
+int logitem_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyLogitemType) < 0)
+ return 0;
+
+ Py_INCREF(&PyLogitemType);
+ PyModule_AddObject(py_module, "Logitem", (PyObject *)&PyLogitemType);
+
+ return 1;
+}
diff --git a/src/objects/logitem-object.h b/src/objects/logitem-object.h
new file mode 100644
index 0000000..b7fd588
--- /dev/null
+++ b/src/objects/logitem-object.h
@@ -0,0 +1,21 @@
+#ifndef _LOG_ITEM_OBJECT_H_
+#define _LOG_ITEM_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyObject_HEAD
+ PyObject *type;
+ PyObject *name;
+ PyObject *servertag;
+} PyLogitem;
+
+extern PyTypeObject PyLogitemType;
+
+int logitem_object_init(void);
+PyObject *pylogitem_new(void *log);
+#define pylogitem_check(op) PyObject_TypeCheck(op, &PyLogitemType)
+
+#endif
diff --git a/src/objects/main-window-object.c b/src/objects/main-window-object.c
new file mode 100644
index 0000000..6541f2b
--- /dev/null
+++ b/src/objects/main-window-object.c
@@ -0,0 +1,199 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "main-window-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+#define MW(data) ((MAIN_WINDOW_REC *) data)
+
+/* monitor "mainwindow destroyed" signal */
+static void main_window_cleanup(MAIN_WINDOW_REC *mw)
+{
+ PyMainWindow *pymw = signal_get_user_data();
+
+ if (mw == pymw->data)
+ {
+ pymw->data = NULL;
+ pymw->cleanup_installed = 0;
+ signal_remove_data("mainwindow destroyed", main_window_cleanup, pymw);
+ }
+}
+
+static void PyMainWindow_dealloc(PyMainWindow *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("mainwindow destroyed", main_window_cleanup, self);
+
+ Py_XDECREF(self->active);
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyMainWindow_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyMainWindow *self;
+
+ self = (PyMainWindow *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* getters */
+PyDoc_STRVAR(PyMainWindow_active_doc,
+ "active window object"
+);
+static PyObject *PyMainWindow_active_get(PyMainWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->active);
+}
+
+PyDoc_STRVAR(PyMainWindow_first_line_doc,
+ "first line used by this window (0..x) (includes statusbars)"
+);
+static PyObject *PyMainWindow_first_line_get(PyMainWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(MW(self->data)->first_line);
+}
+
+PyDoc_STRVAR(PyMainWindow_last_line_doc,
+ "last line used by this window (0..x) (includes statusbars)"
+);
+static PyObject *PyMainWindow_last_line_get(PyMainWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(MW(self->data)->last_line);
+}
+
+PyDoc_STRVAR(PyMainWindow_width_doc,
+ "width of the window (includes statusbars)"
+);
+static PyObject *PyMainWindow_width_get(PyMainWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(MW(self->data)->width);
+}
+
+PyDoc_STRVAR(PyMainWindow_height_doc,
+ "height of the window (includes statusbars)"
+);
+static PyObject *PyMainWindow_height_get(PyMainWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(MW(self->data)->height);
+}
+
+PyDoc_STRVAR(PyMainWindow_statusbar_lines_doc,
+ "???"
+);
+static PyObject *PyMainWindow_statusbar_lines_get(PyMainWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(MW(self->data)->statusbar_lines);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyMainWindow_getseters[] = {
+ {"active", (getter)PyMainWindow_active_get, NULL,
+ PyMainWindow_active_doc, NULL},
+ {"first_line", (getter)PyMainWindow_first_line_get, NULL,
+ PyMainWindow_first_line_doc, NULL},
+ {"last_line", (getter)PyMainWindow_last_line_get, NULL,
+ PyMainWindow_last_line_doc, NULL},
+ {"width", (getter)PyMainWindow_width_get, NULL,
+ PyMainWindow_width_doc, NULL},
+ {"height", (getter)PyMainWindow_height_get, NULL,
+ PyMainWindow_height_doc, NULL},
+ {"statusbar_lines", (getter)PyMainWindow_statusbar_lines_get, NULL,
+ PyMainWindow_statusbar_lines_doc, NULL},
+ {NULL}
+};
+
+/* Methods for object */
+static PyMethodDef PyMainWindow_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyMainWindowType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "MainWindow", /*tp_name*/
+ sizeof(PyMainWindow), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyMainWindow_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyMainWindow objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyMainWindow_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyMainWindow_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyMainWindow_new, /* tp_new */
+};
+
+
+/* main window wrapper factory function */
+PyObject *pymain_window_new(MAIN_WINDOW_REC *mw)
+{
+ PyObject *pyactive = NULL;
+ PyMainWindow *pymw;
+
+ pyactive = pywindow_new(mw->active);
+ if (!pyactive)
+ return NULL;
+
+ pymw = py_inst(PyMainWindow, PyMainWindowType);
+ if (!pymw)
+ {
+ Py_DECREF(pyactive);
+ return NULL;
+ }
+
+ pymw->active = pyactive;
+ pymw->data = mw;
+ pymw->cleanup_installed = 1;
+ signal_add_last_data("mainwindow destroyed", main_window_cleanup, pymw);
+
+ return (PyObject *)pymw;
+}
+
+int main_window_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyMainWindowType) < 0)
+ return 0;
+
+ Py_INCREF(&PyMainWindowType);
+ PyModule_AddObject(py_module, "MainWindow", (PyObject *)&PyMainWindowType);
+
+ return 1;
+}
diff --git a/src/objects/main-window-object.h b/src/objects/main-window-object.h
new file mode 100644
index 0000000..fbbdac0
--- /dev/null
+++ b/src/objects/main-window-object.h
@@ -0,0 +1,20 @@
+#ifndef _MAIN_WINDOW_OBJECT_H_
+#define _MAIN_WINDOW_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+#include "pyirssi.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+ PyObject *active;
+} PyMainWindow;
+
+extern PyTypeObject PyMainWindowType;
+
+int main_window_object_init(void);
+PyObject *pymain_window_new(MAIN_WINDOW_REC *mw);
+#define pymain_window_check(op) PyObject_TypeCheck(op, &PyMainWindowType)
+
+#endif
diff --git a/src/objects/netsplit-channel-object.c b/src/objects/netsplit-channel-object.c
new file mode 100644
index 0000000..3e2639b
--- /dev/null
+++ b/src/objects/netsplit-channel-object.c
@@ -0,0 +1,170 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "netsplit-channel-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+/* value copied -- no special cleanup */
+
+static void PyNetsplitChannel_dealloc(PyNetsplitChannel *self)
+{
+ Py_XDECREF(self->name);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyNetsplitChannel_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyNetsplitChannel *self;
+
+ self = (PyNetsplitChannel *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyNetsplitChannel_name_doc,
+ "Channel name"
+);
+static PyObject *PyNetsplitChannel_name_get(PyNetsplitChannel *self, void *closure)
+{
+ RET_AS_OBJ_OR_NONE(self->name);
+}
+
+PyDoc_STRVAR(PyNetsplitChannel_op_doc,
+ "is op"
+);
+static PyObject *PyNetsplitChannel_op_get(PyNetsplitChannel *self, void *closure)
+{
+ return PyBool_FromLong(self->op);
+}
+
+PyDoc_STRVAR(PyNetsplitChannel_halfop_doc,
+ "is halfop"
+);
+static PyObject *PyNetsplitChannel_halfop_get(PyNetsplitChannel *self, void *closure)
+{
+ return PyBool_FromLong(self->halfop);
+}
+
+PyDoc_STRVAR(PyNetsplitChannel_voice_doc,
+ "is voice"
+);
+static PyObject *PyNetsplitChannel_voice_get(PyNetsplitChannel *self, void *closure)
+{
+ return PyBool_FromLong(self->voice);
+}
+
+PyDoc_STRVAR(PyNetsplitChannel_other_doc,
+ "?"
+);
+static PyObject *PyNetsplitChannel_other_get(PyNetsplitChannel *self, void *closure)
+{
+ return PyInt_FromLong(self->other);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyNetsplitChannel_getseters[] = {
+ {"name", (getter)PyNetsplitChannel_name_get, NULL,
+ PyNetsplitChannel_name_doc, NULL},
+ {"op", (getter)PyNetsplitChannel_op_get, NULL,
+ PyNetsplitChannel_op_doc, NULL},
+ {"halfop", (getter)PyNetsplitChannel_halfop_get, NULL,
+ PyNetsplitChannel_halfop_doc, NULL},
+ {"voice", (getter)PyNetsplitChannel_voice_get, NULL,
+ PyNetsplitChannel_voice_doc, NULL},
+ {"other", (getter)PyNetsplitChannel_other_get, NULL,
+ PyNetsplitChannel_other_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+/* Methods for object */
+static PyMethodDef PyNetsplitChannel_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyNetsplitChannelType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "NetsplitChannel", /*tp_name*/
+ sizeof(PyNetsplitChannel), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyNetsplitChannel_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyNetsplitChannel objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyNetsplitChannel_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyNetsplitChannel_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyNetsplitChannel_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pynetsplit_channel_new(void *netsplit)
+{
+ NETSPLIT_CHAN_REC *rec = netsplit;
+ PyNetsplitChannel *pynetsplit;
+ PyObject *name;
+
+ name = PyString_FromString(rec->name);
+ if (!name)
+ return NULL;
+
+ pynetsplit = py_inst(PyNetsplitChannel, PyNetsplitChannelType);
+ if (!pynetsplit)
+ {
+ Py_DECREF(name);
+ return NULL;
+ }
+
+ pynetsplit->name = name;
+ pynetsplit->op = rec->op;
+ pynetsplit->halfop = rec->halfop;
+ pynetsplit->other = rec->other;
+
+ return (PyObject *)pynetsplit;
+}
+
+int netsplit_channel_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyNetsplitChannelType) < 0)
+ return 0;
+
+ Py_INCREF(&PyNetsplitChannelType);
+ PyModule_AddObject(py_module, "NetsplitChannel", (PyObject *)&PyNetsplitChannelType);
+
+ return 1;
+}
diff --git a/src/objects/netsplit-channel-object.h b/src/objects/netsplit-channel-object.h
new file mode 100644
index 0000000..3a00fbb
--- /dev/null
+++ b/src/objects/netsplit-channel-object.h
@@ -0,0 +1,21 @@
+#ifndef _NETSPLIT_CHANNEL_OBJECT_H_
+#define _NETSPLIT_CHANNEL_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyObject_HEAD
+ PyObject *name;
+ int op, halfop;
+ int voice, other;
+} PyNetsplitChannel;
+
+extern PyTypeObject PyNetsplitChannelType;
+
+int netsplit_channel_object_init(void);
+PyObject *pynetsplit_channel_new(void *ns);
+#define pynetsplit_channel_check(op) PyObject_TypeCheck(op, &PyNetsplitChannelType)
+
+#endif
diff --git a/src/objects/netsplit-object.c b/src/objects/netsplit-object.c
new file mode 100644
index 0000000..d64bd97
--- /dev/null
+++ b/src/objects/netsplit-object.c
@@ -0,0 +1,184 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "netsplit-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+#define NETSPLIT(ns) ((NETSPLIT_REC*)ns)
+
+/* monitor "netsplit remove" signal */
+static void netsplit_cleanup(NETSPLIT_REC *netsplit)
+{
+ PyNetsplit *pynetsplit = signal_get_user_data();
+
+ if (netsplit == pynetsplit->data)
+ {
+ pynetsplit->data = NULL;
+ pynetsplit->cleanup_installed = 0;
+ signal_remove_data("netsplit remove", netsplit_cleanup, pynetsplit);
+ }
+}
+
+static void PyNetsplit_dealloc(PyNetsplit *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("netsplit remove", netsplit_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyNetsplit_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyNetsplit *self;
+
+ self = (PyNetsplit *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyNetsplit_nick_doc,
+ "Nick"
+);
+static PyObject *PyNetsplit_nick_get(PyNetsplit *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(NETSPLIT(self->data)->nick);
+}
+
+PyDoc_STRVAR(PyNetsplit_address_doc,
+ "Nick's host"
+);
+static PyObject *PyNetsplit_address_get(PyNetsplit *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(NETSPLIT(self->data)->address);
+}
+
+PyDoc_STRVAR(PyNetsplit_destroy_doc,
+ "Timestamp when this record should be destroyed"
+);
+static PyObject *PyNetsplit_destroy_get(PyNetsplit *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(NETSPLIT(self->data)->destroy);
+}
+
+PyDoc_STRVAR(PyNetsplit_server_doc,
+ "Netsplitserver object"
+);
+static PyObject *PyNetsplit_server_get(PyNetsplit *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->server);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyNetsplit_getseters[] = {
+ {"nick", (getter)PyNetsplit_nick_get, NULL,
+ PyNetsplit_nick_doc, NULL},
+ {"address", (getter)PyNetsplit_address_get, NULL,
+ PyNetsplit_address_doc, NULL},
+ {"destroy", (getter)PyNetsplit_destroy_get, NULL,
+ PyNetsplit_destroy_doc, NULL},
+ {"server", (getter)PyNetsplit_server_get, NULL,
+ PyNetsplit_server_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyNetsplit_channels_doc,
+ "channels() -> list of NetsplitChannel objects\n"
+ "\n"
+ "Return list of NetsplitChannel objects\n"
+);
+static PyObject *PyNetsplit_channels(PyNetsplit *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return py_irssi_objlist_new(NETSPLIT(self->data)->channels, 1,
+ (InitFunc)pynetsplit_channel_new);
+}
+
+/* Methods for object */
+static PyMethodDef PyNetsplit_methods[] = {
+ {"channels", (PyCFunction)PyNetsplit_channels, METH_NOARGS,
+ PyNetsplit_channels_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyNetsplitType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Netsplit", /*tp_name*/
+ sizeof(PyNetsplit), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyNetsplit_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyNetsplit objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyNetsplit_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyNetsplit_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyNetsplit_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pynetsplit_new(void *netsplit)
+{
+ PyNetsplit *pynetsplit;
+
+ //FIXME: add netsplit server
+
+ pynetsplit = py_inst(PyNetsplit, PyNetsplitType);
+ if (!pynetsplit)
+ return NULL;
+
+ pynetsplit->data = netsplit;
+ pynetsplit->cleanup_installed = 1;
+ signal_add_last_data("netsplit remove", netsplit_cleanup, pynetsplit);
+
+ return (PyObject *)pynetsplit;
+}
+
+int netsplit_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyNetsplitType) < 0)
+ return 0;
+
+ Py_INCREF(&PyNetsplitType);
+ PyModule_AddObject(py_module, "Netsplit", (PyObject *)&PyNetsplitType);
+
+ return 1;
+}
diff --git a/src/objects/netsplit-object.h b/src/objects/netsplit-object.h
new file mode 100644
index 0000000..bd166e0
--- /dev/null
+++ b/src/objects/netsplit-object.h
@@ -0,0 +1,19 @@
+#ifndef _NETSPLIT_OBJECT_H_
+#define _NETSPLIT_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+ PyObject *server;
+} PyNetsplit;
+
+extern PyTypeObject PyNetsplitType;
+
+int netsplit_object_init(void);
+PyObject *pynetsplit_new(void *ns);
+#define pynetsplit_check(op) PyObject_TypeCheck(op, &PyNetsplitType)
+
+#endif
diff --git a/src/objects/netsplit-server-object.c b/src/objects/netsplit-server-object.c
new file mode 100644
index 0000000..7208ae1
--- /dev/null
+++ b/src/objects/netsplit-server-object.c
@@ -0,0 +1,157 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "netsplit-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+#define NETSPLIT_SERVER(ns) ((NETSPLIT_SERVER_REC*)ns)
+
+/* monitor "netsplit remove" signal */
+static void netsplit_server_cleanup(NETSPLIT_SERVER_REC *netsplit)
+{
+ PyNetsplitServer *pynetsplit = signal_get_user_data();
+
+ if (netsplit == pynetsplit->data)
+ {
+ pynetsplit->data = NULL;
+ pynetsplit->cleanup_installed = 0;
+ signal_remove_data("netsplit remove", netsplit_server_cleanup, pynetsplit);
+ }
+}
+
+static void PyNetsplitServer_dealloc(PyNetsplitServer *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("netsplit remove", netsplit_server_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyNetsplitServer_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyNetsplitServer *self;
+
+ self = (PyNetsplitServer *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyNetsplitServer_server_doc,
+ "The server nick was in"
+);
+static PyObject *PyNetsplitServer_server_get(PyNetsplitServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(NETSPLIT_SERVER(self->data)->server);
+}
+
+PyDoc_STRVAR(PyNetsplitServer_destserver_doc,
+ "The other server where split occured."
+);
+static PyObject *PyNetsplitServer_destserver_get(PyNetsplitServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(NETSPLIT_SERVER(self->data)->destserver);
+}
+
+PyDoc_STRVAR(PyNetsplitServer_count_doc,
+ "Number of splits in server"
+);
+static PyObject *PyNetsplitServer_count_get(PyNetsplitServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(NETSPLIT_SERVER(self->data)->count);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyNetsplitServer_getseters[] = {
+ {"server", (getter)PyNetsplitServer_server_get, NULL,
+ PyNetsplitServer_server_doc, NULL},
+ {"destserver", (getter)PyNetsplitServer_destserver_get, NULL,
+ PyNetsplitServer_destserver_doc, NULL},
+ {"count", (getter)PyNetsplitServer_count_get, NULL,
+ PyNetsplitServer_count_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+/* Methods for object */
+static PyMethodDef PyNetsplitServer_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyNetsplitServerType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "NetsplitServer", /*tp_name*/
+ sizeof(PyNetsplitServer), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyNetsplitServer_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyNetsplitServer objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyNetsplitServer_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyNetsplitServer_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyNetsplitServer_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pynetsplit_server_new(void *nss)
+{
+ PyNetsplitServer *pynss;
+
+ pynss = py_inst(PyNetsplitServer, PyNetsplitServerType);
+ if (!pynss)
+ return NULL;
+
+ pynss->data = nss;
+ pynss->cleanup_installed = 1;
+ signal_add_last_data("netsplit server remove", netsplit_server_cleanup, pynss);
+
+ return (PyObject *)pynss;
+}
+
+int netsplit_server_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyNetsplitServerType) < 0)
+ return 0;
+
+ Py_INCREF(&PyNetsplitServerType);
+ PyModule_AddObject(py_module, "NetsplitServer", (PyObject *)&PyNetsplitServerType);
+
+ return 1;
+}
diff --git a/src/objects/netsplit-server-object.h b/src/objects/netsplit-server-object.h
new file mode 100644
index 0000000..f1d5fbf
--- /dev/null
+++ b/src/objects/netsplit-server-object.h
@@ -0,0 +1,18 @@
+#ifndef _NETSPLIT_SERVER_OBJECT_H_
+#define _NETSPLIT_SERVER_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+} PyNetsplitServer;
+
+extern PyTypeObject PyNetsplitServerType;
+
+int netsplit_server_object_init(void);
+PyObject *pynetsplit_server_new(void *nss);
+#define pynetsplit_server_check(op) PyObject_TypeCheck(op, &PyNetsplitServerType)
+
+#endif
diff --git a/src/objects/nick-object.c b/src/objects/nick-object.c
new file mode 100644
index 0000000..79f3a85
--- /dev/null
+++ b/src/objects/nick-object.c
@@ -0,0 +1,237 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "nick-object.h"
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+static void nick_cleanup(CHANNEL_REC *chan, NICK_REC *nick)
+{
+ PyNick *pynick = signal_get_user_data();
+
+ if (nick == pynick->data)
+ {
+ pynick->data = NULL;
+ pynick->cleanup_installed = 0;
+ signal_remove_data("nicklist remove", nick_cleanup, pynick);
+ }
+}
+
+static void PyNick_dealloc(PyNick *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("nicklist remove", nick_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyNick_send_massjoin_doc,
+ "Waiting to be sent in a 'massjoin' signal, True or False"
+);
+static PyObject *PyNick_send_massjoin_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->send_massjoin);
+}
+
+PyDoc_STRVAR(PyNick_nick_doc,
+ "Plain nick"
+);
+static PyObject *PyNick_nick_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->nick);
+}
+
+PyDoc_STRVAR(PyNick_host_doc,
+ "Host address"
+);
+static PyObject *PyNick_host_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->host);
+}
+
+PyDoc_STRVAR(PyNick_realname_doc,
+ "Real name"
+);
+static PyObject *PyNick_realname_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->realname);
+}
+
+PyDoc_STRVAR(PyNick_hops_doc,
+ "Hop count to the server the nick is using"
+);
+static PyObject *PyNick_hops_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->hops);
+}
+
+PyDoc_STRVAR(PyNick_gone_doc,
+ "User status"
+);
+static PyObject *PyNick_gone_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->gone);
+}
+
+PyDoc_STRVAR(PyNick_serverop_doc,
+ "User status"
+);
+static PyObject *PyNick_serverop_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->serverop);
+}
+
+PyDoc_STRVAR(PyNick_op_doc,
+ "User status"
+);
+static PyObject *PyNick_op_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->op);
+}
+
+PyDoc_STRVAR(PyNick_voice_doc,
+ "User status"
+);
+static PyObject *PyNick_voice_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->voice);
+}
+
+PyDoc_STRVAR(PyNick_halfop_doc,
+ "User status"
+);
+static PyObject *PyNick_halfop_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->halfop);
+}
+
+PyDoc_STRVAR(PyNick_last_check_doc,
+ "timestamp when last checked gone/ircop status."
+);
+static PyObject *PyNick_last_check_get(PyNick *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(self->data->last_check);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyNick_getseters[] = {
+ {"send_massjoin", (getter)PyNick_send_massjoin_get, NULL,
+ PyNick_send_massjoin_doc, NULL},
+ {"nick", (getter)PyNick_nick_get, NULL,
+ PyNick_nick_doc, NULL},
+ {"host", (getter)PyNick_host_get, NULL,
+ PyNick_host_doc, NULL},
+ {"realname", (getter)PyNick_realname_get, NULL,
+ PyNick_realname_doc, NULL},
+ {"hops", (getter)PyNick_hops_get, NULL,
+ PyNick_hops_doc, NULL},
+ {"gone", (getter)PyNick_gone_get, NULL,
+ PyNick_gone_doc, NULL},
+ {"serverop", (getter)PyNick_serverop_get, NULL,
+ PyNick_serverop_doc, NULL},
+ {"op", (getter)PyNick_op_get, NULL,
+ PyNick_op_doc, NULL},
+ {"voice", (getter)PyNick_voice_get, NULL,
+ PyNick_voice_doc, NULL},
+ {"halfop", (getter)PyNick_halfop_get, NULL,
+ PyNick_halfop_doc, NULL},
+ {"last_check", (getter)PyNick_last_check_get, NULL,
+ PyNick_last_check_doc, NULL},
+ {NULL}
+};
+
+static PyMethodDef PyNick_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyNickType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Nick", /*tp_name*/
+ sizeof(PyNick), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyNick_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyNick objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyNick_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyNick_getseters, /* tp_getset */
+ &PyIrssiChatBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* nick factory function */
+PyObject *pynick_sub_new(void *nick, PyTypeObject *subclass)
+{
+ static const char *name = "NICK";
+ PyNick *pynick = NULL;
+
+ pynick = py_instp(PyNick, subclass);
+ if (!pynick)
+ return NULL;
+
+ pynick->data = nick;
+ pynick->base_name = name;
+ signal_add_last_data("nicklist remove", nick_cleanup, pynick);
+ pynick->cleanup_installed = 1;
+
+ return (PyObject *)pynick;
+}
+
+PyObject *pynick_new(void *nick)
+{
+ return pynick_sub_new(nick, &PyNickType);
+}
+
+int nick_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyNickType) < 0)
+ return 0;
+
+ Py_INCREF(&PyNickType);
+ PyModule_AddObject(py_module, "Nick", (PyObject *)&PyNickType);
+
+ return 1;
+}
diff --git a/src/objects/nick-object.h b/src/objects/nick-object.h
new file mode 100644
index 0000000..6831994
--- /dev/null
+++ b/src/objects/nick-object.h
@@ -0,0 +1,22 @@
+#ifndef _NICK_OBJECT_H_
+#define _NICK_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _NICK_REC;
+
+typedef struct
+{
+ PyIrssi_HEAD(struct _NICK_REC)
+} PyNick;
+
+extern PyTypeObject PyNickType;
+
+int nick_object_init(void);
+PyObject *pynick_sub_new(void *nick, PyTypeObject *subclass);
+PyObject *pynick_new(void *nick);
+#define pynick_check(op) PyObject_TypeCheck(op, &PyNickType)
+
+#endif
diff --git a/src/objects/notifylist-object.c b/src/objects/notifylist-object.c
new file mode 100644
index 0000000..3366d0f
--- /dev/null
+++ b/src/objects/notifylist-object.c
@@ -0,0 +1,221 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "notifylist-object.h"
+#include "pycore.h"
+
+#define NOTIFYLIST(nl) ((NOTIFYLIST_REC *)nl)
+
+/* monitor "notifylist remove" signal */
+static void notifylist_cleanup(NOTIFYLIST_REC *notifylist)
+{
+ PyNotifylist *pynotifylist = signal_get_user_data();
+
+ if (notifylist == pynotifylist->data)
+ {
+ pynotifylist->data = NULL;
+ pynotifylist->cleanup_installed = 0;
+ signal_remove_data("notifylist remove", notifylist_cleanup, pynotifylist);
+ }
+}
+
+static void PyNotifylist_dealloc(PyNotifylist *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("notifylist remove", notifylist_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyNotifylist_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyNotifylist *self;
+
+ self = (PyNotifylist *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyNotifylist_mask_doc,
+ "Notify nick mask"
+);
+static PyObject *PyNotifylist_mask_get(PyNotifylist *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(NOTIFYLIST(self->data)->mask);
+}
+
+PyDoc_STRVAR(PyNotifylist_away_check_doc,
+ "Notify away status changes"
+);
+static PyObject *PyNotifylist_away_check_get(PyNotifylist *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(NOTIFYLIST(self->data)->away_check);
+}
+
+PyDoc_STRVAR(PyNotifylist_idle_check_time_doc,
+ "Notify when idle time is reset and idle was bigger than this (seconds)"
+);
+static PyObject *PyNotifylist_idle_check_time_get(PyNotifylist *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(NOTIFYLIST(self->data)->idle_check_time);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyNotifylist_getseters[] = {
+ {"mask", (getter)PyNotifylist_mask_get, NULL,
+ PyNotifylist_mask_doc, NULL},
+ {"away_check", (getter)PyNotifylist_away_check_get, NULL,
+ PyNotifylist_away_check_doc, NULL},
+ {"idle_check_time", (getter)PyNotifylist_idle_check_time_get, NULL,
+ PyNotifylist_idle_check_time_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyNotifylist_ircnets_doc,
+ "ircnets() -> list of str\n"
+ "\n"
+ "Return list of ircnets the notify is checked\n"
+);
+static PyObject *PyNotifylist_ircnets(PyNotifylist *self, PyObject *args)
+{
+ PyObject *list;
+ char **nets;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ nets = NOTIFYLIST(self->data)->ircnets;
+ list = PyList_New(0);
+ if (!list)
+ return NULL;
+
+ while (nets && *nets)
+ {
+ int ret;
+ PyObject *str = PyString_FromString(*nets);
+
+ if (!str)
+ {
+ Py_DECREF(list);
+ return NULL;
+ }
+
+ ret = PyList_Append(list, str);
+ Py_DECREF(str);
+ if (ret != 0)
+ {
+ Py_DECREF(list);
+ return NULL;
+ }
+
+ nets++;
+ }
+
+ return list;
+}
+
+PyDoc_STRVAR(PyNotifylist_ircnets_match_doc,
+ "ircnets_match(ircnet) -> bool\n"
+ "\n"
+ "Return True if notify is checked in ircnet\n"
+);
+static PyObject *PyNotifylist_ircnets_match(PyNotifylist *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"ircnet", NULL};
+ char *ircnet = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &ircnet))
+ return NULL;
+
+ return PyBool_FromLong(notifylist_ircnets_match(self->data, ircnet));
+}
+
+/* Methods for object */
+static PyMethodDef PyNotifylist_methods[] = {
+ {"ircnets", (PyCFunction)PyNotifylist_ircnets, METH_NOARGS,
+ PyNotifylist_ircnets_doc},
+ {"ircnets_match", (PyCFunction)PyNotifylist_ircnets_match, METH_VARARGS | METH_KEYWORDS,
+ PyNotifylist_ircnets_match_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyNotifylistType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Notifylist", /*tp_name*/
+ sizeof(PyNotifylist), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyNotifylist_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyNotifylist objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyNotifylist_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyNotifylist_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyNotifylist_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pynotifylist_new(void *notifylist)
+{
+ PyNotifylist *pynotifylist;
+
+ pynotifylist = py_inst(PyNotifylist, PyNotifylistType);
+ if (!pynotifylist)
+ return NULL;
+
+ pynotifylist->data = notifylist;
+ pynotifylist->cleanup_installed = 1;
+ signal_add_last_data("notifylist remove", notifylist_cleanup, pynotifylist);
+
+ return (PyObject *)pynotifylist;
+}
+
+int notifylist_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyNotifylistType) < 0)
+ return 0;
+
+ Py_INCREF(&PyNotifylistType);
+ PyModule_AddObject(py_module, "Notifylist", (PyObject *)&PyNotifylistType);
+
+ return 1;
+}
diff --git a/src/objects/notifylist-object.h b/src/objects/notifylist-object.h
new file mode 100644
index 0000000..9db6922
--- /dev/null
+++ b/src/objects/notifylist-object.h
@@ -0,0 +1,18 @@
+#ifndef _NOTIFYLIST_OBJECT_H_
+#define _NOTIFYLIST_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+} PyNotifylist;
+
+extern PyTypeObject PyNotifylistType;
+
+int notifylist_object_init(void);
+PyObject *pynotifylist_new(void *notifylist);
+#define pynotifylist_check(op) PyObject_TypeCheck(op, &PyNotifylistType)
+
+#endif
diff --git a/src/objects/process-object.c b/src/objects/process-object.c
new file mode 100644
index 0000000..43c0fd7
--- /dev/null
+++ b/src/objects/process-object.c
@@ -0,0 +1,222 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "process-object.h"
+#include "pycore.h"
+
+/* monitor "exec remove" signal */
+static void process_cleanup(PROCESS_REC *process, int status)
+{
+ PyProcess *pyprocess = signal_get_user_data();
+
+ if (process == pyprocess->data)
+ {
+ pyprocess->data = NULL;
+ pyprocess->cleanup_installed = 0;
+ signal_remove_data("exec remove", process_cleanup, pyprocess);
+ }
+}
+
+static void PyProcess_dealloc(PyProcess *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("exec remove", process_cleanup, self);
+
+ Py_XDECREF(self->target_win);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyProcess_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyProcess *self;
+
+ self = (PyProcess *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyProcess_id_doc,
+ "ID for the process"
+);
+static PyObject *PyProcess_id_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->id);
+}
+
+PyDoc_STRVAR(PyProcess_name_doc,
+ "Name for the process (if given)"
+);
+static PyObject *PyProcess_name_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->name);
+}
+
+PyDoc_STRVAR(PyProcess_args_doc,
+ "The command that is being executed"
+);
+static PyObject *PyProcess_args_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->args);
+}
+
+PyDoc_STRVAR(PyProcess_pid_doc,
+ "PID for the executed command"
+);
+static PyObject *PyProcess_pid_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->pid);
+}
+
+PyDoc_STRVAR(PyProcess_target_doc,
+ "send text with /msg <target> ..."
+);
+static PyObject *PyProcess_target_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->target);
+}
+
+PyDoc_STRVAR(PyProcess_target_win_doc,
+ "print text to this window"
+);
+static PyObject *PyProcess_target_win_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->target_win);
+}
+
+PyDoc_STRVAR(PyProcess_shell_doc,
+ "start the program via /bin/sh"
+);
+static PyObject *PyProcess_shell_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->shell);
+}
+
+PyDoc_STRVAR(PyProcess_notice_doc,
+ "send text with /notice, not /msg if target is set"
+);
+static PyObject *PyProcess_notice_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->notice);
+}
+
+PyDoc_STRVAR(PyProcess_silent_doc,
+ "don't print \"process exited with level xx\""
+);
+static PyObject *PyProcess_silent_get(PyProcess *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->silent);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyProcess_getseters[] = {
+ {"id", (getter)PyProcess_id_get, NULL,
+ PyProcess_id_doc, NULL},
+ {"name", (getter)PyProcess_name_get, NULL,
+ PyProcess_name_doc, NULL},
+ {"args", (getter)PyProcess_args_get, NULL,
+ PyProcess_args_doc, NULL},
+ {"pid", (getter)PyProcess_pid_get, NULL,
+ PyProcess_pid_doc, NULL},
+ {"target", (getter)PyProcess_target_get, NULL,
+ PyProcess_target_doc, NULL},
+ {"target_win", (getter)PyProcess_target_win_get, NULL,
+ PyProcess_target_win_doc, NULL},
+ {"shell", (getter)PyProcess_shell_get, NULL,
+ PyProcess_shell_doc, NULL},
+ {"notice", (getter)PyProcess_notice_get, NULL,
+ PyProcess_notice_doc, NULL},
+ {"silent", (getter)PyProcess_silent_get, NULL,
+ PyProcess_silent_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+/* Methods for object */
+static PyMethodDef PyProcess_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyProcessType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Process", /*tp_name*/
+ sizeof(PyProcess), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyProcess_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyProcess objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyProcess_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyProcess_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyProcess_new, /* tp_new */
+};
+
+
+/* process factory function */
+PyObject *pyprocess_new(void *process)
+{
+ PyProcess *pyprocess;
+
+ pyprocess = py_inst(PyProcess, PyProcessType);
+ if (!pyprocess)
+ return NULL;
+
+ pyprocess->data = process;
+ pyprocess->cleanup_installed = 1;
+ signal_add_last_data("exec remove", process_cleanup, pyprocess);
+
+ return (PyObject *)pyprocess;
+}
+
+int process_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyProcessType) < 0)
+ return 0;
+
+ Py_INCREF(&PyProcessType);
+ PyModule_AddObject(py_module, "Process", (PyObject *)&PyProcessType);
+
+ return 1;
+}
diff --git a/src/objects/process-object.h b/src/objects/process-object.h
new file mode 100644
index 0000000..041f187
--- /dev/null
+++ b/src/objects/process-object.h
@@ -0,0 +1,22 @@
+#ifndef _PROCESS_OBJECT_H_
+#define _PROCESS_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct PROCESS_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct PROCESS_REC)
+ PyObject *target_win;
+} PyProcess;
+
+extern PyTypeObject PyProcessType;
+
+int process_object_init(void);
+PyObject *pyprocess_new(void *process);
+#define pyprocess_check(op) PyObject_TypeCheck(op, &PyProcessType)
+
+#endif
diff --git a/src/objects/pyscript-object.c b/src/objects/pyscript-object.c
new file mode 100644
index 0000000..ae4770e
--- /dev/null
+++ b/src/objects/pyscript-object.c
@@ -0,0 +1,801 @@
+#include <Python.h>
+#include <structmember.h>
+#include "pyscript-object.h"
+#include "pyirssi.h"
+#include "pysignals.h"
+#include "pymodule.h"
+#include "pysource.h"
+#include "pythemes.h"
+#include "pystatusbar.h"
+
+/* handle cycles...
+ Can't think of any reason why the user would put script into one of the lists
+ but who knows. Call GC after unloading module.
+*/
+static int PyScript_traverse(PyScript *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->module);
+ Py_VISIT(self->argv);
+ Py_VISIT(self->modules);
+
+ return 0;
+}
+
+static int PyScript_clear(PyScript *self)
+{
+ Py_CLEAR(self->module);
+ Py_CLEAR(self->argv);
+ Py_CLEAR(self->modules);
+
+ return 0;
+}
+
+static void PyScript_dealloc(PyScript* self)
+{
+ PyScript_clear(self);
+ pyscript_remove_signals((PyObject*)self);
+ pyscript_remove_sources((PyObject*)self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyScript_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyScript *self;
+ PyObject *argv = NULL, *modules = NULL;
+
+ argv = PyList_New(0);
+ if (!argv)
+ goto error;
+
+ modules = PyDict_New();
+ if (!modules)
+ goto error;
+
+ self = (PyScript *)type->tp_alloc(type, 0);
+ if (!self)
+ goto error;
+
+ self->argv = argv;
+ self->modules = modules;
+
+ return (PyObject *)self;
+
+error:
+ Py_XDECREF(argv);
+ Py_XDECREF(modules);
+ return NULL;
+}
+
+PyDoc_STRVAR(PyScript_command_bind_doc,
+ "command_bind(command, func, catetory=None, priority=SIGNAL_PRIORITY_DEFAULT) -> None\n"
+ "\n"
+ "Add handler for a command\n"
+);
+static PyObject *PyScript_command_bind(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", "func", "category", "priority", NULL};
+ char *cmd;
+ PyObject *func;
+ char *category = NULL;
+ int priority = SIGNAL_PRIORITY_DEFAULT;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|zi", kwlist,
+ &cmd, &func, &category, &priority))
+ return NULL;
+
+ if (!PyCallable_Check(func))
+ return PyErr_Format(PyExc_TypeError, "func must be callable");
+
+ if (!pysignals_command_bind_list(&self->signals, cmd, func, category, priority))
+ return PyErr_Format(PyExc_RuntimeError, "unable to bind command");
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_signal_add_doc,
+ "signal_add(signal, func, priority=SIGNAL_PRIORITY_DEFAULT) -> None\n"
+ "\n"
+ "Add handler for signal"
+);
+static PyObject *PyScript_signal_add(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"signal", "func", "priority", NULL};
+ char *signal;
+ PyObject *func;
+ int priority = SIGNAL_PRIORITY_DEFAULT;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|i", kwlist,
+ &signal, &func, &priority))
+ return NULL;
+
+ if (!PyCallable_Check(func))
+ return PyErr_Format(PyExc_TypeError, "func must be callable");
+
+ if (!pysignals_signal_add_list(&self->signals, signal, func, priority))
+ return PyErr_Format(PyExc_KeyError, "unable to find signal, '%s'", signal);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_signal_remove_doc,
+ "signal_remove(signal, func=None) -> None\n"
+ "\n"
+ "Remove signal handler\n"
+);
+static PyObject *PyScript_signal_remove(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"signal", "func", NULL};
+ char *signal = "";
+ PyObject *func = Py_None;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist,
+ &signal, &func))
+ return NULL;
+
+ if (!PyCallable_Check(func) && func != Py_None)
+ return PyErr_Format(PyExc_TypeError, "func must be callable or None");
+
+ if (func == Py_None)
+ func = NULL;
+
+ if (!pysignals_remove_search(&self->signals, signal, func, PSG_SIGNAL))
+ return PyErr_Format(PyExc_KeyError, "can't find signal");
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_command_unbind_doc,
+ "command_unbind(command, func=None) -> None\n"
+ "\n"
+ "Remove command handler\n"
+);
+static PyObject *PyScript_command_unbind(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"command", "func", NULL};
+ char *command = "";
+ PyObject *func = Py_None;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist,
+ &command, &func))
+ return NULL;
+
+ if (!PyCallable_Check(func) && func != Py_None)
+ return PyErr_Format(PyExc_TypeError, "func must be callable or None");
+
+ if (func == Py_None)
+ func = NULL;
+
+ if (!pysignals_remove_search(&self->signals, command, func, PSG_COMMAND))
+ return PyErr_Format(PyExc_KeyError, "can't find command");
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_signal_register_doc,
+ "signal_register(signal, arglist) -> None\n"
+ "\n"
+ "Register a new dynamic signal for use with irssi_python\n"
+ "arglist is a string of character codes representing the type of each argument\n"
+ "of the signal handler function.\n"
+ "\n"
+ " Scalars\n"
+ " s -> char *\n"
+ " i -> int\n"
+ "\n"
+ " Chat objects\n"
+ " c -> CHATNET_REC\n"
+ " S -> SERVER_REC\n"
+ " C -> CHANNEL_REC\n"
+ " q -> QUERY_REC\n"
+ " n -> NICK_REC\n"
+ " W -> WI_ITEM_REC\n"
+ "\n"
+ " Irssi objects\n"
+ " d -> DCC_REC\n"
+ "\n"
+ " Other objects\n"
+ " r -> RECONNECT_REC\n"
+ " o -> COMMAND_REC\n"
+ " l -> LOG_REC\n"
+ " a -> RAWLOG_REC\n"
+ " g -> IGNORE_REC\n"
+ " b -> BAN_REC\n"
+ " N -> NETSPLIT_REC\n"
+ " e -> NETSPLIT_SERVER_REC\n"
+ " O -> NOTIFYLIST_REC\n"
+ " p -> PROCESS_REC\n"
+ " t -> TEXT_DEST_REC\n"
+ " w -> WINDOW_REC\n"
+);
+static PyObject *PyScript_signal_register(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"signal", "arglist", NULL};
+ static const char *good_codes = "sicSCqnWdrolagbNeOptw";
+ char *signal = "";
+ char *arglist = "";
+ int i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &signal, &arglist))
+ return NULL;
+
+ for (i = 0; arglist[i]; i++)
+ if (!strchr(good_codes, arglist[i]))
+ return PyErr_Format(PyExc_TypeError, "invalid code, %c", arglist[i]);
+
+ if (i >= SIGNAL_MAX_ARGUMENTS)
+ return PyErr_Format(PyExc_TypeError,
+ "arglist greater than SIGNAL_MAX_ARGUMENTS (%d)",
+ SIGNAL_MAX_ARGUMENTS);
+
+ if (!pysignals_register(signal, arglist))
+ return PyErr_Format(PyExc_TypeError, "signal present with different args");
+
+ self->registered_signals = g_slist_append(self->registered_signals,
+ g_strdup(signal));
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_signal_unregister_doc,
+ "signal_unregister(signal) -> None\n"
+ "\n"
+ "Unregister dynamic signal\n"
+);
+static PyObject *PyScript_signal_unregister(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"signal", NULL};
+ char *signal = "";
+ GSList *search;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &signal))
+ return NULL;
+
+ search = g_slist_find_custom(self->registered_signals, signal, (GCompareFunc)strcmp);
+ if (!search)
+ return PyErr_Format(PyExc_KeyError, "script has not registered that signal");
+
+ g_free(search->data);
+ self->registered_signals = g_slist_delete_link(self->registered_signals, search);
+
+ if (!pysignals_unregister(signal))
+ return PyErr_Format(PyExc_SystemError,
+ "script registered signal, but signal does not exist");
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_timeout_add_doc,
+ "timeout_add(msecs, func, data=None) -> int source tag\n"
+ "\n"
+ "Add a timeout handler called every 'msecs' milliseconds until func\n"
+ "returns False or the source is removed with source_remove().\n"
+ "\n"
+ "func is called as func(data) or func(), depending on whether data\n"
+ "is specified or not.\n"
+);
+static PyObject *PyScript_timeout_add(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"msecs", "func", "data", NULL};
+ int msecs = 0;
+ PyObject *func = NULL;
+ PyObject *data = NULL;
+ int ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "iO|O", kwlist,
+ &msecs, &func, &data))
+ return NULL;
+
+ if (msecs < 10)
+ return PyErr_Format(PyExc_ValueError, "msecs must be at least 10");
+
+ if (!PyCallable_Check(func))
+ return PyErr_Format(PyExc_TypeError, "func not callable");
+
+ ret = pysource_timeout_add_list(&self->sources, msecs, func, data);
+
+ return PyInt_FromLong(ret);
+}
+
+PyDoc_STRVAR(PyScript_io_add_watch_doc,
+ "io_add_watch(fd, func, data=None, condition=IO_IN|IO_PRI) -> int source tag\n"
+);
+static PyObject *PyScript_io_add_watch(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"fd", "func", "data", "condition", NULL};
+ int fd = 0;
+ PyObject *pyfd = NULL;
+ PyObject *func = NULL;
+ PyObject *data = NULL;
+ int condition = G_IO_IN | G_IO_PRI;
+ int ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|Oi", kwlist,
+ &pyfd, &func, &data, &condition))
+ return NULL;
+
+ fd = PyObject_AsFileDescriptor(pyfd);
+ if (fd < 0)
+ return NULL;
+
+ if (!PyCallable_Check(func))
+ return PyErr_Format(PyExc_TypeError, "func not callable");
+
+ ret = pysource_io_add_watch_list(&self->sources, fd, condition, func, data);
+
+ return PyInt_FromLong(ret);
+}
+
+PyDoc_STRVAR(PyScript_source_remove_doc,
+ "source_remove(tag) -> bool\n"
+ "\n"
+ "Remove IO or timeout source by tag. Return True if tag found and removed.\n"
+);
+static PyObject *PyScript_source_remove(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"tag", NULL};
+ int tag = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &tag))
+ return NULL;
+
+ /* the destroy notify func will remove the list link, but first
+ check that the tag exists in this Script object */
+ if (g_slist_find(self->sources, GINT_TO_POINTER(tag)))
+ return PyBool_FromLong(g_source_remove(tag));
+
+ Py_RETURN_FALSE;
+}
+
+static int py_settings_add(PyScript *self, const char *name)
+{
+ GSList *node;
+
+ node = gslist_find_icase_string(self->settings, name);
+ if (node)
+ return 0;
+
+ self->settings = g_slist_append(self->settings, g_strdup(name));
+
+ return 1;
+}
+
+static int py_settings_remove(PyScript *self, const char *name)
+{
+ GSList *node;
+
+ node = gslist_find_icase_string(self->settings, name);
+ if (!node)
+ return 0;
+
+ settings_remove(node->data);
+ g_free(node->data);
+
+ self->settings = g_slist_delete_link(self->settings, node);
+
+ return 1;
+}
+
+PyDoc_STRVAR(PyScript_settings_add_str_doc,
+ "settings_add_str(section, key, def) -> None\n"
+);
+static PyObject *PyScript_settings_add_str(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"section", "key", "def", NULL};
+ char *section = "";
+ char *key = "";
+ char *def = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &section, &key, &def))
+ return NULL;
+
+ if (!py_settings_add(self, key))
+ return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key);
+
+ settings_add_str_module(MODULE_NAME"/scripts", section, key, def);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_settings_add_int_doc,
+ "settings_add_int(section, key, def) -> None\n"
+);
+static PyObject *PyScript_settings_add_int(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"section", "key", "def", NULL};
+ char *section = "";
+ char *key = "";
+ int def = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssi", kwlist,
+ &section, &key, &def))
+ return NULL;
+
+ if (!py_settings_add(self, key))
+ return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key);
+
+ settings_add_int_module(MODULE_NAME"/scripts", section, key, def);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_settings_add_bool_doc,
+ "settings_add_bool(section, key, def) -> None\n"
+);
+static PyObject *PyScript_settings_add_bool(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"section", "key", "def", NULL};
+ char *section = "";
+ char *key = "";
+ int def = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssi", kwlist,
+ &section, &key, &def))
+ return NULL;
+
+ if (!py_settings_add(self, key))
+ return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key);
+
+ settings_add_bool_module(MODULE_NAME"/scripts", section, key, def);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_settings_add_time_doc,
+ "settings_add_time(section, key, def) -> None\n"
+);
+static PyObject *PyScript_settings_add_time(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"section", "key", "def", NULL};
+ char *section = "";
+ char *key = "";
+ char *def = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &section, &key, &def))
+ return NULL;
+
+ if (!py_settings_add(self, key))
+ return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key);
+
+ settings_add_time_module(MODULE_NAME"/scripts", section, key, def);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_settings_add_level_doc,
+ "settings_add_level(section, key, def) -> None\n"
+);
+static PyObject *PyScript_settings_add_level(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"section", "key", "def", NULL};
+ char *section = "";
+ char *key = "";
+ char *def = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &section, &key, &def))
+ return NULL;
+
+ if (!py_settings_add(self, key))
+ return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key);
+
+ settings_add_level_module(MODULE_NAME"/scripts", section, key, def);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_settings_add_size_doc,
+ "settings_add_size(section, key, def) -> None\n"
+);
+static PyObject *PyScript_settings_add_size(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"section", "key", "def", NULL};
+ char *section = "";
+ char *key = "";
+ char *def = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &section, &key, &def))
+ return NULL;
+
+ if (!py_settings_add(self, key))
+ return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key);
+
+ settings_add_size_module(MODULE_NAME"/scripts", section, key, def);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_settings_remove_doc,
+ "settings_remove(key) -> bool\n"
+);
+static PyObject *PyScript_settings_remove(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", NULL};
+ char *key = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &key))
+ return NULL;
+
+ return PyBool_FromLong(py_settings_remove(self, key));
+}
+
+PyDoc_STRVAR(PyScript_theme_register_doc,
+ "theme_register(list) -> None\n"
+);
+static PyObject *PyScript_theme_register(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"list", NULL};
+ PyObject *list = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &list))
+ return NULL;
+
+ if (!pythemes_register(pyscript_get_name(self), list))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyScript_statusbar_item_register_doc,
+ "statusbar_item_register(name, value=None, func=None) -> None\n"
+);
+static PyObject *PyScript_statusbar_item_register(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", "value", "func", NULL};
+ char *name = "";
+ char *value = NULL;
+ PyObject *func = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|zO", kwlist,
+ &name, &value, &func))
+ return NULL;
+
+ pystatusbar_item_register((PyObject *)self, name, value, func);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyScript_methods[] = {
+ {"command_bind", (PyCFunction)PyScript_command_bind, METH_VARARGS | METH_KEYWORDS,
+ PyScript_command_bind_doc},
+ {"signal_add", (PyCFunction)PyScript_signal_add, METH_VARARGS | METH_KEYWORDS,
+ PyScript_signal_add_doc},
+ {"signal_remove", (PyCFunction)PyScript_signal_remove, METH_VARARGS | METH_KEYWORDS,
+ PyScript_signal_remove_doc},
+ {"command_unbind", (PyCFunction)PyScript_command_unbind, METH_VARARGS | METH_KEYWORDS,
+ PyScript_command_unbind_doc},
+ {"signal_register", (PyCFunction)PyScript_signal_register, METH_VARARGS | METH_KEYWORDS,
+ PyScript_signal_register_doc},
+ {"signal_unregister", (PyCFunction)PyScript_signal_unregister, METH_VARARGS | METH_KEYWORDS,
+ PyScript_signal_unregister_doc},
+ {"timeout_add", (PyCFunction)PyScript_timeout_add, METH_VARARGS | METH_KEYWORDS,
+ PyScript_timeout_add_doc},
+ {"io_add_watch", (PyCFunction)PyScript_io_add_watch, METH_VARARGS | METH_KEYWORDS,
+ PyScript_io_add_watch_doc},
+ {"source_remove", (PyCFunction)PyScript_source_remove, METH_VARARGS | METH_KEYWORDS,
+ PyScript_source_remove_doc},
+ {"settings_add_str", (PyCFunction)PyScript_settings_add_str, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_add_str_doc},
+ {"settings_add_int", (PyCFunction)PyScript_settings_add_int, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_add_int_doc},
+ {"settings_add_bool", (PyCFunction)PyScript_settings_add_bool, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_add_bool_doc},
+ {"settings_add_time", (PyCFunction)PyScript_settings_add_time, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_add_time_doc},
+ {"settings_add_level", (PyCFunction)PyScript_settings_add_level, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_add_level_doc},
+ {"settings_add_size", (PyCFunction)PyScript_settings_add_size, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_add_size_doc},
+ {"settings_remove", (PyCFunction)PyScript_settings_remove, METH_VARARGS | METH_KEYWORDS,
+ PyScript_settings_remove_doc},
+ {"theme_register", (PyCFunction)PyScript_theme_register, METH_VARARGS | METH_KEYWORDS,
+ PyScript_theme_register_doc},
+ {"statusbar_item_register", (PyCFunction)PyScript_statusbar_item_register, METH_VARARGS | METH_KEYWORDS,
+ PyScript_statusbar_item_register_doc},
+ {NULL} /* Sentinel */
+};
+
+static PyMemberDef PyScript_members[] = {
+ {"argv", T_OBJECT, offsetof(PyScript, argv), 0, "Script arguments"},
+ {"module", T_OBJECT_EX, offsetof(PyScript, module), RO, "Script module"},
+ {"modules", T_OBJECT_EX, offsetof(PyScript, modules), 0, "Imported modules"},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyScriptType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Script", /*tp_name*/
+ sizeof(PyScript), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyScript_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ "PyScript objects", /* tp_doc */
+ (traverseproc)PyScript_traverse, /* tp_traverse */
+ (inquiry)PyScript_clear, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyScript_methods, /* tp_methods */
+ PyScript_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyScript_new, /* tp_new */
+};
+
+/* PyScript factory function */
+PyObject *pyscript_new(PyObject *module, char **argv)
+{
+ PyObject *script;
+
+ script = PyObject_CallFunction((PyObject*)&PyScriptType, "()");
+
+ if (script)
+ {
+ PyScript *scr = (PyScript *)script;
+
+ while (*argv)
+ {
+ if (**argv != '\0')
+ {
+ PyObject *str = PyString_FromString(*argv);
+ if (!str)
+ {
+ /* The destructor should DECREF argv */
+ Py_DECREF(script);
+ return NULL;
+ }
+
+ PyList_Append(scr->argv, str);
+ Py_DECREF(str);
+ }
+
+ *argv++;
+ }
+
+ Py_INCREF(module);
+ scr->module = module;
+ }
+
+ return script;
+}
+
+void pyscript_remove_signals(PyObject *script)
+{
+ GSList *node;
+ PyScript *self;
+
+ g_return_if_fail(pyscript_check(script));
+
+ self = (PyScript *) script;
+
+ /* remove bound signals */
+ pysignals_remove_list(self->signals);
+ g_slist_free(self->signals);
+ self->signals = NULL;
+
+ /* remove registered signals */
+ for (node = self->registered_signals; node; node = node->next)
+ {
+ pysignals_unregister(node->data);
+ g_free(node->data);
+ }
+
+ g_slist_free(self->registered_signals);
+ self->registered_signals = NULL;
+}
+
+void pyscript_remove_sources(PyObject *script)
+{
+ GSList *node;
+ PyScript *self;
+
+ g_return_if_fail(pyscript_check(script));
+
+ self = (PyScript *) script;
+
+ node = self->sources;
+ while (node)
+ {
+ /* the notify func will destroy the link so save next */
+ GSList *next = node->next;
+ g_source_remove(GPOINTER_TO_INT(node->data));
+ node = next;
+ }
+
+ g_return_if_fail(self->sources == NULL);
+}
+
+void pyscript_remove_settings(PyObject *script)
+{
+ PyScript *self;
+
+ g_return_if_fail(pyscript_check(script));
+
+ self = (PyScript *) script;
+
+ g_slist_foreach(self->settings, (GFunc)settings_remove, NULL);
+ g_slist_foreach(self->settings, (GFunc)g_free, NULL);
+ g_slist_free(self->settings);
+}
+
+void pyscript_remove_themes(PyObject *script)
+{
+ PyScript *self;
+
+ g_return_if_fail(pyscript_check(script));
+
+ self = (PyScript *) script;
+
+ pythemes_unregister(pyscript_get_name(script));
+}
+
+void pyscript_remove_statusbars(PyObject *script)
+{
+ g_return_if_fail(pyscript_check(script));
+
+ pystatusbar_cleanup_script(script);
+}
+
+void pyscript_clear_modules(PyObject *script)
+{
+ PyScript *self;
+
+ g_return_if_fail(pyscript_check(script));
+
+ self = (PyScript *) script;
+
+ PyDict_Clear(self->modules);
+}
+
+void pyscript_cleanup(PyObject *script)
+{
+ pyscript_remove_signals(script);
+ pyscript_remove_sources(script);
+ pyscript_remove_settings(script);
+ pyscript_remove_themes(script);
+ pyscript_remove_statusbars(script);
+ pyscript_clear_modules(script);
+}
+
+int pyscript_init(void)
+{
+ if (PyType_Ready(&PyScriptType) < 0)
+ return 0;
+
+ Py_INCREF(&PyScriptType);
+ PyModule_AddObject(py_module, "Script", (PyObject *)&PyScriptType);
+
+ return 1;
+}
+
diff --git a/src/objects/pyscript-object.h b/src/objects/pyscript-object.h
new file mode 100644
index 0000000..624a578
--- /dev/null
+++ b/src/objects/pyscript-object.h
@@ -0,0 +1,33 @@
+#ifndef _PYSCRIPT_OBJECT_H_
+#define _PYSCRIPT_OBJECT_H_
+#include <Python.h>
+#include <glib.h>
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *module; /* module object */
+ PyObject *argv; /* list of argument strings from the load command */
+ PyObject *modules; /* dict of imported modules for script */
+ GSList *signals; /* list of bound signals and commands */
+ GSList *registered_signals; /* list of signal names registered */
+ GSList *sources; /* list of io and timeout sources */
+ GSList *settings; /* list of settings from settings_add_*() */
+} PyScript;
+
+extern PyTypeObject PyScriptType;
+
+int pyscript_init(void);
+PyObject *pyscript_new(PyObject *module, char **argv);
+void pyscript_remove_signals(PyObject *script);
+void pyscript_remove_sources(PyObject *script);
+void pyscript_remove_settings(PyObject *script);
+void pyscript_remove_themes(PyObject *script);
+void pyscript_remove_statusbars(PyObject *script);
+void pyscript_clear_modules(PyObject *script);
+void pyscript_cleanup(PyObject *script);
+#define pyscript_check(op) PyObject_TypeCheck(op, &PyScriptType)
+#define pyscript_get_name(scr) PyModule_GetName(((PyScript*)scr)->module)
+#define pyscript_get_filename(scr) PyModule_GetFilename(((PyScript*)scr)->module)
+#define pyscript_get_module(scr) (((PyScript*)scr)->module)
+
+#endif
diff --git a/src/objects/query-object.c b/src/objects/query-object.c
new file mode 100644
index 0000000..47badd5
--- /dev/null
+++ b/src/objects/query-object.c
@@ -0,0 +1,172 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "base-objects.h"
+#include "window-item-object.h"
+#include "query-object.h"
+#include "server-object.h"
+#include "pycore.h"
+
+/* monitor "query destroyed" signal */
+static void query_cleanup(QUERY_REC *query)
+{
+ PyQuery *pyquery = signal_get_user_data();
+
+ if (query == pyquery->data)
+ {
+ pyquery->data = NULL;
+ pyquery->cleanup_installed = 0;
+ signal_remove_data("query destroyed", query_cleanup, pyquery);
+ }
+}
+
+static void PyQuery_dealloc(PyQuery *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("query destroyed", query_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyQuery_address_doc,
+ "Host address of the queries nick"
+);
+static PyObject *PyQuery_address_get(PyQuery *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->address);
+}
+
+PyDoc_STRVAR(PyQuery_server_tag_doc,
+ "Server tag used for this nick (doesn't get erased if server gets disconnected)"
+);
+static PyObject *PyQuery_server_tag_get(PyQuery *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->server_tag);
+}
+
+PyDoc_STRVAR(PyQuery_unwanted_doc,
+ "1 if the other side closed or some error occured (DCC chats)"
+);
+static PyObject *PyQuery_unwanted_get(PyQuery *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->unwanted);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyQuery_getseters[] = {
+ {"address", (getter)PyQuery_address_get, NULL,
+ PyQuery_address_doc, NULL},
+ {"server_tag", (getter)PyQuery_server_tag_get, NULL,
+ PyQuery_server_tag_doc, NULL},
+ {"unwanted", (getter)PyQuery_unwanted_get, NULL,
+ PyQuery_unwanted_doc, NULL},
+ {NULL}
+};
+
+PyDoc_STRVAR(change_server_doc,
+ "change_server(server) -> None\n"
+ "\n"
+ "Change the active server for the query.\n"
+);
+static PyObject *PyQuery_change_server(PyQuery *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"server", NULL};
+ PyObject *server;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &server))
+ return NULL;
+
+ if (!pyserver_check(server))
+ return PyErr_Format(PyExc_TypeError, "argument must be server object");
+
+ query_change_server(self->data, ((PyServer*)server)->data);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyQuery_methods[] = {
+ {"change_server", (PyCFunction)PyQuery_change_server, METH_VARARGS | METH_KEYWORDS,
+ change_server_doc},
+
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyQueryType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Query", /*tp_name*/
+ sizeof(PyQuery), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyQuery_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyQuery objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyQuery_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyQuery_getseters, /* tp_getset */
+ &PyWindowItemType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* query factory function */
+PyObject *pyquery_new(void *query)
+{
+ static const char *BASE_NAME = "QUERY";
+ PyObject *pyquery;
+
+ pyquery = pywindow_item_sub_new(query, BASE_NAME, &PyQueryType);
+ if (pyquery)
+ {
+ PyQuery *pyq = (PyQuery *)pyquery;
+ signal_add_last_data("query destroyed", query_cleanup, pyq);
+ pyq->cleanup_installed = 1;
+ }
+
+ return pyquery;
+}
+
+int query_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyQueryType) < 0)
+ return 0;
+
+ Py_INCREF(&PyQueryType);
+ PyModule_AddObject(py_module, "Query", (PyObject *)&PyQueryType);
+
+ return 1;
+}
diff --git a/src/objects/query-object.h b/src/objects/query-object.h
new file mode 100644
index 0000000..844ce55
--- /dev/null
+++ b/src/objects/query-object.h
@@ -0,0 +1,21 @@
+#ifndef _QUERY_OBJECT_H_
+#define _QUERY_OBJECT_H_
+
+#include <Python.h>
+#include "window-item-object.h"
+
+/* forward */
+struct _QUERY_REC;
+
+typedef struct
+{
+ PyWindowItem_HEAD(struct _QUERY_REC)
+} PyQuery;
+
+extern PyTypeObject PyQueryType;
+
+int query_object_init(void);
+PyObject *pyquery_new(void *query);
+#define pyquery_check(op) PyObject_TypeCheck(op, &PyQueryType)
+
+#endif
diff --git a/src/objects/rawlog-object.c b/src/objects/rawlog-object.c
new file mode 100644
index 0000000..340d60a
--- /dev/null
+++ b/src/objects/rawlog-object.c
@@ -0,0 +1,323 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "rawlog-object.h"
+#include "pycore.h"
+
+/* monitor "????" signal */
+static void rawlog_cleanup(RAWLOG_REC *ban)
+{
+ /* XXX */
+}
+
+static void PyRawlog_dealloc(PyRawlog *self)
+{
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyRawlog_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyRawlog *self;
+
+ self = (PyRawlog *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* XXX: Need function to create the rawlog */
+
+/* Getters */
+PyDoc_STRVAR(PyRawlog_logging_doc,
+ "The raw log is being written to file currently."
+);
+static PyObject *PyRawlog_logging_get(PyRawlog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->logging);
+}
+
+PyDoc_STRVAR(PyRawlog_nlines_doc,
+ "Number of lines in rawlog."
+);
+static PyObject *PyRawlog_nlines_get(PyRawlog *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->nlines);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyRawlog_getseters[] = {
+ {"logging", (getter)PyRawlog_logging_get, NULL,
+ PyRawlog_logging_doc, NULL},
+ {"nlines", (getter)PyRawlog_nlines_get, NULL,
+ PyRawlog_nlines_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyRawlog_get_lines_doc,
+ "get_lines() -> list of str\n"
+ "\n"
+ "Return a list of lines for rawlog.\n"
+);
+static PyObject *PyRawlog_get_lines(PyRawlog *self, PyObject *args)
+{
+ PyObject *lines = NULL;
+ GSList *node;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ lines = PyList_New(0);
+ if (!lines)
+ return NULL;
+
+ for (node = self->data->lines; node; node = node->next)
+ {
+ int ret;
+ PyObject *line = PyString_FromString(node->data);
+
+ if (!line)
+ {
+ Py_XDECREF(lines);
+ return NULL;
+ }
+
+ ret = PyList_Append(lines, line);
+ Py_DECREF(line);
+ if (ret != 0)
+ {
+ Py_XDECREF(lines);
+ return NULL;
+ }
+ }
+
+ return lines;
+}
+
+PyDoc_STRVAR(PyRawlog_destroy_doc,
+ "destroy() -> None\n"
+ "\n"
+ "Destroy rawlog\n"
+);
+static PyObject *PyRawlog_destroy(PyRawlog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ rawlog_destroy(self->data);
+
+ /*XXX: what about signal handler ? */
+ self->data = NULL;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyRawlog_input_doc,
+ "input(str) -> None\n"
+ "\n"
+ "Send str to rawlog as input text.\n"
+);
+static PyObject *PyRawlog_input(PyRawlog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", NULL};
+ char *str = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &str))
+ return NULL;
+
+ rawlog_input(self->data, str);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyRawlog_output_doc,
+ "output(str) -> None\n"
+ "\n"
+ "Send str to rawlog as output text.\n"
+);
+static PyObject *PyRawlog_output(PyRawlog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", NULL};
+ char *str = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &str))
+ return NULL;
+
+ rawlog_output(self->data, str);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyRawlog_redirect_doc,
+ "redirect(str) -> None\n"
+ "\n"
+ "Send str to rawlog as redirection text."
+);
+static PyObject *PyRawlog_redirect(PyRawlog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", NULL};
+ char *str = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &str))
+ return NULL;
+
+ rawlog_redirect(self->data, str);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyRawlog_open_doc,
+ "open(fname) -> None\n"
+ "\n"
+ "Start logging new messages in rawlog to specified file.\n"
+);
+static PyObject *PyRawlog_open(PyRawlog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"fname", NULL};
+ char *fname = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &fname))
+ return NULL;
+
+ rawlog_open(self->data, fname);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyRawlog_close_doc,
+ "close() -> None\n"
+ "\n"
+ "Stop logging to file\n"
+);
+static PyObject *PyRawlog_close(PyRawlog *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ rawlog_close(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyRawlog_save_doc,
+ "save(fname) -> None\n"
+ "\n"
+ "Save the current rawlog history to specified file.\n"
+);
+static PyObject *PyRawlog_save(PyRawlog *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"fname", NULL};
+ char *fname = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &fname))
+ return NULL;
+
+ rawlog_save(self->data, fname);
+
+ Py_RETURN_NONE;
+}
+/* Methods for object */
+static PyMethodDef PyRawlog_methods[] = {
+ {"get_lines", (PyCFunction)PyRawlog_get_lines, METH_NOARGS,
+ PyRawlog_get_lines_doc},
+ {"destroy", (PyCFunction)PyRawlog_destroy, METH_NOARGS,
+ PyRawlog_destroy_doc},
+ {"input", (PyCFunction)PyRawlog_input, METH_VARARGS | METH_KEYWORDS,
+ PyRawlog_input_doc},
+ {"output", (PyCFunction)PyRawlog_output, METH_VARARGS | METH_KEYWORDS,
+ PyRawlog_output_doc},
+ {"redirect", (PyCFunction)PyRawlog_redirect, METH_VARARGS | METH_KEYWORDS,
+ PyRawlog_redirect_doc},
+ {"open", (PyCFunction)PyRawlog_open, METH_VARARGS | METH_KEYWORDS,
+ PyRawlog_open_doc},
+ {"close", (PyCFunction)PyRawlog_close, METH_NOARGS,
+ PyRawlog_close_doc},
+ {"save", (PyCFunction)PyRawlog_save, METH_VARARGS | METH_KEYWORDS,
+ PyRawlog_save_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyRawlogType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Rawlog", /*tp_name*/
+ sizeof(PyRawlog), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyRawlog_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyRawlog objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyRawlog_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyRawlog_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyRawlog_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pyrawlog_new(void *rlog)
+{
+ PyRawlog *pyrlog;
+
+ pyrlog = py_inst(PyRawlog, PyRawlogType);
+ if (!pyrlog)
+ return NULL;
+
+ pyrlog->data = rlog;
+
+ return (PyObject *)pyrlog;
+}
+
+int rawlog_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyRawlogType) < 0)
+ return 0;
+
+ Py_INCREF(&PyRawlogType);
+ PyModule_AddObject(py_module, "Rawlog", (PyObject *)&PyRawlogType);
+
+ return 1;
+}
diff --git a/src/objects/rawlog-object.h b/src/objects/rawlog-object.h
new file mode 100644
index 0000000..d2a9a2d
--- /dev/null
+++ b/src/objects/rawlog-object.h
@@ -0,0 +1,22 @@
+#ifndef _RAWLOG_OBJECT_H_
+#define _RAWLOG_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _RAWLOG_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct _RAWLOG_REC)
+ int owned;
+} PyRawlog;
+
+extern PyTypeObject PyRawlogType;
+
+int rawlog_object_init(void);
+PyObject *pyrawlog_new(void *rlog);
+#define pyrawlog_check(op) PyObject_TypeCheck(op, &PyRawlogType)
+
+#endif
diff --git a/src/objects/reconnect-object.c b/src/objects/reconnect-object.c
new file mode 100644
index 0000000..6483242
--- /dev/null
+++ b/src/objects/reconnect-object.c
@@ -0,0 +1,151 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "pycore.h"
+#include "factory.h"
+#include "reconnect-object.h"
+
+/*XXX: no Reconnect cleanup/destroy sig. Maybe value copy the two members? */
+
+static void PyReconnect_dealloc(PyReconnect *self)
+{
+ Py_XDECREF(self->connect);
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyReconnect_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyReconnect *self;
+
+ self = (PyReconnect *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyReconnect_tag_doc,
+ "Unique numeric tag"
+);
+static PyObject *PyReconnect_tag_get(PyReconnect *self, void *closure)
+{
+ RECONNECT_REC *data = self->data;
+ RET_NULL_IF_INVALID(self->data);
+
+ return PyInt_FromLong(data->tag);
+}
+
+PyDoc_STRVAR(PyReconnect_next_connect_doc,
+ "Unix time stamp when the next connection occurs"
+);
+static PyObject *PyReconnect_next_connect_get(PyReconnect *self, void *closure)
+{
+ RECONNECT_REC *data = self->data;
+ RET_NULL_IF_INVALID(self->data);
+
+ return PyLong_FromUnsignedLong(data->next_connect);
+}
+
+PyDoc_STRVAR(PyReconnect_connect_doc,
+ "Connection object"
+);
+static PyObject *PyReconnect_connect_get(PyReconnect *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->connect);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyReconnect_getseters[] = {
+ {"tag", (getter)PyReconnect_tag_get, NULL,
+ PyReconnect_tag_doc, NULL},
+ {"next_connect", (getter)PyReconnect_next_connect_get, NULL,
+ PyReconnect_next_connect_doc, NULL},
+ {"connect", (getter)PyReconnect_connect_get, NULL,
+ PyReconnect_connect_doc, NULL},
+ {NULL}
+};
+
+/* Methods for object */
+static PyMethodDef PyReconnect_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyReconnectType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Reconnect", /*tp_name*/
+ sizeof(PyReconnect), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyReconnect_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyReconnect objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyReconnect_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyReconnect_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyReconnect_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pyreconnect_new(void *recon)
+{
+ RECONNECT_REC *rec = recon;
+ PyObject *connect;
+ PyReconnect *pyrecon;
+
+ /* XXX: get a managed connect because there's no signals to manage reconnect */
+ connect = py_irssi_chat_new(rec->conn, 1);
+ if (!connect)
+ return NULL;
+
+ pyrecon = py_inst(PyReconnect, PyReconnectType);
+ if (!pyrecon)
+ return NULL;
+
+ pyrecon->data = recon;
+ pyrecon->connect = connect;
+
+ return (PyObject *)pyrecon;
+}
+
+int reconnect_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyReconnectType) < 0)
+ return 0;
+
+ Py_INCREF(&PyReconnectType);
+ PyModule_AddObject(py_module, "Reconnect", (PyObject *)&PyReconnectType);
+
+ return 1;
+}
diff --git a/src/objects/reconnect-object.h b/src/objects/reconnect-object.h
new file mode 100644
index 0000000..ec0b094
--- /dev/null
+++ b/src/objects/reconnect-object.h
@@ -0,0 +1,20 @@
+#ifndef _RECONNECT_OBJECT_H_
+#define _RECONNECT_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/*XXX: no Reconnect cleanup/destroy sig. Maybe value copy the two members? */
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+ PyObject *connect;
+} PyReconnect;
+
+extern PyTypeObject PyReconnectType;
+
+int reconnect_object_init(void);
+PyObject *pyreconnect_new(void *recon);
+#define pyreconnect_check(op) PyObject_TypeCheck(op, &PyReconnectType)
+
+#endif
diff --git a/src/objects/server-object.c b/src/objects/server-object.c
new file mode 100644
index 0000000..31415b8
--- /dev/null
+++ b/src/objects/server-object.c
@@ -0,0 +1,807 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "factory.h"
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+static void server_cleanup(SERVER_REC *server)
+{
+ PyServer *pyserver = signal_get_user_data();
+
+ if (server == pyserver->data)
+ {
+ if (pyserver->connect)
+ ((PyConnect *)pyserver->connect)->data = NULL;
+
+ if (pyserver->rawlog)
+ ((PyRawlog *)pyserver->rawlog)->data = NULL;
+
+ pyserver->data = NULL;
+ pyserver->cleanup_installed = 0;
+ signal_remove_data("server disconnected", server_cleanup, pyserver);
+ }
+}
+
+static void PyServer_dealloc(PyServer *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("server disconnected", server_cleanup, self);
+
+ Py_XDECREF(self->connect);
+ Py_XDECREF(self->rawlog);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+/* Getters */
+PyDoc_STRVAR(PyServer_connect_time_doc,
+ "Time when connect() to server finished"
+);
+static PyObject *PyServer_connect_time_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromLong(self->data->connect_time);
+}
+
+PyDoc_STRVAR(PyServer_real_connect_time_doc,
+ "Time when server sent 'connected' message"
+);
+static PyObject *PyServer_real_connect_time_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromLong(self->data->real_connect_time);
+}
+
+PyDoc_STRVAR(PyServer_tag_doc,
+ "Unique server tag"
+);
+static PyObject *PyServer_tag_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->tag);
+}
+
+PyDoc_STRVAR(PyServer_nick_doc,
+ "Current nick"
+);
+static PyObject *PyServer_nick_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->nick);
+}
+
+PyDoc_STRVAR(PyServer_connected_doc,
+ "Is connection finished? 1|0"
+);
+static PyObject *PyServer_connected_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->connected);
+}
+
+PyDoc_STRVAR(PyServer_connection_lost_doc,
+ "Did we lose the connection (1) or was the connection just /DISCONNECTed (0)"
+);
+static PyObject *PyServer_connection_lost_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->connection_lost);
+}
+
+PyDoc_STRVAR(PyServer_rawlog_doc,
+ "Rawlog object for the server"
+);
+static PyObject *PyServer_rawlog_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->rawlog);
+}
+
+PyDoc_STRVAR(PyServer_connect_doc,
+ "Connect object for the server"
+);
+static PyObject *PyServer_connect_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->connect);
+}
+
+PyDoc_STRVAR(PyServer_version_doc,
+ "Server version"
+);
+static PyObject *PyServer_version_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->version);
+}
+
+PyDoc_STRVAR(PyServer_last_invite_doc,
+ "Last channel we were invited to"
+);
+static PyObject *PyServer_last_invite_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->last_invite);
+}
+
+PyDoc_STRVAR(PyServer_server_operator_doc,
+ "Are we server operator (IRC op) 1|0"
+);
+static PyObject *PyServer_server_operator_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->server_operator);
+}
+
+PyDoc_STRVAR(PyServer_usermode_away_doc,
+ "Are we marked as away? 1|0"
+);
+static PyObject *PyServer_usermode_away_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->usermode_away);
+}
+
+PyDoc_STRVAR(PyServer_away_reason_doc,
+ "Away reason message"
+);
+static PyObject *PyServer_away_reason_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->away_reason);
+}
+
+PyDoc_STRVAR(PyServer_banned_doc,
+ "Were we banned from this server? 1|0"
+);
+static PyObject *PyServer_banned_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->banned);
+}
+
+PyDoc_STRVAR(PyServer_lag_doc,
+ "Current lag to server in milliseconds"
+);
+static PyObject *PyServer_lag_get(PyServer *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->lag);
+}
+
+static PyGetSetDef PyServer_getseters[] = {
+ {"connect_time", (getter)PyServer_connect_time_get, NULL,
+ PyServer_connect_time_doc, NULL},
+ {"real_connect_time", (getter)PyServer_real_connect_time_get, NULL,
+ PyServer_real_connect_time_doc, NULL},
+ {"tag", (getter)PyServer_tag_get, NULL,
+ PyServer_tag_doc, NULL},
+ {"nick", (getter)PyServer_nick_get, NULL,
+ PyServer_nick_doc, NULL},
+ {"connected", (getter)PyServer_connected_get, NULL,
+ PyServer_connected_doc, NULL},
+ {"connection_lost", (getter)PyServer_connection_lost_get, NULL,
+ PyServer_connection_lost_doc, NULL},
+ {"rawlog", (getter)PyServer_rawlog_get, NULL,
+ PyServer_rawlog_doc, NULL},
+ {"connect", (getter)PyServer_connect_get, NULL,
+ PyServer_connect_doc, NULL},
+ {"version", (getter)PyServer_version_get, NULL,
+ PyServer_version_doc, NULL},
+ {"last_invite", (getter)PyServer_last_invite_get, NULL,
+ PyServer_last_invite_doc, NULL},
+ {"server_operator", (getter)PyServer_server_operator_get, NULL,
+ PyServer_server_operator_doc, NULL},
+ {"usermode_away", (getter)PyServer_usermode_away_get, NULL,
+ PyServer_usermode_away_doc, NULL},
+ {"away_reason", (getter)PyServer_away_reason_get, NULL,
+ PyServer_away_reason_doc, NULL},
+ {"banned", (getter)PyServer_banned_get, NULL,
+ PyServer_banned_doc, NULL},
+ {"lag", (getter)PyServer_lag_get, NULL,
+ PyServer_lag_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(print_doc,
+ "prnt(channel, str, level) -> None\n"
+ "\n"
+ "Print to server\n"
+);
+static PyObject *PyServer_prnt(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"channel", "str", "level", NULL};
+ char *str, *channel;
+ int level = MSGLEVEL_CLIENTNOTICE;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|i", kwlist, &channel, &str, &level))
+ return NULL;
+
+ printtext_string(self->data, channel, level, str);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(command_doc,
+ "command(cmd) -> None\n"
+ "\n"
+ "Send command\n"
+);
+static PyObject *PyServer_command(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", NULL};
+ char *cmd;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd))
+ return NULL;
+
+ py_command(cmd, self->data, NULL);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(disconnect_doc,
+ "disconnect() -> None\n"
+ "\n"
+ "Disconnect from server\n"
+);
+static PyObject *PyServer_disconnect(PyServer *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ server_disconnect(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(isnickflag_doc,
+ "isnickflag(flag) -> bool\n"
+ "\n"
+ "Returns True if flag is a nick mode flag (@, + or % in IRC)\n"
+);
+static PyObject *PyServer_isnickflag(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"flag", NULL};
+ char flag;
+ int ret;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "c", kwlist, &flag))
+ return NULL;
+
+ ret = self->data->isnickflag(self->data, flag);
+
+ return PyBool_FromLong(ret);
+}
+
+PyDoc_STRVAR(ischannel_doc,
+ "ischannel(data) -> bool\n"
+ "\n"
+ "Returns True if start of `data' seems to mean channel.\n"
+);
+static PyObject *PyServer_ischannel(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"data", NULL};
+ char *data;
+ int ret;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &data))
+ return NULL;
+
+ ret = self->data->ischannel(self->data, data);
+
+ return PyBool_FromLong(ret);
+}
+
+PyDoc_STRVAR(get_nick_flags_doc,
+ "get_nick_flags() -> str\n"
+ "\n"
+ "Returns nick flag characters in order: op, voice, halfop (\"@+%\") in IRC\n"
+);
+static PyObject *PyServer_get_nick_flags(PyServer *self, PyObject *args)
+{
+ char *ret;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ ret = (char *)self->data->get_nick_flags(self->data);
+
+ return PyString_FromString(ret);
+}
+
+PyDoc_STRVAR(send_message_doc,
+ "send_message(target, msg, target_type) -> None\n"
+ "\n"
+ "Sends a message to nick/channel. target_type 0 = channel, 1 = nick\n"
+);
+static PyObject *PyServer_send_message(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"target", "msg", "target_type", NULL};
+ char *target, *msg;
+ int target_type;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssi", kwlist, &target, &msg, &target_type))
+ return NULL;
+
+ self->data->send_message(self->data, target, msg, target_type);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(channels_join_doc,
+ "channels_join(channels, automatic=False) -> None\n"
+ "\n"
+ "Join to channels in server. `channels' may also contain keys for\n"
+ "channels just like with /JOIN command. `automatic' specifies if this\n"
+ "channel was joined 'automatically' or if it was joined because join\n"
+ "was requested by user. If channel join is 'automatic', irssi doesn't\n"
+ "jump to the window where the channel was joined.\n"
+);
+static PyObject *PyServer_channels_join(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"channels", "automatic", NULL};
+ char *channels;
+ int automatic = 0;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &channels, &automatic))
+ return NULL;
+
+ self->data->channels_join(self->data, channels, automatic);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyServer_window_item_find_doc,
+ "window_item_find(name) -> WindowItem object or None\n"
+ "\n"
+ "Find window item that matches best to given arguments\n"
+);
+static PyObject *PyServer_window_item_find(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ return py_irssi_chat_new(window_item_find(self->data, name), 1);
+}
+
+PyDoc_STRVAR(PyServer_window_find_item_doc,
+ "window_find_item(name) -> Window object or None\n"
+ "\n"
+ "Find window which contains window item with specified name/server\n"
+);
+static PyObject *PyServer_window_find_item(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+ WINDOW_REC *win;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ win = window_find_item(self->data, name);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyServer_window_find_level_doc,
+ "window_find_level(level) -> Window object or None\n"
+ "\n"
+ "Find window with level\n"
+);
+static PyObject *PyServer_window_find_level(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"level", NULL};
+ int level = 0;
+ WINDOW_REC *win;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &level))
+ return NULL;
+
+ win = window_find_level(self->data, level);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyServer_window_find_closest_doc,
+ "window_find_closest(name, level) -> Window object or None\n"
+ "\n"
+ "Find window that matches best to given arguments. `name' can be either\n"
+ "window name or name of one of the window items.\n"
+);
+static PyObject *PyServer_window_find_closest(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", "level", NULL};
+ char *name = "";
+ int level = 0;
+ WINDOW_REC *win;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist,
+ &name, &level))
+ return NULL;
+
+ win = window_find_closest(self->data, name, level);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyServer_channels_doc,
+ "channels() -> list of Channel objects\n"
+ "\n"
+ "Return list of channels for server\n"
+);
+static PyObject *PyServer_channels(PyServer *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ return py_irssi_chatlist_new(self->data->channels, 1);
+}
+
+PyDoc_STRVAR(PyServer_channel_find_doc,
+ "channel_find(name) -> Channel object or None\n"
+ "\n"
+ "Find channel from this server\n"
+);
+static PyObject *PyServer_channel_find(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ return py_irssi_chat_new(channel_find(self->data, name), 1);
+}
+
+PyDoc_STRVAR(PyServer_nicks_get_same_doc,
+ "nicks_get_same(nick)\n"
+ "\n"
+ "Return all nick objects in all channels in server. List is in format:\n"
+ "[(Channel, Nick), (Channel, Nick), ...]\n"
+);
+static PyObject *PyServer_nicks_get_same(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ char *nick = "";
+ GSList *list, *node;
+ PyObject *pylist = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &nick))
+ return NULL;
+
+ pylist = PyList_New(0);
+ if (!pylist)
+ return NULL;
+
+ list = nicklist_get_same(self->data, nick);
+ for (node = list; node != NULL; node = node->next->next)
+ {
+ int ret;
+ PyObject *tup;
+
+ tup = Py_BuildValue("(NN)",
+ py_irssi_chat_new(node->data, 1),
+ py_irssi_chat_new(node->next->data, 1));
+ if (!tup)
+ {
+ Py_XDECREF(pylist);
+ return NULL;
+ }
+
+ ret = PyList_Append(pylist, tup);
+ Py_DECREF(tup);
+ if (ret != 0)
+ {
+ Py_XDECREF(pylist);
+ return NULL;
+ }
+ }
+
+ return pylist;
+}
+
+PyDoc_STRVAR(PyServer_queries_doc,
+ "queries() -> list of Query objects\n"
+ "\n"
+ "Return a list of queries for server.\n"
+);
+static PyObject *PyServer_queries(PyServer *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ return py_irssi_chatlist_new(self->data->queries, 1);
+}
+
+PyDoc_STRVAR(PyServer_query_find_doc,
+ "query_find(nick) -> Query object or None\n"
+ "\n"
+ "Find a query on this server.\n"
+);
+static PyObject *PyServer_query_find(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ char *nick = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &nick))
+ return NULL;
+
+ return py_irssi_chat_new(query_find(self->data, nick), 1);
+}
+
+PyDoc_STRVAR(PyServer_mask_match_doc,
+ "mask_match(mask, nick, user, host) -> bool\n"
+ "\n"
+ "Return true if mask matches nick!user@host\n"
+);
+static PyObject *PyServer_mask_match(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"mask", "nick", "user", "host", NULL};
+ char *mask = "";
+ char *nick = "";
+ char *user = "";
+ char *host = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssss", kwlist,
+ &mask, &nick, &user, &host))
+ return NULL;
+
+ return PyBool_FromLong(mask_match(self->data, mask, nick, user, host));
+}
+
+PyDoc_STRVAR(PyServer_mask_match_address_doc,
+ "mask_match_address(mask, nick, address) -> bool\n"
+ "\n"
+ "Return True if mask matches nick!address\n"
+);
+static PyObject *PyServer_mask_match_address(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"mask", "nick", "address", NULL};
+ char *mask = "";
+ char *nick = "";
+ char *address = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &mask, &nick, &address))
+ return NULL;
+
+ return PyBool_FromLong(mask_match_address(self->data, mask, nick, address));
+}
+
+PyDoc_STRVAR(PyServer_masks_match_doc,
+ "masks_match(masks, nick, address) -> bool\n"
+ "\n"
+ "Return True if any mask in the masks (string separated by spaces)\n"
+ "matches nick!address\n"
+);
+static PyObject *PyServer_masks_match(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"masks", "nick", "address", NULL};
+ char *masks = "";
+ char *nick = "";
+ char *address = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &masks, &nick, &address))
+ return NULL;
+
+ return PyBool_FromLong(masks_match(self->data, masks, nick, address));
+}
+
+PyDoc_STRVAR(PyServer_ignore_check_doc,
+ "ignore_check(nick, host, channel, text, level) -> bool\n"
+ "\n"
+ "Return True if ignore matches\n"
+);
+static PyObject *PyServer_ignore_check(PyServer *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", "host", "channel", "text", "level", NULL};
+ char *nick = "";
+ char *host = "";
+ char *channel = "";
+ char *text = "";
+ int level = 0;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssssi", kwlist,
+ &nick, &host, &channel, &text, &level))
+ return NULL;
+
+ return PyBool_FromLong(ignore_check(self->data,
+ nick, host, channel, text, level));
+}
+
+/* Methods for object */
+static PyMethodDef PyServer_methods[] = {
+ {"prnt", (PyCFunction)PyServer_prnt, METH_VARARGS | METH_KEYWORDS,
+ print_doc},
+ {"command", (PyCFunction)PyServer_command, METH_VARARGS | METH_KEYWORDS,
+ command_doc},
+ {"disconnect", (PyCFunction)PyServer_disconnect, METH_NOARGS,
+ disconnect_doc},
+ {"isnickflag", (PyCFunction)PyServer_isnickflag, METH_VARARGS | METH_KEYWORDS,
+ isnickflag_doc},
+ {"ischannel", (PyCFunction)PyServer_ischannel, METH_VARARGS | METH_KEYWORDS,
+ ischannel_doc},
+ {"get_nick_flags", (PyCFunction)PyServer_get_nick_flags, METH_NOARGS,
+ get_nick_flags_doc},
+ {"send_message", (PyCFunction)PyServer_send_message, METH_VARARGS | METH_KEYWORDS,
+ send_message_doc},
+ {"channels_join", (PyCFunction)PyServer_channels_join, METH_VARARGS | METH_KEYWORDS,
+ channels_join_doc},
+ {"window_item_find", (PyCFunction)PyServer_window_item_find, METH_VARARGS | METH_KEYWORDS,
+ PyServer_window_item_find_doc},
+ {"window_find_item", (PyCFunction)PyServer_window_find_item, METH_VARARGS | METH_KEYWORDS,
+ PyServer_window_find_item_doc},
+ {"window_find_level", (PyCFunction)PyServer_window_find_level, METH_VARARGS | METH_KEYWORDS,
+ PyServer_window_find_level_doc},
+ {"window_find_closest", (PyCFunction)PyServer_window_find_closest, METH_VARARGS | METH_KEYWORDS,
+ PyServer_window_find_closest_doc},
+ {"channels", (PyCFunction)PyServer_channels, METH_NOARGS,
+ PyServer_channels_doc},
+ {"channel_find", (PyCFunction)PyServer_channel_find, METH_VARARGS | METH_KEYWORDS,
+ PyServer_channel_find_doc},
+ {"nicks_get_same", (PyCFunction)PyServer_nicks_get_same, METH_VARARGS | METH_KEYWORDS,
+ PyServer_nicks_get_same_doc},
+ {"queries", (PyCFunction)PyServer_queries, METH_NOARGS,
+ PyServer_queries_doc},
+ {"query_find", (PyCFunction)PyServer_query_find, METH_VARARGS | METH_KEYWORDS,
+ PyServer_query_find_doc},
+ {"mask_match", (PyCFunction)PyServer_mask_match, METH_VARARGS | METH_KEYWORDS,
+ PyServer_mask_match_doc},
+ {"mask_match_address", (PyCFunction)PyServer_mask_match_address, METH_VARARGS | METH_KEYWORDS,
+ PyServer_mask_match_address_doc},
+ {"masks_match", (PyCFunction)PyServer_masks_match, METH_VARARGS | METH_KEYWORDS,
+ PyServer_masks_match_doc},
+ {"ignore_check", (PyCFunction)PyServer_ignore_check, METH_VARARGS | METH_KEYWORDS,
+ PyServer_ignore_check_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyServerType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Server", /*tp_name*/
+ sizeof(PyServer), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyServer_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyServer objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyServer_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyServer_getseters, /* tp_getset */
+ &PyIrssiChatBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* server factory function
+ connect arg should point to a wrapped SERVER_CONNECT */
+PyObject *pyserver_sub_new(void *server, PyTypeObject *subclass)
+{
+ static const char *SERVER_TYPE = "SERVER";
+ SERVER_REC *srec = server;
+ PyServer *pyserver = NULL;
+ PyObject *rawlog = NULL;
+ PyObject *connect = NULL;
+
+ g_return_val_if_fail(server != NULL, NULL);
+
+ connect = py_irssi_chat_new(srec->connrec, 0);
+ if (!connect)
+ return NULL;
+
+ /* FIXME */
+ /*
+ if (srec->rawlog)
+ {
+ rawlog = pyrawlog_new(srec->rawlog);
+ if (!rawlog)
+ return NULL;
+ }
+ */
+
+ pyserver = py_instp(PyServer, subclass);
+ if (!pyserver)
+ return NULL;
+
+ pyserver->base_name = SERVER_TYPE;
+ pyserver->data = server;
+ signal_add_last_data("server disconnected", server_cleanup, pyserver);
+ pyserver->cleanup_installed = 1;
+ pyserver->rawlog = rawlog;
+ pyserver->connect = connect;
+
+ return (PyObject *)pyserver;
+}
+
+PyObject *pyserver_new(void *server)
+{
+ return pyserver_sub_new(server, &PyServerType);
+}
+
+int server_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyServerType) < 0)
+ return 0;
+
+ Py_INCREF(&PyServerType);
+ PyModule_AddObject(py_module, "Server", (PyObject *)&PyServerType);
+
+ return 1;
+}
diff --git a/src/objects/server-object.h b/src/objects/server-object.h
new file mode 100644
index 0000000..0703f47
--- /dev/null
+++ b/src/objects/server-object.h
@@ -0,0 +1,28 @@
+#ifndef _SERVER_OBJECT_H_
+#define _SERVER_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _SERVER_REC;
+
+#define PyServer_HEAD(type) \
+ PyIrssi_HEAD(type) \
+ PyObject *connect; \
+ PyObject *rawlog;
+
+typedef struct
+{
+ PyServer_HEAD(struct _SERVER_REC)
+} PyServer;
+
+extern PyTypeObject PyServerType;
+
+int server_object_init(void);
+PyObject *pyserver_sub_new(void *server, PyTypeObject *subclass);
+PyObject *pyserver_new(void *server);
+
+#define pyserver_check(op) PyObject_TypeCheck(op, &PyServerType)
+
+#endif
diff --git a/src/objects/statusbar-item-object.c b/src/objects/statusbar-item-object.c
new file mode 100644
index 0000000..f6768af
--- /dev/null
+++ b/src/objects/statusbar-item-object.c
@@ -0,0 +1,253 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "factory.h"
+#include "statusbar-item-object.h"
+
+/* monitor "statusbar item destroyed" signal */
+static void statusbar_item_cleanup(SBAR_ITEM_REC *sbar_item)
+{
+ PyStatusbarItem *pysbar_item = signal_get_user_data();
+
+ if (sbar_item == pysbar_item->data)
+ {
+ pysbar_item->data = NULL;
+ pysbar_item->cleanup_installed = 0;
+ signal_remove_data("statusbar item_destroy", statusbar_item_cleanup, pysbar_item);
+ }
+}
+
+static void PyStatusbarItem_dealloc(PyStatusbarItem *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("sbar_itemlist remove", statusbar_item_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyStatusbarItem_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyStatusbarItem *self;
+
+ self = (PyStatusbarItem *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getter */
+PyDoc_STRVAR(PyStatusbarItem_min_size_doc,
+ "min size"
+);
+static PyObject *PyStatusbarItem_min_size_get(PyStatusbarItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->min_size);
+}
+
+PyDoc_STRVAR(PyStatusbarItem_max_size_doc,
+ "max size"
+);
+static PyObject *PyStatusbarItem_max_size_get(PyStatusbarItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->max_size);
+}
+
+PyDoc_STRVAR(PyStatusbarItem_xpos_doc,
+ "x position"
+);
+static PyObject *PyStatusbarItem_xpos_get(PyStatusbarItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->xpos);
+}
+
+PyDoc_STRVAR(PyStatusbarItem_size_doc,
+ "size"
+);
+static PyObject *PyStatusbarItem_size_get(PyStatusbarItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->size);
+}
+
+PyDoc_STRVAR(PyStatusbarItem_window_doc,
+ "parent window for statusbar item"
+);
+static PyObject *PyStatusbarItem_window_get(PyStatusbarItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->window);
+}
+
+/* setters */
+static int py_setint(int *iv, PyObject *value)
+{
+ int tmp;
+
+ if (value == NULL)
+ {
+ PyErr_SetString(PyExc_AttributeError, "can't delete member");
+ return -1;
+ }
+
+ if (!PyInt_Check(value))
+ {
+ PyErr_SetString(PyExc_TypeError, "value must be int");
+ return -1;
+ }
+
+ tmp = PyInt_AsLong(value);
+ if (PyErr_Occurred())
+ return -1;
+
+ *iv = tmp;
+
+ return 0;
+}
+
+static int PyStatusbarItem_min_size_set(PyStatusbarItem *self, PyObject *value, void *closure)
+{
+ return py_setint(&self->data->min_size, value);
+}
+
+static int PyStatusbarItem_max_size_set(PyStatusbarItem *self, PyObject *value, void *closure)
+{
+ return py_setint(&self->data->max_size, value);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyStatusbarItem_getseters[] = {
+ {"min_size", (getter)PyStatusbarItem_min_size_get, (setter)PyStatusbarItem_min_size_set,
+ PyStatusbarItem_min_size_doc, NULL},
+ {"max_size", (getter)PyStatusbarItem_max_size_get, (setter)PyStatusbarItem_max_size_set,
+ PyStatusbarItem_max_size_doc, NULL},
+ {"xpos", (getter)PyStatusbarItem_xpos_get, NULL,
+ PyStatusbarItem_xpos_doc, NULL},
+ {"size", (getter)PyStatusbarItem_size_get, NULL,
+ PyStatusbarItem_size_doc, NULL},
+ {"window", (getter)PyStatusbarItem_window_get, NULL,
+ PyStatusbarItem_window_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyStatusbarItem_default_handler_doc,
+ "default_handler(get_size_only, str=None, data="", escape_vars=True) -> None\n"
+ "\n"
+ "Run default handler of item to print to statusbar\n"
+);
+static PyObject *PyStatusbarItem_default_handler(PyStatusbarItem *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"get_size_only", "str", "data", "escape_vars", NULL};
+ int get_size_only = 0;
+ char *str = NULL;
+ char *data = "";
+ int escape_vars = TRUE;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|zsi", kwlist,
+ &get_size_only, &str, &data, &escape_vars))
+ return NULL;
+
+ if (str && !*str)
+ str = NULL;
+
+ statusbar_item_default_handler(self->data, get_size_only, str, data, escape_vars);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyStatusbarItem_methods[] = {
+ {"default_handler", (PyCFunction)PyStatusbarItem_default_handler, METH_VARARGS | METH_KEYWORDS,
+ PyStatusbarItem_default_handler_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyStatusbarItemType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "StatusbarItem", /*tp_name*/
+ sizeof(PyStatusbarItem), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyStatusbarItem_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyStatusbarItem objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyStatusbarItem_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyStatusbarItem_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyStatusbarItem_new, /* tp_new */
+};
+
+
+/* sbar_item factory function */
+PyObject *pystatusbar_item_new(void *sbar_item)
+{
+ SBAR_ITEM_REC *si;
+ PyStatusbarItem *pysbar_item;
+ PyObject *window = NULL;
+
+ si = sbar_item;
+ if (si->bar->parent_window)
+ {
+ window = pywindow_new(si->bar->parent_window);
+ if (!window)
+ return NULL;
+ }
+
+ pysbar_item = py_inst(PyStatusbarItem, PyStatusbarItemType);
+ if (!pysbar_item)
+ return NULL;
+
+ pysbar_item->window = window;
+
+ pysbar_item->data = sbar_item;
+ pysbar_item->cleanup_installed = 1;
+ signal_add_last_data("statusbar item destroyed", statusbar_item_cleanup, pysbar_item);
+
+ return (PyObject *)pysbar_item;
+}
+
+int statusbar_item_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyStatusbarItemType) < 0)
+ return 0;
+
+ Py_INCREF(&PyStatusbarItemType);
+ PyModule_AddObject(py_module, "StatusbarItem", (PyObject *)&PyStatusbarItemType);
+
+ return 1;
+}
diff --git a/src/objects/statusbar-item-object.h b/src/objects/statusbar-item-object.h
new file mode 100644
index 0000000..19243f4
--- /dev/null
+++ b/src/objects/statusbar-item-object.h
@@ -0,0 +1,22 @@
+#ifndef _SBAR_ITEM_OBJECT_H_
+#define _SBAR_ITEM_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct SBAR_ITEM_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct SBAR_ITEM_REC)
+ PyObject *window;
+} PyStatusbarItem;
+
+extern PyTypeObject PyStatusbarItemType;
+
+int statusbar_item_object_init(void);
+PyObject *pystatusbar_item_new(void *sbar_item);
+#define pystatusbar_item_check(op) PyObject_TypeCheck(op, &PyStatusbarItemType)
+
+#endif
diff --git a/src/objects/textdest-object.c b/src/objects/textdest-object.c
new file mode 100644
index 0000000..2ad6523
--- /dev/null
+++ b/src/objects/textdest-object.c
@@ -0,0 +1,285 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "textdest-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+static int pytextdest_setup(PyTextDest *pytdest, void *td, int owned);
+
+/* XXX: no cleanup signal for textdest */
+static void PyTextDest_dealloc(PyTextDest *self)
+{
+ Py_XDECREF(self->window);
+ Py_XDECREF(self->server);
+
+ if (self->owned)
+ {
+ g_free((char*)self->data->target);
+ g_free(self->data);
+ }
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyTextDest_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyTextDest *self;
+
+ self = (PyTextDest *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* init function to create the textdest */
+PyDoc_STRVAR(PyTextDest_doc,
+ "__init__(target, level=MSGLEVEL_CLIENTNOTICE, server=None, window=None)\n"
+ "\n"
+ "Create a TextDest\n"
+);
+static int PyTextDest_init(PyTextDest *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"target", "level", "server", "window", NULL};
+ char *target;
+ int level = MSGLEVEL_CLIENTNOTICE;
+ PyObject *server = NULL, *window = NULL;
+ TEXT_DEST_REC *dest;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ioo", kwlist,
+ &target, &level, &server, &window))
+ return -1;
+
+ if (server == Py_None)
+ server = NULL;
+ if (window == Py_None)
+ window = NULL;
+
+ if (server && !pyserver_check(server))
+ {
+ PyErr_Format(PyExc_TypeError, "arg 3 isnt server");
+ return -1;
+ }
+
+ if (window && !pywindow_check(window))
+ {
+ PyErr_Format(PyExc_TypeError, "arg 4 isnt window");
+ return -1;
+ }
+
+ if (self->data)
+ {
+ PyErr_Format(PyExc_RuntimeError, "TextDest already wrapped");
+ return -1;
+ }
+
+ dest = g_new0(TEXT_DEST_REC, 1);
+ format_create_dest(dest, DATA(server), g_strdup(target), level, DATA(window));
+
+ if (!pytextdest_setup(self, dest, 1))
+ return -1;
+
+ return 0;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyTextDest_window_doc,
+ "Window where the text will be written"
+);
+static PyObject *PyTextDest_window_get(PyTextDest *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->window);
+}
+
+PyDoc_STRVAR(PyTextDest_server_doc,
+ "Target server"
+);
+static PyObject *PyTextDest_server_get(PyTextDest *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->server);
+}
+
+PyDoc_STRVAR(PyTextDest_target_doc,
+ "Target channel/query/etc name"
+);
+static PyObject *PyTextDest_target_get(PyTextDest *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->target);
+}
+
+PyDoc_STRVAR(PyTextDest_level_doc,
+ "Text level"
+);
+static PyObject *PyTextDest_level_get(PyTextDest *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->level);
+}
+
+PyDoc_STRVAR(PyTextDest_hilight_priority_doc,
+ "Priority for the hilighted text"
+);
+static PyObject *PyTextDest_hilight_priority_get(PyTextDest *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->hilight_priority);
+}
+
+PyDoc_STRVAR(PyTextDest_hilight_color_doc,
+ "Color for the hilighted text"
+);
+static PyObject *PyTextDest_hilight_color_get(PyTextDest *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->hilight_color);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyTextDest_getseters[] = {
+ {"window", (getter)PyTextDest_window_get, NULL,
+ PyTextDest_window_doc, NULL},
+ {"server", (getter)PyTextDest_server_get, NULL,
+ PyTextDest_server_doc, NULL},
+ {"target", (getter)PyTextDest_target_get, NULL,
+ PyTextDest_target_doc, NULL},
+ {"level", (getter)PyTextDest_level_get, NULL,
+ PyTextDest_level_doc, NULL},
+ {"hilight_priority", (getter)PyTextDest_hilight_priority_get, NULL,
+ PyTextDest_hilight_priority_doc, NULL},
+ {"hilight_color", (getter)PyTextDest_hilight_color_get, NULL,
+ PyTextDest_hilight_color_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyTextDest_prnt_doc,
+ "prnt(str) -> None\n"
+ "\n"
+ "Print str to TextDest\n"
+);
+static PyObject *PyTextDest_prnt(PyTextDest *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", NULL};
+ char *str = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &str))
+ return NULL;
+
+ printtext_dest(self->data, "%s", str);
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyTextDest_methods[] = {
+ {"prnt", (PyCFunction)PyTextDest_prnt, METH_VARARGS | METH_KEYWORDS,
+ PyTextDest_prnt_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyTextDestType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "TextDest", /*tp_name*/
+ sizeof(PyTextDest), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyTextDest_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ PyTextDest_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyTextDest_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyTextDest_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)PyTextDest_init, /* tp_init */
+ 0, /* tp_alloc */
+ PyTextDest_new, /* tp_new */
+};
+
+static int pytextdest_setup(PyTextDest *pytdest, void *td, int owned)
+{
+ PyObject *window, *server;
+ TEXT_DEST_REC *tdest = td;
+
+ if (tdest->window)
+ {
+ window = pywindow_new(tdest->window);
+ if (!window)
+ return 0;
+ }
+
+ server = py_irssi_chat_new(tdest->server, 1);
+ if (!server)
+ {
+ Py_DECREF(window);
+ return 0;
+ }
+
+ pytdest->data = td;
+ pytdest->window = window;
+ pytdest->server = server;
+ pytdest->owned = owned;
+
+ return 1;
+}
+
+/* TextDest factory function */
+PyObject *pytextdest_new(void *td)
+{
+ PyTextDest *pytdest;
+
+ pytdest = py_inst(PyTextDest, PyTextDestType);
+ if (!pytdest)
+ return NULL;
+
+ if (!pytextdest_setup(pytdest, td, 0))
+ {
+ Py_DECREF(pytdest);
+ return NULL;
+ }
+
+ return (PyObject *)pytdest;
+}
+
+int textdest_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyTextDestType) < 0)
+ return 0;
+
+ Py_INCREF(&PyTextDestType);
+ PyModule_AddObject(py_module, "TextDest", (PyObject *)&PyTextDestType);
+
+ return 1;
+}
diff --git a/src/objects/textdest-object.h b/src/objects/textdest-object.h
new file mode 100644
index 0000000..3e273e6
--- /dev/null
+++ b/src/objects/textdest-object.h
@@ -0,0 +1,24 @@
+#ifndef _TEXTDEST_OBJECT_H_
+#define _TEXTDEST_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _TEXT_DEST_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct _TEXT_DEST_REC)
+ PyObject *window;
+ PyObject *server;
+ int owned;
+} PyTextDest;
+
+extern PyTypeObject PyTextDestType;
+
+int textdest_object_init(void);
+PyObject *pytextdest_new(void *td);
+#define pytextdest_check(op) PyObject_TypeCheck(op, &PyTextDestType)
+
+#endif
diff --git a/src/objects/theme-object.c b/src/objects/theme-object.c
new file mode 100644
index 0000000..0f12217
--- /dev/null
+++ b/src/objects/theme-object.c
@@ -0,0 +1,195 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "theme-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+/* monitor "theme destroyed" signal */
+static void theme_cleanup(THEME_REC *rec)
+{
+ PyTheme *pytheme = signal_get_user_data();
+ if (pytheme->data == rec)
+ {
+ pytheme->data = NULL;
+ pytheme->cleanup_installed = 0;
+ signal_remove_data("theme destroyed", theme_cleanup, pytheme);
+ }
+}
+
+static void PyTheme_dealloc(PyTheme *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("theme destroyed", theme_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyTheme_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyTheme *self;
+
+ self = (PyTheme *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+/* specialized getters/setters */
+static PyGetSetDef PyTheme_getseters[] = {
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyTheme_format_expand_doc,
+ "format_expand(format, flags=0) -> str or None\n"
+);
+static PyObject *PyTheme_format_expand(PyTheme *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"format", "flags", NULL};
+ char *format = "";
+ int flags = 0;
+ char *ret;
+ PyObject *pyret;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist,
+ &format, &flags))
+ return NULL;
+
+ if (flags == 0)
+ ret = theme_format_expand(self->data, format);
+ else
+ ret = theme_format_expand_data(self->data, (const char **)&format, 'n', 'n',
+ NULL, NULL, EXPAND_FLAG_ROOT | flags);
+
+ if (ret)
+ {
+ pyret = PyString_FromString(ret);
+ g_free(ret);
+ return pyret;
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyTheme_get_format_doc,
+ "get_format(module, tag) -> str\n"
+);
+static PyObject *PyTheme_get_format(PyTheme *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"module", "tag", NULL};
+ char *module = "";
+ char *tag = "";
+ THEME_REC *theme = self->data;
+ FORMAT_REC *formats;
+ MODULE_THEME_REC *modtheme;
+ int i;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &module, &tag))
+ return NULL;
+
+ formats = g_hash_table_lookup(default_formats, module);
+ if (!formats)
+ return PyErr_Format(PyExc_KeyError, "unknown module, %s", module);
+
+ for (i = 0; formats[i].def; i++)
+ {
+ if (formats[i].tag && !g_strcasecmp(formats[i].tag, tag))
+ {
+ modtheme = g_hash_table_lookup(theme->modules, module);
+ if (modtheme && modtheme->formats[i])
+ return PyString_FromString(modtheme->formats[i]);
+ else
+ return PyString_FromString(formats[i].def);
+ }
+ }
+
+ return PyErr_Format(PyExc_KeyError, "unknown format tag, %s", tag);
+}
+
+/* Methods for object */
+static PyMethodDef PyTheme_methods[] = {
+ {"format_expand", (PyCFunction)PyTheme_format_expand, METH_VARARGS | METH_KEYWORDS,
+ PyTheme_format_expand_doc},
+ {"get_format", (PyCFunction)PyTheme_get_format, METH_VARARGS | METH_KEYWORDS,
+ PyTheme_get_format_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyThemeType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Theme", /*tp_name*/
+ sizeof(PyTheme), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyTheme_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyTheme_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyTheme_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyTheme_new, /* tp_new */
+};
+
+/* Theme factory function */
+PyObject *pytheme_new(void *td)
+{
+ PyTheme *pytheme;
+
+ pytheme = py_inst(PyTheme, PyThemeType);
+ if (!pytheme)
+ return NULL;
+
+ pytheme->data = td;
+ signal_add_last_data("theme destroyed", theme_cleanup, pytheme);
+ pytheme->cleanup_installed = 1;
+
+ return (PyObject *)pytheme;
+}
+
+int theme_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyThemeType) < 0)
+ return 0;
+
+ Py_INCREF(&PyThemeType);
+ PyModule_AddObject(py_module, "Theme", (PyObject *)&PyThemeType);
+
+ return 1;
+}
diff --git a/src/objects/theme-object.h b/src/objects/theme-object.h
new file mode 100644
index 0000000..299d54d
--- /dev/null
+++ b/src/objects/theme-object.h
@@ -0,0 +1,18 @@
+#ifndef _THEME_OBJECT_H_
+#define _THEME_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(void)
+} PyTheme;
+
+extern PyTypeObject PyThemeType;
+
+int theme_object_init(void);
+PyObject *pytheme_new(void *td);
+#define pytheme_check(op) PyObject_TypeCheck(op, &PyThemeType)
+
+#endif
diff --git a/src/objects/window-item-object.c b/src/objects/window-item-object.c
new file mode 100644
index 0000000..2f18f7d
--- /dev/null
+++ b/src/objects/window-item-object.c
@@ -0,0 +1,334 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "window-item-object.h"
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyutils.h"
+#include "factory.h"
+
+/* Dealloc is overridden by sub types */
+
+PyDoc_STRVAR(PyWindowItem_server_doc,
+ "Active name for item"
+);
+static PyObject *PyWindowItem_server_get(PyWindowItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_OBJ_OR_NONE(self->server);
+}
+
+PyDoc_STRVAR(PyWindowItem_name_doc,
+ "Name of the item"
+);
+static PyObject *PyWindowItem_name_get(PyWindowItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->visible_name);
+}
+
+PyDoc_STRVAR(PyWindowItem_createtime_doc,
+ "Time the witem was created"
+);
+static PyObject *PyWindowItem_createtime_get(PyWindowItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromLong(self->data->createtime);
+}
+
+PyDoc_STRVAR(PyWindowItem_data_level_doc,
+ "0=no new data, 1=text, 2=msg, 3=highlighted text"
+);
+static PyObject *PyWindowItem_data_level_get(PyWindowItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->data_level);
+}
+
+PyDoc_STRVAR(PyWindowItem_hilight_color_doc,
+ "Color of the last highlighted text"
+);
+static PyObject *PyWindowItem_hilight_color_get(PyWindowItem *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->hilight_color);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyWindowItem_getseters[] = {
+ {"server", (getter)PyWindowItem_server_get, NULL,
+ PyWindowItem_server_doc, NULL},
+ {"name", (getter)PyWindowItem_name_get, NULL,
+ PyWindowItem_name_doc, NULL},
+ {"createtime", (getter)PyWindowItem_createtime_get, NULL,
+ PyWindowItem_createtime_doc, NULL},
+ {"data_level", (getter)PyWindowItem_data_level_get, NULL,
+ PyWindowItem_data_level_doc, NULL},
+ {"hilight_color", (getter)PyWindowItem_hilight_color_get, NULL,
+ PyWindowItem_hilight_color_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyWindowItem_prnt_doc,
+ "prnt(str, level) -> None\n"
+ "\n"
+ "Print to window item\n"
+);
+static PyObject *PyWindowItem_prnt(PyWindowItem *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", "level", NULL};
+ char *str;
+ int level = MSGLEVEL_CLIENTNOTICE;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &str, &level))
+ return NULL;
+
+ printtext_string(self->data->server, self->data->visible_name, level, str);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_command_doc,
+ "command(cmd) -> None\n"
+ "\n"
+ "Send command to window item\n"
+);
+static PyObject *PyWindowItem_command(PyWindowItem *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", NULL};
+ char *cmd;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd))
+ return NULL;
+
+ py_command(cmd, self->data->server, self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_window_doc,
+ "window() -> Window object or None\n"
+ "\n"
+ "Return parent window for window item\n"
+);
+static PyObject *PyWindowItem_window(PyWindowItem *self, PyObject *args)
+{
+ WINDOW_REC *win;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ win = window_item_window(self->data);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_change_server_doc,
+ "change_server(server) -> None\n"
+ "\n"
+ "Change server for window item\n"
+);
+static PyObject *PyWindowItem_change_server(PyWindowItem *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"server", NULL};
+ PyObject *server = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &server))
+ return NULL;
+
+ if (!pyserver_check(server))
+ return PyErr_Format(PyExc_TypeError, "arg must be server");
+
+ window_item_change_server(self->data, ((PyServer*)server)->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_is_active_doc,
+ "is_active() -> bool\n"
+ "\n"
+ "Returns true if window item is active\n"
+);
+static PyObject *PyWindowItem_is_active(PyWindowItem *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ return PyBool_FromLong(window_item_is_active(self->data));
+}
+
+PyDoc_STRVAR(PyWindowItem_set_active_doc,
+ "set_active() -> None\n"
+ "\n"
+ "Set window item active\n"
+);
+static PyObject *PyWindowItem_set_active(PyWindowItem *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_item_set_active(window_item_window(self->data), self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_activity_doc,
+ "activity(data_level, hilight_color) -> None\n"
+ "\n"
+);
+static PyObject *PyWindowItem_activity(PyWindowItem *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"data_level", "hilight_color", NULL};
+ int data_level = 0;
+ char *hilight_color = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "is", kwlist,
+ &data_level, &hilight_color))
+ return NULL;
+
+ window_item_activity(self->data, data_level, hilight_color);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_destroy_doc,
+ "destroy() -> None\n"
+ "\n"
+ "Destroy channel or query\n"
+);
+static PyObject *PyWindowItem_destroy(PyWindowItem *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_item_destroy(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindowItem_get_dcc_doc,
+ "get_dcc() -> DccChat object or None\n"
+ "\n"
+ "If item is a query of a =nick, return DCC chat record of nick\n"
+);
+static PyObject *PyWindowItem_get_dcc(PyWindowItem *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return py_irssi_new(self->data, 1);
+}
+
+/* Methods for object */
+static PyMethodDef PyWindowItem_methods[] = {
+ {"prnt", (PyCFunction)PyWindowItem_prnt, METH_VARARGS | METH_KEYWORDS,
+ PyWindowItem_prnt_doc},
+ {"command", (PyCFunction)PyWindowItem_command, METH_VARARGS | METH_KEYWORDS,
+ PyWindowItem_command_doc},
+ {"window", (PyCFunction)PyWindowItem_window, METH_NOARGS,
+ PyWindowItem_window_doc},
+ {"change_server", (PyCFunction)PyWindowItem_change_server, METH_VARARGS | METH_KEYWORDS,
+ PyWindowItem_change_server_doc},
+ {"is_active", (PyCFunction)PyWindowItem_is_active, METH_NOARGS,
+ PyWindowItem_is_active_doc},
+ {"set_active", (PyCFunction)PyWindowItem_set_active, METH_NOARGS,
+ PyWindowItem_set_active_doc},
+ {"activity", (PyCFunction)PyWindowItem_activity, METH_VARARGS | METH_KEYWORDS,
+ PyWindowItem_activity_doc},
+ {"destroy", (PyCFunction)PyWindowItem_destroy, METH_NOARGS,
+ PyWindowItem_destroy_doc},
+ {"get_dcc", (PyCFunction)PyWindowItem_get_dcc, METH_NOARGS,
+ PyWindowItem_get_dcc_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyWindowItemType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "WindowItem", /*tp_name*/
+ sizeof(PyWindowItem), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyWindowItem objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyWindowItem_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyWindowItem_getseters, /* tp_getset */
+ &PyIrssiChatBaseType, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pywindow_item_sub_new(void *witem, const char *name, PyTypeObject *subclass)
+{
+ WI_ITEM_REC *rec = witem;
+ PyWindowItem *pywitem = NULL;
+ PyObject *server;
+
+ g_return_val_if_fail(witem != NULL, NULL);
+
+ server = py_irssi_chat_new(rec->server, 1);
+ if (!server)
+ return NULL;
+
+ pywitem = py_instp(PyWindowItem, subclass);
+ if (!pywitem)
+ return NULL;
+
+ pywitem->data = witem;
+ pywitem->base_name = name;
+ pywitem->server = server;
+
+ return (PyObject *)pywitem;
+}
+
+PyObject *pywindow_item_new(void *witem)
+{
+ return pywindow_item_sub_new(witem, NULL, &PyWindowItemType);
+}
+
+int window_item_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyWindowItemType) < 0)
+ return 0;
+
+ Py_INCREF(&PyWindowItemType);
+ PyModule_AddObject(py_module, "WindowItem", (PyObject *)&PyWindowItemType);
+
+ return 1;
+}
diff --git a/src/objects/window-item-object.h b/src/objects/window-item-object.h
new file mode 100644
index 0000000..e205ebe
--- /dev/null
+++ b/src/objects/window-item-object.h
@@ -0,0 +1,26 @@
+#ifndef _WITEM_OBJECT_H_
+#define _WITEM_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+#define PyWindowItem_HEAD(type) \
+ PyIrssi_HEAD(type) \
+ PyObject *server;
+
+/* forward */
+struct _WI_ITEM_REC;
+
+typedef struct
+{
+ PyWindowItem_HEAD(struct _WI_ITEM_REC)
+} PyWindowItem;
+
+extern PyTypeObject PyWindowItemType;
+
+int window_item_object_init(void);
+PyObject *pywindow_item_sub_new(void *witem, const char *name, PyTypeObject *subclass);
+PyObject *pywindow_item_new(void *witem);
+#define pywindow_item_check(op) PyObject_TypeCheck(op, &PyWindowItemType)
+
+#endif
diff --git a/src/objects/window-object.c b/src/objects/window-object.c
new file mode 100644
index 0000000..d5aa475
--- /dev/null
+++ b/src/objects/window-object.c
@@ -0,0 +1,678 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "window-object.h"
+#include "factory.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+/* monitor "window destroyed" signal */
+static void window_cleanup(WINDOW_REC *win)
+{
+ PyWindow *pywindow = signal_get_user_data();
+
+ if (win == pywindow->data)
+ {
+ pywindow->data = NULL;
+ pywindow->cleanup_installed = 0;
+ signal_remove_data("window destroyed", window_cleanup, pywindow);
+ }
+}
+
+static void PyWindow_dealloc(PyWindow *self)
+{
+ if (self->cleanup_installed)
+ signal_remove_data("window destroyed", window_cleanup, self);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyWindow_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyWindow *self;
+
+ self = (PyWindow *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+/* Getters */
+PyDoc_STRVAR(PyWindow_refnum_doc,
+ "Reference number"
+);
+static PyObject *PyWindow_refnum_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->refnum);
+}
+
+PyDoc_STRVAR(PyWindow_name_doc,
+ "Name"
+);
+static PyObject *PyWindow_name_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->name);
+}
+
+PyDoc_STRVAR(PyWindow_width_doc,
+ "Width"
+);
+static PyObject *PyWindow_width_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->width);
+}
+
+PyDoc_STRVAR(PyWindow_height_doc,
+ "Height"
+);
+static PyObject *PyWindow_height_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->height);
+}
+
+PyDoc_STRVAR(PyWindow_history_name_doc,
+ "Name of named historylist for this window"
+);
+static PyObject *PyWindow_history_name_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->history_name);
+}
+
+PyDoc_STRVAR(PyWindow_active_doc,
+ "Active window item"
+);
+static PyObject *PyWindow_active_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return py_irssi_chat_new(self->data->active, 1);
+}
+
+PyDoc_STRVAR(PyWindow_active_server_doc,
+ "Active server"
+);
+static PyObject *PyWindow_active_server_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return py_irssi_chat_new(self->data->active_server, 1);
+}
+
+PyDoc_STRVAR(PyWindow_servertag_doc,
+ "active_server must be either None or have this same tag"
+ "(unless there's items in this window). This is used by"
+ "/WINDOW SERVER -sticky"
+);
+static PyObject *PyWindow_servertag_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->servertag);
+}
+
+PyDoc_STRVAR(PyWindow_level_doc,
+ "Current window level"
+);
+static PyObject *PyWindow_level_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->level);
+}
+
+PyDoc_STRVAR(PyWindow_sticky_refnum_doc,
+ "True if reference number is sticky"
+);
+static PyObject *PyWindow_sticky_refnum_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyBool_FromLong(self->data->sticky_refnum);
+}
+
+PyDoc_STRVAR(PyWindow_data_level_doc,
+ "Current data level"
+);
+static PyObject *PyWindow_data_level_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyInt_FromLong(self->data->data_level);
+}
+
+PyDoc_STRVAR(PyWindow_hilight_color_doc,
+ "Current activity hilight color"
+);
+static PyObject *PyWindow_hilight_color_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->hilight_color);
+}
+
+PyDoc_STRVAR(PyWindow_last_timestamp_doc,
+ "Last time timestamp was written in window"
+);
+static PyObject *PyWindow_last_timestamp_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(self->data->last_timestamp);
+}
+
+PyDoc_STRVAR(PyWindow_last_line_doc,
+ "Last time text was written in window"
+);
+static PyObject *PyWindow_last_line_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return PyLong_FromUnsignedLong(self->data->last_line);
+}
+
+PyDoc_STRVAR(PyWindow_theme_name_doc,
+ "Active theme in window, None = default"
+);
+static PyObject *PyWindow_theme_name_get(PyWindow *self, void *closure)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(self->data->theme_name);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyWindow_getseters[] = {
+ {"refnum", (getter)PyWindow_refnum_get, NULL,
+ PyWindow_refnum_doc, NULL},
+ {"name", (getter)PyWindow_name_get, NULL,
+ PyWindow_name_doc, NULL},
+ {"width", (getter)PyWindow_width_get, NULL,
+ PyWindow_width_doc, NULL},
+ {"height", (getter)PyWindow_height_get, NULL,
+ PyWindow_height_doc, NULL},
+ {"history_name", (getter)PyWindow_history_name_get, NULL,
+ PyWindow_history_name_doc, NULL},
+ {"active", (getter)PyWindow_active_get, NULL,
+ PyWindow_active_doc, NULL},
+ {"active_server", (getter)PyWindow_active_server_get, NULL,
+ PyWindow_active_server_doc, NULL},
+ {"servertag", (getter)PyWindow_servertag_get, NULL,
+ PyWindow_servertag_doc, NULL},
+ {"level", (getter)PyWindow_level_get, NULL,
+ PyWindow_level_doc, NULL},
+ {"sticky_refnum", (getter)PyWindow_sticky_refnum_get, NULL,
+ PyWindow_sticky_refnum_doc, NULL},
+ {"data_level", (getter)PyWindow_data_level_get, NULL,
+ PyWindow_data_level_doc, NULL},
+ {"hilight_color", (getter)PyWindow_hilight_color_get, NULL,
+ PyWindow_hilight_color_doc, NULL},
+ {"last_timestamp", (getter)PyWindow_last_timestamp_get, NULL,
+ PyWindow_last_timestamp_doc, NULL},
+ {"last_line", (getter)PyWindow_last_line_get, NULL,
+ PyWindow_last_line_doc, NULL},
+ {"theme_name", (getter)PyWindow_theme_name_get, NULL,
+ PyWindow_theme_name_doc, NULL},
+ {NULL}
+};
+
+/* Methods */
+PyDoc_STRVAR(PyWindow_items_doc,
+ "items() -> list of WindowItem objects\n"
+ "\n"
+ "Return a list of items in window.\n"
+);
+static PyObject *PyWindow_items(PyWindow *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+ return py_irssi_chatlist_new(self->data->items, 1);
+}
+
+PyDoc_STRVAR(PyWindow_prnt_doc,
+ "prnt(str, level=MSGLEVEL_CLIENTNOTICE) -> None\n"
+ "\n"
+ "Print to window\n"
+);
+static PyObject *PyWindow_prnt(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", "level", NULL};
+ char *str = "";
+ int level = MSGLEVEL_CLIENTNOTICE;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist,
+ &str, &level))
+ return NULL;
+
+ printtext_string_window(self->data, level, str);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_command_doc,
+ "command(cmd) -> None\n"
+ "\n"
+ "Send command to window\n"
+);
+static PyObject *PyWindow_command(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", NULL};
+ char *cmd = "";
+ WINDOW_REC *old;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &cmd))
+ return NULL;
+
+ old = active_win;
+ active_win = self->data;
+ py_command(cmd, active_win->active_server, active_win->active);
+ if (g_slist_find(windows, old) != NULL)
+ active_win = old;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_item_add_doc,
+ "item_add(item, automatic=False) -> None\n"
+ "\n"
+ "Add window item\n"
+);
+static PyObject *PyWindow_item_add(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", "automatic", NULL};
+ PyObject *item = NULL;
+ int automatic = 0;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist,
+ &item, &automatic))
+ return NULL;
+
+ if (!pywindow_item_check(item))
+ return PyErr_Format(PyExc_TypeError, "item must be window item");
+
+ window_item_add(self->data, ((PyWindowItem*)item)->data, automatic);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_item_remove_doc,
+ "item_remove(item) -> None\n"
+ "\n"
+ "Remove window item\n"
+);
+static PyObject *PyWindow_item_remove(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", NULL};
+ PyObject *item = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &item))
+ return NULL;
+
+ if (!pywindow_item_check(item))
+ return PyErr_Format(PyExc_TypeError, "item must be window item");
+
+ window_item_remove(((PyWindowItem*)item)->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_item_destroy_doc,
+ "item_destroy(item) -> None\n"
+ "\n"
+ "Destroy window item\n"
+);
+static PyObject *PyWindow_item_destroy(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", NULL};
+ PyObject *item = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &item))
+ return NULL;
+
+ if (!pywindow_item_check(item))
+ return PyErr_Format(PyExc_TypeError, "item must be window item");
+
+ window_item_destroy(((PyWindowItem*)item)->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_item_prev_doc,
+ "item_prev() -> None\n"
+ "\n"
+ "Change to previous window item\n"
+);
+static PyObject *PyWindow_item_prev(PyWindow *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_item_prev(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_item_next_doc,
+ "item_next() -> None\n"
+ "\n"
+ "Change to next window item\n"
+);
+static PyObject *PyWindow_item_next(PyWindow *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_item_next(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_destroy_doc,
+ "destroy() -> None\n"
+ "\n"
+ "Destroy the window.\n"
+);
+static PyObject *PyWindow_destroy(PyWindow *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_destroy(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_set_active_doc,
+ "set_active() -> None\n"
+ "\n"
+ "Set window active.\n"
+);
+static PyObject *PyWindow_set_active(PyWindow *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_set_active(self->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_change_server_doc,
+ "change_server(server) -> None\n"
+ "\n"
+ "Change server in window\n"
+);
+static PyObject *PyWindow_change_server(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"server", NULL};
+ PyObject *server = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist,
+ &server))
+ return NULL;
+
+ if (!pyserver_check(server))
+ return PyErr_Format(PyExc_TypeError, "arg must be server");
+
+ window_change_server(self->data, ((PyServer*)server)->data);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_set_refnum_doc,
+ "set_refnum(refnum) -> None\n"
+ "\n"
+ "Set window refnum\n"
+);
+static PyObject *PyWindow_set_refnum(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"refnum", NULL};
+ int refnum = 0;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &refnum))
+ return NULL;
+
+ window_set_refnum(self->data, refnum);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_set_name_doc,
+ "set_name(name) -> None\n"
+ "\n"
+ "Set window name\n"
+);
+static PyObject *PyWindow_set_name(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ window_set_name(self->data, name);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_set_history_doc,
+ "set_history(history) -> None\n"
+ "\n"
+ "Set window history\n"
+);
+static PyObject *PyWindow_set_history(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"history", NULL};
+ char *history = "";
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &history))
+ return NULL;
+
+ window_set_history(self->data, history);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_set_level_doc,
+ "set_level(level) -> None\n"
+ "\n"
+ "Set window level\n"
+);
+static PyObject *PyWindow_set_level(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"level", NULL};
+ int level = 0;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &level))
+ return NULL;
+
+ window_set_level(self->data, level);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_activity_doc,
+ "activity(data_level, hilight_color) -> None\n"
+ "\n"
+);
+static PyObject *PyWindow_activity(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"data_level", "hilight_color", NULL};
+ int data_level = 0;
+ char *hilight_color = NULL;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|s", kwlist,
+ &data_level, &hilight_color))
+ return NULL;
+
+ window_activity(self->data, data_level, hilight_color);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PyWindow_get_active_name_doc,
+ "get_active_name() -> str or None\n"
+ "\n"
+ "Return active item's name, or if none is active, window's name.\n"
+);
+static PyObject *PyWindow_get_active_name(PyWindow *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+ RET_AS_STRING_OR_NONE(window_get_active_name(self->data));
+}
+
+PyDoc_STRVAR(PyWindow_item_find_doc,
+ "item_find(server, name) -> WindowItem or None\n"
+ "\n"
+ "Find window item that matches best to given arguments\n"
+);
+static PyObject *PyWindow_item_find(PyWindow *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"server", "name", NULL};
+ PyObject *server = NULL;
+ char *name = "";
+ WI_ITEM_REC *witem;
+
+ RET_NULL_IF_INVALID(self->data);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "Os", kwlist,
+ &server, &name))
+ return NULL;
+
+ if (!pyserver_check(server))
+ return PyErr_Format(PyExc_TypeError, "arg 1 must be server");
+
+ witem = window_item_find_window(self->data, ((PyServer*)server)->data, name);
+ return py_irssi_chat_new(witem, 1);
+}
+
+static PyMethodDef PyWindow_methods[] = {
+ {"items", (PyCFunction)PyWindow_items, METH_NOARGS,
+ PyWindow_items_doc},
+ {"prnt", (PyCFunction)PyWindow_prnt, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_prnt_doc},
+ {"command", (PyCFunction)PyWindow_command, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_command_doc},
+ {"item_add", (PyCFunction)PyWindow_item_add, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_item_add_doc},
+ {"item_remove", (PyCFunction)PyWindow_item_remove, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_item_remove_doc},
+ {"item_destroy", (PyCFunction)PyWindow_item_destroy, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_item_destroy_doc},
+ {"item_prev", (PyCFunction)PyWindow_item_prev, METH_NOARGS,
+ PyWindow_item_prev_doc},
+ {"item_next", (PyCFunction)PyWindow_item_next, METH_NOARGS,
+ PyWindow_item_next_doc},
+ {"destroy", (PyCFunction)PyWindow_destroy, METH_NOARGS,
+ PyWindow_destroy_doc},
+ {"set_active", (PyCFunction)PyWindow_set_active, METH_NOARGS,
+ PyWindow_set_active_doc},
+ {"change_server", (PyCFunction)PyWindow_change_server, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_change_server_doc},
+ {"set_refnum", (PyCFunction)PyWindow_set_refnum, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_set_refnum_doc},
+ {"set_name", (PyCFunction)PyWindow_set_name, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_set_name_doc},
+ {"set_history", (PyCFunction)PyWindow_set_history, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_set_history_doc},
+ {"set_level", (PyCFunction)PyWindow_set_level, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_set_level_doc},
+ {"activity", (PyCFunction)PyWindow_activity, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_activity_doc},
+ {"get_active_name", (PyCFunction)PyWindow_get_active_name, METH_NOARGS,
+ PyWindow_get_active_name_doc},
+ {"item_find", (PyCFunction)PyWindow_item_find, METH_VARARGS | METH_KEYWORDS,
+ PyWindow_item_find_doc},
+ {NULL} /* Sentinel */
+};
+
+PyTypeObject PyWindowType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Window", /*tp_name*/
+ sizeof(PyWindow), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)PyWindow_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "PyWindow objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PyWindow_methods, /* tp_methods */
+ 0, /* tp_members */
+ PyWindow_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyWindow_new, /* tp_new */
+};
+
+
+/* window item wrapper factory function */
+PyObject *pywindow_new(void *win)
+{
+ PyWindow *pywindow;
+
+ pywindow = py_inst(PyWindow, PyWindowType);
+ if (!pywindow)
+ return NULL;
+
+ pywindow->data = win;
+ pywindow->cleanup_installed = 1;
+ signal_add_last_data("window destroyed", window_cleanup, pywindow);
+
+ return (PyObject *)pywindow;
+}
+
+int window_object_init(void)
+{
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ if (PyType_Ready(&PyWindowType) < 0)
+ return 0;
+
+ Py_INCREF(&PyWindowType);
+ PyModule_AddObject(py_module, "Window", (PyObject *)&PyWindowType);
+
+ return 1;
+}
diff --git a/src/objects/window-object.h b/src/objects/window-object.h
new file mode 100644
index 0000000..b9860af
--- /dev/null
+++ b/src/objects/window-object.h
@@ -0,0 +1,21 @@
+#ifndef _WINDOW_OBJECT_H_
+#define _WINDOW_OBJECT_H_
+
+#include <Python.h>
+#include "base-objects.h"
+
+/* forward */
+struct _WINDOW_REC;
+
+typedef struct
+{
+ PyIrssiFinal_HEAD(struct _WINDOW_REC)
+} PyWindow;
+
+extern PyTypeObject PyWindowType;
+
+int window_object_init(void);
+PyObject *pywindow_new(void *win);
+#define pywindow_check(op) PyObject_TypeCheck(op, &PyWindowType)
+
+#endif
diff --git a/src/pyconstants.h b/src/pyconstants.h
new file mode 100644
index 0000000..f898ded
--- /dev/null
+++ b/src/pyconstants.h
@@ -0,0 +1,7 @@
+#ifndef _PYCONSTANTS_H_
+#define _PYCONSTANTS_H_
+
+void pyconstants_init(void);
+
+#endif
+
diff --git a/src/pycore.c b/src/pycore.c
new file mode 100644
index 0000000..f9e4c3d
--- /dev/null
+++ b/src/pycore.c
@@ -0,0 +1,160 @@
+#include <Python.h>
+#include <string.h>
+#include <signal.h>
+#include <assert.h>
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyloader.h"
+#include "pymodule.h"
+#include "pysignals.h"
+#include "pythemes.h"
+#include "pystatusbar.h"
+#include "pyconstants.h"
+#include "factory.h"
+
+/*XXX: copy parse into utils */
+static void cmd_exec(const char *data)
+{
+ PyObject *co;
+ PyObject *ret;
+ PyObject *d;
+ PyObject *m;
+ char *cmd;
+
+ if (!*data)
+ cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+ cmd = g_strconcat(data, "\n", NULL);
+
+ m = PyImport_AddModule("__main__");
+ if (!m)
+ goto error;
+
+ d = PyModule_GetDict(m);
+ if (!d)
+ goto error;
+
+ co = Py_CompileString(cmd, "<stdin>", Py_single_input);
+ if (!co)
+ goto error;
+
+ ret = PyEval_EvalCode((PyCodeObject *)co, d, d);
+ Py_DECREF(co);
+ Py_XDECREF(ret);
+
+error:
+ g_free(cmd);
+ if (PyErr_Occurred())
+ PyErr_Print();
+}
+
+static void cmd_load(const char *data)
+{
+ char **argv;
+
+ argv = g_strsplit(data, " ", -1);
+ if (*argv == NULL || **argv == '\0')
+ {
+ g_strfreev(argv);
+ cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+ }
+
+ pyloader_load_script_argv(argv);
+ g_strfreev(argv);
+}
+
+static void cmd_unload(const char *data)
+{
+ void *free_arg;
+ char *script;
+
+ if (!cmd_get_params(data, &free_arg, 1, &script))
+ return;
+
+ if (*script == '\0')
+ cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+ pyloader_unload_script(script);
+
+ cmd_params_free(free_arg);
+}
+
+static void cmd_list()
+{
+ char buf[128];
+ GSList *list;
+
+ list = pyloader_list();
+
+ g_snprintf(buf, sizeof(buf), "%-15s %s", "Name", "File");
+
+ if (list != NULL)
+ {
+ GSList *node;
+
+ printtext_string(NULL, NULL, MSGLEVEL_CLIENTCRAP, buf);
+ for (node = list; node != NULL; node = node->next)
+ {
+ PY_LIST_REC *item = node->data;
+ g_snprintf(buf, sizeof(buf), "%-15s %s", item->name, item->file);
+
+ printtext_string(NULL, NULL, MSGLEVEL_CLIENTCRAP, buf);
+ }
+ }
+ else
+ printtext_string(NULL, NULL, MSGLEVEL_CLIENTERROR, "No python scripts are loaded");
+
+ pyloader_list_destroy(&list);
+}
+
+#if 0
+/* why doesn't this get called? */
+static void intr_catch(int sig)
+{
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "got sig %d", sig);
+ PyErr_SetInterrupt();
+}
+#endif
+
+void irssi_python_init(void)
+{
+ Py_InitializeEx(0);
+
+ pysignals_init();
+ pystatusbar_init();
+ if (!pyloader_init() || !pymodule_init() || !factory_init() || !pythemes_init())
+ {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Failed to load Python");
+ return;
+ }
+ pyconstants_init();
+
+ /*PyImport_ImportModule("irssi_startup");*/
+ /* Install the custom output handlers, import hook and reload function */
+ /* XXX: handle import error */
+ PyRun_SimpleString(
+ "import irssi_startup\n"
+ );
+
+ //assert(signal(SIGINT, intr_catch) != SIG_ERR);
+
+ command_bind("pyload", NULL, (SIGNAL_FUNC) cmd_load);
+ command_bind("pyunload", NULL, (SIGNAL_FUNC) cmd_unload);
+ command_bind("pylist", NULL, (SIGNAL_FUNC) cmd_list);
+ command_bind("pyexec", NULL, (SIGNAL_FUNC) cmd_exec);
+ module_register(MODULE_NAME, "core");
+}
+
+void irssi_python_deinit(void)
+{
+ command_unbind("pyload", (SIGNAL_FUNC) cmd_load);
+ command_unbind("pyunload", (SIGNAL_FUNC) cmd_unload);
+ command_unbind("pylist", (SIGNAL_FUNC) cmd_list);
+ command_unbind("pyexec", (SIGNAL_FUNC) cmd_exec);
+
+ pymodule_deinit();
+ pyloader_deinit();
+ pystatusbar_deinit();
+ pysignals_deinit();
+ Py_Finalize();
+}
diff --git a/src/pycore.h b/src/pycore.h
new file mode 100644
index 0000000..ae51924
--- /dev/null
+++ b/src/pycore.h
@@ -0,0 +1,7 @@
+#ifndef _PYCORE_H
+#define _PYCORE_H
+
+void irssi_python_init(void);
+void irssi_python_deinit(void);
+
+#endif
diff --git a/src/pyirssi.h b/src/pyirssi.h
new file mode 100644
index 0000000..3fc4223
--- /dev/null
+++ b/src/pyirssi.h
@@ -0,0 +1,32 @@
+#ifndef _PYIRSSI_H_
+#define _PYIRSSI_H_
+
+#define MODULE_NAME "irssi_python"
+#include "config.h"
+#include "core.h"
+#include "common.h"
+#include "modules.h"
+#include "commands.h"
+#include "settings.h"
+#include "printtext.h"
+#include "statusbar.h"
+#include "mainwindows.h"
+#include "window-items.h"
+#include "window-activity.h"
+#include "levels.h"
+#include "servers.h"
+#include "chat-protocols.h"
+#include "channels.h"
+#include "queries.h"
+#include "nicklist.h"
+#include "chatnets.h"
+#include "servers-reconnect.h"
+#include "masks.h"
+#include "misc.h"
+#include "rawlog.h"
+#include "log.h"
+#include "ignore.h"
+#include "fe-exec.h"
+#include "pidwait.h"
+
+#endif
diff --git a/src/pyirssi_irc.h b/src/pyirssi_irc.h
new file mode 100644
index 0000000..0ebd617
--- /dev/null
+++ b/src/pyirssi_irc.h
@@ -0,0 +1,20 @@
+#ifndef _PY_IRSSI_IRC_H_
+#define _PY_IRSSI_IRC_H_
+
+#include "pyirssi.h"
+#include "irc.h"
+#include "irc-servers.h"
+#include "servers-redirect.h"
+#include "irc-channels.h"
+#include "ctcp.h"
+#include "mode-lists.h"
+#include "bans.h"
+#include "dcc.h"
+#include "dcc-get.h"
+#include "dcc-send.h"
+#include "dcc-chat.h"
+#include "netsplit.h"
+#include "notifylist.h"
+#include "irc-masks.h"
+
+#endif
diff --git a/src/pyloader.c b/src/pyloader.c
new file mode 100644
index 0000000..a087827
--- /dev/null
+++ b/src/pyloader.c
@@ -0,0 +1,358 @@
+#include <Python.h>
+#include <frameobject.h>
+#include <string.h>
+#include "pyirssi.h"
+#include "pyloader.h"
+#include "pyutils.h"
+#include "pyscript-object.h"
+
+/* List of loaded modules */
+static PyObject *script_modules;
+
+/* List of load paths for scripts */
+static GSList *script_paths = NULL;
+
+static PyObject *py_get_script(const char *name, int *id);
+static int py_load_module(PyObject *module, const char *path);
+static char *py_find_script(const char *name);
+
+/* Add to the list of script load paths */
+void pyloader_add_script_path(const char *path)
+{
+ PyObject *ppath = PySys_GetObject("path");
+ if (ppath)
+ {
+ PyList_Append(ppath, PyString_FromString(path));
+ script_paths = g_slist_append(script_paths, g_strdup(path));
+ }
+}
+
+/* Loads a file into a module; it is not inserted into sys.modules */
+static int py_load_module(PyObject *module, const char *path)
+{
+ PyObject *dict, *ret, *fp;
+
+ if (PyModule_AddStringConstant(module, "__file__", (char *)path) < 0)
+ return 0;
+
+ dict = PyModule_GetDict(module);
+
+ if (PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()) < 0)
+ return 0;
+
+ /* Dont use the standard library to avoid incompatabilities with
+ the FILE structure and Python */
+ fp = PyFile_FromString((char *)path, "r");
+ if (!fp)
+ return 0;
+
+ ret = PyRun_File(PyFile_AsFile(fp), path, Py_file_input, dict, dict);
+ Py_DECREF(fp); /* XXX: I assume that the file is closed when refs drop to zero? */
+ if (!ret)
+ return 0;
+
+ Py_DECREF(ret);
+ return 1;
+
+}
+
+/* looks up name in Irssi script directories
+ returns full path or NULL if not found */
+static char *py_find_script(const char *name)
+{
+ GSList *node;
+ char *fname;
+ char *path = NULL;
+
+ //XXX: what if there's another ext?
+ if (!file_has_ext(name, "py"))
+ fname = g_strdup_printf("%s.py", name);
+ else
+ fname = (char *)name;
+
+ /*XXX: use case insensitive path search? */
+ for (node = script_paths; node && !path; node = node->next)
+ {
+ path = g_strdup_printf("%s/%s", (char *)node->data, fname);
+
+ if (!g_file_test(path, G_FILE_TEST_IS_REGULAR))
+ {
+ g_free(path);
+ path = NULL;
+ }
+ }
+
+ if (fname != name)
+ g_free(fname);
+
+ return path;
+}
+
+/* Load a script manually using PyRun_File.
+ * This expects a null terminated array of strings
+ * (such as from g_strsplit) of the command line.
+ * The array needs at least one item
+ */
+int pyloader_load_script_argv(char **argv)
+{
+ PyObject *module = NULL, *script = NULL;
+ char *name = NULL, *path = NULL;
+
+ if (py_get_script(argv[0], NULL) != NULL)
+ {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "script %s already loaded", argv[0]);
+ return 0;
+ }
+
+ path = py_find_script(argv[0]);
+ if (!path)
+ {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "script %s does not exist", argv[0]);
+ return 0;
+ }
+
+ name = file_get_filename(path);
+ module = PyModule_New(name);
+ g_free(name);
+ if (!module)
+ goto error;
+
+ script = pyscript_new(module, argv);
+ Py_DECREF(module);
+ if (!script)
+ goto error;
+
+ /* insert script obj into module dict, load file */
+ if (PyModule_AddObject(module, "_script", script) != 0)
+ goto error;
+
+ Py_INCREF(script);
+
+ if (!py_load_module(module, path))
+ goto error;
+
+ if (PyList_Append(script_modules, script) != 0)
+ goto error;
+
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "loaded script %s", argv[0]);
+ /* PySys_WriteStdout("load %s, script -> 0x%x\n", argv[0], script); */
+
+ Py_DECREF(script);
+ g_free(path);
+
+ return 1;
+
+error:
+ if (PyErr_Occurred())
+ PyErr_Print();
+ else
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "error loading script %s", argv[0]);
+
+ if (script)
+ {
+ /* make sure to clean up any formats, signals, commands that may have been
+ attached before the exception took place */
+ pyscript_cleanup(script);
+ Py_DECREF(script);
+ }
+
+ g_free(path);
+
+ return 0;
+}
+
+int pyloader_load_script(char *name)
+{
+ char *argv[2];
+
+ argv[0] = name;
+ argv[1] = NULL;
+
+ return pyloader_load_script_argv(argv);
+}
+
+static PyObject *py_get_script(const char *name, int *id)
+{
+ int i;
+
+ g_return_val_if_fail(script_modules != NULL, NULL);
+
+ for (i = 0; i < PyList_Size(script_modules); i++)
+ {
+ PyObject *script;
+ char *sname;
+
+ script = PyList_GET_ITEM(script_modules, i);
+ sname = pyscript_get_name(script);
+
+ if (sname && !strcmp(sname, name))
+ {
+ if (id)
+ *id = i;
+ return script;
+ }
+ }
+
+ return NULL;
+}
+
+int pyloader_unload_script(const char *name)
+{
+ int id;
+ PyObject *script = py_get_script(name, &id);
+
+ if (!script)
+ {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s is not loaded", name);
+ return 0;
+ }
+
+ PySys_WriteStdout("unload %s, script -> 0x%x\n", name, script);
+
+ pyscript_cleanup(script);
+
+ if (PySequence_DelItem(script_modules, id) < 0)
+ {
+ PyErr_Print();
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "error unloading script %s", name);
+ return 0;
+ }
+
+ /* Probably a good time to call the garbage collecter to clean up reference cycles */
+ PyGC_Collect();
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "unloaded script %s", name);
+
+ return 1;
+}
+
+/* Traverse stack backwards to find the nearest valid _script object in globals */
+PyObject *pyloader_find_script_obj(void)
+{
+ PyFrameObject *frame;
+
+ for (frame = PyEval_GetFrame(); frame != NULL; frame = frame->f_back)
+ {
+ g_return_val_if_fail(frame->f_globals != NULL, NULL);
+ PyObject *script = PyDict_GetItemString(frame->f_globals, "_script");
+ if (script && pyscript_check(script))
+ {
+ /*
+ PySys_WriteStdout("Found script at %s in %s, script -> 0x%x\n",
+ PyString_AS_STRING(frame->f_code->co_name),
+ PyString_AS_STRING(frame->f_code->co_filename), script);
+ */
+ return script;
+ }
+ }
+
+ return NULL;
+}
+
+char *pyloader_find_script_name(void)
+{
+ PyObject *script = pyloader_find_script_obj();
+
+ if (!script)
+ return NULL;
+
+ return pyscript_get_name(script);
+}
+
+GSList *pyloader_list(void)
+{
+ int i;
+ GSList *list = NULL;
+
+ g_return_val_if_fail(script_modules != NULL, NULL);
+
+ for (i = 0; i < PyList_Size(script_modules); i++)
+ {
+ PyObject *scr;
+ char *name, *file;
+
+ scr = PyList_GET_ITEM(script_modules, i);
+ name = pyscript_get_name(scr);
+ file = pyscript_get_filename(scr);
+
+ if (name && file)
+ {
+ PY_LIST_REC *rec;
+ rec = g_new0(PY_LIST_REC, 1);
+
+ rec->name = g_strdup(name);
+ rec->file = g_strdup(file);
+ list = g_slist_append(list, rec);
+ }
+ }
+
+ return list;
+}
+
+void pyloader_list_destroy(GSList **list)
+{
+ GSList *node;
+
+ if (*list == NULL)
+ return;
+
+ for (node = *list; node != NULL; node = node->next)
+ {
+ PY_LIST_REC *rec = node->data;
+
+ g_free(rec->name);
+ g_free(rec->file);
+ g_free(rec);
+ }
+
+ g_slist_free(*list);
+
+ *list = NULL;
+}
+
+int pyloader_init(void)
+{
+ char *pyhome;
+
+ g_return_val_if_fail(script_paths == NULL, 0);
+ g_return_val_if_fail(script_modules == NULL, 0);
+
+ script_modules = PyList_New(0);
+ if (!script_modules)
+ return 0;
+
+ /* XXX: load autorun scripts here */
+ /* Add script location to the load path (add more paths later) */
+ pyhome = g_strdup_printf("%s/scripts", get_irssi_dir());
+ pyloader_add_script_path(pyhome);
+ g_free(pyhome);
+
+ return 1;
+}
+
+static void py_clear_scripts()
+{
+ int i;
+
+ for (i = 0; i < PyList_Size(script_modules); i++)
+ {
+ PyObject *scr = PyList_GET_ITEM(script_modules, i);
+ pyscript_cleanup(scr);
+ }
+
+ Py_DECREF(script_modules);
+}
+
+void pyloader_deinit(void)
+{
+ GSList *node;
+
+ g_return_if_fail(script_paths != NULL);
+ g_return_if_fail(script_modules != NULL);
+
+ for (node = script_paths; node != NULL; node = node->next)
+ g_free(node->data);
+ g_slist_free(script_paths);
+ script_paths = NULL;
+
+ py_clear_scripts();
+}
diff --git a/src/pyloader.h b/src/pyloader.h
new file mode 100644
index 0000000..b7befd3
--- /dev/null
+++ b/src/pyloader.h
@@ -0,0 +1,23 @@
+#ifndef _PYLOADER_H_
+#define _PYLOADER_H_
+
+typedef struct
+{
+ char *name;
+ char *file;
+} PY_LIST_REC;
+
+void pyloader_add_script_path(const char *path);
+int pyloader_load_script_argv(char **argv);
+int pyloader_load_script(char *name);
+int pyloader_unload_script(const char *name);
+PyObject *pyloader_find_script_obj(void);
+char *pyloader_find_script_name(void);
+
+GSList *pyloader_list(void);
+void pyloader_list_destroy(GSList **list);
+
+int pyloader_init(void);
+void pyloader_deinit(void);
+
+#endif
diff --git a/src/pymodule.c b/src/pymodule.c
new file mode 100644
index 0000000..7795c1a
--- /dev/null
+++ b/src/pymodule.c
@@ -0,0 +1,1909 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "pyirssi_irc.h"
+#include "factory.h"
+#include "pyutils.h"
+#include "pysignals.h"
+#include "pyloader.h"
+#include "pythemes.h"
+#include "pystatusbar.h"
+
+/*
+ * This module is some what different than the Perl's.
+ * Script specific operations are handled by the Script object
+ * instead of by a function in this module. command_bind,
+ * signal_add, etc require data to be saved about the script
+ * for cleanup purposes, so I moved those functions to the script
+ * object.
+ */
+
+/* Main embedded module */
+PyObject *py_module = NULL;
+
+/* Module functions */
+/*XXX: prefix PY to avoid ambiguity with py_command function */
+PyDoc_STRVAR(PY_command_doc,
+ "command(cmd) -> None\n"
+ "\n"
+ "Execute command\n"
+);
+static PyObject *PY_command(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", NULL};
+ char *cmd = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &cmd))
+ return NULL;
+
+ py_command(cmd, NULL, NULL);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_prnt_doc,
+ "prnt(text, msglvl=MSGLEVEL_CLIENTNOTICE) -> None\n"
+ "\n"
+ "print output\n"
+);
+/*XXX: print is a python keyword, so abbreviate it */
+static PyObject *py_prnt(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = {"text", "msglvl", NULL};
+ int msglvl = MSGLEVEL_CLIENTNOTICE;
+ char *text = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i:prnt", kwlist,
+ &text, &msglvl))
+ return NULL;
+
+ printtext_string(NULL, NULL, msglvl, text);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_get_script_doc,
+ "get_script() -> Script object\n"
+ "\n"
+ "Find current Irssi script object\n"
+);
+static PyObject *py_get_script(PyObject *self, PyObject *args)
+{
+ PyObject *ret = pyloader_find_script_obj();
+
+ /* XXX: type check */
+
+ if (!ret)
+ PyErr_SetString(PyExc_RuntimeError, "unable to find script object");
+ else
+ Py_INCREF(ret);
+
+ return ret;
+}
+
+PyDoc_STRVAR(py_chatnet_find_doc,
+ "chatnet_find(name) -> Chatnet object or None\n"
+ "\n"
+ "Find chat network with name\n"
+);
+static PyObject *py_chatnet_find(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ return py_irssi_chat_new(chatnet_find(name), 1);
+}
+
+PyDoc_STRVAR(py_chatnets_doc,
+ "chatnets() -> list of Chatnet objects\n"
+ "\n"
+ "Return a list of all chatnets\n"
+);
+static PyObject *py_chatnets(PyObject *self, PyObject *args)
+{
+ return py_irssi_chatlist_new(chatnets, 1);
+}
+
+PyDoc_STRVAR(py_reconnects_doc,
+ "reconnects() -> list of Reconnect objects\n"
+ "\n"
+ "Return a list of all reconnects\n"
+);
+static PyObject *py_reconnects(PyObject *self, PyObject *args)
+{
+ return py_irssi_objlist_new(reconnects, 1, (InitFunc)pyreconnect_new);
+}
+
+PyDoc_STRVAR(py_servers_doc,
+ "servers() -> list of Server objects\n"
+ "\n"
+ "Return a list of all servers\n"
+);
+static PyObject *py_servers(PyObject *self, PyObject *args)
+{
+ return py_irssi_chatlist_new(servers, 1);
+}
+
+PyDoc_STRVAR(py_channels_doc,
+ "channels() -> list of Channel objects\n"
+ "\n"
+ "Return channel list\n"
+);
+static PyObject *py_channels(PyObject *self, PyObject *args)
+{
+ return py_irssi_chatlist_new(channels, 1);
+}
+
+PyDoc_STRVAR(py_channel_find_doc,
+ "channel_find(name) -> Channel object or None\n"
+ "\n"
+ "Find channel from any server\n"
+);
+static PyObject *py_channel_find(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ return py_irssi_chat_new(channel_find(NULL, name), 1);
+}
+
+PyDoc_STRVAR(py_windows_doc,
+ "windows() -> list of Window objects\n"
+ "\n"
+ "Get a list of all windows\n"
+);
+static PyObject *py_windows(PyObject *self, PyObject *args)
+{
+ return py_irssi_objlist_new(windows, 1, (InitFunc)pywindow_new);
+}
+
+PyDoc_STRVAR(py_active_win_doc,
+ "active_win() -> Window object\n"
+ "\n"
+ "Return active window\n"
+);
+static PyObject *py_active_win(PyObject *self, PyObject *args)
+{
+ if (active_win)
+ return pywindow_new(active_win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_active_server_doc,
+ "active_server() -> Server object or None\n"
+ "\n"
+ "Return active server\n"
+);
+static PyObject *py_active_server(PyObject *self, PyObject *args)
+{
+ if (active_win)
+ return py_irssi_chat_new(active_win->active_server, 1);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_window_find_name_doc,
+ "window_find_name(name) -> Window object or None\n"
+ "\n"
+ "Find window with name\n"
+);
+static PyObject *py_window_find_name(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+ WINDOW_REC *win;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ win = window_find_name(name);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_window_find_refnum_doc,
+ "window_find_refnum(refnum) -> Window object or None\n"
+ "\n"
+ "Find window with reference number\n"
+);
+static PyObject *py_window_find_refnum(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"refnum", NULL};
+ int refnum = 0;
+ WINDOW_REC *win;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &refnum))
+ return NULL;
+
+ win = window_find_refnum(refnum);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_window_refnum_prev_doc,
+ "window_refnum_prev(refnum, wrap) -> int\n"
+ "\n"
+ "Return refnum for window that's previous in window list\n"
+);
+static PyObject *py_window_refnum_prev(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"refnum", "wrap", NULL};
+ int refnum = 0;
+ int wrap = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwlist,
+ &refnum, &wrap))
+ return NULL;
+
+ return PyInt_FromLong(window_refnum_prev(refnum, wrap));
+}
+
+PyDoc_STRVAR(py_window_refnum_next_doc,
+ "window_refnum_next(refnum, wrap) -> int\n"
+ "\n"
+ "Return refnum for window that's next in window list\n"
+);
+static PyObject *py_window_refnum_next(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"refnum", "wrap", NULL};
+ int refnum = 0;
+ int wrap = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwlist,
+ &refnum, &wrap))
+ return NULL;
+
+ return PyInt_FromLong(window_refnum_next(refnum, wrap));
+}
+
+PyDoc_STRVAR(py_windows_refnum_last_doc,
+ "windows_refnum_last() -> int\n"
+ "\n"
+ "Return refnum for last window.\n"
+);
+static PyObject *py_windows_refnum_last(PyObject *self, PyObject *args)
+{
+ return PyInt_FromLong(windows_refnum_last());
+}
+
+PyDoc_STRVAR(py_window_find_level_doc,
+ "window_find_level(level) -> Window object or None\n"
+ "\n"
+ "Find window with level.\n"
+);
+static PyObject *py_window_find_level(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"level", NULL};
+ int level = 0;
+ WINDOW_REC *win;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &level))
+ return NULL;
+
+ win = window_find_level(NULL, level);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_window_find_item_doc,
+ "window_find_item(name) -> Window object or None\n"
+ "\n"
+ "Find window which contains window item with specified name.\n"
+);
+static PyObject *py_window_find_item(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+ WINDOW_REC *win;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ win = window_find_item(NULL, name);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_window_find_closest_doc,
+ "window_find_closest(name, level) -> Window object or None\n"
+ "\n"
+ "Find window that matches best to given arguments. `name' can be either\n"
+ "window name or name of one of the window items.\n"
+);
+static PyObject *py_window_find_closest(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", "level", NULL};
+ char *name = "";
+ int level = 0;
+ WINDOW_REC *win;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist,
+ &name, &level))
+ return NULL;
+
+ win = window_find_closest(NULL, name, level);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_window_item_find_doc,
+ "window_item_find(name) -> WindowItem object or None\n"
+ "\n"
+ "Find window item that matches best to given arguments.\n"
+);
+static PyObject *py_window_item_find(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ return py_irssi_chat_new(window_item_find(NULL, name), 1);
+}
+
+/*XXX: this could be __init__ for Window */
+PyDoc_STRVAR(py_window_create_doc,
+ "window_create(item=None, automatic=False) -> Window object\n"
+ "\n"
+ "Create a new window\n"
+);
+static PyObject *py_window_create(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"item", "automatic", NULL};
+ PyObject *item = NULL;
+ int automatic = 0;
+ WI_ITEM_REC *witem = NULL;
+ WINDOW_REC *win;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi", kwlist,
+ &item, &automatic))
+ return NULL;
+
+ if (item)
+ {
+ if (pywindow_item_check(item))
+ {
+ witem = ((PyWindowItem*)item)->data;
+ if (!witem)
+ return PyErr_Format(PyExc_TypeError, "invalid window item");
+ else if (witem->server != NULL)
+ return PyErr_Format(PyExc_TypeError, "window item already assigned to window");
+ }
+ else if (item == Py_None)
+ ;
+ else
+ return PyErr_Format(PyExc_TypeError, "item must be window item or None");
+ }
+
+ win = window_create(witem, automatic);
+ if (win)
+ return pywindow_new(win);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_server_find_tag_doc,
+ "server_find_tag(tag) -> Server object or None\n"
+ "\n"
+ "Find server with tag\n"
+);
+static PyObject *py_server_find_tag(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"tag", NULL};
+ char *tag = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &tag))
+ return NULL;
+
+ return py_irssi_chat_new(server_find_tag(tag), 1);
+}
+
+PyDoc_STRVAR(py_server_find_chatnet_doc,
+ "server_find_chatnet(chatnet) -> Server object or None\n"
+ "\n"
+ "Find first server that is in chatnet\n"
+);
+static PyObject *py_server_find_chatnet(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"chatnet", NULL};
+ char *chatnet = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &chatnet))
+ return NULL;
+
+ return py_irssi_chat_new(server_find_chatnet(chatnet), 1);
+}
+
+PyDoc_STRVAR(py_queries_doc,
+ "queries() -> list of Query objects\n"
+ "\n"
+ "Return a list of open queries.\n"
+);
+static PyObject *py_queries(PyObject *self, PyObject *args)
+{
+ return py_irssi_chatlist_new(queries, 1);
+}
+
+PyDoc_STRVAR(py_query_find_doc,
+ "query_find(nick) -> Query object or None\n"
+ "\n"
+ "Find a query from any server.\n"
+);
+static PyObject *py_query_find(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", NULL};
+ char *nick = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &nick))
+ return NULL;
+
+ return py_irssi_chat_new(query_find(NULL, nick), 1);
+}
+
+PyDoc_STRVAR(py_mask_match_doc,
+ "mask_match(mask, nick, user, host) -> bool\n"
+ "\n"
+ "Return true if mask matches nick!user@host\n"
+);
+static PyObject *py_mask_match(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"mask", "nick", "user", "host", NULL};
+ char *mask = "";
+ char *nick = "";
+ char *user = "";
+ char *host = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssss", kwlist,
+ &mask, &nick, &user, &host))
+ return NULL;
+
+ return PyBool_FromLong(mask_match(NULL, mask, nick, user, host));
+}
+
+PyDoc_STRVAR(py_mask_match_address_doc,
+ "mask_match_address(mask, nick, address) -> bool\n"
+ "\n"
+ "Return True if mask matches nick!address\n"
+);
+static PyObject *py_mask_match_address(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"mask", "nick", "address", NULL};
+ char *mask = "";
+ char *nick = "";
+ char *address = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &mask, &nick, &address))
+ return NULL;
+
+ return PyBool_FromLong(mask_match_address(NULL, mask, nick, address));
+}
+
+PyDoc_STRVAR(py_masks_match_doc,
+ "masks_match(masks, nick, address) -> bool\n"
+ "\n"
+ "Return True if any mask in the masks (string separated by spaces)\n"
+ "matches nick!address\n"
+);
+static PyObject *py_masks_match(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"masks", "nick", "address", NULL};
+ char *masks = "";
+ char *nick = "";
+ char *address = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist,
+ &masks, &nick, &address))
+ return NULL;
+
+ return PyBool_FromLong(masks_match(NULL, masks, nick, address));
+}
+
+PyDoc_STRVAR(py_rawlog_set_size_doc,
+ "rawlog_set_size(lines) -> None\n"
+ "\n"
+ "Set the default rawlog size for new rawlogs.\n"
+);
+static PyObject *py_rawlog_set_size(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"lines", NULL};
+ int lines = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &lines))
+ return NULL;
+
+ rawlog_set_size(lines);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_logs_doc,
+ "logs() -> list of Log objects\n"
+ "\n"
+ "Return list of logs.\n"
+);
+static PyObject *py_logs(PyObject *self, PyObject *args)
+{
+ return py_irssi_objlist_new(logs, 1, (InitFunc)pylog_new);
+}
+
+PyDoc_STRVAR(py_log_find_doc,
+ "log_find(fname) -> Log object or None\n"
+ "\n"
+ "Find log by file name.\n"
+);
+static PyObject *py_log_find(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"fname", NULL};
+ char *fname = "";
+ LOG_REC *log;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &fname))
+ return NULL;
+
+ log = log_find(fname);
+ if (log)
+ return pylog_new(log);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_ignores_doc,
+ "ignores() -> list of Ignore objects\n"
+ "\n"
+ "Return a list of ignore entries\n"
+);
+static PyObject *py_ignores(PyObject *self, PyObject *args)
+{
+ return py_irssi_objlist_new(ignores, 1, (InitFunc)pyignore_new);
+}
+
+PyDoc_STRVAR(py_ignore_check_doc,
+ "ignore_check(nick, host, channel, text, level) -> bool\n"
+ "\n"
+ "Return True if ignore matches\n"
+);
+static PyObject *py_ignore_check(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", "host", "channel", "text", "level", NULL};
+ char *nick = "";
+ char *host = "";
+ char *channel = "";
+ char *text = "";
+ int level = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssssi", kwlist,
+ &nick, &host, &channel, &text, &level))
+ return NULL;
+
+ return PyBool_FromLong(ignore_check(NULL, nick, host, channel, text, level));
+}
+
+PyDoc_STRVAR(py_dccs_doc,
+ "dccs() -> list of Dcc objects\n"
+ "\n"
+ "Return list of active DCCs\n"
+);
+static PyObject *py_dccs(PyObject *self, PyObject *args)
+{
+ return py_irssi_list_new(dcc_conns, 1);
+}
+
+PyDoc_STRVAR(py_dcc_register_type_doc,
+ "dcc_register_type(type) -> None\n"
+ "\n"
+ "???\n"
+);
+static PyObject *py_dcc_register_type(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"type", NULL};
+ char *type = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &type))
+ return NULL;
+
+ dcc_register_type(type);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_dcc_unregister_type_doc,
+ "dcc_unregister_type(type) -> None\n"
+ "\n"
+ "???\n"
+);
+static PyObject *py_dcc_unregister_type(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"type", NULL};
+ char *type = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &type))
+ return NULL;
+
+ dcc_unregister_type(type);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_dcc_find_request_latest_doc,
+ "dcc_find_request_latest(type) -> Dcc object or None\n"
+ "\n"
+ "???\n"
+);
+static PyObject *py_dcc_find_request_latest(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"type", NULL};
+ int type = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &type))
+ return NULL;
+
+ return py_irssi_new(dcc_find_request_latest(type), 1);
+}
+
+PyDoc_STRVAR(py_dcc_find_request_doc,
+ "dcc_find_request(type, nick, arg) -> Dcc object or None\n"
+ "\n"
+ "???\n"
+);
+static PyObject *py_dcc_find_request(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"type", "nick", "arg", NULL};
+ int type = 0;
+ char *nick = "";
+ char *arg = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "iss", kwlist,
+ &type, &nick, &arg))
+ return NULL;
+
+ return py_irssi_new(dcc_find_request(type, nick, arg), 1);
+}
+
+PyDoc_STRVAR(py_dcc_chat_find_id_doc,
+ "dcc_chat_find_id(id) -> Dcc object or None\n"
+ "\n"
+ "???\n"
+);
+static PyObject *py_dcc_chat_find_id(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"id", NULL};
+ char *id = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &id))
+ return NULL;
+
+ return py_irssi_new(dcc_chat_find_id(id), 1);
+}
+
+PyDoc_STRVAR(py_dcc_str2type_doc,
+ "dcc_str2type(type) -> int\n"
+ "\n"
+ "???\n"
+);
+static PyObject *py_dcc_str2type(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"type", NULL};
+ char *type = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &type))
+ return NULL;
+
+ return PyInt_FromLong(dcc_str2type(type));
+}
+
+PyDoc_STRVAR(py_dcc_type2str_doc,
+ "dcc_type2str(type) -> str or None\n"
+ "\n"
+ "???\n"
+);
+static PyObject *py_dcc_type2str(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"type", NULL};
+ int type = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &type))
+ return NULL;
+
+ RET_AS_STRING_OR_NONE(dcc_type2str(type));
+}
+
+PyDoc_STRVAR(py_dcc_get_download_path_doc,
+ "dcc_get_download(fname) -> str\n"
+ "\n"
+ "???\n"
+);
+static PyObject *py_dcc_get_download_path(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"fname", NULL};
+ char *fname = "";
+ char *path;
+ PyObject *pypath;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &fname))
+ return NULL;
+
+ path = dcc_get_download_path(fname);
+ if (!path)
+ Py_RETURN_NONE; /*XXX: how to handle? */
+
+ pypath = PyString_FromString(path);
+ g_free(path);
+
+ return pypath;
+}
+
+PyDoc_STRVAR(py_notifies_doc,
+ "notifies() -> list of Notifylist objects\n"
+ "\n"
+ "Return list of notifies\n"
+);
+static PyObject *py_notifies(PyObject *self, PyObject *args)
+{
+ return py_irssi_objlist_new(notifies, 1, (InitFunc)pynotifylist_new);
+}
+
+PyDoc_STRVAR(py_notifylist_add_doc,
+ "notifylist_add(mask, ircnets=None, away_check=0, idle_time_check=0) -> Notifylist object\n"
+ "\n"
+ "Add new item to notify list\n"
+);
+static PyObject *py_notifylist_add(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"mask", "ircnets", "away_check", "idle_check_time", NULL};
+ char *mask = "";
+ char *ircnets = NULL;
+ int away_check = 0;
+ int idle_check_time = 0;
+ NOTIFYLIST_REC *rec;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|zii", kwlist,
+ &mask, &ircnets, &away_check, &idle_check_time))
+ return NULL;
+
+ rec = notifylist_add(mask, ircnets, away_check, idle_check_time);
+ if (rec)
+ return pynotifylist_new(rec);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_notifylist_remove_doc,
+ "notifylist_remove(mask) -> None\n"
+ "\n"
+ "Remove notify item from notify list\n"
+);
+static PyObject *py_notifylist_remove(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"mask", NULL};
+ char *mask = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &mask))
+ return NULL;
+
+ notifylist_remove(mask);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_notifylist_ison_doc,
+ "notifylist_ison(nick, serverlist=\"\") -> IrcServer object\n"
+ "\n"
+ "Check if nick is in IRC. serverlist is a space separated list of server tags.\n"
+ "If it's empty string, all servers will be checked\n"
+);
+static PyObject *py_notifylist_ison(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"nick", "serverlist", NULL};
+ char *nick = "";
+ char *serverlist = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist,
+ &nick, &serverlist))
+ return NULL;
+
+ return py_irssi_chat_new(notifylist_ison(nick, serverlist), 1);
+}
+
+PyDoc_STRVAR(py_notifylist_find_doc,
+ "notifylist_find(mask, ircnet=None) -> Notifylist object\n"
+ "\n"
+ "Find notify\n"
+);
+static PyObject *py_notifylist_find(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"mask", "ircnet", NULL};
+ char *mask = "";
+ char *ircnet = NULL;
+ NOTIFYLIST_REC *rec;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|z", kwlist,
+ &mask, &ircnet))
+ return NULL;
+
+ rec = notifylist_find(mask, ircnet);
+ if (rec)
+ return pynotifylist_new(rec);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_commands_doc,
+ "commands() -> list of Command objects\n"
+ "\n"
+ "Return a list of all commands.\n"
+);
+static PyObject *py_commands(PyObject *self, PyObject *args)
+{
+ return py_irssi_objlist_new(commands, 1, (InitFunc)pycommand_new);
+}
+
+PyDoc_STRVAR(py_level2bits_doc,
+ "level2bits(level) -> long\n"
+ "\n"
+ "Level string -> number\n"
+);
+static PyObject *py_level2bits(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"level", NULL};
+ char *level = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &level))
+ return NULL;
+
+ return PyLong_FromUnsignedLong(level2bits(level));
+}
+
+PyDoc_STRVAR(py_bits2level_doc,
+ "bits2level(bits) -> str\n"
+ "\n"
+ "Level number -> string\n"
+);
+static PyObject *py_bits2level(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"bits", NULL};
+ unsigned bits;
+ char *str;
+ PyObject *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "I", kwlist,
+ &bits))
+ return NULL;
+
+ str = bits2level(bits);
+ if (str)
+ {
+ ret = PyString_FromString(str);
+ g_free(str);
+ return ret;
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_combine_level_doc,
+ "combine_level(level, str) -> long\n"
+ "\n"
+ "Combine level number to level string ('+level -level'). Return new level number.\n"
+);
+static PyObject *py_combine_level(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"level", "str", NULL};
+ int level = 0;
+ char *str = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "is", kwlist,
+ &level, &str))
+ return NULL;
+
+ return PyLong_FromUnsignedLong(combine_level(level, str));
+}
+
+PyDoc_STRVAR(py_signal_emit_doc,
+ "signal_emit(signal, *args) -> None\n"
+ "\n"
+ "Emit an Irssi signal with up to 6 arguments\n"
+);
+static PyObject *py_signal_emit(PyObject *self, PyObject *args)
+{
+ PyObject *pysig;
+ PyObject *sigargs;
+ char *name;
+ int ret;
+
+ if (PyTuple_Size(args) < 1)
+ return PyErr_Format(PyExc_TypeError, "signal name required");
+
+ if (PyTuple_Size(args) > SIGNAL_MAX_ARGUMENTS+1)
+ return PyErr_Format(PyExc_TypeError,
+ "no more than %d arguments for signal accepted", SIGNAL_MAX_ARGUMENTS);
+
+ pysig = PyTuple_GET_ITEM(args, 0);
+ if (!PyString_Check(pysig))
+ return PyErr_Format(PyExc_TypeError, "signal must be string");
+
+ name = PyString_AsString(pysig);
+ if (!name)
+ return NULL;
+
+ sigargs = PySequence_GetSlice(args, 1, PyTuple_Size(args));
+ if (!sigargs)
+ return NULL;
+
+ ret = pysignals_emit(name, sigargs);
+ Py_DECREF(sigargs);
+ if (!ret)
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_signal_continue_doc,
+ "signal_continue(*args) -> None\n"
+ "\n"
+ "Continue (reemit?) the current Irssi signal with up to 6 arguments\n"
+);
+static PyObject *py_signal_continue(PyObject *self, PyObject *args)
+{
+ if (PyTuple_Size(args) > SIGNAL_MAX_ARGUMENTS)
+ return PyErr_Format(PyExc_TypeError,
+ "no more than %d arguments for signal accepted", SIGNAL_MAX_ARGUMENTS);
+
+ if (!pysignals_continue(args))
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_signal_stop_doc,
+ "signal_stop() -> None\n"
+ "\n"
+ "Stop the signal that's currently being emitted.\n"
+);
+static PyObject *py_signal_stop(PyObject *self, PyObject *args)
+{
+ signal_stop();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_signal_stop_by_name_doc,
+ "signal_stop_by_name(signal) -> None\n"
+ "\n"
+ "Stop the signal, 'signal', thats currently being emitted by name\n"
+);
+static PyObject *py_signal_stop_by_name(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"signal", NULL};
+ char *signal = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &signal))
+ return NULL;
+
+ signal_stop_by_name(signal);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_signal_get_emitted_doc,
+ "signal_get_emmited() -> signal name string\n"
+ "\n"
+ "Get name of current signal\n"
+);
+static PyObject *py_signal_get_emitted(PyObject *self, PyObject *args)
+{
+ RET_AS_STRING_OR_NONE(signal_get_emitted());
+}
+
+PyDoc_STRVAR(py_signal_get_emitted_id_doc,
+ "signal_get_emmited_id() -> signal id int\n"
+ "\n"
+ "Get id of current signal\n"
+);
+static PyObject *py_signal_get_emitted_id(PyObject *self, PyObject *args)
+{
+ return PyInt_FromLong(signal_get_emitted_id());
+}
+
+PyDoc_STRVAR(py_settings_get_str_doc,
+ "settings_get_str(key) -> str\n"
+ "\n"
+ "Get value for setting.\n"
+);
+static PyObject *py_settings_get_str(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", NULL};
+ char *key = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &key))
+ return NULL;
+
+ RET_AS_STRING_OR_NONE(settings_get_str(key));
+}
+
+PyDoc_STRVAR(py_settings_get_int_doc,
+ "settings_get_int(key) -> int\n"
+ "\n"
+ "Get value for setting."
+);
+static PyObject *py_settings_get_int(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", NULL};
+ char *key = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &key))
+ return NULL;
+
+ return PyInt_FromLong(settings_get_int(key));
+}
+
+PyDoc_STRVAR(py_settings_get_bool_doc,
+ "settings_get_bool(key) -> bool\n"
+ "\n"
+ "Get value for setting.\n"
+);
+static PyObject *py_settings_get_bool(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", NULL};
+ char *key = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &key))
+ return NULL;
+
+ return PyBool_FromLong(settings_get_bool(key));
+}
+
+PyDoc_STRVAR(py_settings_get_time_doc,
+ "settings_get_time(key) -> long\n"
+ "\n"
+ "Get value for setting.\n"
+);
+static PyObject *py_settings_get_time(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", NULL};
+ char *key = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &key))
+ return NULL;
+
+ return PyLong_FromLong(settings_get_time(key));
+}
+
+PyDoc_STRVAR(py_settings_get_level_doc,
+ "settings_get_level(key) -> int\n"
+ "\n"
+ "Get value for setting.\n"
+);
+static PyObject *py_settings_get_level(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", NULL};
+ char *key = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &key))
+ return NULL;
+
+ return PyInt_FromLong(settings_get_level(key));
+}
+
+PyDoc_STRVAR(py_settings_get_size_doc,
+ "settings_get_size(key) -> long\n"
+ "\n"
+ "Get value for setting.\n"
+);
+static PyObject *py_settings_get_size(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", NULL};
+ char *key = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &key))
+ return NULL;
+
+ return PyLong_FromLong(settings_get_size(key));
+}
+
+PyDoc_STRVAR(py_settings_set_str_doc,
+ "settings_set_str(key, value) -> None\n"
+ "\n"
+ "Set string value for setting\n"
+);
+static PyObject *py_settings_set_str(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", "value", NULL};
+ char *key = "";
+ char *value = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &key, &value))
+ return NULL;
+
+ settings_set_str(key, value);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_settings_set_int_doc,
+ "settings_set_int(key, value) -> None\n"
+ "\n"
+ "Set int value for setting"
+);
+static PyObject *py_settings_set_int(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", "value", NULL};
+ char *key = "";
+ int value = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist,
+ &key, &value))
+ return NULL;
+
+ settings_set_int(key, value);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_settings_set_bool_doc,
+ "settings_set_bool(key, value) -> None\n"
+ "\n"
+ "Set bool value for setting\n"
+);
+static PyObject *py_settings_set_bool(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", "value", NULL};
+ char *key = "";
+ int value = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist,
+ &key, &value))
+ return NULL;
+
+ settings_set_bool(key, value);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_settings_set_time_doc,
+ "settings_set_time(key, value) -> bool\n"
+ "\n"
+ "Set string value for setting\n"
+);
+static PyObject *py_settings_set_time(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", "value", NULL};
+ char *key = "";
+ char *value = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &key, &value))
+ return NULL;
+
+ return PyBool_FromLong(settings_set_time(key, value));
+}
+
+PyDoc_STRVAR(py_settings_set_level_doc,
+ "settings_set_level(key, value) -> bool\n"
+ "\n"
+ "Set string value for setting\n"
+);
+static PyObject *py_settings_set_level(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", "value", NULL};
+ char *key = "";
+ char *value = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &key, &value))
+ return NULL;
+
+ return PyBool_FromLong(settings_set_level(key, value));
+}
+
+PyDoc_STRVAR(py_settings_set_size_doc,
+ "settings_set_size(key, value) -> bool\n"
+ "\n"
+ "Set string value for setting\n"
+);
+static PyObject *py_settings_set_size(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"key", "value", NULL};
+ char *key = "";
+ char *value = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
+ &key, &value))
+ return NULL;
+
+ return PyBool_FromLong(settings_set_size(key, value));
+}
+
+PyDoc_STRVAR(py_pidwait_add_doc,
+ "pidwait_add(pid) -> None\n"
+ "\n"
+ "Add pid to wait list\n"
+);
+static PyObject *py_pidwait_add(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"pid", NULL};
+ int pid = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &pid))
+ return NULL;
+
+ pidwait_add(pid);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_pidwait_remove_doc,
+ "pidwait_remove(pid) -> None\n"
+ "\n"
+ "Remove pid from wait list\n"
+);
+static PyObject *py_pidwait_remove(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"pid", NULL};
+ int pid = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist,
+ &pid))
+ return NULL;
+
+ pidwait_remove(pid);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_format_get_length_doc,
+ "format_get_length(str) -> int length\n"
+);
+static PyObject *py_format_get_length(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", NULL};
+ char *str = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &str))
+ return NULL;
+
+ return PyInt_FromLong(format_get_length(str));
+}
+
+PyDoc_STRVAR(py_format_real_length_doc,
+ "format_real_length(str, len) -> int length\n"
+);
+static PyObject *py_format_real_length(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"str", "len", NULL};
+ char *str = "";
+ int len;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sl", kwlist,
+ &str, &len))
+ return NULL;
+
+ return PyInt_FromLong(format_real_length(str, len));
+}
+
+PyDoc_STRVAR(py_strip_codes_doc,
+ "strip_codes(input) -> str\n"
+);
+static PyObject *py_strip_codes(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"input", NULL};
+ char *input = "";
+ char *ret;
+ PyObject *pyret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &input))
+ return NULL;
+
+ ret = strip_codes(input);
+ if (ret)
+ {
+ pyret = PyString_FromString(ret);
+ g_free(ret);
+ return pyret;
+ }
+
+ Py_RETURN_NONE;
+}
+
+#if 0
+PyDoc_STRVAR(py_format_get_text_doc,
+ "format_get_text(textdest, module, formatnum, ...) -> str\n"
+ "\n"
+ "Return a substituted format string from module and formatnum.\n"
+ "module is a string.\n"
+ "\n"
+ "Example:\n"
+ "TODO\n"
+);
+static PyObject *py_format_get_text(PyObject *self, PyObject *varargs)
+{
+ PyTextDest *textdest = NULL;
+ char *module = "";
+ unsigned int formatnum = 0;
+ PyObject *args = NULL, *pycharargs = NULL;
+ THEME_REC *theme;
+ MODULE_THEME_REC *modtheme;
+ char **charargs = NULL;
+ char *ret;
+ int i;
+
+ args = PySequence_GetSlice(varargs, 0, 3);
+ if (!args)
+ goto error;
+
+ pycharargs = PySequence_GetSlice(varargs, 3, PyTuple_Size(varargs));
+ if (!pycharargs)
+ goto error;
+
+ if (!PyArg_ParseTuple(args, "OsI",
+ &textdest, &module, &formatnum))
+ goto error;
+
+ if (!pytextdest_check((PyObject *)textdest))
+ {
+ PyErr_Format(PyExc_TypeError, "arg 1 must be TextDest");
+ goto error;
+ }
+
+ /* Bleh, check that formatnum is within range */
+ theme = window_get_theme(textdest->data->window);
+
+ /* FIXME: how to boundscheck formatnum ?? */
+
+ /* size + 1 for terminating NULL ptr */
+ charargs = g_new0(char *, PyTuple_Size(pycharargs) + 1);
+
+ for (i = 0; i < PyTuple_Size(pycharargs); i++)
+ {
+ PyObject *obj = PyTuple_GET_ITEM(pycharargs, i);
+ char *str;
+
+ if (!PyString_Check(obj))
+ {
+ PyErr_Format(PyExc_TypeError,
+ "non string in string argument list (arg %d)",
+ i + 4);
+ goto error;
+ }
+
+ str = PyString_AsString(obj);
+ if (!str)
+ goto error;
+
+ charargs[i] = str;
+ }
+
+ /* return string, or if string is NULL, return None */
+ ret = format_get_text_theme_charargs(theme, module, DATA(textdest), formatnum, charargs);
+ Py_DECREF(args);
+ Py_DECREF(pycharargs);
+ g_free(charargs);
+
+ if (ret)
+ {
+ PyObject *pyret;
+
+ pyret = PyString_FromString(ret);
+ g_free(ret);
+
+ return pyret;
+ }
+
+ Py_RETURN_NONE;
+
+error:
+ Py_XDECREF(args);
+ Py_XDECREF(pycharargs);
+ g_free(charargs);
+
+ return NULL;
+}
+#endif
+
+PyDoc_STRVAR(py_themes_reload_doc,
+ "themes_reload() -> None\n"
+);
+static PyObject *py_themes_reload(PyObject *self, PyObject *args)
+{
+ themes_reload();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_current_theme_doc,
+ "current_theme() -> Theme object\n"
+);
+static PyObject *py_current_theme(PyObject *self, PyObject *args)
+{
+ if (current_theme)
+ return pytheme_new(current_theme);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_statusbar_items_redraw_doc,
+ "statusbar_items_redraw(name) -> None\n"
+);
+static PyObject *py_statusbar_items_redraw(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ statusbar_items_redraw(name);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_statusbars_recreate_items_doc,
+ "statusbars_recreate_items() -> None"
+);
+static PyObject *py_statusbars_recreate_items(PyObject *self, PyObject *args)
+{
+ statusbars_recreate_items();
+
+ Py_RETURN_NONE;
+}
+
+/* XXX: we can unregister any statusbar items, not just the ones from this script */
+PyDoc_STRVAR(py_statusbar_item_unregister_doc,
+ "statusbar_item_unregister(name) -> None"
+);
+static PyObject *py_statusbar_item_unregister(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"name", NULL};
+ char *name = "";
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist,
+ &name))
+ return NULL;
+
+ pystatusbar_item_unregister(name);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(py_active_mainwin_doc,
+ "active_mainwin() -> MainWindow object\n"
+ "\n"
+ "return active main window\n"
+);
+static PyObject *py_active_mainwin(PyObject *self, PyObject *args)
+{
+ if (!active_mainwin)
+ Py_RETURN_NONE;
+
+ return pymain_window_new(active_mainwin);
+}
+
+PyDoc_STRVAR(py_mainwindows_doc,
+ "mainwindows() -> list of MainWindow objects\n"
+ "\n"
+ "return a list of mainwindows\n"
+);
+static PyObject *py_mainwindows(PyObject *self, PyObject *args)
+{
+ return py_irssi_objlist_new(mainwindows, 1, (InitFunc)pymain_window_new);
+}
+
+/* expect a list of tuples [('str', 0), ...] */
+static GSList *py_register_conv(PyObject *list)
+{
+ int i;
+ GSList *ret = NULL;
+
+ if (list == Py_None)
+ return NULL;
+
+ if (!PyList_Check(list))
+ {
+ PyErr_Format(PyExc_TypeError, "expect a list of tuples of string and int");
+ return NULL;
+ }
+
+ for (i = 0; i < PyList_Size(list); i++)
+ {
+ char *key;
+ int val;
+ PyObject *tup = PyList_GET_ITEM(list, i);
+
+ if (!PyTuple_Check(tup) || !PyArg_ParseTuple(tup, "si", &key, &val))
+ {
+ if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_TypeError))
+ {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError, "expect a list of tuples of string and int");
+ }
+
+ /* return the list so far; caller will clear it on error */
+ break;
+ }
+
+ ret = g_slist_append(ret, g_strdup(key));
+ ret = g_slist_append(ret, GINT_TO_POINTER(val));
+ }
+
+ return ret;
+}
+
+PyDoc_STRVAR(py_server_redirect_register_doc,
+ "server_redirect_register(command, stop, start=None, opt=None, remote=False, timeout=0) -> None\n"
+ "\n"
+ "Register new redirection command. By default irssi has already\n"
+ "registered at least: whois, whowas, who, list, ison, userhost, ping,\n"
+ "\"mode channel\" (/MODE #channel), \"mode b\" (/MODE #channel b), \"mode e\"\n"
+ "and \"mode I\".\n"
+ "\n"
+ "`command' specifies the name of the command to register, it doesn't\n"
+ "have to be a real command name, but something you just specify to\n"
+ "Server.redirect_event() when using this redirection.\n"
+ "\n"
+ "`start', `stop', `opt' - lists of ('event', argpos) tuples.\n"
+ "List of events that start and stop this redirection.\n"
+ "Start event list may be empty, but there must be at least one\n"
+ "stop event. Optional events are checked only if they are received\n"
+ "immediately after one of the stop-events. `argpos' specifies the\n"
+ "word number in event string which is compared to wanted argument,\n"
+ "-1 = don't compare, TRUE always.\n"
+ "\n"
+ "`remote' specifies if the command is by default a remote command\n"
+ "(eg. sent to another server). Server.redirect_event() may override this.\n"
+ "\n"
+ "`timeout' - If remote is TRUE, specifies how many seconds to wait for\n"
+ "reply before aborting.\n"
+ "\n"
+ "Example (already done by irssi):\n"
+ "\n"
+ "server_redirect_register('mode channel',\n"
+ " start = None,\n"
+ " stop = [ \n"
+ " ('event 324', 1), # MODE-reply\n"
+ " ('event 403', 1), # no such channel\n"
+ " ('event 442', 1), # \"you're not on that channel\"\n"
+ " ('event 479', 1) # \"Cannot join channel (illegal name)\"\n"
+ " ], \n"
+ " opt = [ \n"
+ " ('event 329', 1) # Channel create time\n"
+ " ] \n"
+ ")\n"
+);
+static PyObject *py_server_redirect_register(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"command", "stop", "start", "opt", "remote", "timeout", NULL};
+ char *command = "";
+ int remote = 0;
+ int timeout = 0;
+ PyObject *start = NULL;
+ PyObject *stop = NULL;
+ PyObject *opt = NULL;
+ GSList *node;
+ GSList *gstart = NULL;
+ GSList *gstop = NULL;
+ GSList *gopt = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|OOii", kwlist,
+ &command, &stop, &start, &opt, &remote, &timeout))
+ return NULL;
+
+ gstart = py_register_conv(start);
+ if (PyErr_Occurred())
+ goto error;
+
+ gstop = py_register_conv(stop);
+ if (!gstop || PyErr_Occurred())
+ {
+ if (!PyErr_Occurred())
+ PyErr_SetString(PyExc_TypeError, "stop list must be provided");
+
+ goto error;
+ }
+
+ gopt = py_register_conv(opt);
+ if (PyErr_Occurred())
+ goto error;
+
+ server_redirect_register_list(command, remote, timeout, gstart, gstop, gopt);
+
+ Py_RETURN_NONE;
+
+error:
+ /* clean up all lists */
+ for (node = gstart; node; node = node->next->next)
+ g_free(node->data);
+ for (node = gstop; node; node = node->next->next)
+ g_free(node->data);
+ for (node = gopt; node; node = node->next->next)
+ g_free(node->data);
+
+ g_slist_free(gstart);
+ g_slist_free(gstop);
+ g_slist_free(gopt);
+
+ return NULL;
+
+}
+
+PyDoc_STRVAR(py_command_runsub_doc,
+ "command_runsub(cmd, data, server=None, item=None) -> None\n"
+ "\n"
+ "Run subcommands for `cmd'. First word in `data' is parsed as\n"
+ "subcommand. `server' is Server object for current\n"
+ "WindowItem `item'.\n"
+ "\n"
+ "Call command_runsub in handler function for `cmd' and bind\n"
+ "with command_bind(\"`cmd' `subcmd'\", subcmdfunc[, category]);\n"
+);
+static PyObject *py_command_runsub(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", "data", "server", "item", NULL};
+ char *cmd = "";
+ char *data = "";
+ PyObject *pserver = Py_None;
+ PyObject *pitem = Py_None;
+ SERVER_REC *server = NULL;
+ WI_ITEM_REC *item = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|OO", kwlist,
+ &cmd, &data, &pserver, &pitem))
+ return NULL;
+
+ if (pserver != Py_None && !pyserver_check(pserver))
+ return PyErr_Format(PyExc_TypeError, "server must be a server");
+
+ if (pitem != Py_None && !pywindow_item_check(pitem))
+ return PyErr_Format(PyExc_TypeError, "item must be a window item");
+
+ if (pserver != Py_None)
+ server = DATA(pserver);
+
+ if (pitem != Py_None)
+ item = DATA(pitem);
+
+ command_runsub(cmd, data, server, item);
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef ModuleMethods[] = {
+ {"prnt", (PyCFunction)py_prnt, METH_VARARGS | METH_KEYWORDS,
+ py_prnt_doc},
+ {"get_script", (PyCFunction)py_get_script, METH_NOARGS,
+ py_get_script_doc},
+ {"chatnet_find", (PyCFunction)py_chatnet_find, METH_VARARGS | METH_KEYWORDS,
+ py_chatnet_find_doc},
+ {"chatnets", (PyCFunction)py_chatnets, METH_NOARGS,
+ py_chatnets_doc},
+ {"reconnects", (PyCFunction)py_reconnects, METH_NOARGS,
+ py_reconnects_doc},
+ {"servers", (PyCFunction)py_servers, METH_NOARGS,
+ py_servers_doc},
+ {"windows", (PyCFunction)py_windows, METH_NOARGS,
+ py_windows_doc},
+ {"active_win", (PyCFunction)py_active_win, METH_NOARGS,
+ py_active_win_doc},
+ {"active_server", (PyCFunction)py_active_server, METH_NOARGS,
+ py_active_server_doc},
+ {"window_find_name", (PyCFunction)py_window_find_name, METH_VARARGS | METH_KEYWORDS,
+ py_window_find_name_doc},
+ {"window_find_refnum", (PyCFunction)py_window_find_refnum, METH_VARARGS | METH_KEYWORDS,
+ py_window_find_refnum_doc},
+ {"window_refnum_prev", (PyCFunction)py_window_refnum_prev, METH_VARARGS | METH_KEYWORDS,
+ py_window_refnum_prev_doc},
+ {"window_refnum_next", (PyCFunction)py_window_refnum_next, METH_VARARGS | METH_KEYWORDS,
+ py_window_refnum_next_doc},
+ {"windows_refnum_last", (PyCFunction)py_windows_refnum_last, METH_NOARGS,
+ py_windows_refnum_last_doc},
+ {"window_find_level", (PyCFunction)py_window_find_level, METH_VARARGS | METH_KEYWORDS,
+ py_window_find_level_doc},
+ {"window_find_item", (PyCFunction)py_window_find_item, METH_VARARGS | METH_KEYWORDS,
+ py_window_find_item_doc},
+ {"window_find_closest", (PyCFunction)py_window_find_closest, METH_VARARGS | METH_KEYWORDS,
+ py_window_find_closest_doc},
+ {"window_item_find", (PyCFunction)py_window_item_find, METH_VARARGS | METH_KEYWORDS,
+ py_window_item_find_doc},
+ {"window_create", (PyCFunction)py_window_create, METH_VARARGS | METH_KEYWORDS,
+ py_window_create_doc},
+ {"server_find_tag", (PyCFunction)py_server_find_tag, METH_VARARGS | METH_KEYWORDS,
+ py_server_find_tag_doc},
+ {"server_find_chatnet", (PyCFunction)py_server_find_chatnet, METH_VARARGS | METH_KEYWORDS,
+ py_server_find_chatnet_doc},
+ {"command", (PyCFunction)PY_command, METH_VARARGS | METH_KEYWORDS,
+ PY_command_doc},
+ {"channels", (PyCFunction)py_channels, METH_NOARGS,
+ py_channels_doc},
+ {"channel_find", (PyCFunction)py_channel_find, METH_VARARGS | METH_KEYWORDS,
+ py_channel_find_doc},
+ {"query_find", (PyCFunction)py_query_find, METH_VARARGS | METH_KEYWORDS,
+ py_query_find_doc},
+ {"queries", (PyCFunction)py_queries, METH_NOARGS,
+ py_queries_doc},
+ {"mask_match", (PyCFunction)py_mask_match, METH_VARARGS | METH_KEYWORDS,
+ py_mask_match_doc},
+ {"mask_match_address", (PyCFunction)py_mask_match_address, METH_VARARGS | METH_KEYWORDS,
+ py_mask_match_address_doc},
+ {"masks_match", (PyCFunction)py_masks_match, METH_VARARGS | METH_KEYWORDS,
+ py_masks_match_doc},
+ {"rawlog_set_size", (PyCFunction)py_rawlog_set_size, METH_VARARGS | METH_KEYWORDS,
+ py_rawlog_set_size_doc},
+ {"logs", (PyCFunction)py_logs, METH_NOARGS,
+ py_logs_doc},
+ {"log_find", (PyCFunction)py_log_find, METH_VARARGS | METH_KEYWORDS,
+ py_log_find_doc},
+ {"ignores", (PyCFunction)py_ignores, METH_NOARGS,
+ py_ignores_doc},
+ {"ignore_check", (PyCFunction)py_ignore_check, METH_VARARGS | METH_KEYWORDS,
+ py_ignore_check_doc},
+ {"dccs", (PyCFunction)py_dccs, METH_NOARGS,
+ py_dccs_doc},
+ {"dcc_register_type", (PyCFunction)py_dcc_register_type, METH_VARARGS | METH_KEYWORDS,
+ py_dcc_register_type_doc},
+ {"dcc_unregister_type", (PyCFunction)py_dcc_unregister_type, METH_VARARGS | METH_KEYWORDS,
+ py_dcc_unregister_type_doc},
+ {"dcc_find_request_latest", (PyCFunction)py_dcc_find_request_latest, METH_VARARGS | METH_KEYWORDS,
+ py_dcc_find_request_latest_doc},
+ {"dcc_find_request", (PyCFunction)py_dcc_find_request, METH_VARARGS | METH_KEYWORDS,
+ py_dcc_find_request_doc},
+ {"dcc_chat_find_id", (PyCFunction)py_dcc_chat_find_id, METH_VARARGS | METH_KEYWORDS,
+ py_dcc_chat_find_id_doc},
+ {"dcc_str2type", (PyCFunction)py_dcc_str2type, METH_VARARGS | METH_KEYWORDS,
+ py_dcc_str2type_doc},
+ {"dcc_type2str", (PyCFunction)py_dcc_type2str, METH_VARARGS | METH_KEYWORDS,
+ py_dcc_type2str_doc},
+ {"dcc_get_download_path", (PyCFunction)py_dcc_get_download_path, METH_VARARGS | METH_KEYWORDS,
+ py_dcc_get_download_path_doc},
+ {"notifies", (PyCFunction)py_notifies, METH_NOARGS,
+ py_notifies_doc},
+ {"notifylist_add", (PyCFunction)py_notifylist_add, METH_VARARGS | METH_KEYWORDS,
+ py_notifylist_add_doc},
+ {"notifylist_remove", (PyCFunction)py_notifylist_remove, METH_VARARGS | METH_KEYWORDS,
+ py_notifylist_remove_doc},
+ {"notifylist_ison", (PyCFunction)py_notifylist_ison, METH_VARARGS | METH_KEYWORDS,
+ py_notifylist_ison_doc},
+ {"notifylist_find", (PyCFunction)py_notifylist_find, METH_VARARGS | METH_KEYWORDS,
+ py_notifylist_find_doc},
+ {"commands", (PyCFunction)py_commands, METH_NOARGS,
+ py_commands_doc},
+ {"level2bits", (PyCFunction)py_level2bits, METH_VARARGS | METH_KEYWORDS,
+ py_level2bits_doc},
+ {"bits2level", (PyCFunction)py_bits2level, METH_VARARGS | METH_KEYWORDS,
+ py_bits2level_doc},
+ {"combine_level", (PyCFunction)py_combine_level, METH_VARARGS | METH_KEYWORDS,
+ py_combine_level_doc},
+ {"signal_emit", (PyCFunction)py_signal_emit, METH_VARARGS,
+ py_signal_emit_doc},
+ {"signal_stop", (PyCFunction)py_signal_stop, METH_NOARGS,
+ py_signal_stop_doc},
+ {"signal_stop_by_name", (PyCFunction)py_signal_stop_by_name, METH_VARARGS | METH_KEYWORDS,
+ py_signal_stop_by_name_doc},
+ {"signal_get_emitted", (PyCFunction)py_signal_get_emitted, METH_NOARGS,
+ py_signal_get_emitted_doc},
+ {"signal_get_emitted_id", (PyCFunction)py_signal_get_emitted_id, METH_NOARGS,
+ py_signal_get_emitted_id_doc},
+ {"signal_continue", (PyCFunction)py_signal_continue, METH_VARARGS,
+ py_signal_continue_doc},
+ {"settings_get_str", (PyCFunction)py_settings_get_str, METH_VARARGS | METH_KEYWORDS,
+ py_settings_get_str_doc},
+ {"settings_get_int", (PyCFunction)py_settings_get_int, METH_VARARGS | METH_KEYWORDS,
+ py_settings_get_int_doc},
+ {"settings_get_bool", (PyCFunction)py_settings_get_bool, METH_VARARGS | METH_KEYWORDS,
+ py_settings_get_bool_doc},
+ {"settings_get_time", (PyCFunction)py_settings_get_time, METH_VARARGS | METH_KEYWORDS,
+ py_settings_get_time_doc},
+ {"settings_get_level", (PyCFunction)py_settings_get_level, METH_VARARGS | METH_KEYWORDS,
+ py_settings_get_level_doc},
+ {"settings_get_size", (PyCFunction)py_settings_get_size, METH_VARARGS | METH_KEYWORDS,
+ py_settings_get_size_doc},
+ {"settings_set_str", (PyCFunction)py_settings_set_str, METH_VARARGS | METH_KEYWORDS,
+ py_settings_set_str_doc},
+ {"settings_set_int", (PyCFunction)py_settings_set_int, METH_VARARGS | METH_KEYWORDS,
+ py_settings_set_int_doc},
+ {"settings_set_bool", (PyCFunction)py_settings_set_bool, METH_VARARGS | METH_KEYWORDS,
+ py_settings_set_bool_doc},
+ {"settings_set_time", (PyCFunction)py_settings_set_time, METH_VARARGS | METH_KEYWORDS,
+ py_settings_set_time_doc},
+ {"settings_set_level", (PyCFunction)py_settings_set_level, METH_VARARGS | METH_KEYWORDS,
+ py_settings_set_level_doc},
+ {"settings_set_size", (PyCFunction)py_settings_set_size, METH_VARARGS | METH_KEYWORDS,
+ py_settings_set_size_doc},
+ {"pidwait_add", (PyCFunction)py_pidwait_add, METH_VARARGS | METH_KEYWORDS,
+ py_pidwait_add_doc},
+ {"pidwait_remove", (PyCFunction)py_pidwait_remove, METH_VARARGS | METH_KEYWORDS,
+ py_pidwait_remove_doc},
+ {"format_get_length", (PyCFunction)py_format_get_length, METH_VARARGS | METH_KEYWORDS,
+ py_format_get_length_doc},
+ {"format_real_length", (PyCFunction)py_format_real_length, METH_VARARGS | METH_KEYWORDS,
+ py_format_real_length_doc},
+ {"strip_codes", (PyCFunction)py_strip_codes, METH_VARARGS | METH_KEYWORDS,
+ py_strip_codes_doc},
+ /*{"format_get_text", (PyCFunction)py_format_get_text, METH_VARARGS,
+ py_format_get_text_doc},*/
+ {"themes_reload", (PyCFunction)py_themes_reload, METH_NOARGS,
+ py_themes_reload_doc},
+ {"current_theme", (PyCFunction)py_current_theme, METH_NOARGS,
+ py_current_theme_doc},
+ {"statusbar_items_redraw", (PyCFunction)py_statusbar_items_redraw, METH_VARARGS | METH_KEYWORDS,
+ py_statusbar_items_redraw_doc},
+ {"statusbars_recreate_items", (PyCFunction)py_statusbars_recreate_items, METH_NOARGS,
+ py_statusbars_recreate_items_doc},
+ {"statusbar_item_unregister", (PyCFunction)py_statusbar_item_unregister, METH_VARARGS | METH_KEYWORDS,
+ py_statusbar_item_unregister_doc},
+ {"active_mainwin", (PyCFunction)py_active_mainwin, METH_NOARGS,
+ py_active_mainwin_doc},
+ {"mainwindows", (PyCFunction)py_mainwindows, METH_NOARGS,
+ py_mainwindows_doc},
+ {"server_redirect_register", (PyCFunction)py_server_redirect_register, METH_VARARGS | METH_KEYWORDS,
+ py_server_redirect_register_doc},
+ {"command_runsub", (PyCFunction)py_command_runsub, METH_VARARGS | METH_KEYWORDS,
+ py_command_runsub_doc},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+int pymodule_init(void)
+{
+ g_return_val_if_fail(py_module == NULL, 0);
+
+ py_module = Py_InitModule("_irssi", ModuleMethods);
+ if (!py_module)
+ return 0;
+
+ return 1;
+}
+
+void pymodule_deinit(void)
+{
+ g_return_if_fail(py_module != NULL);
+
+ Py_DECREF(py_module);
+ py_module = NULL;
+}
diff --git a/src/pymodule.h b/src/pymodule.h
new file mode 100644
index 0000000..242eecf
--- /dev/null
+++ b/src/pymodule.h
@@ -0,0 +1,11 @@
+#ifndef _PY_MODULE_H_
+#define _PY_MODULE_H_
+
+#include <Python.h>
+
+/* This is global so that type objects and such can be easily attached */
+extern PyObject *py_module;
+int pymodule_init(void);
+void pymodule_deinit(void);
+
+#endif
diff --git a/src/pysigmap.h b/src/pysigmap.h
new file mode 100644
index 0000000..c34fbf7
--- /dev/null
+++ b/src/pysigmap.h
@@ -0,0 +1,188 @@
+/* Include in your C module */
+static PY_SIGNAL_SPEC_REC py_sigmap[] = {
+ {"gui exit", "", 0, 0, 0},
+ {"gui dialog", "ss", 0, 0, 0},
+ {"send command", "sSW", 0, 0, 0},
+ {"chat protocol created", "?", 0, 0, 0},
+ {"chat protocol updated", "?", 0, 0, 0},
+ {"chat protocol destroyed", "?", 0, 0, 0},
+ {"channel created", "Ci", 0, 0, 0},
+ {"channel destroyed", "C", 0, 0, 0},
+ {"chatnet created", "c", 0, 0, 0},
+ {"chatnet destroyed", "c", 0, 0, 0},
+ {"commandlist new", "o", 0, 0, 0},
+ {"commandlist remove", "o", 0, 0, 0},
+ {"error command", "is", 0, 0, 0},
+ {"send command", "sSW", 0, 0, 0},
+ {"send text", "sSW", 0, 0, 0},
+ {"command ", "sSW", 0, 0, 1},
+ {"default command", "sSW", 0, 0, 0},
+ {"ignore created", "g", 0, 0, 0},
+ {"ignore destroyed", "g", 0, 0, 0},
+ {"ignore changed", "g", 0, 0, 0},
+ {"log new", "l", 0, 0, 0},
+ {"log remove", "l", 0, 0, 0},
+ {"log create failed", "l", 0, 0, 0},
+ {"log locked", "l", 0, 0, 0},
+ {"log started", "l", 0, 0, 0},
+ {"log stopped", "l", 0, 0, 0},
+ {"log rotated", "l", 0, 0, 0},
+ {"log written", "ls", 0, 0, 0},
+ {"module loaded", "??", 0, 0, 0},
+ {"module unloaded", "??", 0, 0, 0},
+ {"module error", "isss", 0, 0, 0},
+ {"nicklist new", "Cn", 0, 0, 0},
+ {"nicklist remove", "Cn", 0, 0, 0},
+ {"nicklist changed", "Cns", 0, 0, 0},
+ {"nicklist host changed", "Cn", 0, 0, 0},
+ {"nicklist gone changed", "Cn", 0, 0, 0},
+ {"nicklist serverop changed", "Cn", 0, 0, 0},
+ {"pidwait", "ii", 0, 0, 0},
+ {"query created", "qi", 0, 0, 0},
+ {"query destroyed", "q", 0, 0, 0},
+ {"query nick changed", "qs", 0, 0, 0},
+ {"window item name changed", "W", 0, 0, 0},
+ {"query address changed", "q", 0, 0, 0},
+ {"query server changed", "qS", 0, 0, 0},
+ {"rawlog", "ls", 0, 0, 0},
+ {"server looking", "S", 0, 0, 0},
+ {"server connected", "S", 0, 0, 0},
+ {"server connecting", "Su", 0, 0, 0},
+ {"server connect failed", "S", 0, 0, 0},
+ {"server disconnected", "S", 0, 0, 0},
+ {"server quit", "Ss", 0, 0, 0},
+ {"server sendmsg", "Sssi", 0, 0, 0},
+ {"setup changed", "", 0, 0, 0},
+ {"setup reread", "s", 0, 0, 0},
+ {"setup saved", "si", 0, 0, 0},
+ {"ban type changed", "s", 0, 0, 0},
+ {"channel joined", "C", 0, 0, 0},
+ {"channel wholist", "C", 0, 0, 0},
+ {"channel sync", "C", 0, 0, 0},
+ {"channel topic changed", "C", 0, 0, 0},
+ {"ctcp msg", "Sssss", 0, 0, 0},
+ {"ctcp msg ", "Sssss", 0, 0, 1},
+ {"default ctcp msg", "Sssss", 0, 0, 0},
+ {"ctcp reply", "Sssss", 0, 0, 0},
+ {"ctcp reply ", "Sssss", 0, 0, 1},
+ {"default ctcp reply", "Sssss", 0, 0, 0},
+ {"ctcp action", "Sssss", 0, 0, 0},
+ {"awaylog show", "lii", 0, 0, 0},
+ {"server nick changed", "S", 0, 0, 0},
+ {"event connected", "S", 0, 0, 0},
+ {"server event", "Ssss", 0, 0, 0},
+ {"event ", "Ssss", 0, 0, 1},
+ {"default event", "Ssss", 0, 0, 0},
+ {"whois default event", "Ssss", 0, 0, 0},
+ {"server incoming", "Ss", 0, 0, 0},
+ {"redir ", "Ssss", 0, 0, 1},
+ {"server lag", "S", 0, 0, 0},
+ {"server lag disconnect", "S", 0, 0, 0},
+ {"massjoin", "CL", 0, 0, 0},
+ {"ban new", "Cb", 0, 0, 0},
+ {"ban remove", "Cbs", 0, 0, 0},
+ {"channel mode changed", "Cs", 0, 0, 0},
+ {"nick mode changed", "Cnsss", 0, 0, 0},
+ {"user mode changed", "Ss", 0, 0, 0},
+ {"away mode changed", "S", 0, 0, 0},
+ {"netsplit server new", "SS", 0, 0, 0},
+ {"netsplit server remove", "SS", 0, 0, 0},
+ {"netsplit new", "N", 0, 0, 0},
+ {"netsplit remove", "N", 0, 0, 0},
+ {"dcc ctcp ", "sd", 0, 0, 1},
+ {"default dcc ctcp", "sd", 0, 0, 0},
+ {"dcc unknown ctcp", "sss", 0, 0, 0},
+ {"dcc reply ", "sd", 0, 0, 1},
+ {"default dcc reply", "sd", 0, 0, 0},
+ {"dcc unknown reply", "sss", 0, 0, 0},
+ {"dcc chat message", "ds", 0, 0, 0},
+ {"dcc created", "d", 0, 0, 0},
+ {"dcc destroyed", "d", 0, 0, 0},
+ {"dcc connected", "d", 0, 0, 0},
+ {"dcc rejecting", "d", 0, 0, 0},
+ {"dcc closed", "d", 0, 0, 0},
+ {"dcc request", "ds", 0, 0, 0},
+ {"dcc request send", "d", 0, 0, 0},
+ {"dcc chat message", "ds", 0, 0, 0},
+ {"dcc transfer update", "d", 0, 0, 0},
+ {"dcc get receive", "d", 0, 0, 0},
+ {"dcc error connect", "d", 0, 0, 0},
+ {"dcc error file create", "ds", 0, 0, 0},
+ {"dcc error file open", "ssi", 0, 0, 0},
+ {"dcc error get not found", "s", 0, 0, 0},
+ {"dcc error send exists", "ss", 0, 0, 0},
+ {"dcc error unknown type", "s", 0, 0, 0},
+ {"dcc error close not found", "sss", 0, 0, 0},
+ {"autoignore new", "S?", 0, 0, 0},
+ {"autoignore remove", "S?", 0, 0, 0},
+ {"flood", "Sssis", 0, 0, 0},
+ {"notifylist new", "O", 0, 0, 0},
+ {"notifylist remove", "O", 0, 0, 0},
+ {"notifylist joined", "Ssssss", 0, 0, 0},
+ {"notifylist away changed", "Ssssss", 0, 0, 0},
+ {"notifylist unidle", "Ssssss", 0, 0, 0},
+ {"notifylist left", "Ssssss", 0, 0, 0},
+ {"proxy client connected", "?", 0, 0, 0},
+ {"proxy client disconnected", "?", 0, 0, 0},
+ {"gui print text", "wiiist", 0, 0, 0},
+ {"gui print text finished", "w", 0, 0, 0},
+ {"complete word", "?wssi", 0, 0, 0},
+ {"irssi init read settings", "", 0, 0, 0},
+ {"exec new", "p", 0, 0, 0},
+ {"exec remove", "pi", 0, 0, 0},
+ {"exec input", "ps", 0, 0, 0},
+ {"message public", "Sssss", 0, 0, 0},
+ {"message private", "Ssss", 0, 0, 0},
+ {"message own_public", "Sss", 0, 0, 0},
+ {"message own_private", "Ssss", 0, 0, 0},
+ {"message join", "Ssss", 0, 0, 0},
+ {"message part", "Sssss", 0, 0, 0},
+ {"message quit", "Ssss", 0, 0, 0},
+ {"message kick", "Ssssss", 0, 0, 0},
+ {"message nick", "Ssss", 0, 0, 0},
+ {"message own_nick", "Ssss", 0, 0, 0},
+ {"message invite", "Ssss", 0, 0, 0},
+ {"message topic", "Sssss", 0, 0, 0},
+ {"keyinfo created", "?", 0, 0, 0},
+ {"keyinfo destroyed", "?", 0, 0, 0},
+ {"print text", "tss", 0, 0, 0},
+ {"theme created", "?", 0, 0, 0},
+ {"theme destroyed", "?", 0, 0, 0},
+ {"window hilight", "w", 0, 0, 0},
+ {"window activity", "wi", 0, 0, 0},
+ {"window item hilight", "W", 0, 0, 0},
+ {"window item activity", "Wi", 0, 0, 0},
+ {"window item new", "wW", 0, 0, 0},
+ {"window item remove", "wW", 0, 0, 0},
+ {"window item changed", "wW", 0, 0, 0},
+ {"window item server changed", "wW", 0, 0, 0},
+ {"window created", "w", 0, 0, 0},
+ {"window destroyed", "w", 0, 0, 0},
+ {"window changed", "ww", 0, 0, 0},
+ {"window changed automatic", "w", 0, 0, 0},
+ {"window server changed", "wS", 0, 0, 0},
+ {"window refnum changed", "wi", 0, 0, 0},
+ {"window name changed", "w", 0, 0, 0},
+ {"window history changed", "ws", 0, 0, 0},
+ {"window level changed", "w", 0, 0, 0},
+ {"message irc op_public", "Sssss", 0, 0, 0},
+ {"message irc own_wall", "Sss", 0, 0, 0},
+ {"message irc own_action", "Sss", 0, 0, 0},
+ {"message irc action", "Sssss", 0, 0, 0},
+ {"message irc own_notice", "Sss", 0, 0, 0},
+ {"message irc notice", "Sssss", 0, 0, 0},
+ {"message irc own_ctcp", "Ssss", 0, 0, 0},
+ {"message irc ctcp", "Ssssss", 0, 0, 0},
+ {"message irc mode", "Sssss", 0, 0, 0},
+ {"message dcc own", "ds", 0, 0, 0},
+ {"message dcc own_action", "ds", 0, 0, 0},
+ {"message dcc own_ctcp", "dss", 0, 0, 0},
+ {"message dcc", "ds", 0, 0, 0},
+ {"message dcc action", "ds", 0, 0, 0},
+ {"message dcc ctcp", "dss", 0, 0, 0},
+ {"gui key pressed", "i", 0, 0, 0},
+ {"beep", "", 0, 0, 0},
+ {NULL}
+};
+
+#define py_sigmap_len() (sizeof(py_sigmap) / sizeof(py_sigmap[0]) - 1)
diff --git a/src/pysignals.c b/src/pysignals.c
new file mode 100644
index 0000000..95718aa
--- /dev/null
+++ b/src/pysignals.c
@@ -0,0 +1,805 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pysignals.h"
+#include "factory.h"
+
+/* NOTE:
+ * There are two different records used to store signal related data:
+ * PY_SIGNAL_SPEC_REC and PY_SIGNAL_REC. Each SPEC_REC declares a "plain"
+ * signal, or a type of "variable" signal. Each PY_SIGNAL_REC stores data
+ * about a handler for a signal.
+ *
+ * Plain signals are emitted using the same text every time. They are never
+ * extended or altered in any way. Plain signals make up the vast majority
+ * of Irssi signals. These include (some random examples): "beep",
+ * "printtext", "log new", "ban new", etc.
+ *
+ * Variable signals are emitted by joining a common prefix with text that
+ * varies from emit to emit. These are signals that have a "<cmd>" suffix
+ * in Irssi's signal listing. irssi-python uses the trailing space to
+ * distinguish between plain signals and variable signals. Example prefixes:
+ * "redir ", "command ", "event ". "command nick" would be emitted when the
+ * user types /nick yournick at the prompt.
+ *
+ * While PY_SIGNAL_SPEC_REC stores data about each individual signal,
+ * PY_SIGNAL_REC stores data about each handler of a signal (or command).
+ * A listing of SPEC_REC entries is stored globally in this module for each
+ * signal known to irssi-python. Each Script holds a list of PY_SIGNAL_REC
+ * entries to remember signals and commands bound by the script. The
+ * PY_SIGNAL_REC data includes references to a callable PyObject and a
+ * PY_SIGNAL_SPEC_REC entry for the argument type list of the signal.
+ *
+ * Signals must be registered to be accessible to Python scripts.
+ * Registering a dynamic signal adds a new SPEC_REC with a refcount of 1.
+ * If another script registers the same signal, the original entry's refcount
+ * is incremented. Binding to the signal also increments its reference count.
+ * Likewise, unregistering or unbinding a dynamic signal will decrement its
+ * refcount. When refcount hits 0, the dynamic signal is removed from the list,
+ * and scripts can no longer bind to or emit the signal untill it is
+ * re-registered. Built-in signals in the sigmap are not from the heap and are
+ * never removed; it is an error for the refcount of any such signal entry to
+ * drop to 0.
+ */
+
+typedef struct _PY_SIGNAL_SPEC_REC
+{
+ char *name;
+ char *arglist;
+ int refcount;
+ int dynamic;
+ int is_var; /* is this entry a prefix for a variable signal? */
+} PY_SIGNAL_SPEC_REC;
+
+#include "pysigmap.h"
+
+#define SIGNAME(sig) (sig->command? sig->command : sig->signal->name)
+/* This macro is useful for PY_SIGNAL_REC entries bound to "variable" signals,
+ * whose names extend the name prefix stored in the SPEC_REC entry.
+ *
+ * Example:
+ * sig->command == "command nick"
+ * sig->signal->name == "command "
+ *
+ * The exact signal text differs from the prefix, so the text must be stored
+ * separately in each PY_SIGNAL_REC entry.
+ *
+ * However, entries for "plain" signals do not require any extra data stored,
+ * so sig->command is NULL.
+ *
+ * Example:
+ * sig->command == NULL;
+ * sig->signal->name == "massjoin";
+ *
+ * "massjoin" is not a prefix for any signal, so any PY_SIGNAL_REC
+ * referencing the "massjoin" SPEC_REC entry will have a NULL command.
+ */
+
+/* hashtable for normal signals, tree for variable signal prefixes. */
+static GHashTable *py_sighash = NULL;
+static GTree *py_sigtree = NULL;
+
+static void py_run_handler(PY_SIGNAL_REC *rec, void **args);
+static void py_sig_proxy(void *p1, void *p2, void *p3, void *p4, void *p5, void *p6);
+static void py_signal_ref(PY_SIGNAL_SPEC_REC *sig);
+static int py_signal_unref(PY_SIGNAL_SPEC_REC *sig);
+static void py_signal_add(PY_SIGNAL_SPEC_REC *sig);
+static PY_SIGNAL_REC *py_signal_rec_new(const char *signal, PyObject *func, const char *command);
+static void py_signal_rec_destroy(PY_SIGNAL_REC *sig);
+static PyObject *py_mkstrlist(void *iobj);
+static PyObject *py_i2py(char code, void *iobj);
+static void *py_py2i(char code, PyObject *pobj, int arg, const char *signal);
+static void py_getstrlist(GList **list, PyObject *pylist);
+static int precmp(const char *spec, const char *test);
+static PY_SIGNAL_SPEC_REC *py_signal_lookup(const char *name);
+static void py_signal_remove(PY_SIGNAL_SPEC_REC *sig);
+static int py_convert_args(void **args, PyObject *argtup, const char *signal);
+
+PY_SIGNAL_REC *pysignals_command_bind(const char *cmd, PyObject *func,
+ const char *category, int priority)
+{
+ PY_SIGNAL_REC *rec = py_signal_rec_new("send command", func, cmd);
+ g_return_val_if_fail(rec != NULL, NULL);
+
+ command_bind_full(MODULE_NAME, priority, cmd,
+ -1, category, (SIGNAL_FUNC)py_sig_proxy, rec);
+
+ return rec;
+}
+
+int pysignals_command_bind_list(GSList **list, const char *command,
+ PyObject *func, const char *category, int priority)
+{
+ PY_SIGNAL_REC *rec = pysignals_command_bind(command, func, category, priority);
+ if (!rec)
+ return 0;
+
+ *list = g_slist_append(*list, rec);
+ return 1;
+}
+
+/* return NULL if signal is invalid */
+PY_SIGNAL_REC *pysignals_signal_add(const char *signal, PyObject *func, int priority)
+{
+ PY_SIGNAL_REC *rec = py_signal_rec_new(signal, func, NULL);
+
+ if (rec == NULL)
+ return NULL;
+
+ signal_add_full(MODULE_NAME, priority, SIGNAME(rec),
+ (SIGNAL_FUNC)py_sig_proxy, rec);
+
+ return rec;
+}
+
+int pysignals_signal_add_list(GSList **list, const char *signal,
+ PyObject *func, int priority)
+{
+ PY_SIGNAL_REC *rec = pysignals_signal_add(signal, func, priority);
+ if (!rec)
+ return 0;
+
+ *list = g_slist_append(*list, rec);
+ return 1;
+}
+
+void pysignals_command_unbind(PY_SIGNAL_REC *rec)
+{
+ g_return_if_fail(rec->is_signal == FALSE);
+ g_return_if_fail(rec->command != NULL);
+
+ command_unbind_full(rec->command, (SIGNAL_FUNC)py_sig_proxy, rec);
+ py_signal_rec_destroy(rec);
+}
+
+void pysignals_signal_remove(PY_SIGNAL_REC *rec)
+{
+ g_return_if_fail(rec->is_signal == TRUE);
+
+ signal_remove_full(SIGNAME(rec), (SIGNAL_FUNC)py_sig_proxy, rec);
+ py_signal_rec_destroy(rec);
+}
+
+void pysignals_remove_generic(PY_SIGNAL_REC *rec)
+{
+ if (rec->is_signal)
+ pysignals_signal_remove(rec);
+ else
+ pysignals_command_unbind(rec);
+}
+
+/* returns 1 when found and removed successfully */
+int pysignals_remove_search(GSList **siglist, const char *name,
+ PyObject *func, PSG_TYPE type)
+{
+ GSList *node;
+
+ for (node = *siglist; node != NULL; node = node->next)
+ {
+ PY_SIGNAL_REC *sig = node->data;
+
+ if ((sig->is_signal && type == PSG_COMMAND) ||
+ (!sig->is_signal && type == PSG_SIGNAL))
+ continue;
+
+ if ((strcmp(SIGNAME(sig), name) == 0) &&
+ (func == NULL || func == sig->handler))
+ {
+ pysignals_remove_generic(sig);
+ *siglist = g_slist_delete_link(*siglist, node);
+
+ /* deleting node won't harm iteration because it quits here */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void pysignals_remove_list(GSList *siglist)
+{
+ GSList *node = siglist;
+
+ for (node = siglist; node != NULL; node = node->next)
+ pysignals_remove_generic(node->data);
+}
+
+static PyObject *py_mkstrlist(void *iobj)
+{
+ PyObject *list;
+ GList *node, **ptr;
+ ptr = iobj;
+
+ list = PyList_New(0);
+ if (!list)
+ return NULL;
+
+ for (node = *ptr; node != NULL; node = node->next)
+ {
+ int ret;
+ PyObject *str;
+
+ str = PyString_FromString(node->data);
+ if (!str)
+ {
+ Py_DECREF(list);
+ return NULL;
+ }
+
+ ret = PyList_Append(list, str);
+ Py_DECREF(str);
+ if (ret != 0)
+ {
+ Py_DECREF(list);
+ return NULL;
+ }
+ }
+
+ return list;
+}
+
+/* irssi obj -> PyObject */
+static PyObject *py_i2py(char code, void *iobj)
+{
+ if (iobj == NULL)
+ Py_RETURN_NONE;
+
+ switch (code)
+ {
+ case '?':
+ Py_RETURN_NONE;
+
+ case 's':
+ return PyString_FromString((char *)iobj);
+ case 'u':
+ return PyLong_FromUnsignedLong(*(unsigned long*)iobj);
+ case 'I':
+ return PyInt_FromLong(*(int *)iobj);
+ case 'i':
+ return PyInt_FromLong((int)iobj);
+
+ case 'G':
+ return py_mkstrlist(iobj);
+ case 'L': /* list of nicks */
+ return py_irssi_chatlist_new((GSList *)iobj, 1);
+
+ case 'c':
+ case 'S':
+ case 'C':
+ case 'q':
+ case 'n':
+ case 'W':
+ return py_irssi_chat_new(iobj, 1);
+
+ case 'd':
+ return py_irssi_new(iobj, 1);
+
+ case 'r':
+ return pyreconnect_new(iobj);
+ case 'o':
+ return pycommand_new(iobj);
+ case 'l':
+ return pylog_new(iobj);
+ case 'a':
+ return pyrawlog_new(iobj);
+ case 'g':
+ return pyignore_new(iobj);
+ case 'b':
+ return pyban_new(iobj);
+ case 'N':
+ return pynetsplit_new(iobj);
+ case 'e':
+ return pynetsplit_server_new(iobj);
+ case 'O':
+ return pynotifylist_new(iobj);
+ case 'p':
+ return pyprocess_new(iobj);
+ case 't':
+ return pytextdest_new(iobj);
+ case 'w':
+ return pywindow_new(iobj);
+ }
+
+ return PyErr_Format(PyExc_TypeError, "unknown code %c", code);
+}
+
+/* PyObject -> irssi obj*/
+static void *py_py2i(char code, PyObject *pobj, int arg, const char *signal)
+{
+ char *type;
+
+ if (pobj == Py_None)
+ return NULL;
+
+ switch (code)
+ {
+ /* XXX: string doesn't persist */
+ case 's':
+ type = "str";
+ if (PyString_Check(pobj)) return PyString_AsString(pobj);
+ break;
+ case 'i':
+ type = "int";
+ if (PyInt_Check(pobj)) return (void*)PyInt_AsLong(pobj);
+ break;
+
+ case 'L': /* list of nicks */
+ /*FIXME*/
+ return NULL;
+
+ case 'c':
+ type = "Chatnet";
+ if (pychatnet_check(pobj)) return DATA(pobj);
+ break;
+ case 'S':
+ type = "Server";
+ if (pyserver_check(pobj)) return DATA(pobj);
+ break;
+ case 'C':
+ type = "Channel";
+ if (pychannel_check(pobj)) return DATA(pobj);
+ break;
+ case 'q':
+ type = "Query";
+ if (pyquery_check(pobj)) return DATA(pobj);
+ break;
+ case 'n':
+ type = "Nick";
+ if (pynick_check(pobj)) return DATA(pobj);
+ break;
+ case 'W':
+ type = "WindowItem";
+ if (pywindow_item_check(pobj)) return DATA(pobj);
+ break;
+
+ case 'd':
+ type = "DCC";
+ if (pydcc_check(pobj)) return DATA(pobj);
+ break;
+
+ case 'r':
+ type = "Reconnect";
+ if (pyreconnect_check(pobj)) return DATA(pobj);
+ break;
+ case 'o':
+ type = "Command";
+ if (pycommand_check(pobj)) return DATA(pobj);
+ break;
+ case 'l':
+ type = "Log";
+ if (pylog_check(pobj)) return DATA(pobj);
+ break;
+ case 'a':
+ type = "Rawlog";
+ if (pyrawlog_check(pobj)) return DATA(pobj);
+ break;
+ case 'g':
+ type = "Ignore";
+ if (pyignore_check(pobj)) return DATA(pobj);
+ break;
+ case 'b':
+ type = "Ban";
+ if (pyban_check(pobj)) return DATA(pobj);
+ break;
+ case 'N':
+ type = "Netsplit";
+ if (pynetsplit_check(pobj)) return DATA(pobj);
+ break;
+ case 'e':
+ type = "NetsplitServer";
+ if (pynetsplit_server_check(pobj)) return DATA(pobj);
+ break;
+ case 'O':
+ type = "Notifylist";
+ if (pynotifylist_check(pobj)) return DATA(pobj);
+ break;
+ case 'p':
+ type = "Process";
+ if (pyprocess_check(pobj)) return DATA(pobj);
+ break;
+ case 't':
+ type = "TextDest";
+ if (pytextdest_check(pobj)) return DATA(pobj);
+ break;
+ case 'w':
+ type = "Window";
+ if (pywindow_check(pobj)) return DATA(pobj);
+ break;
+ default:
+ PyErr_Format(PyExc_TypeError, "don't know type code %c", code);
+ return NULL;
+ }
+
+ PyErr_Format(PyExc_TypeError, "signal `%s': expected type %s for arg %d, but got %s",
+ signal, type, arg, pobj->ob_type->tp_name);
+ return NULL;
+}
+
+static void py_getstrlist(GList **list, PyObject *pylist)
+{
+ GList *out = NULL;
+ int i;
+ PyObject *str;
+ char *cstr;
+
+ for (i = 0; i < PyList_Size(pylist); i++)
+ {
+ str = PyList_GET_ITEM(pylist, i);
+ if (!PyString_Check(str))
+ {
+ PyErr_SetString(PyExc_TypeError, "string list contains invalid elements");
+ PyErr_Print();
+ return;
+ }
+
+ cstr = g_strdup(PyString_AS_STRING(str));
+ out = g_list_append(out, cstr);
+ }
+
+ g_list_foreach(*list, (GFunc)g_free, NULL);
+ g_list_free(*list);
+ *list = out;
+}
+
+static void py_run_handler(PY_SIGNAL_REC *rec, void **args)
+{
+ PyObject *argtup, *ret;
+ char *arglist = rec->signal->arglist;
+ int arglen, i, j;
+
+ arglen = strlen(arglist);
+ g_return_if_fail(arglen <= SIGNAL_MAX_ARGUMENTS);
+
+ argtup = PyTuple_New(arglen);
+ if (!argtup)
+ goto error;
+
+ for (i = 0; i < arglen; i++)
+ {
+ PyObject *arg = py_i2py(arglist[i], args[i]);
+ if (!arg)
+ goto error;
+
+ PyTuple_SET_ITEM(argtup, i, arg);
+ }
+
+ ret = PyObject_CallObject(rec->handler, argtup);
+ if (!ret)
+ goto error;
+
+ /*XXX: IN/OUT arg handling not well tested */
+ for (i = 0, j = 0; i < arglen; i++)
+ {
+ GList **list;
+ PyObject *pyarg = PyTuple_GET_ITEM(argtup, i);
+
+ switch (arglist[i])
+ {
+ case 'G':
+ list = args[i];
+ py_getstrlist(list, pyarg);
+ break;
+
+ case 'I':
+ if (ret != Py_None)
+ {
+ PyObject *value;
+ int *intarg = args[i];
+
+ /* expect a proper return value to set reference arg.
+ * if return is a tuple, find the next item
+ */
+ if (PyTuple_Check(ret))
+ value = PyTuple_GET_ITEM(ret, j++);
+ else
+ value = ret;
+
+ if (!PyInt_Check(value))
+ continue;
+
+ *intarg = PyInt_AS_LONG(value);
+ }
+ break;
+ }
+ }
+
+ Py_XDECREF(ret);
+
+error:
+ Py_XDECREF(argtup);
+ if (PyErr_Occurred())
+ PyErr_Print();
+}
+
+static void py_sig_proxy(void *p1, void *p2, void *p3, void *p4, void *p5, void *p6)
+{
+ PY_SIGNAL_REC *rec = signal_get_user_data();
+ void *args[6];
+
+ args[0] = p1; args[1] = p2; args[2] = p3;
+ args[3] = p4; args[4] = p5; args[5] = p6;
+ py_run_handler(rec, args);
+}
+
+static int py_convert_args(void **args, PyObject *argtup, const char *signal)
+{
+ char *arglist;
+ PY_SIGNAL_SPEC_REC *spec;
+ int i;
+ int maxargs;
+
+ spec = py_signal_lookup(signal);
+ if (!spec)
+ {
+ PyErr_Format(PyExc_KeyError, "signal not found");
+ return 0;
+ }
+
+ /*XXX: specifying fewer signal args than in the format implicitly
+ sets overlooked args to NULL or 0 */
+
+ arglist = spec->arglist;
+ maxargs = strlen(arglist);
+ for (i = 0; i < maxargs && i < PyTuple_Size(argtup); i++)
+ {
+ args[i] = py_py2i(arglist[i],
+ PyTuple_GET_ITEM(argtup, i),
+ i+1, signal);
+
+ if (PyErr_Occurred()) /* XXX: any cleanup needed? */
+ return -1;
+ }
+
+ return maxargs;
+}
+
+int pysignals_emit(const char *signal, PyObject *argtup)
+{
+ int arglen;
+ void *args[6];
+
+ memset(args, 0, sizeof args);
+
+ arglen = py_convert_args(args, argtup, signal);
+ if (arglen < 0)
+ return 0;
+
+ signal_emit(signal, arglen,
+ args[0], args[1], args[2],
+ args[3], args[4], args[5]);
+
+ return 1;
+}
+
+int pysignals_continue(PyObject *argtup)
+{
+ const char *signal;
+ int arglen;
+ void *args[6];
+
+ memset(args, 0, sizeof args);
+
+ signal = signal_get_emitted();
+ if (!signal)
+ {
+ PyErr_Format(PyExc_LookupError, "cannot determine current signal");
+ return 0;
+ }
+
+ arglen = py_convert_args(args, argtup, signal);
+ if (arglen < 0)
+ return 0;
+
+ signal_continue(arglen,
+ args[0], args[1], args[2],
+ args[3], args[4], args[5]);
+
+ return 1;
+}
+
+/* returns NULL if signal is invalid, incr reference to func */
+static PY_SIGNAL_REC *py_signal_rec_new(const char *signal, PyObject *func, const char *command)
+{
+ PY_SIGNAL_REC *rec;
+ PY_SIGNAL_SPEC_REC *spec;
+
+ g_return_val_if_fail(func != NULL, NULL);
+
+ spec = py_signal_lookup(signal);
+ if (!spec)
+ return NULL;
+
+ rec = g_new0(PY_SIGNAL_REC, 1);
+ rec->signal = spec;
+ rec->handler = func;
+ Py_INCREF(func);
+
+ if (command)
+ {
+ rec->is_signal = FALSE;
+ rec->command = g_strdup(command);
+ }
+ else
+ {
+ rec->is_signal = TRUE;
+ /* handle variable signal. requested signal will be longer than spec->name, ie
+ * signal = "var signal POOOM", spec->name = "var signal "
+ */
+ if (strcmp(signal, spec->name) != 0)
+ rec->command = g_strdup(signal);
+ }
+
+ py_signal_ref(spec);
+
+ return rec;
+}
+
+static void py_signal_rec_destroy(PY_SIGNAL_REC *sig)
+{
+ py_signal_unref(sig->signal);
+ Py_DECREF(sig->handler);
+ g_free(sig->command);
+ g_free(sig);
+}
+
+static void py_signal_add(PY_SIGNAL_SPEC_REC *sig)
+{
+ if (sig->is_var)
+ g_tree_insert(py_sigtree, sig->name, sig);
+ else
+ g_hash_table_insert(py_sighash, sig->name, sig);
+}
+
+static void py_signal_remove(PY_SIGNAL_SPEC_REC *sig)
+{
+ int ret;
+
+ if (sig->is_var)
+ g_tree_remove(py_sigtree, sig->name);
+ else
+ {
+ ret = g_hash_table_remove(py_sighash, sig->name);
+ g_return_if_fail(ret != FALSE);
+ }
+}
+
+static int precmp(const char *spec, const char *test)
+{
+ while (*spec == *test++)
+ if (*spec++ == '\0')
+ return 0;
+
+ /* Variable event prefix matches (spec must never be empty string)*/
+ /* precmp("var event ", "var event POOOM") -> 0 */
+ if (*spec == '\0' && *(spec-1) == ' ')
+ return 0;
+
+ return *(const unsigned char *)(test - 1) - *(const unsigned char *)spec;
+}
+
+static PY_SIGNAL_SPEC_REC *py_signal_lookup(const char *name)
+{
+ PY_SIGNAL_SPEC_REC *ret;
+
+ /* First check the normal signals hash, then check the variable signal prefixes in the tree */
+ ret = g_hash_table_lookup(py_sighash, name);
+ if (!ret)
+ ret = g_tree_search(py_sigtree, (GCompareFunc)precmp, name);
+
+ return ret;
+}
+
+static void py_signal_ref(PY_SIGNAL_SPEC_REC *sig)
+{
+ g_return_if_fail(sig->refcount >= 0);
+
+ sig->refcount++;
+}
+
+static int py_signal_unref(PY_SIGNAL_SPEC_REC *sig)
+{
+ g_return_val_if_fail(sig->refcount >= 1, 0);
+ g_return_val_if_fail(sig->refcount > 1 || sig->dynamic, 0);
+
+ sig->refcount--;
+
+ if (sig->refcount == 0)
+ {
+ py_signal_remove(sig);
+
+ /* freeing name also takes care of the key */
+ g_free(sig->name);
+ g_free(sig->arglist);
+ g_free(sig);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* returns 0 when signal already exists, but with different args, or when
+ similar signal prefix is already present. */
+int pysignals_register(const char *name, const char *arglist)
+{
+ int len;
+ PY_SIGNAL_SPEC_REC *spec;
+
+ len = strlen(name);
+ g_return_val_if_fail(len > 0, 0);
+
+ spec = py_signal_lookup(name);
+ if (!spec)
+ {
+ spec = g_new0(PY_SIGNAL_SPEC_REC, 1);
+ spec->is_var = name[len-1] == ' '; /* trailing space means signal prefix */
+ spec->dynamic = 1;
+ spec->refcount = 0;
+ spec->name = g_strdup(name);
+ spec->arglist = g_strdup(arglist);
+
+ py_signal_add(spec);
+ }
+ else if (strcmp(spec->arglist, arglist) || strcmp(spec->name, name))
+ return 0;
+
+ spec->refcount++;
+
+ return 1;
+}
+
+/* returns 0 when name doesn't exist */
+int pysignals_unregister(const char *name)
+{
+ PY_SIGNAL_SPEC_REC *spec;
+
+ spec = py_signal_lookup(name);
+ if (!spec)
+ return 0;
+
+ py_signal_unref(spec);
+ return 1;
+}
+
+void pysignals_init(void)
+{
+ int i;
+
+ g_return_if_fail(py_sighash == NULL);
+ g_return_if_fail(py_sigtree == NULL);
+
+ py_sigtree = g_tree_new((GCompareFunc)strcmp);
+ py_sighash = g_hash_table_new(g_str_hash, g_str_equal);
+
+ for (i = 0; i < py_sigmap_len(); i++)
+ {
+ py_sigmap[i].refcount = 1;
+ py_sigmap[i].dynamic = 0;
+ py_signal_add(&py_sigmap[i]);
+ }
+}
+
+static int py_check_sig(char *key, PY_SIGNAL_SPEC_REC *value, void *data)
+{
+ /* shouldn't need to deallocate any script recs -- all remaining at
+ this point should not be dynamic. non dynamic signals should have
+ no outstanding references */
+ g_return_val_if_fail(value->dynamic == 0, FALSE);
+ g_return_val_if_fail(value->refcount == 1, FALSE);
+
+ return FALSE;
+}
+
+/* XXX: remember to remove all scripts before calling this deinit */
+void pysignals_deinit(void)
+{
+ g_return_if_fail(py_sighash != NULL);
+ g_return_if_fail(py_sigtree != NULL);
+
+ g_tree_foreach(py_sigtree, (GTraverseFunc)py_check_sig, NULL);
+ g_hash_table_foreach_remove(py_sighash, (GHRFunc)py_check_sig, NULL);
+
+ g_tree_destroy(py_sigtree);
+ g_hash_table_destroy(py_sighash);
+ py_sigtree = NULL;
+ py_sighash = NULL;
+}
diff --git a/src/pysignals.h b/src/pysignals.h
new file mode 100644
index 0000000..bec72c9
--- /dev/null
+++ b/src/pysignals.h
@@ -0,0 +1,44 @@
+#ifndef _PYSIGNALS_H_
+#define _PYSIGNALS_H_
+#include <Python.h>
+
+/* forward */
+struct _PY_SIGNAL_SPEC_REC;
+
+typedef struct _PY_SIGNAL_REC
+{
+ struct _PY_SIGNAL_SPEC_REC *signal;
+ char *command; /* used for command and variable signal */
+ PyObject *handler;
+ int is_signal;
+} PY_SIGNAL_REC;
+
+typedef enum
+{
+ PSG_COMMAND,
+ PSG_SIGNAL,
+ PSG_ALL,
+} PSG_TYPE;
+
+PY_SIGNAL_REC *pysignals_command_bind(const char *cmd, PyObject *func,
+ const char *category, int priority);
+PY_SIGNAL_REC *pysignals_signal_add(const char *signal, PyObject *func,
+ int priority);
+int pysignals_command_bind_list(GSList **list, const char *command,
+ PyObject *func, const char *category, int priority);
+int pysignals_signal_add_list(GSList **list, const char *signal,
+ PyObject *func, int priority);
+void pysignals_command_unbind(PY_SIGNAL_REC *rec);
+void pysignals_signal_remove(PY_SIGNAL_REC *rec);
+void pysignals_remove_generic(PY_SIGNAL_REC *rec);
+int pysignals_remove_search(GSList **siglist, const char *name,
+ PyObject *func, PSG_TYPE type);
+void pysignals_remove_list(GSList *siglist);
+int pysignals_emit(const char *signal, PyObject *argtup);
+int pysignals_continue(PyObject *argtup);
+int pysignals_register(const char *name, const char *arglist);
+int pysignals_unregister(const char *name);
+void pysignals_init(void);
+void pysignals_deinit(void);
+
+#endif
diff --git a/src/pysource.c b/src/pysource.c
new file mode 100644
index 0000000..413ab2c
--- /dev/null
+++ b/src/pysource.c
@@ -0,0 +1,130 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pysource.h"
+
+typedef struct _PY_SOURCE_REC
+{
+ int tag;
+ GSList **tag_list;
+ int fd;
+ PyObject *func;
+ PyObject *data;
+} PY_SOURCE_REC;
+
+static PY_SOURCE_REC *py_source_rec_new(GSList **tag_list, int fd, PyObject *func, PyObject *data)
+{
+ PY_SOURCE_REC *rec;
+
+ rec = g_new0(PY_SOURCE_REC, 1);
+ rec->tag_list = tag_list;
+ rec->fd = fd;
+ rec->func = func;
+ rec->data = data;
+
+ Py_INCREF(func);
+ Py_XINCREF(data);
+
+ return rec;
+}
+
+static int py_remove_tag(GSList **list, int handle)
+{
+ GSList *node;
+
+ node = g_slist_find(*list, GINT_TO_POINTER(handle));
+ if (!node)
+ return 0;
+
+ *list = g_slist_delete_link(*list, node);
+
+ return 1;
+}
+
+static void py_source_destroy(PY_SOURCE_REC *rec)
+{
+ g_return_if_fail(py_remove_tag(rec->tag_list, rec->tag) == 1);
+ Py_DECREF(rec->func);
+ Py_XDECREF(rec->data);
+ g_free(rec);
+}
+
+static int py_handle_ret(PyObject *ret)
+{
+ int res;
+
+ if (!ret)
+ {
+ PyErr_Print();
+ res = FALSE;
+ }
+ else
+ {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ return res;
+}
+
+static int py_timeout_proxy(PY_SOURCE_REC *rec)
+{
+ PyObject *ret;
+
+ g_return_val_if_fail(rec != NULL, FALSE);
+
+ if (rec->data)
+ ret = PyObject_CallFunction(rec->func, "O", rec->data);
+ else
+ ret = PyObject_CallFunction(rec->func, "");
+
+ return py_handle_ret(ret);
+}
+
+static int py_io_proxy(GIOChannel *src, GIOCondition condition, PY_SOURCE_REC *rec)
+{
+ PyObject *ret;
+
+ g_return_val_if_fail(rec != NULL, FALSE);
+
+ if (rec->data)
+ ret = PyObject_CallFunction(rec->func, "iiO", rec->fd, condition, rec->data);
+ else
+ ret = PyObject_CallFunction(rec->func, "ii", rec->fd, condition);
+
+ return py_handle_ret(ret);
+}
+
+int pysource_timeout_add_list(GSList **list, int msecs, PyObject *func, PyObject *data)
+{
+ PY_SOURCE_REC *rec;
+
+ g_return_val_if_fail(func != NULL, -1);
+
+ rec = py_source_rec_new(list, -1, func, data);
+ rec->tag = g_timeout_add_full(G_PRIORITY_DEFAULT, msecs,
+ (GSourceFunc)py_timeout_proxy, rec,
+ (GDestroyNotify)py_source_destroy);
+
+ *list = g_slist_append(*list, GINT_TO_POINTER(rec->tag));
+
+ return rec->tag;
+}
+
+int pysource_io_add_watch_list(GSList **list, int fd, int cond, PyObject *func, PyObject *data)
+{
+ PY_SOURCE_REC *rec;
+ GIOChannel *channel;
+
+ g_return_val_if_fail(func != NULL, 1);
+
+ rec = py_source_rec_new(list, fd, func, data);
+ channel = g_io_channel_unix_new(fd);
+ rec->tag = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
+ (GIOFunc)py_io_proxy, rec,
+ (GDestroyNotify)py_source_destroy);
+ g_io_channel_unref(channel);
+
+ *list = g_slist_append(*list, GINT_TO_POINTER(rec->tag));
+
+ return rec->tag;
+}
diff --git a/src/pysource.h b/src/pysource.h
new file mode 100644
index 0000000..78b55ec
--- /dev/null
+++ b/src/pysource.h
@@ -0,0 +1,10 @@
+#ifndef _PYSOURCE_H_
+#define _PYSOURCE_H_
+
+#include <Python.h>
+
+/* condition is G_INPUT_READ or G_INPUT_WRITE */
+int pysource_io_add_watch_list(GSList **list, int fd, int cond, PyObject *func, PyObject *data);
+int pysource_timeout_add_list(GSList **list, int msecs, PyObject *func, PyObject *data);
+
+#endif
diff --git a/src/pystatusbar.c b/src/pystatusbar.c
new file mode 100644
index 0000000..8537718
--- /dev/null
+++ b/src/pystatusbar.c
@@ -0,0 +1,132 @@
+#include "pystatusbar.h"
+#include "pyirssi.h"
+#include "factory.h"
+
+typedef struct
+{
+ char *name;
+ PyObject *script;
+ PyObject *handler;
+} PY_BAR_ITEM_REC;
+
+/* Map: item name -> bar item obj */
+static GHashTable *py_bar_items = NULL;
+
+static void py_add_bar_handler(const char *iname, PyObject *script, PyObject *handler)
+{
+ PY_BAR_ITEM_REC *sitem;
+
+ sitem = g_new0(PY_BAR_ITEM_REC, 1);
+ sitem->name = g_strdup(iname);
+ sitem->script = script;
+ sitem->handler = handler;
+ Py_INCREF(script);
+ Py_INCREF(handler);
+
+ g_hash_table_insert(py_bar_items, sitem->name, sitem);
+}
+
+static void py_destroy_handler(PY_BAR_ITEM_REC *sitem)
+{
+ statusbar_item_unregister(sitem->name);
+
+ g_free(sitem->name); /* destroy key */
+ Py_DECREF(sitem->script);
+ Py_DECREF(sitem->handler);
+ g_free(sitem);
+}
+
+static void py_statusbar_proxy_call(SBAR_ITEM_REC *item, int sizeonly, PY_BAR_ITEM_REC *sitem)
+{
+ PyObject *pybaritem;
+ PyObject *ret;
+
+ g_return_if_fail(PyCallable_Check(sitem->handler));
+
+ pybaritem = pystatusbar_item_new(item);
+ if (!pybaritem)
+ {
+ PyErr_Print();
+ pystatusbar_item_unregister(sitem->name);
+ }
+
+ ret = PyObject_CallFunction(sitem->handler, "Oi", pybaritem, sizeonly);
+ if (!ret)
+ {
+ PyErr_Print();
+ pystatusbar_item_unregister(sitem->name);
+ }
+ else
+ Py_DECREF(ret);
+}
+
+static void py_statusbar_proxy(SBAR_ITEM_REC *item, int sizeonly)
+{
+ PY_BAR_ITEM_REC *sitem;
+
+ sitem = g_hash_table_lookup(py_bar_items, item->config->name);
+ if (sitem)
+ py_statusbar_proxy_call(item, sizeonly, sitem);
+ else
+ {
+ statusbar_item_default_handler(item, sizeonly, NULL, "", TRUE);
+ g_critical("unknown handler for Python statusbar proxy: %s", item->config->name);
+ }
+}
+
+void pystatusbar_item_register(PyObject *script, const char *sitem,
+ const char *value, PyObject *func)
+{
+ if (func)
+ {
+ g_return_if_fail(PyCallable_Check(func));
+ py_add_bar_handler(sitem, script, func);
+ }
+
+ statusbar_item_register(sitem, value, func? py_statusbar_proxy : NULL);
+}
+
+/* remove selected status bar item handler */
+void pystatusbar_item_unregister(const char *iname)
+{
+ if (!g_hash_table_remove(py_bar_items, iname))
+ statusbar_item_unregister(iname);
+}
+
+/* remove all statusbar item handlers for script */
+/* XXX: Only status bar items registered with a handler are stored in the hash table.
+ * Items registered with only a value are not stored, so there is no way to unregister
+ * them when the script is unloaded.
+ */
+static int py_check_clean(char *key, PY_BAR_ITEM_REC *value, PyObject *script)
+{
+ if (value->script == script)
+ return 1;
+
+ return 0;
+}
+
+void pystatusbar_cleanup_script(PyObject *script)
+{
+ g_hash_table_foreach_remove(py_bar_items, (GHRFunc)py_check_clean, script);
+}
+
+void pystatusbar_init(void)
+{
+ g_return_if_fail(py_bar_items == NULL);
+
+ /* key is freed by destroy_handler */
+ py_bar_items = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify)py_destroy_handler);
+}
+
+/* XXX: this must be called after cleaning up all the loaded scripts */
+void pystatusbar_deinit(void)
+{
+ g_return_if_fail(py_bar_items != NULL);
+ g_return_if_fail(g_hash_table_size(py_bar_items) == 0);
+
+ g_hash_table_destroy(py_bar_items);
+ py_bar_items = NULL;
+}
+
diff --git a/src/pystatusbar.h b/src/pystatusbar.h
new file mode 100644
index 0000000..9749c9f
--- /dev/null
+++ b/src/pystatusbar.h
@@ -0,0 +1,13 @@
+#ifndef _PYSTATUSBAR_H_
+#define _PYSTATUSBAR_H_
+
+#include <Python.h>
+
+void pystatusbar_item_register(PyObject *script, const char *sitem,
+ const char *value, PyObject *func);
+void pystatusbar_item_unregister(const char *iname);
+void pystatusbar_cleanup_script(PyObject *script);
+void pystatusbar_init(void);
+void pystatusbar_deinit(void);
+
+#endif
diff --git a/src/pythemes.c b/src/pythemes.c
new file mode 100644
index 0000000..a3e8927
--- /dev/null
+++ b/src/pythemes.c
@@ -0,0 +1,287 @@
+#include <Python.h>
+#include "pythemes.h"
+#include "pyirssi.h"
+#include "factory.h"
+#include "pymodule.h"
+#include "pyloader.h"
+
+static void py_get_mod(char *full, int fullsz, const char *script)
+{
+ g_snprintf(full, fullsz, "irssi_python/%s.py", script);
+}
+
+/* Edited from Perl Themes.xs */
+int pythemes_printformat(TEXT_DEST_REC *dest, const char *name, const char *format, PyObject *argtup)
+{
+ char *arglist[MAX_FORMAT_PARAMS + 1];
+ THEME_REC *theme;
+ char *str;
+ char script[256];
+ int formatnum;
+ int i;
+
+ py_get_mod(script, sizeof script, name);
+
+ formatnum = format_find_tag(script, format);
+ if (formatnum < 0) {
+ PyErr_Format(PyExc_KeyError, "unregistered format '%s'", format);
+ return 0;
+ }
+
+ memset(arglist, 0, sizeof arglist);
+ for (i = 0; i < MAX_FORMAT_PARAMS && i < PyTuple_Size(argtup); i++) {
+ PyObject *obj = PyTuple_GET_ITEM(argtup, i);
+ char *str;
+
+ if (!PyString_Check(obj)) {
+ PyErr_Format(PyExc_TypeError, "format argument list contains non-string data");
+ return 0;
+ }
+
+ str = PyString_AsString(obj);
+ if (!str)
+ return 0;
+
+ arglist[i] = str;
+ }
+
+ theme = window_get_theme(dest->window);
+ signal_emit("print format", 5, theme, script,
+ dest, GINT_TO_POINTER(formatnum), arglist);
+
+ str = format_get_text_theme_charargs(theme, script, dest, formatnum, arglist);
+ if (*str != '\0') printtext_dest(dest, "%s", str);
+ g_free(str);
+
+ return 1;
+}
+
+static void py_destroy_format_list(FORMAT_REC *recs)
+{
+ int i;
+
+ for (i = 0; recs[i].def; i++)
+ {
+ g_free(recs[i].def);
+ g_free(recs[i].tag);
+ }
+
+ g_free(recs);
+}
+
+/* register a list of formats in this format:
+ * [ (name, format), ... ]
+ */
+int pythemes_register(const char *name, PyObject *list)
+{
+ char script[256];
+ FORMAT_REC *formatrecs;
+ int i;
+
+ py_get_mod(script, sizeof script, name);
+
+ if (!PyList_Check(list))
+ {
+ PyErr_Format(PyExc_TypeError, "arg must be list");
+ return 0;
+ }
+
+ if (PyList_Size(list) == 0)
+ {
+ PyErr_Format(PyExc_TypeError, "cannot register empty list");
+ return 0;
+ }
+
+ if (g_hash_table_lookup(default_formats, script))
+ {
+ PyErr_Format(PyExc_KeyError, "format list already registered by script");
+ return 0;
+ }
+
+ formatrecs = g_new0(FORMAT_REC, PyList_Size(list) + 2);
+ formatrecs[0].tag = g_strdup(script);
+ formatrecs[0].def = g_strdup("Python script");
+
+ for (i = 0; i < PyList_Size(list); i++)
+ {
+ FORMAT_REC *rec;
+ PyObject *item;
+ char *key, *value;
+
+ rec = &formatrecs[i + 1];
+ item = PyList_GET_ITEM(list, i);
+ if (!PyTuple_Check(item) || !PyArg_ParseTuple(item, "ss", &key, &value))
+ {
+ if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_TypeError))
+ {
+ PyErr_Clear();
+ PyErr_Format(PyExc_TypeError, "format list must contain tuples of two strings");
+ }
+ py_destroy_format_list(formatrecs);
+ return 0;
+ }
+
+ rec->tag = g_strdup(key);
+ rec->def = g_strdup(value);
+ rec->params = MAX_FORMAT_PARAMS;
+ }
+
+ theme_register_module(script, formatrecs);
+
+ return 1;
+}
+
+void pythemes_unregister(const char *name)
+{
+ char script[256];
+ FORMAT_REC *formats;
+
+ py_get_mod(script, sizeof script, name);
+
+ formats = g_hash_table_lookup(default_formats, script);
+ if (!formats)
+ return;
+
+ py_destroy_format_list(formats);
+ theme_unregister_module(script);
+}
+
+/* XXX: test binding a PyCFunction to different sources. Not sure
+ if this is a good thing or not, but it seems to work */
+PyDoc_STRVAR(py_printformat_doc,
+ "for Server objects:\n"
+ "printformat(target, level, format, ...) -> None\n"
+ "\n"
+ "For all else:\n"
+ "printformat(level, format, ...) -> None\n"
+);
+static PyObject *py_printformat(PyObject *self, PyObject *all)
+{
+ int level;
+ char *format;
+ char *target;
+ PyObject *args = NULL, *varargs = NULL;
+ TEXT_DEST_REC dest;
+ char *script;
+ int formatstart;
+
+ if (self && pyserver_check(self))
+ formatstart = 3;
+ else
+ formatstart = 2;
+
+ args = PySequence_GetSlice(all, 0, formatstart);
+ if (!args)
+ goto error;
+
+ varargs = PySequence_GetSlice(all, formatstart, PyTuple_Size(all));
+ if (!varargs)
+ goto error;
+
+ if (self && pyserver_check(self))
+ {
+ if (!PyArg_ParseTuple(args, "sis", &target, &level, &format))
+ goto error;
+ }
+ else
+ {
+ if (!PyArg_ParseTuple(args, "is", &level, &format))
+ goto error;
+ }
+
+ script = pyloader_find_script_name();
+ if (!script)
+ {
+ PyErr_Format(PyExc_RuntimeError, "No script found");
+ goto error;
+ }
+
+ /* create the text dest depending on whether this function is called from
+ module level or as a method of one of the objects */
+ if (self == NULL) /* module */
+ format_create_dest(&dest, NULL, NULL, level, NULL);
+ else if (pyserver_check(self))
+ format_create_dest(&dest, DATA(self), target, level, NULL);
+ else if (pywindow_check(self))
+ format_create_dest(&dest, NULL, NULL, level, DATA(self));
+ else if (pywindow_item_check(self))
+ {
+ PyWindowItem *pywi = (PyWindowItem *)self;
+ format_create_dest(&dest, pywi->data->server, pywi->data->visible_name, level, NULL);
+ }
+
+ if (!pythemes_printformat(&dest, script, format, varargs))
+ goto error;
+
+ Py_DECREF(args);
+ Py_DECREF(varargs);
+
+ Py_RETURN_NONE;
+
+error:
+ Py_XDECREF(args);
+ Py_XDECREF(varargs);
+
+ return NULL;
+}
+
+/* XXX: these funcs could be moved to pyutils.c */
+static int py_add_module_func(PyMethodDef *mdef)
+{
+ PyObject *func;
+
+ g_return_val_if_fail(py_module != NULL, 0);
+
+ func = PyCFunction_New(mdef, NULL);
+ if (!func)
+ return 0;
+
+ if (PyModule_AddObject(py_module, mdef->ml_name, func) != 0)
+ {
+ Py_DECREF(func);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int py_add_method(PyTypeObject *type, PyMethodDef *mdef)
+{
+ int ret;
+ PyObject *func;
+
+ g_return_val_if_fail(type->tp_dict != NULL, 0);
+
+ func = PyDescr_NewMethod(type, mdef);
+ if (!func)
+ return 0;
+
+ ret = PyDict_SetItemString(type->tp_dict, mdef->ml_name, func);
+ Py_DECREF(func);
+ if (ret != 0)
+ return 0;
+
+ return 1;
+}
+
+int pythemes_init(void)
+{
+ static PyMethodDef pfdef = {"printformat", (PyCFunction)py_printformat,
+ METH_VARARGS, py_printformat_doc};
+
+ /* add function to main module and as member some types */
+
+ if (!py_add_module_func(&pfdef))
+ return 0;
+
+ if (!py_add_method(&PyServerType, &pfdef))
+ return 0;
+
+ if (!py_add_method(&PyWindowType, &pfdef))
+ return 0;
+
+ if (!py_add_method(&PyWindowItemType, &pfdef))
+ return 0;
+
+ return 1;
+}
diff --git a/src/pythemes.h b/src/pythemes.h
new file mode 100644
index 0000000..92af965
--- /dev/null
+++ b/src/pythemes.h
@@ -0,0 +1,13 @@
+#ifndef _PYTHEMES_H_
+#define _PYTHEMES_H_
+
+#include <Python.h>
+
+struct _TEXT_DEST_REC;
+
+int pythemes_printformat(struct _TEXT_DEST_REC *dest, const char *script, const char *format, PyObject *argtup);
+int pythemes_register(const char *script, PyObject *list);
+void pythemes_unregister(const char *script);
+int pythemes_init(void);
+
+#endif
diff --git a/src/pyutils.c b/src/pyutils.c
new file mode 100644
index 0000000..05e1f31
--- /dev/null
+++ b/src/pyutils.c
@@ -0,0 +1,81 @@
+#include <string.h>
+#include "pyirssi.h"
+#include "pyutils.h"
+#include "settings.h"
+#include "servers.h"
+
+/* copy paste from perl bindings */
+void py_command(const char *cmd, SERVER_REC *server, WI_ITEM_REC *item)
+{
+ const char *cmdchars;
+ char *sendcmd = (char *) cmd;
+
+ if (*cmd == '\0')
+ return;
+
+ cmdchars = settings_get_str("cmdchars");
+ if (strchr(cmdchars, *cmd) == NULL) {
+ /* no command char - let's put it there.. */
+ sendcmd = g_strdup_printf("%c%s", *cmdchars, cmd);
+ }
+
+ signal_emit("send command", 3, sendcmd, server, item);
+ if (sendcmd != cmd) g_free(sendcmd);
+}
+
+/* return the file extension for a file, or empty string
+ don't free result */
+char *file_get_ext(const char *file)
+{
+ const char *dot = NULL;
+
+ while (*file)
+ {
+ if (*file == '.')
+ dot = file;
+
+ file++;
+ }
+
+ if (dot)
+ return (char *) dot + 1;
+
+ return (char *) file;
+}
+
+int file_has_ext(const char *file, const char *ext)
+{
+ const char *fext = file_get_ext(file);
+
+ return !strcmp(fext, ext);
+}
+
+
+/* return whats in the braces -> /path/to/{filename}.py
+ result must be freed */
+char *file_get_filename(const char *path)
+{
+ const char *begin;
+ const char *end;
+ char *name;
+ size_t len;
+
+ begin = strrchr(path, '/');
+ if (!begin)
+ begin = path;
+ else
+ begin++;
+
+ end = strrchr(begin, '.');
+ if (end != NULL && end > begin)
+ len = end - begin;
+ else
+ len = strlen(begin);
+
+ name = g_strnfill(len, 0);
+
+ strncpy(name, begin, len);
+
+ return name;
+}
+
diff --git a/src/pyutils.h b/src/pyutils.h
new file mode 100644
index 0000000..7b2ca7b
--- /dev/null
+++ b/src/pyutils.h
@@ -0,0 +1,12 @@
+#ifndef _PYUTILS_H_
+#define _PYUTILS_H_
+
+#include "servers.h"
+
+void py_command(const char *cmd, SERVER_REC *server, WI_ITEM_REC *item);
+char *file_get_ext(const char *file);
+int file_has_ext(const char *file, const char *ext);
+char *file_get_filename(const char *path);
+
+
+#endif
diff --git a/src/sig2code.awk b/src/sig2code.awk
new file mode 100644
index 0000000..d6bdb0d
--- /dev/null
+++ b/src/sig2code.awk
@@ -0,0 +1,61 @@
+BEGIN {
+ FS = "[ \t]*->[ \t]*";
+
+ #read in codes
+ while (getline < "sig2code.txt")
+ {
+ sub(/^[ \t]*/, "");
+ if (NF < 2)
+ continue;
+
+ #print $1, $2
+ sigmap[$2] = $1
+ }
+
+ close("sig2code.txt");
+
+ FS = "[ \t]*,[ \t]*";
+
+ print "/* Include in your C module */";
+ print "static PY_SIGNAL_SPEC_REC py_sigmap[] = {";
+}
+
+function match_type(t)
+{
+ for (type in sigmap)
+ {
+ if (index(t, type) != 0)
+ return sigmap[type];
+ }
+
+ return "?";
+}
+
+$1 ~ /^[ \t]*"/ && $1 !~ /"script error"/ {
+ sub(/^[ \t]*/, "");
+
+ signal = $1
+ if (signal ~ /.*<cmd>$/)
+ {
+ varsig = 1;
+ sub(/<cmd>/, "", signal);
+ }
+ else
+ varsig = 0;
+
+ args = "";
+ for (i = 2; i <= NF; i++)
+ {
+ args = args""match_type($i);
+ }
+
+ printf(" {%s, \"%s\", 0, 0, %d},\n", signal, args, varsig);
+}
+
+END {
+ print " {NULL}" ;
+ print "};";
+ print "";
+ print "#define py_sigmap_len() (sizeof(py_sigmap) / sizeof(py_sigmap[0]) - 1)";
+}
+
diff --git a/src/sig2code.txt b/src/sig2code.txt
new file mode 100644
index 0000000..379e88e
--- /dev/null
+++ b/src/sig2code.txt
@@ -0,0 +1,44 @@
+ Conversion codes notes
+ I = int *arg IN/OUT
+ G = string GList **arg IN/OUT (list must be reconstructed)
+ L = list of nicks
+
+ Scalars
+ s -> char *
+ u -> ulong *
+ I -> int *
+ i -> int
+
+ Lists of things (completion.c and massjoin.c)
+ G -> GList * of char*
+ L -> GSList of NICK_RECs
+
+ Chat objects
+ c -> CHATNET_REC
+ S -> SERVER_REC
+ C -> CHANNEL_REC
+ q -> QUERY_REC
+ n -> NICK_REC
+ W -> WI_ITEM_REC
+
+ Irssi objects
+ d -> DCC_REC
+
+ Other objects
+ r -> RECONNECT_REC
+ o -> COMMAND_REC
+ l -> LOG_REC
+ a -> RAWLOG_REC
+ g -> IGNORE_REC
+ ? -> MODULE_REC
+ b -> BAN_REC
+ N -> NETSPLIT_REC
+ e -> NETSPLIT_SERVER_REC
+ ? -> AUTOIGNORE_REC
+ O -> NOTIFYLIST_REC
+ ? -> THEME_REC
+ ? -> KEYINFO_REC
+ p -> PROCESS_REC
+ t -> TEXT_DEST_REC
+ w -> WINDOW_REC
+