Import from http://svn.freenode.net/ircd-seven/private/beu/seven (r196).
[seven-1.x.git] / unsupported / hurt.c
blobff91733a4cc56ab2195a88bf594ddea079bf9aec
1 /*
2 * charybdis: an advanced Internet Relay Chat Daemon(ircd).
4 * Copyright (C) 2006 charybdis development team
5 * All rights reserved
7 * $Id: hurt.c 85 2006-09-24 19:10:39Z beu $
8 */
9 #include "stdinc.h"
10 #include "modules.h"
11 #include "hook.h"
12 #include "client.h"
13 #include "ircd.h"
14 #include "send.h"
15 #include "numeric.h"
16 #include "hostmask.h"
17 #include "event.h"
18 #include "s_conf.h"
19 #include "s_newconf.h"
20 #include "hash.h"
22 /* {{{ Structures */
23 #define HURT_CUTOFF (10) /* protocol messages. */
24 #define HURT_DEFAULT_EXPIRE (7 * 24 * 60) /* minutes. */
25 #define HURT_EXIT_REASON "Hurt: Failed to identify to services"
27 enum {
28 HEAL_NICK = 0,
29 HEAL_IP
32 typedef struct _hurt_state {
33 time_t start_time;
34 uint32_t n_hurts;
35 dlink_list hurt_clients;
36 uint16_t cutoff;
37 time_t default_expire;
38 const char *exit_reason;
39 } hurt_state_t;
41 typedef struct _hurt {
42 const char *ip;
43 struct sockaddr *saddr;
44 int saddr_bits;
45 const char *reason;
46 time_t expire;
47 } hurt_t;
48 /* }}} */
50 /* {{{ Prototypes */
51 static int mo_hurt(struct Client *, struct Client *, int, const char **);
52 static int me_hurt(struct Client *, struct Client *, int, const char **);
53 static int mo_heal(struct Client *, struct Client *, int, const char **);
54 static int me_heal(struct Client *, struct Client *, int, const char **);
56 static int modinit(void);
57 static void modfini(void);
59 static void client_exit_hook(hook_data_client_exit *);
60 static void new_local_user_hook(struct Client *);
61 static void doing_stats_hook(hook_data_int *hdata);
63 static void hurt_check_event(void *);
64 static void hurt_expire_event(void *);
66 static hurt_t *hurt_new(time_t, const char *, const char *);
67 static void hurt_add(hurt_t *);
68 static void hurt_propagate(struct Client *, struct Client *, hurt_t *);
69 static hurt_t *hurt_find(const char *ip);
70 static hurt_t *hurt_find_exact(const char *ip);
71 static void hurt_remove(const char *ip);
72 static void hurt_destroy(void *hurt);
74 static int heal_nick(struct Client *, struct Client *);
76 static int nick_is_valid(const char *);
77 /* }}} */
79 /* {{{ State containers */
81 dlink_list hurt_confs = { NULL, NULL, 0 };
83 /* }}} */
85 /* {{{ Messages */
86 struct Message hurt_msgtab = {
87 "HURT", 0, 0, 0, MFLG_SLOW, {
88 mg_ignore, mg_ignore, mg_ignore,
89 mg_ignore, {me_hurt, 0}, {mo_hurt, 3}
93 struct Message heal_msgtab = {
94 "HEAL", 0, 0, 0, MFLG_SLOW, {
95 mg_ignore, mg_ignore, mg_ignore,
96 mg_ignore, {me_heal, 0}, {mo_heal, 2}
99 /* }}} */
101 /* {{{ Misc module stuff */
102 mapi_hfn_list_av1 hurt_hfnlist[] = {
103 {"client_exit", (hookfn) client_exit_hook},
104 {"new_local_user", (hookfn) new_local_user_hook},
105 {"doing_stats", (hookfn) doing_stats_hook},
106 {NULL, NULL},
109 mapi_clist_av1 hurt_clist[] = { &hurt_msgtab, &heal_msgtab, NULL };
111 DECLARE_MODULE_AV1(
112 hurt,
113 modinit,
114 modfini,
115 hurt_clist,
116 NULL,
117 hurt_hfnlist,
118 "$Revision: 85 $"
120 /* }}} */
122 hurt_state_t hurt_state = {
123 .cutoff = HURT_CUTOFF,
124 .default_expire = HURT_DEFAULT_EXPIRE,
125 .exit_reason = HURT_EXIT_REASON,
129 * Module constructor/destructor.
132 /* {{{ static int modinit() */
133 static int
134 modinit(void)
136 /* set-up hurt_state. */
137 hurt_state.start_time = CurrentTime;
139 /* add our event handlers. */
140 eventAdd("hurt_expire", hurt_expire_event, NULL, 60);
141 eventAdd("hurt_check", hurt_check_event, NULL, 5);
143 return 0;
145 /* }}} */
147 /* {{{ static void modfini() */
148 static void
149 modfini(void)
151 dlink_node *ptr, *next_ptr;
153 /* and delete our events. */
154 eventDelete(hurt_expire_event, NULL);
155 eventDelete(hurt_check_event, NULL);
157 DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_state.hurt_clients.head)
159 dlinkDestroy(ptr, &hurt_state.hurt_clients);
162 /* }}} */
165 * Message handlers.
168 /* {{{ static int mo_hurt()
170 * HURT [<expire>] <ip> <reason>
172 * parv[1] - expire or ip
173 * parv[2] - ip or reason
174 * parv[3] - reason or NULL
176 static int
177 mo_hurt(struct Client *client_p, struct Client *source_p,
178 int parc, const char **parv)
180 const char *ip, *expire, *reason;
181 int expire_time;
182 hurt_t *hurt;
183 struct Client *target_p;
185 if (!IsOperK(source_p)) {
186 sendto_one(source_p, form_str(ERR_NOPRIVS), me.name,
187 source_p->name, "kline");
188 return 0;
191 if (parc == 3)
192 expire = NULL, ip = parv[1], reason = parv[2];
193 else
194 expire = parv[1], ip = parv[2], reason = parv[3];
196 if (!expire)
197 expire_time = HURT_DEFAULT_EXPIRE;
198 if (expire && (expire_time = valid_temp_time(expire)) < 1) {
199 sendto_one(source_p,
200 ":%s NOTICE %s :Permanent HURTs are not supported",
201 me.name, source_p->name);
202 return 0;
204 if (EmptyString(reason)) {
205 sendto_one(source_p,
206 ":%s NOTICE %s :Empty HURT reasons are bad for business",
207 me.name, source_p->name);
208 return 0;
211 /* Is this a client? */
212 if (strchr(ip, '.') == NULL && strchr(ip, ':') == NULL)
214 target_p = find_named_person(ip);
215 if (target_p == NULL)
217 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
218 form_str(ERR_NOSUCHNICK), ip);
219 return 0;
221 ip = target_p->orighost;
223 else
225 if (!strncmp(ip, "*@", 2))
226 ip += 2;
227 if (strchr(ip, '!') || strchr(ip, '@'))
229 sendto_one_notice(source_p, ":Invalid HURT mask [%s]",
230 ip);
231 return 0;
235 if (hurt_find(ip) != NULL) {
236 sendto_one(source_p,
237 ":%s NOTICE %s :[%s] already HURT",
238 me.name, source_p->name, ip);
239 return 0;
243 * okay, we've got this far, now it's time to add the the HURT locally
244 * and propagate it to other servers on the network.
246 sendto_realops_snomask(SNO_GENERAL, L_ALL,
247 "%s added HURT on [%s] for %ld minutes with reason [%s]",
248 get_oper_name(source_p), ip, (long) expire_time / 60, reason);
250 hurt = hurt_new(expire_time, ip, reason);
251 hurt_add(hurt);
252 hurt_propagate(NULL, source_p, hurt);
254 return 0;
256 /* }}} */
258 /* {{{ static int me_hurt()
260 * [ENCAP mask] HURT <target> <expire> <ip> <reason>
262 * parv[1] - expire
263 * parv[2] - ip
264 * parv[3] - reason
266 static int
267 me_hurt(struct Client *client_p, struct Client *source_p,
268 int parc, const char **parv)
270 time_t expire_time;
271 hurt_t *hurt;
274 * right... if we don't get enough arguments, or if we get any invalid
275 * arguments, just ignore this request - shit happens, and it's not worth
276 * dropping a server over.
278 if (parc < 4 || !IsPerson(source_p))
279 return 0;
280 if ((expire_time = atoi(parv[1])) < 1)
281 return 0;
282 if (hurt_find(parv[2]) != NULL)
283 return 0;
284 if (EmptyString(parv[3]))
285 return 0;
287 sendto_realops_snomask(SNO_GENERAL, L_ALL,
288 "%s added HURT on [%s] for %ld minutes with reason [%s]",
289 get_oper_name(source_p), parv[2], (long) expire_time / 60, parv[3]);
290 hurt = hurt_new(expire_time, parv[2], parv[3]);
291 hurt_add(hurt);
293 return 0;
295 /* }}} */
297 /* {{{ static int mo_heal()
299 * HURT <nick>|<ip>
301 * parv[1] - nick or ip
303 static int
304 mo_heal(struct Client *client_p, struct Client *source_p,
305 int parc, const char **parv)
307 struct Client *target_p;
309 if (!IsOperUnkline(source_p))
311 sendto_one(source_p, form_str(ERR_NOPRIVS),
312 me.name, source_p->name, "unkline");
313 return 0;
316 if (nick_is_valid(parv[1]))
318 target_p = find_named_person(parv[1]);
319 if (target_p == NULL)
321 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
322 form_str(ERR_NOSUCHNICK), parv[1]);
323 return 0;
325 if (MyConnect(target_p))
326 heal_nick(source_p, target_p);
327 else
328 sendto_one(target_p, ":%s ENCAP %s HEAL %s",
329 get_id(source_p, target_p),
330 target_p->servptr->name,
331 get_id(target_p, target_p));
333 else if (strchr(parv[1], '.'))
335 if (hurt_find_exact(parv[1]) == NULL)
337 sendto_one(source_p, ":%s NOTICE %s :Mask [%s] is not HURT",
338 me.name, source_p->name, parv[1]);
339 return 0;
341 hurt_remove(parv[1]);
342 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s removed HURT on %s",
343 get_oper_name(source_p), parv[1]);
344 sendto_server(NULL, NULL, NOCAPS, NOCAPS, ":%s ENCAP * HEAL %s",
345 source_p->name, parv[1]);
347 else
349 sendto_one(source_p,
350 ":%s NOTICE %s :[%s] is not a valid IP address/nick",
351 me.name, source_p->name, parv[1]);
352 return 0;
355 return 0;
357 /* }}} */
359 static int
360 me_heal(struct Client *client_p, struct Client *source_p,
361 int parc, const char **parv)
363 struct Client *target_p;
365 /* as noted in me_hurt(), if we don't get sufficient arguments...
366 * *poof*, it's dropped...
368 if (parc < 2)
369 return 0;
371 if (nick_is_valid(parv[1]))
373 target_p = find_person(parv[1]);
374 if (target_p != NULL && MyConnect(target_p))
375 heal_nick(source_p, target_p);
377 else if (strchr(parv[1], '.')) /* host or mask to remove ban for */
379 if (hurt_find_exact(parv[1]) == NULL)
380 return 0;
382 hurt_remove(parv[1]);
383 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s removed HURT on %s",
384 get_oper_name(source_p), parv[1]);
386 else
387 return 0;
389 return 0;
393 * Event handlers.
396 /* {{{ static void hurt_check_event() */
397 static void
398 hurt_check_event(void *arg)
400 dlink_node *ptr, *next_ptr;
401 struct Client *client_p;
403 DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_state.hurt_clients.head) {
404 client_p = ptr->data;
405 if (!EmptyString(client_p->user->suser))
407 dlinkDestroy(ptr, &hurt_state.hurt_clients);
408 sendto_one_notice(client_p, ":HURT restriction removed for this session");
409 USED_TARGETS(client_p) = 0;
410 client_p->localClient->target_last = CurrentTime; /* don't ask --nenolod */
412 else if (client_p->localClient->receiveM > hurt_state.cutoff)
413 exit_client(NULL, client_p, &me, hurt_state.exit_reason);
416 /* }}} */
418 /* {{{ static void hurt_expire_event() */
419 static void
420 hurt_expire_event(void *unused)
422 dlink_node *ptr, *next_ptr;
423 hurt_t *hurt;
425 DLINK_FOREACH_SAFE (ptr, next_ptr, hurt_confs.head)
427 hurt = (hurt_t *) ptr->data;
429 if (hurt->expire <= CurrentTime)
431 dlinkFindDestroy(hurt, &hurt_confs);
432 hurt_destroy(hurt);
436 /* }}} */
439 * Hook functions.
442 /* {{{ static void client_exit_hook() */
443 static void
444 client_exit_hook(hook_data_client_exit *data)
446 s_assert(data != NULL);
447 s_assert(data->target != NULL);
449 dlinkFindDestroy(data->target, &hurt_state.hurt_clients);
451 /* }}} */
453 /* {{{ static void new_local_user_hook() */
454 static void
455 new_local_user_hook(struct Client *source_p)
457 if (IsAnyDead(source_p) || !EmptyString(source_p->user->suser) ||
458 IsExemptKline(source_p))
459 return;
461 if (hurt_find(source_p->sockhost) || hurt_find(source_p->orighost))
463 USED_TARGETS(source_p) = 10;
464 source_p->localClient->target_last = CurrentTime + 600; /* don't ask --nenolod */
465 SetTGChange(source_p);
466 dlinkAddAlloc(source_p, &hurt_state.hurt_clients);
467 sendto_one_notice(source_p, ":You are hurt. Please identify to services immediately, or use /stats p for assistance.");
470 /* }}} */
472 /* {{{ static void doing_stats_hook() */
473 static void
474 doing_stats_hook(hook_data_int *hdata)
476 dlink_node *ptr;
477 hurt_t *hurt;
478 struct Client *source_p;
480 s_assert(hdata);
481 s_assert(hdata->client);
483 source_p = hdata->client;
484 if(hdata->arg2 != (int) 's')
485 return;
486 if((ConfigFileEntry.stats_k_oper_only == 2) && !IsOper(source_p))
487 return;
488 if ((ConfigFileEntry.stats_k_oper_only == 1) && !IsOper(source_p))
490 hurt = hurt_find(source_p->sockhost);
491 if (hurt != NULL)
493 sendto_one_numeric(source_p, RPL_STATSKLINE,
494 form_str(RPL_STATSKLINE), 's',
495 "*", hurt->ip, hurt->reason, "", "");
496 return;
499 hurt = hurt_find(source_p->orighost);
500 if (hurt != NULL)
502 sendto_one_numeric(source_p, RPL_STATSKLINE,
503 form_str(RPL_STATSKLINE), 's',
504 "*", hurt->ip, hurt->reason, "", "");
505 return;
507 return;
510 DLINK_FOREACH(ptr, hurt_confs.head)
512 hurt = (hurt_t *) ptr->data;
513 sendto_one_numeric(source_p, RPL_STATSKLINE,
514 form_str(RPL_STATSKLINE), 's',
515 "*", hurt->ip, hurt->reason, "", "");
518 /* }}} */
520 /* {{{ static void hurt_propagate()
522 * client_p - specific server to propagate HURT to, or NULL to propagate to all
523 * servers.
524 * source_p - source (oper who added the HURT)
525 * hurt - HURT to be propagated
527 static void
528 hurt_propagate(struct Client *client_p, struct Client *source_p, hurt_t *hurt)
530 if (client_p)
531 sendto_one(client_p,
532 ":%s ENCAP %s HURT %ld %s :%s",
533 source_p->name, client_p->name,
534 (long)(hurt->expire - CurrentTime),
535 hurt->ip, hurt->reason);
536 else
537 sendto_server(&me, NULL, NOCAPS, NOCAPS,
538 ":%s ENCAP * HURT %ld %s :%s",
539 source_p->name,
540 (long)(hurt->expire - CurrentTime),
541 hurt->ip, hurt->reason);
543 /* }}} */
545 /* {{{ static hurt_t *hurt_new() */
546 static hurt_t *
547 hurt_new(time_t expire, const char *ip, const char *reason)
549 hurt_t *hurt;
551 hurt = MyMalloc(sizeof(hurt_t));
553 DupString(hurt->ip, ip);
554 DupString(hurt->reason, reason);
555 hurt->expire = CurrentTime + expire;
557 return hurt;
559 /* }}} */
561 /* {{{ static void hurt_destroy() */
562 static void
563 hurt_destroy(void *hurt)
565 hurt_t *h;
567 if (!hurt)
568 return;
570 h = (hurt_t *) hurt;
571 MyFree((char *) h->ip);
572 MyFree((char *) h->reason);
573 MyFree(h);
575 /* }}} */
577 static void
578 hurt_add(hurt_t *hurt)
580 dlinkAddAlloc(hurt, &hurt_confs);
583 static hurt_t *
584 hurt_find_exact(const char *ip)
586 dlink_node *ptr;
587 hurt_t *hurt;
589 DLINK_FOREACH(ptr, hurt_confs.head)
591 hurt = (hurt_t *) ptr->data;
593 if (!strcasecmp(ip, hurt->ip))
594 return hurt;
597 return NULL;
600 static hurt_t *
601 hurt_find(const char *ip)
603 dlink_node *ptr;
604 hurt_t *hurt;
606 DLINK_FOREACH(ptr, hurt_confs.head)
608 hurt = (hurt_t *) ptr->data;
610 if (match(hurt->ip, ip))
611 return hurt;
614 return NULL;
617 static void
618 hurt_remove(const char *ip)
620 hurt_t *hurt = hurt_find_exact(ip);
622 dlinkFindDestroy(hurt, &hurt_confs);
623 hurt_destroy(hurt);
626 /* {{{ static int heal_nick() */
627 static int
628 heal_nick(struct Client *source_p, struct Client *target_p)
630 if (dlinkFindDestroy(target_p, &hurt_state.hurt_clients))
632 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s used HEAL on %s",
633 get_oper_name(source_p), get_client_name(target_p, HIDE_IP));
634 sendto_one_notice(target_p, ":HURT restriction temporarily removed by operator");
635 sendto_one_notice(source_p, ":HURT restriction on %s temporarily removed", target_p->name);
636 USED_TARGETS(target_p) = 0;
637 target_p->localClient->target_last = CurrentTime; /* don't ask --nenolod */
638 return 1;
640 else
642 sendto_one_notice(source_p, ":%s was not hurt", target_p->name);
643 return 0;
646 /* }}} */
649 * Anything else...
652 /* {{{ static int nick_is_valid() */
653 static int
654 nick_is_valid(const char *nick)
656 const char *s = nick;
658 for (; *s != '\0'; s++) {
659 if (!IsNickChar(*s))
660 return 0;
663 return 1;
665 /* }}} */
668 * vim: ts=8 sw=8 noet fdm=marker tw=80