added simple anti flood system
[NeonServV5.git] / src / IRCQueue.c
diff --git a/src/IRCQueue.c b/src/IRCQueue.c
new file mode 100644 (file)
index 0000000..c30a27e
--- /dev/null
@@ -0,0 +1,219 @@
+/* IRCQueue.c - NeonServ v5.1
+ * Copyright (C) 2011  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#include "IRCQueue.h"
+#include "ClientSocket.h"
+
+#define MAXPENALTY 6
+
+struct QueueEntry {
+    char *msg;
+    struct QueueEntry *next;
+};
+
+struct BotQueue {
+    struct ClientSocket *client;
+    int penalty;
+    struct QueueEntry *fastqueue_first, *fastqueue_last;
+    struct QueueEntry *normalqueue_first, *normalqueue_last;
+    struct QueueEntry *textqueue_first, *textqueue_last;
+};
+
+static struct BotQueue *initialize_queue(struct ClientSocket *client) {
+    struct BotQueue *queue = malloc(sizeof(*queue));
+    if (!queue) {
+        perror("malloc() failed");
+        return NULL;
+    }
+    queue->client = client;
+    client->queue = queue;
+    queue->penalty = 0;
+    queue->fastqueue_first = NULL;
+    queue->fastqueue_last = NULL;
+    queue->normalqueue_first = NULL;
+    queue->normalqueue_last = NULL;
+    queue->textqueue_first = NULL;
+    queue->textqueue_last = NULL;
+    return queue;
+}
+
+int queue_add(struct ClientSocket *client, char* msg, int len) {
+    if(!client->queue)
+        client->queue = initialize_queue(client);
+    struct BotQueue *queue = client->queue;
+    char *args = strstr(msg, " ");
+    int type;
+    if(args) {
+        *args = '\0';
+        if(!stricmp(msg, "MODE")) 
+            type = 3;
+        else if(!stricmp(msg, "KICK")) 
+            type = 3;
+        else if(!stricmp(msg, "PONG")) 
+            type = 3;
+        else if(!stricmp(msg, "PRIVMSG")) 
+            type = 1;
+        else if(!stricmp(msg, "NOTICE")) 
+            type = 1;
+        else if(!stricmp(msg, "WHO")) 
+            type = 1;
+        else
+            type = 2;
+        *args = ' ';
+    } else
+        type = 2;
+    struct QueueEntry *entry = malloc(sizeof(*entry));
+    if (!entry) {
+        perror("malloc() failed");
+        return 0;
+    }
+    entry->msg = strdup(msg);
+    entry->next = NULL;
+    if(type == 1) { //low priority
+        if(queue->textqueue_last) {
+            queue->textqueue_last->next = entry;
+            queue->textqueue_last = entry;
+        } else {
+            queue->textqueue_last = entry;
+            queue->textqueue_first = entry;
+        }
+    } else if(type == 2) { //normal priority
+        if(queue->normalqueue_last) {
+            queue->normalqueue_last->next = entry;
+            queue->normalqueue_last = entry;
+        } else {
+            queue->normalqueue_last = entry;
+            queue->normalqueue_first = entry;
+        }
+    } else if(type == 3) { //high priority
+        if(queue->fastqueue_last) {
+            queue->fastqueue_last->next = entry;
+            queue->fastqueue_last = entry;
+        } else {
+            queue->fastqueue_last = entry;
+            queue->fastqueue_first = entry;
+        }
+    }
+    return 1;
+}
+
+static int calculate_penalty(char *message) {
+    int msglen = strlen(message);
+    int penalty = (2 + msglen / 100);
+    return penalty;
+}
+
+static void dequeue_bot(struct ClientSocket *client) {
+    if(client->queue->penalty >= MAXPENALTY) return;
+    int penalty;
+    //try to send high priority messages
+    if(client->queue->fastqueue_first) {
+        do {
+            struct QueueEntry *entry = client->queue->fastqueue_first;
+            if(!entry->next)
+                client->queue->fastqueue_last = NULL;
+            client->queue->fastqueue_first = client->queue->fastqueue_first->next;
+            penalty = calculate_penalty(entry->msg);
+            write_socket_force(client, entry->msg, strlen(entry->msg));
+            client->queue->penalty += penalty;
+            free(entry->msg);
+            free(entry);
+        } while(client->queue->penalty < MAXPENALTY && client->queue->fastqueue_first);
+    }
+    if(client->queue->penalty >= MAXPENALTY) return;
+    //try to send normal priority messages
+    if(client->queue->normalqueue_first) {
+        do {
+            struct QueueEntry *entry = client->queue->normalqueue_first;
+            if(!entry->next)
+                client->queue->normalqueue_last = NULL;
+            client->queue->normalqueue_first = client->queue->normalqueue_first->next;
+            penalty = calculate_penalty(entry->msg);
+            write_socket_force(client, entry->msg, strlen(entry->msg));
+            client->queue->penalty += penalty;
+            free(entry->msg);
+            free(entry);
+        } while(client->queue->penalty < MAXPENALTY && client->queue->normalqueue_first);
+    }
+    if(client->queue->penalty >= MAXPENALTY) return;
+    //try to send low priority messages
+    if(client->queue->textqueue_first) {
+        do {
+            struct QueueEntry *entry = client->queue->textqueue_first;
+            if(!entry->next)
+                client->queue->textqueue_last = NULL;
+            client->queue->textqueue_first = client->queue->textqueue_first->next;
+            penalty = calculate_penalty(entry->msg);
+            write_socket_force(client, entry->msg, strlen(entry->msg));
+            client->queue->penalty += penalty;
+            free(entry->msg);
+            free(entry);
+        } while(client->queue->penalty < MAXPENALTY && client->queue->textqueue_first);
+    }
+}
+
+void queue_destroy(struct ClientSocket *client) {
+    if(!client->queue) return;
+    struct QueueEntry *entry, *next;
+    for(entry = client->queue->fastqueue_first; entry; entry = next) {
+        next = entry->next;
+        free(entry->msg);
+        free(entry);
+    }
+    for(entry = client->queue->normalqueue_first; entry; entry = next) {
+        next = entry->next;
+        free(entry->msg);
+        free(entry);
+    }
+    for(entry = client->queue->textqueue_first; entry; entry = next) {
+        next = entry->next;
+        free(entry->msg);
+        free(entry);
+    }
+    free(client->queue);
+    client->queue = NULL;
+}
+
+static struct timeval lastloop;
+void queue_init() {
+    gettimeofday(&lastloop, NULL);
+}
+
+
+void queue_loop() {
+    struct ClientSocket *bot;
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    long mtime, seconds, useconds;
+    seconds  = now.tv_sec  - lastloop.tv_sec;
+    useconds = now.tv_usec - lastloop.tv_usec;
+    mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;
+    int fullseconds = mtime/1000;
+    if(fullseconds) {
+        lastloop.tv_sec += fullseconds;
+        for(bot = getBots(0, NULL); bot; bot = getBots(0, bot)) {
+            if(bot->queue && bot->queue->penalty) {
+                bot->queue->penalty -= fullseconds;
+                if(bot->queue->penalty < 0)
+                    bot->queue->penalty = 0;
+            }
+        }
+    }
+    for(bot = getBots(0, NULL); bot; bot = getBots(0, bot)) {
+        if(bot->queue)
+            dequeue_bot(bot);
+    }
+}