Dev:Channel Mode API

= Introduction = By using the "extended channel modes" API you can create your own channel modes. You can create both channel modes with or without parameters. The only thing that is currently unavailable to modules are "list" type channel modes such as creating something similar to +vhoaq and +beI (but we have the Extended Bans API to compensate for the latter).

Channel modes with no parameter (eg: +X) are considerably easier than channel modes that require a parameter (eg: +X 123). That being said, the API tries to make it as easy as possible for both.

= Creating a channel mode =

You create a channel mode by calling CmodeAdd from MOD_INIT like this: long EXTCMODE_DUMMY = 0L;

DLLFUNC int MOD_INIT(mymod)(ModuleInfo *modinfo) { CmodeInfo cmodereq;

memset(&cmodereq, 0, sizeof(cmodereq)); cmodereq.flag = 'X'; cmodereq.paracount = 0; cmodereq.is_ok = modeX_is_ok; /* your own function */ // cmodereq.is_ok = extcmode_default_requirechop; /* or... for paramless modes that simply require +o/+a/+q */

CmodeAdd(modinfo->handle, cmodereq, &EXTCMODE_DUMMY);

So basically you pass: 1) the module handle, 2) a CmodeInfo struct, and 3) a pointer to a long int that will be set to the integer value of the channel mode.

The CmodeInfo struct contains two (regular) variables which you may set:
 * flag specifies which letter to assign to the channel mode. There may be no other module using this letter.
 * local specifies if the channel mode is local, that is: remote servers will never know this channel mode is set or unset. This is rarely used.

Other than that, the CmodeInfo struct is full of function pointers. In the example of above we only set the is_ok function. There are 3 categories of functions:
 * Required functions for both paramless and parameter modes
 * Optional functions
 * Required functions for parameter modes

They are explained below.

is_ok
Access and parameter checking.

SHORTCUT: If you are writing a paramless channel mode and you simply want to require +o/+a/+q for setting and unsetting the mode, then you can save yourself from writing your own is_ok function. In that case simply use extcmode_default_requirechop as commented out in the example in previous section.

Declaration of the is_ok function: int        (*is_ok)(aClient *sptr, aChannel *chptr, char mode, char *para, int checkt, int what);

The is_ok function is called for a number of cases:
 * If checkt is EXCHK_ACCESS then the function is called to check if the user may set/unset the mode. You must only check access and NOT send any error message(s). If access is granted you should return EX_ALLOW. If you want to disallow a set/unset then return EX_DENY. Normally IRC Operators with override capabilities may still override a deny, if you also want to prevent IRCOp's from (un)setting then you can return EX_ALWAYS_DENY.
 * When checkt is EXCHK_ACCESS_ERR it means your function is called (again) to check access but this time you may send an error message to the user (in fact, you are expected to), indicating why setting or unsetting is not permitted.
 * Finally, when checkt is EXCHK_PARAM then you are supposed to check the correctness of the parameter being passed. If it's not good (eg: you expect a value between 1 and 20 and you recieve 'lalala' or '30') then you return EX_DENY. Otherwise you return EX_ALLOW. You should also send an error message to the user telling them what's wrong.

Example: int modeX_is_ok(aClient *sptr, aChannel *chptr, char mode, char *para, int checkt, int what) {

if ((checkt == EXCHK_ACCESS) || (checkt == EXCHK_ACCESS_ERR)) {       if (!is_chan_op(sptr, chptr)) {           if (checkt == EXCHK_ACCESS_ERR) sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, sptr->name, chptr->chname); return EX_DENY; }       return EX_ALLOW; } else if (checkt == EXCHK_PARAM) {       int v = atoi(para); if ((v < 1) || (v > 20)) {           sendnotice(sptr, "ChanMode +X: ERROR: Expect a value between 1 and 20"); return EX_DENY; }       return EX_ALLOW; }

return EX_ALLOW; /* Falltrough... normally never reached */ }

conv_param
Convert input parameter to 'correct' parameter. For example +X a123 could be converted to +X 123.

Generally is_ok should be used for checking and simply reject invalid user input there. However in some cases you may want to transform user input. That's what this function is for: transform the string if necessary. You SHOULD NOT send any errors here.

If you don't need this, then simply don't register the function.

Declaration: char *     (*conv_param)(char *para, aClient *sptr);

Example: /* Instead of rejecting 20 values in +X, the author of this module chose to transform these to 1 and 20 respectively. */ char *modeX_conv_param(char *param) { static char convbuf[32]; int t;

t = atoi(param); if (t < 1) t=1; if (t > 20) t=20; ircsprintf(convbuf, "%d", t); return convbuf; }

Required functions for parameter modes
Parameter modes are more complex because they need to store and retrieve data, duplicate and free data and have to deal with server synchronization.

put_param
Store a parameter (or more accurately, a setting) in memory for the channel.

Declaration: void *put_param(void *data, char *para);

Remarks:
 * The return value must be the HEAD of the linked list (FIXME: is that so? hmm.)
 * Before allocating a new entry you should check if there's already one stored first. For example if a channel is +X 5 and then someone does +X 6 then you don't need to allocate a new struct)

Best to give an example as the above makes it all sounds rather complex: /* Example of mode +X module that accepts a number */

/* This is how you should declare your own custom struct. You normally put this quite high in the source, before all your MOD_TEST / MOD_INIT / etc functions */ typedef struct { EXTCM_PAR_HEADER int t; // change this or add more variables, whatever suits you. } aModeX;

CmodeParam *modeX_put_param(CmodeParam *Xpara, char *para) { aModeX *r = (aModeX *)Xpara;

if (!r) {       r = (aModeX *)MyMallocEx(sizeof(aModeX)); r->flag = 'X'; } /* else... 'r' already exists and contains an (soon to be) old value. */

r->t = atoi(para); /* set or overwrite value */

return (CmodeParam *)r; }

get_param
Retrieve a parameter/setting of the channel from memory. Or, in other words: convert a setting from your struct to a readable string.

Declaration: char *get_param(void *data);

Example: /* Example of mode +X module that accepts a number */

/* This is how you should declare your own custom struct. You normally put this quite high in the source, before all your MOD_TEST / MOD_INIT / etc functions */ typedef struct { EXTCM_PAR_HEADER int t; // change this or add more variables, whatever suits you. } aModeX;

char *modeX_get_param(CmodeParam *Xpara) { aModeX *r = (aModeX *)Xpara; static char buf[32];

if (!r) return NULL; ircsprintf(buf, "%d", r->t); return buf; }

free_param
Free parameter/setting from memory.

Declaration: void free_param(void *data);

Example: void modeX_free_param(CmodeParam *para) {   free(para); }

dup_struct
Duplicate your param/settings struct. Yes, this function is mandatory for parameter modes. It is only called from a few places but it is required.

Declaration: void *dup_struct(void *data);

Example: typedef struct { EXTCM_PAR_HEADER int t; // change this or add more variables, whatever suits you. } aModeX; [..] CmodeParam *modeX_dup_struct(CmodeParam *src) {   aModeX *dst = MyMallocEx(sizeof(aModeX));

memcpy(dst, src, sizeof(aModeX)); dst->next = dst->prev = NULL; /* paranoid */ return (CmodeParam *)dst; }