1 /*
2 * atheme-services: A collection of minimalist IRC services
3 * ptasks.c: Implementation of common protocol tasks.
4 *
5 * Copyright (c) 2005-2007 Atheme Project (http://www.atheme.org)
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
12 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
13 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
15 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
16 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
17 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
19 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
20 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21 * POSSIBILITY OF SUCH DAMAGE.
22 */
23
24 #include "atheme.h"
25 #include "uplink.h"
26 #include "pmodule.h"
27 #include "privs.h"
28
handle_info(user_t * u)29 void handle_info(user_t *u)
30 {
31 unsigned int i;
32
33 if (u == NULL)
34 return;
35 if (floodcheck(u, NULL))
36 return;
37
38 for (i = 0; infotext[i]; i++)
39 numeric_sts(me.me, 371, u, ":%s", infotext[i]);
40
41 numeric_sts(me.me, 374, u, ":End of /INFO list");
42 }
43
get_version_string(char * buf,size_t bufsize)44 int get_version_string(char *buf, size_t bufsize)
45 {
46 const crypt_impl_t *ci = crypt_get_default_provider();
47 #ifdef REPRODUCIBLE_BUILDS
48 return snprintf(buf, bufsize, "%s. %s %s :%s [%s] [enc:%s]",
49 PACKAGE_STRING, me.name, revision, get_conf_opts(), ircd->ircdname, ci->id);
50 #else
51 return snprintf(buf, bufsize, "%s. %s %s :%s [%s] [enc:%s] Build Date: %s",
52 PACKAGE_STRING, me.name, revision, get_conf_opts(), ircd->ircdname, ci->id, __DATE__);
53 #endif
54 }
55
handle_version(user_t * u)56 void handle_version(user_t *u)
57 {
58 if (u == NULL)
59 return;
60 if (floodcheck(u, NULL))
61 return;
62
63 char ver[BUFSIZE];
64 get_version_string(ver, sizeof(ver));
65 numeric_sts(me.me, 351, u, "%s", ver);
66 }
67
handle_admin(user_t * u)68 void handle_admin(user_t *u)
69 {
70
71 if (u == NULL)
72 return;
73
74 if (floodcheck(u, NULL))
75 return;
76
77 numeric_sts(me.me, 256, u, ":Administrative info about %s", me.name);
78 numeric_sts(me.me, 257, u, ":%s", me.adminname);
79 numeric_sts(me.me, 258, u, ":Atheme IRC Services (%s)", PACKAGE_STRING);
80 numeric_sts(me.me, 259, u, ":<%s>", me.adminemail);
81 }
82
dictionary_stats_cb(const char * line,void * privdata)83 static void dictionary_stats_cb(const char *line, void *privdata)
84 {
85 numeric_sts(me.me, 249, ((user_t *)privdata), "B :%s", line);
86 }
87
connection_stats_cb(const char * line,void * privdata)88 static void connection_stats_cb(const char *line, void *privdata)
89 {
90 numeric_sts(me.me, 249, ((user_t *)privdata), "F :%s", line);
91 }
92
handle_stats(user_t * u,char req)93 void handle_stats(user_t *u, char req)
94 {
95 kline_t *k;
96 xline_t *x;
97 qline_t *q;
98 mowgli_node_t *n;
99 uplink_t *uplink;
100 soper_t *soper;
101 int j;
102 char fl[10];
103
104 if (floodcheck(u, NULL))
105 return;
106 logcommand_user(NULL, u, CMDLOG_GET, "STATS: \2%c\2", req);
107
108 switch (req)
109 {
110 case 'B':
111 if (!has_priv_user(u, PRIV_SERVER_AUSPEX))
112 break;
113
114 mowgli_patricia_stats(userlist, dictionary_stats_cb, u);
115 mowgli_patricia_stats(chanlist, dictionary_stats_cb, u);
116 mowgli_patricia_stats(servlist, dictionary_stats_cb, u);
117 myentity_stats(dictionary_stats_cb, u);
118 mowgli_patricia_stats(nicklist, dictionary_stats_cb, u);
119 mowgli_patricia_stats(mclist, dictionary_stats_cb, u);
120 break;
121
122 case 'C':
123 case 'c':
124 if (!has_priv_user(u, PRIV_SERVER_AUSPEX))
125 break;
126
127 MOWGLI_ITER_FOREACH(n, uplinks.head)
128 {
129 uplink = (uplink_t *)n->data;
130 numeric_sts(me.me, 213, u, "C *@127.0.0.1 A %s %d uplink", uplink->name, uplink->port);
131 }
132 break;
133
134 case 'E':
135 case 'e':
136 if (!has_priv_user(u, PRIV_SERVER_AUSPEX))
137 break;
138
139 numeric_sts(me.me, 249, u, "E :Last event to run: %s", base_eventloop->last_ran);
140
141 numeric_sts(me.me, 249, u, "E :%-28s %s", "Operation", "Next Execution");
142 MOWGLI_ITER_FOREACH(n, base_eventloop->timer_list.head)
143 {
144 mowgli_eventloop_timer_t *timer = n->data;
145
146 if (timer->active)
147 numeric_sts(me.me, 249, u, "E :%-28s %4ld seconds (%ld)", timer->name, (long)(timer->deadline - mowgli_eventloop_get_time(base_eventloop)), (long)timer->frequency);
148 }
149
150 break;
151
152 case 'f':
153 case 'F':
154 if (!has_priv_user(u, PRIV_SERVER_AUSPEX))
155 break;
156
157 connection_stats(connection_stats_cb, u);
158 break;
159
160 case 'H':
161 case 'h':
162 if (!has_priv_user(u, PRIV_SERVER_AUSPEX))
163 break;
164
165 MOWGLI_ITER_FOREACH(n, uplinks.head)
166 {
167 uplink = (uplink_t *)n->data;
168 numeric_sts(me.me, 244, u, "H * * %s", uplink->name);
169 }
170 break;
171
172 case 'I':
173 case 'i':
174 numeric_sts(me.me, 215, u, "I * * *@%s 0 nonopered", me.name);
175 break;
176
177 #ifdef OBJECT_DEBUG
178 case 'j':
179 case 'J':
180 MOWGLI_ITER_FOREACH(n, object_list.head)
181 {
182 object_t *obj = n->data;
183 numeric_sts(me.me, 249, u, "J :object:%p refs:%d destructor:%p",
184 obj, obj->refcount, obj->destructor);
185 }
186 break;
187 #endif
188
189 case 'K':
190 case 'k':
191 if (!has_priv_user(u, PRIV_AKILL))
192 break;
193
194 MOWGLI_ITER_FOREACH(n, klnlist.head)
195 {
196 k = (kline_t *)n->data;
197
198 numeric_sts(me.me, 216, u, "%c %s * %s :%s",
199 k->duration ? 'k' : 'K',
200 k->host, k->user, k->reason);
201 }
202
203 break;
204
205 case 'o':
206 case 'O':
207 if (!has_priv_user(u, PRIV_VIEWPRIVS))
208 break;
209
210 MOWGLI_ITER_FOREACH(n, soperlist.head)
211 {
212 soper = n->data;
213
214 j = 0;
215 if (!(soper->flags & SOPER_CONF))
216 fl[j++] = 'D';
217 if (soper->operclass != NULL && soper->operclass->flags & OPERCLASS_NEEDOPER)
218 fl[j++] = 'O';
219 if (j == 0)
220 fl[j++] = '*';
221 fl[j] = '\0';
222 numeric_sts(me.me, 243, u, "O *@* %s %s %s %s",
223 fl, soper->myuser ? entity(soper->myuser)->name : soper->name,
224 soper->operclass ? soper->operclass->name : soper->classname, "-1");
225 }
226 break;
227
228 case 'T':
229 case 't':
230 if (!has_priv_user(u, PRIV_SERVER_AUSPEX))
231 break;
232
233 numeric_sts(me.me, 249, u, "T :event %7d", claro_state.event);
234 numeric_sts(me.me, 249, u, "T :node %7d", claro_state.node);
235 numeric_sts(me.me, 249, u, "T :connection %7d", connection_count());
236 numeric_sts(me.me, 249, u, "T :operclass %7d", cnt.operclass);
237 numeric_sts(me.me, 249, u, "T :soper %7d", cnt.soper);
238 numeric_sts(me.me, 249, u, "T :tld %7d", cnt.tld);
239 numeric_sts(me.me, 249, u, "T :kline %7d", cnt.kline);
240 numeric_sts(me.me, 249, u, "T :xline %7d", cnt.xline);
241 numeric_sts(me.me, 249, u, "T :server %7d", cnt.server);
242 numeric_sts(me.me, 249, u, "T :user %7d", cnt.user);
243 numeric_sts(me.me, 249, u, "T :chan %7d", cnt.chan);
244 numeric_sts(me.me, 249, u, "T :chanuser %7d", cnt.chanuser);
245 numeric_sts(me.me, 249, u, "T :myuser %7d", cnt.myuser);
246 numeric_sts(me.me, 249, u, "T :myuser_acc %7d", cnt.myuser_access);
247 numeric_sts(me.me, 249, u, "T :mynick %7d", cnt.mynick);
248 numeric_sts(me.me, 249, u, "T :myuser_nam %7d", cnt.myuser_name);
249 numeric_sts(me.me, 249, u, "T :mychan %7d", cnt.mychan);
250 numeric_sts(me.me, 249, u, "T :chanacs %7d", cnt.chanacs);
251
252 #ifdef OBJECT_DEBUG
253 numeric_sts(me.me, 249, u, "T :objects %7zu", MOWGLI_LIST_LENGTH(&object_list));
254 #endif
255
256 numeric_sts(me.me, 249, u, "T :bytes sent %7.2f%s", bytes(cnt.bout), sbytes(cnt.bout));
257 numeric_sts(me.me, 249, u, "T :bytes recv %7.2f%s", bytes(cnt.bin), sbytes(cnt.bin));
258 break;
259
260 case 'u':
261 numeric_sts(me.me, 242, u, ":Services Uptime: %s", timediff(CURRTIME - me.start));
262 break;
263
264 case 'V':
265 case 'v':
266 if (!has_priv_user(u, PRIV_SERVER_AUSPEX))
267 break;
268
269 /* we received this command from the uplink, so,
270 * hmm, it is not idle */
271 numeric_sts(me.me, 249, u, "V :%s (AutoConn.!*@*) Idle: 0 SendQ: ? Connected: %s",
272 curr_uplink->name,
273 timediff(CURRTIME - curr_uplink->conn->first_recv));
274 break;
275
276 case 'q':
277 case 'Q':
278 if (!has_priv_user(u, PRIV_MASS_AKILL))
279 break;
280
281 MOWGLI_ITER_FOREACH(n, qlnlist.head)
282 {
283 q = (qline_t *)n->data;
284
285 numeric_sts(me.me, 217, u, "%c %d %s :%s",
286 q->duration ? 'q' : 'Q',
287 0, /* hit count */
288 q->mask, q->reason);
289 }
290
291 break;
292
293 case 'x':
294 case 'X':
295 if (!has_priv_user(u, PRIV_MASS_AKILL))
296 break;
297
298 MOWGLI_ITER_FOREACH(n, xlnlist.head)
299 {
300 x = (xline_t *)n->data;
301
302 numeric_sts(me.me, 247, u, "%c %d %s :%s",
303 x->duration ? 'x' : 'X',
304 0, /* hit count */
305 x->realname, x->reason);
306 }
307
308 break;
309
310 case 'y':
311 case 'Y':
312 if (!has_priv_user(u, PRIV_SERVER_AUSPEX))
313 break;
314
315 numeric_sts(me.me, 218, u, "Y uplink 300 %u 1 %u 0.0 0.0 1",
316 me.recontime, config_options.uplink_sendq_limit);
317 break;
318
319 default:
320 break;
321 }
322
323 numeric_sts(me.me, 219, u, "%c :End of /STATS report", req);
324 }
325
handle_whois(user_t * u,const char * target)326 void handle_whois(user_t *u, const char *target)
327 {
328 user_t *t = user_find_named(target);
329
330 if (u == NULL)
331 return;
332 if (floodcheck(u, NULL))
333 return;
334
335 if (t != NULL)
336 {
337 numeric_sts(me.me, 311, u, "%s %s %s * :%s", t->nick, t->user, t->vhost, t->gecos);
338 /* channels purposely omitted */
339 numeric_sts(me.me, 312, u, "%s %s :%s", t->nick, t->server->name, t->server->desc);
340 if (t->flags & UF_AWAY)
341 numeric_sts(me.me, 301, u, "%s :Gone", t->nick);
342 if (is_ircop(t))
343 numeric_sts(me.me, 313, u, "%s :%s", t->nick, is_service(t) ? "is a Network Service" : "is an IRC Operator");
344 if (t->myuser && !(t->myuser->flags & MU_WAITAUTH))
345 numeric_sts(me.me, 330, u, "%s %s :is logged in as", t->nick, entity(t->myuser)->name);
346 }
347 else
348 numeric_sts(me.me, 401, u, "%s :No such nick", target);
349 numeric_sts(me.me, 318, u, "%s :End of WHOIS", target);
350 }
351
single_trace(user_t * u,user_t * t)352 static void single_trace(user_t *u, user_t *t)
353 {
354 const char *classname;
355
356 classname = t->flags & UF_ENFORCER ? "enforcer" : "service";
357 if (is_ircop(t))
358 numeric_sts(me.me, 204, u, "Oper %s %s[%s@%s] (255.255.255.255) 0 0", classname, t->nick, t->user, t->vhost);
359 else
360 numeric_sts(me.me, 205, u, "User %s %s[%s@%s] (255.255.255.255) 0 0", classname, t->nick, t->user, t->vhost);
361 }
362
363 /* target -> object to trace
364 * dest -> server to execute command on
365 */
handle_trace(user_t * u,const char * target,const char * dest)366 void handle_trace(user_t *u, const char *target, const char *dest)
367 {
368 user_t *t;
369 mowgli_node_t *n;
370 int nusers;
371
372 if (u == NULL)
373 return;
374 if (floodcheck(u, NULL))
375 return;
376 if (!match(target, me.name) || !irccasecmp(target, ME))
377 {
378 nusers = cnt.user;
379 MOWGLI_ITER_FOREACH(n, me.me->userlist.head)
380 {
381 t = n->data;
382 if (is_ircop(t))
383 single_trace(u, t);
384 nusers--;
385 }
386 if (has_priv_user(u, PRIV_SERVER_AUSPEX))
387 numeric_sts(me.me, 206, u, "Serv uplink %dS %dC %s *!*@%s 0", cnt.server - 1, nusers, me.actual, me.name);
388 target = me.name;
389 }
390 else
391 {
392 t = dest != NULL ? user_find_named(target) : user_find(target);
393 if (t != NULL && t->server == me.me)
394 {
395 single_trace(u, t);
396 target = t->nick;
397 }
398 }
399 numeric_sts(me.me, 262, u, "%s :End of TRACE", target);
400 }
401
handle_motd(user_t * u)402 void handle_motd(user_t *u)
403 {
404 FILE *f;
405 char lbuf[BUFSIZE];
406 char nebuf[BUFSIZE];
407 char cebuf[BUFSIZE];
408 char ubuf[BUFSIZE];
409 char cbuf[BUFSIZE];
410 char nbuf[BUFSIZE];
411
412 if (u == NULL)
413 return;
414 if (floodcheck(u, NULL))
415 return;
416
417 f = fopen(SYSCONFDIR "/atheme.motd", "r");
418 if (!f)
419 {
420 numeric_sts(me.me, 422, u, ":The MOTD file is unavailable.");
421 return;
422 }
423
424 snprintf(nebuf, BUFSIZE, "%d", nicksvs.expiry / 86400);
425 snprintf(cebuf, BUFSIZE, "%d", chansvs.expiry / 86400);
426 snprintf(ubuf, BUFSIZE, "%d", cnt.myuser);
427 snprintf(nbuf, BUFSIZE, "%d", nicksvs.no_nick_ownership ? 0 : cnt.mynick);
428 snprintf(cbuf, BUFSIZE, "%d", cnt.mychan);
429
430 numeric_sts(me.me, 375, u, ":- %s Message of the Day -", me.name);
431
432 while (fgets(lbuf, BUFSIZE, f))
433 {
434 strip(lbuf);
435
436 replace(lbuf, BUFSIZE, "&network&", me.netname);
437 replace(lbuf, BUFSIZE, "&nickexpiry&", nebuf);
438 replace(lbuf, BUFSIZE, "&chanexpiry&", cebuf);
439 replace(lbuf, BUFSIZE, "&myusers&", ubuf);
440 replace(lbuf, BUFSIZE, "&mynicks&", nbuf);
441 replace(lbuf, BUFSIZE, "&mychans&", cbuf);
442
443 numeric_sts(me.me, 372, u, ":- %s", lbuf);
444 }
445
446 numeric_sts(me.me, 376, u, ":End of the Message of the Day.");
447
448 fclose(f);
449 }
450
handle_away(user_t * u,const char * message)451 void handle_away(user_t *u, const char *message)
452 {
453 if (message == NULL || *message == '\0')
454 {
455 if (u->flags & UF_AWAY)
456 {
457 u->flags &= ~UF_AWAY;
458 hook_call_user_away(u);
459 }
460 }
461 else
462 {
463 if (!(u->flags & UF_AWAY))
464 {
465 u->flags |= UF_AWAY;
466 hook_call_user_away(u);
467 }
468 }
469 }
470
471 static void
handle_channel_message(sourceinfo_t * si,char * target,bool is_notice,char * message)472 handle_channel_message(sourceinfo_t *si, char *target, bool is_notice, char *message)
473 {
474 char *vec[3];
475 hook_cmessage_data_t cdata;
476 mowgli_node_t *n, *tn;
477 mowgli_list_t l = { NULL, NULL, 0 };
478 service_t *svs;
479
480 /* Call hook here */
481 cdata.u = si->su;
482 cdata.c = channel_find(target);
483 cdata.msg = message;
484
485 /* No such channel, ignore... */
486 if (cdata.c == NULL)
487 return;
488
489 hook_call_channel_message(&cdata);
490
491 vec[0] = target;
492 vec[1] = message;
493 vec[2] = NULL;
494
495 MOWGLI_ITER_FOREACH(n, cdata.c->members.head)
496 {
497 chanuser_t *cu = (chanuser_t *) n->data;
498
499 if (!is_internal_client(cu->user))
500 continue;
501
502 svs = service_find_nick(cu->user->nick);
503
504 if (svs == NULL)
505 continue;
506
507 if (svs->chanmsg == false)
508 continue;
509
510 mowgli_node_add(svs, mowgli_node_create(), &l);
511 }
512 /* Note: this assumes a fantasy command will not remove another
513 * service.
514 */
515 MOWGLI_ITER_FOREACH_SAFE(n, tn, l.head)
516 {
517 si->service = n->data;
518 if (is_notice)
519 si->service->notice_handler(si, 2, vec);
520 else
521 si->service->handler(si, 2, vec);
522 mowgli_node_delete(n, &l);
523 mowgli_node_free(n);
524 }
525 }
526
handle_message(sourceinfo_t * si,char * target,bool is_notice,char * message)527 void handle_message(sourceinfo_t *si, char *target, bool is_notice, char *message)
528 {
529 char *vec[3];
530 user_t *u, *target_u;
531 char *p;
532 char name2[NICKLEN];
533 char *sentinel;
534 mowgli_node_t *n;
535
536 /* message from server, ignore */
537 if (si->su == NULL)
538 return;
539
540 /* if this is a channel, handle it differently. */
541 if (*target == '#')
542 {
543 handle_channel_message(si, target, is_notice, message);
544 return;
545 }
546
547 target_u = NULL;
548 si->service = NULL;
549 p = strchr(target, '@');
550 if (p != NULL)
551 {
552 /* Make sure it's for us, not for a jupe -- jilles */
553 if (!irccasecmp(p + 1, me.name))
554 {
555 mowgli_strlcpy(name2, target, sizeof name2);
556 p = strchr(name2, '@');
557 if (p != NULL)
558 *p = '\0';
559 si->service = service_find_nick(name2);
560 if (si->service == NULL)
561 {
562 target_u = NULL;
563 MOWGLI_ITER_FOREACH(n, me.me->userlist.head)
564 {
565 u = n->data;
566 /* don't leak info about enforcers */
567 if (u->flags & UF_ENFORCER)
568 continue;
569 if (irccasecmp(u->user, name2))
570 continue;
571 if (target_u == NULL)
572 target_u = u;
573 else
574 {
575 numeric_sts(me.me, 407, si->su, "%s :Ambiguous recipient", target);
576 return;
577 }
578 }
579 if (target_u != NULL)
580 si->service = service_find_nick(target_u->nick);
581 }
582 }
583 }
584 else
585 {
586 target_u = user_find(target);
587 if (target_u != NULL)
588 si->service = service_find_nick(target_u->nick);
589 }
590
591 if (si->service == NULL)
592 {
593 if (!is_notice && target_u != NULL && target_u->server == me.me)
594 {
595 notice(target_u->nick, si->su->nick, "This is a registered nick enforcer, and not a real user.");
596 return;
597 }
598 if (!is_notice && (isalnum((unsigned char)target[0]) || strchr("[\\]^_`{|}~", target[0])))
599 {
600 /* If it's not a notice and looks like a nick or
601 * user@server, send back an error message */
602 if (strchr(target, '@') || !ircd->uses_uid || (!ircd->uses_p10 && !isdigit((unsigned char)target[0])))
603 numeric_sts(me.me, 401, si->su, "%s :No such nick", target);
604 else
605 numeric_sts(me.me, 401, si->su, "* :Target left IRC. Failed to deliver: [%.20s]", message);
606 }
607 return;
608 }
609
610 /* Run it through flood checks. Channel commands are checked
611 * separately.
612 */
613 if (si->service->me != NULL && floodcheck(si->su, si->service->me))
614 return;
615
616 if (!is_notice && config_options.secure && irccasecmp(target, si->service->disp))
617 {
618 notice(si->service->me->nick, si->su->nick, "For security reasons, \2/msg %s\2 has been disabled."
619 " Use \2/%s%s <command>\2 to send a command.",
620 si->service->me->nick, (ircd->uses_rcommand ? "" : "msg "), si->service->disp);
621 return;
622 }
623
624 /* strip OTR tagging (github issue #8) */
625 sentinel = strstr(message, "\x20\x09\x20\x20\x09\x09\x09\x09");
626 if (sentinel != NULL)
627 *sentinel = '\0';
628
629 vec[0] = target;
630 vec[1] = message;
631 vec[2] = NULL;
632 if (is_notice)
633 si->service->notice_handler(si, 2, vec);
634 else
635 si->service->handler(si, 2, vec);
636 }
637
handle_topic_from(sourceinfo_t * si,channel_t * c,const char * setter,time_t ts,const char * topic)638 void handle_topic_from(sourceinfo_t *si, channel_t *c, const char *setter, time_t ts, const char *topic)
639 {
640 hook_channel_topic_check_t hdata;
641
642 if (topic != NULL && topic[0] == '\0')
643 topic = NULL;
644 hdata.u = si->su;
645 hdata.s = si->s;
646 hdata.c = c;
647 hdata.setter = setter;
648 hdata.ts = ts;
649 hdata.topic = topic;
650 hdata.approved = 0;
651 if (topic != NULL ? c->topic == NULL || strcmp(topic, c->topic) : c->topic != NULL)
652 {
653 /* Only call the hook if the topic actually changed */
654 hook_call_channel_can_change_topic(&hdata);
655 }
656 if (hdata.approved == 0)
657 {
658 if (topic == hdata.topic || !strcmp(topic, hdata.topic))
659 /* Allowed, process the change further */
660 handle_topic(c, setter, ts, topic);
661 else
662 {
663 /* Allowed, but topic tweaked */
664 handle_topic(c, setter, ts, hdata.topic);
665 topic_sts(c, chansvs.me->me, setter, ts, ts, hdata.topic);
666 }
667 }
668 else
669 {
670 /* Not allowed, change it back */
671 if (c->topic != NULL)
672 topic_sts(c, chansvs.me->me, c->topic_setter, c->topicts, ts, c->topic);
673 else
674 {
675 /* Ick, there was no topic */
676 topic_sts(c, chansvs.me->me, chansvs.nick != NULL ? chansvs.nick : me.name, ts - 1, ts, "");
677 }
678 }
679 }
680
handle_topic(channel_t * c,const char * setter,time_t ts,const char * topic)681 void handle_topic(channel_t *c, const char *setter, time_t ts, const char *topic)
682 {
683 char newsetter[HOSTLEN], *p;
684
685 /* setter can be a nick, nick!user@host or server.
686 * strip off !user@host part if it's there
687 * (do we really want this?) */
688 mowgli_strlcpy(newsetter, setter, sizeof newsetter);
689 p = strchr(newsetter, '!');
690 if (p != NULL)
691 *p = '\0';
692 /* drop identical topics from servers or chanserv, and ones with
693 * identical topicts */
694 if (topic != NULL && c->topic != NULL && !strcmp(topic, c->topic)
695 && (strchr(newsetter, '.') ||
696 (chansvs.nick && !irccasecmp(newsetter, chansvs.nick)) ||
697 ts == c->topicts))
698 return;
699 if (c->topic != NULL)
700 free(c->topic);
701 if (c->topic_setter != NULL)
702 free(c->topic_setter);
703 if (topic != NULL && topic[0] != '\0')
704 {
705 c->topic = sstrdup(topic);
706 c->topic_setter = sstrdup(newsetter);
707 }
708 else
709 c->topic = c->topic_setter = NULL;
710 c->topicts = ts;
711
712 hook_call_channel_topic(c);
713 }
714
skip_kill_path(const char * reason)715 static const char *skip_kill_path(const char *reason)
716 {
717 const char *p;
718 bool have_dot = false;
719
720 p = reason;
721 while (*p != ' ')
722 {
723 if (strchr("<>()#&?%$,;", *p) || *p == '\0')
724 return reason;
725 if (*p == '!' || *p == '.')
726 have_dot = true;
727 p++;
728 }
729 return have_dot ? p + 1 : reason;
730 }
731
handle_kill(sourceinfo_t * si,const char * victim,const char * reason)732 void handle_kill(sourceinfo_t *si, const char *victim, const char *reason)
733 {
734 const char *source, *source1, *origreason;
735 char qreason[512];
736 user_t *u;
737 static time_t lastkill = 0;
738 static unsigned int killcount = 0;
739
740 source = get_oper_name(si);
741 if (si->su)
742 source1 = si->su->nick;
743 else if (si->s)
744 source1 = si->s->name;
745 else
746 source1 = me.name;
747 origreason = reason;
748 reason = skip_kill_path(reason);
749 if (reason[0] == '[' || !strncmp(reason, "Killed", 6))
750 snprintf(qreason, sizeof qreason, "%s", reason);
751 else if (origreason == reason)
752 snprintf(qreason, sizeof qreason, "Killed (%s (%s))",
753 source1, reason);
754 else
755 snprintf(qreason, sizeof qreason, "Killed (%s %s)",
756 source1, reason);
757
758 u = user_find(victim);
759 if (u == NULL)
760 slog(LG_DEBUG, "handle_kill(): %s killed unknown user %s (%s)", source, victim, reason);
761 else if (u->flags & UF_ENFORCER)
762 {
763 slog(LG_INFO, "handle_kill(): %s killed enforcer %s (%s)", source, u->nick, reason);
764 user_delete(u, qreason);
765 }
766 else if (u->server == me.me)
767 {
768 slog(LG_INFO, "handle_kill(): %s killed service %s (%s)", source, u->nick, reason);
769 if (lastkill != CURRTIME && killcount < 5 + me.me->users)
770 killcount = 0, lastkill = CURRTIME;
771 killcount++;
772 if (killcount < 5 + me.me->users)
773 reintroduce_user(u);
774 else
775 {
776 slog(LG_ERROR, "handle_kill(): services kill fight (\2%s\2 -> \2%s\2), shutting down", source, u->nick);
777 wallops(_("Services kill fight (%s -> %s), shutting down!"), source, u->nick);
778 runflags |= RF_SHUTDOWN;
779 }
780 }
781 else
782 {
783 slog(LG_DEBUG, "handle_kill(): %s killed user %s (%s)", source, u->nick, reason);
784 user_delete(u, qreason);
785 }
786 }
787
handle_server(sourceinfo_t * si,const char * name,const char * sid,int hops,const char * desc)788 server_t *handle_server(sourceinfo_t *si, const char *name, const char *sid,
789 int hops, const char *desc)
790 {
791 server_t *s = NULL;
792
793 if (si->s != NULL)
794 {
795 /* A server introducing another server */
796 s = server_add(name, hops, si->s, sid, desc);
797 }
798 else if (cnt.server == 1)
799 {
800 /* Our uplink introducing itself */
801 if (irccasecmp(name, curr_uplink->name))
802 slog(LG_ERROR, "handle_server(): uplink %s actually has name %s, continuing anyway", curr_uplink->name, name);
803 s = server_add(name, hops, me.me, sid, desc);
804 me.actual = s->name;
805 me.recvsvr = true;
806 }
807 else
808 slog(LG_ERROR, "handle_server(): unregistered/unknown server attempting to introduce another server %s", name);
809 return s;
810 }
811
handle_eob(server_t * s)812 void handle_eob(server_t *s)
813 {
814 mowgli_node_t *n;
815 server_t *s2;
816
817 if (s == NULL)
818 return;
819 if (s->flags & SF_EOB)
820 return;
821 slog(LG_NETWORK, "handle_eob(): end of burst from %s (%d users)",
822 s->name, s->users);
823 hook_call_server_eob(s);
824 s->flags |= SF_EOB;
825 /* convert P10 style EOB to ircnet/ratbox style */
826 MOWGLI_ITER_FOREACH(n, s->children.head)
827 {
828 s2 = n->data;
829 if (s2->flags & SF_EOB2)
830 handle_eob(s2);
831 }
832 }
833
834 /* Received a message from a user, check if they are flooding
835 * Returns true if the message should be ignored.
836 * u - user sending the message
837 * t - target of the message (to be used in warning the user, may be NULL
838 * to use the server name)
839 */
floodcheck(user_t * u,user_t * t)840 int floodcheck(user_t *u, user_t *t)
841 {
842 const char *from;
843 static time_t last_ignore_notice = 0;
844 unsigned int reduction;
845
846 if (t == NULL)
847 from = me.name;
848 else if (t->server == me.me)
849 from = t->nick;
850 else
851 {
852 slog(LG_ERROR, "BUG: tried to floodcheck message to non-service %s", t->nick);
853 return 0;
854 }
855
856 /* Check if we match a services ignore */
857 if (svsignore_find(u) && !has_priv_user(u, PRIV_ADMIN))
858 {
859 if (u->msgs == 0 && last_ignore_notice != CURRTIME)
860 {
861 /* tell them once per session, don't flood */
862 u->msgs = 1;
863 last_ignore_notice = CURRTIME;
864 notice(from, u->nick, _("You are on services ignore. You may not use any service."));
865 }
866 return 1;
867 }
868
869 if (config_options.flood_msgs)
870 {
871 /* check if they're being ignored */
872 if (u->offenses > 10)
873 {
874 if ((CURRTIME - u->lastmsg) > 30)
875 {
876 u->offenses -= 10;
877 u->lastmsg = CURRTIME;
878 u->msgs = 0;
879 }
880 else
881 return 1;
882 }
883
884 if (u->lastmsg != 0 && CURRTIME > u->lastmsg)
885 {
886 reduction = (CURRTIME - u->lastmsg) * (config_options.flood_msgs * FLOOD_MSGS_FACTOR / config_options.flood_time);
887 if (u->msgs > reduction)
888 u->msgs -= reduction;
889 else
890 u->msgs = 0;
891 }
892 u->lastmsg = CURRTIME;
893 u->msgs += FLOOD_MSGS_FACTOR;
894
895 if (u->msgs > config_options.flood_msgs * FLOOD_MSGS_FACTOR)
896 {
897 /* they're flooding. */
898 /* perhaps allowed to? -- jilles */
899 if (has_priv_user(u, PRIV_FLOOD))
900 {
901 u->msgs = 0;
902 return 0;
903 }
904 if (!u->offenses)
905 {
906 /* ignore them the first time */
907 u->lastmsg = CURRTIME;
908 u->msgs = 0;
909 u->offenses = 11;
910
911 notice(from, u->nick, _("You have triggered services flood protection."));
912 notice(from, u->nick, _("This is your first offense. You will be ignored for 30 seconds."));
913
914 slog(LG_INFO, "FLOOD: \2%s\2", u->nick);
915
916 return 1;
917 }
918
919 if (u->offenses == 1)
920 {
921 /* ignore them the second time */
922 u->lastmsg = CURRTIME;
923 u->msgs = 0;
924 u->offenses = 12;
925
926 notice(from, u->nick, _("You have triggered services flood protection."));
927 notice(from, u->nick, _("This is your last warning. You will be ignored for 30 seconds."));
928
929 slog(LG_INFO, "FLOOD: \2%s\2", u->nick);
930
931 return 1;
932 }
933
934 if (u->offenses == 2)
935 {
936 kline_t *k;
937
938 k = kline_add_user(u, "ten minute ban - flooding services", 600, chansvs.nick);
939
940 slog(LG_INFO, "FLOOD:KLINE: \2%s\2", u->nick);
941
942 return 1;
943 }
944 }
945 }
946
947 return 0;
948 }
949
command_add_flood(sourceinfo_t * si,unsigned int amount)950 void command_add_flood(sourceinfo_t *si, unsigned int amount)
951 {
952 if (si->su != NULL)
953 si->su->msgs += amount;
954 }
955
should_reg_umode(user_t * u)956 bool should_reg_umode(user_t *u)
957 {
958 mynick_t *mn;
959
960 if (nicksvs.me == NULL || nicksvs.no_nick_ownership ||
961 u->myuser == NULL || u->myuser->flags & MU_WAITAUTH)
962 return false;
963 mn = mynick_find(u->nick);
964 return mn != NULL && mn->owner == u->myuser;
965 }
966
967 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
968 * vim:ts=8
969 * vim:sw=8
970 * vim:noexpandtab
971 */
972