Spelling fix: "nick name" -> "nickname"
[ngircd-alex.git] / src / ngircd / lists.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #include "portab.h"
13
14 /**
15  * @file
16  * Management of IRC lists: ban, invite, etc.
17  */
18
19 #include "imp.h"
20 #include <assert.h>
21
22 #include "defines.h"
23 #include "conn.h"
24 #include "channel.h"
25 #include "log.h"
26 #include "match.h"
27 #include "messages.h"
28 #include "irc-write.h"
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <strings.h>
33
34 #include "exp.h"
35 #include "lists.h"
36
37 #define MASK_LEN        (2*CLIENT_HOST_LEN)
38
39 struct list_elem {
40         struct list_elem *next; /** pointer to next list element */
41         char mask[MASK_LEN];    /** IRC mask */
42         char *reason;           /** Optional "reason" text */
43         time_t valid_until;     /** 0: unlimited; 1: once; t(>1): until t */
44 };
45
46 /**
47  * Get IRC mask stored in list element.
48  *
49  * @param list_elem List element.
50  * @return Pointer to IRC mask
51  */
52 GLOBAL const char *
53 Lists_GetMask(const struct list_elem *e)
54 {
55         assert(e != NULL);
56         return e->mask;
57 }
58
59 /**
60  * Get optional "reason" text stored in list element.
61  *
62  * @param list_elem List element.
63  * @return Pointer to "reason" text or empty string ("").
64  */
65 GLOBAL const char *
66 Lists_GetReason(const struct list_elem *e)
67 {
68         assert(e != NULL);
69         return e->reason ? e->reason : "";
70 }
71
72 /**
73  * Get "validity" value stored in list element.
74  *
75  * @param list_elem List element.
76  * @return Validity: 0=unlimited, 1=once, >1 until this time stamp.
77  */
78 GLOBAL time_t
79 Lists_GetValidity(const struct list_elem *e)
80 {
81         assert(e != NULL);
82         return e->valid_until;
83 }
84
85 /**
86  * Get first list element of a list.
87  *
88  * @param h List head.
89  * @return Pointer to first list element.
90  */
91 GLOBAL struct list_elem*
92 Lists_GetFirst(const struct list_head *h)
93 {
94         assert(h != NULL);
95         return h->first;
96 }
97
98 /**
99  * Get next list element of a list.
100  *
101  * @param e Current list element.
102  * @return Pointer to next list element.
103  */
104 GLOBAL struct list_elem*
105 Lists_GetNext(const struct list_elem *e)
106 {
107         assert(e != NULL);
108         return e->next;
109 }
110
111 /**
112  * Add a new mask to a list.
113  *
114  * @param h List head.
115  * @param Mask The IRC mask to add to the list.
116  * @param ValidUntil 0: unlimited, 1: only once, t>1: until given time_t.
117  * @param Reason Reason string or NULL, if no reason should be saved.
118  * @return true on success, false otherwise.
119  */
120 bool
121 Lists_Add(struct list_head *h, const char *Mask, time_t ValidUntil,
122           const char *Reason)
123 {
124         struct list_elem *e, *newelem;
125
126         assert(h != NULL);
127         assert(Mask != NULL);
128
129         e = Lists_CheckDupeMask(h, Mask);
130         if (e) {
131                 e->valid_until = ValidUntil;
132                 if (Reason) {
133                         free(e->reason);
134                         e->reason = strdup(Reason);
135                 }
136                 return true;
137         }
138
139         e = Lists_GetFirst(h);
140
141         newelem = malloc(sizeof(struct list_elem));
142         if (!newelem) {
143                 Log(LOG_EMERG,
144                     "Can't allocate memory for new list entry!");
145                 return false;
146         }
147
148         strlcpy(newelem->mask, Mask, sizeof(newelem->mask));
149         if (Reason) {
150                 newelem->reason = strdup(Reason);
151                 if (!newelem->reason)
152                         Log(LOG_EMERG,
153                             "Can't allocate memory for new list reason text!");
154         }
155         else
156                 newelem->reason = NULL;
157         newelem->valid_until = ValidUntil;
158         newelem->next = e;
159         h->first = newelem;
160
161         return true;
162 }
163
164 /**
165  * Delete a list element from a list.
166  *
167  * @param h List head.
168  * @param p Pointer to previous list element or NULL, if there is none.
169  * @param victim List element to delete.
170  */
171 static void
172 Lists_Unlink(struct list_head *h, struct list_elem *p, struct list_elem *victim)
173 {
174         assert(victim != NULL);
175         assert(h != NULL);
176
177         if (p)
178                 p->next = victim->next;
179         else
180                 h->first = victim->next;
181
182         if (victim->reason)
183                 free(victim->reason);
184
185         free(victim);
186 }
187
188 /**
189  * Delete a given IRC mask from a list.
190  *
191  * @param h List head.
192  * @param Mask IRC mask to delete from the list.
193  */
194 GLOBAL void
195 Lists_Del(struct list_head *h, const char *Mask)
196 {
197         struct list_elem *e, *last, *victim;
198
199         assert(h != NULL);
200         assert(Mask != NULL);
201
202         last = NULL;
203         e = Lists_GetFirst(h);
204         while (e) {
205                 if (strcasecmp(e->mask, Mask) == 0) {
206                         LogDebug("Deleted \"%s\" from list", e->mask);
207                         victim = e;
208                         e = victim->next;
209                         Lists_Unlink(h, last, victim);
210                         continue;
211                 }
212                 last = e;
213                 e = e->next;
214         }
215 }
216
217 /**
218  * Free a complete list.
219  *
220  * @param head List head.
221  */
222 GLOBAL void
223 Lists_Free(struct list_head *head)
224 {
225         struct list_elem *e, *victim;
226
227         assert(head != NULL);
228
229         e = head->first;
230         head->first = NULL;
231         while (e) {
232                 LogDebug("Deleted \"%s\" from list" , e->mask);
233                 victim = e;
234                 e = e->next;
235                 if (victim->reason)
236                         free(victim->reason);
237                 free(victim);
238         }
239 }
240
241 /**
242  * Check if an IRC mask is already contained in a list.
243  *
244  * @param h List head.
245  * @param Mask IRC mask to test.
246  * @return true if mask is already stored in the list, false otherwise.
247  */
248 GLOBAL struct list_elem *
249 Lists_CheckDupeMask(const struct list_head *h, const char *Mask )
250 {
251         struct list_elem *e;
252         e = h->first;
253         while (e) {
254                 if (strcasecmp(e->mask, Mask) == 0)
255                         return e;
256                 e = e->next;
257         }
258         return NULL;
259 }
260
261 /**
262  * Generate a valid IRC mask from "any" string given.
263  *
264  * Attention: This mask is only valid until the next call to Lists_MakeMask(),
265  * because a single global buffer ist used! You have to copy the generated
266  * mask to some sane location yourself!
267  *
268  * @param Pattern Source string to generate an IRC mask for.
269  * @return Pointer to global result buffer.
270  */
271 GLOBAL const char *
272 Lists_MakeMask(const char *Pattern)
273 {
274         static char TheMask[MASK_LEN];
275         char *excl, *at;
276
277         assert(Pattern != NULL);
278
279         excl = strchr(Pattern, '!');
280         at = strchr(Pattern, '@');
281
282         if (at && at < excl)
283                 excl = NULL;
284
285         if (!at && !excl) {
286                 /* Neither "!" nor "@" found: use string as nickname */
287                 strlcpy(TheMask, Pattern, sizeof(TheMask) - 5);
288                 strlcat(TheMask, "!*@*", sizeof(TheMask));
289                 return TheMask;
290         }
291
292         if (!at && excl) {
293                 /* Domain part is missing */
294                 strlcpy(TheMask, Pattern, sizeof(TheMask) - 3);
295                 strlcat(TheMask, "@*", sizeof(TheMask));
296                 return TheMask;
297         }
298
299         if (at && !excl) {
300                 /* User name is missing */
301                 *at = '\0'; at++;
302                 strlcpy(TheMask, Pattern, sizeof(TheMask) - 5);
303                 strlcat(TheMask, "!*@", sizeof(TheMask));
304                 strlcat(TheMask, at, sizeof(TheMask));
305                 return TheMask;
306         }
307
308         /* All parts (nick, user and domain name) are given */
309         strlcpy(TheMask, Pattern, sizeof(TheMask));
310         return TheMask;
311 } /* Lists_MakeMask */
312
313 /**
314  * Check if a client is listed in a list.
315  *
316  * @param h List head.
317  * @param Client Client to check.
318  * @return true if client is listed, false if not.
319  */
320 bool
321 Lists_Check(struct list_head *h, CLIENT *Client)
322 {
323         return Lists_CheckReason(h, Client) != NULL;
324 }
325
326 /**
327  * Check if a client is listed in a list and return the "reason".
328  *
329  * @param h List head.
330  * @param Client Client to check.
331  * @return true if client is listed, false if not.
332  */
333 char *
334 Lists_CheckReason(struct list_head *h, CLIENT *Client)
335 {
336         struct list_elem *e, *last, *next;
337
338         assert(h != NULL);
339
340         e = h->first;
341         last = NULL;
342
343         while (e) {
344                 next = e->next;
345                 if (Match(e->mask, Client_Mask(Client))) {
346                         if (e->valid_until == 1) {
347                                 /* Entry is valid only once, delete it */
348                                 LogDebug("Deleted \"%s\" from list (used).",
349                                          e->mask);
350                                 Lists_Unlink(h, last, e);
351                         }
352                         return e->reason ? e->reason : "";
353                 }
354                 last = e;
355                 e = next;
356         }
357
358         return NULL;
359 }
360
361 /**
362  * Check list and purge expired entries.
363  *
364  * @param h List head.
365  */
366 GLOBAL void
367 Lists_Expire(struct list_head *h, const char *ListName)
368 {
369         struct list_elem *e, *last, *next;
370         time_t now;
371
372         assert(h != NULL);
373
374         e = h->first;
375         last = NULL;
376         now = time(NULL);
377
378         while (e) {
379                 next = e->next;
380                 if (e->valid_until > 1 && e->valid_until < now) {
381                         /* Entry is expired, delete it */
382                         if (e->reason)
383                                 Log(LOG_INFO,
384                                     "Deleted \"%s\" (\"%s\") from %s list (expired).",
385                                     e->mask, e->reason, ListName);
386                         else
387                                 Log(LOG_INFO,
388                                     "Deleted \"%s\" from %s list (expired).",
389                                     e->mask, ListName);
390                         Lists_Unlink(h, last, e);
391                         e = next;
392                         continue;
393                 }
394                 last = e;
395                 e = next;
396         }
397 }
398
399 /**
400  * Return the number of entries of a list.
401  *
402  * @param h List head.
403  * @return Number of items.
404  */
405 GLOBAL unsigned long
406 Lists_Count(struct list_head *h)
407 {
408         struct list_elem *e;
409         unsigned long count = 0;
410
411         assert(h != NULL);
412
413         e = h->first;
414         while (e) {
415                 count++;
416                 e = e->next;
417         }
418         return count;
419 }
420
421 /* -eof- */