1 /*
2  * Copyright (c) 2005-2006 William Pitcock, et al.
3  * Rights to this code are documented in doc/LICENSE.
4  *
5  * This file contains protocol support for IRCnet ircd's.
6  * Derived mainly from the documentation (or lack thereof)
7  * in my protocol bridge.
8  *
9  */
10 
11 #include "atheme.h"
12 #include "uplink.h"
13 #include "pmodule.h"
14 #include "protocol/ircnet.h"
15 
16 DECLARE_MODULE_V1("protocol/ircnet", true, _modinit, NULL, PACKAGE_STRING, VENDOR_STRING);
17 
18 /* *INDENT-OFF* */
19 
20 ircd_t IRCNet = {
21 	.ircdname = "ircd 2.11.1p1 or later",
22 	.tldprefix = "$$",
23 	.uses_uid = true,
24 	.uses_rcommand = false,
25 	.uses_owner = false,
26 	.uses_protect = false,
27 	.uses_halfops = false,
28 	.uses_p10 = false,
29 	.uses_vhost = false,
30 	.oper_only_modes = 0,
31 	.owner_mode = 0,
32 	.protect_mode = 0,
33 	.halfops_mode = 0,
34 	.owner_mchar = "+",
35 	.protect_mchar = "+",
36 	.halfops_mchar = "+",
37 	.type = PROTOCOL_IRCNET,
38 	.perm_mode = 0,
39 	.oimmune_mode = 0,
40 	.ban_like_modes = "beIR",
41 	.except_mchar = 'e',
42 	.invex_mchar = 'I',
43 	.flags = IRCD_CIDR_BANS,
44 };
45 
46 struct cmode_ ircnet_mode_list[] = {
47   { 'i', CMODE_INVITE },
48   { 'm', CMODE_MOD    },
49   { 'n', CMODE_NOEXT  },
50   { 'p', CMODE_PRIV   },
51   { 's', CMODE_SEC    },
52   { 't', CMODE_TOPIC  },
53   { '\0', 0 }
54 };
55 
56 struct extmode ircnet_ignore_mode_list[] = {
57   { '\0', 0 }
58 };
59 
60 struct cmode_ ircnet_status_mode_list[] = {
61   { 'o', CSTATUS_OP    },
62   { 'v', CSTATUS_VOICE },
63   { '\0', 0 }
64 };
65 
66 struct cmode_ ircnet_prefix_mode_list[] = {
67   { '@', CSTATUS_OP    },
68   { '+', CSTATUS_VOICE },
69   { '\0', 0 }
70 };
71 
72 struct cmode_ ircnet_user_mode_list[] = {
73   { 'i', UF_INVIS    },
74   { 'o', UF_IRCOP    },
75   { '\0', 0 }
76 };
77 
78 /* *INDENT-ON* */
79 
80 /* login to our uplink */
ircnet_server_login(void)81 static unsigned int ircnet_server_login(void)
82 {
83 	int ret;
84 
85 	ret = sts("PASS %s 0211010000 IRC|aDEFiIJMuw P", curr_uplink->send_pass);
86 	if (ret == 1)
87 		return 1;
88 
89 	me.bursting = true;
90 
91 	sts("SERVER %s 1 %s :%s", me.name, me.numeric, me.desc);
92 
93 	services_init();
94 
95 	sts(":%s EOB", me.numeric);
96 
97 	return 0;
98 }
99 
100 /* introduce a client */
ircnet_introduce_nick(user_t * u)101 static void ircnet_introduce_nick(user_t *u)
102 {
103 	const char *umode = user_get_umodestr(u);
104 
105 	sts(":%s UNICK %s %s %s %s 0.0.0.0 %s :%s", me.numeric, u->nick, u->uid, u->user, u->host, umode, u->gecos);
106 }
107 
108 /* invite a user to a channel */
ircnet_invite_sts(user_t * sender,user_t * target,channel_t * channel)109 static void ircnet_invite_sts(user_t *sender, user_t *target, channel_t *channel)
110 {
111 	int joined = 0;
112 
113 	/* Need to join to invite -- jilles */
114 	if (!chanuser_find(channel, sender))
115 	{
116 		sts(":%s NJOIN %s :@%s", ME, channel->name, CLIENT_NAME(sender));
117 		joined = 1;
118 	}
119 	/* ircnet's UID implementation is incomplete, in many places,
120 	 * like this one, it does not accept UIDs -- jilles */
121 	sts(":%s INVITE %s %s", CLIENT_NAME(sender), target->nick, channel->name);
122 	if (joined)
123 		sts(":%s PART %s :Invited %s", CLIENT_NAME(sender), channel->name, target->nick);
124 }
125 
ircnet_quit_sts(user_t * u,const char * reason)126 static void ircnet_quit_sts(user_t *u, const char *reason)
127 {
128 	sts(":%s QUIT :%s", u->nick, reason);
129 }
130 
131 /* join a channel */
ircnet_join_sts(channel_t * c,user_t * u,bool isnew,char * modes)132 static void ircnet_join_sts(channel_t *c, user_t *u, bool isnew, char *modes)
133 {
134 	sts(":%s NJOIN %s :@%s", me.numeric, c->name, u->uid);
135 	if (isnew && modes[0] && modes[1])
136 		sts(":%s MODE %s %s", me.numeric, c->name, modes);
137 }
138 
139 /* kicks a user from a channel */
ircnet_kick(user_t * source,channel_t * c,user_t * u,const char * reason)140 static void ircnet_kick(user_t *source, channel_t *c, user_t *u, const char *reason)
141 {
142 	/* sigh server kicks will generate snotes
143 	 * but let's avoid joining N times for N kicks */
144 	sts(":%s KICK %s %s :%s", source != NULL && chanuser_find(c, source) ? CLIENT_NAME(source) : ME, c->name, CLIENT_NAME(u), reason);
145 
146 	chanuser_delete(c, u);
147 }
148 
149 /* PRIVMSG wrapper */
ircnet_msg(const char * from,const char * target,const char * fmt,...)150 static void ircnet_msg(const char *from, const char *target, const char *fmt, ...)
151 {
152 	va_list ap;
153 	char buf[BUFSIZE];
154 
155 	va_start(ap, fmt);
156 	vsnprintf(buf, BUFSIZE, fmt, ap);
157 	va_end(ap);
158 
159 	sts(":%s PRIVMSG %s :%s", from, target, buf);
160 }
161 
ircnet_msg_global_sts(user_t * from,const char * mask,const char * text)162 static void ircnet_msg_global_sts(user_t *from, const char *mask, const char *text)
163 {
164 	mowgli_node_t *n;
165 	tld_t *tld;
166 
167 	if (!strcmp(mask, "*"))
168 	{
169 		MOWGLI_ITER_FOREACH(n, tldlist.head)
170 		{
171 			tld = n->data;
172 			sts(":%s PRIVMSG %s*%s :%s", from ? CLIENT_NAME(from) : ME, ircd->tldprefix, tld->name, text);
173 		}
174 	}
175 	else
176 		sts(":%s PRIVMSG %s%s :%s", from ? CLIENT_NAME(from) : ME, ircd->tldprefix, mask, text);
177 }
178 
179 /* NOTICE wrapper */
ircnet_notice_user_sts(user_t * from,user_t * target,const char * text)180 static void ircnet_notice_user_sts(user_t *from, user_t *target, const char *text)
181 {
182 	sts(":%s NOTICE %s :%s", from ? CLIENT_NAME(from) : ME, CLIENT_NAME(target), text);
183 }
184 
ircnet_notice_global_sts(user_t * from,const char * mask,const char * text)185 static void ircnet_notice_global_sts(user_t *from, const char *mask, const char *text)
186 {
187 	mowgli_node_t *n;
188 	tld_t *tld;
189 
190 	if (!strcmp(mask, "*"))
191 	{
192 		MOWGLI_ITER_FOREACH(n, tldlist.head)
193 		{
194 			tld = n->data;
195 			sts(":%s NOTICE %s*%s :%s", from ? CLIENT_NAME(from) : ME, ircd->tldprefix, tld->name, text);
196 		}
197 	}
198 	else
199 		sts(":%s NOTICE %s%s :%s", from ? CLIENT_NAME(from) : ME, ircd->tldprefix, mask, text);
200 }
201 
ircnet_notice_channel_sts(user_t * from,channel_t * target,const char * text)202 static void ircnet_notice_channel_sts(user_t *from, channel_t *target, const char *text)
203 {
204 	if (from == NULL || chanuser_find(target, from))
205 		sts(":%s NOTICE %s :%s", from ? CLIENT_NAME(from) : ME, target->name, text);
206 	else
207 		sts(":%s NOTICE %s :[%s:%s] %s", ME, target->name, from->nick, target->name, text);
208 }
209 
210 /* numeric wrapper */
ircnet_numeric_sts(server_t * from,int numeric,user_t * target,const char * fmt,...)211 static void ircnet_numeric_sts(server_t *from, int numeric, user_t *target, const char *fmt, ...)
212 {
213 	va_list ap;
214 	char buf[BUFSIZE];
215 
216 	va_start(ap, fmt);
217 	vsnprintf(buf, BUFSIZE, fmt, ap);
218 	va_end(ap);
219 
220 	/* if we were to use SID/UID here, the user would see SID/UID :( */
221 	sts(":%s %d %s %s", from->name, numeric, target->nick, buf);
222 }
223 
224 /* KILL wrapper */
ircnet_kill_id_sts(user_t * killer,const char * id,const char * reason)225 static void ircnet_kill_id_sts(user_t *killer, const char *id, const char *reason)
226 {
227 	if (killer != NULL)
228 		sts(":%s KILL %s :%s!%s (%s)", CLIENT_NAME(killer), id, killer->host, killer->nick, reason);
229 	else
230 		sts(":%s KILL %s :%s (%s)", ME, id, me.name, reason);
231 }
232 
233 /* PART wrapper */
ircnet_part_sts(channel_t * c,user_t * u)234 static void ircnet_part_sts(channel_t *c, user_t *u)
235 {
236 	sts(":%s PART %s", u->nick, c->name);
237 }
238 
239 /* server-to-server KLINE wrapper */
ircnet_kline_sts(const char * server,const char * user,const char * host,long duration,const char * reason)240 static void ircnet_kline_sts(const char *server, const char *user, const char *host, long duration, const char *reason)
241 {
242 	service_t *svs;
243 
244 	/* this won't propagate!
245 	 * you'll need some bot/service on each server to do that */
246 	if (irccasecmp(server, me.actual) && cnt.server > 2)
247 		wallops("Missed a tkline");
248 
249 	svs = service_find("operserv");
250 	sts(":%s TKLINE %lds %s@%s :%s", svs != NULL ? CLIENT_NAME(svs->me) : me.actual, duration, user, host, reason);
251 }
252 
253 /* server-to-server UNKLINE wrapper */
ircnet_unkline_sts(const char * server,const char * user,const char * host)254 static void ircnet_unkline_sts(const char *server, const char *user, const char *host)
255 {
256 	service_t *svs;
257 
258 	if (irccasecmp(server, me.actual) && cnt.server > 2)
259 		wallops("Missed an untkline");
260 
261 	svs = service_find("operserv");
262 	sts(":%s UNTKLINE %s@%s", svs != NULL ? CLIENT_NAME(svs->me) : me.actual, user, host);
263 }
264 
265 /* topic wrapper */
ircnet_topic_sts(channel_t * c,user_t * source,const char * setter,time_t ts,time_t prevts,const char * topic)266 static void ircnet_topic_sts(channel_t *c, user_t *source, const char *setter, time_t ts, time_t prevts, const char *topic)
267 {
268 	int joined = 0;
269 
270 	return_if_fail(c != NULL);
271 
272 	/* Need to join to set topic -- jilles */
273 	if (!chanuser_find(c, source))
274 	{
275 		sts(":%s NJOIN %s :@%s", ME, c->name, CLIENT_NAME(source));
276 		joined = 1;
277 	}
278 	sts(":%s TOPIC %s :%s", CLIENT_NAME(source), c->name, topic);
279 	if (joined)
280 		sts(":%s PART %s :Topic set for %s",
281 				CLIENT_NAME(source), c->name, setter);
282 }
283 
284 /* mode wrapper */
ircnet_mode_sts(char * sender,channel_t * target,char * modes)285 static void ircnet_mode_sts(char *sender, channel_t *target, char *modes)
286 {
287 	user_t *u;
288 
289 	return_if_fail(sender != NULL);
290 	return_if_fail(target != NULL);
291 	return_if_fail(modes != NULL);
292 
293 	u = user_find(sender);
294 
295 	/* send it from the server if that service isn't on channel
296 	 * -- jilles */
297 	sts(":%s MODE %s %s", chanuser_find(target, u) ? CLIENT_NAME(u) : ME, target->name, modes);
298 }
299 
300 /* ping wrapper */
ircnet_ping_sts(void)301 static void ircnet_ping_sts(void)
302 {
303 	sts("PING :%s", me.name);
304 }
305 
306 /* protocol-specific stuff to do on login */
ircnet_on_login(user_t * u,myuser_t * account,const char * wantedhost)307 static void ircnet_on_login(user_t *u, myuser_t *account, const char *wantedhost)
308 {
309 	/* nothing to do on ratbox */
310 	return;
311 }
312 
313 /* protocol-specific stuff to do on login */
ircnet_on_logout(user_t * u,const char * account)314 static bool ircnet_on_logout(user_t *u, const char *account)
315 {
316 	/* nothing to do on ratbox */
317 	return false;
318 }
319 
ircnet_jupe(const char * server,const char * reason)320 static void ircnet_jupe(const char *server, const char *reason)
321 {
322 	service_t *svs;
323 	static char sid[4+1];
324 	int i;
325 	server_t *s;
326 
327 	svs = service_find("operserv");
328 	sts(":%s SQUIT %s :%s", svs != NULL ? CLIENT_NAME(svs->me) : me.actual, server, reason);
329 
330 	s = server_find(server);
331 	/* We need to wait for the SQUIT to be processed -- jilles */
332 	if (s != NULL)
333 	{
334 		s->flags |= SF_JUPE_PENDING;
335 		return;
336 	}
337 
338 	/* dirty dirty make up some sid */
339 	if (sid[0] == '\0')
340 		mowgli_strlcpy(sid, me.numeric, sizeof sid);
341 	do
342 	{
343 		i = 3;
344 		for (;;)
345 		{
346 			if (sid[i] == 'Z')
347 			{
348 				sid[i] = '0';
349 				i--;
350 				/* eek, no more sids */
351 				if (i < 0)
352 					return;
353 				continue;
354 			}
355 			else if (sid[i] == '9')
356 				sid[i] = 'A';
357 			else sid[i]++;
358 			break;
359 		}
360 	} while (server_find(sid));
361 
362 	sts(":%s SERVER %s 2 %s 0211010000 :%s", me.name, server, sid, reason);
363 }
364 
m_topic(sourceinfo_t * si,int parc,char * parv[])365 static void m_topic(sourceinfo_t *si, int parc, char *parv[])
366 {
367 	channel_t *c = channel_find(parv[0]);
368 
369 	if (!c)
370 		return;
371 
372 	handle_topic_from(si, c, si->su->nick, CURRTIME, parv[1]);
373 }
374 
m_ping(sourceinfo_t * si,int parc,char * parv[])375 static void m_ping(sourceinfo_t *si, int parc, char *parv[])
376 {
377 	/* reply to PING's */
378 	sts(":%s PONG %s %s", me.name, me.name, parv[0]);
379 }
380 
m_pong(sourceinfo_t * si,int parc,char * parv[])381 static void m_pong(sourceinfo_t *si, int parc, char *parv[])
382 {
383 	/* someone replied to our PING */
384 	if ((!parv[0]) || (strcasecmp(me.actual, parv[0])))
385 		return;
386 
387 	me.uplinkpong = CURRTIME;
388 
389 	/* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
390 }
391 
m_eob(sourceinfo_t * si,int parc,char * parv[])392 static void m_eob(sourceinfo_t *si, int parc, char *parv[])
393 {
394 	server_t *serv;
395 	char sidbuf[4+1], *p;
396 
397 	handle_eob(si->s);
398 	if (parc >= 1)
399 	{
400 		sidbuf[4] = '\0';
401 		p = parv[0];
402 		while (p[0] && p[1] && p[2] && p[3])
403 		{
404 			memcpy(sidbuf, p, 4);
405 			serv = server_find(sidbuf);
406 			handle_eob(serv);
407 			if (p[4] != ',')
408 				break;
409 			p += 5;
410 		}
411 	}
412 
413 	if (me.bursting)
414 	{
415 		sts(":%s EOBACK", me.numeric);
416 #ifdef HAVE_GETTIMEOFDAY
417 		e_time(burstime, &burstime);
418 
419 		slog(LG_INFO, "m_eob(): finished synching with uplink (%d %s)", (tv2ms(&burstime) > 1000) ? (tv2ms(&burstime) / 1000) : tv2ms(&burstime), (tv2ms(&burstime) > 1000) ? "s" : "ms");
420 
421 		wallops("Finished synchronizing with network in %d %s.", (tv2ms(&burstime) > 1000) ? (tv2ms(&burstime) / 1000) : tv2ms(&burstime), (tv2ms(&burstime) > 1000) ? "s" : "ms");
422 #else
423 		slog(LG_INFO, "m_eob(): finished synching with uplink");
424 		wallops("Finished synchronizing with network.");
425 #endif
426 
427 		me.bursting = false;
428 	}
429 }
430 
m_privmsg(sourceinfo_t * si,int parc,char * parv[])431 static void m_privmsg(sourceinfo_t *si, int parc, char *parv[])
432 {
433 	if (parc != 2)
434 		return;
435 
436 	handle_message(si, parv[0], false, parv[1]);
437 }
438 
m_notice(sourceinfo_t * si,int parc,char * parv[])439 static void m_notice(sourceinfo_t *si, int parc, char *parv[])
440 {
441 	if (parc != 2)
442 		return;
443 
444 	handle_message(si, parv[0], true, parv[1]);
445 }
446 
m_njoin(sourceinfo_t * si,int parc,char * parv[])447 static void m_njoin(sourceinfo_t *si, int parc, char *parv[])
448 {
449 	channel_t *c;
450 	unsigned int userc;
451 	char *userv[256];
452 	unsigned int i;
453 
454 	c = channel_find(parv[0]);
455 
456 	if (!c)
457 	{
458 		slog(LG_DEBUG, "m_njoin(): new channel: %s", parv[0]);
459 		/* Give channels created during burst an older "TS"
460 		 * so they won't be deopped -- jilles */
461 		c = channel_add(parv[0], si->s->flags & SF_EOB ? CURRTIME : CURRTIME - 601, si->s);
462 		/* if !/+ channel, we don't want to do anything with it */
463 		if (c == NULL)
464 			return;
465 		/* Check mode locks */
466 		channel_mode_va(NULL, c, 1, "+");
467 	}
468 
469 	userc = sjtoken(parv[parc - 1], ',', userv);
470 
471 	for (i = 0; i < userc; i++)
472 		chanuser_add(c, userv[i]);
473 
474 	if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
475 		channel_delete(c);
476 }
477 
m_part(sourceinfo_t * si,int parc,char * parv[])478 static void m_part(sourceinfo_t *si, int parc, char *parv[])
479 {
480 	int chanc;
481 	char *chanv[256];
482 	int i;
483 
484 	chanc = sjtoken(parv[0], ',', chanv);
485 	for (i = 0; i < chanc; i++)
486 	{
487 		slog(LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
488 
489 		chanuser_delete(channel_find(chanv[i]), si->su);
490 	}
491 }
492 
m_nick(sourceinfo_t * si,int parc,char * parv[])493 static void m_nick(sourceinfo_t *si, int parc, char *parv[])
494 {
495 	user_t *u;
496 
497 	/* got the right number of args for an introduction? */
498 	if (parc == 7)
499 	{
500 		slog(LG_DEBUG, "m_nick(): new user on `%s': %s", si->s->name, parv[0]);
501 
502 		u = user_add(parv[0], parv[2], parv[3], NULL, parv[4], parv[1], parv[6], si->s, 0);
503 		if (u == NULL)
504 			return;
505 
506 		user_mode(u, parv[5]);
507 		if (strchr(parv[5], 'a'))
508 			handle_away(u, "Gone");
509 
510 		handle_nickchange(u);
511 	}
512 
513 	/* if it's only 1 then it's a nickname change */
514 	else if (parc == 1)
515 	{
516 		if (!si->su)
517 		{
518 			slog(LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
519 			return;
520 		}
521 
522 		slog(LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
523 
524 		if (user_changenick(si->su, parv[0], 0))
525 			return;
526 
527 		handle_nickchange(si->su);
528 	}
529 	else
530 	{
531 		int i;
532 		slog(LG_DEBUG, "m_nick(): got NICK with wrong number of params");
533 
534 		for (i = 0; i < parc; i++)
535 			slog(LG_DEBUG, "m_nick():   parv[%d] = %s", i, parv[i]);
536 	}
537 }
538 
m_save(sourceinfo_t * si,int parc,char * parv[])539 static void m_save(sourceinfo_t *si, int parc, char *parv[])
540 {
541 	user_t *u;
542 
543 	u = user_find(parv[0]);
544 	if (u == NULL)
545 		return;
546 	if (!strcmp(u->nick, u->uid))
547 	{
548 		slog(LG_DEBUG, "m_save(): ignoring noop SAVE message for %s", u->nick);
549 		return;
550 	}
551 	if (is_internal_client(u))
552 	{
553 		slog(LG_INFO, "m_save(): service %s got hit, changing back", u->nick);
554 		sts(":%s NICK %s", u->uid, u->nick);
555 		/* XXX services wars */
556 	}
557 	else
558 	{
559 		slog(LG_DEBUG, "m_save(): nickname change for `%s': %s", u->nick, u->uid);
560 
561 		if (user_changenick(u, u->uid, 0))
562 			return;
563 
564 		handle_nickchange(u);
565 	}
566 }
567 
m_quit(sourceinfo_t * si,int parc,char * parv[])568 static void m_quit(sourceinfo_t *si, int parc, char *parv[])
569 {
570 	slog(LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
571 
572 	/* user_delete() takes care of removing channels and so forth */
573 	user_delete(si->su, parv[0]);
574 }
575 
m_mode(sourceinfo_t * si,int parc,char * parv[])576 static void m_mode(sourceinfo_t *si, int parc, char *parv[])
577 {
578 	/* The following is hackish, but it works, because user MODE
579 	 * is not used in bursts and users are not allowed to change away
580 	 * status using MODE.
581 	 * -- jilles */
582 	if (*parv[0] == '#')
583 		channel_mode(NULL, channel_find(parv[0]), parc - 1, &parv[1]);
584 	else if (!strcmp(parv[1], "-a"))
585 		handle_away(user_find(parv[0]), NULL);
586 	else if (!strcmp(parv[1], "+a"))
587 		handle_away(user_find(parv[0]), "Gone");
588 	else
589 		user_mode(user_find(parv[0]), parv[1]);
590 }
591 
m_kick(sourceinfo_t * si,int parc,char * parv[])592 static void m_kick(sourceinfo_t *si, int parc, char *parv[])
593 {
594 	user_t *u = user_find(parv[1]);
595 	channel_t *c = channel_find(parv[0]);
596 
597 	/* -> :rakaur KICK #shrike rintaun :test */
598 	slog(LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
599 
600 	if (!u)
601 	{
602 		slog(LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
603 		return;
604 	}
605 
606 	if (!c)
607 	{
608 		slog(LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
609 		return;
610 	}
611 
612 	if (!chanuser_find(c, u))
613 	{
614 		slog(LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
615 		return;
616 	}
617 
618 	chanuser_delete(c, u);
619 
620 	/* if they kicked us, let's rejoin */
621 	if (is_internal_client(u))
622 	{
623 		slog(LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
624 		join(parv[0], u->nick);
625 	}
626 }
627 
m_kill(sourceinfo_t * si,int parc,char * parv[])628 static void m_kill(sourceinfo_t *si, int parc, char *parv[])
629 {
630 	handle_kill(si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
631 }
632 
m_squit(sourceinfo_t * si,int parc,char * parv[])633 static void m_squit(sourceinfo_t *si, int parc, char *parv[])
634 {
635 	slog(LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
636 	if (server_find(parv[0]))
637 		server_delete(parv[0]);
638 	else if (si->su != NULL)
639 	{
640 		/* XXX we don't have a list of jupes, so let's just
641 		 * assume it is one if we don't know it */
642 		slog(LG_INFO, "m_squit(): accepting SQUIT for jupe %s from %s", parv[0], si->su->nick);
643 		sts(":%s WALLOPS :Received SQUIT %s from %s (%s)", me.numeric, parv[0], si->su->nick, parv[1]);
644 		sts(":%s SQUIT %s :%s", me.numeric, parv[0], parv[1]);
645 	}
646 }
647 
m_server(sourceinfo_t * si,int parc,char * parv[])648 static void m_server(sourceinfo_t *si, int parc, char *parv[])
649 {
650 	slog(LG_DEBUG, "m_server(): new server: %s", parv[0]);
651 	handle_server(si, parv[0], parv[2], atoi(parv[1]), parv[parc - 1]);
652 }
653 
m_smask(sourceinfo_t * si,int parc,char * parv[])654 static void m_smask(sourceinfo_t *si, int parc, char *parv[])
655 {
656 	slog(LG_DEBUG, "m_smask(): new masked server: %s (%s)",
657 			si->s->name, parv[0]);
658 	handle_server(si, NULL, parv[0], si->s->hops + 1, si->s->desc);
659 }
660 
m_stats(sourceinfo_t * si,int parc,char * parv[])661 static void m_stats(sourceinfo_t *si, int parc, char *parv[])
662 {
663 	handle_stats(si->su, parv[0][0]);
664 }
665 
m_admin(sourceinfo_t * si,int parc,char * parv[])666 static void m_admin(sourceinfo_t *si, int parc, char *parv[])
667 {
668 	handle_admin(si->su);
669 }
670 
m_version(sourceinfo_t * si,int parc,char * parv[])671 static void m_version(sourceinfo_t *si, int parc, char *parv[])
672 {
673 	handle_version(si->su);
674 }
675 
m_info(sourceinfo_t * si,int parc,char * parv[])676 static void m_info(sourceinfo_t *si, int parc, char *parv[])
677 {
678 	handle_info(si->su);
679 }
680 
m_whois(sourceinfo_t * si,int parc,char * parv[])681 static void m_whois(sourceinfo_t *si, int parc, char *parv[])
682 {
683 	handle_whois(si->su, parv[1]);
684 }
685 
m_trace(sourceinfo_t * si,int parc,char * parv[])686 static void m_trace(sourceinfo_t *si, int parc, char *parv[])
687 {
688 	handle_trace(si->su, parv[0], parc >= 2 ? parv[1] : NULL);
689 }
690 
m_join(sourceinfo_t * si,int parc,char * parv[])691 static void m_join(sourceinfo_t *si, int parc, char *parv[])
692 {
693 	chanuser_t *cu;
694 	mowgli_node_t *n, *tn;
695 
696 	/* JOIN 0 is really a part from all channels */
697 	if (parv[0][0] == '0')
698 	{
699 		MOWGLI_ITER_FOREACH_SAFE(n, tn, si->su->channels.head)
700 		{
701 			cu = (chanuser_t *)n->data;
702 			chanuser_delete(cu->chan, si->su);
703 		}
704 	}
705 }
706 
m_pass(sourceinfo_t * si,int parc,char * parv[])707 static void m_pass(sourceinfo_t *si, int parc, char *parv[])
708 {
709 	if (strcmp(curr_uplink->receive_pass, parv[0]))
710 	{
711 		slog(LG_INFO, "m_pass(): password mismatch from uplink; aborting");
712 		runflags |= RF_SHUTDOWN;
713 	}
714 }
715 
m_error(sourceinfo_t * si,int parc,char * parv[])716 static void m_error(sourceinfo_t *si, int parc, char *parv[])
717 {
718 	slog(LG_INFO, "m_error(): error from server: %s", parv[0]);
719 }
720 
m_motd(sourceinfo_t * si,int parc,char * parv[])721 static void m_motd(sourceinfo_t *si, int parc, char *parv[])
722 {
723 	handle_motd(si->su);
724 }
725 
_modinit(module_t * m)726 void _modinit(module_t * m)
727 {
728 	MODULE_TRY_REQUEST_DEPENDENCY(m, "transport/rfc1459");
729 	MODULE_TRY_REQUEST_DEPENDENCY(m, "protocol/base36uid");
730 
731 	/* Symbol relocation voodoo. */
732 	server_login = &ircnet_server_login;
733 	introduce_nick = &ircnet_introduce_nick;
734 	quit_sts = &ircnet_quit_sts;
735 	join_sts = &ircnet_join_sts;
736 	kick = &ircnet_kick;
737 	msg = &ircnet_msg;
738 	msg_global_sts = &ircnet_msg_global_sts;
739 	notice_user_sts = &ircnet_notice_user_sts;
740 	notice_global_sts = &ircnet_notice_global_sts;
741 	notice_channel_sts = &ircnet_notice_channel_sts;
742 	/* no wallchops, ircnet ircd does not support this */
743 	numeric_sts = &ircnet_numeric_sts;
744 	kill_id_sts = &ircnet_kill_id_sts;
745 	part_sts = &ircnet_part_sts;
746 	kline_sts = &ircnet_kline_sts;
747 	unkline_sts = &ircnet_unkline_sts;
748 	topic_sts = &ircnet_topic_sts;
749 	mode_sts = &ircnet_mode_sts;
750 	ping_sts = &ircnet_ping_sts;
751 	ircd_on_login = &ircnet_on_login;
752 	ircd_on_logout = &ircnet_on_logout;
753 	jupe = &ircnet_jupe;
754 	invite_sts = &ircnet_invite_sts;
755 
756 	mode_list = ircnet_mode_list;
757 	ignore_mode_list = ircnet_ignore_mode_list;
758 	status_mode_list = ircnet_status_mode_list;
759 	prefix_mode_list = ircnet_prefix_mode_list;
760 	user_mode_list = ircnet_user_mode_list;
761 	ignore_mode_list_size = ARRAY_SIZE(ircnet_ignore_mode_list);
762 
763 	ircd = &IRCNet;
764 
765 	pcommand_add("PING", m_ping, 1, MSRC_USER | MSRC_SERVER);
766 	pcommand_add("PONG", m_pong, 1, MSRC_SERVER);
767 	pcommand_add("EOB", m_eob, 0, MSRC_SERVER);
768 	pcommand_add("PRIVMSG", m_privmsg, 2, MSRC_USER);
769 	pcommand_add("NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER);
770 	pcommand_add("NJOIN", m_njoin, 2, MSRC_SERVER);
771 	pcommand_add("PART", m_part, 1, MSRC_USER);
772 	pcommand_add("NICK", m_nick, 1, MSRC_USER);
773 	pcommand_add("UNICK", m_nick, 7, MSRC_SERVER);
774 	pcommand_add("SAVE", m_save, 1, MSRC_SERVER);
775 	pcommand_add("QUIT", m_quit, 1, MSRC_USER);
776 	pcommand_add("MODE", m_mode, 2, MSRC_USER | MSRC_SERVER);
777 	pcommand_add("KICK", m_kick, 2, MSRC_USER | MSRC_SERVER);
778 	pcommand_add("KILL", m_kill, 1, MSRC_USER | MSRC_SERVER);
779 	pcommand_add("SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER);
780 	pcommand_add("SERVER", m_server, 4, MSRC_UNREG | MSRC_SERVER);
781 	pcommand_add("SMASK", m_smask, 2, MSRC_SERVER);
782 	pcommand_add("STATS", m_stats, 2, MSRC_USER);
783 	pcommand_add("ADMIN", m_admin, 1, MSRC_USER);
784 	pcommand_add("VERSION", m_version, 1, MSRC_USER);
785 	pcommand_add("INFO", m_info, 1, MSRC_USER);
786 	pcommand_add("WHOIS", m_whois, 2, MSRC_USER);
787 	pcommand_add("TRACE", m_trace, 1, MSRC_USER);
788 	pcommand_add("JOIN", m_join, 1, MSRC_USER);
789 	pcommand_add("PASS", m_pass, 1, MSRC_UNREG);
790 	pcommand_add("ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER);
791 	pcommand_add("TOPIC", m_topic, 2, MSRC_USER);
792 	pcommand_add("MOTD", m_motd, 1, MSRC_USER);
793 
794 	m->mflags = MODTYPE_CORE;
795 
796 	pmodule_loaded = true;
797 }
798 
799 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
800  * vim:ts=8
801  * vim:sw=8
802  * vim:noexpandtab
803  */
804