diff options
author | Christopher Davis <loafier@gmail.com> | 2006-08-12 22:16:53 +0000 |
---|---|---|
committer | Christopher Davis <loafier@gmail.com> | 2006-08-12 22:16:53 +0000 |
commit | 3a028090359e5d5d24ccbfc11d9b6ff5681aab4f (patch) | |
tree | 0cfb8ec1eb8a49366fc663bef00bf4dfb1f7c307 /src/objects | |
parent | f13ea25509e932d426ebd69d90368fe9b1d4c1ab (diff) | |
download | irssi-python-3a028090359e5d5d24ccbfc11d9b6ff5681aab4f.tar.gz irssi-python-3a028090359e5d5d24ccbfc11d9b6ff5681aab4f.tar.xz irssi-python-3a028090359e5d5d24ccbfc11d9b6ff5681aab4f.zip |
directory structure change
git-svn-id: http://svn.irssi.org/repos/irssi-python@4312 dbcabf3a-b0e7-0310-adc4-f8d773084564
Diffstat (limited to 'src/objects')
69 files changed, 10311 insertions, 0 deletions
diff --git a/src/objects/Makefile b/src/objects/Makefile new file mode 100644 index 0000000..6cc56fb --- /dev/null +++ b/src/objects/Makefile @@ -0,0 +1,28 @@ +CC = gcc + +PYTHON = /usr/include/python2.4 +IRSSI = /home/chrisd/irssi-0.8.10 +CFLAGS = -fpic -ggdb -Wall -I$(PYTHON) -I$(IRSSI) -I$(IRSSI)/src \ +-I$(IRSSI)/src/fe-common/core -I$(IRSSI)/src/core -I$(IRSSI)/src/fe-text \ +-I$(IRSSI)/src/irc -I$(IRSSI)/src/irc/core -I$(IRSSI)/src/irc/dcc \ +-I$(IRSSI)/src/irc/notifylist -I.. \ +`pkg-config glib-2.0 --cflags` + +OBJ = pyscript-object.o base-objects.o window-item-object.o channel-object.o \ +query-object.o server-object.o connect-object.o irc-server-object.o \ +irc-connect-object.o irc-channel-object.o ban-object.o nick-object.o \ +chatnet-object.o reconnect-object.o window-object.o textdest-object.o \ +rawlog-object.o log-object.o logitem-object.o ignore-object.o \ +dcc-object.o dcc-chat-object.o dcc-get-object.o dcc-send-object.o \ +netsplit-object.o netsplit-server-object.o netsplit-channel-object.o \ +notifylist-object.o process-object.o command-object.o theme-object.o \ +statusbar-item-object.o main-window-object.o factory.o + +pyobjects.a: $(OBJ) + ar r pyobjects.a $(OBJ) + +%.o: %.c + $(CC) -c $< $(CFLAGS) + +clean: + rm -f *.o *.so *.a diff --git a/src/objects/ban-object.c b/src/objects/ban-object.c new file mode 100644 index 0000000..908bae2 --- /dev/null +++ b/src/objects/ban-object.c @@ -0,0 +1,155 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "ban-object.h" +#include "pycore.h" + +/* monitor "ban remove" signal */ +static void ban_cleanup(CHANNEL_REC *chan, BAN_REC *ban) +{ + PyBan *pyban = signal_get_user_data(); + + if (ban == pyban->data) + { + pyban->data = NULL; + pyban->cleanup_installed = 0; + signal_remove_data("ban remove", ban_cleanup, pyban); + } +} + +static void PyBan_dealloc(PyBan *self) +{ + if (self->cleanup_installed) + signal_remove_data("ban remove", ban_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyBan_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyBan *self; + + self = (PyBan *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +PyDoc_STRVAR(PyBan_ban_doc, + "The ban" +); +static PyObject *PyBan_ban_get(PyBan *self, void *closure) +{ + BAN_REC *data = self->data; + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(data->ban); +} + +PyDoc_STRVAR(PyBan_setby_doc, + "Nick of who set the ban" +); +static PyObject *PyBan_setby_get(PyBan *self, void *closure) +{ + BAN_REC *data = self->data; + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(data->setby); +} + +PyDoc_STRVAR(PyBan_time_doc, + "Timestamp when ban was set" +); +static PyObject *PyBan_time_get(PyBan *self, void *closure) +{ + BAN_REC *data = self->data; + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(data->time); +} + +/* specialized getters/setters */ +static PyGetSetDef PyBan_getseters[] = { + {"ban", (getter)PyBan_ban_get, NULL, + PyBan_ban_doc, NULL}, + {"setby", (getter)PyBan_setby_get, NULL, + PyBan_setby_doc, NULL}, + {"time", (getter)PyBan_time_get, NULL, + PyBan_time_doc, NULL}, + {NULL} +}; + +/* Methods for object */ +static PyMethodDef PyBan_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyBanType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Ban", /*tp_name*/ + sizeof(PyBan), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyBan_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyBan objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyBan_methods, /* tp_methods */ + 0, /* tp_members */ + PyBan_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyBan_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pyban_new(void *ban) +{ + PyBan *pyban; + + pyban = py_inst(PyBan, PyBanType); + if (!pyban) + return NULL; + + pyban->data = ban; + pyban->cleanup_installed = 1; + signal_add_last_data("ban remove", ban_cleanup, pyban); + + return (PyObject *)pyban; +} + +int ban_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyBanType) < 0) + return 0; + + Py_INCREF(&PyBanType); + PyModule_AddObject(py_module, "Ban", (PyObject *)&PyBanType); + + return 1; +} diff --git a/src/objects/ban-object.h b/src/objects/ban-object.h new file mode 100644 index 0000000..59ec9e7 --- /dev/null +++ b/src/objects/ban-object.h @@ -0,0 +1,18 @@ +#ifndef _BAN_OBJECT_H_ +#define _BAN_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) +} PyBan; + +extern PyTypeObject PyBanType; + +int ban_object_init(void); +PyObject *pyban_new(void *ban); +#define pyban_check(op) PyObject_TypeCheck(op, &PyBanType) + +#endif diff --git a/src/objects/base-objects.c b/src/objects/base-objects.c new file mode 100644 index 0000000..42f5cac --- /dev/null +++ b/src/objects/base-objects.c @@ -0,0 +1,235 @@ +#include <Python.h> +#include "structmember.h" +#include "pymodule.h" +#include "base-objects.h" +#include "pyirssi.h" + +/* This is the base type for Irssi objects with a type id. The user can find + * the type name, type id, and check if the object is wrapping a valid Irssi + * record. + */ + +static void PyIrssiBase_dealloc(PyIrssiBase *self) +{ + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyIrssiBase_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyIrssiBase *self; + + self = (PyIrssiBase *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyIrssiBase_type_id_doc, + "Irssi's type id for object" +); +static PyObject *PyIrssiBase_type_id_get(PyIrssiBase *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->type); +} + +PyDoc_STRVAR(PyIrssiBase_type_doc, + "Irssi's name for object" +); +static PyObject *PyIrssiBase_type_get(PyIrssiBase *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->base_name); +} + +PyDoc_STRVAR(PyIrssiBase_valid_doc, + "True if the object is valid" +); +static PyObject *PyIrssiBase_valid_get(PyIrssiBase *self, void *closure) +{ + if (self->data != NULL) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +/* specialized getters/setters */ +static PyGetSetDef PyIrssiBase_getseters[] = { + {"type_id", (getter)PyIrssiBase_type_id_get, NULL, + PyIrssiBase_type_id_doc, NULL}, + {"type", (getter)PyIrssiBase_type_get, NULL, + PyIrssiBase_type_doc, NULL}, + {"valid", (getter)PyIrssiBase_valid_get, NULL, + PyIrssiBase_valid_doc, NULL}, + {NULL} +}; + +/* Methods for object */ +static PyMethodDef PyIrssiBase_methods[] = { + {NULL} +}; + +PyTypeObject PyIrssiBaseType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "IrssiBase", /*tp_name*/ + sizeof(PyIrssiBase), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyIrssiBase_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyIrssiBase objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyIrssiBase_methods, /* tp_methods */ + 0, /* tp_members */ + PyIrssiBase_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyIrssiBase_new, /* tp_new */ +}; + + +/* IrssiChatBase is a base type for any object with a chat type. The user + can find the chat type string name with the chat_type member or + the type id with the chat_type_id member. It inherits from IrssiBase + so the type, valid, and type_id members are visible to the user, too */ + +static void PyIrssiChatBase_dealloc(PyIrssiChatBase *self) +{ + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyIrssiChatBase_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyIrssiChatBase *self; + + self = (PyIrssiChatBase *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyIrssiChatBase_chat_type_id_doc, + "Chat Type id (int)" +); +static PyObject *PyIrssiChatBase_chat_type_id_get(PyIrssiChatBase *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->chat_type); +} + +PyDoc_STRVAR(PyIrssiChatBase_chat_type_doc, + "Chat name (str)" +); +static PyObject *PyIrssiChatBase_chat_type_get(PyIrssiChatBase *self, void *closure) +{ + CHAT_PROTOCOL_REC *rec; + + RET_NULL_IF_INVALID(self->data); + + rec = chat_protocol_find_id(self->data->chat_type); + if (rec) + RET_AS_STRING_OR_NONE(rec->name); + else + Py_RETURN_NONE; +} + +/* specialized getters/setters */ +static PyGetSetDef PyIrssiChatBase_getseters[] = { + {"chat_type_id", (getter)PyIrssiChatBase_chat_type_id_get, NULL, + PyIrssiChatBase_chat_type_id_doc, NULL}, + {"chat_type", (getter)PyIrssiChatBase_chat_type_get, NULL, + PyIrssiChatBase_chat_type_doc, NULL}, + {NULL} +}; + +/* Methods */ +static PyMethodDef PyIrssiChatBase_methods[] = { + {NULL} +}; + +PyTypeObject PyIrssiChatBaseType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "IrssiChatBase", /*tp_name*/ + sizeof(PyIrssiChatBase), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyIrssiChatBase_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyIrssiChatBase objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyIrssiChatBase_methods, /* tp_methods */ + 0, /* tp_members */ + PyIrssiChatBase_getseters, /* tp_getset */ + &PyIrssiBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyIrssiChatBase_new, /* tp_new */ +}; + +int base_objects_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyIrssiBaseType) < 0) + return 0; + if (PyType_Ready(&PyIrssiChatBaseType) < 0) + return 0; + + Py_INCREF(&PyIrssiBaseType); + Py_INCREF(&PyIrssiChatBaseType); + PyModule_AddObject(py_module, "IrssiBase", (PyObject *)&PyIrssiBaseType); + PyModule_AddObject(py_module, "IrssiChatBase", (PyObject *)&PyIrssiChatBaseType); + + return 1; +} diff --git a/src/objects/base-objects.h b/src/objects/base-objects.h new file mode 100644 index 0000000..5351ead --- /dev/null +++ b/src/objects/base-objects.h @@ -0,0 +1,105 @@ +#ifndef _BASE_OBJECTS_H_ +#define _BASE_OBJECTS_H_ + +#include <Python.h> + +/* data is a pointer to the underlying Irssi record */ +/* base_name is the type name of the object returned from the type member + it can be SERVER, CHANNEL, QUERY, etc. Note: base_name isn't freed, so + it cannot point to heap memory */ +/* cleanup_installed: 1 = a cleanup signal handler is installed, 0 = not installed */ +#define PyIrssi_HEAD(type) \ + PyObject_HEAD \ + type *data; \ + const char *base_name; \ + int cleanup_installed; + +/* for uninheritable objects without a type id (Ban, Log, etc) */ +#define PyIrssiFinal_HEAD(type) \ + PyObject_HEAD \ + type *data; \ + int cleanup_installed; + +/* to access data from any irssi object */ +typedef struct +{ + PyObject_HEAD + void *data; +} PyIrssiObject; + +#define DATA(obj) (obj? ((PyIrssiObject *)obj)->data : NULL) + +/* base for classes with a type */ +typedef struct +{ + int type; +} IRSSI_BASE_REC; + +typedef struct +{ + PyIrssi_HEAD(IRSSI_BASE_REC) +} PyIrssiBase; + + +/* base for classes with type and a chat type */ +typedef struct +{ + int type; + int chat_type; +} IRSSI_CHAT_REC; + +typedef struct +{ + PyIrssi_HEAD(IRSSI_CHAT_REC) +} PyIrssiChatBase; + +extern PyTypeObject PyIrssiBaseType; +extern PyTypeObject PyIrssiChatBaseType; + +#define pybase_check(op) PyObject_TypeCheck(op, &PyIrssiBaseType) +#define pychatbase_check(op) PyObject_TypeCheck(op, &PyIrssiChatBaseType) +#define py_instp(tp, to) ((tp *) (to)->tp_alloc(to, 0)) +#define py_inst(tp, to) py_instp(tp, &to) + +int base_objects_init(void); + +#define RET_NULL_IF_INVALID(data) \ + if (data == NULL) \ + return PyErr_Format(PyExc_RuntimeError, "wrapped object is invalid") + +#define RET_N1_IF_INVALID(data) \ +do { \ + if (data == NULL) \ + { \ + PyErr_Format(PyExc_RuntimeError, "wrapped object is invalid"); \ + return -1; \ + } \ +} while (0) + +#define RET_AS_STRING_OR_NONE(str) \ +do { \ + if (str) \ + return PyString_FromString(str); \ + else \ + { \ + Py_INCREF(Py_None); \ + return Py_None; \ + } \ +} while (0) + + +#define RET_AS_STRING_OR_EMPTY(str) return PyString_FromString(str? str : "") + +#define RET_AS_OBJ_OR_NONE(obj) \ +do { \ + PyObject *tmp = obj; \ + if (!tmp) \ + tmp = Py_None; \ + Py_INCREF(tmp); \ + return tmp; \ +} while (0) + +#define INVALID_MEMBER(member) \ + return PyErr_Format(PyExc_RuntimeError, "invalid member id, %d", member) + +#endif diff --git a/src/objects/channel-object.c b/src/objects/channel-object.c new file mode 100644 index 0000000..fa4edd4 --- /dev/null +++ b/src/objects/channel-object.c @@ -0,0 +1,379 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "factory.h" +#include "channel-object.h" +#include "pycore.h" + +/* monitor "channel destroyed" signal */ +static void chan_cleanup(CHANNEL_REC *chan) +{ + PyChannel *pychan = signal_get_user_data(); + + if (chan == pychan->data) + { + pychan->data = NULL; + pychan->cleanup_installed = 0; + signal_remove_data("channel destroyed", chan_cleanup, pychan); + } +} + +static void PyChannel_dealloc(PyChannel *self) +{ + if (self->cleanup_installed) + signal_remove_data("channel destroyed", chan_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyChannel_topic_doc, + "Channel topic" +); +static PyObject *PyChannel_topic_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->topic); +} + +PyDoc_STRVAR(PyChannel_topic_by_doc, + "Nick who set the topic" +); +static PyObject *PyChannel_topic_by_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->topic_by); +} + +PyDoc_STRVAR(PyChannel_topic_time_doc, + "Timestamp when the topic was set" +); +static PyObject *PyChannel_topic_time_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromLong(self->data->topic_time); +} + +PyDoc_STRVAR(PyChannel_no_modes_doc, + "Channel is modeless" +); +static PyObject *PyChannel_no_modes_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->no_modes); +} + +PyDoc_STRVAR(PyChannel_mode_doc, + "Channel mode" +); +static PyObject *PyChannel_mode_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->mode); +} + +PyDoc_STRVAR(PyChannel_limit_doc, + "Max. users in channel (+l mode)" +); +static PyObject *PyChannel_limit_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->limit); +} + +PyDoc_STRVAR(PyChannel_key_doc, + "Channel key (password)" +); +static PyObject *PyChannel_key_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->key); +} + +PyDoc_STRVAR(PyChannel_chanop_doc, + "You are channel operator" +); +static PyObject *PyChannel_chanop_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->chanop); +} + +PyDoc_STRVAR(PyChannel_names_got_doc, + "/NAMES list has been received" +); +static PyObject *PyChannel_names_got_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->names_got); +} + +PyDoc_STRVAR(PyChannel_wholist_doc, + "/WHO list has been received" +); +static PyObject *PyChannel_wholist_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->wholist); +} + +PyDoc_STRVAR(PyChannel_synced_doc, + "Channel is fully synchronized" +); +static PyObject *PyChannel_synced_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->synced); +} + +PyDoc_STRVAR(PyChannel_joined_doc, + "JOIN event for this channel has been received" +); +static PyObject *PyChannel_joined_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->joined); +} + +PyDoc_STRVAR(PyChannel_left_doc, + "You just left the channel (for 'channel destroyed' event)" +); +static PyObject *PyChannel_left_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->left); +} + +PyDoc_STRVAR(PyChannel_kicked_doc, + "You were just kicked out of the channel (for 'channel destroyed' event)" +); +static PyObject *PyChannel_kicked_get(PyChannel *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->kicked); +} + +/* specialized getters/setters */ +static PyGetSetDef PyChannel_getseters[] = { + {"topic", (getter)PyChannel_topic_get, NULL, + PyChannel_topic_doc, NULL}, + {"topic_by", (getter)PyChannel_topic_by_get, NULL, + PyChannel_topic_by_doc, NULL}, + {"topic_time", (getter)PyChannel_topic_time_get, NULL, + PyChannel_topic_time_doc, NULL}, + {"no_modes", (getter)PyChannel_no_modes_get, NULL, + PyChannel_no_modes_doc, NULL}, + {"mode", (getter)PyChannel_mode_get, NULL, + PyChannel_mode_doc, NULL}, + {"limit", (getter)PyChannel_limit_get, NULL, + PyChannel_limit_doc, NULL}, + {"key", (getter)PyChannel_key_get, NULL, + PyChannel_key_doc, NULL}, + {"chanop", (getter)PyChannel_chanop_get, NULL, + PyChannel_chanop_doc, NULL}, + {"names_got", (getter)PyChannel_names_got_get, NULL, + PyChannel_names_got_doc, NULL}, + {"wholist", (getter)PyChannel_wholist_get, NULL, + PyChannel_wholist_doc, NULL}, + {"synced", (getter)PyChannel_synced_get, NULL, + PyChannel_synced_doc, NULL}, + {"joined", (getter)PyChannel_joined_get, NULL, + PyChannel_joined_doc, NULL}, + {"left", (getter)PyChannel_left_get, NULL, + PyChannel_left_doc, NULL}, + {"kicked", (getter)PyChannel_kicked_get, NULL, + PyChannel_kicked_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyChannel_nicks_doc, + "nicks() -> list of Nick objects\n" + "\n" + "Return a list of nicks in the channel.\n" +); +static PyObject *PyChannel_nicks(PyChannel *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + return py_irssi_chatlist_new(nicklist_getnicks(self->data), 1); +} + +PyDoc_STRVAR(PyChannel_nicks_find_mask_doc, + "nicks_find_mask(mask) -> Nick object or None\n" + "\n" + "Find nick mask from nicklist, wildcards allowed.\n" +); +static PyObject *PyChannel_nicks_find_mask(PyChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"mask", NULL}; + char *mask = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &mask)) + return NULL; + + return py_irssi_chat_new(nicklist_find_mask(self->data, mask), 1); +} + +PyDoc_STRVAR(PyChannel_nick_find_doc, + "nick_find(nick) -> Nick object or None\n" + "\n" + "Find nick from nicklist.\n" +); +static PyObject *PyChannel_nick_find(PyChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + char *nick = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &nick)) + return NULL; + + return py_irssi_chat_new(nicklist_find(self->data, nick), 1); +} + +PyDoc_STRVAR(PyChannel_nick_remove_doc, + "nick_remove(nick) -> None\n" + "\n" + "Remove nick from nicklist.\n" +); +static PyObject *PyChannel_nick_remove(PyChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + PyObject *nick = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &nick)) + return NULL; + + if (!pynick_check(nick)) + return PyErr_Format(PyExc_TypeError, "arg must be nick"); + + nicklist_remove(self->data, ((PyNick*)nick)->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyChannel_nick_insert_obj_doc, + "nick_insert(nick) -> None\n" + "\n" + "Insert nick object into nicklist.\n" +); +static PyObject *PyChannel_nick_insert_obj(PyChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + PyObject *nick = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &nick)) + return NULL; + + if (!pynick_check(nick)) + return PyErr_Format(PyExc_TypeError, "arg must be nick"); + + nicklist_insert(self->data, ((PyNick*)nick)->data); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyChannel_methods[] = { + {"nicks", (PyCFunction)PyChannel_nicks, METH_NOARGS, + PyChannel_nicks_doc}, + {"nicks_find_mask", (PyCFunction)PyChannel_nicks_find_mask, METH_VARARGS | METH_KEYWORDS, + PyChannel_nicks_find_mask_doc}, + {"nick_find", (PyCFunction)PyChannel_nick_find, METH_VARARGS | METH_KEYWORDS, + PyChannel_nick_find_doc}, + {"nick_remove", (PyCFunction)PyChannel_nick_remove, METH_VARARGS | METH_KEYWORDS, + PyChannel_nick_remove_doc}, + {"nick_insert_obj", (PyCFunction)PyChannel_nick_insert_obj, METH_VARARGS | METH_KEYWORDS, + PyChannel_nick_insert_obj_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyChannelType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Channel", /*tp_name*/ + sizeof(PyChannel), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyChannel_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyChannel objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyChannel_methods, /* tp_methods */ + 0, /* tp_members */ + PyChannel_getseters, /* tp_getset */ + &PyWindowItemType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pychannel_sub_new(void *chan, const char *name, PyTypeObject *type) +{ + PyObject *pychan; + + pychan = pywindow_item_sub_new(chan, name, type); + if (pychan) + { + PyChannel *pych = (PyChannel *)pychan; + signal_add_last_data("channel destroyed", chan_cleanup, pych); + pych->cleanup_installed = 1; + } + + return pychan; +} + +PyObject *pychannel_new(void *chan) +{ + static const char *name = "CHANNEL"; + return pychannel_sub_new(chan, name, &PyChannelType); +} + +int channel_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyChannelType) < 0) + return 0; + + Py_INCREF(&PyChannelType); + PyModule_AddObject(py_module, "Channel", (PyObject *)&PyChannelType); + + return 1; +} diff --git a/src/objects/channel-object.h b/src/objects/channel-object.h new file mode 100644 index 0000000..0858e7b --- /dev/null +++ b/src/objects/channel-object.h @@ -0,0 +1,22 @@ +#ifndef _CHANNEL_OBJECT_H_ +#define _CHANNEL_OBJECT_H_ + +#include <Python.h> +#include "window-item-object.h" + +/* forward */ +struct _CHANNEL_REC; + +typedef struct +{ + PyWindowItem_HEAD(struct _CHANNEL_REC) +} PyChannel; + +extern PyTypeObject PyChannelType; + +int channel_object_init(void); +PyObject *pychannel_sub_new(void *chan, const char *name, PyTypeObject *type); +PyObject *pychannel_new(void *chan); +#define pychannel_check(op) PyObject_TypeCheck(op, &PyChannelType) + +#endif diff --git a/src/objects/chatnet-object.c b/src/objects/chatnet-object.c new file mode 100644 index 0000000..c22f0cb --- /dev/null +++ b/src/objects/chatnet-object.c @@ -0,0 +1,182 @@ +#include <Python.h> +#include "pymodule.h" +#include "base-objects.h" +#include "chatnet-object.h" +#include "pyirssi.h" +#include "pycore.h" +#include "pyutils.h" + +static void chatnet_cleanup(CHATNET_REC *cn) +{ + PyChatnet *pycn = signal_get_user_data(); + + if (cn == pycn->data) + { + pycn->data = NULL; + pycn->cleanup_installed = 0; + signal_remove_data("chatnet destroyed", chatnet_cleanup, pycn); + } +} + +static void PyChatnet_dealloc(PyChatnet *self) +{ + if (self->cleanup_installed) + signal_remove_data("chatnet destroyed", chatnet_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyChatnet_name_doc, + "name of chat network" +); +static PyObject *PyChatnet_name_get(PyChatnet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->name); +} + +PyDoc_STRVAR(PyChatnet_nick_doc, + "if not empty, nick preferred in this network" +); +static PyObject *PyChatnet_nick_get(PyChatnet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->nick); +} + +PyDoc_STRVAR(PyChatnet_username_doc, + "if not empty, username preferred in this network" +); +static PyObject *PyChatnet_username_get(PyChatnet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->username); +} + +PyDoc_STRVAR(PyChatnet_realname_doc, + "if not empty, realname preferred in this network" +); +static PyObject *PyChatnet_realname_get(PyChatnet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->realname); +} + +PyDoc_STRVAR(PyChatnet_own_host_doc, + "address to use when connecting to this network" +); +static PyObject *PyChatnet_own_host_get(PyChatnet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->own_host); +} + +PyDoc_STRVAR(PyChatnet_autosendcmd_doc, + "command to send after connecting to this network" +); +static PyObject *PyChatnet_autosendcmd_get(PyChatnet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->autosendcmd); +} + +/* specialized getters/setters */ +static PyGetSetDef PyChatnet_getseters[] = { + {"name", (getter)PyChatnet_name_get, NULL, + PyChatnet_name_doc, NULL}, + {"nick", (getter)PyChatnet_nick_get, NULL, + PyChatnet_nick_doc, NULL}, + {"username", (getter)PyChatnet_username_get, NULL, + PyChatnet_username_doc, NULL}, + {"realname", (getter)PyChatnet_realname_get, NULL, + PyChatnet_realname_doc, NULL}, + {"own_host", (getter)PyChatnet_own_host_get, NULL, + PyChatnet_own_host_doc, NULL}, + {"autosendcmd", (getter)PyChatnet_autosendcmd_get, NULL, + PyChatnet_autosendcmd_doc, NULL}, + {NULL} +}; + +static PyMethodDef PyChatnet_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyChatnetType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Chatnet", /*tp_name*/ + sizeof(PyChatnet), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyChatnet_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyChatnet objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyChatnet_methods, /* tp_methods */ + 0, /* tp_members */ + PyChatnet_getseters, /* tp_getset */ + &PyIrssiChatBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* chatnet factory function */ +PyObject *pychatnet_sub_new(void *cn, PyTypeObject *subclass) +{ + static const char *name = "CHATNET"; + PyChatnet *pycn = NULL; + + pycn = py_instp(PyChatnet, subclass); + if (!pycn) + return NULL; + + pycn->data = cn; + pycn->base_name = name; + signal_add_last_data("chatnet destroyed", chatnet_cleanup, pycn); + pycn->cleanup_installed = 1; + + return (PyObject *)pycn; +} + +PyObject *pychatnet_new(void *cn) +{ + return pychatnet_sub_new(cn, &PyChatnetType); +} + +int chatnet_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyChatnetType) < 0) + return 0; + + Py_INCREF(&PyChatnetType); + PyModule_AddObject(py_module, "Chatnet", (PyObject *)&PyChatnetType); + + return 1; +} diff --git a/src/objects/chatnet-object.h b/src/objects/chatnet-object.h new file mode 100644 index 0000000..8a6d1fc --- /dev/null +++ b/src/objects/chatnet-object.h @@ -0,0 +1,22 @@ +#ifndef _CHATNET_OBJECT_H_ +#define _CHATNET_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _CHATNET_REC; + +typedef struct +{ + PyIrssi_HEAD(struct _CHATNET_REC) +} PyChatnet; + +extern PyTypeObject PyChatnetType; + +int chatnet_object_init(void); +PyObject *pychatnet_sub_new(void *cn, PyTypeObject *subclass); +PyObject *pychatnet_new(void *cn); +#define pychatnet_check(op) PyObject_TypeCheck(op, &PyChatnetType) + +#endif diff --git a/src/objects/command-object.c b/src/objects/command-object.c new file mode 100644 index 0000000..ad60012 --- /dev/null +++ b/src/objects/command-object.c @@ -0,0 +1,145 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "command-object.h" +#include "pycore.h" + +#define COMMAND(cmd) ((COMMAND_REC *)cmd) + +/* monitor "commandlist remove" signal */ +static void command_cleanup(COMMAND_REC *command) +{ + PyCommand *pycommand = signal_get_user_data(); + + if (command == pycommand->data) + { + pycommand->data = NULL; + pycommand->cleanup_installed = 0; + signal_remove_data("commandlist remove", command_cleanup, pycommand); + } +} + +static void PyCommand_dealloc(PyCommand *self) +{ + if (self->cleanup_installed) + signal_remove_data("commandlist remove", command_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyCommand_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyCommand *self; + + self = (PyCommand *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyCommand_cmd_doc, + "Command name" +); +static PyObject *PyCommand_cmd_get(PyCommand *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(COMMAND(self->data)->cmd); +} + +PyDoc_STRVAR(PyCommand_category_doc, + "Category" +); +static PyObject *PyCommand_category_get(PyCommand *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(COMMAND(self->data)->category); +} + +/* specialized getters/setters */ +static PyGetSetDef PyCommand_getseters[] = { + {"cmd", (getter)PyCommand_cmd_get, NULL, + PyCommand_cmd_doc, NULL}, + {"category", (getter)PyCommand_category_get, NULL, + PyCommand_category_doc, NULL}, + {NULL} +}; + +/* Methods */ +/* Methods for object */ +static PyMethodDef PyCommand_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyCommandType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Command", /*tp_name*/ + sizeof(PyCommand), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyCommand_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyCommand objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyCommand_methods, /* tp_methods */ + 0, /* tp_members */ + PyCommand_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyCommand_new, /* tp_new */ +}; + + +/* command factory function */ +PyObject *pycommand_new(void *command) +{ + PyCommand *pycommand; + + pycommand = py_inst(PyCommand, PyCommandType); + if (!pycommand) + return NULL; + + pycommand->data = command; + pycommand->cleanup_installed = 1; + signal_add_last_data("commandlist remove", command_cleanup, pycommand); + + return (PyObject *)pycommand; +} + +int command_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyCommandType) < 0) + return 0; + + Py_INCREF(&PyCommandType); + PyModule_AddObject(py_module, "Command", (PyObject *)&PyCommandType); + + return 1; +} diff --git a/src/objects/command-object.h b/src/objects/command-object.h new file mode 100644 index 0000000..e6402cf --- /dev/null +++ b/src/objects/command-object.h @@ -0,0 +1,18 @@ +#ifndef _COMMAND_OBJECT_H_ +#define _COMMAND_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) +} PyCommand; + +extern PyTypeObject PyCommandType; + +int command_object_init(void); +PyObject *pycommand_new(void *command); +#define pycommand_check(op) PyObject_TypeCheck(op, &PyCommandType) + +#endif diff --git a/src/objects/connect-object.c b/src/objects/connect-object.c new file mode 100644 index 0000000..8ef9a29 --- /dev/null +++ b/src/objects/connect-object.c @@ -0,0 +1,198 @@ +#include <Python.h> +#include "pymodule.h" +#include "base-objects.h" +#include "connect-object.h" +#include "pyirssi.h" +#include "pycore.h" +#include "pyutils.h" + +static void connect_cleanup(SERVER_CONNECT_REC *connect) +{ + PyConnect *pyconn = signal_get_user_data(); + + /* + if (server == pyconn->data) + { + pyserver->data = NULL; + pyserver->cleanup_installed = 0; + signal_remove_data("server disconnected", connect_cleanup, pyserver); + } + */ +} + +static void PyConnect_dealloc(PyConnect *self) +{ + if (self->cleanup_installed) + signal_remove_data("server disconnected", connect_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyConnect_address_doc, + "Address where we connected (irc.blah.org)" +); +static PyObject *PyConnect_address_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->address); +} + +PyDoc_STRVAR(PyConnect_port_doc, + "Port where we're connected" +); +static PyObject *PyConnect_port_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->port); +} + +PyDoc_STRVAR(PyConnect_chatnet_doc, + "Chat network" +); +static PyObject *PyConnect_chatnet_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->chatnet); +} + +PyDoc_STRVAR(PyConnect_password_doc, + "Password we used in connection." +); +static PyObject *PyConnect_password_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->password); +} + +PyDoc_STRVAR(PyConnect_wanted_nick_doc, + "Nick which we would prefer to use" +); +static PyObject *PyConnect_wanted_nick_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->nick); +} + +PyDoc_STRVAR(PyConnect_username_doc, + "User name" +); +static PyObject *PyConnect_username_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->username); +} + +PyDoc_STRVAR(PyConnect_realname_doc, + "Real name" +); +static PyObject *PyConnect_realname_get(PyConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->realname); +} + +/* Get/Set */ +static PyGetSetDef PyConnect_getseters[] = { + {"address", (getter)PyConnect_address_get, NULL, + PyConnect_address_doc, NULL}, + {"port", (getter)PyConnect_port_get, NULL, + PyConnect_port_doc, NULL}, + {"chatnet", (getter)PyConnect_chatnet_get, NULL, + PyConnect_chatnet_doc, NULL}, + {"password", (getter)PyConnect_password_get, NULL, + PyConnect_password_doc, NULL}, + {"wanted_nick", (getter)PyConnect_wanted_nick_get, NULL, + PyConnect_wanted_nick_doc, NULL}, + {"username", (getter)PyConnect_username_get, NULL, + PyConnect_username_doc, NULL}, + {"realname", (getter)PyConnect_realname_get, NULL, + PyConnect_realname_doc, NULL}, + {NULL} +}; + +PyTypeObject PyConnectType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Connect", /*tp_name*/ + sizeof(PyConnect), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyConnect_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyConnect objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + PyConnect_getseters, /* tp_getset */ + &PyIrssiChatBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* server connect factory function (managed == 0, don't do signal cleanup, 1 == do sig cleanup */ +PyObject *pyconnect_sub_new(void *connect, PyTypeObject *subclass, int managed) +{ + static const char *CONNECT_TYPE = "SERVER CONNECT"; + PyConnect *pyconn = NULL; + + g_return_val_if_fail(connect != NULL, NULL); + + pyconn = py_instp(PyConnect, subclass); + if (!pyconn) + return NULL; + + pyconn->base_name = CONNECT_TYPE; + pyconn->data = connect; + + if (managed) + { + //XXX: how to handle cleanup? + //signal_add_last_data("server disconnected", connect_cleanup, pyconn); + //pyconn->cleanup_installed = 1; + } + + return (PyObject *)pyconn; +} + +PyObject *pyconnect_new(void *connect, int managed) +{ + return pyconnect_sub_new(connect, &PyConnectType, managed); +} + +int connect_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyConnectType) < 0) + return 0; + + Py_INCREF(&PyConnectType); + PyModule_AddObject(py_module, "Connect", (PyObject *)&PyConnectType); + + return 1; +} diff --git a/src/objects/connect-object.h b/src/objects/connect-object.h new file mode 100644 index 0000000..53bd509 --- /dev/null +++ b/src/objects/connect-object.h @@ -0,0 +1,22 @@ +#ifndef _CONNECT_OBJECT_H_ +#define _CONNECT_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _SERVER_CONNECT_REC; + +typedef struct +{ + PyIrssi_HEAD(struct _SERVER_CONNECT_REC) +} PyConnect; + +extern PyTypeObject PyConnectType; + +int connect_object_init(void); +PyObject *pyconnect_sub_new(void *connect, PyTypeObject *subtype, int managed); +PyObject *pyconnect_new(void *connect, int managed); +#define pyconnect_check(op) PyObject_TypeCheck(op, &PyConnectType) + +#endif diff --git a/src/objects/dcc-chat-object.c b/src/objects/dcc-chat-object.c new file mode 100644 index 0000000..5cfe796 --- /dev/null +++ b/src/objects/dcc-chat-object.c @@ -0,0 +1,137 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "dcc-chat-object.h" +#include "factory.h" +#include "pycore.h" + +/* inherit destroy and cleanup from DccChat type */ + +/* Getters */ +PyDoc_STRVAR(PyDccChat_id_doc, + "Unique identifier - usually same as nick" +); +static PyObject *PyDccChat_id_get(PyDccChat *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->id); +} + +PyDoc_STRVAR(PyDccChat_mirc_ctcp_doc, + "Send CTCPs without the CTCP_MESSAGE prefix" +); +static PyObject *PyDccChat_mirc_ctcp_get(PyDccChat *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->mirc_ctcp); +} + +PyDoc_STRVAR(PyDccChat_connection_lost_doc, + "Other side closed connection" +); +static PyObject *PyDccChat_connection_lost_get(PyDccChat *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->connection_lost); +} + +/* specialized getters/setters */ +static PyGetSetDef PyDccChat_getseters[] = { + {"id", (getter)PyDccChat_id_get, NULL, + PyDccChat_id_doc, NULL}, + {"mirc_ctcp", (getter)PyDccChat_mirc_ctcp_get, NULL, + PyDccChat_mirc_ctcp_doc, NULL}, + {"connection_lost", (getter)PyDccChat_connection_lost_get, NULL, + PyDccChat_connection_lost_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyDccChat_chat_send_doc, + "chat_send(data) -> None\n" + "\n" + "Send data to a dcc chat session.\n" +); +static PyObject *PyDccChat_chat_send(PyDccChat *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"data", NULL}; + char *data = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &data)) + return NULL; + + dcc_chat_send(self->data, data); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyDccChat_methods[] = { + {"chat_send", (PyCFunction)PyDccChat_chat_send, METH_VARARGS | METH_KEYWORDS, + PyDccChat_chat_send_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyDccChatType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "DccChat", /*tp_name*/ + sizeof(PyDccChat), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyDccChat objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyDccChat_methods, /* tp_methods */ + 0, /* tp_members */ + PyDccChat_getseters, /* tp_getset */ + &PyDccType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyObject *pydcc_chat_new(void *dcc) +{ + static const char *name = "DCC CHAT"; + return pydcc_sub_new(dcc, name, &PyDccChatType); +} + +int dcc_chat_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyDccChatType) < 0) + return 0; + + Py_INCREF(&PyDccChatType); + PyModule_AddObject(py_module, "DccChat", (PyObject *)&PyDccChatType); + + return 1; +} diff --git a/src/objects/dcc-chat-object.h b/src/objects/dcc-chat-object.h new file mode 100644 index 0000000..955e6be --- /dev/null +++ b/src/objects/dcc-chat-object.h @@ -0,0 +1,21 @@ +#ifndef _DCC_CHAT_OBJECT_H_ +#define _DCC_CHAT_OBJECT_H_ + +#include <Python.h> +#include "dcc-object.h" + +/* forward */ +struct CHAT_DCC_REC; + +typedef struct +{ + PyDcc_HEAD(struct CHAT_DCC_REC) +} PyDccChat; + +extern PyTypeObject PyDccChatType; + +PyObject *pydcc_chat_new(void *dcc); +#define pydcc_chat_check(op) PyObject_TypeCheck(op, &PyDccChatType) +int dcc_chat_object_init(void); + +#endif diff --git a/src/objects/dcc-get-object.c b/src/objects/dcc-get-object.c new file mode 100644 index 0000000..fa7c13a --- /dev/null +++ b/src/objects/dcc-get-object.c @@ -0,0 +1,138 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "dcc-get-object.h" +#include "factory.h" +#include "pycore.h" + +#define DCC_GET_CAST(rec) ((GET_DCC_REC *)rec) + +/* inherit destroy and cleanup from Dcc type */ + +/* Getters */ +PyDoc_STRVAR(PyDccGet_size_doc, + "File size" +); +static PyObject *PyDccGet_size_get(PyDccGet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC_GET_CAST(self->data)->size); +} + +PyDoc_STRVAR(PyDccGet_skipped_doc, + "Bytes skipped from start (resuming file)" +); +static PyObject *PyDccGet_skipped_get(PyDccGet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC_GET_CAST(self->data)->skipped); +} + +PyDoc_STRVAR(PyDccGet_get_type_doc, + "What to do if file exists? 0=default, 1=rename, 2=overwrite, 3=resume" +); +static PyObject *PyDccGet_get_type_get(PyDccGet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(DCC_GET_CAST(self->data)->get_type); +} + +PyDoc_STRVAR(PyDccGet_file_doc, + "The real file name which we use." +); +static PyObject *PyDccGet_file_get(PyDccGet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC_GET_CAST(self->data)->file); +} + +PyDoc_STRVAR(PyDccGet_file_quoted_doc, + "true if file name was received quoted (\"file name\")" +); +static PyObject *PyDccGet_file_quoted_get(PyDccGet *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(DCC_GET_CAST(self->data)->file_quoted); +} + +/* specialized getters/setters */ +static PyGetSetDef PyDccGet_getseters[] = { + {"size", (getter)PyDccGet_size_get, NULL, + PyDccGet_size_doc, NULL}, + {"skipped", (getter)PyDccGet_skipped_get, NULL, + PyDccGet_skipped_doc, NULL}, + {"get_type", (getter)PyDccGet_get_type_get, NULL, + PyDccGet_get_type_doc, NULL}, + {"file", (getter)PyDccGet_file_get, NULL, + PyDccGet_file_doc, NULL}, + {"file_quoted", (getter)PyDccGet_file_quoted_get, NULL, + PyDccGet_file_quoted_doc, NULL}, + {NULL} +}; + +/* Methods */ +/* Methods for object */ +static PyMethodDef PyDccGet_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyDccGetType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "DccGet", /*tp_name*/ + sizeof(PyDccGet), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyDccGet objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyDccGet_methods, /* tp_methods */ + 0, /* tp_members */ + PyDccGet_getseters, /* tp_getset */ + &PyDccType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyObject *pydcc_get_new(void *dcc) +{ + static const char *name = "DCC GET"; + return pydcc_sub_new(dcc, name, &PyDccGetType); +} + +int dcc_get_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyDccGetType) < 0) + return 0; + + Py_INCREF(&PyDccGetType); + PyModule_AddObject(py_module, "DccGet", (PyObject *)&PyDccGetType); + + return 1; +} diff --git a/src/objects/dcc-get-object.h b/src/objects/dcc-get-object.h new file mode 100644 index 0000000..532b854 --- /dev/null +++ b/src/objects/dcc-get-object.h @@ -0,0 +1,18 @@ +#ifndef _DCC_GET_OBJECT_H_ +#define _DCC_GET_OBJECT_H_ + +#include <Python.h> +#include "dcc-object.h" + +typedef struct +{ + PyDcc_HEAD(void) +} PyDccGet; + +extern PyTypeObject PyDccGetType; + +PyObject *pydcc_get_new(void *dcc); +#define pydcc_get_check(op) PyObject_TypeCheck(op, &PyDccGetType) +int dcc_get_object_init(void); + +#endif diff --git a/src/objects/dcc-object.c b/src/objects/dcc-object.c new file mode 100644 index 0000000..95a3a4a --- /dev/null +++ b/src/objects/dcc-object.c @@ -0,0 +1,347 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "dcc-object.h" +#include "factory.h" +#include "pycore.h" + + +/* monitor "dcc destroyed signal" */ +static void dcc_cleanup(DCC_REC *dcc) +{ + PyDcc *pydcc = signal_get_user_data(); + + if (dcc == pydcc->data) + { + pydcc->data = NULL; + pydcc->cleanup_installed = 0; + signal_remove_data("dcc destroyed", dcc_cleanup, pydcc); + } +} + +static void PyDcc_dealloc(PyDcc *self) +{ + if (self->cleanup_installed) + signal_remove_data("dcc destroyed", dcc_cleanup, self); + + Py_XDECREF(self->server); + Py_XDECREF(self->chat); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyDcc_orig_type_doc, + "Original DCC type that was sent to us - same as type except GET and SEND are swapped" +); +static PyObject *PyDcc_orig_type_get(PyDcc *self, void *closure) +{ + const char *type; + + RET_NULL_IF_INVALID(self->data); + + type = module_find_id_str("DCC", DCC(self->data)->orig_type); + RET_AS_STRING_OR_NONE(type); +} + +PyDoc_STRVAR(PyDcc_created_doc, + "Time stamp when the DCC record was created" +); +static PyObject *PyDcc_created_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC(self->data)->created); +} + +PyDoc_STRVAR(PyDcc_server_doc, + "Server record where the DCC was initiated." +); +static PyObject *PyDcc_server_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->server); +} + +PyDoc_STRVAR(PyDcc_servertag_doc, + "Tag of the server where the DCC was initiated." +); +static PyObject *PyDcc_servertag_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC(self->data)->servertag); +} + +PyDoc_STRVAR(PyDcc_mynick_doc, + "Our nick to use in DCC chat." +); +static PyObject *PyDcc_mynick_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC(self->data)->mynick); +} + +PyDoc_STRVAR(PyDcc_nick_doc, + "Other side's nick name." +); +static PyObject *PyDcc_nick_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC(self->data)->nick); +} + +PyDoc_STRVAR(PyDcc_chat_doc, + "Dcc chat record if the request came through DCC chat" +); +static PyObject *PyDcc_chat_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->chat); +} + +PyDoc_STRVAR(PyDcc_target_doc, + "Who the request was sent to - your nick, channel or empty if you sent the request" +); +static PyObject *PyDcc_target_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC(self->data)->target); +} + +PyDoc_STRVAR(PyDcc_arg_doc, + "Given argument .. file name usually" +); +static PyObject *PyDcc_arg_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC(self->data)->arg); +} + +PyDoc_STRVAR(PyDcc_addr_doc, + "Other side's IP address." +); +static PyObject *PyDcc_addr_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(DCC(self->data)->addrstr); +} + +PyDoc_STRVAR(PyDcc_port_doc, + "Port we're connecting in." +); +static PyObject *PyDcc_port_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(DCC(self->data)->port); +} + +PyDoc_STRVAR(PyDcc_starttime_doc, + "Unix time stamp when the DCC transfer was started" +); +static PyObject *PyDcc_starttime_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC(self->data)->starttime); +} + +PyDoc_STRVAR(PyDcc_transfd_doc, + "Bytes transferred" +); +static PyObject *PyDcc_transfd_get(PyDcc *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC(self->data)->transfd); +} + +/* specialized getters/setters */ +static PyGetSetDef PyDcc_getseters[] = { + {"orig_type", (getter)PyDcc_orig_type_get, NULL, + PyDcc_orig_type_doc, NULL}, + {"created", (getter)PyDcc_created_get, NULL, + PyDcc_created_doc, NULL}, + {"server", (getter)PyDcc_server_get, NULL, + PyDcc_server_doc, NULL}, + {"servertag", (getter)PyDcc_servertag_get, NULL, + PyDcc_servertag_doc, NULL}, + {"mynick", (getter)PyDcc_mynick_get, NULL, + PyDcc_mynick_doc, NULL}, + {"nick", (getter)PyDcc_nick_get, NULL, + PyDcc_nick_doc, NULL}, + {"chat", (getter)PyDcc_chat_get, NULL, + PyDcc_chat_doc, NULL}, + {"target", (getter)PyDcc_target_get, NULL, + PyDcc_target_doc, NULL}, + {"arg", (getter)PyDcc_arg_get, NULL, + PyDcc_arg_doc, NULL}, + {"addr", (getter)PyDcc_addr_get, NULL, + PyDcc_addr_doc, NULL}, + {"port", (getter)PyDcc_port_get, NULL, + PyDcc_port_doc, NULL}, + {"starttime", (getter)PyDcc_starttime_get, NULL, + PyDcc_starttime_doc, NULL}, + {"transfd", (getter)PyDcc_transfd_get, NULL, + PyDcc_transfd_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyDcc_destroy_doc, + "destroy() -> None\n" + "\n" + "Destroy DCC connection\n" +); +static PyObject *PyDcc_destroy(PyDcc *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + dcc_destroy(DCC(self->data)); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyDcc_reject_doc, + "reject() -> None\n" + "\n" + "?\n" +); +static PyObject *PyDcc_reject(PyDcc *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"server", NULL}; + PyObject *server = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &server)) + return NULL; + + if (!pyirc_server_check(server)) + return PyErr_Format(PyExc_TypeError, "arg must be IRC server object"); + + dcc_reject(self->data, ((PyIrcServer*)server)->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyDcc_close_doc, + "close() -> None\n" + "\n" + "Close and destroy DCC connection.\n" +); +static PyObject *PyDcc_close(PyDcc *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + dcc_close(self->data); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyDcc_methods[] = { + {"destroy", (PyCFunction)PyDcc_destroy, METH_NOARGS, + PyDcc_destroy_doc}, + {"reject", (PyCFunction)PyDcc_reject, METH_VARARGS | METH_KEYWORDS, + PyDcc_reject_doc}, + {"close", (PyCFunction)PyDcc_close, METH_NOARGS, + PyDcc_close_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyDccType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Dcc", /*tp_name*/ + sizeof(PyDcc), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyDcc_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyDcc objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyDcc_methods, /* tp_methods */ + 0, /* tp_members */ + PyDcc_getseters, /* tp_getset */ + &PyIrssiBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* Dcc factory function */ +PyObject *pydcc_sub_new(void *dcc, const char *name, PyTypeObject *subclass) +{ + PyObject *chat = NULL, *server = NULL; + PyDcc *pydcc; + DCC_REC *rec = dcc; + + server = py_irssi_chat_new(rec->server, 1); + if (!server) + return NULL; + + chat = py_irssi_chat_new(rec->chat, 1); + if (!chat) + { + Py_DECREF(server); + return NULL; + } + + pydcc = py_instp(PyDcc, subclass); + if (!pydcc) + { + Py_DECREF(server); + Py_DECREF(chat); + return NULL; + } + + pydcc->data = dcc; + pydcc->server = server; + pydcc->chat = chat; + pydcc->base_name = name; + + pydcc->cleanup_installed = 1; + signal_add_last_data("dcc destroyed", dcc_cleanup, pydcc); + + return (PyObject *)pydcc; +} + +PyObject *pydcc_new(void *dcc) +{ + static const char *name = "DCC"; + return pydcc_sub_new(dcc, name, &PyDccType); +} + +int dcc_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyDccType) < 0) + return 0; + + Py_INCREF(&PyDccType); + PyModule_AddObject(py_module, "Dcc", (PyObject *)&PyDccType); + + return 1; +} diff --git a/src/objects/dcc-object.h b/src/objects/dcc-object.h new file mode 100644 index 0000000..d44d3db --- /dev/null +++ b/src/objects/dcc-object.h @@ -0,0 +1,24 @@ +#ifndef _DCC_OBJECT_H_ +#define _DCC_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +#define PyDcc_HEAD(type) \ + PyIrssi_HEAD(type) \ + PyObject *server; \ + PyObject *chat; + +typedef struct +{ + PyDcc_HEAD(void) +} PyDcc; + +extern PyTypeObject PyDccType; + +PyObject *pydcc_sub_new(void *dcc, const char *name, PyTypeObject *subclass); +PyObject *pydcc_new(void *dcc); +#define pydcc_check(op) PyObject_TypeCheck(op, &PyDccType) +int dcc_object_init(void); + +#endif diff --git a/src/objects/dcc-send-object.c b/src/objects/dcc-send-object.c new file mode 100644 index 0000000..643f741 --- /dev/null +++ b/src/objects/dcc-send-object.c @@ -0,0 +1,138 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "dcc-send-object.h" +#include "factory.h" +#include "pycore.h" + +#define DCC_SEND_CAST(rec) ((SEND_DCC_REC *)rec) + +/* inherit destroy and cleanup from Dcc type */ + +/* Getters */ +PyDoc_STRVAR(PyDccSend_size_doc, + "File size" +); +static PyObject *PyDccSend_size_get(PyDccSend *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC_SEND_CAST(self->data)->size); +} + +PyDoc_STRVAR(PyDccSend_skipped_doc, + "Bytes skipped from start (resuming file)" +); +static PyObject *PyDccSend_skipped_get(PyDccSend *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(DCC_SEND_CAST(self->data)->skipped); +} + +PyDoc_STRVAR(PyDccSend_file_quoted_doc, + "True if file name was received quoted (\"file name\")" +); +static PyObject *PyDccSend_file_quoted_get(PyDccSend *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(DCC_SEND_CAST(self->data)->file_quoted); +} + +PyDoc_STRVAR(PyDccSend_waitforend_doc, + "File is sent, just wait for the replies from the other side" +); +static PyObject *PyDccSend_waitforend_get(PyDccSend *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(DCC_SEND_CAST(self->data)->waitforend); +} + +PyDoc_STRVAR(PyDccSend_gotalldata_doc, + "Got all acks from the other end" +); +static PyObject *PyDccSend_gotalldata_get(PyDccSend *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(DCC_SEND_CAST(self->data)->gotalldata); +} + +/* specialized getters/setters */ +static PyGetSetDef PyDccSend_getseters[] = { + {"size", (getter)PyDccSend_size_get, NULL, + PyDccSend_size_doc, NULL}, + {"skipped", (getter)PyDccSend_skipped_get, NULL, + PyDccSend_skipped_doc, NULL}, + {"file_quoted", (getter)PyDccSend_file_quoted_get, NULL, + PyDccSend_file_quoted_doc, NULL}, + {"waitforend", (getter)PyDccSend_waitforend_get, NULL, + PyDccSend_waitforend_doc, NULL}, + {"gotalldata", (getter)PyDccSend_gotalldata_get, NULL, + PyDccSend_gotalldata_doc, NULL}, + {NULL} +}; + +/* Methods */ +/* Methods for object */ +static PyMethodDef PyDccSend_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyDccSendType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "DccSend", /*tp_name*/ + sizeof(PyDccSend), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyDccSend objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyDccSend_methods, /* tp_methods */ + 0, /* tp_members */ + PyDccSend_getseters, /* tp_getset */ + &PyDccType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyObject *pydcc_send_new(void *dcc) +{ + static const char *name = "DCC SEND"; + return pydcc_sub_new(dcc, name, &PyDccSendType); +} + +int dcc_send_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyDccSendType) < 0) + return 0; + + Py_INCREF(&PyDccSendType); + PyModule_AddObject(py_module, "DccSend", (PyObject *)&PyDccSendType); + + return 1; +} diff --git a/src/objects/dcc-send-object.h b/src/objects/dcc-send-object.h new file mode 100644 index 0000000..6303f86 --- /dev/null +++ b/src/objects/dcc-send-object.h @@ -0,0 +1,18 @@ +#ifndef _DCC_SEND_OBJECT_H_ +#define _DCC_SEND_OBJECT_H_ + +#include <Python.h> +#include "dcc-object.h" + +typedef struct +{ + PyDcc_HEAD(void) +} PyDccSend; + +extern PyTypeObject PyDccSendType; + +PyObject *pydcc_send_new(void *dcc); +#define pydcc_send_check(op) PyObject_TypeCheck(op, &PyDccSendType) +int dcc_send_object_init(void); + +#endif diff --git a/src/objects/factory.c b/src/objects/factory.c new file mode 100644 index 0000000..49336e6 --- /dev/null +++ b/src/objects/factory.c @@ -0,0 +1,326 @@ +#include <Python.h> +#include "pyirssi.h" +#include "factory.h" + +/* Irssi object factory works for all items with at least a type member. + * + * Use py_irssi_new() or py_irssi_chat_new() to get a new wrapper for an + * IrssiObject or an IrssiChatObject, respectively. + * + * For objects not descending from IrssiObject or IrssiChatObject, you must + * use the object-specific init function directly. + */ + +#define MAKEKEY(type, chat) ((chat << 16 ) | type) +#define GETTYPE(key) (key & 0xffff) +#define GETCHAT(key) ((key >> 16) & 0xffff) + + +GHashTable *init_map = NULL; + +static int init_objects(void); +static void register_chat(CHAT_PROTOCOL_REC *rec); +static void unregister_chat(CHAT_PROTOCOL_REC *rec); +static void insert_map(int type, int chat_type, InitFunc func); +static int remove_chat(void *key, void *value, void *chat_typep); +static void register_nonchat(void); +static InitFunc find_map(int type, int chat_type); + +static int init_objects(void) +{ + if (!pyscript_init()) + return 0; + + /* order is somewhat important here */ + if (!base_objects_init()) + return 0; + + if (!window_item_object_init()) + return 0; + + if (!channel_object_init()) + return 0; + + if (!query_object_init()) + return 0; + + if (!server_object_init()) + return 0; + + if (!connect_object_init()) + return 0; + + if (!irc_server_object_init()) + return 0; + + if (!irc_connect_object_init()) + return 0; + + if (!irc_channel_object_init()) + return 0; + + if (!ban_object_init()) + return 0; + + if (!nick_object_init()) + return 0; + + if (!chatnet_object_init()) + return 0; + + if (!reconnect_object_init()) + return 0; + + if (!window_object_init()) + return 0; + + if (!textdest_object_init()) + return 0; + + if (!rawlog_object_init()) + return 0; + + if (!log_object_init()) + return 0; + + if (!logitem_object_init()) + return 0; + + if (!ignore_object_init()) + return 0; + + if (!dcc_object_init()) + return 0; + + if (!dcc_chat_object_init()) + return 0; + + if (!dcc_get_object_init()) + return 0; + + if (!dcc_send_object_init()) + return 0; + + if (!netsplit_object_init()) + return 0; + + if (!netsplit_server_object_init()) + return 0; + + if (!netsplit_channel_object_init()) + return 0; + + if (!notifylist_object_init()) + return 0; + + if (!process_object_init()) + return 0; + + if (!command_object_init()) + return 0; + + if (!theme_object_init()) + return 0; + + if (!statusbar_item_object_init()) + return 0; + + if (!main_window_object_init()) + return 0; + + return 1; +} + +static InitFunc find_map(int type, int chat_type) +{ + unsigned hash; + + g_return_val_if_fail(type <= 0xffff, NULL); + g_return_val_if_fail(chat_type <= 0xffff, NULL); + + hash = MAKEKEY(type, chat_type); + return g_hash_table_lookup(init_map, GUINT_TO_POINTER(hash)); +} + +static void insert_map(int type, int chat_type, InitFunc func) +{ + unsigned hash; + + g_return_if_fail(type <= 0xffff); + g_return_if_fail(chat_type <= 0xffff); + + hash = MAKEKEY(type, chat_type); + g_hash_table_insert(init_map, GUINT_TO_POINTER(hash), func); +} + +static void register_chat(CHAT_PROTOCOL_REC *rec) +{ + int type; + int chat_type; + int is_irc = 0; + + /* chat_type == rec->id ??? */ + chat_type = chat_protocol_lookup(rec->name); + g_return_if_fail(chat_type >= 0 && chat_type < 0xffff); + + if (!g_strcasecmp(rec->name, "IRC")) + is_irc = 1; + + type = module_get_uniq_id("SERVER", 0); + if (is_irc) + insert_map(type, chat_type, (InitFunc)pyirc_server_new); + else + insert_map(type, chat_type, (InitFunc)pyserver_new); + + type = module_get_uniq_id("SERVER CONNECT", 0); + if (is_irc) + insert_map(type, chat_type, (InitFunc)pyirc_connect_new); + else + insert_map(type, chat_type, (InitFunc)pyconnect_new); + + type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL"); + if (is_irc) + insert_map(type, chat_type, (InitFunc)pyirc_channel_new); + else + insert_map(type, chat_type, (InitFunc)pychannel_new); + + type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY"); + insert_map(type, chat_type, (InitFunc)pyquery_new); + + type = module_get_uniq_id("CHATNET", 0); + insert_map(type, chat_type, (InitFunc)pychatnet_new); + + type = module_get_uniq_id("NICK", 0); + insert_map(type, chat_type, (InitFunc)pynick_new); +} + +/* register funcs for objects without a chat type */ +static void register_nonchat(void) +{ + int type; + int chat_type = 0xffff; + + type = module_get_uniq_id_str("DCC", "CHAT"); + insert_map(type, chat_type, (InitFunc)pydcc_chat_new); + + type = module_get_uniq_id_str("DCC", "GET"); + insert_map(type, chat_type, (InitFunc)pydcc_get_new); + + type = module_get_uniq_id_str("DCC", "SEND"); + insert_map(type, chat_type, (InitFunc)pydcc_send_new); + + type = module_get_uniq_id_str("DCC", "SERVER"); + insert_map(type, chat_type, (InitFunc)pydcc_new); +} + +static int remove_chat(void *key, void *value, void *chat_typep) +{ + unsigned hash = GPOINTER_TO_UINT(key); + int chat_type = GPOINTER_TO_INT(chat_type); + + if (GETCHAT(hash) == chat_type) + return TRUE; + + return FALSE; +} + +/* remove all items matching chat_type */ +static void unregister_chat(CHAT_PROTOCOL_REC *rec) +{ + /*int chat_type = chat_protocol_lookup(rec->name);*/ + g_hash_table_foreach_remove(init_map, + (GHRFunc)remove_chat, + GINT_TO_POINTER(rec->id)); +} + +PyObject *py_irssi_new(void *typeobj, int managed) +{ + IRSSI_BASE_REC *base = typeobj; + InitFunc ifunc; + + if (!base) + Py_RETURN_NONE; + + ifunc = find_map(base->type, 0xffff); + + if (ifunc) + return ifunc(typeobj, managed); + + return PyErr_Format(PyExc_RuntimeError, "no initfunc for object type %d", base->type); +} + +PyObject *py_irssi_chat_new(void *typeobj, int managed) +{ + IRSSI_CHAT_REC *chat = typeobj; + InitFunc ifunc; + + if (!chat) + Py_RETURN_NONE; + + ifunc = find_map(chat->type, chat->chat_type); + + if (ifunc) + return ifunc(typeobj, managed); + + return PyErr_Format(PyExc_RuntimeError, "no initfunc for object type %d, chat_type %d", + chat->type, chat->chat_type); +} + +PyObject *py_irssi_objlist_new(GSList *node, int managed, InitFunc init) +{ + PyObject *list = NULL; + + list = PyList_New(0); + if (!list) + goto error; + + for (; node != NULL; node = node->next) + { + int ret; + PyObject *obj = init(node->data, managed); + + if (!obj) + goto error; + + ret = PyList_Append(list, obj); + Py_DECREF(obj); + + if (ret != 0) + goto error; + } + + return list; + +error: + Py_XDECREF(list); + return NULL; +} + +int factory_init(void) +{ + g_return_val_if_fail(init_map == NULL, 0); + + if (!init_objects()) + return 0; + + init_map = g_hash_table_new(g_direct_hash, g_direct_equal); + g_slist_foreach(chat_protocols, (GFunc) register_chat, NULL); + register_nonchat(); + + signal_add("chat protocol created", (SIGNAL_FUNC) register_chat); + signal_add("chat protocol destroyed", (SIGNAL_FUNC) unregister_chat); + + return 1; +} + +void factory_deinit(void) +{ + g_return_if_fail(init_map != NULL); + + g_hash_table_destroy(init_map); + init_map = NULL; + + signal_remove("chat protocol created", (SIGNAL_FUNC) register_chat); + signal_remove("chat protocol destroyed", (SIGNAL_FUNC) unregister_chat); +} + diff --git a/src/objects/factory.h b/src/objects/factory.h new file mode 100644 index 0000000..9d4bd79 --- /dev/null +++ b/src/objects/factory.h @@ -0,0 +1,58 @@ +#ifndef _FACTORY_H_ +#define _FACTORY_H_ + +#include <Python.h> +#include "pyscript-object.h" +#include "base-objects.h" +#include "window-item-object.h" +#include "channel-object.h" +#include "query-object.h" +#include "server-object.h" +#include "connect-object.h" +#include "irc-server-object.h" +#include "irc-connect-object.h" +#include "irc-channel-object.h" +#include "ban-object.h" +#include "nick-object.h" +#include "chatnet-object.h" +#include "reconnect-object.h" +#include "window-object.h" +#include "textdest-object.h" +#include "rawlog-object.h" +#include "log-object.h" +#include "logitem-object.h" +#include "ignore-object.h" +#include "dcc-object.h" +#include "dcc-chat-object.h" +#include "dcc-get-object.h" +#include "dcc-send-object.h" +#include "netsplit-object.h" +#include "netsplit-server-object.h" +#include "netsplit-channel-object.h" +#include "notifylist-object.h" +#include "process-object.h" +#include "command-object.h" +#include "theme-object.h" +#include "statusbar-item-object.h" +#include "main-window-object.h" + +int factory_init(void); +void factory_deinit(void); + +/*managed == 1: object invalidates itself + *managed == 0: caller responsible for invalidating object + *XXX: most objects invalidate themselves, ignoring "managed" switch, + * and some are never managed (Reconnect) + */ + +/* For objects with a type member but no chat_type */ +PyObject *py_irssi_new(void *typeobj, int managed); +/* For objects with both type and chat_type members */ +PyObject *py_irssi_chat_new(void *typeobj, int managed); + +typedef PyObject *(*InitFunc)(void *, int); +PyObject *py_irssi_objlist_new(GSList *node, int managed, InitFunc init); +#define py_irssi_chatlist_new(n, m) py_irssi_objlist_new(n, m, py_irssi_chat_new) +#define py_irssi_list_new(n, m) py_irssi_objlist_new(n, m, py_irssi_new) + +#endif diff --git a/src/objects/ignore-object.c b/src/objects/ignore-object.c new file mode 100644 index 0000000..979c9b1 --- /dev/null +++ b/src/objects/ignore-object.c @@ -0,0 +1,284 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "ignore-object.h" +#include "factory.h" +#include "pycore.h" + +/* monitor "ignore destroy" signal */ +static void ignore_cleanup(IGNORE_REC *ignore) +{ + PyIgnore *pyignore = signal_get_user_data(); + + if (ignore == pyignore->data) + { + pyignore->data = NULL; + pyignore->cleanup_installed = 0; + signal_remove_data("ignore destroy", ignore_cleanup, pyignore); + } +} + +static void PyIgnore_dealloc(PyIgnore *self) +{ + if (self->cleanup_installed) + signal_remove_data("ignore destroy", ignore_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyIgnore_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyIgnore *self; + + self = (PyIgnore *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyIgnore_mask_doc, + "Ignore mask" +); +static PyObject *PyIgnore_mask_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->mask); +} + +PyDoc_STRVAR(PyIgnore_servertag_doc, + "Ignore only in server" +); +static PyObject *PyIgnore_servertag_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->servertag); +} + +PyDoc_STRVAR(PyIgnore_pattern_doc, + "Ignore text patern" +); +static PyObject *PyIgnore_pattern_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->pattern); +} + +PyDoc_STRVAR(PyIgnore_level_doc, + "Ignore level" +); +static PyObject *PyIgnore_level_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->level); +} + +PyDoc_STRVAR(PyIgnore_exception_doc, + "This is an exception ignore" +); +static PyObject *PyIgnore_exception_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->exception); +} + +PyDoc_STRVAR(PyIgnore_regexp_doc, + "Regexp pattern matching" +); +static PyObject *PyIgnore_regexp_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->regexp); +} + +PyDoc_STRVAR(PyIgnore_fullword_doc, + "Pattern matches only full words" +); +static PyObject *PyIgnore_fullword_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->fullword); +} + +PyDoc_STRVAR(PyIgnore_replies_doc, + "Ignore replies to nick in channel" +); +static PyObject *PyIgnore_replies_get(PyIgnore *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->replies); +} + +/* specialized getters/setters */ +static PyGetSetDef PyIgnore_getseters[] = { + {"mask", (getter)PyIgnore_mask_get, NULL, + PyIgnore_mask_doc, NULL}, + {"servertag", (getter)PyIgnore_servertag_get, NULL, + PyIgnore_servertag_doc, NULL}, + {"pattern", (getter)PyIgnore_pattern_get, NULL, + PyIgnore_pattern_doc, NULL}, + {"level", (getter)PyIgnore_level_get, NULL, + PyIgnore_level_doc, NULL}, + {"exception", (getter)PyIgnore_exception_get, NULL, + PyIgnore_exception_doc, NULL}, + {"regexp", (getter)PyIgnore_regexp_get, NULL, + PyIgnore_regexp_doc, NULL}, + {"fullword", (getter)PyIgnore_fullword_get, NULL, + PyIgnore_fullword_doc, NULL}, + {"replies", (getter)PyIgnore_replies_get, NULL, + PyIgnore_replies_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyIgnore_channels_doc, + "channels() -> list of str\n" + "\n" + "Ignore only in channels (list of names)\n" +); +static PyObject *PyIgnore_channels(PyIgnore *self, PyObject *args) +{ + char **p; + PyObject *list; + + RET_NULL_IF_INVALID(self->data); + + list = PyList_New(0); + if (!list) + return NULL; + + for (p = self->data->channels; *p; p++) + { + int ret; + PyObject *str; + + str = PyString_FromString(*p); + if (!str) + { + Py_XDECREF(list); + return NULL; + } + + ret = PyList_Append(list, str); + Py_DECREF(str); + if (ret != 0) + { + Py_XDECREF(list); + return NULL; + } + } + + return list; +} + +PyDoc_STRVAR(PyIgnore_add_rec_doc, + "add_rec() -> None\n" + "\n" + "Add ignore record" +); +static PyObject *PyIgnore_add_rec(PyIgnore *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + ignore_add_rec(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyIgnore_update_rec_doc, + "update_rec() -> None\n" + "\n" + "Update ignore record in configuration" +); +static PyObject *PyIgnore_update_rec(PyIgnore *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + ignore_update_rec(self->data); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyIgnore_methods[] = { + {"add_rec", (PyCFunction)PyIgnore_add_rec, METH_NOARGS, + PyIgnore_add_rec_doc}, + {"update_rec", (PyCFunction)PyIgnore_update_rec, METH_NOARGS, + PyIgnore_update_rec_doc}, + {"channels", (PyCFunction)PyIgnore_channels, METH_NOARGS, + PyIgnore_channels_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyIgnoreType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Ignore", /*tp_name*/ + sizeof(PyIgnore), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyIgnore_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyIgnore objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyIgnore_methods, /* tp_methods */ + 0, /* tp_members */ + PyIgnore_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyIgnore_new, /* tp_new */ +}; + + +/* ignore factory function */ +PyObject *pyignore_new(void *ignore) +{ + PyIgnore *pyignore; + + pyignore = py_inst(PyIgnore, PyIgnoreType); + if (!pyignore) + return NULL; + + pyignore->data = ignore; + pyignore->cleanup_installed = 1; + signal_add_last_data("ignore destroy", ignore_cleanup, pyignore); + + return (PyObject *)pyignore; +} + +int ignore_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyIgnoreType) < 0) + return 0; + + Py_INCREF(&PyIgnoreType); + PyModule_AddObject(py_module, "Ignore", (PyObject *)&PyIgnoreType); + + return 1; +} diff --git a/src/objects/ignore-object.h b/src/objects/ignore-object.h new file mode 100644 index 0000000..39f7425 --- /dev/null +++ b/src/objects/ignore-object.h @@ -0,0 +1,21 @@ +#ifndef _IGNORE_OBJECT_H_ +#define _IGNORE_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _IGNORE_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct _IGNORE_REC) +} PyIgnore; + +extern PyTypeObject PyIgnoreType; + +int ignore_object_init(void); +PyObject *pyignore_new(void *ignore); +#define pyignore_check(op) PyObject_TypeCheck(op, &PyIgnoreType) + +#endif diff --git a/src/objects/irc-channel-object.c b/src/objects/irc-channel-object.c new file mode 100644 index 0000000..5e9531d --- /dev/null +++ b/src/objects/irc-channel-object.c @@ -0,0 +1,173 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "pycore.h" +#include "irc-channel-object.h" +#include "factory.h" + +/* PyIrcChannel destructor is inherited from PyChannel */ + +/* specialized getters/setters */ +static PyGetSetDef PyIrcChannel_getseters[] = { + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(bans_doc, + "bans() -> list of Ban objects\n" + "\n" + "Returns a list of bans in the channel.\n" +); +static PyObject *PyIrcChannel_bans(PyIrcChannel *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + return py_irssi_objlist_new(self->data->banlist, 1, (InitFunc)pyban_new); +} + +PyDoc_STRVAR(ban_get_mask_doc, + "ban_get_mask(nick, ban_type=0) -> str\n" + "\n" + "Get ban mask for 'nick'.\n" +); +static PyObject *PyIrcChannel_ban_get_mask(PyIrcChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", "ban_type", NULL}; + char *nick, *str; + int ban_type = 0; + PyObject *ret; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &nick, &ban_type)) + return NULL; + + str = ban_get_mask(self->data, nick, ban_type); + if (!str) + Py_RETURN_NONE; + + ret = PyString_FromString(str); + g_free(str); + + return ret; +} + +PyDoc_STRVAR(banlist_add_doc, + "banlist_add(ban, nick, time) -> Ban object or None\n" + "\n" + "Add a new ban to channel. Return None if duplicate." +); +static PyObject *PyIrcChannel_banlist_add(PyIrcChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"ban", "nick", "time", NULL}; + char *ban, *nick; + time_t btime; + BAN_REC *newban; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssk", kwlist, &ban, &nick, &btime)) + return NULL; + + newban = banlist_add(self->data, ban, nick, btime); + /* XXX: return none or throw error? */ + if (!newban) + Py_RETURN_NONE; + + return pyban_new(newban); +} + +PyDoc_STRVAR(banlist_remove_doc, + "banlist_remove(ban, nick) -> None\n" + "\n" + "Remove a new ban from channel.\n" +); +static PyObject *PyIrcChannel_banlist_remove(PyIrcChannel *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"ban", "nick", NULL}; + char *ban, *nick; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, &ban, &nick)) + return NULL; + + banlist_remove(self->data, ban, nick); + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyIrcChannel_methods[] = { + {"bans", (PyCFunction)PyIrcChannel_bans, METH_NOARGS, + bans_doc}, + {"ban_get_mask", (PyCFunction)PyIrcChannel_ban_get_mask, METH_VARARGS | METH_KEYWORDS, + ban_get_mask_doc}, + {"banlist_add", (PyCFunction)PyIrcChannel_banlist_add, METH_VARARGS | METH_KEYWORDS, + banlist_add_doc}, + {"banlist_remove", (PyCFunction)PyIrcChannel_banlist_remove, METH_VARARGS | METH_KEYWORDS, + banlist_remove_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyIrcChannelType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "IrcChannel", /*tp_name*/ + sizeof(PyIrcChannel), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyIrcChannel objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyIrcChannel_methods, /* tp_methods */ + 0, /* tp_members */ + PyIrcChannel_getseters, /* tp_getset */ + &PyChannelType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* irc channel factory function */ +PyObject *pyirc_channel_new(void *chan) +{ + static const char *BASE_NAME = "CHANNEL"; + return pychannel_sub_new(chan, BASE_NAME, &PyIrcChannelType); +} + +int irc_channel_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyIrcChannelType) < 0) + return 0; + + Py_INCREF(&PyIrcChannelType); + PyModule_AddObject(py_module, "IrcChannel", (PyObject *)&PyIrcChannelType); + + return 1; +} diff --git a/src/objects/irc-channel-object.h b/src/objects/irc-channel-object.h new file mode 100644 index 0000000..a1d845a --- /dev/null +++ b/src/objects/irc-channel-object.h @@ -0,0 +1,21 @@ +#ifndef _IRC_CHANNEL_OBJECT_H_ +#define _IRC_CHANNEL_OBJECT_H_ + +#include <Python.h> +#include "window-item-object.h" + +/* forward */ +struct _IRC_CHANNEL_REC; + +typedef struct +{ + PyWindowItem_HEAD(struct _IRC_CHANNEL_REC) +} PyIrcChannel; + +extern PyTypeObject PyIrcChannelType; + +int irc_channel_object_init(void); +PyObject *pyirc_channel_new(void *chan); +#define pyirc_channel_check(op) PyObject_TypeCheck(op, &PyIrcChannelType) + +#endif diff --git a/src/objects/irc-connect-object.c b/src/objects/irc-connect-object.c new file mode 100644 index 0000000..5068dea --- /dev/null +++ b/src/objects/irc-connect-object.c @@ -0,0 +1,86 @@ +#include <Python.h> +#include "pymodule.h" +#include "base-objects.h" +#include "irc-connect-object.h" +#include "pyirssi_irc.h" +#include "pycore.h" +#include "pyutils.h" + +/* cleanup and deallocation handled by Connect base */ + +/* Getters */ +PyDoc_STRVAR(PyIrcConnect_alternate_nick_doc, + "Alternate nick to use if default nick is taken" +); +static PyObject *PyIrcConnect_alternate_nick_get(PyIrcConnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->alternate_nick); +} + +/* Get/Set */ +static PyGetSetDef PyIrcConnect_getseters[] = { + {"alternate_nick", (getter)PyIrcConnect_alternate_nick_get, NULL, + PyIrcConnect_alternate_nick_doc, NULL}, + {NULL} +}; + +PyTypeObject PyIrcConnectType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "IrcConnect", /*tp_name*/ + sizeof(PyIrcConnect), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyIrcConnect objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + PyIrcConnect_getseters, /* tp_getset */ + &PyConnectType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyObject *pyirc_connect_new(void *connect, int managed) +{ + return pyconnect_sub_new(connect, &PyIrcConnectType, managed); +} + +int irc_connect_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyIrcConnectType) < 0) + return 0; + + Py_INCREF(&PyIrcConnectType); + PyModule_AddObject(py_module, "IrcConnect", (PyObject *)&PyIrcConnectType); + + return 1; +} diff --git a/src/objects/irc-connect-object.h b/src/objects/irc-connect-object.h new file mode 100644 index 0000000..3f6cad4 --- /dev/null +++ b/src/objects/irc-connect-object.h @@ -0,0 +1,21 @@ +#ifndef _IRC_CONNECT_OBJECT_H_ +#define _IRC_CONNECT_OBJECT_H_ + +#include <Python.h> +#include "connect-object.h" + +/* forward */ +struct _IRC_SERVER_CONNECT_REC; + +typedef struct +{ + PyIrssi_HEAD(struct _IRC_SERVER_CONNECT_REC) +} PyIrcConnect; + +extern PyTypeObject PyIrcConnectType; + +int irc_connect_object_init(void); +PyObject *pyirc_connect_new(void *connect, int managed); +#define pyirc_connect_check(op) PyObject_TypeCheck(op, &PyIrcConnectType) + +#endif diff --git a/src/objects/irc-server-object.c b/src/objects/irc-server-object.c new file mode 100644 index 0000000..4e9a28e --- /dev/null +++ b/src/objects/irc-server-object.c @@ -0,0 +1,491 @@ +#include <Python.h> +#include "pymodule.h" +#include "base-objects.h" +#include "irc-server-object.h" +#include "factory.h" +#include "pyirssi_irc.h" +#include "pycore.h" +#include "pyutils.h" + +/* cleanup and dealloc inherited from base Server */ + +/* Getters */ +PyDoc_STRVAR(PyIrcServer_real_address_doc, + "Address the IRC server gives" +); +static PyObject *PyIrcServer_real_address_get(PyIrcServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->real_address); +} + +PyDoc_STRVAR(PyIrcServer_usermode_doc, + "User mode in server" +); +static PyObject *PyIrcServer_usermode_get(PyIrcServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->usermode); +} + +PyDoc_STRVAR(PyIrcServer_userhost_doc, + "Your user host in server" +); +static PyObject *PyIrcServer_userhost_get(PyIrcServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->userhost); +} + +static PyGetSetDef PyIrcServer_getseters[] = { + {"real_address", (getter)PyIrcServer_real_address_get, NULL, + PyIrcServer_real_address_doc, NULL}, + {"usermode", (getter)PyIrcServer_usermode_get, NULL, + PyIrcServer_usermode_doc, NULL}, + {"userhost", (getter)PyIrcServer_userhost_get, NULL, + PyIrcServer_userhost_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(get_channels_doc, + "get_channels() -> str\n" + "\n" + "Return a string of all channels (and keys, if any have them) in server,\n" + "like '#a,#b,#c,#d x,b_chan_key,x,x' or just '#e,#f,#g'\n" +); +static PyObject *PyIrcServer_get_channels(PyIrcServer *self, PyObject *args) +{ + char *list; + PyObject *ret; + + RET_NULL_IF_INVALID(self->data); + + list = irc_server_get_channels(self->data); + ret = PyString_FromString(list); + g_free(list); + + return ret; +} + +PyDoc_STRVAR(send_raw_doc, + "send_raw(cmd) -> None\n" + "\n" + "Send raw message to server, it will be flood protected so you\n" + "don't need to worry about it.\n" +); +static PyObject *PyIrcServer_send_raw(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", NULL}; + char *cmd; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd)) + return NULL; + + irc_send_cmd(self->data, cmd); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(send_raw_now_doc, + "send_raw_now(cmd) -> None\n" + "\n" + "Send raw message to server immediately without flood protection.\n" +); +static PyObject *PyIrcServer_send_raw_now(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", NULL}; + char *cmd; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd)) + return NULL; + + irc_send_cmd_now(self->data, cmd); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(send_raw_split_doc, + "send_raw_split(cmd, nickarg, max_nicks) -> None\n" + "\n" + "Split the `cmd' into several commands so `nickarg' argument has only\n" + "`max_nicks' number of nicks.\n" + "\n" + "Example:\n" + "server.send_raw_split('KICK #channel nick1,nick2,nick3 :byebye', 3, 2)\n" + "\n" + "Irssi will send commands 'KICK #channel nick1,nick2 :byebye' and\n" + "'KICK #channel nick3 :byebye' to server.\n" +); +static PyObject *PyIrcServer_send_raw_split(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", "nickarg", "max_nicks", NULL}; + char *cmd; + int nickarg; + int max_nicks; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sii", kwlist, &cmd, &nickarg, &max_nicks)) + return NULL; + + irc_send_cmd_split(self->data, cmd, nickarg, max_nicks); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(ctcp_send_reply_doc, + "ctcp_send_reply(data) -> None\n" + "\n" + "Send CTCP reply. This will be 'CTCP flood protected' so if there's too\n" + "many CTCP requests in buffer, this reply might not get sent. The data\n" + "is the full raw command to be sent to server, like\n" + "'NOTICE nick :\001VERSION irssi\001'\n" +); +static PyObject *PyIrcServer_ctcp_send_reply(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"data", NULL}; + char *data; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &data)) + return NULL; + + ctcp_send_reply(self->data, data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(isupport_doc, + "isupport(name) -> str or None\n" + "\n" + "Returns the value of the named item in the ISUPPORT (005) numeric to the\n" + "script. If the item is not present returns undef, if the item has no value\n" + "then '' is returned use defined server.isupport('name') if you need to\n" + "check whether a property is present.\n" + "See http://www.ietf.org/internet-drafts/draft-brocklesby-irc-isupport-01.txt\n" + "for more information on the ISUPPORT numeric.\n" +); +static PyObject *PyIrcServer_isupport(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name; + char *found; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &name)) + return NULL; + + found = g_hash_table_lookup(self->data->isupport, name); + + RET_AS_STRING_OR_NONE(found); +} + +PyDoc_STRVAR(PyIrcServer_netsplit_find_doc, + "netsplit_find(nick, address) -> Netsplit object or None\n" + "\n" + "Check if nick!address is on the other side of netsplit. Netsplit records\n" + "are automatically removed after 30 minutes (current default)..\n" +); +static PyObject *PyIrcServer_netsplit_find(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", "address", NULL}; + char *nick = ""; + char *address = ""; + NETSPLIT_REC *ns; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &nick, &address)) + return NULL; + + ns = netsplit_find(self->data, nick, address); + if (ns) + return pynetsplit_new(ns); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyIrcServer_netsplit_find_channel_doc, + "netsplit_find_channel(nick, address, channel) -> NetsplitChannel object or None\n" + "\n" + "Find nick record for nick!address in channel `channel'.\n" +); +static PyObject *PyIrcServer_netsplit_find_channel(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", "address", "channel", NULL}; + char *nick = ""; + char *address = ""; + char *channel = ""; + NETSPLIT_CHAN_REC *nsc; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + &nick, &address, &channel)) + return NULL; + + nsc = netsplit_find_channel(self->data, nick, address, channel); + if (nsc) + return pynetsplit_channel_new(nsc); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyIrcServer_notifylist_ison_doc, + "notifylist_ison(nick) -> bool\n" + "\n" + "Check if nick is on server\n" +); +static PyObject *PyIrcServer_notifylist_ison(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + char *nick = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &nick)) + return NULL; + + return PyBool_FromLong(notifylist_ison_server(self->data, nick)); +} + +/* expect a list of tuples [('str', 'str'), ...] */ +static GSList *py_event_conv(PyObject *list) +{ + int i; + GSList *ret = NULL; + + if (!PyList_Check(list)) + { + PyErr_Format(PyExc_TypeError, "expect a list of tuples of two strings"); + return NULL; + } + + for (i = 0; i < PyList_Size(list); i++) + { + char *key; + char *val; + PyObject *tup = PyList_GET_ITEM(list, i); + + if (!PyTuple_Check(tup) || !PyArg_ParseTuple(tup, "ss", &key, &val)) + { + GSList *node; + + for (node = ret; node; node = node->next) + g_free(node->data); + + g_slist_free(ret); + + if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_TypeError)) + { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, "expect a list of tuples of two strings"); + } + + return NULL; + } + + ret = g_slist_append(ret, g_strdup(key)); + ret = g_slist_append(ret, g_strdup(val)); + } + + return ret; +} + +PyDoc_STRVAR(PyIrcServer_redirect_event_doc, + "redirect_event(command, signals, arg=None, count=1, remote=-1, failure_signal=None) -> None\n" + "\n" + "Specify that the next command sent to server will be redirected.\n" + "NOTE: This command MUST be called before sending the command to server.\n" + "\n" + "`command' - Name of the registered redirection that we're using.\n" + "\n" + "`count' - How many times to execute the redirection. Some commands may\n" + "send multiple stop events, like MODE #a,#b.\n" + "\n" + "`arg' - The argument to be compared in event strings. You can give multiple\n" + "arguments separated with space.\n" + "\n" + "`remote' - Specifies if the command is a remote command, -1 = use default.\n" + "\n" + "`failure_signal' - If irssi can't find the stop signal for the redirection,\n" + "this signal is called.\n" + "\n" + "`signals' - hash reference with \"event\" => \"redir signal\" entries.\n" + "If the event is "", all the events belonging to the redirection but not\n" + "specified here, will be sent there.\n" + "\n" + "Example:\n" + "\n" + "# ignore all events generated by whois query, except 311.\n" + "\n" + "server.redirect_event(\"whois\",\n" + " remote = 0,\n" + " arg = \"cras\",\n" + " signals = [\n" + " ('event 311', 'redir whois'),\n" + " ('', 'event empty') \n" + " ]\n" + ")\n" + "server.send_raw(\"WHOIS :cras\")\n" +); +static PyObject *PyIrcServer_redirect_event(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"command", "signals", "arg", "count", "remote", "failure_signal", NULL}; + char *command = ""; + int count = 1; + char *arg = NULL; + int remote = -1; + char *failure_signal = NULL; + PyObject *signals = NULL; + GSList *gsignals; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|ziiz", kwlist, + &command, &signals, &arg, &count, &remote, &failure_signal)) + return NULL; + + gsignals = py_event_conv(signals); + if (!gsignals) + return NULL; + + server_redirect_event(self->data, command, count, arg, remote, failure_signal, gsignals); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyIrcServer_redirect_get_signal_doc, + "redirect_get_signal(event, args) -> str\n" +); +static PyObject *PyIrcServer_redirect_get_signal(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"event", "args", NULL}; + char *event = ""; + char *pargs = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &event, &pargs)) + return NULL; + + RET_AS_STRING_OR_NONE(server_redirect_get_signal(self->data, event, pargs)); +} + +PyDoc_STRVAR(PyIrcServer_redirect_peek_signal_doc, + "redirect_peek_signal(event, args) -> str\n" +); +static PyObject *PyIrcServer_redirect_peek_signal(PyIrcServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"event", "args", NULL}; + char *event = ""; + char *pargs = ""; + int redirection; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &event, &pargs)) + return NULL; + + RET_AS_STRING_OR_NONE(server_redirect_peek_signal(self->data, event, pargs, &redirection)); +} + +/* Methods for object */ +static PyMethodDef PyIrcServer_methods[] = { + {"get_channels", (PyCFunction)PyIrcServer_get_channels, METH_NOARGS, + get_channels_doc}, + {"send_raw", (PyCFunction)PyIrcServer_send_raw, METH_VARARGS | METH_KEYWORDS, + send_raw_doc}, + {"send_raw_now", (PyCFunction)PyIrcServer_send_raw_now, METH_VARARGS | METH_KEYWORDS, + send_raw_now_doc}, + {"send_raw_split", (PyCFunction)PyIrcServer_send_raw_split, METH_VARARGS | METH_KEYWORDS, + send_raw_split_doc}, + {"ctcp_send_reply", (PyCFunction)PyIrcServer_ctcp_send_reply, METH_VARARGS | METH_KEYWORDS, + ctcp_send_reply_doc}, + {"isupport", (PyCFunction)PyIrcServer_isupport, METH_VARARGS | METH_KEYWORDS, + isupport_doc}, + {"netsplit_find", (PyCFunction)PyIrcServer_netsplit_find, METH_VARARGS | METH_KEYWORDS, + PyIrcServer_netsplit_find_doc}, + {"netsplit_find_channel", (PyCFunction)PyIrcServer_netsplit_find_channel, METH_VARARGS | METH_KEYWORDS, + PyIrcServer_netsplit_find_channel_doc}, + {"notifylist_ison", (PyCFunction)PyIrcServer_notifylist_ison, METH_VARARGS | METH_KEYWORDS, + PyIrcServer_notifylist_ison_doc}, + {"redirect_event", (PyCFunction)PyIrcServer_redirect_event, METH_VARARGS | METH_KEYWORDS, + PyIrcServer_redirect_event_doc}, + {"redirect_get_signal", (PyCFunction)PyIrcServer_redirect_get_signal, METH_VARARGS | METH_KEYWORDS, + PyIrcServer_redirect_get_signal_doc}, + {"redirect_peek_signal", (PyCFunction)PyIrcServer_redirect_peek_signal, METH_VARARGS | METH_KEYWORDS, + PyIrcServer_redirect_peek_signal_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyIrcServerType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "IrcServer", /*tp_name*/ + sizeof(PyIrcServer), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyIrcServer objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyIrcServer_methods, /* tp_methods */ + 0, /* tp_members */ + PyIrcServer_getseters, /* tp_getset */ + &PyServerType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PyObject *pyirc_server_new(void *server) +{ + return pyserver_sub_new(server, &PyIrcServerType); +} + +int irc_server_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyIrcServerType) < 0) + return 0; + + Py_INCREF(&PyIrcServerType); + PyModule_AddObject(py_module, "IrcServer", (PyObject *)&PyIrcServerType); + + return 1; +} diff --git a/src/objects/irc-server-object.h b/src/objects/irc-server-object.h new file mode 100644 index 0000000..b3eb76a --- /dev/null +++ b/src/objects/irc-server-object.h @@ -0,0 +1,21 @@ +#ifndef _IRC_SERVER_OBJECT_H_ +#define _IRC_SERVER_OBJECT_H_ + +#include <Python.h> +#include "server-object.h" + +/* forward */ +struct _IRC_SERVER_REC; + +typedef struct +{ + PyServer_HEAD(struct _IRC_SERVER_REC) +} PyIrcServer; + +extern PyTypeObject PyIrcServerType; + +int irc_server_object_init(void); +PyObject *pyirc_server_new(void *server); +#define pyirc_server_check(op) PyObject_TypeCheck(op, &PyIrcServerType) + +#endif diff --git a/src/objects/log-object.c b/src/objects/log-object.c new file mode 100644 index 0000000..b18f826 --- /dev/null +++ b/src/objects/log-object.c @@ -0,0 +1,477 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "log-object.h" +#include "factory.h" +#include "pycore.h" + +static LOG_ITEM_REC *find_item(LOG_REC *log, PyLogitem *item); +static void log_cleanup(LOG_REC *log); +static int logtype(int *type, int target, int window); + +/* find/convert a py log item */ +static LOG_ITEM_REC *find_item(LOG_REC *log, PyLogitem *item) +{ + int type; + char *name; + char *servertag = NULL; + + if (!item->type || !item->name) + return NULL; + + type = PyInt_AS_LONG(item->type); + name = PyString_AS_STRING(item->name); + if (item->servertag) + servertag = PyString_AS_STRING(item->servertag); + + return log_item_find(log, type, name, servertag); +} + +/* monitor "log remove" signal */ +static void log_cleanup(LOG_REC *log) +{ + PyLog *pylog = signal_get_user_data(); + + if (log == pylog->data) + { + pylog->data = NULL; + pylog->cleanup_installed = 0; + signal_remove_data("log remove", log_cleanup, pylog); + } +} + +static void PyLog_dealloc(PyLog *self) +{ + if (self->cleanup_installed) + signal_remove_data("log remove", log_cleanup, self); + + if (self->data && !g_slist_find(logs, self->data)) + { + printtext(NULL, NULL, MSGLEVEL_CRAP, "destroying orphan log %s", self->data->fname); + log_close(self->data); + } + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyLog_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyLog *self; + + self = (PyLog *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* function to create the log */ +PyDoc_STRVAR(PyLog_doc, + "__init__(fname, level=MSGLEVEL_ALL)\n" + "\n" + "Create a log\n" +); +static int PyLog_init(PyLog *self, PyObject *args, PyObject *kwds) +{ + char *fname; + int level = MSGLEVEL_ALL; + LOG_REC *log; + + static char *kwlist[] = {"fname", "level", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, + &fname, &level)) + return -1; + + /*XXX: anything better than RuntimeError ? */ + if (self->data || self->cleanup_installed) + { + PyErr_Format(PyExc_RuntimeError, "log already opened; close it first"); + return -1; + } + + log = log_create_rec(fname, level); + if (!log) + { + PyErr_Format(PyExc_RuntimeError, "failed to create log"); + return -1; + } + + self->data = log; + self->cleanup_installed = 1; + signal_add_last_data("log remove", log_cleanup, self); + + return 0; +} + +/* Getters */ +PyDoc_STRVAR(PyLog_fname_doc, + "Log file name" +); +static PyObject *PyLog_fname_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->fname); +} + +PyDoc_STRVAR(PyLog_real_fname_doc, + "The actual opened log file (after %d.%m.Y etc. are expanded)" +); +static PyObject *PyLog_real_fname_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->real_fname); +} + +PyDoc_STRVAR(PyLog_opened_doc, + "Log file is open" +); +static PyObject *PyLog_opened_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(self->data->opened); +} + +PyDoc_STRVAR(PyLog_level_doc, + "Log only these levels" +); +static PyObject *PyLog_level_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->level); +} + +PyDoc_STRVAR(PyLog_last_doc, + "Timestamp when last message was written" +); +static PyObject *PyLog_last_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(self->data->last); +} + +PyDoc_STRVAR(PyLog_autoopen_doc, + "Automatically open log at startup" +); +static PyObject *PyLog_autoopen_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->autoopen); +} + +PyDoc_STRVAR(PyLog_failed_doc, + "Opening log failed last time" +); +static PyObject *PyLog_failed_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->failed); +} + +PyDoc_STRVAR(PyLog_temp_doc, + "Log isn't saved to config file" +); +static PyObject *PyLog_temp_get(PyLog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->temp); +} + +/* specialized getters/setters */ +static PyGetSetDef PyLog_getseters[] = { + {"fname", (getter)PyLog_fname_get, NULL, + PyLog_fname_doc, NULL}, + {"real_fname", (getter)PyLog_real_fname_get, NULL, + PyLog_real_fname_doc, NULL}, + {"opened", (getter)PyLog_opened_get, NULL, + PyLog_opened_doc, NULL}, + {"level", (getter)PyLog_level_get, NULL, + PyLog_level_doc, NULL}, + {"last", (getter)PyLog_last_get, NULL, + PyLog_last_doc, NULL}, + {"autoopen", (getter)PyLog_autoopen_get, NULL, + PyLog_autoopen_doc, NULL}, + {"failed", (getter)PyLog_failed_get, NULL, + PyLog_failed_doc, NULL}, + {"temp", (getter)PyLog_temp_get, NULL, + PyLog_temp_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyLog_items_doc, + "items() -> list of Log objects\n" + "\n" + "Return a list of log items\n" +); +static PyObject *PyLog_items(PyLog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + return py_irssi_objlist_new(self->data->items, 1, (InitFunc)pylogitem_new); +} + +PyDoc_STRVAR(PyLog_update_doc, + "update() -> None\n" + "\n" + "Add log to list of logs / save changes to config file.\n" +); +static PyObject *PyLog_update(PyLog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + log_update(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyLog_close_doc, + "destroy() -> None\n" + "\n" + "Destroy the log file\n" +); +static PyObject *PyLog_close(PyLog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + log_close(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyLog_start_logging_doc, + "start_logging() -> None\n" + "\n" + "Open log file and start logging.\n" +); +static PyObject *PyLog_start_logging(PyLog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + log_start_logging(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyLog_stop_logging_doc, + "stop_logging() -> None\n" + "\n" + "Stop and close the log file.\n" +); +static PyObject *PyLog_stop_logging(PyLog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + log_stop_logging(self->data); + + Py_RETURN_NONE; +} + +static int logtype(int *type, int target, int window) +{ + if (target || window) + { + if (target && window) + { + PyErr_SetString(PyExc_TypeError, "must specify target or window, not both"); + return 0; + } + + *type = target? 0 : 1; + } + else if (*type < 0) + { + PyErr_SetString(PyExc_TypeError, "must specify type, target, or window"); + return 0; + } + + return 1; +} + +PyDoc_STRVAR(PyLog_item_add_doc, + "item_add(item, servertag=None, type=0, target=False, window=False) -> None\n" + "\n" + "Add a log item to log.\n" + "\n" + "Add a target item (nick, chan): \n" + " item_add('#linux', target=True)\n" + " item_add('#linux', type=0)\n" + "\n" + "Add a window ref: \n" + " item_add('2', window=True)\n" + " item_add('2', type=1)\n" +); +static PyObject *PyLog_item_add(PyLog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", "servertag", "type", "target", "window", NULL}; + char *item = ""; + char *servertag = NULL; + int type = 0; + int target = 0; + int window = 0; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ziii", kwlist, + &item, &servertag, &type, &target, &window)) + return NULL; + + if (!logtype(&type, target, window)) + return NULL; + + log_item_add(self->data, type, item, servertag); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyLog_item_destroy_doc, + "item_destroy(item) -> None\n" + "\n" + "Remove log item from log.\n" +); +static PyObject *PyLog_item_destroy(PyLog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", NULL}; + PyObject *item = NULL; + LOG_ITEM_REC *li; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &item)) + return NULL; + + if (!pylogitem_check(item)) + return PyErr_Format(PyExc_TypeError, "arg 1 should be log item"); + + li = find_item(self->data, (PyLogitem *)item); + if (!li) + return PyErr_Format(PyExc_TypeError, "log item invalid or not found"); + + log_item_destroy(self->data, li); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyLog_item_find_doc, + "item_find(item, servertag=None, type=-1, target=False, window=False) -> item or None\n" + "\n" + "Find item from log.\n" +); +static PyObject *PyLog_item_find(PyLog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", "servertag", "type", "target", "window", NULL}; + char *item = ""; + char *server = NULL; + int type = 0; + int target = 0; + int window = 0; + LOG_ITEM_REC *li; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ziii", kwlist, + &item, &server, &type, &target, &window)) + return NULL; + + if (!logtype(&type, target, window)) + return NULL; + + li = log_item_find(self->data, type, item, server); + if (li) + return pylogitem_new(li); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyLog_methods[] = { + {"items", (PyCFunction)PyLog_items, METH_NOARGS, + PyLog_items_doc}, + {"update", (PyCFunction)PyLog_update, METH_NOARGS, + PyLog_update_doc}, + {"close", (PyCFunction)PyLog_close, METH_NOARGS, + PyLog_close_doc}, + {"start_logging", (PyCFunction)PyLog_start_logging, METH_NOARGS, + PyLog_start_logging_doc}, + {"stop_logging", (PyCFunction)PyLog_stop_logging, METH_NOARGS, + PyLog_stop_logging_doc}, + {"item_add", (PyCFunction)PyLog_item_add, METH_VARARGS | METH_KEYWORDS, + PyLog_item_add_doc}, + {"item_destroy", (PyCFunction)PyLog_item_destroy, METH_VARARGS | METH_KEYWORDS, + PyLog_item_destroy_doc}, + {"item_find", (PyCFunction)PyLog_item_find, METH_VARARGS | METH_KEYWORDS, + PyLog_item_find_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyLogType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Log", /*tp_name*/ + sizeof(PyLog), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyLog_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + PyLog_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyLog_methods, /* tp_methods */ + 0, /* tp_members */ + PyLog_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)PyLog_init, /* tp_init */ + 0, /* tp_alloc */ + PyLog_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pylog_new(void *log) +{ + PyLog *pylog; + + pylog = (PyLog *)PyLogType.tp_alloc(&PyLogType, 0); + if (!pylog) + return NULL; + + pylog->data = log; + pylog->cleanup_installed = 1; + signal_add_last_data("log remove", log_cleanup, pylog); + + return (PyObject *)pylog; +} + +int log_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyLogType) < 0) + return 0; + + Py_INCREF(&PyLogType); + PyModule_AddObject(py_module, "Log", (PyObject *)&PyLogType); + + return 1; +} diff --git a/src/objects/log-object.h b/src/objects/log-object.h new file mode 100644 index 0000000..9893bc5 --- /dev/null +++ b/src/objects/log-object.h @@ -0,0 +1,21 @@ +#ifndef _LOG_OBJECT_H_ +#define _LOG_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _LOG_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct _LOG_REC) +} PyLog; + +extern PyTypeObject PyLogType; + +int log_object_init(void); +PyObject *pylog_new(void *log); +#define pylog_check(op) PyObject_TypeCheck(op, &PyLogType) + +#endif diff --git a/src/objects/logitem-object.c b/src/objects/logitem-object.c new file mode 100644 index 0000000..4dc2275 --- /dev/null +++ b/src/objects/logitem-object.c @@ -0,0 +1,156 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "logitem-object.h" +#include "pycore.h" + +/* no special cleanup -- value copy is made */ + +static void PyLogitem_dealloc(PyLogitem *self) +{ + Py_XDECREF(self->type); + Py_XDECREF(self->name); + Py_XDECREF(self->servertag); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyLogitem_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyLogitem *self; + + self = (PyLogitem *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyLogitem_type_doc, + "0=target, 1=window refnum" +); +static PyObject *PyLogitem_type_get(PyLogitem *self, void *closure) +{ + RET_AS_OBJ_OR_NONE(self->type); +} + +PyDoc_STRVAR(PyLogitem_name_doc, + "Name" +); +static PyObject *PyLogitem_name_get(PyLogitem *self, void *closure) +{ + RET_AS_OBJ_OR_NONE(self->name); +} + +PyDoc_STRVAR(PyLogitem_servertag_doc, + "Server tag" +); +static PyObject *PyLogitem_servertag_get(PyLogitem *self, void *closure) +{ + RET_AS_OBJ_OR_NONE(self->servertag); +} + +/* specialized getters/setters */ +static PyGetSetDef PyLogitem_getseters[] = { + {"type", (getter)PyLogitem_type_get, NULL, + PyLogitem_type_doc, NULL}, + {"name", (getter)PyLogitem_name_get, NULL, + PyLogitem_name_doc, NULL}, + {"servertag", (getter)PyLogitem_servertag_get, NULL, + PyLogitem_servertag_doc, NULL}, + {NULL} +}; + +/* Methods for object */ +static PyMethodDef PyLogitem_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyLogitemType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Logitem", /*tp_name*/ + sizeof(PyLogitem), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyLogitem_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyLogitem objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyLogitem_methods, /* tp_methods */ + 0, /* tp_members */ + PyLogitem_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyLogitem_new, /* tp_new */ +}; + + +/* log item factory function */ +PyObject *pylogitem_new(void *log) +{ + LOG_ITEM_REC *li = log; + PyLogitem *pylog = NULL; + + pylog = py_inst(PyLogitem, PyLogitemType); + if (!pylog) + return NULL; + + pylog->type = PyInt_FromLong(li->type); + if (!pylog->type) + goto error; + + pylog->name = PyString_FromString(li->name); + if (!pylog->name) + goto error; + + if (li->servertag) + { + pylog->servertag = PyString_FromString(li->servertag); + if (!pylog->servertag) + goto error; + } + + return (PyObject *)pylog; + +error: + Py_XDECREF(pylog); + return NULL; +} + +int logitem_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyLogitemType) < 0) + return 0; + + Py_INCREF(&PyLogitemType); + PyModule_AddObject(py_module, "Logitem", (PyObject *)&PyLogitemType); + + return 1; +} diff --git a/src/objects/logitem-object.h b/src/objects/logitem-object.h new file mode 100644 index 0000000..b7fd588 --- /dev/null +++ b/src/objects/logitem-object.h @@ -0,0 +1,21 @@ +#ifndef _LOG_ITEM_OBJECT_H_ +#define _LOG_ITEM_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyObject_HEAD + PyObject *type; + PyObject *name; + PyObject *servertag; +} PyLogitem; + +extern PyTypeObject PyLogitemType; + +int logitem_object_init(void); +PyObject *pylogitem_new(void *log); +#define pylogitem_check(op) PyObject_TypeCheck(op, &PyLogitemType) + +#endif diff --git a/src/objects/main-window-object.c b/src/objects/main-window-object.c new file mode 100644 index 0000000..6541f2b --- /dev/null +++ b/src/objects/main-window-object.c @@ -0,0 +1,199 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "main-window-object.h" +#include "factory.h" +#include "pycore.h" + +#define MW(data) ((MAIN_WINDOW_REC *) data) + +/* monitor "mainwindow destroyed" signal */ +static void main_window_cleanup(MAIN_WINDOW_REC *mw) +{ + PyMainWindow *pymw = signal_get_user_data(); + + if (mw == pymw->data) + { + pymw->data = NULL; + pymw->cleanup_installed = 0; + signal_remove_data("mainwindow destroyed", main_window_cleanup, pymw); + } +} + +static void PyMainWindow_dealloc(PyMainWindow *self) +{ + if (self->cleanup_installed) + signal_remove_data("mainwindow destroyed", main_window_cleanup, self); + + Py_XDECREF(self->active); + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyMainWindow_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyMainWindow *self; + + self = (PyMainWindow *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* getters */ +PyDoc_STRVAR(PyMainWindow_active_doc, + "active window object" +); +static PyObject *PyMainWindow_active_get(PyMainWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->active); +} + +PyDoc_STRVAR(PyMainWindow_first_line_doc, + "first line used by this window (0..x) (includes statusbars)" +); +static PyObject *PyMainWindow_first_line_get(PyMainWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(MW(self->data)->first_line); +} + +PyDoc_STRVAR(PyMainWindow_last_line_doc, + "last line used by this window (0..x) (includes statusbars)" +); +static PyObject *PyMainWindow_last_line_get(PyMainWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(MW(self->data)->last_line); +} + +PyDoc_STRVAR(PyMainWindow_width_doc, + "width of the window (includes statusbars)" +); +static PyObject *PyMainWindow_width_get(PyMainWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(MW(self->data)->width); +} + +PyDoc_STRVAR(PyMainWindow_height_doc, + "height of the window (includes statusbars)" +); +static PyObject *PyMainWindow_height_get(PyMainWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(MW(self->data)->height); +} + +PyDoc_STRVAR(PyMainWindow_statusbar_lines_doc, + "???" +); +static PyObject *PyMainWindow_statusbar_lines_get(PyMainWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(MW(self->data)->statusbar_lines); +} + +/* specialized getters/setters */ +static PyGetSetDef PyMainWindow_getseters[] = { + {"active", (getter)PyMainWindow_active_get, NULL, + PyMainWindow_active_doc, NULL}, + {"first_line", (getter)PyMainWindow_first_line_get, NULL, + PyMainWindow_first_line_doc, NULL}, + {"last_line", (getter)PyMainWindow_last_line_get, NULL, + PyMainWindow_last_line_doc, NULL}, + {"width", (getter)PyMainWindow_width_get, NULL, + PyMainWindow_width_doc, NULL}, + {"height", (getter)PyMainWindow_height_get, NULL, + PyMainWindow_height_doc, NULL}, + {"statusbar_lines", (getter)PyMainWindow_statusbar_lines_get, NULL, + PyMainWindow_statusbar_lines_doc, NULL}, + {NULL} +}; + +/* Methods for object */ +static PyMethodDef PyMainWindow_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyMainWindowType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "MainWindow", /*tp_name*/ + sizeof(PyMainWindow), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyMainWindow_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyMainWindow objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyMainWindow_methods, /* tp_methods */ + 0, /* tp_members */ + PyMainWindow_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyMainWindow_new, /* tp_new */ +}; + + +/* main window wrapper factory function */ +PyObject *pymain_window_new(MAIN_WINDOW_REC *mw) +{ + PyObject *pyactive = NULL; + PyMainWindow *pymw; + + pyactive = pywindow_new(mw->active); + if (!pyactive) + return NULL; + + pymw = py_inst(PyMainWindow, PyMainWindowType); + if (!pymw) + { + Py_DECREF(pyactive); + return NULL; + } + + pymw->active = pyactive; + pymw->data = mw; + pymw->cleanup_installed = 1; + signal_add_last_data("mainwindow destroyed", main_window_cleanup, pymw); + + return (PyObject *)pymw; +} + +int main_window_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyMainWindowType) < 0) + return 0; + + Py_INCREF(&PyMainWindowType); + PyModule_AddObject(py_module, "MainWindow", (PyObject *)&PyMainWindowType); + + return 1; +} diff --git a/src/objects/main-window-object.h b/src/objects/main-window-object.h new file mode 100644 index 0000000..fbbdac0 --- /dev/null +++ b/src/objects/main-window-object.h @@ -0,0 +1,20 @@ +#ifndef _MAIN_WINDOW_OBJECT_H_ +#define _MAIN_WINDOW_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" +#include "pyirssi.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) + PyObject *active; +} PyMainWindow; + +extern PyTypeObject PyMainWindowType; + +int main_window_object_init(void); +PyObject *pymain_window_new(MAIN_WINDOW_REC *mw); +#define pymain_window_check(op) PyObject_TypeCheck(op, &PyMainWindowType) + +#endif diff --git a/src/objects/netsplit-channel-object.c b/src/objects/netsplit-channel-object.c new file mode 100644 index 0000000..3e2639b --- /dev/null +++ b/src/objects/netsplit-channel-object.c @@ -0,0 +1,170 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "netsplit-channel-object.h" +#include "factory.h" +#include "pycore.h" + +/* value copied -- no special cleanup */ + +static void PyNetsplitChannel_dealloc(PyNetsplitChannel *self) +{ + Py_XDECREF(self->name); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyNetsplitChannel_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyNetsplitChannel *self; + + self = (PyNetsplitChannel *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyNetsplitChannel_name_doc, + "Channel name" +); +static PyObject *PyNetsplitChannel_name_get(PyNetsplitChannel *self, void *closure) +{ + RET_AS_OBJ_OR_NONE(self->name); +} + +PyDoc_STRVAR(PyNetsplitChannel_op_doc, + "is op" +); +static PyObject *PyNetsplitChannel_op_get(PyNetsplitChannel *self, void *closure) +{ + return PyBool_FromLong(self->op); +} + +PyDoc_STRVAR(PyNetsplitChannel_halfop_doc, + "is halfop" +); +static PyObject *PyNetsplitChannel_halfop_get(PyNetsplitChannel *self, void *closure) +{ + return PyBool_FromLong(self->halfop); +} + +PyDoc_STRVAR(PyNetsplitChannel_voice_doc, + "is voice" +); +static PyObject *PyNetsplitChannel_voice_get(PyNetsplitChannel *self, void *closure) +{ + return PyBool_FromLong(self->voice); +} + +PyDoc_STRVAR(PyNetsplitChannel_other_doc, + "?" +); +static PyObject *PyNetsplitChannel_other_get(PyNetsplitChannel *self, void *closure) +{ + return PyInt_FromLong(self->other); +} + +/* specialized getters/setters */ +static PyGetSetDef PyNetsplitChannel_getseters[] = { + {"name", (getter)PyNetsplitChannel_name_get, NULL, + PyNetsplitChannel_name_doc, NULL}, + {"op", (getter)PyNetsplitChannel_op_get, NULL, + PyNetsplitChannel_op_doc, NULL}, + {"halfop", (getter)PyNetsplitChannel_halfop_get, NULL, + PyNetsplitChannel_halfop_doc, NULL}, + {"voice", (getter)PyNetsplitChannel_voice_get, NULL, + PyNetsplitChannel_voice_doc, NULL}, + {"other", (getter)PyNetsplitChannel_other_get, NULL, + PyNetsplitChannel_other_doc, NULL}, + {NULL} +}; + +/* Methods */ +/* Methods for object */ +static PyMethodDef PyNetsplitChannel_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyNetsplitChannelType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "NetsplitChannel", /*tp_name*/ + sizeof(PyNetsplitChannel), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyNetsplitChannel_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyNetsplitChannel objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyNetsplitChannel_methods, /* tp_methods */ + 0, /* tp_members */ + PyNetsplitChannel_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyNetsplitChannel_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pynetsplit_channel_new(void *netsplit) +{ + NETSPLIT_CHAN_REC *rec = netsplit; + PyNetsplitChannel *pynetsplit; + PyObject *name; + + name = PyString_FromString(rec->name); + if (!name) + return NULL; + + pynetsplit = py_inst(PyNetsplitChannel, PyNetsplitChannelType); + if (!pynetsplit) + { + Py_DECREF(name); + return NULL; + } + + pynetsplit->name = name; + pynetsplit->op = rec->op; + pynetsplit->halfop = rec->halfop; + pynetsplit->other = rec->other; + + return (PyObject *)pynetsplit; +} + +int netsplit_channel_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyNetsplitChannelType) < 0) + return 0; + + Py_INCREF(&PyNetsplitChannelType); + PyModule_AddObject(py_module, "NetsplitChannel", (PyObject *)&PyNetsplitChannelType); + + return 1; +} diff --git a/src/objects/netsplit-channel-object.h b/src/objects/netsplit-channel-object.h new file mode 100644 index 0000000..3a00fbb --- /dev/null +++ b/src/objects/netsplit-channel-object.h @@ -0,0 +1,21 @@ +#ifndef _NETSPLIT_CHANNEL_OBJECT_H_ +#define _NETSPLIT_CHANNEL_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyObject_HEAD + PyObject *name; + int op, halfop; + int voice, other; +} PyNetsplitChannel; + +extern PyTypeObject PyNetsplitChannelType; + +int netsplit_channel_object_init(void); +PyObject *pynetsplit_channel_new(void *ns); +#define pynetsplit_channel_check(op) PyObject_TypeCheck(op, &PyNetsplitChannelType) + +#endif diff --git a/src/objects/netsplit-object.c b/src/objects/netsplit-object.c new file mode 100644 index 0000000..d64bd97 --- /dev/null +++ b/src/objects/netsplit-object.c @@ -0,0 +1,184 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "netsplit-object.h" +#include "factory.h" +#include "pycore.h" + +#define NETSPLIT(ns) ((NETSPLIT_REC*)ns) + +/* monitor "netsplit remove" signal */ +static void netsplit_cleanup(NETSPLIT_REC *netsplit) +{ + PyNetsplit *pynetsplit = signal_get_user_data(); + + if (netsplit == pynetsplit->data) + { + pynetsplit->data = NULL; + pynetsplit->cleanup_installed = 0; + signal_remove_data("netsplit remove", netsplit_cleanup, pynetsplit); + } +} + +static void PyNetsplit_dealloc(PyNetsplit *self) +{ + if (self->cleanup_installed) + signal_remove_data("netsplit remove", netsplit_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyNetsplit_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyNetsplit *self; + + self = (PyNetsplit *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyNetsplit_nick_doc, + "Nick" +); +static PyObject *PyNetsplit_nick_get(PyNetsplit *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(NETSPLIT(self->data)->nick); +} + +PyDoc_STRVAR(PyNetsplit_address_doc, + "Nick's host" +); +static PyObject *PyNetsplit_address_get(PyNetsplit *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(NETSPLIT(self->data)->address); +} + +PyDoc_STRVAR(PyNetsplit_destroy_doc, + "Timestamp when this record should be destroyed" +); +static PyObject *PyNetsplit_destroy_get(PyNetsplit *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(NETSPLIT(self->data)->destroy); +} + +PyDoc_STRVAR(PyNetsplit_server_doc, + "Netsplitserver object" +); +static PyObject *PyNetsplit_server_get(PyNetsplit *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->server); +} + +/* specialized getters/setters */ +static PyGetSetDef PyNetsplit_getseters[] = { + {"nick", (getter)PyNetsplit_nick_get, NULL, + PyNetsplit_nick_doc, NULL}, + {"address", (getter)PyNetsplit_address_get, NULL, + PyNetsplit_address_doc, NULL}, + {"destroy", (getter)PyNetsplit_destroy_get, NULL, + PyNetsplit_destroy_doc, NULL}, + {"server", (getter)PyNetsplit_server_get, NULL, + PyNetsplit_server_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyNetsplit_channels_doc, + "channels() -> list of NetsplitChannel objects\n" + "\n" + "Return list of NetsplitChannel objects\n" +); +static PyObject *PyNetsplit_channels(PyNetsplit *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + return py_irssi_objlist_new(NETSPLIT(self->data)->channels, 1, + (InitFunc)pynetsplit_channel_new); +} + +/* Methods for object */ +static PyMethodDef PyNetsplit_methods[] = { + {"channels", (PyCFunction)PyNetsplit_channels, METH_NOARGS, + PyNetsplit_channels_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyNetsplitType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Netsplit", /*tp_name*/ + sizeof(PyNetsplit), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyNetsplit_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyNetsplit objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyNetsplit_methods, /* tp_methods */ + 0, /* tp_members */ + PyNetsplit_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyNetsplit_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pynetsplit_new(void *netsplit) +{ + PyNetsplit *pynetsplit; + + //FIXME: add netsplit server + + pynetsplit = py_inst(PyNetsplit, PyNetsplitType); + if (!pynetsplit) + return NULL; + + pynetsplit->data = netsplit; + pynetsplit->cleanup_installed = 1; + signal_add_last_data("netsplit remove", netsplit_cleanup, pynetsplit); + + return (PyObject *)pynetsplit; +} + +int netsplit_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyNetsplitType) < 0) + return 0; + + Py_INCREF(&PyNetsplitType); + PyModule_AddObject(py_module, "Netsplit", (PyObject *)&PyNetsplitType); + + return 1; +} diff --git a/src/objects/netsplit-object.h b/src/objects/netsplit-object.h new file mode 100644 index 0000000..bd166e0 --- /dev/null +++ b/src/objects/netsplit-object.h @@ -0,0 +1,19 @@ +#ifndef _NETSPLIT_OBJECT_H_ +#define _NETSPLIT_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) + PyObject *server; +} PyNetsplit; + +extern PyTypeObject PyNetsplitType; + +int netsplit_object_init(void); +PyObject *pynetsplit_new(void *ns); +#define pynetsplit_check(op) PyObject_TypeCheck(op, &PyNetsplitType) + +#endif diff --git a/src/objects/netsplit-server-object.c b/src/objects/netsplit-server-object.c new file mode 100644 index 0000000..7208ae1 --- /dev/null +++ b/src/objects/netsplit-server-object.c @@ -0,0 +1,157 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "netsplit-object.h" +#include "factory.h" +#include "pycore.h" + +#define NETSPLIT_SERVER(ns) ((NETSPLIT_SERVER_REC*)ns) + +/* monitor "netsplit remove" signal */ +static void netsplit_server_cleanup(NETSPLIT_SERVER_REC *netsplit) +{ + PyNetsplitServer *pynetsplit = signal_get_user_data(); + + if (netsplit == pynetsplit->data) + { + pynetsplit->data = NULL; + pynetsplit->cleanup_installed = 0; + signal_remove_data("netsplit remove", netsplit_server_cleanup, pynetsplit); + } +} + +static void PyNetsplitServer_dealloc(PyNetsplitServer *self) +{ + if (self->cleanup_installed) + signal_remove_data("netsplit remove", netsplit_server_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyNetsplitServer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyNetsplitServer *self; + + self = (PyNetsplitServer *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyNetsplitServer_server_doc, + "The server nick was in" +); +static PyObject *PyNetsplitServer_server_get(PyNetsplitServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(NETSPLIT_SERVER(self->data)->server); +} + +PyDoc_STRVAR(PyNetsplitServer_destserver_doc, + "The other server where split occured." +); +static PyObject *PyNetsplitServer_destserver_get(PyNetsplitServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(NETSPLIT_SERVER(self->data)->destserver); +} + +PyDoc_STRVAR(PyNetsplitServer_count_doc, + "Number of splits in server" +); +static PyObject *PyNetsplitServer_count_get(PyNetsplitServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(NETSPLIT_SERVER(self->data)->count); +} + +/* specialized getters/setters */ +static PyGetSetDef PyNetsplitServer_getseters[] = { + {"server", (getter)PyNetsplitServer_server_get, NULL, + PyNetsplitServer_server_doc, NULL}, + {"destserver", (getter)PyNetsplitServer_destserver_get, NULL, + PyNetsplitServer_destserver_doc, NULL}, + {"count", (getter)PyNetsplitServer_count_get, NULL, + PyNetsplitServer_count_doc, NULL}, + {NULL} +}; + +/* Methods */ +/* Methods for object */ +static PyMethodDef PyNetsplitServer_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyNetsplitServerType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "NetsplitServer", /*tp_name*/ + sizeof(PyNetsplitServer), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyNetsplitServer_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyNetsplitServer objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyNetsplitServer_methods, /* tp_methods */ + 0, /* tp_members */ + PyNetsplitServer_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyNetsplitServer_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pynetsplit_server_new(void *nss) +{ + PyNetsplitServer *pynss; + + pynss = py_inst(PyNetsplitServer, PyNetsplitServerType); + if (!pynss) + return NULL; + + pynss->data = nss; + pynss->cleanup_installed = 1; + signal_add_last_data("netsplit server remove", netsplit_server_cleanup, pynss); + + return (PyObject *)pynss; +} + +int netsplit_server_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyNetsplitServerType) < 0) + return 0; + + Py_INCREF(&PyNetsplitServerType); + PyModule_AddObject(py_module, "NetsplitServer", (PyObject *)&PyNetsplitServerType); + + return 1; +} diff --git a/src/objects/netsplit-server-object.h b/src/objects/netsplit-server-object.h new file mode 100644 index 0000000..f1d5fbf --- /dev/null +++ b/src/objects/netsplit-server-object.h @@ -0,0 +1,18 @@ +#ifndef _NETSPLIT_SERVER_OBJECT_H_ +#define _NETSPLIT_SERVER_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) +} PyNetsplitServer; + +extern PyTypeObject PyNetsplitServerType; + +int netsplit_server_object_init(void); +PyObject *pynetsplit_server_new(void *nss); +#define pynetsplit_server_check(op) PyObject_TypeCheck(op, &PyNetsplitServerType) + +#endif diff --git a/src/objects/nick-object.c b/src/objects/nick-object.c new file mode 100644 index 0000000..79f3a85 --- /dev/null +++ b/src/objects/nick-object.c @@ -0,0 +1,237 @@ +#include <Python.h> +#include "pymodule.h" +#include "base-objects.h" +#include "nick-object.h" +#include "pyirssi.h" +#include "pycore.h" +#include "pyutils.h" + +static void nick_cleanup(CHANNEL_REC *chan, NICK_REC *nick) +{ + PyNick *pynick = signal_get_user_data(); + + if (nick == pynick->data) + { + pynick->data = NULL; + pynick->cleanup_installed = 0; + signal_remove_data("nicklist remove", nick_cleanup, pynick); + } +} + +static void PyNick_dealloc(PyNick *self) +{ + if (self->cleanup_installed) + signal_remove_data("nicklist remove", nick_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyNick_send_massjoin_doc, + "Waiting to be sent in a 'massjoin' signal, True or False" +); +static PyObject *PyNick_send_massjoin_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->send_massjoin); +} + +PyDoc_STRVAR(PyNick_nick_doc, + "Plain nick" +); +static PyObject *PyNick_nick_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->nick); +} + +PyDoc_STRVAR(PyNick_host_doc, + "Host address" +); +static PyObject *PyNick_host_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->host); +} + +PyDoc_STRVAR(PyNick_realname_doc, + "Real name" +); +static PyObject *PyNick_realname_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->realname); +} + +PyDoc_STRVAR(PyNick_hops_doc, + "Hop count to the server the nick is using" +); +static PyObject *PyNick_hops_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->hops); +} + +PyDoc_STRVAR(PyNick_gone_doc, + "User status" +); +static PyObject *PyNick_gone_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->gone); +} + +PyDoc_STRVAR(PyNick_serverop_doc, + "User status" +); +static PyObject *PyNick_serverop_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->serverop); +} + +PyDoc_STRVAR(PyNick_op_doc, + "User status" +); +static PyObject *PyNick_op_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->op); +} + +PyDoc_STRVAR(PyNick_voice_doc, + "User status" +); +static PyObject *PyNick_voice_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->voice); +} + +PyDoc_STRVAR(PyNick_halfop_doc, + "User status" +); +static PyObject *PyNick_halfop_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->halfop); +} + +PyDoc_STRVAR(PyNick_last_check_doc, + "timestamp when last checked gone/ircop status." +); +static PyObject *PyNick_last_check_get(PyNick *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(self->data->last_check); +} + +/* specialized getters/setters */ +static PyGetSetDef PyNick_getseters[] = { + {"send_massjoin", (getter)PyNick_send_massjoin_get, NULL, + PyNick_send_massjoin_doc, NULL}, + {"nick", (getter)PyNick_nick_get, NULL, + PyNick_nick_doc, NULL}, + {"host", (getter)PyNick_host_get, NULL, + PyNick_host_doc, NULL}, + {"realname", (getter)PyNick_realname_get, NULL, + PyNick_realname_doc, NULL}, + {"hops", (getter)PyNick_hops_get, NULL, + PyNick_hops_doc, NULL}, + {"gone", (getter)PyNick_gone_get, NULL, + PyNick_gone_doc, NULL}, + {"serverop", (getter)PyNick_serverop_get, NULL, + PyNick_serverop_doc, NULL}, + {"op", (getter)PyNick_op_get, NULL, + PyNick_op_doc, NULL}, + {"voice", (getter)PyNick_voice_get, NULL, + PyNick_voice_doc, NULL}, + {"halfop", (getter)PyNick_halfop_get, NULL, + PyNick_halfop_doc, NULL}, + {"last_check", (getter)PyNick_last_check_get, NULL, + PyNick_last_check_doc, NULL}, + {NULL} +}; + +static PyMethodDef PyNick_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyNickType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Nick", /*tp_name*/ + sizeof(PyNick), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyNick_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyNick objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyNick_methods, /* tp_methods */ + 0, /* tp_members */ + PyNick_getseters, /* tp_getset */ + &PyIrssiChatBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* nick factory function */ +PyObject *pynick_sub_new(void *nick, PyTypeObject *subclass) +{ + static const char *name = "NICK"; + PyNick *pynick = NULL; + + pynick = py_instp(PyNick, subclass); + if (!pynick) + return NULL; + + pynick->data = nick; + pynick->base_name = name; + signal_add_last_data("nicklist remove", nick_cleanup, pynick); + pynick->cleanup_installed = 1; + + return (PyObject *)pynick; +} + +PyObject *pynick_new(void *nick) +{ + return pynick_sub_new(nick, &PyNickType); +} + +int nick_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyNickType) < 0) + return 0; + + Py_INCREF(&PyNickType); + PyModule_AddObject(py_module, "Nick", (PyObject *)&PyNickType); + + return 1; +} diff --git a/src/objects/nick-object.h b/src/objects/nick-object.h new file mode 100644 index 0000000..6831994 --- /dev/null +++ b/src/objects/nick-object.h @@ -0,0 +1,22 @@ +#ifndef _NICK_OBJECT_H_ +#define _NICK_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _NICK_REC; + +typedef struct +{ + PyIrssi_HEAD(struct _NICK_REC) +} PyNick; + +extern PyTypeObject PyNickType; + +int nick_object_init(void); +PyObject *pynick_sub_new(void *nick, PyTypeObject *subclass); +PyObject *pynick_new(void *nick); +#define pynick_check(op) PyObject_TypeCheck(op, &PyNickType) + +#endif diff --git a/src/objects/notifylist-object.c b/src/objects/notifylist-object.c new file mode 100644 index 0000000..3366d0f --- /dev/null +++ b/src/objects/notifylist-object.c @@ -0,0 +1,221 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "notifylist-object.h" +#include "pycore.h" + +#define NOTIFYLIST(nl) ((NOTIFYLIST_REC *)nl) + +/* monitor "notifylist remove" signal */ +static void notifylist_cleanup(NOTIFYLIST_REC *notifylist) +{ + PyNotifylist *pynotifylist = signal_get_user_data(); + + if (notifylist == pynotifylist->data) + { + pynotifylist->data = NULL; + pynotifylist->cleanup_installed = 0; + signal_remove_data("notifylist remove", notifylist_cleanup, pynotifylist); + } +} + +static void PyNotifylist_dealloc(PyNotifylist *self) +{ + if (self->cleanup_installed) + signal_remove_data("notifylist remove", notifylist_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyNotifylist_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyNotifylist *self; + + self = (PyNotifylist *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyNotifylist_mask_doc, + "Notify nick mask" +); +static PyObject *PyNotifylist_mask_get(PyNotifylist *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(NOTIFYLIST(self->data)->mask); +} + +PyDoc_STRVAR(PyNotifylist_away_check_doc, + "Notify away status changes" +); +static PyObject *PyNotifylist_away_check_get(PyNotifylist *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(NOTIFYLIST(self->data)->away_check); +} + +PyDoc_STRVAR(PyNotifylist_idle_check_time_doc, + "Notify when idle time is reset and idle was bigger than this (seconds)" +); +static PyObject *PyNotifylist_idle_check_time_get(PyNotifylist *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(NOTIFYLIST(self->data)->idle_check_time); +} + +/* specialized getters/setters */ +static PyGetSetDef PyNotifylist_getseters[] = { + {"mask", (getter)PyNotifylist_mask_get, NULL, + PyNotifylist_mask_doc, NULL}, + {"away_check", (getter)PyNotifylist_away_check_get, NULL, + PyNotifylist_away_check_doc, NULL}, + {"idle_check_time", (getter)PyNotifylist_idle_check_time_get, NULL, + PyNotifylist_idle_check_time_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyNotifylist_ircnets_doc, + "ircnets() -> list of str\n" + "\n" + "Return list of ircnets the notify is checked\n" +); +static PyObject *PyNotifylist_ircnets(PyNotifylist *self, PyObject *args) +{ + PyObject *list; + char **nets; + + RET_NULL_IF_INVALID(self->data); + + nets = NOTIFYLIST(self->data)->ircnets; + list = PyList_New(0); + if (!list) + return NULL; + + while (nets && *nets) + { + int ret; + PyObject *str = PyString_FromString(*nets); + + if (!str) + { + Py_DECREF(list); + return NULL; + } + + ret = PyList_Append(list, str); + Py_DECREF(str); + if (ret != 0) + { + Py_DECREF(list); + return NULL; + } + + nets++; + } + + return list; +} + +PyDoc_STRVAR(PyNotifylist_ircnets_match_doc, + "ircnets_match(ircnet) -> bool\n" + "\n" + "Return True if notify is checked in ircnet\n" +); +static PyObject *PyNotifylist_ircnets_match(PyNotifylist *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"ircnet", NULL}; + char *ircnet = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &ircnet)) + return NULL; + + return PyBool_FromLong(notifylist_ircnets_match(self->data, ircnet)); +} + +/* Methods for object */ +static PyMethodDef PyNotifylist_methods[] = { + {"ircnets", (PyCFunction)PyNotifylist_ircnets, METH_NOARGS, + PyNotifylist_ircnets_doc}, + {"ircnets_match", (PyCFunction)PyNotifylist_ircnets_match, METH_VARARGS | METH_KEYWORDS, + PyNotifylist_ircnets_match_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyNotifylistType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Notifylist", /*tp_name*/ + sizeof(PyNotifylist), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyNotifylist_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyNotifylist objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyNotifylist_methods, /* tp_methods */ + 0, /* tp_members */ + PyNotifylist_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyNotifylist_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pynotifylist_new(void *notifylist) +{ + PyNotifylist *pynotifylist; + + pynotifylist = py_inst(PyNotifylist, PyNotifylistType); + if (!pynotifylist) + return NULL; + + pynotifylist->data = notifylist; + pynotifylist->cleanup_installed = 1; + signal_add_last_data("notifylist remove", notifylist_cleanup, pynotifylist); + + return (PyObject *)pynotifylist; +} + +int notifylist_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyNotifylistType) < 0) + return 0; + + Py_INCREF(&PyNotifylistType); + PyModule_AddObject(py_module, "Notifylist", (PyObject *)&PyNotifylistType); + + return 1; +} diff --git a/src/objects/notifylist-object.h b/src/objects/notifylist-object.h new file mode 100644 index 0000000..9db6922 --- /dev/null +++ b/src/objects/notifylist-object.h @@ -0,0 +1,18 @@ +#ifndef _NOTIFYLIST_OBJECT_H_ +#define _NOTIFYLIST_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) +} PyNotifylist; + +extern PyTypeObject PyNotifylistType; + +int notifylist_object_init(void); +PyObject *pynotifylist_new(void *notifylist); +#define pynotifylist_check(op) PyObject_TypeCheck(op, &PyNotifylistType) + +#endif diff --git a/src/objects/process-object.c b/src/objects/process-object.c new file mode 100644 index 0000000..43c0fd7 --- /dev/null +++ b/src/objects/process-object.c @@ -0,0 +1,222 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "process-object.h" +#include "pycore.h" + +/* monitor "exec remove" signal */ +static void process_cleanup(PROCESS_REC *process, int status) +{ + PyProcess *pyprocess = signal_get_user_data(); + + if (process == pyprocess->data) + { + pyprocess->data = NULL; + pyprocess->cleanup_installed = 0; + signal_remove_data("exec remove", process_cleanup, pyprocess); + } +} + +static void PyProcess_dealloc(PyProcess *self) +{ + if (self->cleanup_installed) + signal_remove_data("exec remove", process_cleanup, self); + + Py_XDECREF(self->target_win); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyProcess_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyProcess *self; + + self = (PyProcess *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyProcess_id_doc, + "ID for the process" +); +static PyObject *PyProcess_id_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->id); +} + +PyDoc_STRVAR(PyProcess_name_doc, + "Name for the process (if given)" +); +static PyObject *PyProcess_name_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->name); +} + +PyDoc_STRVAR(PyProcess_args_doc, + "The command that is being executed" +); +static PyObject *PyProcess_args_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->args); +} + +PyDoc_STRVAR(PyProcess_pid_doc, + "PID for the executed command" +); +static PyObject *PyProcess_pid_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->pid); +} + +PyDoc_STRVAR(PyProcess_target_doc, + "send text with /msg <target> ..." +); +static PyObject *PyProcess_target_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->target); +} + +PyDoc_STRVAR(PyProcess_target_win_doc, + "print text to this window" +); +static PyObject *PyProcess_target_win_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->target_win); +} + +PyDoc_STRVAR(PyProcess_shell_doc, + "start the program via /bin/sh" +); +static PyObject *PyProcess_shell_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->shell); +} + +PyDoc_STRVAR(PyProcess_notice_doc, + "send text with /notice, not /msg if target is set" +); +static PyObject *PyProcess_notice_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->notice); +} + +PyDoc_STRVAR(PyProcess_silent_doc, + "don't print \"process exited with level xx\"" +); +static PyObject *PyProcess_silent_get(PyProcess *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->silent); +} + +/* specialized getters/setters */ +static PyGetSetDef PyProcess_getseters[] = { + {"id", (getter)PyProcess_id_get, NULL, + PyProcess_id_doc, NULL}, + {"name", (getter)PyProcess_name_get, NULL, + PyProcess_name_doc, NULL}, + {"args", (getter)PyProcess_args_get, NULL, + PyProcess_args_doc, NULL}, + {"pid", (getter)PyProcess_pid_get, NULL, + PyProcess_pid_doc, NULL}, + {"target", (getter)PyProcess_target_get, NULL, + PyProcess_target_doc, NULL}, + {"target_win", (getter)PyProcess_target_win_get, NULL, + PyProcess_target_win_doc, NULL}, + {"shell", (getter)PyProcess_shell_get, NULL, + PyProcess_shell_doc, NULL}, + {"notice", (getter)PyProcess_notice_get, NULL, + PyProcess_notice_doc, NULL}, + {"silent", (getter)PyProcess_silent_get, NULL, + PyProcess_silent_doc, NULL}, + {NULL} +}; + +/* Methods */ +/* Methods for object */ +static PyMethodDef PyProcess_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyProcessType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Process", /*tp_name*/ + sizeof(PyProcess), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyProcess_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyProcess objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyProcess_methods, /* tp_methods */ + 0, /* tp_members */ + PyProcess_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyProcess_new, /* tp_new */ +}; + + +/* process factory function */ +PyObject *pyprocess_new(void *process) +{ + PyProcess *pyprocess; + + pyprocess = py_inst(PyProcess, PyProcessType); + if (!pyprocess) + return NULL; + + pyprocess->data = process; + pyprocess->cleanup_installed = 1; + signal_add_last_data("exec remove", process_cleanup, pyprocess); + + return (PyObject *)pyprocess; +} + +int process_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyProcessType) < 0) + return 0; + + Py_INCREF(&PyProcessType); + PyModule_AddObject(py_module, "Process", (PyObject *)&PyProcessType); + + return 1; +} diff --git a/src/objects/process-object.h b/src/objects/process-object.h new file mode 100644 index 0000000..041f187 --- /dev/null +++ b/src/objects/process-object.h @@ -0,0 +1,22 @@ +#ifndef _PROCESS_OBJECT_H_ +#define _PROCESS_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct PROCESS_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct PROCESS_REC) + PyObject *target_win; +} PyProcess; + +extern PyTypeObject PyProcessType; + +int process_object_init(void); +PyObject *pyprocess_new(void *process); +#define pyprocess_check(op) PyObject_TypeCheck(op, &PyProcessType) + +#endif diff --git a/src/objects/pyscript-object.c b/src/objects/pyscript-object.c new file mode 100644 index 0000000..ae4770e --- /dev/null +++ b/src/objects/pyscript-object.c @@ -0,0 +1,801 @@ +#include <Python.h> +#include <structmember.h> +#include "pyscript-object.h" +#include "pyirssi.h" +#include "pysignals.h" +#include "pymodule.h" +#include "pysource.h" +#include "pythemes.h" +#include "pystatusbar.h" + +/* handle cycles... + Can't think of any reason why the user would put script into one of the lists + but who knows. Call GC after unloading module. +*/ +static int PyScript_traverse(PyScript *self, visitproc visit, void *arg) +{ + Py_VISIT(self->module); + Py_VISIT(self->argv); + Py_VISIT(self->modules); + + return 0; +} + +static int PyScript_clear(PyScript *self) +{ + Py_CLEAR(self->module); + Py_CLEAR(self->argv); + Py_CLEAR(self->modules); + + return 0; +} + +static void PyScript_dealloc(PyScript* self) +{ + PyScript_clear(self); + pyscript_remove_signals((PyObject*)self); + pyscript_remove_sources((PyObject*)self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyScript_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyScript *self; + PyObject *argv = NULL, *modules = NULL; + + argv = PyList_New(0); + if (!argv) + goto error; + + modules = PyDict_New(); + if (!modules) + goto error; + + self = (PyScript *)type->tp_alloc(type, 0); + if (!self) + goto error; + + self->argv = argv; + self->modules = modules; + + return (PyObject *)self; + +error: + Py_XDECREF(argv); + Py_XDECREF(modules); + return NULL; +} + +PyDoc_STRVAR(PyScript_command_bind_doc, + "command_bind(command, func, catetory=None, priority=SIGNAL_PRIORITY_DEFAULT) -> None\n" + "\n" + "Add handler for a command\n" +); +static PyObject *PyScript_command_bind(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", "func", "category", "priority", NULL}; + char *cmd; + PyObject *func; + char *category = NULL; + int priority = SIGNAL_PRIORITY_DEFAULT; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|zi", kwlist, + &cmd, &func, &category, &priority)) + return NULL; + + if (!PyCallable_Check(func)) + return PyErr_Format(PyExc_TypeError, "func must be callable"); + + if (!pysignals_command_bind_list(&self->signals, cmd, func, category, priority)) + return PyErr_Format(PyExc_RuntimeError, "unable to bind command"); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_signal_add_doc, + "signal_add(signal, func, priority=SIGNAL_PRIORITY_DEFAULT) -> None\n" + "\n" + "Add handler for signal" +); +static PyObject *PyScript_signal_add(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"signal", "func", "priority", NULL}; + char *signal; + PyObject *func; + int priority = SIGNAL_PRIORITY_DEFAULT; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|i", kwlist, + &signal, &func, &priority)) + return NULL; + + if (!PyCallable_Check(func)) + return PyErr_Format(PyExc_TypeError, "func must be callable"); + + if (!pysignals_signal_add_list(&self->signals, signal, func, priority)) + return PyErr_Format(PyExc_KeyError, "unable to find signal, '%s'", signal); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_signal_remove_doc, + "signal_remove(signal, func=None) -> None\n" + "\n" + "Remove signal handler\n" +); +static PyObject *PyScript_signal_remove(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"signal", "func", NULL}; + char *signal = ""; + PyObject *func = Py_None; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist, + &signal, &func)) + return NULL; + + if (!PyCallable_Check(func) && func != Py_None) + return PyErr_Format(PyExc_TypeError, "func must be callable or None"); + + if (func == Py_None) + func = NULL; + + if (!pysignals_remove_search(&self->signals, signal, func, PSG_SIGNAL)) + return PyErr_Format(PyExc_KeyError, "can't find signal"); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_command_unbind_doc, + "command_unbind(command, func=None) -> None\n" + "\n" + "Remove command handler\n" +); +static PyObject *PyScript_command_unbind(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"command", "func", NULL}; + char *command = ""; + PyObject *func = Py_None; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist, + &command, &func)) + return NULL; + + if (!PyCallable_Check(func) && func != Py_None) + return PyErr_Format(PyExc_TypeError, "func must be callable or None"); + + if (func == Py_None) + func = NULL; + + if (!pysignals_remove_search(&self->signals, command, func, PSG_COMMAND)) + return PyErr_Format(PyExc_KeyError, "can't find command"); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_signal_register_doc, + "signal_register(signal, arglist) -> None\n" + "\n" + "Register a new dynamic signal for use with irssi_python\n" + "arglist is a string of character codes representing the type of each argument\n" + "of the signal handler function.\n" + "\n" + " Scalars\n" + " s -> char *\n" + " i -> int\n" + "\n" + " Chat objects\n" + " c -> CHATNET_REC\n" + " S -> SERVER_REC\n" + " C -> CHANNEL_REC\n" + " q -> QUERY_REC\n" + " n -> NICK_REC\n" + " W -> WI_ITEM_REC\n" + "\n" + " Irssi objects\n" + " d -> DCC_REC\n" + "\n" + " Other objects\n" + " r -> RECONNECT_REC\n" + " o -> COMMAND_REC\n" + " l -> LOG_REC\n" + " a -> RAWLOG_REC\n" + " g -> IGNORE_REC\n" + " b -> BAN_REC\n" + " N -> NETSPLIT_REC\n" + " e -> NETSPLIT_SERVER_REC\n" + " O -> NOTIFYLIST_REC\n" + " p -> PROCESS_REC\n" + " t -> TEXT_DEST_REC\n" + " w -> WINDOW_REC\n" +); +static PyObject *PyScript_signal_register(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"signal", "arglist", NULL}; + static const char *good_codes = "sicSCqnWdrolagbNeOptw"; + char *signal = ""; + char *arglist = ""; + int i; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &signal, &arglist)) + return NULL; + + for (i = 0; arglist[i]; i++) + if (!strchr(good_codes, arglist[i])) + return PyErr_Format(PyExc_TypeError, "invalid code, %c", arglist[i]); + + if (i >= SIGNAL_MAX_ARGUMENTS) + return PyErr_Format(PyExc_TypeError, + "arglist greater than SIGNAL_MAX_ARGUMENTS (%d)", + SIGNAL_MAX_ARGUMENTS); + + if (!pysignals_register(signal, arglist)) + return PyErr_Format(PyExc_TypeError, "signal present with different args"); + + self->registered_signals = g_slist_append(self->registered_signals, + g_strdup(signal)); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_signal_unregister_doc, + "signal_unregister(signal) -> None\n" + "\n" + "Unregister dynamic signal\n" +); +static PyObject *PyScript_signal_unregister(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"signal", NULL}; + char *signal = ""; + GSList *search; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &signal)) + return NULL; + + search = g_slist_find_custom(self->registered_signals, signal, (GCompareFunc)strcmp); + if (!search) + return PyErr_Format(PyExc_KeyError, "script has not registered that signal"); + + g_free(search->data); + self->registered_signals = g_slist_delete_link(self->registered_signals, search); + + if (!pysignals_unregister(signal)) + return PyErr_Format(PyExc_SystemError, + "script registered signal, but signal does not exist"); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_timeout_add_doc, + "timeout_add(msecs, func, data=None) -> int source tag\n" + "\n" + "Add a timeout handler called every 'msecs' milliseconds until func\n" + "returns False or the source is removed with source_remove().\n" + "\n" + "func is called as func(data) or func(), depending on whether data\n" + "is specified or not.\n" +); +static PyObject *PyScript_timeout_add(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"msecs", "func", "data", NULL}; + int msecs = 0; + PyObject *func = NULL; + PyObject *data = NULL; + int ret; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "iO|O", kwlist, + &msecs, &func, &data)) + return NULL; + + if (msecs < 10) + return PyErr_Format(PyExc_ValueError, "msecs must be at least 10"); + + if (!PyCallable_Check(func)) + return PyErr_Format(PyExc_TypeError, "func not callable"); + + ret = pysource_timeout_add_list(&self->sources, msecs, func, data); + + return PyInt_FromLong(ret); +} + +PyDoc_STRVAR(PyScript_io_add_watch_doc, + "io_add_watch(fd, func, data=None, condition=IO_IN|IO_PRI) -> int source tag\n" +); +static PyObject *PyScript_io_add_watch(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"fd", "func", "data", "condition", NULL}; + int fd = 0; + PyObject *pyfd = NULL; + PyObject *func = NULL; + PyObject *data = NULL; + int condition = G_IO_IN | G_IO_PRI; + int ret; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|Oi", kwlist, + &pyfd, &func, &data, &condition)) + return NULL; + + fd = PyObject_AsFileDescriptor(pyfd); + if (fd < 0) + return NULL; + + if (!PyCallable_Check(func)) + return PyErr_Format(PyExc_TypeError, "func not callable"); + + ret = pysource_io_add_watch_list(&self->sources, fd, condition, func, data); + + return PyInt_FromLong(ret); +} + +PyDoc_STRVAR(PyScript_source_remove_doc, + "source_remove(tag) -> bool\n" + "\n" + "Remove IO or timeout source by tag. Return True if tag found and removed.\n" +); +static PyObject *PyScript_source_remove(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"tag", NULL}; + int tag = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &tag)) + return NULL; + + /* the destroy notify func will remove the list link, but first + check that the tag exists in this Script object */ + if (g_slist_find(self->sources, GINT_TO_POINTER(tag))) + return PyBool_FromLong(g_source_remove(tag)); + + Py_RETURN_FALSE; +} + +static int py_settings_add(PyScript *self, const char *name) +{ + GSList *node; + + node = gslist_find_icase_string(self->settings, name); + if (node) + return 0; + + self->settings = g_slist_append(self->settings, g_strdup(name)); + + return 1; +} + +static int py_settings_remove(PyScript *self, const char *name) +{ + GSList *node; + + node = gslist_find_icase_string(self->settings, name); + if (!node) + return 0; + + settings_remove(node->data); + g_free(node->data); + + self->settings = g_slist_delete_link(self->settings, node); + + return 1; +} + +PyDoc_STRVAR(PyScript_settings_add_str_doc, + "settings_add_str(section, key, def) -> None\n" +); +static PyObject *PyScript_settings_add_str(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"section", "key", "def", NULL}; + char *section = ""; + char *key = ""; + char *def = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + §ion, &key, &def)) + return NULL; + + if (!py_settings_add(self, key)) + return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key); + + settings_add_str_module(MODULE_NAME"/scripts", section, key, def); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_settings_add_int_doc, + "settings_add_int(section, key, def) -> None\n" +); +static PyObject *PyScript_settings_add_int(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"section", "key", "def", NULL}; + char *section = ""; + char *key = ""; + int def = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssi", kwlist, + §ion, &key, &def)) + return NULL; + + if (!py_settings_add(self, key)) + return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key); + + settings_add_int_module(MODULE_NAME"/scripts", section, key, def); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_settings_add_bool_doc, + "settings_add_bool(section, key, def) -> None\n" +); +static PyObject *PyScript_settings_add_bool(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"section", "key", "def", NULL}; + char *section = ""; + char *key = ""; + int def = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssi", kwlist, + §ion, &key, &def)) + return NULL; + + if (!py_settings_add(self, key)) + return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key); + + settings_add_bool_module(MODULE_NAME"/scripts", section, key, def); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_settings_add_time_doc, + "settings_add_time(section, key, def) -> None\n" +); +static PyObject *PyScript_settings_add_time(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"section", "key", "def", NULL}; + char *section = ""; + char *key = ""; + char *def = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + §ion, &key, &def)) + return NULL; + + if (!py_settings_add(self, key)) + return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key); + + settings_add_time_module(MODULE_NAME"/scripts", section, key, def); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_settings_add_level_doc, + "settings_add_level(section, key, def) -> None\n" +); +static PyObject *PyScript_settings_add_level(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"section", "key", "def", NULL}; + char *section = ""; + char *key = ""; + char *def = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + §ion, &key, &def)) + return NULL; + + if (!py_settings_add(self, key)) + return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key); + + settings_add_level_module(MODULE_NAME"/scripts", section, key, def); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_settings_add_size_doc, + "settings_add_size(section, key, def) -> None\n" +); +static PyObject *PyScript_settings_add_size(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"section", "key", "def", NULL}; + char *section = ""; + char *key = ""; + char *def = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + §ion, &key, &def)) + return NULL; + + if (!py_settings_add(self, key)) + return PyErr_Format(PyExc_ValueError, "key, %s, already added by script", key); + + settings_add_size_module(MODULE_NAME"/scripts", section, key, def); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_settings_remove_doc, + "settings_remove(key) -> bool\n" +); +static PyObject *PyScript_settings_remove(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"key", NULL}; + char *key = ""; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &key)) + return NULL; + + return PyBool_FromLong(py_settings_remove(self, key)); +} + +PyDoc_STRVAR(PyScript_theme_register_doc, + "theme_register(list) -> None\n" +); +static PyObject *PyScript_theme_register(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"list", NULL}; + PyObject *list = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &list)) + return NULL; + + if (!pythemes_register(pyscript_get_name(self), list)) + return NULL; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyScript_statusbar_item_register_doc, + "statusbar_item_register(name, value=None, func=None) -> None\n" +); +static PyObject *PyScript_statusbar_item_register(PyScript *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", "value", "func", NULL}; + char *name = ""; + char *value = NULL; + PyObject *func = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|zO", kwlist, + &name, &value, &func)) + return NULL; + + pystatusbar_item_register((PyObject *)self, name, value, func); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyScript_methods[] = { + {"command_bind", (PyCFunction)PyScript_command_bind, METH_VARARGS | METH_KEYWORDS, + PyScript_command_bind_doc}, + {"signal_add", (PyCFunction)PyScript_signal_add, METH_VARARGS | METH_KEYWORDS, + PyScript_signal_add_doc}, + {"signal_remove", (PyCFunction)PyScript_signal_remove, METH_VARARGS | METH_KEYWORDS, + PyScript_signal_remove_doc}, + {"command_unbind", (PyCFunction)PyScript_command_unbind, METH_VARARGS | METH_KEYWORDS, + PyScript_command_unbind_doc}, + {"signal_register", (PyCFunction)PyScript_signal_register, METH_VARARGS | METH_KEYWORDS, + PyScript_signal_register_doc}, + {"signal_unregister", (PyCFunction)PyScript_signal_unregister, METH_VARARGS | METH_KEYWORDS, + PyScript_signal_unregister_doc}, + {"timeout_add", (PyCFunction)PyScript_timeout_add, METH_VARARGS | METH_KEYWORDS, + PyScript_timeout_add_doc}, + {"io_add_watch", (PyCFunction)PyScript_io_add_watch, METH_VARARGS | METH_KEYWORDS, + PyScript_io_add_watch_doc}, + {"source_remove", (PyCFunction)PyScript_source_remove, METH_VARARGS | METH_KEYWORDS, + PyScript_source_remove_doc}, + {"settings_add_str", (PyCFunction)PyScript_settings_add_str, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_add_str_doc}, + {"settings_add_int", (PyCFunction)PyScript_settings_add_int, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_add_int_doc}, + {"settings_add_bool", (PyCFunction)PyScript_settings_add_bool, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_add_bool_doc}, + {"settings_add_time", (PyCFunction)PyScript_settings_add_time, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_add_time_doc}, + {"settings_add_level", (PyCFunction)PyScript_settings_add_level, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_add_level_doc}, + {"settings_add_size", (PyCFunction)PyScript_settings_add_size, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_add_size_doc}, + {"settings_remove", (PyCFunction)PyScript_settings_remove, METH_VARARGS | METH_KEYWORDS, + PyScript_settings_remove_doc}, + {"theme_register", (PyCFunction)PyScript_theme_register, METH_VARARGS | METH_KEYWORDS, + PyScript_theme_register_doc}, + {"statusbar_item_register", (PyCFunction)PyScript_statusbar_item_register, METH_VARARGS | METH_KEYWORDS, + PyScript_statusbar_item_register_doc}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef PyScript_members[] = { + {"argv", T_OBJECT, offsetof(PyScript, argv), 0, "Script arguments"}, + {"module", T_OBJECT_EX, offsetof(PyScript, module), RO, "Script module"}, + {"modules", T_OBJECT_EX, offsetof(PyScript, modules), 0, "Imported modules"}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyScriptType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Script", /*tp_name*/ + sizeof(PyScript), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyScript_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + "PyScript objects", /* tp_doc */ + (traverseproc)PyScript_traverse, /* tp_traverse */ + (inquiry)PyScript_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyScript_methods, /* tp_methods */ + PyScript_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyScript_new, /* tp_new */ +}; + +/* PyScript factory function */ +PyObject *pyscript_new(PyObject *module, char **argv) +{ + PyObject *script; + + script = PyObject_CallFunction((PyObject*)&PyScriptType, "()"); + + if (script) + { + PyScript *scr = (PyScript *)script; + + while (*argv) + { + if (**argv != '\0') + { + PyObject *str = PyString_FromString(*argv); + if (!str) + { + /* The destructor should DECREF argv */ + Py_DECREF(script); + return NULL; + } + + PyList_Append(scr->argv, str); + Py_DECREF(str); + } + + *argv++; + } + + Py_INCREF(module); + scr->module = module; + } + + return script; +} + +void pyscript_remove_signals(PyObject *script) +{ + GSList *node; + PyScript *self; + + g_return_if_fail(pyscript_check(script)); + + self = (PyScript *) script; + + /* remove bound signals */ + pysignals_remove_list(self->signals); + g_slist_free(self->signals); + self->signals = NULL; + + /* remove registered signals */ + for (node = self->registered_signals; node; node = node->next) + { + pysignals_unregister(node->data); + g_free(node->data); + } + + g_slist_free(self->registered_signals); + self->registered_signals = NULL; +} + +void pyscript_remove_sources(PyObject *script) +{ + GSList *node; + PyScript *self; + + g_return_if_fail(pyscript_check(script)); + + self = (PyScript *) script; + + node = self->sources; + while (node) + { + /* the notify func will destroy the link so save next */ + GSList *next = node->next; + g_source_remove(GPOINTER_TO_INT(node->data)); + node = next; + } + + g_return_if_fail(self->sources == NULL); +} + +void pyscript_remove_settings(PyObject *script) +{ + PyScript *self; + + g_return_if_fail(pyscript_check(script)); + + self = (PyScript *) script; + + g_slist_foreach(self->settings, (GFunc)settings_remove, NULL); + g_slist_foreach(self->settings, (GFunc)g_free, NULL); + g_slist_free(self->settings); +} + +void pyscript_remove_themes(PyObject *script) +{ + PyScript *self; + + g_return_if_fail(pyscript_check(script)); + + self = (PyScript *) script; + + pythemes_unregister(pyscript_get_name(script)); +} + +void pyscript_remove_statusbars(PyObject *script) +{ + g_return_if_fail(pyscript_check(script)); + + pystatusbar_cleanup_script(script); +} + +void pyscript_clear_modules(PyObject *script) +{ + PyScript *self; + + g_return_if_fail(pyscript_check(script)); + + self = (PyScript *) script; + + PyDict_Clear(self->modules); +} + +void pyscript_cleanup(PyObject *script) +{ + pyscript_remove_signals(script); + pyscript_remove_sources(script); + pyscript_remove_settings(script); + pyscript_remove_themes(script); + pyscript_remove_statusbars(script); + pyscript_clear_modules(script); +} + +int pyscript_init(void) +{ + if (PyType_Ready(&PyScriptType) < 0) + return 0; + + Py_INCREF(&PyScriptType); + PyModule_AddObject(py_module, "Script", (PyObject *)&PyScriptType); + + return 1; +} + diff --git a/src/objects/pyscript-object.h b/src/objects/pyscript-object.h new file mode 100644 index 0000000..624a578 --- /dev/null +++ b/src/objects/pyscript-object.h @@ -0,0 +1,33 @@ +#ifndef _PYSCRIPT_OBJECT_H_ +#define _PYSCRIPT_OBJECT_H_ +#include <Python.h> +#include <glib.h> + +typedef struct { + PyObject_HEAD + PyObject *module; /* module object */ + PyObject *argv; /* list of argument strings from the load command */ + PyObject *modules; /* dict of imported modules for script */ + GSList *signals; /* list of bound signals and commands */ + GSList *registered_signals; /* list of signal names registered */ + GSList *sources; /* list of io and timeout sources */ + GSList *settings; /* list of settings from settings_add_*() */ +} PyScript; + +extern PyTypeObject PyScriptType; + +int pyscript_init(void); +PyObject *pyscript_new(PyObject *module, char **argv); +void pyscript_remove_signals(PyObject *script); +void pyscript_remove_sources(PyObject *script); +void pyscript_remove_settings(PyObject *script); +void pyscript_remove_themes(PyObject *script); +void pyscript_remove_statusbars(PyObject *script); +void pyscript_clear_modules(PyObject *script); +void pyscript_cleanup(PyObject *script); +#define pyscript_check(op) PyObject_TypeCheck(op, &PyScriptType) +#define pyscript_get_name(scr) PyModule_GetName(((PyScript*)scr)->module) +#define pyscript_get_filename(scr) PyModule_GetFilename(((PyScript*)scr)->module) +#define pyscript_get_module(scr) (((PyScript*)scr)->module) + +#endif diff --git a/src/objects/query-object.c b/src/objects/query-object.c new file mode 100644 index 0000000..47badd5 --- /dev/null +++ b/src/objects/query-object.c @@ -0,0 +1,172 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "base-objects.h" +#include "window-item-object.h" +#include "query-object.h" +#include "server-object.h" +#include "pycore.h" + +/* monitor "query destroyed" signal */ +static void query_cleanup(QUERY_REC *query) +{ + PyQuery *pyquery = signal_get_user_data(); + + if (query == pyquery->data) + { + pyquery->data = NULL; + pyquery->cleanup_installed = 0; + signal_remove_data("query destroyed", query_cleanup, pyquery); + } +} + +static void PyQuery_dealloc(PyQuery *self) +{ + if (self->cleanup_installed) + signal_remove_data("query destroyed", query_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyQuery_address_doc, + "Host address of the queries nick" +); +static PyObject *PyQuery_address_get(PyQuery *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->address); +} + +PyDoc_STRVAR(PyQuery_server_tag_doc, + "Server tag used for this nick (doesn't get erased if server gets disconnected)" +); +static PyObject *PyQuery_server_tag_get(PyQuery *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->server_tag); +} + +PyDoc_STRVAR(PyQuery_unwanted_doc, + "1 if the other side closed or some error occured (DCC chats)" +); +static PyObject *PyQuery_unwanted_get(PyQuery *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->unwanted); +} + +/* specialized getters/setters */ +static PyGetSetDef PyQuery_getseters[] = { + {"address", (getter)PyQuery_address_get, NULL, + PyQuery_address_doc, NULL}, + {"server_tag", (getter)PyQuery_server_tag_get, NULL, + PyQuery_server_tag_doc, NULL}, + {"unwanted", (getter)PyQuery_unwanted_get, NULL, + PyQuery_unwanted_doc, NULL}, + {NULL} +}; + +PyDoc_STRVAR(change_server_doc, + "change_server(server) -> None\n" + "\n" + "Change the active server for the query.\n" +); +static PyObject *PyQuery_change_server(PyQuery *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"server", NULL}; + PyObject *server; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &server)) + return NULL; + + if (!pyserver_check(server)) + return PyErr_Format(PyExc_TypeError, "argument must be server object"); + + query_change_server(self->data, ((PyServer*)server)->data); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyQuery_methods[] = { + {"change_server", (PyCFunction)PyQuery_change_server, METH_VARARGS | METH_KEYWORDS, + change_server_doc}, + + {NULL} /* Sentinel */ +}; + +PyTypeObject PyQueryType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Query", /*tp_name*/ + sizeof(PyQuery), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyQuery_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyQuery objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyQuery_methods, /* tp_methods */ + 0, /* tp_members */ + PyQuery_getseters, /* tp_getset */ + &PyWindowItemType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* query factory function */ +PyObject *pyquery_new(void *query) +{ + static const char *BASE_NAME = "QUERY"; + PyObject *pyquery; + + pyquery = pywindow_item_sub_new(query, BASE_NAME, &PyQueryType); + if (pyquery) + { + PyQuery *pyq = (PyQuery *)pyquery; + signal_add_last_data("query destroyed", query_cleanup, pyq); + pyq->cleanup_installed = 1; + } + + return pyquery; +} + +int query_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyQueryType) < 0) + return 0; + + Py_INCREF(&PyQueryType); + PyModule_AddObject(py_module, "Query", (PyObject *)&PyQueryType); + + return 1; +} diff --git a/src/objects/query-object.h b/src/objects/query-object.h new file mode 100644 index 0000000..844ce55 --- /dev/null +++ b/src/objects/query-object.h @@ -0,0 +1,21 @@ +#ifndef _QUERY_OBJECT_H_ +#define _QUERY_OBJECT_H_ + +#include <Python.h> +#include "window-item-object.h" + +/* forward */ +struct _QUERY_REC; + +typedef struct +{ + PyWindowItem_HEAD(struct _QUERY_REC) +} PyQuery; + +extern PyTypeObject PyQueryType; + +int query_object_init(void); +PyObject *pyquery_new(void *query); +#define pyquery_check(op) PyObject_TypeCheck(op, &PyQueryType) + +#endif diff --git a/src/objects/rawlog-object.c b/src/objects/rawlog-object.c new file mode 100644 index 0000000..340d60a --- /dev/null +++ b/src/objects/rawlog-object.c @@ -0,0 +1,323 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "rawlog-object.h" +#include "pycore.h" + +/* monitor "????" signal */ +static void rawlog_cleanup(RAWLOG_REC *ban) +{ + /* XXX */ +} + +static void PyRawlog_dealloc(PyRawlog *self) +{ + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyRawlog_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyRawlog *self; + + self = (PyRawlog *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* XXX: Need function to create the rawlog */ + +/* Getters */ +PyDoc_STRVAR(PyRawlog_logging_doc, + "The raw log is being written to file currently." +); +static PyObject *PyRawlog_logging_get(PyRawlog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->logging); +} + +PyDoc_STRVAR(PyRawlog_nlines_doc, + "Number of lines in rawlog." +); +static PyObject *PyRawlog_nlines_get(PyRawlog *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->nlines); +} + +/* specialized getters/setters */ +static PyGetSetDef PyRawlog_getseters[] = { + {"logging", (getter)PyRawlog_logging_get, NULL, + PyRawlog_logging_doc, NULL}, + {"nlines", (getter)PyRawlog_nlines_get, NULL, + PyRawlog_nlines_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyRawlog_get_lines_doc, + "get_lines() -> list of str\n" + "\n" + "Return a list of lines for rawlog.\n" +); +static PyObject *PyRawlog_get_lines(PyRawlog *self, PyObject *args) +{ + PyObject *lines = NULL; + GSList *node; + + RET_NULL_IF_INVALID(self->data); + + lines = PyList_New(0); + if (!lines) + return NULL; + + for (node = self->data->lines; node; node = node->next) + { + int ret; + PyObject *line = PyString_FromString(node->data); + + if (!line) + { + Py_XDECREF(lines); + return NULL; + } + + ret = PyList_Append(lines, line); + Py_DECREF(line); + if (ret != 0) + { + Py_XDECREF(lines); + return NULL; + } + } + + return lines; +} + +PyDoc_STRVAR(PyRawlog_destroy_doc, + "destroy() -> None\n" + "\n" + "Destroy rawlog\n" +); +static PyObject *PyRawlog_destroy(PyRawlog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + rawlog_destroy(self->data); + + /*XXX: what about signal handler ? */ + self->data = NULL; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyRawlog_input_doc, + "input(str) -> None\n" + "\n" + "Send str to rawlog as input text.\n" +); +static PyObject *PyRawlog_input(PyRawlog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"str", NULL}; + char *str = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &str)) + return NULL; + + rawlog_input(self->data, str); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyRawlog_output_doc, + "output(str) -> None\n" + "\n" + "Send str to rawlog as output text.\n" +); +static PyObject *PyRawlog_output(PyRawlog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"str", NULL}; + char *str = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &str)) + return NULL; + + rawlog_output(self->data, str); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyRawlog_redirect_doc, + "redirect(str) -> None\n" + "\n" + "Send str to rawlog as redirection text." +); +static PyObject *PyRawlog_redirect(PyRawlog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"str", NULL}; + char *str = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &str)) + return NULL; + + rawlog_redirect(self->data, str); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyRawlog_open_doc, + "open(fname) -> None\n" + "\n" + "Start logging new messages in rawlog to specified file.\n" +); +static PyObject *PyRawlog_open(PyRawlog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"fname", NULL}; + char *fname = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &fname)) + return NULL; + + rawlog_open(self->data, fname); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyRawlog_close_doc, + "close() -> None\n" + "\n" + "Stop logging to file\n" +); +static PyObject *PyRawlog_close(PyRawlog *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + rawlog_close(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyRawlog_save_doc, + "save(fname) -> None\n" + "\n" + "Save the current rawlog history to specified file.\n" +); +static PyObject *PyRawlog_save(PyRawlog *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"fname", NULL}; + char *fname = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &fname)) + return NULL; + + rawlog_save(self->data, fname); + + Py_RETURN_NONE; +} +/* Methods for object */ +static PyMethodDef PyRawlog_methods[] = { + {"get_lines", (PyCFunction)PyRawlog_get_lines, METH_NOARGS, + PyRawlog_get_lines_doc}, + {"destroy", (PyCFunction)PyRawlog_destroy, METH_NOARGS, + PyRawlog_destroy_doc}, + {"input", (PyCFunction)PyRawlog_input, METH_VARARGS | METH_KEYWORDS, + PyRawlog_input_doc}, + {"output", (PyCFunction)PyRawlog_output, METH_VARARGS | METH_KEYWORDS, + PyRawlog_output_doc}, + {"redirect", (PyCFunction)PyRawlog_redirect, METH_VARARGS | METH_KEYWORDS, + PyRawlog_redirect_doc}, + {"open", (PyCFunction)PyRawlog_open, METH_VARARGS | METH_KEYWORDS, + PyRawlog_open_doc}, + {"close", (PyCFunction)PyRawlog_close, METH_NOARGS, + PyRawlog_close_doc}, + {"save", (PyCFunction)PyRawlog_save, METH_VARARGS | METH_KEYWORDS, + PyRawlog_save_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyRawlogType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Rawlog", /*tp_name*/ + sizeof(PyRawlog), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyRawlog_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyRawlog objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyRawlog_methods, /* tp_methods */ + 0, /* tp_members */ + PyRawlog_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyRawlog_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pyrawlog_new(void *rlog) +{ + PyRawlog *pyrlog; + + pyrlog = py_inst(PyRawlog, PyRawlogType); + if (!pyrlog) + return NULL; + + pyrlog->data = rlog; + + return (PyObject *)pyrlog; +} + +int rawlog_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyRawlogType) < 0) + return 0; + + Py_INCREF(&PyRawlogType); + PyModule_AddObject(py_module, "Rawlog", (PyObject *)&PyRawlogType); + + return 1; +} diff --git a/src/objects/rawlog-object.h b/src/objects/rawlog-object.h new file mode 100644 index 0000000..d2a9a2d --- /dev/null +++ b/src/objects/rawlog-object.h @@ -0,0 +1,22 @@ +#ifndef _RAWLOG_OBJECT_H_ +#define _RAWLOG_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _RAWLOG_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct _RAWLOG_REC) + int owned; +} PyRawlog; + +extern PyTypeObject PyRawlogType; + +int rawlog_object_init(void); +PyObject *pyrawlog_new(void *rlog); +#define pyrawlog_check(op) PyObject_TypeCheck(op, &PyRawlogType) + +#endif diff --git a/src/objects/reconnect-object.c b/src/objects/reconnect-object.c new file mode 100644 index 0000000..6483242 --- /dev/null +++ b/src/objects/reconnect-object.c @@ -0,0 +1,151 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "pycore.h" +#include "factory.h" +#include "reconnect-object.h" + +/*XXX: no Reconnect cleanup/destroy sig. Maybe value copy the two members? */ + +static void PyReconnect_dealloc(PyReconnect *self) +{ + Py_XDECREF(self->connect); + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyReconnect_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyReconnect *self; + + self = (PyReconnect *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyReconnect_tag_doc, + "Unique numeric tag" +); +static PyObject *PyReconnect_tag_get(PyReconnect *self, void *closure) +{ + RECONNECT_REC *data = self->data; + RET_NULL_IF_INVALID(self->data); + + return PyInt_FromLong(data->tag); +} + +PyDoc_STRVAR(PyReconnect_next_connect_doc, + "Unix time stamp when the next connection occurs" +); +static PyObject *PyReconnect_next_connect_get(PyReconnect *self, void *closure) +{ + RECONNECT_REC *data = self->data; + RET_NULL_IF_INVALID(self->data); + + return PyLong_FromUnsignedLong(data->next_connect); +} + +PyDoc_STRVAR(PyReconnect_connect_doc, + "Connection object" +); +static PyObject *PyReconnect_connect_get(PyReconnect *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->connect); +} + +/* specialized getters/setters */ +static PyGetSetDef PyReconnect_getseters[] = { + {"tag", (getter)PyReconnect_tag_get, NULL, + PyReconnect_tag_doc, NULL}, + {"next_connect", (getter)PyReconnect_next_connect_get, NULL, + PyReconnect_next_connect_doc, NULL}, + {"connect", (getter)PyReconnect_connect_get, NULL, + PyReconnect_connect_doc, NULL}, + {NULL} +}; + +/* Methods for object */ +static PyMethodDef PyReconnect_methods[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject PyReconnectType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Reconnect", /*tp_name*/ + sizeof(PyReconnect), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyReconnect_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyReconnect objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyReconnect_methods, /* tp_methods */ + 0, /* tp_members */ + PyReconnect_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyReconnect_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pyreconnect_new(void *recon) +{ + RECONNECT_REC *rec = recon; + PyObject *connect; + PyReconnect *pyrecon; + + /* XXX: get a managed connect because there's no signals to manage reconnect */ + connect = py_irssi_chat_new(rec->conn, 1); + if (!connect) + return NULL; + + pyrecon = py_inst(PyReconnect, PyReconnectType); + if (!pyrecon) + return NULL; + + pyrecon->data = recon; + pyrecon->connect = connect; + + return (PyObject *)pyrecon; +} + +int reconnect_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyReconnectType) < 0) + return 0; + + Py_INCREF(&PyReconnectType); + PyModule_AddObject(py_module, "Reconnect", (PyObject *)&PyReconnectType); + + return 1; +} diff --git a/src/objects/reconnect-object.h b/src/objects/reconnect-object.h new file mode 100644 index 0000000..ec0b094 --- /dev/null +++ b/src/objects/reconnect-object.h @@ -0,0 +1,20 @@ +#ifndef _RECONNECT_OBJECT_H_ +#define _RECONNECT_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/*XXX: no Reconnect cleanup/destroy sig. Maybe value copy the two members? */ +typedef struct +{ + PyIrssiFinal_HEAD(void) + PyObject *connect; +} PyReconnect; + +extern PyTypeObject PyReconnectType; + +int reconnect_object_init(void); +PyObject *pyreconnect_new(void *recon); +#define pyreconnect_check(op) PyObject_TypeCheck(op, &PyReconnectType) + +#endif diff --git a/src/objects/server-object.c b/src/objects/server-object.c new file mode 100644 index 0000000..31415b8 --- /dev/null +++ b/src/objects/server-object.c @@ -0,0 +1,807 @@ +#include <Python.h> +#include "pymodule.h" +#include "factory.h" +#include "pyirssi.h" +#include "pycore.h" +#include "pyutils.h" + +static void server_cleanup(SERVER_REC *server) +{ + PyServer *pyserver = signal_get_user_data(); + + if (server == pyserver->data) + { + if (pyserver->connect) + ((PyConnect *)pyserver->connect)->data = NULL; + + if (pyserver->rawlog) + ((PyRawlog *)pyserver->rawlog)->data = NULL; + + pyserver->data = NULL; + pyserver->cleanup_installed = 0; + signal_remove_data("server disconnected", server_cleanup, pyserver); + } +} + +static void PyServer_dealloc(PyServer *self) +{ + if (self->cleanup_installed) + signal_remove_data("server disconnected", server_cleanup, self); + + Py_XDECREF(self->connect); + Py_XDECREF(self->rawlog); + + self->ob_type->tp_free((PyObject*)self); +} + +/* Getters */ +PyDoc_STRVAR(PyServer_connect_time_doc, + "Time when connect() to server finished" +); +static PyObject *PyServer_connect_time_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromLong(self->data->connect_time); +} + +PyDoc_STRVAR(PyServer_real_connect_time_doc, + "Time when server sent 'connected' message" +); +static PyObject *PyServer_real_connect_time_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromLong(self->data->real_connect_time); +} + +PyDoc_STRVAR(PyServer_tag_doc, + "Unique server tag" +); +static PyObject *PyServer_tag_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->tag); +} + +PyDoc_STRVAR(PyServer_nick_doc, + "Current nick" +); +static PyObject *PyServer_nick_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->nick); +} + +PyDoc_STRVAR(PyServer_connected_doc, + "Is connection finished? 1|0" +); +static PyObject *PyServer_connected_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->connected); +} + +PyDoc_STRVAR(PyServer_connection_lost_doc, + "Did we lose the connection (1) or was the connection just /DISCONNECTed (0)" +); +static PyObject *PyServer_connection_lost_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->connection_lost); +} + +PyDoc_STRVAR(PyServer_rawlog_doc, + "Rawlog object for the server" +); +static PyObject *PyServer_rawlog_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->rawlog); +} + +PyDoc_STRVAR(PyServer_connect_doc, + "Connect object for the server" +); +static PyObject *PyServer_connect_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->connect); +} + +PyDoc_STRVAR(PyServer_version_doc, + "Server version" +); +static PyObject *PyServer_version_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->version); +} + +PyDoc_STRVAR(PyServer_last_invite_doc, + "Last channel we were invited to" +); +static PyObject *PyServer_last_invite_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->last_invite); +} + +PyDoc_STRVAR(PyServer_server_operator_doc, + "Are we server operator (IRC op) 1|0" +); +static PyObject *PyServer_server_operator_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->server_operator); +} + +PyDoc_STRVAR(PyServer_usermode_away_doc, + "Are we marked as away? 1|0" +); +static PyObject *PyServer_usermode_away_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->usermode_away); +} + +PyDoc_STRVAR(PyServer_away_reason_doc, + "Away reason message" +); +static PyObject *PyServer_away_reason_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->away_reason); +} + +PyDoc_STRVAR(PyServer_banned_doc, + "Were we banned from this server? 1|0" +); +static PyObject *PyServer_banned_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->banned); +} + +PyDoc_STRVAR(PyServer_lag_doc, + "Current lag to server in milliseconds" +); +static PyObject *PyServer_lag_get(PyServer *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->lag); +} + +static PyGetSetDef PyServer_getseters[] = { + {"connect_time", (getter)PyServer_connect_time_get, NULL, + PyServer_connect_time_doc, NULL}, + {"real_connect_time", (getter)PyServer_real_connect_time_get, NULL, + PyServer_real_connect_time_doc, NULL}, + {"tag", (getter)PyServer_tag_get, NULL, + PyServer_tag_doc, NULL}, + {"nick", (getter)PyServer_nick_get, NULL, + PyServer_nick_doc, NULL}, + {"connected", (getter)PyServer_connected_get, NULL, + PyServer_connected_doc, NULL}, + {"connection_lost", (getter)PyServer_connection_lost_get, NULL, + PyServer_connection_lost_doc, NULL}, + {"rawlog", (getter)PyServer_rawlog_get, NULL, + PyServer_rawlog_doc, NULL}, + {"connect", (getter)PyServer_connect_get, NULL, + PyServer_connect_doc, NULL}, + {"version", (getter)PyServer_version_get, NULL, + PyServer_version_doc, NULL}, + {"last_invite", (getter)PyServer_last_invite_get, NULL, + PyServer_last_invite_doc, NULL}, + {"server_operator", (getter)PyServer_server_operator_get, NULL, + PyServer_server_operator_doc, NULL}, + {"usermode_away", (getter)PyServer_usermode_away_get, NULL, + PyServer_usermode_away_doc, NULL}, + {"away_reason", (getter)PyServer_away_reason_get, NULL, + PyServer_away_reason_doc, NULL}, + {"banned", (getter)PyServer_banned_get, NULL, + PyServer_banned_doc, NULL}, + {"lag", (getter)PyServer_lag_get, NULL, + PyServer_lag_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(print_doc, + "prnt(channel, str, level) -> None\n" + "\n" + "Print to server\n" +); +static PyObject *PyServer_prnt(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"channel", "str", "level", NULL}; + char *str, *channel; + int level = MSGLEVEL_CLIENTNOTICE; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss|i", kwlist, &channel, &str, &level)) + return NULL; + + printtext_string(self->data, channel, level, str); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(command_doc, + "command(cmd) -> None\n" + "\n" + "Send command\n" +); +static PyObject *PyServer_command(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", NULL}; + char *cmd; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd)) + return NULL; + + py_command(cmd, self->data, NULL); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(disconnect_doc, + "disconnect() -> None\n" + "\n" + "Disconnect from server\n" +); +static PyObject *PyServer_disconnect(PyServer *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + server_disconnect(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(isnickflag_doc, + "isnickflag(flag) -> bool\n" + "\n" + "Returns True if flag is a nick mode flag (@, + or % in IRC)\n" +); +static PyObject *PyServer_isnickflag(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"flag", NULL}; + char flag; + int ret; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "c", kwlist, &flag)) + return NULL; + + ret = self->data->isnickflag(self->data, flag); + + return PyBool_FromLong(ret); +} + +PyDoc_STRVAR(ischannel_doc, + "ischannel(data) -> bool\n" + "\n" + "Returns True if start of `data' seems to mean channel.\n" +); +static PyObject *PyServer_ischannel(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"data", NULL}; + char *data; + int ret; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &data)) + return NULL; + + ret = self->data->ischannel(self->data, data); + + return PyBool_FromLong(ret); +} + +PyDoc_STRVAR(get_nick_flags_doc, + "get_nick_flags() -> str\n" + "\n" + "Returns nick flag characters in order: op, voice, halfop (\"@+%\") in IRC\n" +); +static PyObject *PyServer_get_nick_flags(PyServer *self, PyObject *args) +{ + char *ret; + + RET_NULL_IF_INVALID(self->data); + + ret = (char *)self->data->get_nick_flags(self->data); + + return PyString_FromString(ret); +} + +PyDoc_STRVAR(send_message_doc, + "send_message(target, msg, target_type) -> None\n" + "\n" + "Sends a message to nick/channel. target_type 0 = channel, 1 = nick\n" +); +static PyObject *PyServer_send_message(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"target", "msg", "target_type", NULL}; + char *target, *msg; + int target_type; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssi", kwlist, &target, &msg, &target_type)) + return NULL; + + self->data->send_message(self->data, target, msg, target_type); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(channels_join_doc, + "channels_join(channels, automatic=False) -> None\n" + "\n" + "Join to channels in server. `channels' may also contain keys for\n" + "channels just like with /JOIN command. `automatic' specifies if this\n" + "channel was joined 'automatically' or if it was joined because join\n" + "was requested by user. If channel join is 'automatic', irssi doesn't\n" + "jump to the window where the channel was joined.\n" +); +static PyObject *PyServer_channels_join(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"channels", "automatic", NULL}; + char *channels; + int automatic = 0; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &channels, &automatic)) + return NULL; + + self->data->channels_join(self->data, channels, automatic); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyServer_window_item_find_doc, + "window_item_find(name) -> WindowItem object or None\n" + "\n" + "Find window item that matches best to given arguments\n" +); +static PyObject *PyServer_window_item_find(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + return py_irssi_chat_new(window_item_find(self->data, name), 1); +} + +PyDoc_STRVAR(PyServer_window_find_item_doc, + "window_find_item(name) -> Window object or None\n" + "\n" + "Find window which contains window item with specified name/server\n" +); +static PyObject *PyServer_window_find_item(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + WINDOW_REC *win; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + win = window_find_item(self->data, name); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyServer_window_find_level_doc, + "window_find_level(level) -> Window object or None\n" + "\n" + "Find window with level\n" +); +static PyObject *PyServer_window_find_level(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"level", NULL}; + int level = 0; + WINDOW_REC *win; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &level)) + return NULL; + + win = window_find_level(self->data, level); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyServer_window_find_closest_doc, + "window_find_closest(name, level) -> Window object or None\n" + "\n" + "Find window that matches best to given arguments. `name' can be either\n" + "window name or name of one of the window items.\n" +); +static PyObject *PyServer_window_find_closest(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", "level", NULL}; + char *name = ""; + int level = 0; + WINDOW_REC *win; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "si", kwlist, + &name, &level)) + return NULL; + + win = window_find_closest(self->data, name, level); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyServer_channels_doc, + "channels() -> list of Channel objects\n" + "\n" + "Return list of channels for server\n" +); +static PyObject *PyServer_channels(PyServer *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + return py_irssi_chatlist_new(self->data->channels, 1); +} + +PyDoc_STRVAR(PyServer_channel_find_doc, + "channel_find(name) -> Channel object or None\n" + "\n" + "Find channel from this server\n" +); +static PyObject *PyServer_channel_find(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + return py_irssi_chat_new(channel_find(self->data, name), 1); +} + +PyDoc_STRVAR(PyServer_nicks_get_same_doc, + "nicks_get_same(nick)\n" + "\n" + "Return all nick objects in all channels in server. List is in format:\n" + "[(Channel, Nick), (Channel, Nick), ...]\n" +); +static PyObject *PyServer_nicks_get_same(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + char *nick = ""; + GSList *list, *node; + PyObject *pylist = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &nick)) + return NULL; + + pylist = PyList_New(0); + if (!pylist) + return NULL; + + list = nicklist_get_same(self->data, nick); + for (node = list; node != NULL; node = node->next->next) + { + int ret; + PyObject *tup; + + tup = Py_BuildValue("(NN)", + py_irssi_chat_new(node->data, 1), + py_irssi_chat_new(node->next->data, 1)); + if (!tup) + { + Py_XDECREF(pylist); + return NULL; + } + + ret = PyList_Append(pylist, tup); + Py_DECREF(tup); + if (ret != 0) + { + Py_XDECREF(pylist); + return NULL; + } + } + + return pylist; +} + +PyDoc_STRVAR(PyServer_queries_doc, + "queries() -> list of Query objects\n" + "\n" + "Return a list of queries for server.\n" +); +static PyObject *PyServer_queries(PyServer *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + return py_irssi_chatlist_new(self->data->queries, 1); +} + +PyDoc_STRVAR(PyServer_query_find_doc, + "query_find(nick) -> Query object or None\n" + "\n" + "Find a query on this server.\n" +); +static PyObject *PyServer_query_find(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", NULL}; + char *nick = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &nick)) + return NULL; + + return py_irssi_chat_new(query_find(self->data, nick), 1); +} + +PyDoc_STRVAR(PyServer_mask_match_doc, + "mask_match(mask, nick, user, host) -> bool\n" + "\n" + "Return true if mask matches nick!user@host\n" +); +static PyObject *PyServer_mask_match(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"mask", "nick", "user", "host", NULL}; + char *mask = ""; + char *nick = ""; + char *user = ""; + char *host = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssss", kwlist, + &mask, &nick, &user, &host)) + return NULL; + + return PyBool_FromLong(mask_match(self->data, mask, nick, user, host)); +} + +PyDoc_STRVAR(PyServer_mask_match_address_doc, + "mask_match_address(mask, nick, address) -> bool\n" + "\n" + "Return True if mask matches nick!address\n" +); +static PyObject *PyServer_mask_match_address(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"mask", "nick", "address", NULL}; + char *mask = ""; + char *nick = ""; + char *address = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + &mask, &nick, &address)) + return NULL; + + return PyBool_FromLong(mask_match_address(self->data, mask, nick, address)); +} + +PyDoc_STRVAR(PyServer_masks_match_doc, + "masks_match(masks, nick, address) -> bool\n" + "\n" + "Return True if any mask in the masks (string separated by spaces)\n" + "matches nick!address\n" +); +static PyObject *PyServer_masks_match(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"masks", "nick", "address", NULL}; + char *masks = ""; + char *nick = ""; + char *address = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sss", kwlist, + &masks, &nick, &address)) + return NULL; + + return PyBool_FromLong(masks_match(self->data, masks, nick, address)); +} + +PyDoc_STRVAR(PyServer_ignore_check_doc, + "ignore_check(nick, host, channel, text, level) -> bool\n" + "\n" + "Return True if ignore matches\n" +); +static PyObject *PyServer_ignore_check(PyServer *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"nick", "host", "channel", "text", "level", NULL}; + char *nick = ""; + char *host = ""; + char *channel = ""; + char *text = ""; + int level = 0; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssssi", kwlist, + &nick, &host, &channel, &text, &level)) + return NULL; + + return PyBool_FromLong(ignore_check(self->data, + nick, host, channel, text, level)); +} + +/* Methods for object */ +static PyMethodDef PyServer_methods[] = { + {"prnt", (PyCFunction)PyServer_prnt, METH_VARARGS | METH_KEYWORDS, + print_doc}, + {"command", (PyCFunction)PyServer_command, METH_VARARGS | METH_KEYWORDS, + command_doc}, + {"disconnect", (PyCFunction)PyServer_disconnect, METH_NOARGS, + disconnect_doc}, + {"isnickflag", (PyCFunction)PyServer_isnickflag, METH_VARARGS | METH_KEYWORDS, + isnickflag_doc}, + {"ischannel", (PyCFunction)PyServer_ischannel, METH_VARARGS | METH_KEYWORDS, + ischannel_doc}, + {"get_nick_flags", (PyCFunction)PyServer_get_nick_flags, METH_NOARGS, + get_nick_flags_doc}, + {"send_message", (PyCFunction)PyServer_send_message, METH_VARARGS | METH_KEYWORDS, + send_message_doc}, + {"channels_join", (PyCFunction)PyServer_channels_join, METH_VARARGS | METH_KEYWORDS, + channels_join_doc}, + {"window_item_find", (PyCFunction)PyServer_window_item_find, METH_VARARGS | METH_KEYWORDS, + PyServer_window_item_find_doc}, + {"window_find_item", (PyCFunction)PyServer_window_find_item, METH_VARARGS | METH_KEYWORDS, + PyServer_window_find_item_doc}, + {"window_find_level", (PyCFunction)PyServer_window_find_level, METH_VARARGS | METH_KEYWORDS, + PyServer_window_find_level_doc}, + {"window_find_closest", (PyCFunction)PyServer_window_find_closest, METH_VARARGS | METH_KEYWORDS, + PyServer_window_find_closest_doc}, + {"channels", (PyCFunction)PyServer_channels, METH_NOARGS, + PyServer_channels_doc}, + {"channel_find", (PyCFunction)PyServer_channel_find, METH_VARARGS | METH_KEYWORDS, + PyServer_channel_find_doc}, + {"nicks_get_same", (PyCFunction)PyServer_nicks_get_same, METH_VARARGS | METH_KEYWORDS, + PyServer_nicks_get_same_doc}, + {"queries", (PyCFunction)PyServer_queries, METH_NOARGS, + PyServer_queries_doc}, + {"query_find", (PyCFunction)PyServer_query_find, METH_VARARGS | METH_KEYWORDS, + PyServer_query_find_doc}, + {"mask_match", (PyCFunction)PyServer_mask_match, METH_VARARGS | METH_KEYWORDS, + PyServer_mask_match_doc}, + {"mask_match_address", (PyCFunction)PyServer_mask_match_address, METH_VARARGS | METH_KEYWORDS, + PyServer_mask_match_address_doc}, + {"masks_match", (PyCFunction)PyServer_masks_match, METH_VARARGS | METH_KEYWORDS, + PyServer_masks_match_doc}, + {"ignore_check", (PyCFunction)PyServer_ignore_check, METH_VARARGS | METH_KEYWORDS, + PyServer_ignore_check_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyServerType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Server", /*tp_name*/ + sizeof(PyServer), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyServer_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyServer objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyServer_methods, /* tp_methods */ + 0, /* tp_members */ + PyServer_getseters, /* tp_getset */ + &PyIrssiChatBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* server factory function + connect arg should point to a wrapped SERVER_CONNECT */ +PyObject *pyserver_sub_new(void *server, PyTypeObject *subclass) +{ + static const char *SERVER_TYPE = "SERVER"; + SERVER_REC *srec = server; + PyServer *pyserver = NULL; + PyObject *rawlog = NULL; + PyObject *connect = NULL; + + g_return_val_if_fail(server != NULL, NULL); + + connect = py_irssi_chat_new(srec->connrec, 0); + if (!connect) + return NULL; + + /* FIXME */ + /* + if (srec->rawlog) + { + rawlog = pyrawlog_new(srec->rawlog); + if (!rawlog) + return NULL; + } + */ + + pyserver = py_instp(PyServer, subclass); + if (!pyserver) + return NULL; + + pyserver->base_name = SERVER_TYPE; + pyserver->data = server; + signal_add_last_data("server disconnected", server_cleanup, pyserver); + pyserver->cleanup_installed = 1; + pyserver->rawlog = rawlog; + pyserver->connect = connect; + + return (PyObject *)pyserver; +} + +PyObject *pyserver_new(void *server) +{ + return pyserver_sub_new(server, &PyServerType); +} + +int server_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyServerType) < 0) + return 0; + + Py_INCREF(&PyServerType); + PyModule_AddObject(py_module, "Server", (PyObject *)&PyServerType); + + return 1; +} diff --git a/src/objects/server-object.h b/src/objects/server-object.h new file mode 100644 index 0000000..0703f47 --- /dev/null +++ b/src/objects/server-object.h @@ -0,0 +1,28 @@ +#ifndef _SERVER_OBJECT_H_ +#define _SERVER_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _SERVER_REC; + +#define PyServer_HEAD(type) \ + PyIrssi_HEAD(type) \ + PyObject *connect; \ + PyObject *rawlog; + +typedef struct +{ + PyServer_HEAD(struct _SERVER_REC) +} PyServer; + +extern PyTypeObject PyServerType; + +int server_object_init(void); +PyObject *pyserver_sub_new(void *server, PyTypeObject *subclass); +PyObject *pyserver_new(void *server); + +#define pyserver_check(op) PyObject_TypeCheck(op, &PyServerType) + +#endif diff --git a/src/objects/statusbar-item-object.c b/src/objects/statusbar-item-object.c new file mode 100644 index 0000000..f6768af --- /dev/null +++ b/src/objects/statusbar-item-object.c @@ -0,0 +1,253 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "factory.h" +#include "statusbar-item-object.h" + +/* monitor "statusbar item destroyed" signal */ +static void statusbar_item_cleanup(SBAR_ITEM_REC *sbar_item) +{ + PyStatusbarItem *pysbar_item = signal_get_user_data(); + + if (sbar_item == pysbar_item->data) + { + pysbar_item->data = NULL; + pysbar_item->cleanup_installed = 0; + signal_remove_data("statusbar item_destroy", statusbar_item_cleanup, pysbar_item); + } +} + +static void PyStatusbarItem_dealloc(PyStatusbarItem *self) +{ + if (self->cleanup_installed) + signal_remove_data("sbar_itemlist remove", statusbar_item_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyStatusbarItem_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyStatusbarItem *self; + + self = (PyStatusbarItem *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getter */ +PyDoc_STRVAR(PyStatusbarItem_min_size_doc, + "min size" +); +static PyObject *PyStatusbarItem_min_size_get(PyStatusbarItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->min_size); +} + +PyDoc_STRVAR(PyStatusbarItem_max_size_doc, + "max size" +); +static PyObject *PyStatusbarItem_max_size_get(PyStatusbarItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->max_size); +} + +PyDoc_STRVAR(PyStatusbarItem_xpos_doc, + "x position" +); +static PyObject *PyStatusbarItem_xpos_get(PyStatusbarItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->xpos); +} + +PyDoc_STRVAR(PyStatusbarItem_size_doc, + "size" +); +static PyObject *PyStatusbarItem_size_get(PyStatusbarItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->size); +} + +PyDoc_STRVAR(PyStatusbarItem_window_doc, + "parent window for statusbar item" +); +static PyObject *PyStatusbarItem_window_get(PyStatusbarItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->window); +} + +/* setters */ +static int py_setint(int *iv, PyObject *value) +{ + int tmp; + + if (value == NULL) + { + PyErr_SetString(PyExc_AttributeError, "can't delete member"); + return -1; + } + + if (!PyInt_Check(value)) + { + PyErr_SetString(PyExc_TypeError, "value must be int"); + return -1; + } + + tmp = PyInt_AsLong(value); + if (PyErr_Occurred()) + return -1; + + *iv = tmp; + + return 0; +} + +static int PyStatusbarItem_min_size_set(PyStatusbarItem *self, PyObject *value, void *closure) +{ + return py_setint(&self->data->min_size, value); +} + +static int PyStatusbarItem_max_size_set(PyStatusbarItem *self, PyObject *value, void *closure) +{ + return py_setint(&self->data->max_size, value); +} + +/* specialized getters/setters */ +static PyGetSetDef PyStatusbarItem_getseters[] = { + {"min_size", (getter)PyStatusbarItem_min_size_get, (setter)PyStatusbarItem_min_size_set, + PyStatusbarItem_min_size_doc, NULL}, + {"max_size", (getter)PyStatusbarItem_max_size_get, (setter)PyStatusbarItem_max_size_set, + PyStatusbarItem_max_size_doc, NULL}, + {"xpos", (getter)PyStatusbarItem_xpos_get, NULL, + PyStatusbarItem_xpos_doc, NULL}, + {"size", (getter)PyStatusbarItem_size_get, NULL, + PyStatusbarItem_size_doc, NULL}, + {"window", (getter)PyStatusbarItem_window_get, NULL, + PyStatusbarItem_window_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyStatusbarItem_default_handler_doc, + "default_handler(get_size_only, str=None, data="", escape_vars=True) -> None\n" + "\n" + "Run default handler of item to print to statusbar\n" +); +static PyObject *PyStatusbarItem_default_handler(PyStatusbarItem *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"get_size_only", "str", "data", "escape_vars", NULL}; + int get_size_only = 0; + char *str = NULL; + char *data = ""; + int escape_vars = TRUE; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|zsi", kwlist, + &get_size_only, &str, &data, &escape_vars)) + return NULL; + + if (str && !*str) + str = NULL; + + statusbar_item_default_handler(self->data, get_size_only, str, data, escape_vars); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyStatusbarItem_methods[] = { + {"default_handler", (PyCFunction)PyStatusbarItem_default_handler, METH_VARARGS | METH_KEYWORDS, + PyStatusbarItem_default_handler_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyStatusbarItemType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "StatusbarItem", /*tp_name*/ + sizeof(PyStatusbarItem), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyStatusbarItem_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyStatusbarItem objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyStatusbarItem_methods, /* tp_methods */ + 0, /* tp_members */ + PyStatusbarItem_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyStatusbarItem_new, /* tp_new */ +}; + + +/* sbar_item factory function */ +PyObject *pystatusbar_item_new(void *sbar_item) +{ + SBAR_ITEM_REC *si; + PyStatusbarItem *pysbar_item; + PyObject *window = NULL; + + si = sbar_item; + if (si->bar->parent_window) + { + window = pywindow_new(si->bar->parent_window); + if (!window) + return NULL; + } + + pysbar_item = py_inst(PyStatusbarItem, PyStatusbarItemType); + if (!pysbar_item) + return NULL; + + pysbar_item->window = window; + + pysbar_item->data = sbar_item; + pysbar_item->cleanup_installed = 1; + signal_add_last_data("statusbar item destroyed", statusbar_item_cleanup, pysbar_item); + + return (PyObject *)pysbar_item; +} + +int statusbar_item_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyStatusbarItemType) < 0) + return 0; + + Py_INCREF(&PyStatusbarItemType); + PyModule_AddObject(py_module, "StatusbarItem", (PyObject *)&PyStatusbarItemType); + + return 1; +} diff --git a/src/objects/statusbar-item-object.h b/src/objects/statusbar-item-object.h new file mode 100644 index 0000000..19243f4 --- /dev/null +++ b/src/objects/statusbar-item-object.h @@ -0,0 +1,22 @@ +#ifndef _SBAR_ITEM_OBJECT_H_ +#define _SBAR_ITEM_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct SBAR_ITEM_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct SBAR_ITEM_REC) + PyObject *window; +} PyStatusbarItem; + +extern PyTypeObject PyStatusbarItemType; + +int statusbar_item_object_init(void); +PyObject *pystatusbar_item_new(void *sbar_item); +#define pystatusbar_item_check(op) PyObject_TypeCheck(op, &PyStatusbarItemType) + +#endif diff --git a/src/objects/textdest-object.c b/src/objects/textdest-object.c new file mode 100644 index 0000000..2ad6523 --- /dev/null +++ b/src/objects/textdest-object.c @@ -0,0 +1,285 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "textdest-object.h" +#include "factory.h" +#include "pycore.h" + +static int pytextdest_setup(PyTextDest *pytdest, void *td, int owned); + +/* XXX: no cleanup signal for textdest */ +static void PyTextDest_dealloc(PyTextDest *self) +{ + Py_XDECREF(self->window); + Py_XDECREF(self->server); + + if (self->owned) + { + g_free((char*)self->data->target); + g_free(self->data); + } + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyTextDest_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyTextDest *self; + + self = (PyTextDest *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* init function to create the textdest */ +PyDoc_STRVAR(PyTextDest_doc, + "__init__(target, level=MSGLEVEL_CLIENTNOTICE, server=None, window=None)\n" + "\n" + "Create a TextDest\n" +); +static int PyTextDest_init(PyTextDest *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"target", "level", "server", "window", NULL}; + char *target; + int level = MSGLEVEL_CLIENTNOTICE; + PyObject *server = NULL, *window = NULL; + TEXT_DEST_REC *dest; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|ioo", kwlist, + &target, &level, &server, &window)) + return -1; + + if (server == Py_None) + server = NULL; + if (window == Py_None) + window = NULL; + + if (server && !pyserver_check(server)) + { + PyErr_Format(PyExc_TypeError, "arg 3 isnt server"); + return -1; + } + + if (window && !pywindow_check(window)) + { + PyErr_Format(PyExc_TypeError, "arg 4 isnt window"); + return -1; + } + + if (self->data) + { + PyErr_Format(PyExc_RuntimeError, "TextDest already wrapped"); + return -1; + } + + dest = g_new0(TEXT_DEST_REC, 1); + format_create_dest(dest, DATA(server), g_strdup(target), level, DATA(window)); + + if (!pytextdest_setup(self, dest, 1)) + return -1; + + return 0; +} + +/* Getters */ +PyDoc_STRVAR(PyTextDest_window_doc, + "Window where the text will be written" +); +static PyObject *PyTextDest_window_get(PyTextDest *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->window); +} + +PyDoc_STRVAR(PyTextDest_server_doc, + "Target server" +); +static PyObject *PyTextDest_server_get(PyTextDest *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->server); +} + +PyDoc_STRVAR(PyTextDest_target_doc, + "Target channel/query/etc name" +); +static PyObject *PyTextDest_target_get(PyTextDest *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->target); +} + +PyDoc_STRVAR(PyTextDest_level_doc, + "Text level" +); +static PyObject *PyTextDest_level_get(PyTextDest *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->level); +} + +PyDoc_STRVAR(PyTextDest_hilight_priority_doc, + "Priority for the hilighted text" +); +static PyObject *PyTextDest_hilight_priority_get(PyTextDest *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->hilight_priority); +} + +PyDoc_STRVAR(PyTextDest_hilight_color_doc, + "Color for the hilighted text" +); +static PyObject *PyTextDest_hilight_color_get(PyTextDest *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->hilight_color); +} + +/* specialized getters/setters */ +static PyGetSetDef PyTextDest_getseters[] = { + {"window", (getter)PyTextDest_window_get, NULL, + PyTextDest_window_doc, NULL}, + {"server", (getter)PyTextDest_server_get, NULL, + PyTextDest_server_doc, NULL}, + {"target", (getter)PyTextDest_target_get, NULL, + PyTextDest_target_doc, NULL}, + {"level", (getter)PyTextDest_level_get, NULL, + PyTextDest_level_doc, NULL}, + {"hilight_priority", (getter)PyTextDest_hilight_priority_get, NULL, + PyTextDest_hilight_priority_doc, NULL}, + {"hilight_color", (getter)PyTextDest_hilight_color_get, NULL, + PyTextDest_hilight_color_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyTextDest_prnt_doc, + "prnt(str) -> None\n" + "\n" + "Print str to TextDest\n" +); +static PyObject *PyTextDest_prnt(PyTextDest *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"str", NULL}; + char *str = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &str)) + return NULL; + + printtext_dest(self->data, "%s", str); + + Py_RETURN_NONE; +} + +/* Methods for object */ +static PyMethodDef PyTextDest_methods[] = { + {"prnt", (PyCFunction)PyTextDest_prnt, METH_VARARGS | METH_KEYWORDS, + PyTextDest_prnt_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyTextDestType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "TextDest", /*tp_name*/ + sizeof(PyTextDest), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyTextDest_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + PyTextDest_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyTextDest_methods, /* tp_methods */ + 0, /* tp_members */ + PyTextDest_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)PyTextDest_init, /* tp_init */ + 0, /* tp_alloc */ + PyTextDest_new, /* tp_new */ +}; + +static int pytextdest_setup(PyTextDest *pytdest, void *td, int owned) +{ + PyObject *window, *server; + TEXT_DEST_REC *tdest = td; + + if (tdest->window) + { + window = pywindow_new(tdest->window); + if (!window) + return 0; + } + + server = py_irssi_chat_new(tdest->server, 1); + if (!server) + { + Py_DECREF(window); + return 0; + } + + pytdest->data = td; + pytdest->window = window; + pytdest->server = server; + pytdest->owned = owned; + + return 1; +} + +/* TextDest factory function */ +PyObject *pytextdest_new(void *td) +{ + PyTextDest *pytdest; + + pytdest = py_inst(PyTextDest, PyTextDestType); + if (!pytdest) + return NULL; + + if (!pytextdest_setup(pytdest, td, 0)) + { + Py_DECREF(pytdest); + return NULL; + } + + return (PyObject *)pytdest; +} + +int textdest_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyTextDestType) < 0) + return 0; + + Py_INCREF(&PyTextDestType); + PyModule_AddObject(py_module, "TextDest", (PyObject *)&PyTextDestType); + + return 1; +} diff --git a/src/objects/textdest-object.h b/src/objects/textdest-object.h new file mode 100644 index 0000000..3e273e6 --- /dev/null +++ b/src/objects/textdest-object.h @@ -0,0 +1,24 @@ +#ifndef _TEXTDEST_OBJECT_H_ +#define _TEXTDEST_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _TEXT_DEST_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct _TEXT_DEST_REC) + PyObject *window; + PyObject *server; + int owned; +} PyTextDest; + +extern PyTypeObject PyTextDestType; + +int textdest_object_init(void); +PyObject *pytextdest_new(void *td); +#define pytextdest_check(op) PyObject_TypeCheck(op, &PyTextDestType) + +#endif diff --git a/src/objects/theme-object.c b/src/objects/theme-object.c new file mode 100644 index 0000000..0f12217 --- /dev/null +++ b/src/objects/theme-object.c @@ -0,0 +1,195 @@ +#include <Python.h> +#include "pyirssi.h" +#include "pymodule.h" +#include "theme-object.h" +#include "factory.h" +#include "pycore.h" + +/* monitor "theme destroyed" signal */ +static void theme_cleanup(THEME_REC *rec) +{ + PyTheme *pytheme = signal_get_user_data(); + if (pytheme->data == rec) + { + pytheme->data = NULL; + pytheme->cleanup_installed = 0; + signal_remove_data("theme destroyed", theme_cleanup, pytheme); + } +} + +static void PyTheme_dealloc(PyTheme *self) +{ + if (self->cleanup_installed) + signal_remove_data("theme destroyed", theme_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyTheme_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyTheme *self; + + self = (PyTheme *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +/* specialized getters/setters */ +static PyGetSetDef PyTheme_getseters[] = { + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyTheme_format_expand_doc, + "format_expand(format, flags=0) -> str or None\n" +); +static PyObject *PyTheme_format_expand(PyTheme *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"format", "flags", NULL}; + char *format = ""; + int flags = 0; + char *ret; + PyObject *pyret; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, + &format, &flags)) + return NULL; + + if (flags == 0) + ret = theme_format_expand(self->data, format); + else + ret = theme_format_expand_data(self->data, (const char **)&format, 'n', 'n', + NULL, NULL, EXPAND_FLAG_ROOT | flags); + + if (ret) + { + pyret = PyString_FromString(ret); + g_free(ret); + return pyret; + } + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyTheme_get_format_doc, + "get_format(module, tag) -> str\n" +); +static PyObject *PyTheme_get_format(PyTheme *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"module", "tag", NULL}; + char *module = ""; + char *tag = ""; + THEME_REC *theme = self->data; + FORMAT_REC *formats; + MODULE_THEME_REC *modtheme; + int i; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &module, &tag)) + return NULL; + + formats = g_hash_table_lookup(default_formats, module); + if (!formats) + return PyErr_Format(PyExc_KeyError, "unknown module, %s", module); + + for (i = 0; formats[i].def; i++) + { + if (formats[i].tag && !g_strcasecmp(formats[i].tag, tag)) + { + modtheme = g_hash_table_lookup(theme->modules, module); + if (modtheme && modtheme->formats[i]) + return PyString_FromString(modtheme->formats[i]); + else + return PyString_FromString(formats[i].def); + } + } + + return PyErr_Format(PyExc_KeyError, "unknown format tag, %s", tag); +} + +/* Methods for object */ +static PyMethodDef PyTheme_methods[] = { + {"format_expand", (PyCFunction)PyTheme_format_expand, METH_VARARGS | METH_KEYWORDS, + PyTheme_format_expand_doc}, + {"get_format", (PyCFunction)PyTheme_get_format, METH_VARARGS | METH_KEYWORDS, + PyTheme_get_format_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyThemeType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Theme", /*tp_name*/ + sizeof(PyTheme), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyTheme_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyTheme_methods, /* tp_methods */ + 0, /* tp_members */ + PyTheme_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyTheme_new, /* tp_new */ +}; + +/* Theme factory function */ +PyObject *pytheme_new(void *td) +{ + PyTheme *pytheme; + + pytheme = py_inst(PyTheme, PyThemeType); + if (!pytheme) + return NULL; + + pytheme->data = td; + signal_add_last_data("theme destroyed", theme_cleanup, pytheme); + pytheme->cleanup_installed = 1; + + return (PyObject *)pytheme; +} + +int theme_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyThemeType) < 0) + return 0; + + Py_INCREF(&PyThemeType); + PyModule_AddObject(py_module, "Theme", (PyObject *)&PyThemeType); + + return 1; +} diff --git a/src/objects/theme-object.h b/src/objects/theme-object.h new file mode 100644 index 0000000..299d54d --- /dev/null +++ b/src/objects/theme-object.h @@ -0,0 +1,18 @@ +#ifndef _THEME_OBJECT_H_ +#define _THEME_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) +} PyTheme; + +extern PyTypeObject PyThemeType; + +int theme_object_init(void); +PyObject *pytheme_new(void *td); +#define pytheme_check(op) PyObject_TypeCheck(op, &PyThemeType) + +#endif diff --git a/src/objects/window-item-object.c b/src/objects/window-item-object.c new file mode 100644 index 0000000..2f18f7d --- /dev/null +++ b/src/objects/window-item-object.c @@ -0,0 +1,334 @@ +#include <Python.h> +#include "pymodule.h" +#include "base-objects.h" +#include "window-item-object.h" +#include "pyirssi.h" +#include "pycore.h" +#include "pyutils.h" +#include "factory.h" + +/* Dealloc is overridden by sub types */ + +PyDoc_STRVAR(PyWindowItem_server_doc, + "Active name for item" +); +static PyObject *PyWindowItem_server_get(PyWindowItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_OBJ_OR_NONE(self->server); +} + +PyDoc_STRVAR(PyWindowItem_name_doc, + "Name of the item" +); +static PyObject *PyWindowItem_name_get(PyWindowItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->visible_name); +} + +PyDoc_STRVAR(PyWindowItem_createtime_doc, + "Time the witem was created" +); +static PyObject *PyWindowItem_createtime_get(PyWindowItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromLong(self->data->createtime); +} + +PyDoc_STRVAR(PyWindowItem_data_level_doc, + "0=no new data, 1=text, 2=msg, 3=highlighted text" +); +static PyObject *PyWindowItem_data_level_get(PyWindowItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->data_level); +} + +PyDoc_STRVAR(PyWindowItem_hilight_color_doc, + "Color of the last highlighted text" +); +static PyObject *PyWindowItem_hilight_color_get(PyWindowItem *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->hilight_color); +} + +/* specialized getters/setters */ +static PyGetSetDef PyWindowItem_getseters[] = { + {"server", (getter)PyWindowItem_server_get, NULL, + PyWindowItem_server_doc, NULL}, + {"name", (getter)PyWindowItem_name_get, NULL, + PyWindowItem_name_doc, NULL}, + {"createtime", (getter)PyWindowItem_createtime_get, NULL, + PyWindowItem_createtime_doc, NULL}, + {"data_level", (getter)PyWindowItem_data_level_get, NULL, + PyWindowItem_data_level_doc, NULL}, + {"hilight_color", (getter)PyWindowItem_hilight_color_get, NULL, + PyWindowItem_hilight_color_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyWindowItem_prnt_doc, + "prnt(str, level) -> None\n" + "\n" + "Print to window item\n" +); +static PyObject *PyWindowItem_prnt(PyWindowItem *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"str", "level", NULL}; + char *str; + int level = MSGLEVEL_CLIENTNOTICE; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &str, &level)) + return NULL; + + printtext_string(self->data->server, self->data->visible_name, level, str); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_command_doc, + "command(cmd) -> None\n" + "\n" + "Send command to window item\n" +); +static PyObject *PyWindowItem_command(PyWindowItem *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", NULL}; + char *cmd; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &cmd)) + return NULL; + + py_command(cmd, self->data->server, self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_window_doc, + "window() -> Window object or None\n" + "\n" + "Return parent window for window item\n" +); +static PyObject *PyWindowItem_window(PyWindowItem *self, PyObject *args) +{ + WINDOW_REC *win; + + RET_NULL_IF_INVALID(self->data); + + win = window_item_window(self->data); + if (win) + return pywindow_new(win); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_change_server_doc, + "change_server(server) -> None\n" + "\n" + "Change server for window item\n" +); +static PyObject *PyWindowItem_change_server(PyWindowItem *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"server", NULL}; + PyObject *server = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &server)) + return NULL; + + if (!pyserver_check(server)) + return PyErr_Format(PyExc_TypeError, "arg must be server"); + + window_item_change_server(self->data, ((PyServer*)server)->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_is_active_doc, + "is_active() -> bool\n" + "\n" + "Returns true if window item is active\n" +); +static PyObject *PyWindowItem_is_active(PyWindowItem *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + return PyBool_FromLong(window_item_is_active(self->data)); +} + +PyDoc_STRVAR(PyWindowItem_set_active_doc, + "set_active() -> None\n" + "\n" + "Set window item active\n" +); +static PyObject *PyWindowItem_set_active(PyWindowItem *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + window_item_set_active(window_item_window(self->data), self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_activity_doc, + "activity(data_level, hilight_color) -> None\n" + "\n" +); +static PyObject *PyWindowItem_activity(PyWindowItem *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"data_level", "hilight_color", NULL}; + int data_level = 0; + char *hilight_color = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "is", kwlist, + &data_level, &hilight_color)) + return NULL; + + window_item_activity(self->data, data_level, hilight_color); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_destroy_doc, + "destroy() -> None\n" + "\n" + "Destroy channel or query\n" +); +static PyObject *PyWindowItem_destroy(PyWindowItem *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + window_item_destroy(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindowItem_get_dcc_doc, + "get_dcc() -> DccChat object or None\n" + "\n" + "If item is a query of a =nick, return DCC chat record of nick\n" +); +static PyObject *PyWindowItem_get_dcc(PyWindowItem *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + return py_irssi_new(self->data, 1); +} + +/* Methods for object */ +static PyMethodDef PyWindowItem_methods[] = { + {"prnt", (PyCFunction)PyWindowItem_prnt, METH_VARARGS | METH_KEYWORDS, + PyWindowItem_prnt_doc}, + {"command", (PyCFunction)PyWindowItem_command, METH_VARARGS | METH_KEYWORDS, + PyWindowItem_command_doc}, + {"window", (PyCFunction)PyWindowItem_window, METH_NOARGS, + PyWindowItem_window_doc}, + {"change_server", (PyCFunction)PyWindowItem_change_server, METH_VARARGS | METH_KEYWORDS, + PyWindowItem_change_server_doc}, + {"is_active", (PyCFunction)PyWindowItem_is_active, METH_NOARGS, + PyWindowItem_is_active_doc}, + {"set_active", (PyCFunction)PyWindowItem_set_active, METH_NOARGS, + PyWindowItem_set_active_doc}, + {"activity", (PyCFunction)PyWindowItem_activity, METH_VARARGS | METH_KEYWORDS, + PyWindowItem_activity_doc}, + {"destroy", (PyCFunction)PyWindowItem_destroy, METH_NOARGS, + PyWindowItem_destroy_doc}, + {"get_dcc", (PyCFunction)PyWindowItem_get_dcc, METH_NOARGS, + PyWindowItem_get_dcc_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyWindowItemType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "WindowItem", /*tp_name*/ + sizeof(PyWindowItem), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyWindowItem objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyWindowItem_methods, /* tp_methods */ + 0, /* tp_members */ + PyWindowItem_getseters, /* tp_getset */ + &PyIrssiChatBaseType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pywindow_item_sub_new(void *witem, const char *name, PyTypeObject *subclass) +{ + WI_ITEM_REC *rec = witem; + PyWindowItem *pywitem = NULL; + PyObject *server; + + g_return_val_if_fail(witem != NULL, NULL); + + server = py_irssi_chat_new(rec->server, 1); + if (!server) + return NULL; + + pywitem = py_instp(PyWindowItem, subclass); + if (!pywitem) + return NULL; + + pywitem->data = witem; + pywitem->base_name = name; + pywitem->server = server; + + return (PyObject *)pywitem; +} + +PyObject *pywindow_item_new(void *witem) +{ + return pywindow_item_sub_new(witem, NULL, &PyWindowItemType); +} + +int window_item_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyWindowItemType) < 0) + return 0; + + Py_INCREF(&PyWindowItemType); + PyModule_AddObject(py_module, "WindowItem", (PyObject *)&PyWindowItemType); + + return 1; +} diff --git a/src/objects/window-item-object.h b/src/objects/window-item-object.h new file mode 100644 index 0000000..e205ebe --- /dev/null +++ b/src/objects/window-item-object.h @@ -0,0 +1,26 @@ +#ifndef _WITEM_OBJECT_H_ +#define _WITEM_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +#define PyWindowItem_HEAD(type) \ + PyIrssi_HEAD(type) \ + PyObject *server; + +/* forward */ +struct _WI_ITEM_REC; + +typedef struct +{ + PyWindowItem_HEAD(struct _WI_ITEM_REC) +} PyWindowItem; + +extern PyTypeObject PyWindowItemType; + +int window_item_object_init(void); +PyObject *pywindow_item_sub_new(void *witem, const char *name, PyTypeObject *subclass); +PyObject *pywindow_item_new(void *witem); +#define pywindow_item_check(op) PyObject_TypeCheck(op, &PyWindowItemType) + +#endif diff --git a/src/objects/window-object.c b/src/objects/window-object.c new file mode 100644 index 0000000..d5aa475 --- /dev/null +++ b/src/objects/window-object.c @@ -0,0 +1,678 @@ +#include <Python.h> +#include "pyirssi_irc.h" +#include "pymodule.h" +#include "window-object.h" +#include "factory.h" +#include "pycore.h" +#include "pyutils.h" + +/* monitor "window destroyed" signal */ +static void window_cleanup(WINDOW_REC *win) +{ + PyWindow *pywindow = signal_get_user_data(); + + if (win == pywindow->data) + { + pywindow->data = NULL; + pywindow->cleanup_installed = 0; + signal_remove_data("window destroyed", window_cleanup, pywindow); + } +} + +static void PyWindow_dealloc(PyWindow *self) +{ + if (self->cleanup_installed) + signal_remove_data("window destroyed", window_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyWindow_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyWindow *self; + + self = (PyWindow *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +PyDoc_STRVAR(PyWindow_refnum_doc, + "Reference number" +); +static PyObject *PyWindow_refnum_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->refnum); +} + +PyDoc_STRVAR(PyWindow_name_doc, + "Name" +); +static PyObject *PyWindow_name_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->name); +} + +PyDoc_STRVAR(PyWindow_width_doc, + "Width" +); +static PyObject *PyWindow_width_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->width); +} + +PyDoc_STRVAR(PyWindow_height_doc, + "Height" +); +static PyObject *PyWindow_height_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->height); +} + +PyDoc_STRVAR(PyWindow_history_name_doc, + "Name of named historylist for this window" +); +static PyObject *PyWindow_history_name_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->history_name); +} + +PyDoc_STRVAR(PyWindow_active_doc, + "Active window item" +); +static PyObject *PyWindow_active_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return py_irssi_chat_new(self->data->active, 1); +} + +PyDoc_STRVAR(PyWindow_active_server_doc, + "Active server" +); +static PyObject *PyWindow_active_server_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return py_irssi_chat_new(self->data->active_server, 1); +} + +PyDoc_STRVAR(PyWindow_servertag_doc, + "active_server must be either None or have this same tag" + "(unless there's items in this window). This is used by" + "/WINDOW SERVER -sticky" +); +static PyObject *PyWindow_servertag_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->servertag); +} + +PyDoc_STRVAR(PyWindow_level_doc, + "Current window level" +); +static PyObject *PyWindow_level_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->level); +} + +PyDoc_STRVAR(PyWindow_sticky_refnum_doc, + "True if reference number is sticky" +); +static PyObject *PyWindow_sticky_refnum_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyBool_FromLong(self->data->sticky_refnum); +} + +PyDoc_STRVAR(PyWindow_data_level_doc, + "Current data level" +); +static PyObject *PyWindow_data_level_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyInt_FromLong(self->data->data_level); +} + +PyDoc_STRVAR(PyWindow_hilight_color_doc, + "Current activity hilight color" +); +static PyObject *PyWindow_hilight_color_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->hilight_color); +} + +PyDoc_STRVAR(PyWindow_last_timestamp_doc, + "Last time timestamp was written in window" +); +static PyObject *PyWindow_last_timestamp_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(self->data->last_timestamp); +} + +PyDoc_STRVAR(PyWindow_last_line_doc, + "Last time text was written in window" +); +static PyObject *PyWindow_last_line_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + return PyLong_FromUnsignedLong(self->data->last_line); +} + +PyDoc_STRVAR(PyWindow_theme_name_doc, + "Active theme in window, None = default" +); +static PyObject *PyWindow_theme_name_get(PyWindow *self, void *closure) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(self->data->theme_name); +} + +/* specialized getters/setters */ +static PyGetSetDef PyWindow_getseters[] = { + {"refnum", (getter)PyWindow_refnum_get, NULL, + PyWindow_refnum_doc, NULL}, + {"name", (getter)PyWindow_name_get, NULL, + PyWindow_name_doc, NULL}, + {"width", (getter)PyWindow_width_get, NULL, + PyWindow_width_doc, NULL}, + {"height", (getter)PyWindow_height_get, NULL, + PyWindow_height_doc, NULL}, + {"history_name", (getter)PyWindow_history_name_get, NULL, + PyWindow_history_name_doc, NULL}, + {"active", (getter)PyWindow_active_get, NULL, + PyWindow_active_doc, NULL}, + {"active_server", (getter)PyWindow_active_server_get, NULL, + PyWindow_active_server_doc, NULL}, + {"servertag", (getter)PyWindow_servertag_get, NULL, + PyWindow_servertag_doc, NULL}, + {"level", (getter)PyWindow_level_get, NULL, + PyWindow_level_doc, NULL}, + {"sticky_refnum", (getter)PyWindow_sticky_refnum_get, NULL, + PyWindow_sticky_refnum_doc, NULL}, + {"data_level", (getter)PyWindow_data_level_get, NULL, + PyWindow_data_level_doc, NULL}, + {"hilight_color", (getter)PyWindow_hilight_color_get, NULL, + PyWindow_hilight_color_doc, NULL}, + {"last_timestamp", (getter)PyWindow_last_timestamp_get, NULL, + PyWindow_last_timestamp_doc, NULL}, + {"last_line", (getter)PyWindow_last_line_get, NULL, + PyWindow_last_line_doc, NULL}, + {"theme_name", (getter)PyWindow_theme_name_get, NULL, + PyWindow_theme_name_doc, NULL}, + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyWindow_items_doc, + "items() -> list of WindowItem objects\n" + "\n" + "Return a list of items in window.\n" +); +static PyObject *PyWindow_items(PyWindow *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + return py_irssi_chatlist_new(self->data->items, 1); +} + +PyDoc_STRVAR(PyWindow_prnt_doc, + "prnt(str, level=MSGLEVEL_CLIENTNOTICE) -> None\n" + "\n" + "Print to window\n" +); +static PyObject *PyWindow_prnt(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"str", "level", NULL}; + char *str = ""; + int level = MSGLEVEL_CLIENTNOTICE; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, + &str, &level)) + return NULL; + + printtext_string_window(self->data, level, str); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_command_doc, + "command(cmd) -> None\n" + "\n" + "Send command to window\n" +); +static PyObject *PyWindow_command(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cmd", NULL}; + char *cmd = ""; + WINDOW_REC *old; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &cmd)) + return NULL; + + old = active_win; + active_win = self->data; + py_command(cmd, active_win->active_server, active_win->active); + if (g_slist_find(windows, old) != NULL) + active_win = old; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_item_add_doc, + "item_add(item, automatic=False) -> None\n" + "\n" + "Add window item\n" +); +static PyObject *PyWindow_item_add(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", "automatic", NULL}; + PyObject *item = NULL; + int automatic = 0; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist, + &item, &automatic)) + return NULL; + + if (!pywindow_item_check(item)) + return PyErr_Format(PyExc_TypeError, "item must be window item"); + + window_item_add(self->data, ((PyWindowItem*)item)->data, automatic); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_item_remove_doc, + "item_remove(item) -> None\n" + "\n" + "Remove window item\n" +); +static PyObject *PyWindow_item_remove(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", NULL}; + PyObject *item = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &item)) + return NULL; + + if (!pywindow_item_check(item)) + return PyErr_Format(PyExc_TypeError, "item must be window item"); + + window_item_remove(((PyWindowItem*)item)->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_item_destroy_doc, + "item_destroy(item) -> None\n" + "\n" + "Destroy window item\n" +); +static PyObject *PyWindow_item_destroy(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"item", NULL}; + PyObject *item = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &item)) + return NULL; + + if (!pywindow_item_check(item)) + return PyErr_Format(PyExc_TypeError, "item must be window item"); + + window_item_destroy(((PyWindowItem*)item)->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_item_prev_doc, + "item_prev() -> None\n" + "\n" + "Change to previous window item\n" +); +static PyObject *PyWindow_item_prev(PyWindow *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + window_item_prev(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_item_next_doc, + "item_next() -> None\n" + "\n" + "Change to next window item\n" +); +static PyObject *PyWindow_item_next(PyWindow *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + window_item_next(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_destroy_doc, + "destroy() -> None\n" + "\n" + "Destroy the window.\n" +); +static PyObject *PyWindow_destroy(PyWindow *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + window_destroy(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_set_active_doc, + "set_active() -> None\n" + "\n" + "Set window active.\n" +); +static PyObject *PyWindow_set_active(PyWindow *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + + window_set_active(self->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_change_server_doc, + "change_server(server) -> None\n" + "\n" + "Change server in window\n" +); +static PyObject *PyWindow_change_server(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"server", NULL}; + PyObject *server = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &server)) + return NULL; + + if (!pyserver_check(server)) + return PyErr_Format(PyExc_TypeError, "arg must be server"); + + window_change_server(self->data, ((PyServer*)server)->data); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_set_refnum_doc, + "set_refnum(refnum) -> None\n" + "\n" + "Set window refnum\n" +); +static PyObject *PyWindow_set_refnum(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"refnum", NULL}; + int refnum = 0; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &refnum)) + return NULL; + + window_set_refnum(self->data, refnum); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_set_name_doc, + "set_name(name) -> None\n" + "\n" + "Set window name\n" +); +static PyObject *PyWindow_set_name(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + char *name = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &name)) + return NULL; + + window_set_name(self->data, name); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_set_history_doc, + "set_history(history) -> None\n" + "\n" + "Set window history\n" +); +static PyObject *PyWindow_set_history(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"history", NULL}; + char *history = ""; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, + &history)) + return NULL; + + window_set_history(self->data, history); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_set_level_doc, + "set_level(level) -> None\n" + "\n" + "Set window level\n" +); +static PyObject *PyWindow_set_level(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"level", NULL}; + int level = 0; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &level)) + return NULL; + + window_set_level(self->data, level); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_activity_doc, + "activity(data_level, hilight_color) -> None\n" + "\n" +); +static PyObject *PyWindow_activity(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"data_level", "hilight_color", NULL}; + int data_level = 0; + char *hilight_color = NULL; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|s", kwlist, + &data_level, &hilight_color)) + return NULL; + + window_activity(self->data, data_level, hilight_color); + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyWindow_get_active_name_doc, + "get_active_name() -> str or None\n" + "\n" + "Return active item's name, or if none is active, window's name.\n" +); +static PyObject *PyWindow_get_active_name(PyWindow *self, PyObject *args) +{ + RET_NULL_IF_INVALID(self->data); + RET_AS_STRING_OR_NONE(window_get_active_name(self->data)); +} + +PyDoc_STRVAR(PyWindow_item_find_doc, + "item_find(server, name) -> WindowItem or None\n" + "\n" + "Find window item that matches best to given arguments\n" +); +static PyObject *PyWindow_item_find(PyWindow *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"server", "name", NULL}; + PyObject *server = NULL; + char *name = ""; + WI_ITEM_REC *witem; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "Os", kwlist, + &server, &name)) + return NULL; + + if (!pyserver_check(server)) + return PyErr_Format(PyExc_TypeError, "arg 1 must be server"); + + witem = window_item_find_window(self->data, ((PyServer*)server)->data, name); + return py_irssi_chat_new(witem, 1); +} + +static PyMethodDef PyWindow_methods[] = { + {"items", (PyCFunction)PyWindow_items, METH_NOARGS, + PyWindow_items_doc}, + {"prnt", (PyCFunction)PyWindow_prnt, METH_VARARGS | METH_KEYWORDS, + PyWindow_prnt_doc}, + {"command", (PyCFunction)PyWindow_command, METH_VARARGS | METH_KEYWORDS, + PyWindow_command_doc}, + {"item_add", (PyCFunction)PyWindow_item_add, METH_VARARGS | METH_KEYWORDS, + PyWindow_item_add_doc}, + {"item_remove", (PyCFunction)PyWindow_item_remove, METH_VARARGS | METH_KEYWORDS, + PyWindow_item_remove_doc}, + {"item_destroy", (PyCFunction)PyWindow_item_destroy, METH_VARARGS | METH_KEYWORDS, + PyWindow_item_destroy_doc}, + {"item_prev", (PyCFunction)PyWindow_item_prev, METH_NOARGS, + PyWindow_item_prev_doc}, + {"item_next", (PyCFunction)PyWindow_item_next, METH_NOARGS, + PyWindow_item_next_doc}, + {"destroy", (PyCFunction)PyWindow_destroy, METH_NOARGS, + PyWindow_destroy_doc}, + {"set_active", (PyCFunction)PyWindow_set_active, METH_NOARGS, + PyWindow_set_active_doc}, + {"change_server", (PyCFunction)PyWindow_change_server, METH_VARARGS | METH_KEYWORDS, + PyWindow_change_server_doc}, + {"set_refnum", (PyCFunction)PyWindow_set_refnum, METH_VARARGS | METH_KEYWORDS, + PyWindow_set_refnum_doc}, + {"set_name", (PyCFunction)PyWindow_set_name, METH_VARARGS | METH_KEYWORDS, + PyWindow_set_name_doc}, + {"set_history", (PyCFunction)PyWindow_set_history, METH_VARARGS | METH_KEYWORDS, + PyWindow_set_history_doc}, + {"set_level", (PyCFunction)PyWindow_set_level, METH_VARARGS | METH_KEYWORDS, + PyWindow_set_level_doc}, + {"activity", (PyCFunction)PyWindow_activity, METH_VARARGS | METH_KEYWORDS, + PyWindow_activity_doc}, + {"get_active_name", (PyCFunction)PyWindow_get_active_name, METH_NOARGS, + PyWindow_get_active_name_doc}, + {"item_find", (PyCFunction)PyWindow_item_find, METH_VARARGS | METH_KEYWORDS, + PyWindow_item_find_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyWindowType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Window", /*tp_name*/ + sizeof(PyWindow), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyWindow_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "PyWindow objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyWindow_methods, /* tp_methods */ + 0, /* tp_members */ + PyWindow_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyWindow_new, /* tp_new */ +}; + + +/* window item wrapper factory function */ +PyObject *pywindow_new(void *win) +{ + PyWindow *pywindow; + + pywindow = py_inst(PyWindow, PyWindowType); + if (!pywindow) + return NULL; + + pywindow->data = win; + pywindow->cleanup_installed = 1; + signal_add_last_data("window destroyed", window_cleanup, pywindow); + + return (PyObject *)pywindow; +} + +int window_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyWindowType) < 0) + return 0; + + Py_INCREF(&PyWindowType); + PyModule_AddObject(py_module, "Window", (PyObject *)&PyWindowType); + + return 1; +} diff --git a/src/objects/window-object.h b/src/objects/window-object.h new file mode 100644 index 0000000..b9860af --- /dev/null +++ b/src/objects/window-object.h @@ -0,0 +1,21 @@ +#ifndef _WINDOW_OBJECT_H_ +#define _WINDOW_OBJECT_H_ + +#include <Python.h> +#include "base-objects.h" + +/* forward */ +struct _WINDOW_REC; + +typedef struct +{ + PyIrssiFinal_HEAD(struct _WINDOW_REC) +} PyWindow; + +extern PyTypeObject PyWindowType; + +int window_object_init(void); +PyObject *pywindow_new(void *win); +#define pywindow_check(op) PyObject_TypeCheck(op, &PyWindowType) + +#endif |