]> jfr.im git - solanum.git/blame - extensions/hurt.c
Move module description headers to the top
[solanum.git] / extensions / hurt.c
CommitLineData
212380e3
AC
1/*
2 * charybdis: an advanced Internet Relay Chat Daemon(ircd).
3 *
4 * Copyright (C) 2006 charybdis development team
5 * All rights reserved
212380e3
AC
6 */
7#include "stdinc.h"
8#include "modules.h"
9#include "hook.h"
10#include "client.h"
11#include "ircd.h"
12#include "send.h"
13#include "numeric.h"
14#include "hostmask.h"
212380e3
AC
15#include "s_conf.h"
16#include "s_newconf.h"
17#include "hash.h"
bd0d352f 18#include "messages.h"
77d3d2db 19#include "s_assert.h"
212380e3
AC
20
21/* {{{ Structures */
22#define HURT_CUTOFF (10) /* protocol messages. */
23#define HURT_DEFAULT_EXPIRE (7 * 24 * 60) /* minutes. */
24#define HURT_EXIT_REASON "Hurt: Failed to identify to services"
25
26enum {
27 HEAL_NICK = 0,
28 HEAL_IP
29};
30
31typedef struct _hurt_state {
32 time_t start_time;
33 uint32_t n_hurts;
ddcb223e 34 rb_dlink_list hurt_clients;
212380e3
AC
35 uint16_t cutoff;
36 time_t default_expire;
37 const char *exit_reason;
38} hurt_state_t;
39
40typedef struct _hurt {
19fcdbd5 41 char *ip;
212380e3
AC
42 struct sockaddr *saddr;
43 int saddr_bits;
19fcdbd5 44 char *reason;
212380e3
AC
45 time_t expire;
46} hurt_t;
47/* }}} */
48
49/* {{{ Prototypes */
760bafda
AC
50static int mo_hurt(struct MsgBuf *msgbuf_p, struct Client *, struct Client *, int, const char **);
51static int me_hurt(struct MsgBuf *msgbuf_p, struct Client *, struct Client *, int, const char **);
52static int mo_heal(struct MsgBuf *msgbuf_p, struct Client *, struct Client *, int, const char **);
53static int me_heal(struct MsgBuf *msgbuf_p, struct Client *, struct Client *, int, const char **);
212380e3
AC
54
55static int modinit(void);
56static void modfini(void);
57
58static void client_exit_hook(hook_data_client_exit *);
59static void new_local_user_hook(struct Client *);
60static void doing_stats_hook(hook_data_int *hdata);
61
62static void hurt_check_event(void *);
63static void hurt_expire_event(void *);
64
65static hurt_t *hurt_new(time_t, const char *, const char *);
66static void hurt_add(hurt_t *);
67static void hurt_propagate(struct Client *, struct Client *, hurt_t *);
68static hurt_t *hurt_find(const char *ip);
69static hurt_t *hurt_find_exact(const char *ip);
70static void hurt_remove(const char *ip);
71static void hurt_destroy(void *hurt);
72
73static int heal_nick(struct Client *, struct Client *);
74
212380e3
AC
75/* }}} */
76
77/* {{{ State containers */
78
ddcb223e 79rb_dlink_list hurt_confs = { NULL, NULL, 0 };
212380e3
AC
80
81/* }}} */
82
83/* {{{ Messages */
84struct Message hurt_msgtab = {
7baa37a9 85 "HURT", 0, 0, 0, 0, {
212380e3
AC
86 mg_ignore, mg_ignore, mg_ignore,
87 mg_ignore, {me_hurt, 0}, {mo_hurt, 3}
88 }
89};
90
91struct Message heal_msgtab = {
7baa37a9 92 "HEAL", 0, 0, 0, 0, {
212380e3
AC
93 mg_ignore, mg_ignore, mg_ignore,
94 mg_ignore, {me_heal, 0}, {mo_heal, 2}
95 }
96};
97/* }}} */
98
99/* {{{ Misc module stuff */
100mapi_hfn_list_av1 hurt_hfnlist[] = {
101 {"client_exit", (hookfn) client_exit_hook},
102 {"new_local_user", (hookfn) new_local_user_hook},
103 {"doing_stats", (hookfn) doing_stats_hook},
104 {NULL, NULL},
105};
106
107mapi_clist_av1 hurt_clist[] = { &hurt_msgtab, &heal_msgtab, NULL };
108
dacd2aa8
EM
109static const char hurt_desc[] =
110 "Prevents \"hurt\" users from messaging anyone but operators or "
111 "services until they identify or are \"healed\"";
112
113DECLARE_MODULE_AV2(
212380e3
AC
114 hurt,
115 modinit,
116 modfini,
117 hurt_clist,
118 NULL,
119 hurt_hfnlist,
dacd2aa8
EM
120 NULL,
121 NULL,
122 hurt_desc
212380e3
AC
123);
124/* }}} */
125
126hurt_state_t hurt_state = {
127 .cutoff = HURT_CUTOFF,
128 .default_expire = HURT_DEFAULT_EXPIRE,
129 .exit_reason = HURT_EXIT_REASON,
130};
131
132/*
133 * Module constructor/destructor.
134 */
135
136/* {{{ static int modinit() */
036a10a9
AC
137
138struct ev_entry *hurt_expire_ev = NULL;
139struct ev_entry *hurt_check_ev = NULL;
140
212380e3
AC
141static int
142modinit(void)
143{
144 /* set-up hurt_state. */
954012d3 145 hurt_state.start_time = rb_current_time();
212380e3
AC
146
147 /* add our event handlers. */
036a10a9
AC
148 hurt_expire_ev = rb_event_add("hurt_expire", hurt_expire_event, NULL, 60);
149 hurt_check_ev = rb_event_add("hurt_check", hurt_check_event, NULL, 5);
212380e3
AC
150
151 return 0;
152}
153/* }}} */
154
155/* {{{ static void modfini() */
156static void
157modfini(void)
158{
ddcb223e 159 rb_dlink_node *ptr, *next_ptr;
212380e3
AC
160
161 /* and delete our events. */
036a10a9
AC
162 rb_event_delete(hurt_expire_ev);
163 rb_event_delete(hurt_check_ev);
212380e3 164
ddcb223e 165 RB_DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_state.hurt_clients.head)
212380e3 166 {
ddcb223e 167 rb_dlinkDestroy(ptr, &hurt_state.hurt_clients);
212380e3
AC
168 }
169}
170/* }}} */
171
172/*
173 * Message handlers.
174 */
175
176/* {{{ static int mo_hurt()
177 *
178 * HURT [<expire>] <ip> <reason>
55abcbb2 179 *
212380e3
AC
180 * parv[1] - expire or ip
181 * parv[2] - ip or reason
182 * parv[3] - reason or NULL
183 */
184static int
760bafda 185mo_hurt(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p,
212380e3
AC
186 int parc, const char **parv)
187{
188 const char *ip, *expire, *reason;
189 int expire_time;
190 hurt_t *hurt;
191 struct Client *target_p;
192
193 if (!IsOperK(source_p)) {
194 sendto_one(source_p, form_str(ERR_NOPRIVS), me.name,
195 source_p->name, "kline");
196 return 0;
197 }
198
199 if (parc == 3)
200 expire = NULL, ip = parv[1], reason = parv[2];
201 else
202 expire = parv[1], ip = parv[2], reason = parv[3];
203
204 if (!expire)
205 expire_time = HURT_DEFAULT_EXPIRE;
206 if (expire && (expire_time = valid_temp_time(expire)) < 1) {
5366977b 207 sendto_one_notice(source_p, ":Permanent HURTs are not supported");
212380e3
AC
208 return 0;
209 }
210 if (EmptyString(reason)) {
5366977b 211 sendto_one_notice(source_p, ":Empty HURT reasons are bad for business");
212380e3
AC
212 return 0;
213 }
214
215 /* Is this a client? */
216 if (strchr(ip, '.') == NULL && strchr(ip, ':') == NULL)
217 {
218 target_p = find_named_person(ip);
219 if (target_p == NULL)
220 {
221 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
222 form_str(ERR_NOSUCHNICK), ip);
223 return 0;
224 }
225 ip = target_p->orighost;
226 }
227 else
228 {
229 if (!strncmp(ip, "*@", 2))
230 ip += 2;
231 if (strchr(ip, '!') || strchr(ip, '@'))
232 {
233 sendto_one_notice(source_p, ":Invalid HURT mask [%s]",
234 ip);
235 return 0;
236 }
237 }
238
239 if (hurt_find(ip) != NULL) {
5366977b 240 sendto_one(source_p, ":[%s] already HURT", ip);
212380e3
AC
241 return 0;
242 }
243
244 /*
245 * okay, we've got this far, now it's time to add the the HURT locally
246 * and propagate it to other servers on the network.
247 */
248 sendto_realops_snomask(SNO_GENERAL, L_ALL,
249 "%s added HURT on [%s] for %ld minutes with reason [%s]",
250 get_oper_name(source_p), ip, (long) expire_time / 60, reason);
251
252 hurt = hurt_new(expire_time, ip, reason);
253 hurt_add(hurt);
254 hurt_propagate(NULL, source_p, hurt);
255
256 return 0;
257}
258/* }}} */
259
260/* {{{ static int me_hurt()
261 *
262 * [ENCAP mask] HURT <target> <expire> <ip> <reason>
263 *
264 * parv[1] - expire
265 * parv[2] - ip
266 * parv[3] - reason
267 */
268static int
760bafda 269me_hurt(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p,
212380e3
AC
270 int parc, const char **parv)
271{
272 time_t expire_time;
273 hurt_t *hurt;
274
275 /*
276 * right... if we don't get enough arguments, or if we get any invalid
277 * arguments, just ignore this request - shit happens, and it's not worth
278 * dropping a server over.
279 */
280 if (parc < 4 || !IsPerson(source_p))
281 return 0;
282 if ((expire_time = atoi(parv[1])) < 1)
283 return 0;
284 if (hurt_find(parv[2]) != NULL)
285 return 0;
286 if (EmptyString(parv[3]))
287 return 0;
288
289 sendto_realops_snomask(SNO_GENERAL, L_ALL,
290 "%s added HURT on [%s] for %ld minutes with reason [%s]",
291 get_oper_name(source_p), parv[2], (long) expire_time / 60, parv[3]);
292 hurt = hurt_new(expire_time, parv[2], parv[3]);
293 hurt_add(hurt);
294
295 return 0;
296}
297/* }}} */
298
299/* {{{ static int mo_heal()
300 *
301 * HURT <nick>|<ip>
302 *
303 * parv[1] - nick or ip
304 */
305static int
760bafda 306mo_heal(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p,
212380e3
AC
307 int parc, const char **parv)
308{
309 struct Client *target_p;
310
311 if (!IsOperUnkline(source_p))
312 {
313 sendto_one(source_p, form_str(ERR_NOPRIVS),
314 me.name, source_p->name, "unkline");
315 return 0;
316 }
317
2d28539c 318 if (clean_nick(parv[1], 0))
212380e3
AC
319 {
320 target_p = find_named_person(parv[1]);
321 if (target_p == NULL)
322 {
323 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
324 form_str(ERR_NOSUCHNICK), parv[1]);
325 return 0;
326 }
327 if (MyConnect(target_p))
328 heal_nick(source_p, target_p);
329 else
330 sendto_one(target_p, ":%s ENCAP %s HEAL %s",
331 get_id(source_p, target_p),
332 target_p->servptr->name,
333 get_id(target_p, target_p));
334 }
335 else if (strchr(parv[1], '.'))
336 {
337 if (hurt_find_exact(parv[1]) == NULL)
338 {
5366977b 339 sendto_one_notice(source_p, ":Mask [%s] is not HURT", parv[1]);
212380e3
AC
340 return 0;
341 }
342 hurt_remove(parv[1]);
343 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s removed HURT on %s",
344 get_oper_name(source_p), parv[1]);
345 sendto_server(NULL, NULL, NOCAPS, NOCAPS, ":%s ENCAP * HEAL %s",
346 source_p->name, parv[1]);
347 }
348 else
349 {
5366977b 350 sendto_one(source_p, ":[%s] is not a valid IP address/nick", parv[1]);
212380e3
AC
351 return 0;
352 }
353
354 return 0;
355}
356/* }}} */
357
358static int
760bafda 359me_heal(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p,
212380e3
AC
360 int parc, const char **parv)
361{
362 struct Client *target_p;
363
364 /* as noted in me_hurt(), if we don't get sufficient arguments...
365 * *poof*, it's dropped...
366 */
367 if (parc < 2)
368 return 0;
369
2d28539c 370 if (clean_nick(parv[1], 0))
212380e3
AC
371 {
372 target_p = find_person(parv[1]);
373 if (target_p != NULL && MyConnect(target_p))
374 heal_nick(source_p, target_p);
375 }
376 else if (strchr(parv[1], '.')) /* host or mask to remove ban for */
377 {
378 if (hurt_find_exact(parv[1]) == NULL)
379 return 0;
380
381 hurt_remove(parv[1]);
382 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s removed HURT on %s",
383 get_oper_name(source_p), parv[1]);
384 }
385 else
386 return 0;
387
388 return 0;
389}
390
391/*
392 * Event handlers.
393 */
394
395/* {{{ static void hurt_check_event() */
396static void
397hurt_check_event(void *arg)
398{
ddcb223e 399 rb_dlink_node *ptr, *next_ptr;
212380e3
AC
400 struct Client *client_p;
401
ddcb223e 402 RB_DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_state.hurt_clients.head) {
212380e3
AC
403 client_p = ptr->data;
404 if (!EmptyString(client_p->user->suser))
405 {
ddcb223e 406 rb_dlinkDestroy(ptr, &hurt_state.hurt_clients);
212380e3 407 sendto_one_notice(client_p, ":HURT restriction removed for this session");
954012d3 408 client_p->localClient->target_last = rb_current_time(); /* don't ask --nenolod */
212380e3
AC
409 }
410 else if (client_p->localClient->receiveM > hurt_state.cutoff)
411 exit_client(NULL, client_p, &me, hurt_state.exit_reason);
412 }
413}
414/* }}} */
415
416/* {{{ static void hurt_expire_event() */
417static void
418hurt_expire_event(void *unused)
419{
ddcb223e 420 rb_dlink_node *ptr, *next_ptr;
212380e3
AC
421 hurt_t *hurt;
422
ddcb223e 423 RB_DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_confs.head)
212380e3
AC
424 {
425 hurt = (hurt_t *) ptr->data;
426
954012d3 427 if (hurt->expire <= rb_current_time())
212380e3 428 {
ddcb223e 429 rb_dlinkFindDestroy(hurt, &hurt_confs);
212380e3
AC
430 hurt_destroy(hurt);
431 }
432 }
433}
434/* }}} */
435
436/*
437 * Hook functions.
438 */
439
440/* {{{ static void client_exit_hook() */
441static void
442client_exit_hook(hook_data_client_exit *data)
443{
444 s_assert(data != NULL);
445 s_assert(data->target != NULL);
446
ddcb223e 447 rb_dlinkFindDestroy(data->target, &hurt_state.hurt_clients);
212380e3
AC
448}
449/* }}} */
450
451/* {{{ static void new_local_user_hook() */
452static void
453new_local_user_hook(struct Client *source_p)
454{
455 if (IsAnyDead(source_p) || !EmptyString(source_p->user->suser) ||
456 IsExemptKline(source_p))
457 return;
458
459 if (hurt_find(source_p->sockhost) || hurt_find(source_p->orighost))
460 {
954012d3 461 source_p->localClient->target_last = rb_current_time() + 600; /* don't ask --nenolod */
212380e3 462 SetTGChange(source_p);
ddcb223e 463 rb_dlinkAddAlloc(source_p, &hurt_state.hurt_clients);
212380e3 464 sendto_one_notice(source_p, ":You are hurt. Please identify to services immediately, or use /stats p for assistance.");
55abcbb2 465 }
212380e3
AC
466}
467/* }}} */
468
469/* {{{ static void doing_stats_hook() */
470static void
471doing_stats_hook(hook_data_int *hdata)
472{
ddcb223e 473 rb_dlink_node *ptr;
212380e3
AC
474 hurt_t *hurt;
475 struct Client *source_p;
476
477 s_assert(hdata);
478 s_assert(hdata->client);
479
480 source_p = hdata->client;
481 if(hdata->arg2 != (int) 's')
482 return;
483 if((ConfigFileEntry.stats_k_oper_only == 2) && !IsOper(source_p))
484 return;
485 if ((ConfigFileEntry.stats_k_oper_only == 1) && !IsOper(source_p))
486 {
487 hurt = hurt_find(source_p->sockhost);
488 if (hurt != NULL)
489 {
490 sendto_one_numeric(source_p, RPL_STATSKLINE,
491 form_str(RPL_STATSKLINE), 's',
492 "*", hurt->ip, hurt->reason, "", "");
493 return;
494 }
495
496 hurt = hurt_find(source_p->orighost);
497 if (hurt != NULL)
498 {
499 sendto_one_numeric(source_p, RPL_STATSKLINE,
500 form_str(RPL_STATSKLINE), 's',
501 "*", hurt->ip, hurt->reason, "", "");
502 return;
503 }
504 return;
505 }
506
ddcb223e 507 RB_DLINK_FOREACH(ptr, hurt_confs.head)
212380e3
AC
508 {
509 hurt = (hurt_t *) ptr->data;
510 sendto_one_numeric(source_p, RPL_STATSKLINE,
511 form_str(RPL_STATSKLINE), 's',
512 "*", hurt->ip, hurt->reason, "", "");
513 }
514}
515/* }}} */
516
517/* {{{ static void hurt_propagate()
518 *
519 * client_p - specific server to propagate HURT to, or NULL to propagate to all
520 * servers.
521 * source_p - source (oper who added the HURT)
522 * hurt - HURT to be propagated
523 */
524static void
525hurt_propagate(struct Client *client_p, struct Client *source_p, hurt_t *hurt)
526{
527 if (client_p)
528 sendto_one(client_p,
529 ":%s ENCAP %s HURT %ld %s :%s",
530 source_p->name, client_p->name,
954012d3 531 (long)(hurt->expire - rb_current_time()),
212380e3
AC
532 hurt->ip, hurt->reason);
533 else
534 sendto_server(&me, NULL, NOCAPS, NOCAPS,
535 ":%s ENCAP * HURT %ld %s :%s",
536 source_p->name,
954012d3 537 (long)(hurt->expire - rb_current_time()),
212380e3
AC
538 hurt->ip, hurt->reason);
539}
540/* }}} */
541
542/* {{{ static hurt_t *hurt_new() */
543static hurt_t *
544hurt_new(time_t expire, const char *ip, const char *reason)
545{
546 hurt_t *hurt;
547
b42e202d 548 hurt = rb_malloc(sizeof(hurt_t));
212380e3 549
b42e202d
VY
550 hurt->ip = rb_strdup(ip);
551 hurt->reason = rb_strdup(reason);
954012d3 552 hurt->expire = rb_current_time() + expire;
212380e3
AC
553
554 return hurt;
555}
556/* }}} */
557
558/* {{{ static void hurt_destroy() */
559static void
560hurt_destroy(void *hurt)
561{
562 hurt_t *h;
563
564 if (!hurt)
565 return;
566
567 h = (hurt_t *) hurt;
b42e202d
VY
568 rb_free(h->ip);
569 rb_free(h->reason);
570 rb_free(h);
212380e3
AC
571}
572/* }}} */
573
574static void
575hurt_add(hurt_t *hurt)
576{
ddcb223e 577 rb_dlinkAddAlloc(hurt, &hurt_confs);
212380e3
AC
578}
579
580static hurt_t *
581hurt_find_exact(const char *ip)
582{
ddcb223e 583 rb_dlink_node *ptr;
212380e3
AC
584 hurt_t *hurt;
585
ddcb223e 586 RB_DLINK_FOREACH(ptr, hurt_confs.head)
212380e3
AC
587 {
588 hurt = (hurt_t *) ptr->data;
589
590 if (!strcasecmp(ip, hurt->ip))
591 return hurt;
592 }
593
594 return NULL;
595}
596
597static hurt_t *
598hurt_find(const char *ip)
599{
ddcb223e 600 rb_dlink_node *ptr;
212380e3
AC
601 hurt_t *hurt;
602
ddcb223e 603 RB_DLINK_FOREACH(ptr, hurt_confs.head)
212380e3
AC
604 {
605 hurt = (hurt_t *) ptr->data;
606
607 if (match(hurt->ip, ip))
608 return hurt;
609 }
610
611 return NULL;
612}
613
614static void
615hurt_remove(const char *ip)
616{
617 hurt_t *hurt = hurt_find_exact(ip);
618
ddcb223e 619 rb_dlinkFindDestroy(hurt, &hurt_confs);
212380e3
AC
620 hurt_destroy(hurt);
621}
622
623/* {{{ static int heal_nick() */
624static int
625heal_nick(struct Client *source_p, struct Client *target_p)
626{
ddcb223e 627 if (rb_dlinkFindDestroy(target_p, &hurt_state.hurt_clients))
212380e3
AC
628 {
629 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s used HEAL on %s",
630 get_oper_name(source_p), get_client_name(target_p, HIDE_IP));
631 sendto_one_notice(target_p, ":HURT restriction temporarily removed by operator");
632 sendto_one_notice(source_p, ":HURT restriction on %s temporarily removed", target_p->name);
954012d3 633 target_p->localClient->target_last = rb_current_time(); /* don't ask --nenolod */
212380e3
AC
634 return 1;
635 }
636 else
637 {
638 sendto_one_notice(source_p, ":%s was not hurt", target_p->name);
639 return 0;
640 }
641}
642/* }}} */
643
212380e3
AC
644/*
645 * vim: ts=8 sw=8 noet fdm=marker tw=80
646 */