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