(thanks to the people reading git and excessively exploiting this bug... It was undet...
[NeonServV5.git] / src / IRCQueue.c
1 /* IRCQueue.c - NeonServ v5.6
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17 #include "IRCQueue.h"
18 #include "ClientSocket.h"
19 #include "IOHandler.h"
20 #include "tools.h"
21 #include "log.h"
22
23 #define MAXPENALTY 8 /* 4 messages */
24
25 struct QueueEntry {
26     char *msg;
27     struct QueueEntry *next;
28 };
29
30 struct BotQueue {
31     struct ClientSocket *client;
32     struct IODescriptor *iofd;
33     int penalty : 8;
34     int rem_penalty : 8;
35     struct QueueEntry *fastqueue_first, *fastqueue_last;
36     struct QueueEntry *normalqueue_first, *normalqueue_last;
37     struct QueueEntry *textqueue_first, *textqueue_last;
38 };
39
40 static IOHANDLER_CALLBACK(queue_callback);
41
42 static struct BotQueue *initialize_queue(struct ClientSocket *client) {
43     struct BotQueue *queue = malloc(sizeof(*queue));
44     if (!queue) {
45         printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
46         return NULL;
47     }
48     queue->client = client;
49     client->queue = queue;
50     queue->iofd = NULL;
51     queue->penalty = 0;
52     queue->fastqueue_first = NULL;
53     queue->fastqueue_last = NULL;
54     queue->normalqueue_first = NULL;
55     queue->normalqueue_last = NULL;
56     queue->textqueue_first = NULL;
57     queue->textqueue_last = NULL;
58     return queue;
59 }
60
61 static int calculate_penalty(char *message) {
62     int msglen = strlen(message);
63     int penalty = (2 + msglen / 100);
64     return penalty;
65 }
66
67 int queue_add(struct ClientSocket *client, char* msg, int len) {
68     if(!client->queue)
69         client->queue = initialize_queue(client);
70     struct BotQueue *queue = client->queue;
71     char *args = strstr(msg, " ");
72     int type;
73     int add_queue = 0;
74     if(args) {
75         *args = '\0';
76         if(!stricmp(msg, "MODE")) 
77             type = 3;
78         else if(!stricmp(msg, "KICK")) 
79             type = 3;
80         else if(!stricmp(msg, "PONG")) 
81             type = 3;
82         else if(!stricmp(msg, "PRIVMSG")) 
83             type = 1;
84         else if(!stricmp(msg, "NOTICE")) 
85             type = 1;
86         else if(!stricmp(msg, "WHO")) 
87             type = 1;
88         else
89             type = 2;
90         *args = ' ';
91     } else
92         type = 2;
93     
94     //check if we need to queue
95     switch(type) {
96     case 1:
97         if(queue->textqueue_first) {
98             add_queue = 1;
99             break;
100         }
101     case 2:
102         if(queue->normalqueue_first) {
103             add_queue = 1;
104             break;
105         }
106     case 3:
107         if(queue->fastqueue_first) {
108             add_queue = 1;
109             break;
110         }
111     default:
112         if(queue->penalty >= MAXPENALTY)
113             add_queue = 1;
114         break;
115     }
116     
117     if(!add_queue) {
118         int penalty = calculate_penalty(msg);
119         write_socket_force(client, msg, len);
120         queue->penalty += penalty;
121         if(!queue->iofd) {
122             struct timeval timeout;
123             gettimeofday(&timeout, NULL);
124             if(queue->penalty >= MAXPENALTY)
125                 queue->rem_penalty = (queue->penalty - MAXPENALTY)+1;
126             else
127                 queue->rem_penalty = queue->penalty;
128             timeout.tv_sec += queue->rem_penalty;
129             queue->iofd = iohandler_timer(timeout, queue_callback);
130             queue->iofd->data = queue;
131         }
132     } else {
133         struct QueueEntry *entry = malloc(sizeof(*entry));
134         if (!entry) {
135             printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
136             return 0;
137         }
138         entry->msg = strdup(msg);
139         entry->next = NULL;
140         if(type == 1) { //low priority
141             if(queue->textqueue_last) {
142                 queue->textqueue_last->next = entry;
143                 queue->textqueue_last = entry;
144             } else {
145                 queue->textqueue_last = entry;
146                 queue->textqueue_first = entry;
147             }
148         } else if(type == 2) { //normal priority
149             if(queue->normalqueue_last) {
150                 queue->normalqueue_last->next = entry;
151                 queue->normalqueue_last = entry;
152             } else {
153                 queue->normalqueue_last = entry;
154                 queue->normalqueue_first = entry;
155             }
156         } else if(type == 3) { //high priority
157             if(queue->fastqueue_last) {
158                 queue->fastqueue_last->next = entry;
159                 queue->fastqueue_last = entry;
160             } else {
161                 queue->fastqueue_last = entry;
162                 queue->fastqueue_first = entry;
163             }
164         }
165     }
166     return 1;
167 }
168
169 static void dequeue_bot(struct ClientSocket *client) {
170     if(client->queue->penalty >= MAXPENALTY) return;
171     int penalty;
172     //try to send high priority messages
173     if(client->queue->fastqueue_first) {
174         do {
175             struct QueueEntry *entry = client->queue->fastqueue_first;
176             if(!entry->next)
177                 client->queue->fastqueue_last = NULL;
178             client->queue->fastqueue_first = client->queue->fastqueue_first->next;
179             penalty = calculate_penalty(entry->msg);
180             write_socket_force(client, entry->msg, strlen(entry->msg));
181             client->queue->penalty += penalty;
182             free(entry->msg);
183             free(entry);
184         } while(client->queue->penalty < MAXPENALTY && client->queue->fastqueue_first);
185     }
186     if(client->queue->penalty >= MAXPENALTY) return;
187     //try to send normal priority messages
188     if(client->queue->normalqueue_first) {
189         do {
190             struct QueueEntry *entry = client->queue->normalqueue_first;
191             if(!entry->next)
192                 client->queue->normalqueue_last = NULL;
193             client->queue->normalqueue_first = client->queue->normalqueue_first->next;
194             penalty = calculate_penalty(entry->msg);
195             write_socket_force(client, entry->msg, strlen(entry->msg));
196             client->queue->penalty += penalty;
197             free(entry->msg);
198             free(entry);
199         } while(client->queue->penalty < MAXPENALTY && client->queue->normalqueue_first);
200     }
201     if(client->queue->penalty >= MAXPENALTY) return;
202     //try to send low priority messages
203     if(client->queue->textqueue_first) {
204         do {
205             struct QueueEntry *entry = client->queue->textqueue_first;
206             if(!entry->next)
207                 client->queue->textqueue_last = NULL;
208             client->queue->textqueue_first = client->queue->textqueue_first->next;
209             penalty = calculate_penalty(entry->msg);
210             write_socket_force(client, entry->msg, strlen(entry->msg));
211             client->queue->penalty += penalty;
212             free(entry->msg);
213             free(entry);
214         } while(client->queue->penalty < MAXPENALTY && client->queue->textqueue_first);
215     }
216 }
217
218 void queue_destroy(struct ClientSocket *client) {
219     if(!client->queue) return;
220     struct QueueEntry *entry, *next;
221     for(entry = client->queue->fastqueue_first; entry; entry = next) {
222         next = entry->next;
223         free(entry->msg);
224         free(entry);
225     }
226     for(entry = client->queue->normalqueue_first; entry; entry = next) {
227         next = entry->next;
228         free(entry->msg);
229         free(entry);
230     }
231     for(entry = client->queue->textqueue_first; entry; entry = next) {
232         next = entry->next;
233         free(entry->msg);
234         free(entry);
235     }
236     if(client->queue->iofd)
237         iohandler_close(client->queue->iofd);
238     free(client->queue);
239     client->queue = NULL;
240 }
241
242 static IOHANDLER_CALLBACK(queue_callback) {
243     struct BotQueue *queue = event->iofd->data;
244     switch(event->type) {
245     case IOEVENT_TIMEOUT:
246         queue->penalty -= queue->rem_penalty;
247         dequeue_bot(queue->client);
248         if(queue->penalty > 0) {
249             struct timeval timeout;
250             gettimeofday(&timeout, NULL);
251             if(queue->penalty >= MAXPENALTY)
252                 queue->rem_penalty = (queue->penalty - MAXPENALTY)+1;
253             else
254                 queue->rem_penalty = queue->penalty;
255             timeout.tv_sec += queue->rem_penalty;
256             queue->iofd = iohandler_timer(timeout, queue_callback);
257             queue->iofd->data = queue;
258         } else {
259             queue->iofd = NULL;
260             queue->penalty = 0;
261         }
262         break;
263     default:
264         break;
265     }
266 }