summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Davis <loafier@gmail.com>2006-06-19 12:25:06 +0000
committerChristopher Davis <loafier@gmail.com>2006-06-19 12:25:06 +0000
commitbb48c914c6239ed1dbcb29eb62d33d3ab91e7215 (patch)
tree0b6910959f96f683f3aa11a8e2f53572e42b3305
parentefef73ae301947875602d67d0979a8ce3bd57dd0 (diff)
downloadirssi-python-bb48c914c6239ed1dbcb29eb62d33d3ab91e7215.tar.gz
irssi-python-bb48c914c6239ed1dbcb29eb62d33d3ab91e7215.tar.xz
irssi-python-bb48c914c6239ed1dbcb29eb62d33d3ab91e7215.zip
initial import
git-svn-id: http://svn.irssi.org/repos/irssi-python@4282 dbcabf3a-b0e7-0310-adc4-f8d773084564
-rw-r--r--Makefile27
-rw-r--r--README62
-rw-r--r--classes.txt39
-rw-r--r--irssi.py68
-rw-r--r--irssi_startup.py202
-rw-r--r--objects/Makefile24
-rw-r--r--objects/ban-object.c155
-rw-r--r--objects/ban-object.h18
-rw-r--r--objects/base-objects.c251
-rw-r--r--objects/base-objects.h96
-rw-r--r--objects/channel-object.c331
-rw-r--r--objects/channel-object.h22
-rw-r--r--objects/chatnet-object.c182
-rw-r--r--objects/chatnet-object.h22
-rw-r--r--objects/connect-object.c187
-rw-r--r--objects/connect-object.h22
-rw-r--r--objects/dcc-object.c321
-rw-r--r--objects/dcc-object.h24
-rw-r--r--objects/factory.c291
-rw-r--r--objects/factory.h46
-rw-r--r--objects/ignore-object.c275
-rw-r--r--objects/ignore-object.h21
-rw-r--r--objects/irc-channel-object.c166
-rw-r--r--objects/irc-channel-object.h21
-rw-r--r--objects/irc-connect-object.c99
-rw-r--r--objects/irc-connect-object.h21
-rw-r--r--objects/irc-server-object.c265
-rw-r--r--objects/irc-server-object.h21
-rw-r--r--objects/log-object.c465
-rw-r--r--objects/log-object.h21
-rw-r--r--objects/logitem-object.c156
-rw-r--r--objects/logitem-object.h21
-rw-r--r--objects/nick-object.c216
-rw-r--r--objects/nick-object.h22
-rw-r--r--objects/pyscript-object.c225
-rw-r--r--objects/pyscript-object.h25
-rw-r--r--objects/query-object.c177
-rw-r--r--objects/query-object.h21
-rw-r--r--objects/rawlog-object.c306
-rw-r--r--objects/rawlog-object.h22
-rw-r--r--objects/reconnect-object.c152
-rw-r--r--objects/reconnect-object.h20
-rw-r--r--objects/server-object.c763
-rw-r--r--objects/server-object.h28
-rw-r--r--objects/textdest-object.c188
-rw-r--r--objects/textdest-object.h23
-rw-r--r--objects/window-item-object.c307
-rw-r--r--objects/window-item-object.h26
-rw-r--r--objects/window-object.c646
-rw-r--r--objects/window-object.h21
-rw-r--r--pycore.c152
-rw-r--r--pycore.h7
-rw-r--r--pyirssi.h25
-rw-r--r--pyirssi_irc.h16
-rw-r--r--pyloader.c306
-rw-r--r--pyloader.h21
-rw-r--r--pymodule.c817
-rw-r--r--pymodule.h11
-rw-r--r--pysignals.c95
-rw-r--r--pysignals.h14
-rw-r--r--pyutils.c81
-rw-r--r--pyutils.h12
-rw-r--r--scripts/CVS/Entries3
-rw-r--r--scripts/CVS/Repository1
-rw-r--r--scripts/CVS/Root1
-rw-r--r--scripts/dumper.py188
-rw-r--r--scripts/hello.py20
-rw-r--r--scripts/test_window.py50
68 files changed, 8950 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2c2faf8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,27 @@
+CC = gcc
+
+PYTHON = /usr/include/python2.4
+IRSSI = /usr/local/include/irssi
+
+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. -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
+
+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)
+
+clean:
+ rm -f *.o *.so
+ cd objects/ && make clean
+
diff --git a/README b/README
new file mode 100644
index 0000000..ed4201f
--- /dev/null
+++ b/README
@@ -0,0 +1,62 @@
+Pytest for Irssi
+
+This module embeds Python into an Irssi module. It provides just enough of an
+interface to port the hello.pl example from the Perl docs to Python. There are
+still a lot of things needed to be done, and some design issues to work out.
+See the scripts/ directory for two small example scripts.
+
+To compile:
+ So far I've compiled it on Debian i386 and on FreeBSD i386. Haven't tried
+ elsewhere. The Makefile will need to be edited for it to be compiled. It
+ requires Python 2.4 installed. Put libpytest.so wherever Irssi recognizes
+ modules.
+
+In pytest so far:
+ -Looks for scripts in ~/.irssi/pyscripts
+ -Created base object wrappers for structs with type and chat_type members.
+ -Wrapped members of most of the core structs:
+ SERVER_REC
+ WI_ITEM_REC
+ CHANNEL_REC
+ QUERY_REC
+ -Wrapped print methods for the global module, server, and window item.
+ -Wrapped command methods for server and window item.
+ -Included a signal handler in the base server and window item types that
+ seems to prevent crashes when an object is accessed after a server is
+ disconnected or a window item is removed (channel close, etc).
+ -Python stdout and stderr are redirected to the Irssi status window.
+ -The command_bind method of the Script object binds commands.
+
+TODO:
+ -Wrap the rest of the *_REC structs.
+ -Wrap the important methods and functions.
+ -Signal handling
+ -Wrapper object factory
+ -Handle settings
+ -Handle timeouts
+ -Bunch of other stuff
+
+Design issues:
+ -Threading. Pytest doesn't consider thread usage by scripts at all. Any
+ thread usage would likely cause a crash, as pytest makes no attempt to
+ manipulate Python's interpreter lock or thread state. It would be easy to
+ use PyGILState_* critical sections around important parts where Irssi
+ calls into Python, but I don't know about the larger impact on Irssi for
+ allowing threads to run. With the signal system and timeouts, is this
+ really an important issue?
+
+ -Structure. C modules could use some adjustment, especially as more code
+ is added. I also noticed how the Perl wrapper, like the rest of Irssi,
+ seems to be organized around interface type. The Python wrapper may need
+ some adjustment for this(?)
+
+ -Also, command_bind currently isn't a function in the main namespace like in
+ the Perl wrapper. Instead, its a method of the Script object, which includes
+ a list of bound signals for record keeping. That way, the context is always
+ certain. Currently, the Script object is inserted into the user's script
+ through a startup hook function (irssi_main). There could be a better way
+ of doing this (perhaps by inserting the script object directly into the
+ script's globals and using an import hook to distribute it to modules the
+ script imports).
+
+
diff --git a/classes.txt b/classes.txt
new file mode 100644
index 0000000..e94fd97
--- /dev/null
+++ b/classes.txt
@@ -0,0 +1,39 @@
+Basic types (source):
+ IrssiObject/
+ IrssiChatObject/
+ Chatnet/ (core/chatnets.h)
+ IrcChatnet (irc/core/irc-chatnets.h)
+ #ServerSetup/ (core/servers-setup.h)
+ # IrcServerSetup (irc/core/irc-servers-setup.h)
+ ServerConnect/ (core/servers.h)
+ IrcServerConnect (irc/core/irc-servers.h)
+ Server/ (core/servers.h)
+ IrcServer (irc/core/irc-servers.h)
+ WindowItem/ (core/window-item*.h)
+ Channel/ (core/channels.h)
+ IrcChannel (irc/core/irc-channels.h)
+ Query/ (core/queries.h)
+ IrcQuery (irc/core/irc-queries.h NOTE: only one additional method)
+ Nick/ (core/nick-rec.h)
+ Dcc/ (irc/dcc/{dcc,dcc-rec}.h)
+ DccFile/ (irc/dcc/dcc-file.h)
+ DccGet (irc/dcc/dcc-get.h)
+ DccSend (irc/dcc/dcc-send.h)
+ DccServer (irc/dcc/dcc-server.h NOTE: is this needed?)
+ DccChat (irc/dcc/dcc-chat.h)
+
+Final Types (source):
+ Window (fe-common/core/fe-windows.h)
+ TextDest (fe-common/core/testdest.h)
+ Reconnect (core/servers-reconnect.h)
+ Netsplit (irc/core/netsplit.h)
+ NetsplitServer (irc/core/netsplit.h)
+ NetsplitChannel (irc/core/netsplit.h)
+ Notifylist (irc/notifylist/notifylist.h)
+ Ban (irc/core/modelists.h)
+ Ignore (core/ignore.h)
+ Log (core/log.h)
+ Logitem (core/log.h)
+ Rawlog (core/rawlog.h)
+ Masks ** NO STRUCT ** (core/masks.h)
+ Process (fe-common/core/fe-exec.h NOTE: what about EXEC_WI_REC sub from WI_ITEM?)
diff --git a/irssi.py b/irssi.py
new file mode 100644
index 0000000..9b0ef6f
--- /dev/null
+++ b/irssi.py
@@ -0,0 +1,68 @@
+"""
+"""
+
+import sys
+import _irssi
+from _irssi import *
+
+#XXX: what if the values change? (better to compile in constants?)
+#Level constants (coppied from levels.h valid Irssi 0.8.10)
+MSGLEVEL_CRAP = 0x0000001
+MSGLEVEL_MSGS = 0x0000002
+MSGLEVEL_PUBLIC = 0x0000004
+MSGLEVEL_NOTICES = 0x0000008
+MSGLEVEL_SNOTES = 0x0000010
+MSGLEVEL_CTCPS = 0x0000020
+MSGLEVEL_ACTIONS = 0x0000040
+MSGLEVEL_JOINS = 0x0000080
+MSGLEVEL_PARTS = 0x0000100
+MSGLEVEL_QUITS = 0x0000200
+MSGLEVEL_KICKS = 0x0000400
+MSGLEVEL_MODES = 0x0000800
+MSGLEVEL_TOPICS = 0x0001000
+MSGLEVEL_WALLOPS = 0x0002000
+MSGLEVEL_INVITES = 0x0004000
+MSGLEVEL_NICKS = 0x0008000
+MSGLEVEL_DCC = 0x0010000
+MSGLEVEL_DCCMSGS = 0x0020000
+MSGLEVEL_CLIENTNOTICE = 0x0040000
+MSGLEVEL_CLIENTCRAP = 0x0080000
+MSGLEVEL_CLIENTERROR = 0x0100000
+MSGLEVEL_HILIGHT = 0x0200000
+MSGLEVEL_ALL = 0x03fffff
+MSGLEVEL_NOHILIGHT = 0x1000000
+MSGLEVEL_NO_ACT = 0x2000000
+MSGLEVEL_NEVER = 0x4000000
+MSGLEVEL_LASTLOG = 0x8000000
+
+"""
+#Link Irssi functions and objects
+get_script = _irssi.get_script
+prnt = _irssi.prnt
+chatnets = _irssi.chatnets
+servers = _irssi.servers
+reconnects = _irssi.reconnects
+chatnet_find = _irssi.chatnet_find
+windows = _irssi.windows
+
+Script = _irssi.Script
+Channel = _irssi.Channel
+Query = _irssi.Query
+WindowItem = _irssi.WindowItem
+Window = _irssi.Window
+Server = _irssi.Server
+Connect = _irssi.Connect
+IrcServer = _irssi.IrcServer
+IrcConnect = _irssi.IrcConnect
+IrcChannel = _irssi.IrcChannel
+Ban = _irssi.Ban
+Nick = _irssi.Nick
+Chatnet = _irssi.Chatnet
+Reconnect = _irssi.Reconnect
+"""
+
+def command_bind(*args, **kwargs):
+ """ see Script.command_bind """
+ get_script().command_bind(*args, **kwargs)
+
+
diff --git a/irssi_startup.py b/irssi_startup.py
new file mode 100644
index 0000000..ee8dbdd
--- /dev/null
+++ b/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/objects/Makefile b/objects/Makefile
new file mode 100644
index 0000000..b48d47e
--- /dev/null
+++ b/objects/Makefile
@@ -0,0 +1,24 @@
+CC = gcc
+
+PYTHON = /usr/include/python2.4
+IRSSI = /usr/local/include/irssi
+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.. \
+`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 factory.o
+
+pyobjects.a: $(OBJ)
+ ar r pyobjects.a $(OBJ)
+
+%.o: %.c
+ $(CC) -c $< $(CFLAGS)
+
+clean:
+ rm -f *.o *.so *.a
diff --git a/objects/ban-object.c b/objects/ban-object.c
new file mode 100644
index 0000000..908bae2
--- /dev/null
+++ b/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/objects/ban-object.h b/objects/ban-object.h
new file mode 100644
index 0000000..59ec9e7
--- /dev/null
+++ b/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/objects/base-objects.c b/objects/base-objects.c
new file mode 100644
index 0000000..3601593
--- /dev/null
+++ b/objects/base-objects.c
@@ -0,0 +1,251 @@
+#include <Python.h>
+#include "structmember.h"
+#include "pymodule.h"
+#include "base-objects.h"
+#include "pyirssi.h"
+
+/* This is the base type for most, if not all, 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. */
+
+/* member IDs */
+enum
+{
+ M_BASE_TYPE,
+ M_BASE_NAME,
+ M_BASE_VALID,
+};
+
+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;
+}
+
+static PyObject *PyIrssiBase_get(PyIrssiBase *self, void *closure)
+{
+ int member = GPOINTER_TO_INT(closure);
+
+ /* If the user passed the valid member, don't trigger an exception */
+ if (member != M_BASE_VALID)
+ RET_NULL_IF_INVALID(self->data);
+
+ switch (member)
+ {
+ case M_BASE_TYPE:
+ return PyInt_FromLong(self->data->type);
+ case M_BASE_NAME:
+ RET_AS_STRING_OR_NONE(self->base_name);
+ case M_BASE_VALID:
+ if (self->data != NULL)
+ Py_RETURN_TRUE;
+ else
+ Py_RETURN_FALSE;
+ }
+
+ INVALID_MEMBER(member);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyIrssiBase_getseters[] = {
+ {"type_id", (getter)PyIrssiBase_get, NULL,
+ "Irssi's type id for object",
+ GINT_TO_POINTER(M_BASE_TYPE)},
+
+ {"type", (getter)PyIrssiBase_get, NULL,
+ "Irssi's name for object",
+ GINT_TO_POINTER(M_BASE_NAME)},
+
+ {"valid", (getter)PyIrssiBase_get, NULL,
+ "True if the object is valid",
+ GINT_TO_POINTER(M_BASE_VALID)},
+ {NULL}
+};
+
+/* Methods for object */
+static PyMethodDef PyIrssiBase_methods[] = {
+ /* {"somemeth", (PyCFunction)PyIrssiBase_name, METH_NOARGS, "docstr"}, */
+ {NULL} /* Sentinel */
+};
+
+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 */
+
+/* member IDs */
+enum
+{
+ M_CHAT_CHAT_TYPE,
+ M_CHAT_CHAT_NAME,
+};
+
+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;
+}
+
+static PyObject *PyIrssiChatBase_get(PyIrssiChatBase *self, void *closure)
+{
+ int member = GPOINTER_TO_INT(closure);
+
+ RET_NULL_IF_INVALID(self->data);
+
+ switch (member)
+ {
+ case M_CHAT_CHAT_TYPE:
+ return PyInt_FromLong(self->data->chat_type);
+ case M_CHAT_CHAT_NAME:
+ {
+ CHAT_PROTOCOL_REC *rec = chat_protocol_find_id(self->data->chat_type);
+ if (rec)
+ RET_AS_STRING_OR_NONE(rec->name);
+ else
+ Py_RETURN_NONE;
+ }
+ }
+
+ INVALID_MEMBER(member);
+}
+
+//specialized getters/setters
+static PyGetSetDef PyIrssiChatBase_getseters[] = {
+ {"chat_type_id", (getter)PyIrssiChatBase_get, NULL,
+ "Chat Type id",
+ GINT_TO_POINTER(M_CHAT_CHAT_TYPE)},
+
+ {"chat_type", (getter)PyIrssiChatBase_get, NULL,
+ "Chat Name",
+ GINT_TO_POINTER(M_CHAT_CHAT_NAME)},
+ {NULL}
+};
+
+static PyMethodDef PyIrssiChatBase_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+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/objects/base-objects.h b/objects/base-objects.h
new file mode 100644
index 0000000..d2f6acb
--- /dev/null
+++ b/objects/base-objects.h
@@ -0,0 +1,96 @@
+#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;
+
+/* 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/objects/channel-object.c b/objects/channel-object.c
new file mode 100644
index 0000000..7e3758d
--- /dev/null
+++ b/objects/channel-object.c
@@ -0,0 +1,331 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pymodule.h"
+#include "factory.h"
+#include "channel-object.h"
+#include "pycore.h"
+
+/* member IDs */
+enum
+{
+ M_CHANNEL_TOPIC,
+ M_CHANNEL_TOPIC_BY,
+ M_CHANNEL_TOPIC_TIME,
+ M_CHANNEL_NO_MODES,
+ M_CHANNEL_MODE,
+ M_CHANNEL_LIMIT,
+ M_CHANNEL_KEY,
+ M_CHANNEL_CHANOP,
+ M_CHANNEL_NAMES_GOT,
+ M_CHANNEL_WHOLIST,
+ M_CHANNEL_SYNCED,
+ M_CHANNEL_JOINED,
+ M_CHANNEL_LEFT,
+ M_CHANNEL_KICKED,
+};
+
+/* 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);
+}
+
+
+static PyObject *PyChannel_get(PyChannel *self, void *closure)
+{
+ int member = GPOINTER_TO_INT(closure);
+
+ RET_NULL_IF_INVALID(self->data);
+
+ switch (member)
+ {
+ case M_CHANNEL_TOPIC:
+ RET_AS_STRING_OR_NONE(self->data->topic);
+ case M_CHANNEL_TOPIC_BY:
+ RET_AS_STRING_OR_NONE(self->data->topic_by);
+ case M_CHANNEL_TOPIC_TIME:
+ return PyLong_FromLong(self->data->topic_time);
+ case M_CHANNEL_NO_MODES:
+ return PyBool_FromLong(self->data->no_modes);
+ case M_CHANNEL_MODE:
+ RET_AS_STRING_OR_NONE(self->data->mode);
+ case M_CHANNEL_LIMIT:
+ return PyInt_FromLong(self->data->limit);
+ case M_CHANNEL_KEY:
+ RET_AS_STRING_OR_NONE(self->data->key);
+ case M_CHANNEL_CHANOP:
+ return PyBool_FromLong(self->data->chanop);
+ case M_CHANNEL_NAMES_GOT:
+ return PyBool_FromLong(self->data->names_got);
+ case M_CHANNEL_WHOLIST:
+ return PyBool_FromLong(self->data->wholist);
+ case M_CHANNEL_SYNCED:
+ return PyBool_FromLong(self->data->synced);
+ case M_CHANNEL_JOINED:
+ return PyBool_FromLong(self->data->joined);
+ case M_CHANNEL_LEFT:
+ return PyBool_FromLong(self->data->left);
+ case M_CHANNEL_KICKED:
+ return PyBool_FromLong(self->data->kicked);
+ }
+
+ /* This shouldn't be reached... but... */
+ return PyErr_Format(PyExc_RuntimeError, "invalid member id, %d", member);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyChannel_getseters[] = {
+ {"topic", (getter)PyChannel_get, NULL,
+ "Channel topic",
+ GINT_TO_POINTER(M_CHANNEL_TOPIC)},
+
+ {"topic_by", (getter)PyChannel_get, NULL,
+ "Nick who set the topic",
+ GINT_TO_POINTER(M_CHANNEL_TOPIC_BY)},
+
+ {"topic_time", (getter)PyChannel_get, NULL,
+ "Timestamp when the topic was set",
+ GINT_TO_POINTER(M_CHANNEL_TOPIC_TIME)},
+
+ {"no_modes", (getter)PyChannel_get, NULL,
+ "Channel is modeless",
+ GINT_TO_POINTER(M_CHANNEL_NO_MODES)},
+
+ {"mode", (getter)PyChannel_get, NULL,
+ "Channel mode",
+ GINT_TO_POINTER(M_CHANNEL_MODE)},
+
+ {"limit", (getter)PyChannel_get, NULL,
+ "Max. users in channel (+l mode)",
+ GINT_TO_POINTER(M_CHANNEL_LIMIT)},
+
+ {"key", (getter)PyChannel_get, NULL,
+ "Channel key (password)",
+ GINT_TO_POINTER(M_CHANNEL_KEY)},
+
+ {"chanop", (getter)PyChannel_get, NULL,
+ "You are channel operator",
+ GINT_TO_POINTER(M_CHANNEL_CHANOP)},
+
+ {"names_got", (getter)PyChannel_get, NULL,
+ "/NAMES list has been received",
+ GINT_TO_POINTER(M_CHANNEL_NAMES_GOT)},
+
+ {"wholist", (getter)PyChannel_get, NULL,
+ "/WHO list has been received",
+ GINT_TO_POINTER(M_CHANNEL_WHOLIST)},
+
+ {"synced", (getter)PyChannel_get, NULL,
+ "Channel is fully synchronized",
+ GINT_TO_POINTER(M_CHANNEL_SYNCED)},
+
+ {"joined", (getter)PyChannel_get, NULL,
+ "JOIN event for this channel has been received",
+ GINT_TO_POINTER(M_CHANNEL_JOINED)},
+
+ {"left", (getter)PyChannel_get, NULL,
+ "You just left the channel (for 'channel destroyed' event)",
+ GINT_TO_POINTER(M_CHANNEL_LEFT)},
+
+ {"kicked", (getter)PyChannel_get, NULL,
+ "You was just kicked out of the channel (for 'channel destroyed' event)",
+ GINT_TO_POINTER(M_CHANNEL_KICKED)},
+
+ {NULL}
+};
+
+PyDoc_STRVAR(PyChannel_nicks_doc,
+ "Return a list of nicks in the channel."
+);
+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,
+ "Find nick mask from nicklist, wildcards allowed."
+);
+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,
+ "Find nick from nicklist."
+);
+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,
+ "Remove nick from nicklist."
+);
+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,
+ "Insert nick object into nicklist."
+);
+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/objects/channel-object.h b/objects/channel-object.h
new file mode 100644
index 0000000..0858e7b
--- /dev/null
+++ b/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/objects/chatnet-object.c b/objects/chatnet-object.c
new file mode 100644
index 0000000..c22f0cb
--- /dev/null
+++ b/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/objects/chatnet-object.h b/objects/chatnet-object.h
new file mode 100644
index 0000000..8a6d1fc
--- /dev/null
+++ b/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/objects/connect-object.c b/objects/connect-object.c
new file mode 100644
index 0000000..2db0e13
--- /dev/null
+++ b/objects/connect-object.c
@@ -0,0 +1,187 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "connect-object.h"
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+/* member IDs */
+enum
+{
+ M_CONNECT_ADDRESS,
+ M_CONNECT_PORT,
+ M_CONNECT_CHATNET,
+ M_CONNECT_PASSWORD,
+ M_CONNECT_WANTED_NICK,
+ M_CONNECT_USERNAME,
+ M_CONNECT_REALNAME,
+};
+
+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);
+}
+
+static PyObject *PyConnect_get(PyConnect *self, void *closure)
+{
+ int member = GPOINTER_TO_INT(closure);
+
+ RET_NULL_IF_INVALID(self->data);
+
+ switch (member)
+ {
+ case M_CONNECT_ADDRESS:
+ RET_AS_STRING_OR_NONE(self->data->address);
+ case M_CONNECT_PORT:
+ return PyInt_FromLong(self->data->port);
+ case M_CONNECT_CHATNET:
+ RET_AS_STRING_OR_NONE(self->data->chatnet);
+ case M_CONNECT_PASSWORD:
+ RET_AS_STRING_OR_NONE(self->data->password);
+ case M_CONNECT_WANTED_NICK:
+ RET_AS_STRING_OR_NONE(self->data->nick);
+ case M_CONNECT_USERNAME:
+ RET_AS_STRING_OR_NONE(self->data->username);
+ case M_CONNECT_REALNAME:
+ RET_AS_STRING_OR_NONE(self->data->realname);
+ }
+
+ /* This shouldn't be reached... but... */
+ return PyErr_Format(PyExc_RuntimeError, "invalid member id, %d", member);
+}
+
+static PyGetSetDef PyConnect_getseters[] = {
+ {"address", (getter)PyConnect_get, NULL,
+ "Address where we connected (irc.blah.org)",
+ GINT_TO_POINTER(M_CONNECT_ADDRESS)},
+
+ {"port", (getter)PyConnect_get, NULL,
+ "Port where we connected",
+ GINT_TO_POINTER(M_CONNECT_PORT)},
+
+ {"chatnet", (getter)PyConnect_get, NULL,
+ "Chat network",
+ GINT_TO_POINTER(M_CONNECT_CHATNET)},
+
+ {"password", (getter)PyConnect_get, NULL,
+ "Password we used in connection.",
+ GINT_TO_POINTER(M_CONNECT_PASSWORD)},
+
+ {"wanted_nick", (getter)PyConnect_get, NULL,
+ "Nick which we would prefer to use",
+ GINT_TO_POINTER(M_CONNECT_WANTED_NICK)},
+
+ {"username", (getter)PyConnect_get, NULL,
+ "User name",
+ GINT_TO_POINTER(M_CONNECT_USERNAME)},
+
+ {"realname", (getter)PyConnect_get, NULL,
+ "Real name",
+ GINT_TO_POINTER(M_CONNECT_REALNAME)},
+
+ {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/objects/connect-object.h b/objects/connect-object.h
new file mode 100644
index 0000000..53bd509
--- /dev/null
+++ b/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/objects/dcc-object.c b/objects/dcc-object.c
new file mode 100644
index 0000000..772c011
--- /dev/null
+++ b/objects/dcc-object.c
@@ -0,0 +1,321 @@
+#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)
+{
+ DCC_REC *data = self->data;
+ const char *type;
+
+ RET_NULL_IF_INVALID(data);
+
+ type = module_find_id_str("DCC", 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)
+{
+ DCC_REC *data = self->data;
+
+ RET_NULL_IF_INVALID(data);
+ return PyLong_FromUnsignedLong(data->created);
+}
+
+PyDoc_STRVAR(PyDcc_server_doc,
+ "Server record where the DCC was initiated."
+);
+static PyObject *PyDcc_server_get(PyDcc *self, void *closure)
+{
+ DCC_REC *data = self->data;
+
+ RET_NULL_IF_INVALID(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)
+{
+ DCC_REC *data = self->data;
+
+ RET_NULL_IF_INVALID(data);
+ RET_AS_STRING_OR_NONE(data->servertag);
+}
+
+PyDoc_STRVAR(PyDcc_mynick_doc,
+ "Our nick to use in DCC chat."
+);
+static PyObject *PyDcc_mynick_get(PyDcc *self, void *closure)
+{
+ DCC_REC *data = self->data;
+
+ RET_NULL_IF_INVALID(data);
+ RET_AS_STRING_OR_NONE(data->mynick);
+}
+
+PyDoc_STRVAR(PyDcc_nick_doc,
+ "Other side's nick name."
+);
+static PyObject *PyDcc_nick_get(PyDcc *self, void *closure)
+{
+ DCC_REC *data = self->data;
+
+ RET_NULL_IF_INVALID(data);
+ RET_AS_STRING_OR_NONE(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)
+{
+ DCC_REC *data = self->data;
+
+ RET_NULL_IF_INVALID(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)
+{
+ DCC_REC *data = self->data;
+
+ RET_NULL_IF_INVALID(data);
+ RET_AS_STRING_OR_NONE(data->target);
+}
+
+PyDoc_STRVAR(PyDcc_arg_doc,
+ "Given argument .. file name usually"
+);
+static PyObject *PyDcc_arg_get(PyDcc *self, void *closure)
+{
+ DCC_REC *data = self->data;
+
+ RET_NULL_IF_INVALID(data);
+ RET_AS_STRING_OR_NONE(data->arg);
+}
+
+PyDoc_STRVAR(PyDcc_addr_doc,
+ "Other side's IP address."
+);
+static PyObject *PyDcc_addr_get(PyDcc *self, void *closure)
+{
+ DCC_REC *data = self->data;
+
+ RET_NULL_IF_INVALID(data);
+ RET_AS_STRING_OR_NONE(data->addrstr);
+}
+
+PyDoc_STRVAR(PyDcc_port_doc,
+ "Port we're connecting in."
+);
+static PyObject *PyDcc_port_get(PyDcc *self, void *closure)
+{
+ DCC_REC *data = self->data;
+
+ RET_NULL_IF_INVALID(data);
+ return PyInt_FromLong(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)
+{
+ DCC_REC *data = self->data;
+
+ RET_NULL_IF_INVALID(data);
+ return PyLong_FromUnsignedLong(data->starttime);
+}
+
+PyDoc_STRVAR(PyDcc_transfd_doc,
+ "Bytes transferred"
+);
+static PyObject *PyDcc_transfd_get(PyDcc *self, void *closure)
+{
+ DCC_REC *data = self->data;
+
+ RET_NULL_IF_INVALID(data);
+ return PyLong_FromUnsignedLong(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 DCC connection"
+);
+static PyObject *PyDcc_destroy(PyDcc *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ dcc_destroy(DCC(self->data));
+
+ Py_RETURN_NONE;
+}
+
+/* Methods for object */
+static PyMethodDef PyDcc_methods[] = {
+ {"destroy", (PyCFunction)PyDcc_destroy, METH_NOARGS,
+ PyDcc_destroy_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;
+
+ //XXX: do dcc chat
+
+ pydcc = py_instp(PyDcc, subclass);
+ if (!pydcc)
+ {
+ Py_XDECREF(server);
+ Py_XDECREF(chat);
+ return NULL;
+ }
+
+ pydcc->data = dcc;
+ pydcc->server = server;
+ 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/objects/dcc-object.h b/objects/dcc-object.h
new file mode 100644
index 0000000..d44d3db
--- /dev/null
+++ b/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/objects/factory.c b/objects/factory.c
new file mode 100644
index 0000000..bf78688
--- /dev/null
+++ b/objects/factory.c
@@ -0,0 +1,291 @@
+#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;
+
+ 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;
+
+ //FIXME: specify init funcs
+ type = module_get_uniq_id_str("DCC", "CHAT");
+ insert_map(type, chat_type, (InitFunc)pydcc_new);
+
+ type = module_get_uniq_id_str("DCC", "GET");
+ insert_map(type, chat_type, (InitFunc)pydcc_new);
+
+ type = module_get_uniq_id_str("DCC", "SEND");
+ insert_map(type, chat_type, (InitFunc)pydcc_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/objects/factory.h b/objects/factory.h
new file mode 100644
index 0000000..1171003
--- /dev/null
+++ b/objects/factory.h
@@ -0,0 +1,46 @@
+#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"
+
+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/objects/ignore-object.c b/objects/ignore-object.c
new file mode 100644
index 0000000..7c6073c
--- /dev/null
+++ b/objects/ignore-object.c
@@ -0,0 +1,275 @@
+#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}
+};
+
+PyDoc_STRVAR(PyIgnore_channels_doc,
+ "Ignore only in channels (list of names)"
+);
+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 = PyString_FromString(*p);
+
+ if (!str)
+ goto error;
+
+ ret = PyList_Append(list, str);
+ Py_DECREF(str);
+ if (ret != 0)
+ goto error;
+ }
+
+ return list;
+
+error:
+ Py_XDECREF(list);
+ return NULL;
+}
+
+/* Methods */
+PyDoc_STRVAR(PyIgnore_add_rec_doc,
+ "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 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/objects/ignore-object.h b/objects/ignore-object.h
new file mode 100644
index 0000000..39f7425
--- /dev/null
+++ b/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/objects/irc-channel-object.c b/objects/irc-channel-object.c
new file mode 100644
index 0000000..daf3329
--- /dev/null
+++ b/objects/irc-channel-object.c
@@ -0,0 +1,166 @@
+#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}
+};
+
+PyDoc_STRVAR(bans_doc,
+ "Returns a list of bans in the channel."
+);
+static PyObject *PyIrcChannel_bans(PyIrcChannel *self, PyObject *args)
+{
+ return py_irssi_objlist_new(self->data->banlist, 1, (InitFunc)pyban_new);
+}
+
+PyDoc_STRVAR(ban_get_mask_doc,
+ "Get ban mask for 'nick'."
+);
+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,
+ "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,
+ "Remove a new ban from channel."
+);
+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/objects/irc-channel-object.h b/objects/irc-channel-object.h
new file mode 100644
index 0000000..a1d845a
--- /dev/null
+++ b/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/objects/irc-connect-object.c b/objects/irc-connect-object.c
new file mode 100644
index 0000000..6e2d3a7
--- /dev/null
+++ b/objects/irc-connect-object.c
@@ -0,0 +1,99 @@
+#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"
+
+/* member IDs */
+enum
+{
+ M_IRC_CONNECT_ALTERNATE_NICK,
+};
+
+/* cleanup and deallocation handled by Connect base */
+
+static PyObject *PyIrcConnect_get(PyIrcConnect *self, void *closure)
+{
+ int member = GPOINTER_TO_INT(closure);
+
+ RET_NULL_IF_INVALID(self->data);
+
+ switch (member)
+ {
+ case M_IRC_CONNECT_ALTERNATE_NICK:
+ RET_AS_STRING_OR_NONE(self->data->alternate_nick);
+ }
+
+ /* This shouldn't be reached... but... */
+ return PyErr_Format(PyExc_RuntimeError, "invalid member id, %d", member);
+}
+
+static PyGetSetDef PyIrcConnect_getseters[] = {
+ {"alternate_nick", (getter)PyIrcConnect_get, NULL,
+ "Alternate nick to use if default nick is taken",
+ GINT_TO_POINTER(M_IRC_CONNECT_ALTERNATE_NICK)},
+
+ {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/objects/irc-connect-object.h b/objects/irc-connect-object.h
new file mode 100644
index 0000000..3f6cad4
--- /dev/null
+++ b/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/objects/irc-server-object.c b/objects/irc-server-object.c
new file mode 100644
index 0000000..0e0ccdc
--- /dev/null
+++ b/objects/irc-server-object.c
@@ -0,0 +1,265 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "server-object.h"
+#include "irc-server-object.h"
+#include "irc-connect-object.h"
+#include "pyirssi_irc.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+/* member IDs */
+enum
+{
+ M_IRC_SERVER_REAL_ADDRESS,
+ M_IRC_SERVER_USERMODE,
+ M_IRC_SERVER_USERHOST,
+};
+
+/* cleanup and dealloc inherited from base Server */
+
+static PyObject *PyIrcServer_get(PyIrcServer *self, void *closure)
+{
+ int member = GPOINTER_TO_INT(closure);
+
+ RET_NULL_IF_INVALID(self->data);
+
+ switch (member)
+ {
+ case M_IRC_SERVER_REAL_ADDRESS:
+ RET_AS_STRING_OR_NONE(self->data->real_address);
+ case M_IRC_SERVER_USERMODE:
+ RET_AS_STRING_OR_NONE(self->data->usermode);
+ case M_IRC_SERVER_USERHOST:
+ RET_AS_STRING_OR_NONE(self->data->userhost);
+ }
+
+ /* This shouldn't be reached... but... */
+ return PyErr_Format(PyExc_RuntimeError, "invalid member id, %d", member);
+}
+
+static PyGetSetDef PyIrcServer_getseters[] = {
+ {"real_address", (getter)PyIrcServer_get, NULL,
+ "Address the IRC server gives",
+ GINT_TO_POINTER(M_IRC_SERVER_REAL_ADDRESS)},
+
+ {"usermode", (getter)PyIrcServer_get, NULL,
+ "User mode in server",
+ GINT_TO_POINTER(M_IRC_SERVER_USERMODE)},
+
+ {"userhost", (getter)PyIrcServer_get, NULL,
+ "Your user host in server",
+ GINT_TO_POINTER(M_IRC_SERVER_USERHOST)},
+
+ {NULL}
+};
+
+PyDoc_STRVAR(get_channels_doc,
+ "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 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 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,
+ "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,
+ "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,
+ "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);
+}
+
+/* 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},
+
+ {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/objects/irc-server-object.h b/objects/irc-server-object.h
new file mode 100644
index 0000000..b3eb76a
--- /dev/null
+++ b/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/objects/log-object.c b/objects/log-object.c
new file mode 100644
index 0000000..fae9cd4
--- /dev/null
+++ b/objects/log-object.c
@@ -0,0 +1,465 @@
+#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,
+ "Return a list of log items"
+);
+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,
+ "Add log to list of logs / save changes to config file."
+);
+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 the log file"
+);
+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,
+ "Open log file and start logging."
+);
+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 and close the log file."
+);
+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,
+ "Remove log item from log."
+);
+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/objects/log-object.h b/objects/log-object.h
new file mode 100644
index 0000000..9893bc5
--- /dev/null
+++ b/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/objects/logitem-object.c b/objects/logitem-object.c
new file mode 100644
index 0000000..4dc2275
--- /dev/null
+++ b/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/objects/logitem-object.h b/objects/logitem-object.h
new file mode 100644
index 0000000..b7fd588
--- /dev/null
+++ b/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/objects/nick-object.c b/objects/nick-object.c
new file mode 100644
index 0000000..7ad4a65
--- /dev/null
+++ b/objects/nick-object.c
@@ -0,0 +1,216 @@
+#include <Python.h>
+#include "pymodule.h"
+#include "base-objects.h"
+#include "nick-object.h"
+#include "pyirssi.h"
+#include "pycore.h"
+#include "pyutils.h"
+
+/* member IDs */
+enum
+{
+ M_NICK_NICK,
+ M_NICK_HOST,
+ M_NICK_REALNAME,
+ M_NICK_HOPS,
+ M_NICK_GONE,
+ M_NICK_SERVEROP,
+ M_NICK_OP,
+ M_NICK_VOICE,
+ M_NICK_HALFOP,
+ M_NICK_LAST_CHECK,
+ M_NICK_SEND_MASSJOIN,
+};
+
+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);
+}
+
+static PyObject *PyNick_get(PyNick *self, void *closure)
+{
+ int member = GPOINTER_TO_INT(closure);
+
+ RET_NULL_IF_INVALID(self->data);
+
+ switch (member)
+ {
+ case M_NICK_NICK:
+ RET_AS_STRING_OR_NONE(self->data->nick);
+ case M_NICK_HOST:
+ RET_AS_STRING_OR_NONE(self->data->host);
+ case M_NICK_REALNAME:
+ RET_AS_STRING_OR_NONE(self->data->realname);
+ case M_NICK_HOPS:
+ return PyInt_FromLong(self->data->hops);
+ case M_NICK_GONE:
+ return PyBool_FromLong(self->data->gone);
+ case M_NICK_SERVEROP:
+ return PyBool_FromLong(self->data->serverop);
+ case M_NICK_OP:
+ return PyBool_FromLong(self->data->op);
+ case M_NICK_VOICE:
+ return PyBool_FromLong(self->data->voice);
+ case M_NICK_HALFOP:
+ return PyBool_FromLong(self->data->halfop);
+ case M_NICK_LAST_CHECK:
+ return PyLong_FromUnsignedLong(self->data->last_check);
+ }
+
+ INVALID_MEMBER(member);
+}
+
+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);
+}
+
+
+
+/* specialized getters/setters */
+static PyGetSetDef PyNick_getseters[] = {
+ {"nick", (getter)PyNick_get, NULL,
+ "Plain nick",
+ GINT_TO_POINTER(M_NICK_NICK)},
+
+ {"host", (getter)PyNick_get, NULL,
+ "Host address",
+ GINT_TO_POINTER(M_NICK_HOST)},
+
+ {"realname", (getter)PyNick_get, NULL,
+ "Real name",
+ GINT_TO_POINTER(M_NICK_REALNAME)},
+
+ {"hops", (getter)PyNick_get, NULL,
+ "Hop count to the server the nick is using",
+ GINT_TO_POINTER(M_NICK_HOPS)},
+
+ {"gone", (getter)PyNick_get, NULL,
+ "User status",
+ GINT_TO_POINTER(M_NICK_GONE)},
+
+ {"serverop", (getter)PyNick_get, NULL,
+ "User status",
+ GINT_TO_POINTER(M_NICK_SERVEROP)},
+
+ {"op", (getter)PyNick_get, NULL,
+ "User status",
+ GINT_TO_POINTER(M_NICK_OP)},
+
+ {"voice", (getter)PyNick_get, NULL,
+ "Channel status",
+ GINT_TO_POINTER(M_NICK_VOICE)},
+
+ {"halfop", (getter)PyNick_get, NULL,
+ "Channel status",
+ GINT_TO_POINTER(M_NICK_HALFOP)},
+
+ {"last_check", (getter)PyNick_get, NULL,
+ "timestamp when last checked gone/ircop status.",
+ GINT_TO_POINTER(M_NICK_LAST_CHECK)},
+ {"send_massjoin", (getter)PyNick_send_massjoin_get, NULL,
+ PyNick_send_massjoin_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/objects/nick-object.h b/objects/nick-object.h
new file mode 100644
index 0000000..6831994
--- /dev/null
+++ b/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/objects/pyscript-object.c b/objects/pyscript-object.c
new file mode 100644
index 0000000..a874b1b
--- /dev/null
+++ b/objects/pyscript-object.c
@@ -0,0 +1,225 @@
+#include <Python.h>
+#include <structmember.h>
+#include "pyscript-object.h"
+#include "pyirssi.h"
+#include "pysignals.h"
+#include "pymodule.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);
+
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *PyScript_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyScript *self;
+
+ self = (PyScript *)type->tp_alloc(type, 0);
+ if (!self)
+ return NULL;
+
+ self->argv = PyList_New(0);
+ if (!self->argv)
+ goto error;
+
+ self->modules = PyDict_New();
+ if (!self->modules)
+ goto error;
+
+ return (PyObject *)self;
+
+error:
+ Py_XDECREF(self->argv);
+ Py_XDECREF(self->modules);
+ Py_DECREF(self);
+
+ return NULL;
+}
+
+static PyObject *PyScript_command_bind(PyScript *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"cmd", "func", "category", NULL};
+ char *cmd;
+ PyObject *func;
+ char *category = NULL;
+ PY_COMMAND_REC *crec;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|s", kwlist, &cmd, &func, &category))
+ return NULL;
+
+ if (!PyCallable_Check(func))
+ return PyErr_Format(PyExc_TypeError, "func must be callable");
+
+ crec = g_new(PY_COMMAND_REC, 1);
+ crec->name = g_strdup(cmd);
+ crec->handler = func;
+ Py_INCREF(func);
+
+ py_command_bind(category, crec);
+
+ /* add record to internal list*/
+ self->signals = g_slist_append(self->signals, crec);
+
+ Py_RETURN_NONE;
+}
+
+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 */
+};
+
+/* Methods for object */
+static PyMethodDef PyScript_methods[] = {
+ {"command_bind", (PyCFunction)PyScript_command_bind,
+ METH_VARARGS | METH_KEYWORDS, "Bind a command"},
+ {NULL} /* Sentinel */
+};
+
+static 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 = NULL;
+
+ script = PyObject_CallFunction((PyObject*)&PyScriptType, "()");
+
+ if (script)
+ {
+ PyScript *scr = (PyScript *)script;
+
+ while (*argv)
+ {
+ 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;
+
+ for (node = self->signals; node != NULL; node = node->next)
+ {
+ PY_COMMAND_REC *crec = node->data;
+
+ py_command_unbind(crec);
+ g_free(crec->name);
+ Py_DECREF(crec->handler);
+ g_free(crec);
+ }
+
+ g_slist_free(self->signals);
+ self->signals = NULL;
+}
+
+void pyscript_clear_modules(PyObject *script)
+{
+ PyScript *self;
+
+ g_return_if_fail(pyscript_check(script));
+
+ self = (PyScript *) script;
+
+ PyDict_Clear(self->modules);
+}
+
+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/objects/pyscript-object.h b/objects/pyscript-object.h
new file mode 100644
index 0000000..260c6da
--- /dev/null
+++ b/objects/pyscript-object.h
@@ -0,0 +1,25 @@
+#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 */
+} PyScript;
+
+extern PyTypeObject PyScriptType;
+
+int pyscript_init(void);
+PyObject *pyscript_new(PyObject *module, char **argv);
+void pyscript_remove_signals(PyObject *script);
+void pyscript_clear_modules(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/objects/query-object.c b/objects/query-object.c
new file mode 100644
index 0000000..1370b34
--- /dev/null
+++ b/objects/query-object.c
@@ -0,0 +1,177 @@
+#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"
+
+/* member IDs */
+enum
+{
+ M_QUERY_ADDRESS,
+ M_QUERY_SERVER_TAG,
+ M_QUERY_UNWANTED,
+};
+
+/* 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);
+}
+
+
+static PyObject *PyQuery_get(PyQuery *self, void *closure)
+{
+ int member = GPOINTER_TO_INT(closure);
+
+ RET_NULL_IF_INVALID(self->data);
+
+ switch (member)
+ {
+ case M_QUERY_ADDRESS:
+ RET_AS_STRING_OR_NONE(self->data->address);
+ case M_QUERY_SERVER_TAG:
+ RET_AS_STRING_OR_NONE(self->data->server_tag);
+ case M_QUERY_UNWANTED:
+ return PyBool_FromLong(self->data->unwanted);
+ }
+
+ /* This shouldn't be reached... but... */
+ return PyErr_Format(PyExc_RuntimeError, "invalid member id, %d", member);
+}
+
+/* specialized getters/setters */
+static PyGetSetDef PyQuery_getseters[] = {
+ {"address", (getter)PyQuery_get, NULL,
+ "Host address of the queries nick",
+ GINT_TO_POINTER(M_QUERY_ADDRESS)},
+
+ {"server_tag", (getter)PyQuery_get, NULL,
+ "Server tag used for this nick (doesn't get erased if server gets disconnected)",
+ GINT_TO_POINTER(M_QUERY_SERVER_TAG)},
+
+ {"unwanted", (getter)PyQuery_get, NULL,
+ "1 if the other side closed or some error occured (DCC chats)",
+ GINT_TO_POINTER(M_QUERY_UNWANTED)},
+
+ {NULL}
+};
+
+PyDoc_STRVAR(change_server_doc,
+ "Change the active server for the query."
+);
+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/objects/query-object.h b/objects/query-object.h
new file mode 100644
index 0000000..844ce55
--- /dev/null
+++ b/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/objects/rawlog-object.c b/objects/rawlog-object.c
new file mode 100644
index 0000000..eed5996
--- /dev/null
+++ b/objects/rawlog-object.c
@@ -0,0 +1,306 @@
+#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,
+ "Return a list of lines for rawlog."
+);
+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)
+ goto error;
+
+ ret = PyList_Append(lines, line);
+ Py_DECREF(line);
+ if (ret != 0)
+ goto error;
+ }
+
+ return lines;
+
+error:
+ Py_XDECREF(lines);
+ return NULL;
+}
+
+PyDoc_STRVAR(PyRawlog_destroy_doc,
+ "Destroy rawlog"
+);
+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,
+ "Send str to rawlog as input text."
+);
+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,
+ "Send str to rawlog as output text."
+);
+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,
+ "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,
+ "Start logging new messages in rawlog to specified file."
+);
+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,
+ "Stop logging to file"
+);
+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 the current rawlog history to specified file."
+);
+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/objects/rawlog-object.h b/objects/rawlog-object.h
new file mode 100644
index 0000000..d2a9a2d
--- /dev/null
+++ b/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/objects/reconnect-object.c b/objects/reconnect-object.c
new file mode 100644
index 0000000..4e60599
--- /dev/null
+++ b/objects/reconnect-object.c
@@ -0,0 +1,152 @@
+#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/objects/reconnect-object.h b/objects/reconnect-object.h
new file mode 100644
index 0000000..ec0b094
--- /dev/null
+++ b/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/objects/server-object.c b/objects/server-object.c
new file mode 100644
index 0000000..23a33b0
--- /dev/null
+++ b/objects/server-object.c
@@ -0,0 +1,763 @@
+#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);
+}
+
+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}
+};
+
+PyDoc_STRVAR(print_doc,
+ "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,
+ "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 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,
+ "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,
+ "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,
+ "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,
+ "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,
+ "Find window item that matches best to given arguments"
+);
+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,
+ "Find window which contains window item with specified name/server"
+);
+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,
+ "Find window with level"
+);
+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,
+ "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,
+ "Return list of channels for server"
+);
+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,
+ "Find channel from this server"
+);
+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,
+ "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)
+ goto error;
+
+ ret = PyList_Append(pylist, tup);
+ Py_DECREF(tup);
+ if (ret != 0)
+ goto error;
+ }
+
+ return pylist;
+
+error:
+ Py_XDECREF(pylist);
+ return NULL;
+}
+
+PyDoc_STRVAR(PyServer_queries_doc,
+ "Return a list of queries for server."
+);
+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,
+ "Find a query on this server."
+);
+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,
+ "Return true if mask matches nick!user@host"
+);
+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,
+ "Return True if mask matches nick!address"
+);
+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,
+ "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,
+ "Return True if ignore matches"
+);
+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/objects/server-object.h b/objects/server-object.h
new file mode 100644
index 0000000..0703f47
--- /dev/null
+++ b/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/objects/textdest-object.c b/objects/textdest-object.c
new file mode 100644
index 0000000..9dd94d6
--- /dev/null
+++ b/objects/textdest-object.c
@@ -0,0 +1,188 @@
+#include <Python.h>
+#include "pyirssi_irc.h"
+#include "pymodule.h"
+#include "textdest-object.h"
+#include "factory.h"
+#include "pycore.h"
+
+/* XXX: no cleanup signal for textdest */
+
+static void PyTextDest_dealloc(PyTextDest *self)
+{
+ Py_XDECREF(self->window);
+ Py_XDECREF(self->server);
+
+ 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;
+}
+
+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 for object */
+static PyMethodDef PyTextDest_methods[] = {
+ {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 objects", /* 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 */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyTextDest_new, /* tp_new */
+};
+
+
+/* TextDest factory function */
+PyObject *pytextdest_new(void *td)
+{
+ PyObject *window, *server;
+ PyTextDest *pytdest;
+ TEXT_DEST_REC *tdest = td;
+
+ window = py_irssi_chat_new(tdest->window, 1);
+ if (!window)
+ return NULL;
+
+ server = py_irssi_chat_new(tdest->server, 1);
+ if (!server)
+ {
+ Py_DECREF(window);
+ return NULL;
+ }
+
+ pytdest = py_inst(PyTextDest, PyTextDestType);
+ if (!pytdest)
+ return NULL;
+
+ pytdest->data = td;
+ pytdest->window = window;
+ pytdest->server = server;
+
+ 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/objects/textdest-object.h b/objects/textdest-object.h
new file mode 100644
index 0000000..366914a
--- /dev/null
+++ b/objects/textdest-object.h
@@ -0,0 +1,23 @@
+#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;
+} 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/objects/window-item-object.c b/objects/window-item-object.c
new file mode 100644
index 0000000..96181fc
--- /dev/null
+++ b/objects/window-item-object.c
@@ -0,0 +1,307 @@
+#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}
+};
+
+PyDoc_STRVAR(PyWindowItem_prnt_doc,
+ "Print to window item"
+);
+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,
+ "Send command to window item"
+);
+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;
+}
+
+/* Methods */
+
+PyDoc_STRVAR(PyWindowItem_window_doc,
+ "Return parent window for window item"
+);
+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 for window item"
+);
+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,
+ "Returns true if window item is active"
+);
+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 window item active"
+);
+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,
+ "Unknown/untested"
+);
+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 channel or query"
+);
+static PyObject *PyWindowItem_destroy(PyWindowItem *self, PyObject *args)
+{
+ RET_NULL_IF_INVALID(self->data);
+
+ window_item_destroy(self->data);
+
+ Py_RETURN_NONE;
+}
+
+/* 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},
+ {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/objects/window-item-object.h b/objects/window-item-object.h
new file mode 100644
index 0000000..e205ebe
--- /dev/null
+++ b/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/objects/window-object.c b/objects/window-object.c
new file mode 100644
index 0000000..df74766
--- /dev/null
+++ b/objects/window-object.c
@@ -0,0 +1,646 @@
+#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,
+ "Return a list of items in window."
+);
+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,
+ "Print to window"
+);
+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,
+ "Send command to window"
+);
+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,
+ "Remove window item"
+);
+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,
+ "Destroy window item"
+);
+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,
+ "Change to previous window item"
+);
+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,
+ "Change to next window item"
+);
+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 the window."
+);
+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 window active."
+);
+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 in window"
+);
+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 window refnum"
+);
+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 window name"
+);
+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;
+}
+
+/* Methods for object */
+PyDoc_STRVAR(PyWindow_set_history_doc,
+ "Set window history"
+);
+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 window level"
+);
+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,
+ "Unknown/Untested"
+);
+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,
+ "Return active item's name, or if none is active, window's name."
+);
+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,
+ "Find window item that matches best to given arguments"
+);
+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/objects/window-object.h b/objects/window-object.h
new file mode 100644
index 0000000..b9860af
--- /dev/null
+++ b/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/pycore.c b/pycore.c
new file mode 100644
index 0000000..63cd2c6
--- /dev/null
+++ b/pycore.c
@@ -0,0 +1,152 @@
+#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 "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);
+
+ if (!pyloader_init() || !pymodule_init() || !factory_init())
+ {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Failed to load Python");
+ return;
+ }
+
+ /*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();
+ Py_Finalize();
+}
diff --git a/pycore.h b/pycore.h
new file mode 100644
index 0000000..ae51924
--- /dev/null
+++ b/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/pyirssi.h b/pyirssi.h
new file mode 100644
index 0000000..045557e
--- /dev/null
+++ b/pyirssi.h
@@ -0,0 +1,25 @@
+#ifndef _PYIRSSI_H_
+#define _PYIRSSI_H_
+
+#define MODULE_NAME "irssi_python"
+#include "config.h"
+#include "common.h"
+#include "modules.h"
+#include "commands.h"
+#include "printtext.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 "rawlog.h"
+#include "log.h"
+#include "ignore.h"
+
+#endif
diff --git a/pyirssi_irc.h b/pyirssi_irc.h
new file mode 100644
index 0000000..31b0908
--- /dev/null
+++ b/pyirssi_irc.h
@@ -0,0 +1,16 @@
+#ifndef _PY_IRSSI_IRC_H_
+#define _PY_IRSSI_IRC_H_
+
+#include "pyirssi.h"
+#include "irc.h"
+#include "irc-servers.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"
+
+#endif
diff --git a/pyloader.c b/pyloader.c
new file mode 100644
index 0000000..6c7fdfe
--- /dev/null
+++ b/pyloader.c
@@ -0,0 +1,306 @@
+#include <Python.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 != NULL && !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;
+
+ PyList_Append(script_modules, script);
+ Py_DECREF(script);
+ g_free(path);
+
+ /* PySys_WriteStdout("load %s, script -> 0x%x\n", argv[0], script); */
+
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "loaded script %s", argv[0]);
+ return 1;
+
+error:
+ if (PyErr_Occurred())
+ PyErr_Print();
+ else
+ printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "error loading script %s", argv[0]);
+
+ Py_XDECREF(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_remove_signals(script);
+ pyscript_clear_modules(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;
+}
+
+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;
+}
+
+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;
+
+ /* script specific resources (cmd + signal handlers) should be removed
+ by the object's destructor */
+ Py_DECREF(script_modules);
+}
diff --git a/pyloader.h b/pyloader.h
new file mode 100644
index 0000000..f7e19fb
--- /dev/null
+++ b/pyloader.h
@@ -0,0 +1,21 @@
+#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);
+
+GSList *pyloader_list(void);
+void pyloader_list_destroy(GSList **list);
+
+int pyloader_init(void);
+void pyloader_deinit(void);
+
+#endif
diff --git a/pymodule.c b/pymodule.c
new file mode 100644
index 0000000..c29940b
--- /dev/null
+++ b/pymodule.c
@@ -0,0 +1,817 @@
+#include <Python.h>
+#include <frameobject.h>
+#include "pymodule.h"
+#include "pyirssi_irc.h"
+#include "pyscript-object.h"
+#include "factory.h"
+#include "pyutils.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_bind, 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;
+
+static PyObject *find_script(void);
+
+/* Module functions */
+/*XXX: prefix PY to avoid ambiguity with py_command function */
+PyDoc_STRVAR(PY_command_doc,
+ "Execute command"
+);
+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,
+ "print output"
+);
+/*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 Irssi script object"
+);
+static PyObject *py_get_script(PyObject *self, PyObject *args)
+{
+ PyObject *ret = find_script();
+
+ /* 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,
+ "Find chat network with name"
+);
+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,
+ "Return a list of all chatnets"
+);
+static PyObject *py_chatnets(PyObject *self, PyObject *args)
+{
+ return py_irssi_chatlist_new(chatnets, 1);
+}
+
+PyDoc_STRVAR(py_reconnects_doc,
+ "Return a list of all reconnects"
+);
+static PyObject *py_reconnects(PyObject *self, PyObject *args)
+{
+ return py_irssi_objlist_new(reconnects, 1, (InitFunc)pyreconnect_new);
+}
+
+PyDoc_STRVAR(py_servers_doc,
+ "Return a list of all servers"
+);
+static PyObject *py_servers(PyObject *self, PyObject *args)
+{
+ return py_irssi_chatlist_new(servers, 1);
+}
+
+PyDoc_STRVAR(py_channels_doc,
+ "Return channel list"
+);
+static PyObject *py_channels(PyObject *self, PyObject *args)
+{
+ return py_irssi_chatlist_new(channels, 1);
+}
+
+PyDoc_STRVAR(py_channel_find_doc,
+ "Find channel from any server"
+);
+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,
+ "Get a list of all windows"
+);
+static PyObject *py_windows(PyObject *self, PyObject *args)
+{
+ return py_irssi_objlist_new(windows, 1, (InitFunc)pywindow_new);
+}
+
+PyDoc_STRVAR(py_active_win_doc,
+ "Return active window"
+);
+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,
+ "Return active server"
+);
+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,
+ "Find window with name"
+);
+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,
+ "Find window with reference number"
+);
+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,
+ "Return refnum for window that's previous in window list"
+);
+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,
+ "Return refnum for window that's next in window list"
+);
+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,
+ "Return refnum for last window."
+);
+static PyObject *py_windows_refnum_last(PyObject *self, PyObject *args)
+{
+ return PyInt_FromLong(windows_refnum_last());
+}
+
+PyDoc_STRVAR(py_window_find_level_doc,
+ "Find window with level."
+);
+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,
+ "Find window which contains window item with specified name."
+);
+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,
+ "Find window that matches best to given arguments. `name' can be either"
+ "window name or name of one of the window items."
+);
+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,
+ "Find window item that matches best to given arguments."
+);
+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,
+ "Find server with tag"
+);
+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,
+ "Find first server that is in chatnet"
+);
+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,
+ "Return a list of open queries."
+);
+static PyObject *py_queries(PyObject *self, PyObject *args)
+{
+ return py_irssi_chatlist_new(queries, 1);
+}
+
+PyDoc_STRVAR(py_query_find_doc,
+ "Find a query from any server."
+);
+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,
+ "Return true if mask matches nick!user@host"
+);
+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,
+ "Return True if mask matches nick!address"
+);
+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,
+ "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,
+ "Set the default rawlog size for new rawlogs."
+);
+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,
+ "Return list of logs."
+);
+static PyObject *py_logs(PyObject *self, PyObject *args)
+{
+ return py_irssi_objlist_new(logs, 1, (InitFunc)pylog_new);
+}
+
+PyDoc_STRVAR(py_log_find_doc,
+ "Find log by file name."
+);
+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,
+ "Return a list of ignore entries"
+);
+static PyObject *py_ignores(PyObject *self, PyObject *args)
+{
+ return py_irssi_objlist_new(ignores, 1, (InitFunc)pyignore_new);
+}
+
+PyDoc_STRVAR(py_ignore_check_doc,
+ "Return True if ignore matches"
+);
+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,
+ "Return list of active DCCs"
+);
+static PyObject *py_dccs(PyObject *self, PyObject *args)
+{
+ return py_irssi_list_new(dcc_conns, 1);
+}
+
+PyDoc_STRVAR(py_dcc_register_type_doc,
+ "???"
+);
+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,
+ "???"
+);
+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,
+ "???"
+);
+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,
+ "???"
+);
+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,
+ "???"
+);
+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,
+ "???"
+);
+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,
+ "???"
+);
+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,
+ "???"
+);
+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;
+}
+
+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},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+/* Traverse stack backwards to find the nearest _script object in globals */
+static PyObject *find_script(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)
+ {
+ /*
+ 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;
+}
+
+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/pymodule.h b/pymodule.h
new file mode 100644
index 0000000..242eecf
--- /dev/null
+++ b/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/pysignals.c b/pysignals.c
new file mode 100644
index 0000000..c3764fb
--- /dev/null
+++ b/pysignals.c
@@ -0,0 +1,95 @@
+#include <Python.h>
+#include "pyirssi.h"
+#include "pysignals.h"
+#include "factory.h"
+
+static void py_command_proxy(char *data, SERVER_REC *server, WI_ITEM_REC *witem);
+
+/* crec should be owned by a Script object */
+void py_command_bind(const char *category, PY_COMMAND_REC *crec)
+{
+ command_bind_full(MODULE_NAME, SIGNAL_PRIORITY_DEFAULT, crec->name,
+ -1, category, (SIGNAL_FUNC)py_command_proxy, crec);
+}
+
+void py_command_unbind(PY_COMMAND_REC *crec)
+{
+ command_unbind_full(crec->name, (SIGNAL_FUNC)py_command_proxy, crec);
+}
+
+/* This is just for testing. A complete version would use a signal map, a better
+ wrapper object factory system, and a generic signal handler like in the Perl
+ bindings */
+static void py_command_proxy(char *data, SERVER_REC *server, WI_ITEM_REC *witem)
+{
+ PY_COMMAND_REC *crec;
+ PyObject *ret, *pyserver, *pywitem;
+
+#if 0
+ if (server)
+ {
+ CHAT_PROTOCOL_REC *chat = chat_protocol_find_id(server->chat_type);
+ if (chat && !strcmp(chat->name, "IRC"))
+ pyserver = pyirc_server_new(server);
+ else
+ pyserver = pyserver_new(server);
+ g_assert(pyserver != NULL);
+ }
+ else
+ {
+ pyserver = Py_None;
+ Py_INCREF(pyserver);
+ }
+ if (witem)
+ {
+ char *type = module_find_id_str("WINDOW ITEM TYPE", witem->type);
+ g_assert(type != NULL);
+
+ if (!strcmp("CHANNEL", type))
+ pywitem = pychannel_new(witem);
+ else if (!strcmp("QUERY", type))
+ pywitem = pyquery_new(witem);
+ else
+ pywitem = pywindow_item_new(witem);
+
+ g_assert(pywitem != NULL);
+ }
+ else
+ {
+ pywitem = Py_None;
+ Py_INCREF(pywitem);
+ }
+#endif
+
+ if (server)
+ {
+ pyserver = py_irssi_chat_new(server, 1);
+ g_assert(pyserver != NULL);
+ }
+ else
+ {
+ pyserver = Py_None;
+ Py_INCREF(Py_None);
+ }
+
+ if (witem)
+ {
+ pywitem = py_irssi_chat_new(witem, 1);
+ g_assert(pywitem != NULL);
+ }
+ else
+ {
+ pywitem = Py_None;
+ Py_INCREF(Py_None);
+ }
+
+ crec = signal_get_user_data();
+ ret = PyObject_CallFunction(crec->handler, "(sOO)", data, pyserver, pywitem);
+ if (!ret)
+ PyErr_Print();
+ else
+ Py_DECREF(ret);
+
+ Py_DECREF(pyserver);
+ Py_DECREF(pywitem);
+}
diff --git a/pysignals.h b/pysignals.h
new file mode 100644
index 0000000..823472e
--- /dev/null
+++ b/pysignals.h
@@ -0,0 +1,14 @@
+#ifndef _PYSIGNALS_H_
+#define _PYSIGNALS_H_
+#include <Python.h>
+
+typedef struct
+{
+ char *name;
+ PyObject *handler;
+} PY_COMMAND_REC;
+
+void py_command_bind(const char *category, PY_COMMAND_REC *crec);
+void py_command_unbind(PY_COMMAND_REC *crec);
+
+#endif
diff --git a/pyutils.c b/pyutils.c
new file mode 100644
index 0000000..05e1f31
--- /dev/null
+++ b/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/pyutils.h b/pyutils.h
new file mode 100644
index 0000000..7b2ca7b
--- /dev/null
+++ b/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/scripts/CVS/Entries b/scripts/CVS/Entries
new file mode 100644
index 0000000..c13e17d
--- /dev/null
+++ b/scripts/CVS/Entries
@@ -0,0 +1,3 @@
+/hello.py/1.3/Thu Jun 8 03:08:39 2006//
+/dumper.py/1.8/Mon Jun 12 11:07:19 2006//
+D
diff --git a/scripts/CVS/Repository b/scripts/CVS/Repository
new file mode 100644
index 0000000..51ee3e5
--- /dev/null
+++ b/scripts/CVS/Repository
@@ -0,0 +1 @@
+irssi-python/scripts
diff --git a/scripts/CVS/Root b/scripts/CVS/Root
new file mode 100644
index 0000000..de78dd2
--- /dev/null
+++ b/scripts/CVS/Root
@@ -0,0 +1 @@
+:ext:loafier@sverige.freeshell.org/arpa/gm/l/loafier/cvsroot
diff --git a/scripts/dumper.py b/scripts/dumper.py
new file mode 100644
index 0000000..b11e24c
--- /dev/null
+++ b/scripts/dumper.py
@@ -0,0 +1,188 @@
+# type /pyload dumper
+
+import sys
+import irssi
+
+__script = None
+__last_witem = None
+__last_server = None
+
+def cmd_pydumper(data, server, witem):
+ assert isinstance(server, irssi.Server), "This should be a Server"
+ assert isinstance(witem, irssi.WindowItem), "This should be a WindowItem"
+ assert isinstance(witem, irssi.Query) or \
+ isinstance(witem, irssi.Channel), \
+ "... and be a Query or Channel"
+
+ server.channels_join("#neblooh")
+ #server.disconnect()
+ sc = server.connect
+
+ print 'witem.server', witem.server
+
+ print 'Server.Connect', sc
+ print 'connect.type', sc.type
+ print 'connect.type_id', sc.type_id
+ print 'connect.chat_type', sc.chat_type
+ print 'connect.chat_type_id', sc.chat_type_id
+ print 'connect.address', sc.address
+ print 'connect.port', sc.port
+ print 'connect.chatnet', sc.chatnet
+ print 'connect.password', sc.password
+ print 'connect.wanted_nick', sc.wanted_nick
+ print 'connect.username', sc.username
+ print 'connect.realname', sc.realname
+ if isinstance(sc, irssi.IrcConnect):
+ print 'IRC Connect items:'
+ print 'connect.alternate_nick', sc.alternate_nick
+
+ print
+ print
+ print 'Server', server
+ print 'server.type', server.type
+ print 'server.type_id', server.type_id
+ print 'server.chat_type', server.chat_type
+ print 'server.chat_type_id', server.chat_type_id
+ print 'server.connect_time', server.connect_time
+ print 'server.real_connect_time', server.real_connect_time
+ print 'server.tag', server.tag
+ print 'server.nick', server.nick
+ print 'server.connected', server.connected
+ print 'server.connection_lost', server.connection_lost
+ print 'server.rawlog', server.rawlog
+ print 'server.version', server.version
+ print 'server.last_invite', server.server_operator
+ print 'server.usermode_away', server.usermode_away
+ print 'server.away_reason', server.away_reason
+ print 'server.banned', server.banned
+ print 'server.lag', server.lag
+ if isinstance(server, irssi.IrcServer):
+ print 'IRC Server items:'
+ print 'server.real_address', server.real_address
+ print 'server.usermode', server.usermode
+ print 'server.userhost', server.userhost
+
+ print
+ print
+ print 'Witem', witem
+ print 'witem.type', witem.type
+ print 'witem.type_id', witem.type_id
+ print 'witem.chat_type', witem.chat_type
+ print 'witem.chat_type_id', witem.chat_type_id
+ print 'witem.server', witem.server
+ print 'witem.name', witem.name
+ print 'witem.createtime', witem.createtime
+ print 'witem.data_level', witem.data_level
+ print 'witem.hilight_color', witem.hilight_color
+
+ #if witem.type == "CHANNEL":
+ if isinstance(witem, irssi.Channel):
+ print 'channel items:'
+ print 'witem.topic', witem.topic
+ print 'witem.topic_by', witem.topic_by
+ print 'witem.topic_time', witem.topic_time
+ print 'witem.no_modes', witem.no_modes
+ print 'witem.mode', witem.mode
+ print 'witem.limit', witem.limit
+ print 'witem.key', witem.key
+ print 'witem.chanop', witem.chanop
+ print 'witem.names_got', witem.names_got
+ print 'witem.wholist', witem.wholist
+ print 'witem.synced', witem.synced
+ #witem.destroy()
+ print 'witem.joined', witem.joined
+ print 'witem.left', witem.left
+ print 'witem.kicked', witem.kicked
+ if isinstance(witem, irssi.IrcChannel):
+ print 'IRC channel:'
+ print 'witem.bans', witem.bans()
+ for ban in witem.bans():
+ print 'ban.ban', ban.ban
+ print 'ban.setby', ban.setby
+ print 'ban.time', ban.time
+
+ #elif witem.type == "QUERY":
+ elif isinstance(witem, irssi.Query):
+ print 'query items:'
+ print 'witem.address', witem.address
+ witem.change_server(server)
+ #witem.change_server(witem)
+ print 'witem.server_tag', witem.server_tag
+ print 'witem.unwanted', witem.unwanted
+
+ print
+ print
+ print 'is nick flag "@"?', server.isnickflag('@')
+ print 'is nick flag "+"?', server.isnickflag('+')
+ print 'is nick flag "%"?', server.isnickflag('%')
+
+ print 'is channel "#fuggerd"', server.ischannel('#fuggerd')
+ print 'is channel "&booh"', server.ischannel('&booh')
+ print 'is channel "xbooh"', server.ischannel('xbooh')
+
+ print 'nick flags', server.get_nick_flags()
+
+ print irssi.chatnets()
+ for cn in irssi.chatnets():
+ print 'cn.type', cn.type
+ print 'cn.chat_type', cn.chat_type
+ print 'cn.name', cn.name
+ print 'cn.nick', cn.nick
+ print 'cn.username', cn.username
+ print 'cn.realname', cn.realname
+ print 'cn.own_host', cn.own_host
+ print 'cn.autosendcmd', cn.autosendcmd
+ print
+
+ print irssi.chatnet_find('ircnet')
+ print irssi.servers()
+ print irssi.reconnects()
+
+ print irssi.windows()
+ for win in irssi.windows():
+ print 'win.refnum', win.refnum
+ print 'win.name', win.name
+ print 'win.width', win.width
+ print 'win.height', win.height
+ print 'win.history_name', win.history_name
+ print 'win.active', win.active
+ print 'win.active_server', win.active_server
+ print 'win.servertag', win.servertag
+ print 'win.level', win.level
+ print 'win.sticky_refnum', win.sticky_refnum
+ print 'win.data_level', win.data_level
+ print 'win.hilight_color', win.hilight_color
+ print 'win.last_timestamp', win.last_timestamp
+ print 'win.last_line', win.last_line
+ print 'win.theme_name', win.theme_name
+ print
+
+ """
+ print 'printing to channel'
+ server.send_message('#booh', 'test msg chan', 0)
+ server.send_message('#booh', 'test msg chan ER', 1)
+
+ print 'printing to nick'
+ server.send_message('melbo', 'test msg nick', 1)
+ server.send_message('melbo', 'test msg nick ER', 0)
+ """
+
+ witem.prnt('hello there')
+ global __last_witem
+ __last_witem = witem
+ global __last_server
+ __last_server = server
+
+ #new = irssi.IrssiChatBase()
+ #print 'New', new.type_id
+
+def cmd_crashme(data, server, witem):
+ __last_server.prnt('#booh', 'what up??')
+ __last_witem.prnt('imma crash mebbe?')
+
+print dir(_script)
+print _script.module
+print _script.argv
+
+irssi.command_bind('pydumper', cmd_pydumper)
+irssi.command_bind('crashme', cmd_crashme)
diff --git a/scripts/hello.py b/scripts/hello.py
new file mode 100644
index 0000000..fd5a814
--- /dev/null
+++ b/scripts/hello.py
@@ -0,0 +1,20 @@
+# type /pyload hello
+
+import irssi
+
+# data - contains the parameters for /HELLO
+# server - the active server in window
+# witem - the active window item (eg. channel, query)
+# or None if the window is empty
+def cmd_hello(data, server, witem):
+ if not server or not server.connected:
+ irssi.prnt("Not connected to server")
+
+ if data:
+ server.command("MSG %s Hello!" % data)
+ elif isinstance(witem, irssi.Channel) or isinstance(witem, irssi.Query):
+ witem.command("MSG %s Hello!" % witem.name)
+ else:
+ irssi.prnt("Nick not given, and no active channel/query in window")
+
+irssi.command_bind('hello', cmd_hello)
diff --git a/scripts/test_window.py b/scripts/test_window.py
new file mode 100644
index 0000000..69521d5
--- /dev/null
+++ b/scripts/test_window.py
@@ -0,0 +1,50 @@
+import irssi
+
+win0 = None
+win1 = None
+
+def cmd_wintest(data, server, witem):
+ act_win = irssi.active_win()
+ act_server = irssi.active_server()
+
+ print 'active_win', act_win, 'ref', act_win.refnum
+ print 'active_server', act_server
+
+ items = act_win.items()
+ print 'win.items()', items
+
+ for i in items:
+ print i, 'window ref', i.window().refnum, 'window name', i.window().name
+
+ print
+ print 'all windows'
+ for i in irssi.windows():
+ print 'window refnum', i.refnum, 'window name', i.name
+ print
+
+ f0 = irssi.window_find_name('melbo')
+ f1 = irssi.window_find_name('(status)')
+ print 'irssi.window_find_name(melbo)', f0
+ print 'irssi.window_find_name(status)', f1
+
+def cmd_opentest(data, server, witem):
+ global win0, win1
+ win0 = irssi.window_create(True)
+ print 'window_create(True) ->', win0
+ win1 = irssi.window_create(False)
+ print 'window_create(False) ->', win1
+
+def cmd_closetest(data, server, witem):
+ print 'destroy win0 && win1'
+ win0.destroy()
+ win1.destroy()
+
+def cmd_postclose(*args):
+ print 'post-close access'
+ print win0.items()
+ print win1.items()
+
+irssi.command_bind('wintest', cmd_wintest)
+irssi.command_bind('closetest', cmd_closetest)
+irssi.command_bind('postclose', cmd_postclose)
+irssi.command_bind('opentest', cmd_opentest)