2 * charybdis: an advanced Internet Relay Chat Daemon(ircd).
4 * Copyright (C) 2006 charybdis development team
16 #include "s_newconf.h"
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"
31 typedef struct _hurt_state
{
34 rb_dlink_list hurt_clients
;
36 time_t default_expire
;
37 const char *exit_reason
;
40 typedef struct _hurt
{
42 struct sockaddr
*saddr
;
50 static void mo_hurt(struct MsgBuf
*msgbuf_p
, struct Client
*, struct Client
*, int, const char **);
51 static void me_hurt(struct MsgBuf
*msgbuf_p
, struct Client
*, struct Client
*, int, const char **);
52 static void mo_heal(struct MsgBuf
*msgbuf_p
, struct Client
*, struct Client
*, int, const char **);
53 static void me_heal(struct MsgBuf
*msgbuf_p
, struct Client
*, struct Client
*, int, const char **);
55 static int modinit(void);
56 static void modfini(void);
58 static void client_exit_hook(hook_data_client_exit
*);
59 static void new_local_user_hook(struct Client
*);
60 static void doing_stats_hook(hook_data_int
*hdata
);
62 static void hurt_check_event(void *);
63 static void hurt_expire_event(void *);
65 static hurt_t
*hurt_new(time_t, const char *, const char *);
66 static void hurt_add(hurt_t
*);
67 static void hurt_propagate(struct Client
*, struct Client
*, hurt_t
*);
68 static hurt_t
*hurt_find(const char *ip
);
69 static hurt_t
*hurt_find_exact(const char *ip
);
70 static void hurt_remove(const char *ip
);
71 static void hurt_destroy(void *hurt
);
73 static void heal_nick(struct Client
*, struct Client
*);
77 /* {{{ State containers */
79 rb_dlink_list hurt_confs
= { NULL
, NULL
, 0 };
84 struct Message hurt_msgtab
= {
86 mg_ignore
, mg_ignore
, mg_ignore
,
87 mg_ignore
, {me_hurt
, 0}, {mo_hurt
, 3}
91 struct Message heal_msgtab
= {
93 mg_ignore
, mg_ignore
, mg_ignore
,
94 mg_ignore
, {me_heal
, 0}, {mo_heal
, 2}
99 /* {{{ Misc module stuff */
100 mapi_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
},
107 mapi_clist_av1 hurt_clist
[] = { &hurt_msgtab
, &heal_msgtab
, NULL
};
109 static const char hurt_desc
[] =
110 "Prevents \"hurt\" users from messaging anyone but operators or "
111 "services until they identify or are \"healed\"";
126 hurt_state_t hurt_state
= {
127 .cutoff
= HURT_CUTOFF
,
128 .default_expire
= HURT_DEFAULT_EXPIRE
,
129 .exit_reason
= HURT_EXIT_REASON
,
133 * Module constructor/destructor.
136 /* {{{ static int modinit() */
138 struct ev_entry
*hurt_expire_ev
= NULL
;
139 struct ev_entry
*hurt_check_ev
= NULL
;
144 /* set-up hurt_state. */
145 hurt_state
.start_time
= rb_current_time();
147 /* add our event handlers. */
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);
155 /* {{{ static void modfini() */
159 rb_dlink_node
*ptr
, *next_ptr
;
161 /* and delete our events. */
162 rb_event_delete(hurt_expire_ev
);
163 rb_event_delete(hurt_check_ev
);
165 RB_DLINK_FOREACH_SAFE (ptr
, next_ptr
, hurt_state
.hurt_clients
.head
)
167 rb_dlinkDestroy(ptr
, &hurt_state
.hurt_clients
);
176 /* {{{ static void mo_hurt()
178 * HURT [<expire>] <ip> <reason>
180 * parv[1] - expire or ip
181 * parv[2] - ip or reason
182 * parv[3] - reason or NULL
185 mo_hurt(struct MsgBuf
*msgbuf_p
, struct Client
*client_p
, struct Client
*source_p
,
186 int parc
, const char **parv
)
188 const char *ip
, *expire
, *reason
;
191 struct Client
*target_p
;
193 if (!IsOperK(source_p
)) {
194 sendto_one(source_p
, form_str(ERR_NOPRIVS
), me
.name
,
195 source_p
->name
, "kline");
200 expire
= NULL
, ip
= parv
[1], reason
= parv
[2];
202 expire
= parv
[1], ip
= parv
[2], reason
= parv
[3];
205 expire_time
= HURT_DEFAULT_EXPIRE
;
206 if (expire
&& (expire_time
= valid_temp_time(expire
)) < 1) {
207 sendto_one_notice(source_p
, ":Permanent HURTs are not supported");
210 if (EmptyString(reason
)) {
211 sendto_one_notice(source_p
, ":Empty HURT reasons are bad for business");
215 /* Is this a client? */
216 if (strchr(ip
, '.') == NULL
&& strchr(ip
, ':') == NULL
)
218 target_p
= find_named_person(ip
);
219 if (target_p
== NULL
)
221 sendto_one_numeric(source_p
, ERR_NOSUCHNICK
,
222 form_str(ERR_NOSUCHNICK
), ip
);
225 ip
= target_p
->orighost
;
229 if (!strncmp(ip
, "*@", 2))
231 if (strchr(ip
, '!') || strchr(ip
, '@'))
233 sendto_one_notice(source_p
, ":Invalid HURT mask [%s]",
239 if (hurt_find(ip
) != NULL
) {
240 sendto_one(source_p
, ":[%s] already HURT", ip
);
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.
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
);
252 hurt
= hurt_new(expire_time
, ip
, reason
);
254 hurt_propagate(NULL
, source_p
, hurt
);
258 /* {{{ static void me_hurt()
260 * [ENCAP mask] HURT <target> <expire> <ip> <reason>
267 me_hurt(struct MsgBuf
*msgbuf_p
, struct Client
*client_p
, struct Client
*source_p
,
268 int parc
, const char **parv
)
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
))
280 if ((expire_time
= atoi(parv
[1])) < 1)
282 if (hurt_find(parv
[2]) != NULL
)
284 if (EmptyString(parv
[3]))
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]);
295 /* {{{ static void mo_heal()
299 * parv[1] - nick or ip
302 mo_heal(struct MsgBuf
*msgbuf_p
, struct Client
*client_p
, struct Client
*source_p
,
303 int parc
, const char **parv
)
305 struct Client
*target_p
;
307 if (!IsOperUnkline(source_p
))
309 sendto_one(source_p
, form_str(ERR_NOPRIVS
),
310 me
.name
, source_p
->name
, "unkline");
314 if (clean_nick(parv
[1], 0))
316 target_p
= find_named_person(parv
[1]);
317 if (target_p
== NULL
)
319 sendto_one_numeric(source_p
, ERR_NOSUCHNICK
,
320 form_str(ERR_NOSUCHNICK
), parv
[1]);
323 if (MyConnect(target_p
))
324 heal_nick(source_p
, target_p
);
326 sendto_one(target_p
, ":%s ENCAP %s HEAL %s",
327 get_id(source_p
, target_p
),
328 target_p
->servptr
->name
,
329 get_id(target_p
, target_p
));
331 else if (strchr(parv
[1], '.'))
333 if (hurt_find_exact(parv
[1]) == NULL
)
335 sendto_one_notice(source_p
, ":Mask [%s] is not HURT", parv
[1]);
338 hurt_remove(parv
[1]);
339 sendto_realops_snomask(SNO_GENERAL
, L_ALL
, "%s removed HURT on %s",
340 get_oper_name(source_p
), parv
[1]);
341 sendto_server(NULL
, NULL
, NOCAPS
, NOCAPS
, ":%s ENCAP * HEAL %s",
342 source_p
->name
, parv
[1]);
346 sendto_one(source_p
, ":[%s] is not a valid IP address/nick", parv
[1]);
353 me_heal(struct MsgBuf
*msgbuf_p
, struct Client
*client_p
, struct Client
*source_p
,
354 int parc
, const char **parv
)
356 struct Client
*target_p
;
358 /* as noted in me_hurt(), if we don't get sufficient arguments...
359 * *poof*, it's dropped...
364 if (clean_nick(parv
[1], 0))
366 target_p
= find_person(parv
[1]);
367 if (target_p
!= NULL
&& MyConnect(target_p
))
368 heal_nick(source_p
, target_p
);
370 else if (strchr(parv
[1], '.')) /* host or mask to remove ban for */
372 if (hurt_find_exact(parv
[1]) == NULL
)
375 hurt_remove(parv
[1]);
376 sendto_realops_snomask(SNO_GENERAL
, L_ALL
, "%s removed HURT on %s",
377 get_oper_name(source_p
), parv
[1]);
385 /* {{{ static void hurt_check_event() */
387 hurt_check_event(void *arg
)
389 rb_dlink_node
*ptr
, *next_ptr
;
390 struct Client
*client_p
;
392 RB_DLINK_FOREACH_SAFE (ptr
, next_ptr
, hurt_state
.hurt_clients
.head
) {
393 client_p
= ptr
->data
;
394 if (!EmptyString(client_p
->user
->suser
))
396 rb_dlinkDestroy(ptr
, &hurt_state
.hurt_clients
);
397 sendto_one_notice(client_p
, ":HURT restriction removed for this session");
398 client_p
->localClient
->target_last
= rb_current_time(); /* don't ask --nenolod */
400 else if (client_p
->localClient
->receiveM
> hurt_state
.cutoff
)
401 exit_client(NULL
, client_p
, &me
, hurt_state
.exit_reason
);
406 /* {{{ static void hurt_expire_event() */
408 hurt_expire_event(void *unused
)
410 rb_dlink_node
*ptr
, *next_ptr
;
413 RB_DLINK_FOREACH_SAFE (ptr
, next_ptr
, hurt_confs
.head
)
415 hurt
= (hurt_t
*) ptr
->data
;
417 if (hurt
->expire
<= rb_current_time())
419 rb_dlinkFindDestroy(hurt
, &hurt_confs
);
430 /* {{{ static void client_exit_hook() */
432 client_exit_hook(hook_data_client_exit
*data
)
434 s_assert(data
!= NULL
);
435 s_assert(data
->target
!= NULL
);
437 rb_dlinkFindDestroy(data
->target
, &hurt_state
.hurt_clients
);
441 /* {{{ static void new_local_user_hook() */
443 new_local_user_hook(struct Client
*source_p
)
445 if (IsAnyDead(source_p
) || !EmptyString(source_p
->user
->suser
) ||
446 IsExemptKline(source_p
))
449 if (hurt_find(source_p
->sockhost
) || hurt_find(source_p
->orighost
))
451 source_p
->localClient
->target_last
= rb_current_time() + 600; /* don't ask --nenolod */
452 SetTGChange(source_p
);
453 rb_dlinkAddAlloc(source_p
, &hurt_state
.hurt_clients
);
454 sendto_one_notice(source_p
, ":You are hurt. Please identify to services immediately, or use /stats p for assistance.");
459 /* {{{ static void doing_stats_hook() */
461 doing_stats_hook(hook_data_int
*hdata
)
465 struct Client
*source_p
;
468 s_assert(hdata
->client
);
470 source_p
= hdata
->client
;
471 if(hdata
->arg2
!= (int) 's')
473 if((ConfigFileEntry
.stats_k_oper_only
== 2) && !IsOper(source_p
))
475 if ((ConfigFileEntry
.stats_k_oper_only
== 1) && !IsOper(source_p
))
477 hurt
= hurt_find(source_p
->sockhost
);
480 sendto_one_numeric(source_p
, RPL_STATSKLINE
,
481 form_str(RPL_STATSKLINE
), 's',
482 "*", hurt
->ip
, hurt
->reason
, "", "");
486 hurt
= hurt_find(source_p
->orighost
);
489 sendto_one_numeric(source_p
, RPL_STATSKLINE
,
490 form_str(RPL_STATSKLINE
), 's',
491 "*", hurt
->ip
, hurt
->reason
, "", "");
496 RB_DLINK_FOREACH(ptr
, hurt_confs
.head
)
498 hurt
= (hurt_t
*) ptr
->data
;
499 sendto_one_numeric(source_p
, RPL_STATSKLINE
,
500 form_str(RPL_STATSKLINE
), 's',
501 "*", hurt
->ip
, hurt
->reason
, "", "");
506 /* {{{ static void hurt_propagate()
508 * client_p - specific server to propagate HURT to, or NULL to propagate to all
510 * source_p - source (oper who added the HURT)
511 * hurt - HURT to be propagated
514 hurt_propagate(struct Client
*client_p
, struct Client
*source_p
, hurt_t
*hurt
)
518 ":%s ENCAP %s HURT %ld %s :%s",
519 source_p
->name
, client_p
->name
,
520 (long)(hurt
->expire
- rb_current_time()),
521 hurt
->ip
, hurt
->reason
);
523 sendto_server(&me
, NULL
, NOCAPS
, NOCAPS
,
524 ":%s ENCAP * HURT %ld %s :%s",
526 (long)(hurt
->expire
- rb_current_time()),
527 hurt
->ip
, hurt
->reason
);
531 /* {{{ static hurt_t *hurt_new() */
533 hurt_new(time_t expire
, const char *ip
, const char *reason
)
537 hurt
= rb_malloc(sizeof(hurt_t
));
539 hurt
->ip
= rb_strdup(ip
);
540 hurt
->reason
= rb_strdup(reason
);
541 hurt
->expire
= rb_current_time() + expire
;
547 /* {{{ static void hurt_destroy() */
549 hurt_destroy(void *hurt
)
564 hurt_add(hurt_t
*hurt
)
566 rb_dlinkAddAlloc(hurt
, &hurt_confs
);
570 hurt_find_exact(const char *ip
)
575 RB_DLINK_FOREACH(ptr
, hurt_confs
.head
)
577 hurt
= (hurt_t
*) ptr
->data
;
579 if (!strcasecmp(ip
, hurt
->ip
))
587 hurt_find(const char *ip
)
592 RB_DLINK_FOREACH(ptr
, hurt_confs
.head
)
594 hurt
= (hurt_t
*) ptr
->data
;
596 if (match(hurt
->ip
, ip
))
604 hurt_remove(const char *ip
)
606 hurt_t
*hurt
= hurt_find_exact(ip
);
608 rb_dlinkFindDestroy(hurt
, &hurt_confs
);
612 /* {{{ static void heal_nick() */
614 heal_nick(struct Client
*source_p
, struct Client
*target_p
)
616 if (rb_dlinkFindDestroy(target_p
, &hurt_state
.hurt_clients
))
618 sendto_realops_snomask(SNO_GENERAL
, L_ALL
, "%s used HEAL on %s",
619 get_oper_name(source_p
), get_client_name(target_p
, HIDE_IP
));
620 sendto_one_notice(target_p
, ":HURT restriction temporarily removed by operator");
621 sendto_one_notice(source_p
, ":HURT restriction on %s temporarily removed", target_p
->name
);
622 target_p
->localClient
->target_last
= rb_current_time(); /* don't ask --nenolod */
626 sendto_one_notice(source_p
, ":%s was not hurt", target_p
->name
);
632 * vim: ts=8 sw=8 noet fdm=marker tw=80