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