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