moved all modes to an extra ModeNode struct
[NeonServV5.git] / ModeNode.c
diff --git a/ModeNode.c b/ModeNode.c
new file mode 100644 (file)
index 0000000..cf9901f
--- /dev/null
@@ -0,0 +1,233 @@
+#include "ModeNode.h"
+#include "ChanNode.h"
+#include "ChanUser.h"
+#include "UserNode.h"
+#include "BanNode.h"
+
+static int modes_with_strarg, modes_with_intarg;
+
+//Types: http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt
+#define CHANNEL_MODE_TYPE_A        0x01 /* ... special (addresses or users) ... */
+#define CHANNEL_MODE_TYPE_B        0x02 /* These modes always take a parameter. */
+#define CHANNEL_MODE_TYPE_C        0x03 /* These modes take a parameter only when set. */
+#define CHANNEL_MODE_TYPE_D        0x04 /* These modes never take a parameter. */
+#define CHANNEL_MODE_TYPE          0x07 /* bit mask to get the type */
+
+#define CHANNEL_MODE_VALUE_STRING  0x10
+#define CHANNEL_MODE_VALUE_INTEGER 0x20
+#define CHANNEL_MODE_VALUE         0x30 /* bit mask to get the value */
+
+#define CHANNEL_MODE_VALUE_INDEX_SHIFT 8
+#define CHANNEL_MODE_VALUE_INDEX_MASK  (0xff << CHANNEL_MODE_VALUE_INDEX_SHIFT) /* this "bitrange" is reserved for storing the array indexes of the mode values */
+
+static unsigned int valid_modes[] = { /* Thats our mode list :P */
+    1,  'b', CHANNEL_MODE_TYPE_A,
+    2,  'o', CHANNEL_MODE_TYPE_A,
+    3,  'v', CHANNEL_MODE_TYPE_A,
+    4,  'k', CHANNEL_MODE_TYPE_B | CHANNEL_MODE_VALUE_STRING,
+    5,  'a', CHANNEL_MODE_TYPE_C | CHANNEL_MODE_VALUE_INTEGER,
+    6,  'l', CHANNEL_MODE_TYPE_C | CHANNEL_MODE_VALUE_INTEGER,
+    7,  'f', CHANNEL_MODE_TYPE_C | CHANNEL_MODE_VALUE_STRING,
+    8,  'F', CHANNEL_MODE_TYPE_C | CHANNEL_MODE_VALUE_STRING,
+    9,  'c', CHANNEL_MODE_TYPE_D,
+    10, 'C', CHANNEL_MODE_TYPE_D,
+    11, 'i', CHANNEL_MODE_TYPE_D,
+    12, 'm', CHANNEL_MODE_TYPE_D,
+    13, 'M', CHANNEL_MODE_TYPE_D,
+    14, 'n', CHANNEL_MODE_TYPE_D,
+    15, 'N', CHANNEL_MODE_TYPE_D,
+    16, 'p', CHANNEL_MODE_TYPE_D,
+    17, 'r', CHANNEL_MODE_TYPE_D,
+    18, 's', CHANNEL_MODE_TYPE_D,
+    19, 't', CHANNEL_MODE_TYPE_D,
+    20, 'u', CHANNEL_MODE_TYPE_D,
+    21, 'D', CHANNEL_MODE_TYPE_D,
+    22, 'd', CHANNEL_MODE_TYPE_D,
+    23, 'R', CHANNEL_MODE_TYPE_D,
+    24, 'z', CHANNEL_MODE_TYPE_D,
+//  ^ maximum is 32!!!
+    0x00, 0x00, 0x00
+};
+
+void init_ModeNode() {
+    unsigned int *mode, flag = 1;
+    modes_with_strarg = 0;
+    modes_with_intarg = 0;
+    for (mode = valid_modes; mode[1]; mode += 3) {
+        if((mode[2] & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING) {
+            mode[2] |= modes_with_strarg << CHANNEL_MODE_VALUE_INDEX_SHIFT;
+            modes_with_strarg++;
+        }
+        if((mode[2] & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_INTEGER) {
+            mode[2] |= modes_with_intarg << CHANNEL_MODE_VALUE_INDEX_SHIFT;
+            modes_with_intarg++;
+        }
+        mode[0] = flag;
+        flag = flag << 1;
+    }
+}
+
+struct ModeNode *createModeNode(struct ChanNode *chan) {
+    struct ModeNode *modes = malloc(sizeof(*modes));
+    if (!modes)
+    {
+        perror("malloc() failed");
+        return NULL;
+    }
+    modes->chan = chan;
+    modes->modes = 0;
+    modes->mode_str_args = calloc(modes_with_strarg, sizeof(char*));
+    modes->mode_int_args = calloc(modes_with_intarg, sizeof(int));
+    return modes;
+}
+
+void freeModeNode(struct ModeNode *modes) {
+    int i;
+    for(i = 0; i < modes_with_strarg; i++) {
+        if(modes->mode_str_args[i])
+            free(modes->mode_str_args[i]);
+    }
+    free(modes->mode_str_args);
+    free(modes->mode_int_args);
+    free(modes);
+}
+
+static unsigned int* getModeOptions(char mode) {
+    unsigned int *cmode;
+    for (cmode = valid_modes; cmode[1]; cmode += 3) {
+        if(cmode[1] == mode)
+            return cmode;
+    }
+    return NULL;
+}
+
+int isModeSet(struct ModeNode* modes, char modeChar) {
+    unsigned int *modeOpt = getModeOptions(modeChar);
+    return (modes->modes & modeOpt[0]);
+}
+
+void* getModeValue(struct ModeNode* modes, char modeChar) {
+    #define MODE_VALUE_INDEX (modeOpt[2] & CHANNEL_MODE_VALUE_INDEX_MASK) >> CHANNEL_MODE_VALUE_INDEX_SHIFT
+    unsigned int *modeOpt = getModeOptions(modeChar);
+    if((modeOpt[2] & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING)
+        return modes->mode_str_args[MODE_VALUE_INDEX];
+    if((modeOpt[2] & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_INTEGER)
+        return &modes->mode_int_args[MODE_VALUE_INDEX];
+    return NULL;
+    #undef MODE_VALUE_INDEX
+}
+
+static void parseModesUserPriv(struct ModeNode* modes, unsigned char flag, int add, char *nick) {
+    if(modes->chan == NULL) return;
+    struct UserNode *user = getUserByNick(nick);
+    if(user == NULL) return;
+    struct ChanUser *chanuser = getChanUser(user, modes->chan);
+    if(chanuser == NULL) return;
+    if(add)
+        chanuser->flags |= flag;
+    else
+        chanuser->flags &= ~flag;
+}
+
+static void parseModesBan(struct ModeNode* modes, int add, char *mask) {
+    if(modes->chan == NULL) return;
+    if(add)
+        addChannelBan(modes->chan, mask);
+    else
+        removeChannelBanMask(modes->chan, mask);
+}
+
+void parseModes(struct ModeNode* modes, char *modeStr, char **argv, int argc) {
+    int i, argpos = 0, add = 1;
+    #define MODE_TYPE (modeOpt[2] & CHANNEL_MODE_TYPE)
+    #define MODE_VALUE (modeOpt[2] & CHANNEL_MODE_VALUE)
+    #define MODE_VALUE_INDEX (modeOpt[2] & CHANNEL_MODE_VALUE_INDEX_MASK) >> CHANNEL_MODE_VALUE_INDEX_SHIFT
+    unsigned int *modeOpt;
+    for(i = 0; i < strlen(modeStr); i++) {
+        if(modeStr[i] == '+') {
+            add = 1;
+            continue;
+        }
+        if(modeStr[i] == '-') {
+            add = 0;
+            continue;
+        }
+        modeOpt = getModeOptions(modeStr[i]);
+        if(!modeOpt) continue; // unknown mode?
+        if(MODE_TYPE == CHANNEL_MODE_TYPE_A) {
+            if(argpos == argc) continue;
+            //special mode ;)
+            switch(modeStr[i]) {
+                case 'o':
+                    parseModesUserPriv(modes, CHANUSERFLAG_OPPED, add, argv[argpos]);
+                    break;
+                case 'v':
+                    parseModesUserPriv(modes, CHANUSERFLAG_VOICED, add, argv[argpos]);
+                    break;
+                case 'b':
+                    parseModesBan(modes, add, argv[argpos]);
+                    break;
+                default:
+                    //we have an unknown TYPE_A mode???
+                    break;
+            }
+            argpos++;
+            continue;
+        }
+        if(add) {
+            if(MODE_TYPE != CHANNEL_MODE_TYPE_D) { //all other types take parameters when set
+                if(argpos == argc) continue;
+                if(MODE_VALUE == CHANNEL_MODE_VALUE_STRING) {
+                    if(modes->mode_str_args[MODE_VALUE_INDEX])
+                        free(modes->mode_str_args[MODE_VALUE_INDEX]);
+                    modes->mode_str_args[MODE_VALUE_INDEX] = strdup(argv[argpos++]);
+                } else if(MODE_VALUE == CHANNEL_MODE_VALUE_INTEGER)
+                    modes->mode_int_args[MODE_VALUE_INDEX] = atoi(argv[argpos++]);
+                else
+                    argpos++; //we simply don't know what to do with the argument...
+            }
+            modes->modes |= modeOpt[0];
+        } else {
+            modes->modes &= ~modeOpt[0];
+            if(MODE_TYPE == CHANNEL_MODE_TYPE_B) {
+                if(argpos == argc) continue;
+                if(MODE_VALUE == CHANNEL_MODE_VALUE_STRING) {
+                    free(modes->mode_str_args[MODE_VALUE_INDEX]);
+                    modes->mode_str_args[MODE_VALUE_INDEX] = NULL;
+                } else if(MODE_VALUE == CHANNEL_MODE_VALUE_INTEGER)
+                    modes->mode_int_args[MODE_VALUE_INDEX] = 0;
+                argpos++; //we don't need the argument when unsetting a mode...
+            }
+        }
+    }
+    #undef MODE_TYPE
+    #undef MODE_VALUE
+    #undef MODE_VALUE_INDEX
+}
+
+void getModeString(struct ModeNode* modes, char *modesStr) {
+    #define MODE_TYPE (mode[2] & CHANNEL_MODE_TYPE)
+    #define MODE_VALUE (mode[2] & CHANNEL_MODE_VALUE)
+    #define MODE_VALUE_INDEX (mode[2] & CHANNEL_MODE_VALUE_INDEX_MASK) >> CHANNEL_MODE_VALUE_INDEX_SHIFT
+    char paramStr[MAXLEN];
+    modesStr[0] = '+';
+    unsigned int *mode;
+    int modePos = 1;
+    int paramPos = 0;
+    for (mode = valid_modes; mode[1]; mode += 3) {
+        if(modes->modes & mode[0]) {
+            modesStr[modePos++] = (char) mode[1];
+            if(MODE_TYPE != CHANNEL_MODE_TYPE_D) {
+                if(MODE_VALUE == CHANNEL_MODE_VALUE_STRING)
+                    paramPos += sprintf(paramStr + paramPos, " %s", modes->mode_str_args[MODE_VALUE_INDEX]);
+                else if(MODE_VALUE == CHANNEL_MODE_VALUE_INTEGER)
+                    paramPos += sprintf(paramStr + paramPos, " %d", modes->mode_int_args[MODE_VALUE_INDEX]);
+            }
+        }
+    }
+    paramStr[paramPos] = '\0';
+    strcpy(modesStr + modePos, paramStr);
+    #undef MODE_TYPE
+    #undef MODE_VALUE
+    #undef MODE_VALUE_INDEX
+}