some fixes for previous commit
[NeonServV5.git] / src / IRCQueue.c
1 /* IRCQueue.c - NeonServ v5.5
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
21 #define MAXPENALTY 8 /* 4 messages */
22
23 struct QueueEntry {
24     char *msg;
25     struct QueueEntry *next;
26 };
27
28 struct BotQueue {
29     struct ClientSocket *client;
30     struct IODescriptor *iofd;
31     int penalty : 8;
32     int rem_penalty : 8;
33     struct QueueEntry *fastqueue_first, *fastqueue_last;
34     struct QueueEntry *normalqueue_first, *normalqueue_last;
35     struct QueueEntry *textqueue_first, *textqueue_last;
36 };
37
38 static IOHANDLER_CALLBACK(queue_callback);
39
40 static struct BotQueue *initialize_queue(struct ClientSocket *client) {
41     struct BotQueue *queue = malloc(sizeof(*queue));
42     if (!queue) {
43         perror("malloc() failed");
44         return NULL;
45     }
46     queue->client = client;
47     client->queue = queue;
48     queue->penalty = 0;
49     queue->fastqueue_first = NULL;
50     queue->fastqueue_last = NULL;
51     queue->normalqueue_first = NULL;
52     queue->normalqueue_last = NULL;
53     queue->textqueue_first = NULL;
54     queue->textqueue_last = NULL;
55     return queue;
56 }
57
58 static int calculate_penalty(char *message) {
59     int msglen = strlen(message);
60     int penalty = (2 + msglen / 100);
61     return penalty;
62 }
63
64 int queue_add(struct ClientSocket *client, char* msg, int len) {
65     if(!client->queue)
66         client->queue = initialize_queue(client);
67     struct BotQueue *queue = client->queue;
68     char *args = strstr(msg, " ");
69     int type;
70     int add_queue = 0;
71     if(args) {
72         *args = '\0';
73         if(!stricmp(msg, "MODE")) 
74             type = 3;
75         else if(!stricmp(msg, "KICK")) 
76             type = 3;
77         else if(!stricmp(msg, "PONG")) 
78             type = 3;
79         else if(!stricmp(msg, "PRIVMSG")) 
80             type = 1;
81         else if(!stricmp(msg, "NOTICE")) 
82             type = 1;
83         else if(!stricmp(msg, "WHO")) 
84             type = 1;
85         else
86             type = 2;
87         *args = ' ';
88     } else
89         type = 2;
90     
91     //check if we need to queue
92     switch(type) {
93     case 1:
94         if(queue->textqueue_first) {
95             add_queue = 1;
96             break;
97         }
98     case 2:
99         if(queue->normalqueue_first) {
100             add_queue = 1;
101             break;
102         }
103     case 3:
104         if(queue->fastqueue_first) {
105             add_queue = 1;
106             break;
107         }
108     default:
109         if(queue->penalty >= MAXPENALTY)
110             add_queue = 1;
111         break;
112     }
113     
114     if(!add_queue) {
115         int penalty = calculate_penalty(msg);
116         write_socket_force(client, msg, len);
117         queue->penalty += penalty;
118         if(!queue->iofd) {
119             struct timeval timeout;
120             gettimeofday(&timeout, NULL);
121             queue->rem_penalty = (MAXPENALTY - queue->penalty) + 1;
122             timeout.tv_sec += queue->rem_penalty;
123             queue->iofd = iohandler_timer(timeout, queue_callback);
124         }
125     } else {
126         struct QueueEntry *entry = malloc(sizeof(*entry));
127         if (!entry) {
128             perror("malloc() failed");
129             return 0;
130         }
131         entry->msg = strdup(msg);
132         entry->next = NULL;
133         if(type == 1) { //low priority
134             if(queue->textqueue_last) {
135                 queue->textqueue_last->next = entry;
136                 queue->textqueue_last = entry;
137             } else {
138                 queue->textqueue_last = entry;
139                 queue->textqueue_first = entry;
140             }
141         } else if(type == 2) { //normal priority
142             if(queue->normalqueue_last) {
143                 queue->normalqueue_last->next = entry;
144                 queue->normalqueue_last = entry;
145             } else {
146                 queue->normalqueue_last = entry;
147                 queue->normalqueue_first = entry;
148             }
149         } else if(type == 3) { //high priority
150             if(queue->fastqueue_last) {
151                 queue->fastqueue_last->next = entry;
152                 queue->fastqueue_last = entry;
153             } else {
154                 queue->fastqueue_last = entry;
155                 queue->fastqueue_first = entry;
156             }
157         }
158     }
159     return 1;
160 }
161
162 static void dequeue_bot(struct ClientSocket *client) {
163     if(client->queue->penalty >= MAXPENALTY) return;
164     int penalty;
165     //try to send high priority messages
166     if(client->queue->fastqueue_first) {
167         do {
168             struct QueueEntry *entry = client->queue->fastqueue_first;
169             if(!entry->next)
170                 client->queue->fastqueue_last = NULL;
171             client->queue->fastqueue_first = client->queue->fastqueue_first->next;
172             penalty = calculate_penalty(entry->msg);
173             write_socket_force(client, entry->msg, strlen(entry->msg));
174             client->queue->penalty += penalty;
175             free(entry->msg);
176             free(entry);
177         } while(client->queue->penalty < MAXPENALTY && client->queue->fastqueue_first);
178     }
179     if(client->queue->penalty >= MAXPENALTY) return;
180     //try to send normal priority messages
181     if(client->queue->normalqueue_first) {
182         do {
183             struct QueueEntry *entry = client->queue->normalqueue_first;
184             if(!entry->next)
185                 client->queue->normalqueue_last = NULL;
186             client->queue->normalqueue_first = client->queue->normalqueue_first->next;
187             penalty = calculate_penalty(entry->msg);
188             write_socket_force(client, entry->msg, strlen(entry->msg));
189             client->queue->penalty += penalty;
190             free(entry->msg);
191             free(entry);
192         } while(client->queue->penalty < MAXPENALTY && client->queue->normalqueue_first);
193     }
194     if(client->queue->penalty >= MAXPENALTY) return;
195     //try to send low priority messages
196     if(client->queue->textqueue_first) {
197         do {
198             struct QueueEntry *entry = client->queue->textqueue_first;
199             if(!entry->next)
200                 client->queue->textqueue_last = NULL;
201             client->queue->textqueue_first = client->queue->textqueue_first->next;
202             penalty = calculate_penalty(entry->msg);
203             write_socket_force(client, entry->msg, strlen(entry->msg));
204             client->queue->penalty += penalty;
205             free(entry->msg);
206             free(entry);
207         } while(client->queue->penalty < MAXPENALTY && client->queue->textqueue_first);
208     }
209 }
210
211 void queue_destroy(struct ClientSocket *client) {
212     if(!client->queue) return;
213     struct QueueEntry *entry, *next;
214     for(entry = client->queue->fastqueue_first; entry; entry = next) {
215         next = entry->next;
216         free(entry->msg);
217         free(entry);
218     }
219     for(entry = client->queue->normalqueue_first; entry; entry = next) {
220         next = entry->next;
221         free(entry->msg);
222         free(entry);
223     }
224     for(entry = client->queue->textqueue_first; entry; entry = next) {
225         next = entry->next;
226         free(entry->msg);
227         free(entry);
228     }
229     if(client->queue->iofd)
230         iohandler_close(client->queue->iofd);
231     free(client->queue);
232     client->queue = NULL;
233 }
234
235 static IOHANDLER_CALLBACK(queue_callback) {
236     struct BotQueue *queue = event->iofd->data;
237     switch(event->type) {
238     case IOEVENT_TIMEOUT:
239         queue->penalty -= queue->rem_penalty;
240         dequeue_bot(queue->client);
241         if(queue->penalty > 0) {
242             struct timeval timeout;
243             gettimeofday(&timeout, NULL);
244             queue->rem_penalty = (MAXPENALTY - queue->penalty) + 1;
245             timeout.tv_sec += queue->rem_penalty;
246             queue->iofd = iohandler_timer(timeout, queue_callback);
247         } else {
248             queue->iofd = NULL;
249             queue->penalty = 0;
250         }
251         break;
252     default:
253         break;
254     }
255 }