modified code to use IOHandler functions instead of own ones
[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 int queue_add(struct ClientSocket *client, char* msg, int len) {
59     if(!client->queue)
60         client->queue = initialize_queue(client);
61     struct BotQueue *queue = client->queue;
62     char *args = strstr(msg, " ");
63     int type;
64     int add_queue = 0;
65     if(args) {
66         *args = '\0';
67         if(!stricmp(msg, "MODE")) 
68             type = 3;
69         else if(!stricmp(msg, "KICK")) 
70             type = 3;
71         else if(!stricmp(msg, "PONG")) 
72             type = 3;
73         else if(!stricmp(msg, "PRIVMSG")) 
74             type = 1;
75         else if(!stricmp(msg, "NOTICE")) 
76             type = 1;
77         else if(!stricmp(msg, "WHO")) 
78             type = 1;
79         else
80             type = 2;
81         *args = ' ';
82     } else
83         type = 2;
84     
85     //check if we need to queue
86     switch(type) {
87     case 1:
88         if(queue->textqueue_first) {
89             add_queue = 1;
90             break;
91         }
92     case 2:
93         if(queue->normalqueue_first) {
94             add_queue = 1;
95             break;
96         }
97     case 3:
98         if(queue->fastqueue_first) {
99             add_queue = 1;
100             break;
101         }
102     default:
103         if(queue->penalty >= MAXPENALTY)
104             add_queue = 1;
105         break;
106     }
107     
108     if(!add_queue) {
109         int penalty = calculate_penalty(msg);
110         write_socket_force(client, msg, len);
111         queue->penalty += penalty;
112         if(!queue->iofd) {
113             struct timeval timeout;
114             gettimeofday(&timeout, NULL);
115             queue->rem_penalty = (MAXPENALTY - queue->penalty) + 1;
116             timeout.tv_sec += queue->rem_penalty;
117             queue->iofd = iohandler_timer(timeout, queue_callback);
118         }
119     } else {
120         struct QueueEntry *entry = malloc(sizeof(*entry));
121         if (!entry) {
122             perror("malloc() failed");
123             return 0;
124         }
125         entry->msg = strdup(msg);
126         entry->next = NULL;
127         if(type == 1) { //low priority
128             if(queue->textqueue_last) {
129                 queue->textqueue_last->next = entry;
130                 queue->textqueue_last = entry;
131             } else {
132                 queue->textqueue_last = entry;
133                 queue->textqueue_first = entry;
134             }
135         } else if(type == 2) { //normal priority
136             if(queue->normalqueue_last) {
137                 queue->normalqueue_last->next = entry;
138                 queue->normalqueue_last = entry;
139             } else {
140                 queue->normalqueue_last = entry;
141                 queue->normalqueue_first = entry;
142             }
143         } else if(type == 3) { //high priority
144             if(queue->fastqueue_last) {
145                 queue->fastqueue_last->next = entry;
146                 queue->fastqueue_last = entry;
147             } else {
148                 queue->fastqueue_last = entry;
149                 queue->fastqueue_first = entry;
150             }
151         }
152     }
153     return 1;
154 }
155
156 static int calculate_penalty(char *message) {
157     int msglen = strlen(message);
158     int penalty = (2 + msglen / 100);
159     return penalty;
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 }