Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/eggdrop.h
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,12 @@ enum {
# define STRINGIFY1(x) #x
#endif

#define MAX_IRC_TOKENS 64 // TODO: does this make sense?
typedef struct parsed_irc {
size_t argc;
char *argv[MAX_IRC_TOKENS];
} parsed_irc_t;

#ifdef EGG_TDNS
#include <pthread.h>
#define DTN_TYPE_HOSTBYIP 0
Expand Down
46 changes: 46 additions & 0 deletions src/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,52 @@ char *newsplit(char **rest)
return r;
}

// WARNING: modifies original text
// Split IRC text into words (without the "from" component, which could also start with ':')
// - replace all ' ' with \0, splitting into words
// - if a word starts with ':' it is the last word, it can contain spaces
// - return argc/argv structure (pointers into original text)
struct parsed_irc parse_irc(char *text)
{
struct parsed_irc result = {.argc = 0};

while (*text) {
while (*text == ' ') {
*text++ = '\0';
}
if (!*text) {
break;
}
if (*text == ':') {
*text++ = '\0';
result.argv[result.argc++] = text;
break;
} else {
result.argv[result.argc++] = text;
while (*text && *text != ' ') {
text++;
}
}
}

return result;
}

char *join_str_array(char **argv, int argc, char *delim)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have last parameter type char or is there a need for delimiters longer than 1 char?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on the other hand, i can imagine things delimiters like ", ". but i think for generic misc functions exported via module API, we should make the output buffer a parameter and len, not a static array. could be useful for threaded code or for code that needs to join into a buffer longer than 512 chars.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think keeping it open for multi-char delimiters is more flexible and doesn't make it any more difficult to use.
Caller-provided buffer with length limitation makes sense, I'll change that.

{
static char buf[512];
size_t written = 0;

if (!argc) {
buf[0] = '\0';
}

for (int i = 0; i < argc; i++) {
written += snprintf(buf + written, sizeof buf - written, "%s%s", argv[i], i == argc - 1 ? "" : delim);
}
return buf;
}

/* maskhost(), modified to support custom mask types, as defined
* by mIRC.
* Does not require a proper hostmask in 's'. Accepts any strings,
Expand Down
5 changes: 5 additions & 0 deletions src/mod/irc.mod/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ clean:

distclean: clean

# manual target in this directory for now
.PHONY: test
test:
gcc -O0 -ggdb3 -std=gnu99 -I. -I../../.. -I.. -DHAVE_CONFIG_H -DMAKING_MODS -I/usr/include/tcl8.6 -o test test.c -ltcl8.6 -lcriterion && ./test

#safety hash
../irc.o: .././irc.mod/irc.c ../../../src/mod/module.h \
../../../src/main.h ../../../config.h ../../../eggint.h ../../../lush.h \
Expand Down
110 changes: 110 additions & 0 deletions src/mod/irc.mod/chan.c
Original file line number Diff line number Diff line change
Expand Up @@ -2838,6 +2838,108 @@ static int parse_maxlist(const char *value)
return 0;
}

// selectively update global information table,
// either chanmodes only or prefix modes only,
// then delete all non-existing modes of that type only
static void update_chanmodes(mode_info_t *modes, int is_prefix)
{
for (int i = 0; i < 256; i++) {
if (modes[i].type) {
// is in the new mode info, must overwrite even if type changed
modecharinfo[i] = modes[i]; // struct copy
} else if (modecharinfo[i].type) {
// was in the old mode info but not in the new mode info -> delete
// but respect if its type has already changed
if ((modecharinfo[i].type == MODETYPE_PREFIX && is_prefix) || (modecharinfo[i].type != MODETYPE_PREFIX && !is_prefix)) {
memset(&modecharinfo[i], 0, sizeof modecharinfo[i]);
}
}
}
if (!is_prefix) {
// assume that if +e/+I are list-type modes that they are exempts and invites
use_exempts = (MODE_TYPE('e') == MODETYPE_LIST);
use_invites = (MODE_TYPE('I') == MODETYPE_LIST);
}
}

// CHANMODES=eIbq,k,flj,CFLMPQScgimnprstuz
// listmodes, keymodes, limitmodes, flagmodes
static int process_chanmodes(char *value)
{
mode_type_t modetype = MODETYPE_LIST;
mode_info_t modes[256];

memset(&modes, 0, sizeof modes);

while (*value) {
// parse all modes until ','
while (*value && isalnum((unsigned char)*value)) {
modes[(unsigned char)*value].type = modetype;
modes[(unsigned char)*value].prefix = '\0';
debug2("Learned mode type: +%c type %s", *value, MODE_TYPE_STR(modetype));
value++;
}
// sanity check
if ((modetype != MODETYPE_FLAG && *value != ',') || (modetype == MODETYPE_FLAG && *value)) {
return -1;
}
value++;
// next section in order
if (modetype == MODETYPE_LIST) {
modetype = MODETYPE_KEY;
} else if (modetype == MODETYPE_KEY) {
modetype = MODETYPE_LIMIT;
} else if (modetype == MODETYPE_LIMIT) {
modetype = MODETYPE_FLAG;
} else {
break;
}
}
if (modetype != MODETYPE_FLAG) {
return -1;
}
// update global info table, but only for chanmodes (not prefix modes)
update_chanmodes(modes, 0);
return 0;
}

// PREFIX=(ov)@+
static int process_prefix(const char *value)
{
const char *prefix = value;
mode_info_t modes[256];

memset(&modes, 0, sizeof modes);

if (*value++ != '(') {
return -1;
}
while (*prefix && *prefix != ')') {
prefix++;
}
if (*prefix++ != ')') {
return -1;
}
// PREFIX=(ov)@+
// *value--^ ^--*prefix
while (*value && *value != ')') {
if (!*prefix || !isalnum((unsigned char)*value)) {
return -1;
}
modes[(unsigned char)*value].type = MODETYPE_PREFIX;
modes[(unsigned char)*value].prefix = *prefix;
debug3("Learned mode type: +%c type %s, prefixchar %c", *value, MODE_TYPE_STR(MODETYPE_PREFIX), *prefix);
value++;
prefix++;
}
if (*value != ')' || *prefix) {
return -1;
}
// update global info table, but only for prefixes
update_chanmodes(modes, 1);
return 0;
}

static int irc_isupport(char *key, char *isset_str, char *value)
{
int isset = !strcmp(isset_str, "1");
Expand All @@ -2860,6 +2962,14 @@ static int irc_isupport(char *key, char *isset_str, char *value)
}
} else if (!strcmp(key, "BOT")) {
botflag005 = value[0];
} else if (!strcmp(key, "CHANMODES")) {
if (process_chanmodes(isset ? value : "beI,k,l,imnpstcCMRrDuNdTaq")) {
putlog(LOG_MISC, "*", "Error: isupport unable to parse CHANMODES=%s", isset ? value : "(unset)");
}
} else if (!strcmp(key, "PREFIX")) {
if (process_prefix(isset ? value : "(ohv)@%+")) {
putlog(LOG_MISC, "*", "Error: isupport unable to parse PREFIX=%s", isset ? value : "(unset)");
}
}
return 0;
}
Expand Down
28 changes: 28 additions & 0 deletions src/mod/irc.mod/irc.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ static char opchars[8]; /* the chars in a /who reply meaning op */

static Tcl_Obj *tcl_account;

static mode_info_t modecharinfo[256];

#include "chan.c"
#include "mode.c"
#include "cmdsirc.c"
Expand Down Expand Up @@ -1118,6 +1120,29 @@ static void tell_account_tracking_status(int idx, int details)
}
}

static void tell_modeparsing_type(int idx, mode_type_t type)
{
dprintf(idx, " %s modes: %s", MODE_TYPE_STR(type), type == MODETYPE_PREFIX ? "" : "+");
for (int i = 0; i < 256; i++) {
if (modecharinfo[i].type == type) {
dprintf(idx, "%c", i);
if (type == MODETYPE_PREFIX) {
dprintf(idx, "(%c) ", modecharinfo[i].prefix);
}
}
}
dprintf(idx, "\n");
}

static void tell_modeparsing(int idx)
{
tell_modeparsing_type(idx, MODETYPE_FLAG);
tell_modeparsing_type(idx, MODETYPE_LIMIT);
tell_modeparsing_type(idx, MODETYPE_KEY);
tell_modeparsing_type(idx, MODETYPE_LIST);
tell_modeparsing_type(idx, MODETYPE_PREFIX);
}

static void irc_report(int idx, int details)
{
struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
Expand Down Expand Up @@ -1157,6 +1182,9 @@ static void irc_report(int idx, int details)
dprintf(idx, " %s\n", q);
}
tell_account_tracking_status(idx, details);
if (details) {
tell_modeparsing(idx);
}
}

/* Many networks either support max_bans/invite/exempts/ *or*
Expand Down
24 changes: 24 additions & 0 deletions src/mod/irc.mod/irc.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,30 @@
#define REVENGE_KICK 1 /* Kicked victim */
#define REVENGE_DEOP 2 /* Took op */

/* Order and values are relied upon, do not change */
typedef enum mode_type {
MODETYPE_INVALID = 0, // invalid, must be 0 for init
MODETYPE_FLAG = 1, // no params
MODETYPE_LIMIT = 2, // param on set only
MODETYPE_KEY = 3, // param always
MODETYPE_LIST = 4, // param always
MODETYPE_PREFIX = 5 // param always
} mode_type_t;

#define MODE_TYPE_NAMES ((const char *[]){[MODETYPE_FLAG] = "Flag", [MODETYPE_LIST] = "List", [MODETYPE_KEY] = "Key", [MODETYPE_LIMIT] = "Limit", [MODETYPE_PREFIX] = "Prefix"})

#define MODE_TYPE(c) (modecharinfo[(unsigned char)(c)].type)
#define MODE_TYPE_STR(t) ((MODE_TYPE_NAMES)[(t)])

#define MODE_HAS_SET_ARG(c) (MODE_TYPE((c)) >= MODETYPE_LIMIT)
#define MODE_HAS_UNSET_ARG(c) (MODE_TYPE((c)) >= MODETYPE_KEY)
#define MODE_PREFIX(c) (modecharinfo[(unsigned char)(c)].prefix)

typedef struct mode_info {
mode_type_t type;
char prefix;
} mode_info_t;

#ifdef MAKING_IRC
static void check_tcl_need(char *, char *);
static void check_tcl_kick(char *, char *, struct userrec *, char *, char *, char *);
Expand Down
Loading
Loading