mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-11-08 17:05:38 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
111b1fa0ad |
No files matched your search
@@ -0,0 +1,35 @@
|
|||||||
|
# Script-supported capabilities
|
||||||
|
|
||||||
|
`register_capability(name, userdata=None, connection_id=None)` returns a normal
|
||||||
|
hook handle. Register before connecting/reconnecting. Core requests the name only
|
||||||
|
if advertised in CAP LS or CAP NEW. Existing built-in requests remain enabled;
|
||||||
|
SASL and STS registration is rejected so scripts cannot change authentication or
|
||||||
|
transport policy through this API. None/-1 means any connection (zero is a valid
|
||||||
|
connection ID). Names are lowercase ASCII alphanumerics plus - . / _, 1-200 bytes.
|
||||||
|
Duplicate registrations do not duplicate requests for an advertised token.
|
||||||
|
|
||||||
|
`unhook(handle)` or script/plugin unload removes future request ownership. It
|
||||||
|
intentionally does not disable a capability already acknowledged on a live
|
||||||
|
connection, because another plugin/core may depend on it. Registering after CAP
|
||||||
|
LS does not retrospectively negotiate an already-advertised capability: reconnect
|
||||||
|
or wait for CAP NEW. Core retains ownership of CAP END; this API does not extend
|
||||||
|
SASL waiting or delay registration awaiting plugin-only CAP ACKs.
|
||||||
|
|
||||||
|
`hook_capability(name, callback, userdata=None, priority=PRI_NORM)` independently
|
||||||
|
observes negotiation through existing raw CAP hooks. Callback receives
|
||||||
|
`(CapabilityEvent, userdata)` with name, subcommand, value, enabled, context and
|
||||||
|
connection_id. LS/NEW mean advertised (`enabled=None`), ACK/LIST mean enabled
|
||||||
|
(unless a negative ACK), NAK means rejected and DEL means removed. Callback
|
||||||
|
returns are ignored so this observer cannot eat core negotiation. Existing raw
|
||||||
|
hooks may still eat CAP before this hook according to their normal priorities.
|
||||||
|
|
||||||
|
C: `zoitechat_register_capability(ph, name, flags, connection_id, userdata)`;
|
||||||
|
flags=0, ID=-1 for any. Check get_info("api_plugin_caps") for "1" before using
|
||||||
|
the appended Windows slot. Use existing server CAP hooks for notifications.
|
||||||
|
Requests are split at complete token boundaries within the existing wire limit.
|
||||||
|
|
||||||
|
```python
|
||||||
|
registration = zoitechat.register_capability('draft/example')
|
||||||
|
zoitechat.hook_capability('draft/example', lambda event, data:
|
||||||
|
event.context.prnt('{}: {}'.format(event.subcommand, event.enabled)))
|
||||||
|
```
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Connection and context navigation
|
|
||||||
|
|
||||||
`get_connections()` returns one Connection snapshot per live connection:
|
|
||||||
`id`, `network`, `server`, `context`. Connection IDs distinguish simultaneous
|
|
||||||
connections with identical names. Prefer its server-tab context where present.
|
|
||||||
`get_connection(context=None)` finds the owner of a live context.
|
|
||||||
|
|
||||||
`get_contexts(connection_id=None, types=None)` lists live contexts, optionally
|
|
||||||
filtered by connection ID (or a Connection object) and session types: server=1,
|
|
||||||
channel=2, query=3. `connection.contexts(types=None)` is a convenience method.
|
|
||||||
`find_context_by_id(connection_id, channel=None)` / `connection.find(channel)`
|
|
||||||
resolve using that connection's IRC comparator; no channel selects the server tab.
|
|
||||||
Missing/closed contexts return None. Existing find_context() remains unchanged.
|
|
||||||
|
|
||||||
These helpers use the existing channels list's ID/context fields, so no new C
|
|
||||||
context ABI is needed. Snapshots are not persistent IDs across client restarts;
|
|
||||||
refresh them after closing/recreating connections. Always check Context.set()
|
|
||||||
when explicitly selecting a context that might have closed.
|
|
||||||
|
|
||||||
```python
|
|
||||||
connection = zoitechat.get_connection()
|
|
||||||
for context in connection.contexts(types=(2,)):
|
|
||||||
context.prnt('A channel on this exact connection')
|
|
||||||
```
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
"""Script-owned capability requests and structured negotiation observations."""
|
||||||
|
from collections import namedtuple
|
||||||
|
import operator
|
||||||
|
import _zoitechat as api
|
||||||
|
from _zoitechat_embedded import ffi, lib
|
||||||
|
|
||||||
|
__all__ = ['CapabilityEvent', 'register_capability', 'hook_capability']
|
||||||
|
CapabilityEvent = namedtuple('CapabilityEvent',
|
||||||
|
'name subcommand value enabled context connection_id')
|
||||||
|
|
||||||
|
|
||||||
|
def register_capability(name, userdata=None, connection_id=None):
|
||||||
|
"""Request an advertised capability on next negotiation; unhook releases ownership."""
|
||||||
|
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(None, userdata)
|
||||||
|
handle = lib.zoitechat_register_capability(lib.ph, name.encode(), 0,
|
||||||
|
connection_id, hook.handle)
|
||||||
|
if handle == ffi.NULL:
|
||||||
|
hook.is_unload = True
|
||||||
|
plugin.remove_hook(id(hook))
|
||||||
|
raise ValueError('invalid capability name, reserved capability or connection ID')
|
||||||
|
hook.zoitechat_hook = handle
|
||||||
|
return id(hook)
|
||||||
|
|
||||||
|
|
||||||
|
def hook_capability(name, callback, userdata=None, priority=api.PRI_NORM):
|
||||||
|
"""Observe LS/NEW/ACK/NAK/DEL/LIST; this hook does not itself request the capability."""
|
||||||
|
if not callable(callback):
|
||||||
|
raise TypeError('callback must be callable')
|
||||||
|
|
||||||
|
def dispatch(word, word_eol, data):
|
||||||
|
if not word_eol:
|
||||||
|
return api.EAT_NONE
|
||||||
|
line = word_eol[0]
|
||||||
|
if line.startswith(':'):
|
||||||
|
unused, separator, line = line.partition(' ')
|
||||||
|
parts = line.split(' ', 3)
|
||||||
|
if len(parts) != 4 or parts[0].upper() != 'CAP':
|
||||||
|
return api.EAT_NONE
|
||||||
|
subcommand, tokens = parts[2].upper(), parts[3]
|
||||||
|
if subcommand not in ('LS', 'NEW', 'ACK', 'NAK', 'DEL', 'LIST'):
|
||||||
|
return api.EAT_NONE
|
||||||
|
if tokens.startswith('* '):
|
||||||
|
tokens = tokens[2:]
|
||||||
|
tokens = tokens.lstrip(':')
|
||||||
|
for token in tokens.split():
|
||||||
|
negative = token.startswith('-')
|
||||||
|
cap, equals, value = token.lstrip('-').partition('=')
|
||||||
|
if cap != name:
|
||||||
|
continue
|
||||||
|
enabled = None
|
||||||
|
if subcommand in ('ACK', 'LIST'):
|
||||||
|
enabled = not negative
|
||||||
|
elif subcommand in ('DEL', 'NAK'):
|
||||||
|
enabled = False
|
||||||
|
event = CapabilityEvent(cap, subcommand, value if equals else None,
|
||||||
|
enabled, api.get_context(), api.get_prefs('id'))
|
||||||
|
callback(event, data)
|
||||||
|
return api.EAT_NONE # observing CAP must not suppress core negotiation
|
||||||
|
|
||||||
|
return api.hook_server('CAP', dispatch, userdata, priority)
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
"""Navigate live connections by ID instead of ambiguous server/network names."""
|
|
||||||
from collections import namedtuple
|
|
||||||
import operator
|
|
||||||
import _zoitechat as api
|
|
||||||
|
|
||||||
__all__ = ['Connection', 'get_connections', 'get_connection', 'get_contexts',
|
|
||||||
'find_context_by_id']
|
|
||||||
|
|
||||||
|
|
||||||
class Connection(namedtuple('_Connection', 'id network server context')):
|
|
||||||
__slots__ = ()
|
|
||||||
|
|
||||||
def contexts(self, types=None):
|
|
||||||
return get_contexts(self.id, types)
|
|
||||||
|
|
||||||
def find(self, channel=None):
|
|
||||||
return find_context_by_id(self.id, channel)
|
|
||||||
|
|
||||||
|
|
||||||
def _id(connection):
|
|
||||||
return operator.index(connection.id if isinstance(connection, Connection) else connection)
|
|
||||||
|
|
||||||
|
|
||||||
def get_connections():
|
|
||||||
"""Return one snapshot per live connection, including disconnected ones."""
|
|
||||||
entries = api.get_list('channels')
|
|
||||||
by_id = {}
|
|
||||||
order = []
|
|
||||||
for entry in entries:
|
|
||||||
if entry.id not in by_id:
|
|
||||||
order.append(entry.id)
|
|
||||||
if entry.id not in by_id or entry.type == 1:
|
|
||||||
by_id[entry.id] = Connection(entry.id, entry.network, entry.server,
|
|
||||||
entry.context)
|
|
||||||
return [by_id[connection_id] for connection_id in order]
|
|
||||||
|
|
||||||
|
|
||||||
def get_connection(context=None):
|
|
||||||
"""Find the owning connection of a Context; return None when it has closed."""
|
|
||||||
context = context or api.get_context()
|
|
||||||
for entry in api.get_list('channels'):
|
|
||||||
if entry.context == context:
|
|
||||||
for connection in get_connections():
|
|
||||||
if connection.id == entry.id:
|
|
||||||
return connection
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_contexts(connection_id=None, types=None):
|
|
||||||
"""Return contexts, optionally restricted to a connection and session types."""
|
|
||||||
if connection_id is not None:
|
|
||||||
connection_id = _id(connection_id)
|
|
||||||
if types is not None:
|
|
||||||
types = frozenset(types)
|
|
||||||
return [entry.context for entry in api.get_list('channels')
|
|
||||||
if (connection_id is None or entry.id == connection_id)
|
|
||||||
and (types is None or entry.type in types)]
|
|
||||||
|
|
||||||
|
|
||||||
def find_context_by_id(connection_id, channel=None):
|
|
||||||
"""Resolve a live context on exactly one connection; None selects its server tab."""
|
|
||||||
connection_id = _id(connection_id)
|
|
||||||
previous = api.get_context()
|
|
||||||
try:
|
|
||||||
for entry in api.get_list('channels'):
|
|
||||||
if entry.id != connection_id:
|
|
||||||
continue
|
|
||||||
if channel is None:
|
|
||||||
if entry.type == 1:
|
|
||||||
return entry.context
|
|
||||||
elif entry.context.set() and api.nickcmp(entry.channel, channel) == 0:
|
|
||||||
return entry.context
|
|
||||||
finally:
|
|
||||||
previous.set()
|
|
||||||
return None
|
|
||||||
+73
-7
@@ -41,6 +41,8 @@
|
|||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "notify.h"
|
#include "notify.h"
|
||||||
#include "outbound.h"
|
#include "outbound.h"
|
||||||
|
#include <gmodule.h>
|
||||||
|
#include "plugin.h"
|
||||||
#include "inbound.h"
|
#include "inbound.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "servlist.h"
|
#include "servlist.h"
|
||||||
@@ -1980,6 +1982,22 @@ inbound_cap_ack (server *serv, char *nick, char *extensions,
|
|||||||
inbound_toggle_caps (serv, extensions, TRUE);
|
inbound_toggle_caps (serv, extensions, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Keep CAP REQ tokens intact and below the existing 500-byte buffer. */
|
||||||
|
static void
|
||||||
|
inbound_cap_request_append (server *serv, char *buffer, gsize size,
|
||||||
|
const char *name, time_t timestamp)
|
||||||
|
{
|
||||||
|
if (strlen (buffer) + strlen (name) + 1 >= size)
|
||||||
|
{
|
||||||
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPREQ, serv->server_session,
|
||||||
|
buffer + 9, NULL, NULL, NULL, 0, timestamp);
|
||||||
|
tcp_sendf (serv, "%s\r\n", g_strchomp (buffer));
|
||||||
|
g_strlcpy (buffer, "CAP REQ :", size);
|
||||||
|
}
|
||||||
|
g_strlcat (buffer, name, size);
|
||||||
|
g_strlcat (buffer, " ", size);
|
||||||
|
}
|
||||||
|
|
||||||
static void inbound_request_new_caps (server *serv, const char *extensions_str,
|
static void inbound_request_new_caps (server *serv, const char *extensions_str,
|
||||||
const message_tags_data *tags_data);
|
const message_tags_data *tags_data);
|
||||||
|
|
||||||
@@ -2094,6 +2112,32 @@ inbound_request_new_caps (server *serv, const char *extensions_str,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
/* Also request plugin-owned capabilities, without repeating core requests. */
|
||||||
|
for (i = 0; extensions[i]; i++)
|
||||||
|
{
|
||||||
|
gsize previous;
|
||||||
|
gboolean builtin = FALSE;
|
||||||
|
for (x = 0; x < G_N_ELEMENTS (supported_caps); x++)
|
||||||
|
{
|
||||||
|
if (!strcmp (extensions[i], supported_caps[x]))
|
||||||
|
{
|
||||||
|
builtin = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (builtin || !plugin_requests_capability (serv, extensions[i]))
|
||||||
|
continue;
|
||||||
|
for (previous = 0; previous < i; previous++)
|
||||||
|
{
|
||||||
|
if (!strcmp (extensions[previous], extensions[i]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (previous != i)
|
||||||
|
continue;
|
||||||
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPREQ, serv->server_session,
|
||||||
|
extensions[i], NULL, NULL, NULL, 0, tags_data->timestamp);
|
||||||
|
tcp_sendf (serv, "CAP REQ :%s\r\n", extensions[i]);
|
||||||
}
|
}
|
||||||
g_strfreev (extensions);
|
g_strfreev (extensions);
|
||||||
}
|
}
|
||||||
@@ -2161,6 +2205,8 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str,
|
|||||||
gboolean want_cap = FALSE; /* format the CAP REQ string based on previous capabilities being requested or not */
|
gboolean want_cap = FALSE; /* format the CAP REQ string based on previous capabilities being requested or not */
|
||||||
gboolean sts_upgrade_triggered = FALSE;
|
gboolean sts_upgrade_triggered = FALSE;
|
||||||
char **extensions;
|
char **extensions;
|
||||||
|
GString *requests = g_string_new (NULL);
|
||||||
|
GHashTable *seen = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (g_str_has_prefix (extensions_str, "* "))
|
if (g_str_has_prefix (extensions_str, "* "))
|
||||||
@@ -2226,16 +2272,23 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str,
|
|||||||
}
|
}
|
||||||
want_cap = TRUE;
|
want_cap = TRUE;
|
||||||
serv->waiting_on_sasl = TRUE;
|
serv->waiting_on_sasl = TRUE;
|
||||||
g_strlcat (buffer, "sasl ", sizeof(buffer));
|
if (g_hash_table_add (seen, g_strdup ("sasl")))
|
||||||
|
g_string_append (requests, "sasl ");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (x = 0; x < G_N_ELEMENTS(supported_caps); ++x)
|
for (x = 0; x < G_N_ELEMENTS (supported_caps); ++x)
|
||||||
{
|
{
|
||||||
if (!g_strcmp0 (extension, supported_caps[x]))
|
if (!g_strcmp0 (extension, supported_caps[x]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (x < G_N_ELEMENTS (supported_caps) ||
|
||||||
|
plugin_requests_capability (serv, extension))
|
||||||
{
|
{
|
||||||
g_strlcat (buffer, extension, sizeof(buffer));
|
if (g_hash_table_add (seen, g_strdup (extension)))
|
||||||
g_strlcat (buffer, " ", sizeof(buffer));
|
{
|
||||||
|
g_string_append (requests, extension);
|
||||||
|
g_string_append_c (requests, ' ');
|
||||||
want_cap = TRUE;
|
want_cap = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2245,17 +2298,30 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str,
|
|||||||
|
|
||||||
if (sts_upgrade_triggered)
|
if (sts_upgrade_triggered)
|
||||||
{
|
{
|
||||||
|
g_hash_table_destroy (seen);
|
||||||
|
g_string_free (requests, TRUE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (want_cap)
|
if (want_cap)
|
||||||
{
|
{
|
||||||
/* buffer + 9 = emit buffer without "CAP REQ :" */
|
char **tokens = g_strsplit (requests->str, " ", 0);
|
||||||
|
for (i = 0; tokens[i]; i++)
|
||||||
|
{
|
||||||
|
if (tokens[i][0])
|
||||||
|
inbound_cap_request_append (serv, buffer, sizeof (buffer),
|
||||||
|
tokens[i], tags_data->timestamp);
|
||||||
|
}
|
||||||
|
g_strfreev (tokens);
|
||||||
|
if (buffer[9])
|
||||||
|
{
|
||||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPREQ, serv->server_session,
|
EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPREQ, serv->server_session,
|
||||||
buffer + 9, NULL, NULL, NULL, 0,
|
buffer + 9, NULL, NULL, NULL, 0, tags_data->timestamp);
|
||||||
tags_data->timestamp);
|
|
||||||
tcp_sendf (serv, "%s\r\n", g_strchomp (buffer));
|
tcp_sendf (serv, "%s\r\n", g_strchomp (buffer));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
g_hash_table_destroy (seen);
|
||||||
|
g_string_free (requests, TRUE);
|
||||||
if (!serv->waiting_on_sasl && !serv->waiting_on_cap)
|
if (!serv->waiting_on_sasl && !serv->waiting_on_cap)
|
||||||
{
|
{
|
||||||
/* if we use SASL, CAP END is dealt via raw numerics */
|
/* if we use SASL, CAP END is dealt via raw numerics */
|
||||||
|
|||||||
+42
-1
@@ -99,6 +99,7 @@ struct _zoitechat_hook
|
|||||||
int tag; /* for timers & FDs only */
|
int tag; /* for timers & FDs only */
|
||||||
int type; /* HOOK_* */
|
int type; /* HOOK_* */
|
||||||
int pri; /* fd */ /* priority / fd for HOOK_FD only */
|
int pri; /* fd */ /* priority / fd for HOOK_FD only */
|
||||||
|
int cap_connection_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _zoitechat_list
|
struct _zoitechat_list
|
||||||
@@ -143,7 +144,8 @@ enum
|
|||||||
HOOK_PRINT_ATTRS = 1 << 4, /* same as above, with attributes */
|
HOOK_PRINT_ATTRS = 1 << 4, /* same as above, with attributes */
|
||||||
HOOK_TIMER = 1 << 5, /* timeouts */
|
HOOK_TIMER = 1 << 5, /* timeouts */
|
||||||
HOOK_FD = 1 << 6, /* sockets & fds */
|
HOOK_FD = 1 << 6, /* sockets & fds */
|
||||||
HOOK_DELETED = 1 << 7 /* marked for deletion */
|
HOOK_DELETED = 1 << 7, /* marked for deletion */
|
||||||
|
HOOK_CAPABILITY = 1 << 9 /* script-owned CAP requests */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
@@ -351,6 +353,7 @@ plugin_add (session *sess, char *filename, void *handle, void *init_func,
|
|||||||
pl->zoitechat_emit_print_attrs = zoitechat_emit_print_attrs;
|
pl->zoitechat_emit_print_attrs = zoitechat_emit_print_attrs;
|
||||||
pl->zoitechat_event_attrs_create = zoitechat_event_attrs_create;
|
pl->zoitechat_event_attrs_create = zoitechat_event_attrs_create;
|
||||||
pl->zoitechat_event_attrs_free = zoitechat_event_attrs_free;
|
pl->zoitechat_event_attrs_free = zoitechat_event_attrs_free;
|
||||||
|
pl->zoitechat_register_capability = zoitechat_register_capability;
|
||||||
|
|
||||||
/* run zoitechat_plugin_init, if it returns 0, close the plugin */
|
/* 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)
|
if (((zoitechat_init_func *)init_func) (pl, &pl->name, &pl->desc, &pl->version, arg) == 0)
|
||||||
@@ -1028,6 +1031,41 @@ zoitechat_unhook (zoitechat_plugin *ph, zoitechat_hook *hook)
|
|||||||
return hook->userdata;
|
return hook->userdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zoitechat_hook *
|
||||||
|
zoitechat_register_capability (zoitechat_plugin *ph, const char *name, int flags,
|
||||||
|
int connection_id, void *userdata)
|
||||||
|
{
|
||||||
|
const unsigned char *p;
|
||||||
|
zoitechat_hook *hook;
|
||||||
|
if (flags != 0 || connection_id < -1 || !name || !name[0] || strlen (name) > 200 ||
|
||||||
|
strcmp (name, "sasl") == 0 || strcmp (name, "sts") == 0)
|
||||||
|
return NULL;
|
||||||
|
for (p = (const unsigned char *)name; *p; p++)
|
||||||
|
{
|
||||||
|
if (!g_ascii_islower (*p) && !g_ascii_isdigit (*p) &&
|
||||||
|
*p != '-' && *p != '.' && *p != '/' && *p != '_')
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
hook = plugin_add_hook (ph, HOOK_CAPABILITY, 0, name, NULL, NULL, 0, userdata);
|
||||||
|
hook->cap_connection_id = connection_id;
|
||||||
|
return hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
plugin_requests_capability (server *serv, const char *name)
|
||||||
|
{
|
||||||
|
GSList *entry;
|
||||||
|
for (entry = hook_list; entry; entry = entry->next)
|
||||||
|
{
|
||||||
|
zoitechat_hook *hook = entry->data;
|
||||||
|
if (hook && hook->type == HOOK_CAPABILITY &&
|
||||||
|
(hook->cap_connection_id == -1 || hook->cap_connection_id == serv->id) &&
|
||||||
|
strcmp (hook->name, name) == 0)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
zoitechat_hook *
|
zoitechat_hook *
|
||||||
zoitechat_hook_command (zoitechat_plugin *ph, const char *name, int pri,
|
zoitechat_hook_command (zoitechat_plugin *ph, const char *name, int pri,
|
||||||
zoitechat_cmd_cb *callb, const char *help_text, void *userdata)
|
zoitechat_cmd_cb *callb, const char *help_text, void *userdata)
|
||||||
@@ -1187,6 +1225,9 @@ zoitechat_get_info (zoitechat_plugin *ph, const char *id)
|
|||||||
return text_find_format_string (e);
|
return text_find_format_string (e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcmp (id, "api_plugin_caps") == 0)
|
||||||
|
return "1";
|
||||||
|
|
||||||
hash = str_hash (id);
|
hash = str_hash (id);
|
||||||
/* do the session independant ones first */
|
/* do the session independant ones first */
|
||||||
switch (hash)
|
switch (hash)
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ int plugin_emit_command (session *sess, char *name, char *word[], char *word_eol
|
|||||||
int plugin_emit_server (session *sess, char *name, char *word[], char *word_eol[],
|
int plugin_emit_server (session *sess, char *name, char *word[], char *word_eol[],
|
||||||
time_t server_time);
|
time_t server_time);
|
||||||
int plugin_emit_print (session *sess, char *word[], time_t server_time);
|
int plugin_emit_print (session *sess, char *word[], time_t server_time);
|
||||||
|
gboolean plugin_requests_capability (server *serv, const char *name);
|
||||||
int plugin_emit_dummy_print (session *sess, char *name);
|
int plugin_emit_dummy_print (session *sess, char *name);
|
||||||
int plugin_emit_keypress (session *sess, unsigned int state, unsigned int keyval, gunichar key);
|
int plugin_emit_keypress (session *sess, unsigned int state, unsigned int keyval, gunichar key);
|
||||||
GList* plugin_command_list(GList *tmp_list);
|
GList* plugin_command_list(GList *tmp_list);
|
||||||
|
|||||||
@@ -206,6 +206,10 @@ struct _zoitechat_plugin
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* api_plugin_caps=1: register before CAP LS; flags=0, connection_id=-1 for any. */
|
||||||
|
zoitechat_hook *zoitechat_register_capability (zoitechat_plugin *ph,
|
||||||
|
const char *name, int flags, int connection_id, void *userdata);
|
||||||
|
|
||||||
zoitechat_hook *
|
zoitechat_hook *
|
||||||
zoitechat_hook_command (zoitechat_plugin *ph,
|
zoitechat_hook_command (zoitechat_plugin *ph,
|
||||||
const char *name,
|
const char *name,
|
||||||
@@ -425,6 +429,7 @@ zoitechat_pluginpref_list (zoitechat_plugin *ph,
|
|||||||
#define zoitechat_hook_server ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_server)
|
#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_server_attrs ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_server_attrs)
|
||||||
#define zoitechat_hook_print ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_print)
|
#define zoitechat_hook_print ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_print)
|
||||||
|
#define zoitechat_register_capability ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_register_capability)
|
||||||
#define zoitechat_hook_print_attrs ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_print_attrs)
|
#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_timer ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_timer)
|
||||||
#define zoitechat_hook_fd ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_fd)
|
#define zoitechat_hook_fd ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_fd)
|
||||||
|
|||||||
Reference in new issue
Block a user