mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-11-08 17:05:38 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
668c3cc0d7 |
No files matched your search
@@ -0,0 +1,24 @@
|
||||
# Core-filtered hooks
|
||||
|
||||
Python: `hook_server_filtered(name, callback, userdata=None, priority=PRI_NORM,
|
||||
connection_id=None, channel=None)` and `hook_print_filtered(...)`.
|
||||
Callbacks match the existing attrs variants: `(word, word_eol, userdata, attrs)`.
|
||||
The connection ID distinguishes duplicate names; None/-1 means any connection
|
||||
(including connection ID zero). A channel restriction uses that connection's
|
||||
IRC comparator. None means any context; an empty string is an exact restriction.
|
||||
|
||||
C: the corresponding zoitechat_hook_*_filtered functions accept
|
||||
`(ph, name, pri, flags, connection_id, channel, callback, userdata)`;
|
||||
flags=0, ID=-1 for any and NULL channel for any. Check
|
||||
`get_info("api_context_filters")` for "1" before accessing the new Windows slots.
|
||||
|
||||
Filtering takes place before Python/interpreter entry. Existing unrestricted
|
||||
hooks, priority, EAT semantics and unload/unhook handling are unchanged.
|
||||
Filters refer to the core-selected context, not an IRC target parsed from the
|
||||
line: unknown queries/channels can still have a server-tab context, and QUIT
|
||||
has its usual single server-hook context. Connection IDs last only this process.
|
||||
|
||||
```python
|
||||
zoitechat.hook_print_filtered('Channel Message', callback,
|
||||
connection_id=zoitechat.get_prefs('id'), channel='#zoitechat')
|
||||
```
|
||||
@@ -1,21 +0,0 @@
|
||||
# Explicit IRC comparison keys
|
||||
|
||||
`IRCName(text, context=None, casemapping=None, connection_id=None)` is an
|
||||
immutable helper, not a str subclass. Construct it explicitly for dictionary/set
|
||||
keys; existing get_info/list/event values remain normal strings.
|
||||
Equality and hashing include the connection ID and captured casemapping, so
|
||||
identical-looking names on different connections are different keys. An IRCName
|
||||
never compares equal to a plain str. `str(key)` / `key.text` recover the spelling.
|
||||
|
||||
`irc_casefold(text, casemapping='rfc1459')` supports ascii, strict-rfc1459 and
|
||||
rfc1459. Only ASCII IRC characters fold; Unicode casefold would be incorrect.
|
||||
The new `get_info('casemapping')` reports the comparator actually used by core.
|
||||
Current master handles ASCII and RFC1459; this patch does not change its handling
|
||||
of advertised strict-rfc1459. An explicit mapping is available for scripts that
|
||||
need strict-rfc1459 independently. Keys capture their mapping so hashes stay
|
||||
stable; rebuild a collection if the connection's mapping changes.
|
||||
|
||||
```python
|
||||
seen = {zoitechat.IRCName('[SomeNick]')}
|
||||
assert zoitechat.IRCName('{somenick}') in seen # RFC1459 connection
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
"""Context restrictions evaluated by core before entering Python."""
|
||||
import operator
|
||||
import _zoitechat as api
|
||||
from _zoitechat_embedded import ffi, lib
|
||||
|
||||
__all__ = ['hook_server_filtered', 'hook_print_filtered']
|
||||
|
||||
|
||||
def _register(name, callback, userdata, priority, connection_id, channel, server):
|
||||
if not callable(callback):
|
||||
raise TypeError('callback must be callable')
|
||||
connection_id = -1 if connection_id is None else operator.index(connection_id)
|
||||
if connection_id < -1 or connection_id > 2147483647:
|
||||
raise ValueError('connection_id must fit the C API; -1 means any')
|
||||
plugin = api.__get_current_plugin()
|
||||
hook = plugin.add_hook(callback, userdata)
|
||||
register = lib.zoitechat_hook_server_filtered if server else lib.zoitechat_hook_print_filtered
|
||||
dispatch = lib._on_server_attrs_hook if server else lib._on_print_attrs_hook
|
||||
handle = register(lib.ph, name.encode(), priority, 0, connection_id,
|
||||
ffi.NULL if channel is None else channel.encode(), dispatch, hook.handle)
|
||||
if handle == ffi.NULL:
|
||||
hook.is_unload = True
|
||||
plugin.remove_hook(id(hook))
|
||||
raise RuntimeError('unable to install filtered hook')
|
||||
hook.zoitechat_hook = handle
|
||||
return id(hook)
|
||||
|
||||
|
||||
def hook_server_filtered(name, callback, userdata=None, priority=api.PRI_NORM,
|
||||
connection_id=None, channel=None):
|
||||
return _register(name, callback, userdata, priority, connection_id, channel, True)
|
||||
|
||||
|
||||
def hook_print_filtered(name, callback, userdata=None, priority=api.PRI_NORM,
|
||||
connection_id=None, channel=None):
|
||||
return _register(name, callback, userdata, priority, connection_id, channel, False)
|
||||
@@ -1,74 +0,0 @@
|
||||
"""Explicit immutable IRC names; existing string-returning APIs are unchanged."""
|
||||
import _zoitechat as api
|
||||
|
||||
__all__ = ['IRCName', 'irc_casefold']
|
||||
|
||||
|
||||
def irc_casefold(text, casemapping='rfc1459'):
|
||||
"""Fold ASCII IRC characters only; never apply Unicode lower()/casefold()."""
|
||||
if casemapping not in ('ascii', 'strict-rfc1459', 'rfc1459'):
|
||||
raise ValueError('unsupported IRC casemapping')
|
||||
extra = {'[': '{', ']': '}', '\\': '|'} if casemapping != 'ascii' else {}
|
||||
if casemapping == 'rfc1459':
|
||||
extra['^'] = '~'
|
||||
return ''.join(chr(ord(c) + 32) if 'A' <= c <= 'Z' else extra.get(c, c)
|
||||
for c in text)
|
||||
|
||||
|
||||
class IRCName:
|
||||
"""Equality/hash include connection identity and the captured casemapping."""
|
||||
__slots__ = ('_text', '_casemapping', '_connection_id', '_key')
|
||||
|
||||
def __init__(self, text, context=None, casemapping=None, connection_id=None):
|
||||
context = context or api.get_context()
|
||||
if casemapping is None:
|
||||
casemapping = context.get_info('casemapping')
|
||||
if casemapping is None:
|
||||
raise RuntimeError('host does not expose its comparison casemapping')
|
||||
if connection_id is None:
|
||||
previous = api.get_context()
|
||||
if not context.set():
|
||||
raise ValueError('context has closed')
|
||||
try:
|
||||
connection_id = api.get_prefs('id')
|
||||
finally:
|
||||
previous.set()
|
||||
object.__setattr__(self, '_text', text)
|
||||
object.__setattr__(self, '_casemapping', casemapping)
|
||||
object.__setattr__(self, '_connection_id', connection_id)
|
||||
object.__setattr__(self, '_key', (connection_id, casemapping,
|
||||
irc_casefold(text, casemapping)))
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
raise AttributeError('IRCName is immutable')
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return self._text
|
||||
|
||||
@property
|
||||
def casemapping(self):
|
||||
return self._casemapping
|
||||
|
||||
@property
|
||||
def connection_id(self):
|
||||
return self._connection_id
|
||||
|
||||
def __str__(self):
|
||||
return self._text
|
||||
|
||||
def __repr__(self):
|
||||
return 'IRCName({!r}, casemapping={!r}, connection_id={!r})'.format(
|
||||
self._text, self._casemapping, self._connection_id)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, IRCName):
|
||||
return NotImplemented
|
||||
return self._key == other._key
|
||||
|
||||
def __ne__(self, other):
|
||||
result = self.__eq__(other)
|
||||
return NotImplemented if result is NotImplemented else not result
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self._key)
|
||||
+51
-9
@@ -99,6 +99,9 @@ struct _zoitechat_hook
|
||||
int tag; /* for timers & FDs only */
|
||||
int type; /* HOOK_* */
|
||||
int pri; /* fd */ /* priority / fd for HOOK_FD only */
|
||||
int connection_filter;
|
||||
char *channel_filter;
|
||||
gboolean context_filtered;
|
||||
};
|
||||
|
||||
struct _zoitechat_list
|
||||
@@ -351,6 +354,8 @@ plugin_add (session *sess, char *filename, void *handle, void *init_func,
|
||||
pl->zoitechat_emit_print_attrs = zoitechat_emit_print_attrs;
|
||||
pl->zoitechat_event_attrs_create = zoitechat_event_attrs_create;
|
||||
pl->zoitechat_event_attrs_free = zoitechat_event_attrs_free;
|
||||
pl->zoitechat_hook_server_filtered = zoitechat_hook_server_filtered;
|
||||
pl->zoitechat_hook_print_filtered = zoitechat_hook_print_filtered;
|
||||
|
||||
/* run zoitechat_plugin_init, if it returns 0, close the plugin */
|
||||
if (((zoitechat_init_func *)init_func) (pl, &pl->name, &pl->desc, &pl->version, arg) == 0)
|
||||
@@ -621,6 +626,15 @@ plugin_hook_run (session *sess, char *name, char *word[], char *word_eol[],
|
||||
|
||||
hook = list->data;
|
||||
next = list->next;
|
||||
if (hook->context_filtered &&
|
||||
(!is_session (sess) ||
|
||||
(hook->connection_filter != -1 && hook->connection_filter != sess->server->id) ||
|
||||
(hook->channel_filter && sess->server->p_cmp (hook->channel_filter, sess->channel))))
|
||||
{
|
||||
list = next;
|
||||
continue; /* filter in C before entering an interpreter */
|
||||
}
|
||||
|
||||
hook->pl->context = sess;
|
||||
|
||||
/* run the plugin's callback function */
|
||||
@@ -1024,10 +1038,45 @@ zoitechat_unhook (zoitechat_plugin *ph, zoitechat_hook *hook)
|
||||
|
||||
g_free (hook->name); /* NULL for timers & fds */
|
||||
g_free (hook->help_text); /* NULL for non-commands */
|
||||
g_free (hook->channel_filter);
|
||||
hook->channel_filter = NULL;
|
||||
|
||||
return hook->userdata;
|
||||
}
|
||||
|
||||
static zoitechat_hook *
|
||||
plugin_add_filtered_hook (zoitechat_plugin *ph, const char *name, int pri,
|
||||
int flags, int connection_id, const char *channel,
|
||||
int type, void *callback, void *userdata)
|
||||
{
|
||||
zoitechat_hook *hook;
|
||||
if (flags != 0 || connection_id < -1 || !name || !callback)
|
||||
return NULL;
|
||||
hook = plugin_add_hook (ph, type, pri, name, NULL, callback, 0, userdata);
|
||||
hook->context_filtered = TRUE;
|
||||
hook->connection_filter = connection_id;
|
||||
hook->channel_filter = g_strdup (channel);
|
||||
return hook;
|
||||
}
|
||||
|
||||
zoitechat_hook *
|
||||
zoitechat_hook_server_filtered (zoitechat_plugin *ph, const char *name, int pri,
|
||||
int flags, int connection_id, const char *channel,
|
||||
zoitechat_serv_attrs_cb *callback, void *userdata)
|
||||
{
|
||||
return plugin_add_filtered_hook (ph, name, pri, flags, connection_id, channel,
|
||||
HOOK_SERVER_ATTRS, callback, userdata);
|
||||
}
|
||||
|
||||
zoitechat_hook *
|
||||
zoitechat_hook_print_filtered (zoitechat_plugin *ph, const char *name, int pri,
|
||||
int flags, int connection_id, const char *channel,
|
||||
zoitechat_print_attrs_cb *callback, void *userdata)
|
||||
{
|
||||
return plugin_add_filtered_hook (ph, name, pri, flags, connection_id, channel,
|
||||
HOOK_PRINT_ATTRS, callback, userdata);
|
||||
}
|
||||
|
||||
zoitechat_hook *
|
||||
zoitechat_hook_command (zoitechat_plugin *ph, const char *name, int pri,
|
||||
zoitechat_cmd_cb *callb, const char *help_text, void *userdata)
|
||||
@@ -1187,15 +1236,8 @@ zoitechat_get_info (zoitechat_plugin *ph, const char *id)
|
||||
return text_find_format_string (e);
|
||||
}
|
||||
|
||||
/* Report the comparison actually used by this connection, not an
|
||||
* advertised mapping that the current core does not implement. */
|
||||
if (strcmp (id, "casemapping") == 0)
|
||||
{
|
||||
if (!is_session (ph->context))
|
||||
return NULL;
|
||||
return ph->context->server->p_cmp == g_ascii_strcasecmp
|
||||
? "ascii" : "rfc1459";
|
||||
}
|
||||
if (strcmp (id, "api_context_filters") == 0)
|
||||
return "1";
|
||||
|
||||
hash = str_hash (id);
|
||||
/* do the session independant ones first */
|
||||
|
||||
@@ -206,6 +206,18 @@ struct _zoitechat_plugin
|
||||
#endif
|
||||
|
||||
|
||||
/* api_context_filters=1: connection_id -1 is any; NULL channel is any. */
|
||||
zoitechat_hook *zoitechat_hook_server_filtered (zoitechat_plugin *ph,
|
||||
const char *name, int pri, int flags, int connection_id, const char *channel,
|
||||
int (*callback) (char *word[], char *word_eol[],
|
||||
zoitechat_event_attrs *attrs, void *userdata),
|
||||
void *userdata);
|
||||
|
||||
zoitechat_hook *zoitechat_hook_print_filtered (zoitechat_plugin *ph,
|
||||
const char *name, int pri, int flags, int connection_id, const char *channel,
|
||||
int (*callback) (char *word[], zoitechat_event_attrs *attrs, void *userdata),
|
||||
void *userdata);
|
||||
|
||||
zoitechat_hook *
|
||||
zoitechat_hook_command (zoitechat_plugin *ph,
|
||||
const char *name,
|
||||
@@ -425,6 +437,8 @@ zoitechat_pluginpref_list (zoitechat_plugin *ph,
|
||||
#define zoitechat_hook_server ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_server)
|
||||
#define zoitechat_hook_server_attrs ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_server_attrs)
|
||||
#define zoitechat_hook_print ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_print)
|
||||
#define zoitechat_hook_server_filtered ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_server_filtered)
|
||||
#define zoitechat_hook_print_filtered ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_print_filtered)
|
||||
#define zoitechat_hook_print_attrs ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_print_attrs)
|
||||
#define zoitechat_hook_timer ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_timer)
|
||||
#define zoitechat_hook_fd ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_fd)
|
||||
|
||||
Reference in new issue
Block a user