1 /*
2  * Copyright (c) 2003-2004 E. Will et al.
3  * Copyright (c) 2005-2006 Atheme Development Group
4  * Rights to this code are documented in doc/LICENSE.
5  *
6  * This file contains protocol support for bahamut-based ircd.
7  *
8  */
9 
10 #include "atheme.h"
11 #include "uplink.h"
12 #include "pmodule.h"
13 #include "protocol/bahamut.h"
14 
15 DECLARE_MODULE_V1("protocol/bahamut", true, _modinit, NULL, PACKAGE_STRING, VENDOR_STRING);
16 
17 /* *INDENT-OFF* */
18 
19 ircd_t Bahamut = {
20 	.ircdname = "Bahamut 1.8.x",
21 	.tldprefix = "$",
22 	.uses_uid = false,
23 	.uses_rcommand = false,
24 	.uses_owner = false,
25 	.uses_protect = false,
26 	.uses_halfops = false,
27 	.uses_p10 = false,
28 	.uses_vhost = false,
29 	.oper_only_modes = CMODE_OPERONLY,
30 	.owner_mode = 0,
31 	.protect_mode = 0,
32 	.halfops_mode = 0,
33 	.owner_mchar = "+",
34 	.protect_mchar = "+",
35 	.halfops_mchar = "+",
36 	.type = PROTOCOL_BAHAMUT,
37 	.perm_mode = 0,
38 	.oimmune_mode = 0,
39 	.ban_like_modes = "beI",
40 	.except_mchar = 'e',
41 	.invex_mchar = 'I',
42 	.flags = IRCD_HOLDNICK,
43 };
44 
45 struct cmode_ bahamut_mode_list[] = {
46   { 'i', CMODE_INVITE	},
47   { 'm', CMODE_MOD	},
48   { 'n', CMODE_NOEXT	},
49   { 'p', CMODE_PRIV	},
50   { 's', CMODE_SEC	},
51   { 't', CMODE_TOPIC	},
52   { 'c', CMODE_NOCOLOR	},
53   { 'M', CMODE_MODREG	},
54   { 'R', CMODE_REGONLY	},
55   { 'O', CMODE_OPERONLY },
56   { 'r', CMODE_CHANREG	},
57   { '\0', 0 }
58 };
59 
60 static bool check_jointhrottle(const char *, channel_t *, mychan_t *, user_t *, myuser_t *);
61 
62 struct extmode bahamut_ignore_mode_list[] = {
63   { 'j', check_jointhrottle },
64   { '\0', 0 }
65 };
66 
67 struct cmode_ bahamut_status_mode_list[] = {
68   { 'o', CSTATUS_OP    },
69   { 'v', CSTATUS_VOICE },
70   { '\0', 0 }
71 };
72 
73 struct cmode_ bahamut_prefix_mode_list[] = {
74   { '@', CSTATUS_OP    },
75   { '+', CSTATUS_VOICE },
76   { '\0', 0 }
77 };
78 
79 struct cmode_ bahamut_user_mode_list[] = {
80   { 'A', UF_ADMIN    },
81   { 'i', UF_INVIS    },
82   { 'o', UF_IRCOP    },
83   { '\0', 0 }
84 };
85 
86 /* *INDENT-ON* */
87 
check_jointhrottle(const char * value,channel_t * c,mychan_t * mc,user_t * u,myuser_t * mu)88 static bool check_jointhrottle(const char *value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
89 {
90 	const char *p, *arg2;
91 	int num, timeslice, v;
92 
93 	if (!strcmp(value, "0") && u == NULL && mu == NULL)
94 		return true;
95 	p = value, arg2 = NULL;
96 	while (*p != '\0')
97 	{
98 		if (*p == ':')
99 		{
100 			if (arg2 != NULL)
101 				return false;
102 			arg2 = p + 1;
103 		}
104 		else if (!isdigit((unsigned char)*p))
105 			return false;
106 		p++;
107 	}
108 	if (arg2 == NULL)
109 		return false;
110 	if (p - arg2 > 3 || arg2 - value - 1 > 3)
111 		return false;
112 	num = atoi(value);
113 	timeslice = atoi(arg2);
114 	if (num <= 0 || num > 127 || timeslice <= 0 || timeslice > 127)
115 		return false;
116 	if (u != NULL || mu != NULL)
117 	{
118 		/* the following are the same restrictions bahamut
119 		 * applies to local clients
120 		 */
121 		if (num < 2 || num > 20 || timeslice > 60)
122 			return false;
123 		v = (timeslice - 1) / 8 + 1;
124 		if (num < v)
125 			return false;
126 		v = num / 2;
127 		if (timeslice < v)
128 			return false;
129 	}
130 	return true;
131 }
132 
133 static bool use_nickipstr = false;
134 
135 /* login to our uplink */
bahamut_server_login(void)136 static unsigned int bahamut_server_login(void)
137 {
138 	int ret;
139 
140 	ret = sts("PASS %s :TS", curr_uplink->send_pass);
141 	if (ret == 1)
142 		return 1;
143 
144 	me.bursting = true;
145 
146 	sts("CAPAB SSJOIN NOQUIT BURST ZIP NICKIP TSMODE NICKIPSTR");
147 	sts("SERVER %s 1 :%s", me.name, me.desc);
148 	sts("SVINFO 5 3 0 :%lu", (unsigned long)CURRTIME);
149 
150 	return 0;
151 }
152 
153 /* introduce a client */
bahamut_introduce_nick(user_t * u)154 static void bahamut_introduce_nick(user_t *u)
155 {
156 	const char *umode = user_get_umodestr(u);
157 
158 	if (use_nickipstr)
159 		sts("NICK %s 1 %lu %s %s %s %s 0 0.0.0.0 :%s", u->nick, (unsigned long)u->ts, umode, u->user, u->host, me.name, u->gecos);
160 	else
161 		sts("NICK %s 1 %lu %s %s %s %s 0 0 :%s", u->nick, (unsigned long)u->ts, umode, u->user, u->host, me.name, u->gecos);
162 }
163 
164 /* invite a user to a channel */
bahamut_invite_sts(user_t * sender,user_t * target,channel_t * channel)165 static void bahamut_invite_sts(user_t *sender, user_t *target, channel_t *channel)
166 {
167 	sts(":%s INVITE %s %s", sender->nick, target->nick, channel->name);
168 }
169 
bahamut_quit_sts(user_t * u,const char * reason)170 static void bahamut_quit_sts(user_t *u, const char *reason)
171 {
172 	sts(":%s QUIT :%s", u->nick, reason);
173 }
174 
175 /* WALLOPS wrapper */
bahamut_wallops_sts(const char * text)176 static void bahamut_wallops_sts(const char *text)
177 {
178 	sts(":%s GLOBOPS :%s", me.name, text);
179 }
180 
181 /* join a channel */
bahamut_join_sts(channel_t * c,user_t * u,bool isnew,char * modes)182 static void bahamut_join_sts(channel_t *c, user_t *u, bool isnew, char *modes)
183 {
184 	if (isnew)
185 		sts(":%s SJOIN %lu %s %s :@%s", me.name, (unsigned long)c->ts,
186 				c->name, modes, u->nick);
187 	else
188 		sts(":%s SJOIN %lu %s + :@%s", me.name, (unsigned long)c->ts,
189 				c->name, u->nick);
190 }
191 
bahamut_chan_lowerts(channel_t * c,user_t * u)192 static void bahamut_chan_lowerts(channel_t *c, user_t *u)
193 {
194 	slog(LG_DEBUG, "bahamut_chan_lowerts(): lowering TS for %s to %lu",
195 			c->name, (unsigned long)c->ts);
196 	sts(":%s SJOIN %lu %s %s :@%s", me.name, (unsigned long)c->ts, c->name,
197 				channel_modes(c, true), u->nick);
198 	chanban_clear(c);
199 	/*handle_topic(c, "", 0, "");*/
200 	/* Don't destroy keeptopic info, I'll admit this is ugly -- jilles */
201 	if (c->topic != NULL)
202 		free(c->topic);
203 	if (c->topic_setter != NULL)
204 		free(c->topic_setter);
205 	c->topic = c->topic_setter = NULL;
206 	c->topicts = 0;
207 }
208 
209 /* kicks a user from a channel */
bahamut_kick(user_t * source,channel_t * c,user_t * u,const char * reason)210 static void bahamut_kick(user_t *source, channel_t *c, user_t *u, const char *reason)
211 {
212 	sts(":%s KICK %s %s :%s", source->nick, c->name, u->nick, reason);
213 
214 	chanuser_delete(c, u);
215 }
216 
217 /* PRIVMSG wrapper */
bahamut_msg(const char * from,const char * target,const char * fmt,...)218 static void bahamut_msg(const char *from, const char *target, const char *fmt, ...)
219 {
220 	va_list ap;
221 	char buf[BUFSIZE];
222 
223 	va_start(ap, fmt);
224 	vsnprintf(buf, BUFSIZE, fmt, ap);
225 	va_end(ap);
226 
227 	sts(":%s PRIVMSG %s :%s", from, target, buf);
228 }
229 
bahamut_msg_global_sts(user_t * from,const char * mask,const char * text)230 static void bahamut_msg_global_sts(user_t *from, const char *mask, const char *text)
231 {
232 	mowgli_node_t *n;
233 	tld_t *tld;
234 	if (!strcmp(mask, "*"))
235 	{
236 		MOWGLI_ITER_FOREACH(n, tldlist.head)
237 		{
238 			tld = n->data;
239 			sts(":%s PRIVMSG %s*%s :%s", from ? from->nick : me.name, ircd->tldprefix, tld->name, text);
240 		}
241 	}
242 	else
243 		sts(":%s PRIVMSG %s%s :%s", from ? from->nick : me.name, ircd->tldprefix, mask, text);
244 }
245 
246 /* NOTICE wrapper */
bahamut_notice_user_sts(user_t * from,user_t * target,const char * text)247 static void bahamut_notice_user_sts(user_t *from, user_t *target, const char *text)
248 {
249 	sts(":%s NOTICE %s :%s", from ? from->nick : me.name, target->nick, text);
250 }
251 
bahamut_notice_global_sts(user_t * from,const char * mask,const char * text)252 static void bahamut_notice_global_sts(user_t *from, const char *mask, const char *text)
253 {
254 	mowgli_node_t *n;
255 	tld_t *tld;
256 
257 	if (!strcmp(mask, "*"))
258 	{
259 		MOWGLI_ITER_FOREACH(n, tldlist.head)
260 		{
261 			tld = n->data;
262 			sts(":%s NOTICE %s*%s :%s", from ? from->nick : me.name, ircd->tldprefix, tld->name, text);
263 		}
264 	}
265 	else
266 		sts(":%s NOTICE %s%s :%s", from ? from->nick : me.name, ircd->tldprefix, mask, text);
267 }
268 
bahamut_notice_channel_sts(user_t * from,channel_t * target,const char * text)269 static void bahamut_notice_channel_sts(user_t *from, channel_t *target, const char *text)
270 {
271 	sts(":%s NOTICE %s :%s", from ? from->nick : me.name, target->name, text);
272 }
273 
bahamut_wallchops(user_t * sender,channel_t * channel,const char * message)274 static void bahamut_wallchops(user_t *sender, channel_t *channel, const char *message)
275 {
276 	sts(":%s NOTICE @%s :%s", sender->nick, channel->name, message);
277 }
278 
bahamut_numeric_sts(server_t * from,int numeric,user_t * target,const char * fmt,...)279 static void bahamut_numeric_sts(server_t *from, int numeric, user_t *target, const char *fmt, ...)
280 {
281 	va_list ap;
282 	char buf[BUFSIZE];
283 
284 	va_start(ap, fmt);
285 	vsnprintf(buf, BUFSIZE, fmt, ap);
286 	va_end(ap);
287 
288 	sts(":%s %d %s %s", from->name, numeric, target->nick, buf);
289 }
290 
291 /* KILL wrapper */
bahamut_kill_id_sts(user_t * killer,const char * id,const char * reason)292 static void bahamut_kill_id_sts(user_t *killer, const char *id, const char *reason)
293 {
294 	if (killer != NULL)
295 		sts(":%s KILL %s :%s!%s (%s)", killer->nick, id, killer->host, killer->nick, reason);
296 	else
297 		sts(":%s KILL %s :%s (%s)", me.name, id, me.name, reason);
298 }
299 
300 /* PART wrapper */
bahamut_part_sts(channel_t * c,user_t * u)301 static void bahamut_part_sts(channel_t *c, user_t *u)
302 {
303 	sts(":%s PART %s", u->nick, c->name);
304 }
305 
306 /* server-to-server KLINE wrapper */
bahamut_kline_sts(const char * server,const char * user,const char * host,long duration,const char * reason)307 static void bahamut_kline_sts(const char *server, const char *user, const char *host, long duration, const char *reason)
308 {
309 	service_t *svs;
310 
311 	svs = service_find("operserv");
312 	sts(":%s AKILL %s %s %ld %s %lu :%s", me.name, host, user, duration, svs != NULL ? svs->nick : me.name, (unsigned long)CURRTIME, reason);
313 }
314 
315 /* server-to-server UNKLINE wrapper */
bahamut_unkline_sts(const char * server,const char * user,const char * host)316 static void bahamut_unkline_sts(const char *server, const char *user, const char *host)
317 {
318 	sts(":%s RAKILL %s %s", me.name, host, user);
319 }
320 
321 /* topic wrapper */
bahamut_topic_sts(channel_t * c,user_t * source,const char * setter,time_t ts,time_t prevts,const char * topic)322 static void bahamut_topic_sts(channel_t *c, user_t *source, const char *setter, time_t ts, time_t prevts, const char *topic)
323 {
324 	return_if_fail(c != NULL);
325 
326 	sts(":%s TOPIC %s %s %lu :%s", source->nick, c->name, setter, (unsigned long)ts, topic);
327 }
328 
329 /* mode wrapper */
bahamut_mode_sts(char * sender,channel_t * target,char * modes)330 static void bahamut_mode_sts(char *sender, channel_t *target, char *modes)
331 {
332 	return_if_fail(sender != NULL);
333 	return_if_fail(target != NULL);
334 	return_if_fail(modes != NULL);
335 
336 	sts(":%s MODE %s %lu %s", sender, target->name, (unsigned long)target->ts, modes);
337 }
338 
339 /* ping wrapper */
bahamut_ping_sts(void)340 static void bahamut_ping_sts(void)
341 {
342 	sts("PING :%s", me.name);
343 }
344 
345 /* protocol-specific stuff to do on login */
bahamut_on_login(user_t * u,myuser_t * account,const char * wantedhost)346 static void bahamut_on_login(user_t *u, myuser_t *account, const char *wantedhost)
347 {
348 	return_if_fail(u != NULL);
349 
350 	/* Can only do this for nickserv, and can only record identified
351 	 * state if logged in to correct nick, sorry -- jilles
352 	 */
353 	if (should_reg_umode(u))
354 		sts(":%s SVSMODE %s +rd %lu", nicksvs.nick, u->nick, (unsigned long)CURRTIME);
355 }
356 
357 /* protocol-specific stuff to do on login */
bahamut_on_logout(user_t * u,const char * account)358 static bool bahamut_on_logout(user_t *u, const char *account)
359 {
360 	return_val_if_fail(u != NULL, false);
361 
362 	if (!nicksvs.no_nick_ownership)
363 		sts(":%s SVSMODE %s -r+d %lu", nicksvs.nick, u->nick, (unsigned long)CURRTIME);
364 	return false;
365 }
366 
bahamut_jupe(const char * server,const char * reason)367 static void bahamut_jupe(const char *server, const char *reason)
368 {
369 	server_t *s;
370 
371 	sts(":%s SQUIT %s :%s", me.name, server, reason);
372 	s = server_find(server);
373 	/* If the server is not directly connected to our uplink, we
374 	 * need to wait for its uplink to process the SQUIT :(
375 	 * -- jilles */
376 	if (s != NULL && s->uplink != NULL && s->uplink->uplink != me.me)
377 		s->flags |= SF_JUPE_PENDING;
378 	else
379 		sts(":%s SERVER %s 2 :%s", me.name, server, reason);
380 }
381 
bahamut_fnc_sts(user_t * source,user_t * u,const char * newnick,int type)382 static void bahamut_fnc_sts(user_t *source, user_t *u, const char *newnick, int type)
383 {
384 	sts(":%s SVSNICK %s %s %lu", source->nick, u->nick, newnick,
385 			(unsigned long)(CURRTIME - 60));
386 }
387 
bahamut_holdnick_sts(user_t * source,int duration,const char * nick,myuser_t * mu)388 static void bahamut_holdnick_sts(user_t *source, int duration, const char *nick, myuser_t *mu)
389 {
390 	sts(":%s SVSHOLD %s %d :Reserved by %s for nickname owner (%s)",
391 			source->nick, nick, duration, source->nick,
392 			mu ? entity(mu)->name : nick);
393 }
394 
m_topic(sourceinfo_t * si,int parc,char * parv[])395 static void m_topic(sourceinfo_t *si, int parc, char *parv[])
396 {
397 	channel_t *c = channel_find(parv[0]);
398 
399 	if (!c)
400 		return;
401 
402 	/* Our uplink is trying to change the topic during burst,
403 	 * and we have already set a topic. Assume our change won.
404 	 * -- jilles */
405 	if (si->s != NULL && si->s->uplink == me.me &&
406 			!(si->s->flags & SF_EOB) && c->topic != NULL)
407 		return;
408 
409 	handle_topic_from(si, c, parv[1], atol(parv[2]), parv[3]);
410 }
411 
m_ping(sourceinfo_t * si,int parc,char * parv[])412 static void m_ping(sourceinfo_t *si, int parc, char *parv[])
413 {
414 	/* reply to PING's */
415 	sts(":%s PONG %s %s", me.name, me.name, parv[0]);
416 }
417 
m_pong(sourceinfo_t * si,int parc,char * parv[])418 static void m_pong(sourceinfo_t *si, int parc, char *parv[])
419 {
420 	server_t *s;
421 
422 	/* someone replied to our PING */
423 	if (!parv[0])
424 		return;
425 	s = server_find(parv[0]);
426 	if (s == NULL)
427 		return;
428 	/* Postpone EOB for our uplink until topic burst is also done */
429 	if (s->uplink != me.me)
430 		handle_eob(s);
431 
432 	if (s != si->s)
433 		return;
434 
435 	me.uplinkpong = CURRTIME;
436 
437 	/* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
438 }
439 
m_burst(sourceinfo_t * si,int parc,char * parv[])440 static void m_burst(sourceinfo_t *si, int parc, char *parv[])
441 {
442 	server_t *s;
443 
444 	/* Ignore "BURST" at start of burst */
445 	if (parc != 1)
446 		return;
447 
448 	s = server_find(me.actual);
449 	if (s != NULL)
450 		handle_eob(s);
451 
452 	if (me.bursting)
453 	{
454 #ifdef HAVE_GETTIMEOFDAY
455 		e_time(burstime, &burstime);
456 
457 		slog(LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms(&burstime) > 1000) ? (tv2ms(&burstime) / 1000) : tv2ms(&burstime), (tv2ms(&burstime) > 1000) ? "s" : "ms");
458 
459 		wallops("Finished synchronizing with network in %d %s.", (tv2ms(&burstime) > 1000) ? (tv2ms(&burstime) / 1000) : tv2ms(&burstime), (tv2ms(&burstime) > 1000) ? "s" : "ms");
460 #else
461 		slog(LG_INFO, "m_pong(): finished synching with uplink");
462 		wallops("Finished synchronizing with network.");
463 #endif
464 
465 		me.bursting = false;
466 	}
467 }
468 
m_privmsg(sourceinfo_t * si,int parc,char * parv[])469 static void m_privmsg(sourceinfo_t *si, int parc, char *parv[])
470 {
471 	if (parc != 2)
472 		return;
473 
474 	handle_message(si, parv[0], false, parv[1]);
475 }
476 
m_notice(sourceinfo_t * si,int parc,char * parv[])477 static void m_notice(sourceinfo_t *si, int parc, char *parv[])
478 {
479 	if (parc != 2)
480 		return;
481 
482 	handle_message(si, parv[0], true, parv[1]);
483 }
484 
m_sjoin(sourceinfo_t * si,int parc,char * parv[])485 static void m_sjoin(sourceinfo_t *si, int parc, char *parv[])
486 {
487 	/*
488 	 *  -> :proteus.malkier.net SJOIN 1073516550 #shrike +tn :@sycobuny @+rakaur
489 	 *	also:
490 	 *  -> :nenolod_ SJOIN 1117334567 #chat
491 	 */
492 
493 	channel_t *c;
494 	bool keep_new_modes = true;
495 	unsigned int userc;
496 	char *userv[256];
497 	unsigned int i;
498 	time_t ts;
499 	char *p;
500 
501 	if (parc >= 4 && si->s != NULL)
502 	{
503 		/* :origin SJOIN ts chan modestr [key or limits] :users */
504 		c = channel_find(parv[1]);
505 		ts = atol(parv[0]);
506 
507 		if (!c)
508 		{
509 			slog(LG_DEBUG, "m_sjoin(): new channel: %s", parv[1]);
510 			c = channel_add(parv[1], ts, si->s);
511 		}
512 
513 		if (ts == 0 || c->ts == 0)
514 		{
515 			if (c->ts != 0)
516 				slog(LG_INFO, "m_sjoin(): server %s changing TS on %s from %lu to 0", si->s->name, c->name, (unsigned long)c->ts);
517 			c->ts = 0;
518 			hook_call_channel_tschange(c);
519 		}
520 		else if (ts < c->ts)
521 		{
522 			chanuser_t *cu;
523 			mowgli_node_t *n;
524 
525 			/* the TS changed.  a TS change requires the following things
526 			 * to be done to the channel:  reset all modes to nothing, remove
527 			 * all status modes on known users on the channel (including ours),
528 			 * and set the new TS.
529 			 * also clear all bans and the topic
530 			 */
531 
532 			clear_simple_modes(c);
533 			chanban_clear(c);
534 			handle_topic_from(si, c, "", 0, "");
535 
536 			MOWGLI_ITER_FOREACH(n, c->members.head)
537 			{
538 				cu = (chanuser_t *)n->data;
539 				if (cu->user->server == me.me)
540 				{
541 					/* it's a service, reop */
542 					sts(":%s PART %s :Reop", cu->user->nick, c->name);
543 					sts(":%s SJOIN %lu %s + :@%s", me.name, (unsigned long)ts, c->name, cu->user->nick);
544 					cu->modes = CSTATUS_OP;
545 				}
546 				else
547 					cu->modes = 0;
548 			}
549 
550 			slog(LG_DEBUG, "m_sjoin(): TS changed for %s (%lu -> %lu)", c->name, (unsigned long)c->ts, (unsigned long)ts);
551 
552 			c->ts = ts;
553 			hook_call_channel_tschange(c);
554 		}
555 		else if (ts > c->ts)
556 			keep_new_modes = false;
557 
558 		if (keep_new_modes)
559 			channel_mode(NULL, c, parc - 3, parv + 2);
560 
561 		userc = sjtoken(parv[parc - 1], ' ', userv);
562 
563 		if (keep_new_modes)
564 			for (i = 0; i < userc; i++)
565 				chanuser_add(c, userv[i]);
566 		else
567 			for (i = 0; i < userc; i++)
568 			{
569 				p = userv[i];
570 				while (*p == '@' || *p == '%' || *p == '+')
571 					p++;
572 				chanuser_add(c, p);
573 			}
574 
575 		if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
576 			channel_delete(c);
577 	}
578 	else if (parc >= 2 && si->su != NULL)
579 	{
580 		c = channel_find(parv[1]);
581 		ts = atol(parv[0]);
582 
583 		if (c == NULL || ts < c->ts)
584 		{
585 			/* just request a resynch, this will include
586 			 * the user joining -- jilles */
587 			slog(LG_DEBUG, "m_sjoin(): requesting resynch for %s",
588 					parv[1]);
589 			sts("RESYNCH %s", parv[1]);
590 			return;
591 		}
592 
593 		chanuser_add(c, CLIENT_NAME(si->su));
594 	}
595 	else
596 	{
597 		slog(LG_DEBUG, "m_sjoin(): invalid source/parameters: origin %s parc %d",
598 				si->su != NULL ? si->su->nick : (si->s != NULL ? si->s->name : "<none>"), parc);
599 	}
600 }
601 
m_part(sourceinfo_t * si,int parc,char * parv[])602 static void m_part(sourceinfo_t *si, int parc, char *parv[])
603 {
604 	int chanc;
605 	char *chanv[256];
606 	int i;
607 
608 	chanc = sjtoken(parv[0], ',', chanv);
609 	for (i = 0; i < chanc; i++)
610 	{
611 		slog(LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
612 
613 		chanuser_delete(channel_find(chanv[i]), si->su);
614 	}
615 }
616 
m_nick(sourceinfo_t * si,int parc,char * parv[])617 static void m_nick(sourceinfo_t *si, int parc, char *parv[])
618 {
619 	server_t *s;
620 	user_t *u;
621 	char ipstring[64];
622 	bool realchange;
623 
624 	/* -> NICK jilles 1 1136143909 +oi ~jilles 192.168.1.5 jaguar.test 0 3232235781 :Jilles Tjoelker */
625 	if (parc == 10)
626 	{
627 		s = server_find(parv[6]);
628 		if (!s)
629 		{
630 			slog(LG_DEBUG, "m_nick(): new user on nonexistant server: %s", parv[6]);
631 			return;
632 		}
633 
634 		slog(LG_DEBUG, "m_nick(): new user on `%s': %s", s->name, parv[0]);
635 
636 		if (!use_nickipstr)
637 		{
638 			struct in_addr ip;
639 
640 			ip.s_addr = ntohl(strtoul(parv[8], NULL, 10));
641 			ipstring[0] = '\0';
642 			if (!inet_ntop(AF_INET, &ip, ipstring, sizeof ipstring))
643 				ipstring[0] = '\0';
644 		}
645 		else
646 			mowgli_strlcpy(ipstring, parv[8], sizeof ipstring);
647 
648 		u = user_add(parv[0], parv[4], parv[5], NULL, ipstring, NULL, parv[9], s, atoi(parv[2]));
649 		if (u == NULL)
650 			return;
651 
652 		user_mode(u, parv[3]);
653 
654 		/* Ok, we have the user ready to go.
655 		 * Here's the deal -- if the user's SVID is before
656 		 * the start time, and not 0, then check to see
657 		 * if it's a registered account or not.
658 		 *
659 		 * If it IS registered, deal with that accordingly,
660 		 * via handle_burstlogin(). --nenolod
661 		 */
662 		/* Changed to just check umode +r for now -- jilles */
663 		/* This is ok because this ircd clears +r on nick changes
664 		 * -- jilles */
665 		if (strchr(parv[3], 'r'))
666 			handle_burstlogin(u, NULL, 0);
667 
668 		handle_nickchange(u);
669 	}
670 
671 	/* if it's only 2 then it's a nickname change */
672 	else if (parc == 2)
673 	{
674 		if (!si->su)
675 		{
676 			slog(LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
677 			return;
678 		}
679 
680 		slog(LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
681 
682 		realchange = irccasecmp(si->su->nick, parv[0]);
683 
684 		if (user_changenick(si->su, parv[0], atoi(parv[1])))
685 			return;
686 
687 		/* fix up +r if necessary -- jilles */
688 		if (realchange && should_reg_umode(si->su))
689 			/* changed nick to registered one, reset +r */
690 			sts(":%s SVSMODE %s +rd %lu", nicksvs.nick, parv[0], (unsigned long)CURRTIME);
691 
692 		handle_nickchange(si->su);
693 	}
694 	else
695 	{
696 		int i;
697 		slog(LG_DEBUG, "m_nick(): got NICK with wrong number of params");
698 
699 		for (i = 0; i < parc; i++)
700 			slog(LG_DEBUG, "m_nick():   parv[%d] = %s", i, parv[i]);
701 	}
702 }
703 
m_quit(sourceinfo_t * si,int parc,char * parv[])704 static void m_quit(sourceinfo_t *si, int parc, char *parv[])
705 {
706 	slog(LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
707 
708 	/* user_delete() takes care of removing channels and so forth */
709 	user_delete(si->su, parv[0]);
710 }
711 
m_mode(sourceinfo_t * si,int parc,char * parv[])712 static void m_mode(sourceinfo_t *si, int parc, char *parv[])
713 {
714 	channel_t *c;
715 
716 	if (*parv[0] == '#')
717 	{
718 		c = channel_find(parv[0]);
719 		if (c == NULL)
720 		{
721 			slog(LG_DEBUG, "m_mode(): unknown channel %s", parv[0]);
722 			return;
723 		}
724 		if (atol(parv[1]) > c->ts)
725 			return;
726 		channel_mode(NULL, channel_find(parv[0]), parc - 2, &parv[2]);
727 	}
728 	else
729 		user_mode(user_find(parv[0]), parv[1]);
730 }
731 
m_kick(sourceinfo_t * si,int parc,char * parv[])732 static void m_kick(sourceinfo_t *si, int parc, char *parv[])
733 {
734 	user_t *u = user_find(parv[1]);
735 	channel_t *c = channel_find(parv[0]);
736 
737 	/* -> :rakaur KICK #shrike rintaun :test */
738 	slog(LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
739 
740 	if (!u)
741 	{
742 		slog(LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
743 		return;
744 	}
745 
746 	if (!c)
747 	{
748 		slog(LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
749 		return;
750 	}
751 
752 	if (!chanuser_find(c, u))
753 	{
754 		slog(LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
755 		return;
756 	}
757 
758 	chanuser_delete(c, u);
759 
760 	/* if they kicked us, let's rejoin */
761 	if (is_internal_client(u))
762 	{
763 		slog(LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
764 		join(parv[0], u->nick);
765 	}
766 }
767 
m_kill(sourceinfo_t * si,int parc,char * parv[])768 static void m_kill(sourceinfo_t *si, int parc, char *parv[])
769 {
770 	handle_kill(si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
771 }
772 
m_squit(sourceinfo_t * si,int parc,char * parv[])773 static void m_squit(sourceinfo_t *si, int parc, char *parv[])
774 {
775 	slog(LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
776 	server_delete(parv[0]);
777 }
778 
m_server(sourceinfo_t * si,int parc,char * parv[])779 static void m_server(sourceinfo_t *si, int parc, char *parv[])
780 {
781 	server_t *s;
782 
783 	slog(LG_DEBUG, "m_server(): new server: %s", parv[0]);
784 	s = handle_server(si, parv[0], NULL, atoi(parv[1]), parv[2]);
785 
786 	if (s != NULL && s->uplink != me.me)
787 	{
788 		/* elicit PONG for EOB detection; pinging uplink is
789 		 * already done elsewhere -- jilles
790 		 */
791 		sts(":%s PING %s %s", me.name, me.name, s->name);
792 	}
793 }
794 
m_stats(sourceinfo_t * si,int parc,char * parv[])795 static void m_stats(sourceinfo_t *si, int parc, char *parv[])
796 {
797 	handle_stats(si->su, parv[0][0]);
798 }
799 
m_admin(sourceinfo_t * si,int parc,char * parv[])800 static void m_admin(sourceinfo_t *si, int parc, char *parv[])
801 {
802 	handle_admin(si->su);
803 }
804 
m_version(sourceinfo_t * si,int parc,char * parv[])805 static void m_version(sourceinfo_t *si, int parc, char *parv[])
806 {
807 	handle_version(si->su);
808 }
809 
m_info(sourceinfo_t * si,int parc,char * parv[])810 static void m_info(sourceinfo_t *si, int parc, char *parv[])
811 {
812 	handle_info(si->su);
813 }
814 
m_whois(sourceinfo_t * si,int parc,char * parv[])815 static void m_whois(sourceinfo_t *si, int parc, char *parv[])
816 {
817 	handle_whois(si->su, parv[1]);
818 }
819 
m_trace(sourceinfo_t * si,int parc,char * parv[])820 static void m_trace(sourceinfo_t *si, int parc, char *parv[])
821 {
822 	handle_trace(si->su, parv[0], parc >= 2 ? parv[1] : NULL);
823 }
824 
m_away(sourceinfo_t * si,int parc,char * parv[])825 static void m_away(sourceinfo_t *si, int parc, char *parv[])
826 {
827 	handle_away(si->su, parc >= 1 ? parv[0] : NULL);
828 }
829 
m_join(sourceinfo_t * si,int parc,char * parv[])830 static void m_join(sourceinfo_t *si, int parc, char *parv[])
831 {
832 	chanuser_t *cu;
833 	mowgli_node_t *n, *tn;
834 
835 	/* JOIN 0 is really a part from all channels */
836 	if (parv[0][0] == '0')
837 	{
838 		MOWGLI_ITER_FOREACH_SAFE(n, tn, si->su->channels.head)
839 		{
840 			cu = (chanuser_t *)n->data;
841 			chanuser_delete(cu->chan, si->su);
842 		}
843 	}
844 }
845 
m_pass(sourceinfo_t * si,int parc,char * parv[])846 static void m_pass(sourceinfo_t *si, int parc, char *parv[])
847 {
848 	if (strcmp(curr_uplink->receive_pass, parv[0]))
849 	{
850 		slog(LG_INFO, "m_pass(): password mismatch from uplink; aborting");
851 		runflags |= RF_SHUTDOWN;
852 	}
853 }
854 
m_error(sourceinfo_t * si,int parc,char * parv[])855 static void m_error(sourceinfo_t *si, int parc, char *parv[])
856 {
857 	slog(LG_INFO, "m_error(): error from server: %s", parv[0]);
858 }
859 
m_motd(sourceinfo_t * si,int parc,char * parv[])860 static void m_motd(sourceinfo_t *si, int parc, char *parv[])
861 {
862 	handle_motd(si->su);
863 }
864 
m_capab(sourceinfo_t * si,int parc,char * parv[])865 static void m_capab(sourceinfo_t *si, int parc, char *parv[])
866 {
867 	int i;
868 
869 	use_nickipstr = false;
870 
871 	for (i = 0; i < parc; i++)
872 	{
873 		if (!irccasecmp(parv[i], "NICKIPSTR"))
874 		{
875 			slog(LG_DEBUG, "m_capab(): uplink supports string-based IP addresses, enabling support.");
876 			use_nickipstr = true;
877 		}
878 	}
879 
880 	/* now burst services with NICKIP set correctly. */
881 	services_init();
882 }
883 
nick_group(hook_user_req_t * hdata)884 static void nick_group(hook_user_req_t *hdata)
885 {
886 	user_t *u;
887 
888 	u = hdata->si->su != NULL && !irccasecmp(hdata->si->su->nick, hdata->mn->nick) ? hdata->si->su : user_find_named(hdata->mn->nick);
889 	if (u != NULL && should_reg_umode(u))
890 		sts(":%s SVSMODE %s +rd %lu", nicksvs.nick, u->nick, (unsigned long)CURRTIME);
891 }
892 
nick_ungroup(hook_user_req_t * hdata)893 static void nick_ungroup(hook_user_req_t *hdata)
894 {
895 	user_t *u;
896 
897 	u = hdata->si->su != NULL && !irccasecmp(hdata->si->su->nick, hdata->mn->nick) ? hdata->si->su : user_find_named(hdata->mn->nick);
898 	if (u != NULL && !nicksvs.no_nick_ownership)
899 		sts(":%s SVSMODE %s -r+d %lu", nicksvs.nick, u->nick, (unsigned long)CURRTIME);
900 }
901 
_modinit(module_t * m)902 void _modinit(module_t * m)
903 {
904 	MODULE_TRY_REQUEST_DEPENDENCY(m, "transport/rfc1459");
905 
906 	/* Symbol relocation voodoo. */
907 	server_login = &bahamut_server_login;
908 	introduce_nick = &bahamut_introduce_nick;
909 	quit_sts = &bahamut_quit_sts;
910 	wallops_sts = &bahamut_wallops_sts;
911 	join_sts = &bahamut_join_sts;
912 	chan_lowerts = &bahamut_chan_lowerts;
913 	kick = &bahamut_kick;
914 	msg = &bahamut_msg;
915 	msg_global_sts = &bahamut_msg_global_sts;
916 	notice_user_sts = &bahamut_notice_user_sts;
917 	notice_global_sts = &bahamut_notice_global_sts;
918 	notice_channel_sts = &bahamut_notice_channel_sts;
919 	wallchops = &bahamut_wallchops;
920 	numeric_sts = &bahamut_numeric_sts;
921 	kill_id_sts = &bahamut_kill_id_sts;
922 	part_sts = &bahamut_part_sts;
923 	kline_sts = &bahamut_kline_sts;
924 	unkline_sts = &bahamut_unkline_sts;
925 	topic_sts = &bahamut_topic_sts;
926 	mode_sts = &bahamut_mode_sts;
927 	ping_sts = &bahamut_ping_sts;
928 	ircd_on_login = &bahamut_on_login;
929 	ircd_on_logout = &bahamut_on_logout;
930 	jupe = &bahamut_jupe;
931 	fnc_sts = &bahamut_fnc_sts;
932 	invite_sts = &bahamut_invite_sts;
933 	holdnick_sts = &bahamut_holdnick_sts;
934 
935 	mode_list = bahamut_mode_list;
936 	ignore_mode_list = bahamut_ignore_mode_list;
937 	status_mode_list = bahamut_status_mode_list;
938 	prefix_mode_list = bahamut_prefix_mode_list;
939 	user_mode_list = bahamut_user_mode_list;
940 	ignore_mode_list_size = ARRAY_SIZE(bahamut_ignore_mode_list);
941 
942 	ircd = &Bahamut;
943 
944 	pcommand_add("CAPAB", m_capab, 0, MSRC_UNREG);
945 	pcommand_add("PING", m_ping, 1, MSRC_USER | MSRC_SERVER);
946 	pcommand_add("PONG", m_pong, 1, MSRC_SERVER);
947 	pcommand_add("PRIVMSG", m_privmsg, 2, MSRC_USER);
948 	pcommand_add("NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER);
949 	pcommand_add("SJOIN", m_sjoin, 2, MSRC_USER | MSRC_SERVER);
950 	pcommand_add("PART", m_part, 1, MSRC_USER);
951 	pcommand_add("NICK", m_nick, 2, MSRC_USER | MSRC_SERVER);
952 	pcommand_add("QUIT", m_quit, 1, MSRC_USER);
953 	pcommand_add("MODE", m_mode, 2, MSRC_USER | MSRC_SERVER);
954 	pcommand_add("KICK", m_kick, 2, MSRC_USER | MSRC_SERVER);
955 	pcommand_add("KILL", m_kill, 1, MSRC_USER | MSRC_SERVER);
956 	pcommand_add("SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER);
957 	pcommand_add("SERVER", m_server, 3, MSRC_UNREG | MSRC_SERVER);
958 	pcommand_add("STATS", m_stats, 2, MSRC_USER);
959 	pcommand_add("ADMIN", m_admin, 1, MSRC_USER);
960 	pcommand_add("VERSION", m_version, 1, MSRC_USER);
961 	pcommand_add("INFO", m_info, 1, MSRC_USER);
962 	pcommand_add("WHOIS", m_whois, 2, MSRC_USER);
963 	pcommand_add("TRACE", m_trace, 1, MSRC_USER);
964 	pcommand_add("AWAY", m_away, 0, MSRC_USER);
965 	pcommand_add("JOIN", m_join, 1, MSRC_USER);
966 	pcommand_add("PASS", m_pass, 1, MSRC_UNREG);
967 	pcommand_add("ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER);
968 	pcommand_add("TOPIC", m_topic, 4, MSRC_USER | MSRC_SERVER);
969 	pcommand_add("MOTD", m_motd, 1, MSRC_USER);
970 	pcommand_add("BURST", m_burst, 0, MSRC_SERVER);
971 
972 	hook_add_event("nick_group");
973 	hook_add_nick_group(nick_group);
974 	hook_add_event("nick_ungroup");
975 	hook_add_nick_ungroup(nick_ungroup);
976 
977 	m->mflags = MODTYPE_CORE;
978 
979 	pmodule_loaded = true;
980 }
981 
982 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
983  * vim:ts=8
984  * vim:sw=8
985  * vim:noexpandtab
986  */
987