563dfa47ba3193bdd30707067d42d1157be35639
[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         } else {
289                 /* All parts (nick, user and domain name) are given */
290                 strlcpy(mask, Pattern, len);
291         }
292 } /* Lists_MakeMask */
293
294 /**
295  * Check if a client is listed in a list.
296  *
297  * @param h List head.
298  * @param Client Client to check.
299  * @return true if client is listed, false if not.
300  */
301 bool
302 Lists_Check(struct list_head *h, CLIENT *Client)
303 {
304         return Lists_CheckReason(h, Client, NULL, 0);
305 }
306
307 /**
308  * Check if a client is listed in a list and store the reason.
309  *
310  * @param h      List head.
311  * @param Client Client to check.
312  * @param reason Buffer to store the reason.
313  * @param len    Size of the buffer if reason should be saved.
314  * @return true if client is listed, false if not.
315  */
316 bool
317 Lists_CheckReason(struct list_head *h, CLIENT *Client, char *reason, size_t len)
318 {
319         struct list_elem *e, *last, *next;
320
321         assert(h != NULL);
322
323         e = h->first;
324         last = NULL;
325
326         while (e) {
327                 next = e->next;
328                 if (Match(e->mask, Client_MaskCloaked(Client))) {
329                         if (len && e->reason)
330                                 strlcpy(reason, e->reason, len);
331                         if (e->valid_until == 1) {
332                                 /* Entry is valid only once, delete it */
333                                 LogDebug("Deleted \"%s\" from list (used).",
334                                          e->mask);
335                                 Lists_Unlink(h, last, e);
336                         }
337                         return true;
338                 }
339                 last = e;
340                 e = next;
341         }
342
343         return false;
344 }
345
346 /**
347  * Check list and purge expired entries.
348  *
349  * @param h List head.
350  */
351 GLOBAL void
352 Lists_Expire(struct list_head *h, const char *ListName)
353 {
354         struct list_elem *e, *last, *next;
355         time_t now;
356
357         assert(h != NULL);
358
359         e = h->first;
360         last = NULL;
361         now = time(NULL);
362
363         while (e) {
364                 next = e->next;
365                 if (e->valid_until > 1 && e->valid_until < now) {
366                         /* Entry is expired, delete it */
367                         if (e->reason)
368                                 Log(LOG_INFO,
369                                     "Deleted \"%s\" (\"%s\") from %s list (expired).",
370                                     e->mask, e->reason, ListName);
371                         else
372                                 Log(LOG_INFO,
373                                     "Deleted \"%s\" from %s list (expired).",
374                                     e->mask, ListName);
375                         Lists_Unlink(h, last, e);
376                         e = next;
377                         continue;
378                 }
379                 last = e;
380                 e = next;
381         }
382 }
383
384 /**
385  * Return the number of entries of a list.
386  *
387  * @param h List head.
388  * @return Number of items.
389  */
390 GLOBAL unsigned long
391 Lists_Count(struct list_head *h)
392 {
393         struct list_elem *e;
394         unsigned long count = 0;
395
396         assert(h != NULL);
397
398         e = h->first;
399         while (e) {
400                 count++;
401                 e = e->next;
402         }
403         return count;
404 }
405
406 /* -eof- */