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/unreal.h"
14 
15 DECLARE_MODULE_V1("protocol/unreal", true, _modinit, NULL, PACKAGE_STRING, VENDOR_STRING);
16 
17 static bool has_protoctl = false;
18 static bool use_esvid = false;
19 static bool use_mlock = false;
20 static char ts6sid[3 + 1] = "";
21 
22 /* *INDENT-OFF* */
23 
24 ircd_t Unreal = {
25 	.ircdname = "UnrealIRCd 3.1 or later",
26 	.tldprefix = "$",
27 	.uses_uid = true,
28 	.uses_rcommand = false,
29 	.uses_owner = true,
30 	.uses_protect = true,
31 	.uses_halfops = true,
32 	.uses_p10 = false,
33 	.uses_vhost = true,
34 	.oper_only_modes = CMODE_OPERONLY | CMODE_ADMONLY,
35 	.owner_mode = CSTATUS_OWNER,
36 	.protect_mode = CSTATUS_PROTECT,
37 	.halfops_mode = CSTATUS_HALFOP,
38 	.owner_mchar = "+q",
39 	.protect_mchar = "+a",
40 	.halfops_mchar = "+h",
41 	.type = PROTOCOL_UNREAL,
42 	.perm_mode = CMODE_PERM,
43 	.oimmune_mode = 0,
44 	.ban_like_modes = "beI",
45 	.except_mchar = 'e',
46 	.invex_mchar = 'I',
47 	.flags = IRCD_HOLDNICK | IRCD_SASL_USE_PUID,
48 };
49 
50 struct cmode_ unreal_mode_list[] = {
51   { 'i', CMODE_INVITE	},
52   { 'm', CMODE_MOD	},
53   { 'n', CMODE_NOEXT	},
54   { 'p', CMODE_PRIV	},
55   { 's', CMODE_SEC	},
56   { 't', CMODE_TOPIC	},
57   { 'c', CMODE_NOCOLOR	},
58   { 'M', CMODE_MODREG	},
59   { 'R', CMODE_REGONLY	},
60   { 'O', CMODE_OPERONLY },
61   { 'A', CMODE_ADMONLY	},
62   { 'Q', CMODE_PEACE	},
63   { 'S', CMODE_STRIP	},
64   { 'K', CMODE_NOKNOCK	},
65   { 'V', CMODE_NOINVITE },
66   { 'C', CMODE_NOCTCP	},
67   { 'u', CMODE_HIDING	},
68   { 'z', CMODE_SSLONLY	},
69   { 'N', CMODE_STICKY	},
70   { 'G', CMODE_CENSOR	},
71   { 'r', CMODE_CHANREG	},
72   { 'P', CMODE_PERM	},
73   { '\0', 0 }
74 };
75 
76 static bool check_jointhrottle(const char *, channel_t *, mychan_t *, user_t *, myuser_t *);
77 static bool check_flood(const char *value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu);
78 static bool check_forward(const char *value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu);
79 
80 struct extmode unreal_ignore_mode_list[] = {
81   { 'j', check_jointhrottle },
82   { 'f', check_flood },
83   { 'L', check_forward },
84   { '\0', 0 }
85 };
86 
87 struct cmode_ unreal_status_mode_list[] = {
88   { 'q', CSTATUS_OWNER	 },
89   { 'a', CSTATUS_PROTECT },
90   { 'o', CSTATUS_OP	 },
91   { 'h', CSTATUS_HALFOP  },
92   { 'v', CSTATUS_VOICE	 },
93   { '\0', 0 }
94 };
95 
96 struct cmode_ unreal_prefix_mode_list[] = {
97   { '*', CSTATUS_OWNER	 },
98   { '~', CSTATUS_PROTECT },
99   { '@', CSTATUS_OP	 },
100   { '%', CSTATUS_HALFOP  },
101   { '+', CSTATUS_VOICE	 },
102   { '\0', 0 }
103 };
104 
105 struct cmode_ unreal_user_mode_list[] = {
106   { 'A', UF_ADMIN    },
107   { 'i', UF_INVIS    },
108   { 'o', UF_IRCOP    },
109   { 'd', UF_DEAF     },
110   { '\0', 0 }
111 };
112 
113 /* *INDENT-ON* */
114 
check_jointhrottle(const char * value,channel_t * c,mychan_t * mc,user_t * u,myuser_t * mu)115 static bool check_jointhrottle(const char *value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
116 {
117 	const char *p, *arg2;
118 
119 	p = value, arg2 = NULL;
120 	while (*p != '\0')
121 	{
122 		if (*p == ':')
123 		{
124 			if (arg2 != NULL)
125 				return false;
126 			arg2 = p + 1;
127 		}
128 		else if (!isdigit((unsigned char)*p))
129 			return false;
130 		p++;
131 	}
132 	if (arg2 == NULL)
133 		return false;
134 	if (p - arg2 > 10 || arg2 - value - 1 > 10 || !atoi(value) || !atoi(arg2))
135 		return false;
136 	return true;
137 }
138 
139 /* +f 3:1 or +f *3:1 (which is like +f [3t]:1 or +f [3t#b]:1) */
check_flood_old(const char * value,channel_t * c,mychan_t * mc,user_t * u,myuser_t * mu)140 static inline bool check_flood_old(const char *value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
141 {
142 	bool found_colon = false;
143 
144 	return_val_if_fail(value != NULL, false);
145 
146 	/* x:y is 3 bytes long, so that is the minimum length of a +f parameter. */
147 	if (strlen(value) < 3)
148 		return false;
149 
150 	/* skip past the * if present */
151 	if (*value == '*')
152 		value++;
153 
154 	/* check to make sure all bytes are numbers, allowing for one colon */
155 	while (*value != '\0')
156 	{
157 		if (*value == ':' && !found_colon)
158 			found_colon = true;
159 		else if (!isdigit((unsigned char)*value))
160 			return false;
161 
162 		value++;
163 	}
164 
165 	/* we have to have a colon in order for it to be valid */
166 	if (!found_colon)
167 		return false;
168 
169 	return true;
170 }
171 
172 #define VALID_FLOOD_CHAR(c)	((c == 'c') || (c == 'j') || (c == 'k') || (c == 'm') || (c == 'n') || (c == 't'))
173 #define VALID_ACTION_CHAR(c)	((c == 'm') || (c == 'M') || (c == 'C') || (c == 'R') || (c == 'i') || (c == 'K') \
174 				 || (c == 'N') || (c == 'b'))
175 
176 /*
177  * +f *X:Y	 (handled by check_flood_old)
178  * +f X:Y	 (handled by check_flood_old)
179  *
180  * +f [<number><letter>(#<letter>)(,...)]
181  */
check_flood(const char * value,channel_t * c,mychan_t * mc,user_t * u,myuser_t * mu)182 static bool check_flood(const char *value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
183 {
184 	char evalbuf[BUFSIZE], *ep, *p;
185 
186 	if (*value != '[')
187 		return check_flood_old(value, c, mc, u, mu);
188 
189 	/* copy this to a local buffer for evaluation */
190 	mowgli_strlcpy(evalbuf, value, sizeof evalbuf);
191 	ep = evalbuf + 1;
192 
193 	/* check that the parameter ends with a ] */
194 	if ((p = strchr(ep, ']')) == NULL)
195 		return false;
196 
197 	/* we have a ], blast it away and check for a colon. */
198 	*p = '\0';
199 	if (*(p + 1) != ':')
200 		return false;
201 
202 	for (p = strtok(ep, ","); p != NULL; p = strtok(NULL, ","))
203 	{
204 		while (isdigit((unsigned char)*p))
205 			p++;
206 
207 		if (!VALID_FLOOD_CHAR(*p))
208 			return false;
209 
210 		*p = '\0';
211 		p++;
212 
213 		if (*p != '\0')
214 		{
215 			if (*p == '#')
216 			{
217 				p++;
218 
219 				if (!VALID_ACTION_CHAR(*p))
220 					return false;
221 
222 				continue;
223 			}
224 
225 			/* not valid, needs to be # or nothing */
226 			return false;
227 		}
228 	}
229 
230 	return true;
231 }
232 
check_forward(const char * value,channel_t * c,mychan_t * mc,user_t * u,myuser_t * mu)233 static bool check_forward(const char *value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
234 {
235 	channel_t *target_c;
236 	mychan_t *target_mc;
237 
238 	if (!VALID_GLOBAL_CHANNEL_PFX(value) || strlen(value) > 50)
239 		return false;
240 	if (u == NULL && mu == NULL)
241 		return true;
242 	target_c = channel_find(value);
243 	target_mc = mychan_from(target_c);
244 	if (target_c == NULL && target_mc == NULL)
245 		return false;
246 	return true;
247 }
248 
unreal_next_matching_ban(channel_t * c,user_t * u,int type,mowgli_node_t * first)249 static mowgli_node_t *unreal_next_matching_ban(channel_t *c, user_t *u, int type, mowgli_node_t *first)
250 {
251 	chanban_t *cb;
252 	mowgli_node_t *n;
253 	char hostbuf[NICKLEN+USERLEN+HOSTLEN];
254 	char realbuf[NICKLEN+USERLEN+HOSTLEN];
255 	char ipbuf[NICKLEN+USERLEN+HOSTLEN];
256 	char *p;
257 	bool matched;
258 	int exttype;
259 	channel_t *target_c;
260 
261 	snprintf(hostbuf, sizeof hostbuf, "%s!%s@%s", u->nick, u->user, u->vhost);
262 	snprintf(realbuf, sizeof realbuf, "%s!%s@%s", u->nick, u->user, u->host);
263 	/* will be nick!user@ if ip unknown, doesn't matter */
264 	snprintf(ipbuf, sizeof ipbuf, "%s!%s@%s", u->nick, u->user, u->ip);
265 
266 	MOWGLI_ITER_FOREACH(n, first)
267 	{
268 		cb = n->data;
269 
270 		if (cb->type != type)
271 			continue;
272 
273 		if ((!match(cb->mask, hostbuf) || !match(cb->mask, realbuf) || !match(cb->mask, ipbuf)))
274 			return n;
275 		if (cb->mask[0] == '~')
276 		{
277 			p = cb->mask + 1;
278 			exttype = *p++;
279 			if (exttype == '\0')
280 				continue;
281 			/* check parameter */
282 			if (*p++ != ':')
283 				p = NULL;
284 			switch (exttype)
285 			{
286 				case 'a':
287 					matched = u->myuser != NULL && !(u->myuser->flags & MU_WAITAUTH) && (p == NULL || !match(p, entity(u->myuser)->name));
288 					break;
289 				case 'c':
290 					if (p == NULL)
291 						continue;
292 					target_c = channel_find(p);
293 					if (target_c == NULL || (target_c->modes & (CMODE_PRIV | CMODE_SEC)))
294 						continue;
295 					matched = chanuser_find(target_c, u) != NULL;
296 					break;
297 				case 'r':
298 					if (p == NULL)
299 						continue;
300 					matched = !match(p, u->gecos);
301 					break;
302 				case 'R':
303 					matched = should_reg_umode(u);
304 					break;
305 				case 'q':
306 					matched = !match(p, hostbuf) || !match(p, ipbuf);
307 					break;
308 				default:
309 					continue;
310 			}
311 			if (matched)
312 				return n;
313 		}
314 	}
315 	return NULL;
316 }
317 
318 /* login to our uplink */
unreal_server_login(void)319 static unsigned int unreal_server_login(void)
320 {
321 	int ret;
322 
323 	ret = sts("PASS %s", curr_uplink->send_pass);
324 	if (ret == 1)
325 		return 1;
326 
327 	me.bursting = true;
328 	has_protoctl = false;
329 
330 	sts("PROTOCTL NICKv2 VHP NICKIP UMODE2 SJOIN SJOIN2 SJ3 NOQUIT TKLEXT ESVID MLOCK");
331 	sts("PROTOCTL SID=%s", me.numeric);
332 	sts("SERVER %s 1 :%s", me.name, me.desc);
333 
334 	sts(":%s EOS", ME);
335 
336 	return 0;
337 }
338 
339 /* introduce a client */
unreal_introduce_nick(user_t * u)340 static void unreal_introduce_nick(user_t *u)
341 {
342 	const char *umode = user_get_umodestr(u);
343 
344 	if (!ircd->uses_uid)
345 		sts("NICK %s 1 %lu %s %s %s 0 %sS * :%s", u->nick, (unsigned long)u->ts, u->user, u->host, me.name, umode, u->gecos);
346 	else
347 		sts(":%s UID %s 1 %lu %s %s %s * %sS * * * :%s", ME, u->nick, (unsigned long)u->ts, u->user, u->host, u->uid, umode, u->gecos);
348 }
349 
350 /* invite a user to a channel */
unreal_invite_sts(user_t * sender,user_t * target,channel_t * channel)351 static void unreal_invite_sts(user_t *sender, user_t *target, channel_t *channel)
352 {
353 	sts(":%s INVITE %s %s", CLIENT_NAME(sender), CLIENT_NAME(target), channel->name);
354 }
355 
unreal_quit_sts(user_t * u,const char * reason)356 static void unreal_quit_sts(user_t *u, const char *reason)
357 {
358 	sts(":%s QUIT :%s", CLIENT_NAME(u), reason);
359 }
360 
361 /* WALLOPS wrapper */
unreal_wallops_sts(const char * text)362 static void unreal_wallops_sts(const char *text)
363 {
364 	sts(":%s GLOBOPS :%s", ME, text);
365 }
366 
367 /* join a channel */
unreal_join_sts(channel_t * c,user_t * u,bool isnew,char * modes)368 static void unreal_join_sts(channel_t *c, user_t *u, bool isnew, char *modes)
369 {
370 	if (isnew)
371 		sts(":%s SJOIN %lu %s %s :@%s", ME, (unsigned long)c->ts,
372 				c->name, modes, CLIENT_NAME(u));
373 	else
374 		sts(":%s SJOIN %lu %s + :@%s", ME, (unsigned long)c->ts,
375 				c->name, CLIENT_NAME(u));
376 }
377 
378 /* lower TS */
unreal_chan_lowerts(channel_t * c,user_t * u)379 static void unreal_chan_lowerts(channel_t *c, user_t *u)
380 {
381 	slog(LG_DEBUG, "unreal_chan_lowerts(): lowering TS for %s to %lu",
382 			c->name, (unsigned long)c->ts);
383 	sts(":%s SJOIN %lu %s %s :@%s", ME, (unsigned long)c->ts, c->name,
384 			channel_modes(c, true), CLIENT_NAME(u));
385 }
386 
387 /* kicks a user from a channel */
unreal_kick(user_t * source,channel_t * c,user_t * u,const char * reason)388 static void unreal_kick(user_t *source, channel_t *c, user_t *u, const char *reason)
389 {
390 	sts(":%s KICK %s %s :%s", source->nick, c->name, u->nick, reason);
391 
392 	chanuser_delete(c, u);
393 }
394 
395 /* PRIVMSG wrapper */
unreal_msg(const char * from,const char * target,const char * fmt,...)396 static void unreal_msg(const char *from, const char *target, const char *fmt, ...)
397 {
398 	va_list ap;
399 	char buf[BUFSIZE];
400 
401 	va_start(ap, fmt);
402 	vsnprintf(buf, BUFSIZE, fmt, ap);
403 	va_end(ap);
404 
405 	sts(":%s PRIVMSG %s :%s", from, target, buf);
406 }
407 
unreal_msg_global_sts(user_t * from,const char * mask,const char * text)408 static void unreal_msg_global_sts(user_t *from, const char *mask, const char *text)
409 {
410 	mowgli_node_t *n;
411 	tld_t *tld;
412 
413 	if (!strcmp(mask, "*"))
414 	{
415 		MOWGLI_ITER_FOREACH(n, tldlist.head)
416 		{
417 			tld = n->data;
418 			sts(":%s PRIVMSG %s*%s :%s", from ? CLIENT_NAME(from) : ME, ircd->tldprefix, tld->name, text);
419 		}
420 	}
421 	else
422 		sts(":%s PRIVMSG %s%s :%s", from ? CLIENT_NAME(from) : ME, ircd->tldprefix, mask, text);
423 }
424 
425 /* NOTICE wrapper */
unreal_notice_user_sts(user_t * from,user_t * target,const char * text)426 static void unreal_notice_user_sts(user_t *from, user_t *target, const char *text)
427 {
428 	sts(":%s NOTICE %s :%s", from ? CLIENT_NAME(from) : ME, CLIENT_NAME(target), text);
429 }
430 
unreal_notice_global_sts(user_t * from,const char * mask,const char * text)431 static void unreal_notice_global_sts(user_t *from, const char *mask, const char *text)
432 {
433 	mowgli_node_t *n;
434 	tld_t *tld;
435 
436 	if (!strcmp(mask, "*"))
437 	{
438 		MOWGLI_ITER_FOREACH(n, tldlist.head)
439 		{
440 			tld = n->data;
441 			sts(":%s NOTICE %s*%s :%s", from ? CLIENT_NAME(from) : ME, ircd->tldprefix, tld->name, text);
442 		}
443 	}
444 	else
445 		sts(":%s NOTICE %s%s :%s", from ? CLIENT_NAME(from) : ME, ircd->tldprefix, mask, text);
446 }
447 
unreal_notice_channel_sts(user_t * from,channel_t * target,const char * text)448 static void unreal_notice_channel_sts(user_t *from, channel_t *target, const char *text)
449 {
450 	sts(":%s NOTICE %s :%s", from ? CLIENT_NAME(from) : ME, target->name, text);
451 }
452 
unreal_numeric_sts(server_t * from,int numeric,user_t * target,const char * fmt,...)453 static void unreal_numeric_sts(server_t *from, int numeric, user_t *target, const char *fmt, ...)
454 {
455 	va_list ap;
456 	char buf[BUFSIZE];
457 
458 	va_start(ap, fmt);
459 	vsnprintf(buf, BUFSIZE, fmt, ap);
460 	va_end(ap);
461 
462 	sts(":%s %d %s %s", SERVER_NAME(from), numeric, CLIENT_NAME(target), buf);
463 }
464 
465 /* KILL wrapper */
unreal_kill_id_sts(user_t * killer,const char * id,const char * reason)466 static void unreal_kill_id_sts(user_t *killer, const char *id, const char *reason)
467 {
468 	if (killer != NULL)
469 	{
470 		if (nicksvs.me != NULL && killer == nicksvs.me->me)
471 		{
472 			sts(":%s SVSKILL %s :Killed (%s (%s))",
473 					killer->nick, id,
474 					killer->nick, reason);
475 			/* We still send KILL too, for two reasons:
476 			 * 1. SVSKILL does not do nick chase as KILL does,
477 			 *    so the below KILL ensures the user is removed
478 			 *    everywhere if kill and nick change cross.
479 			 *    If this happens between two ircds, unrealircd's
480 			 *    unknown prefix kills (nick(?) <- someserver)
481 			 *    will fix it up, but we do not kill unknown
482 			 *    prefixes.
483 			 * 2. SVSKILL is ignored if we are not U:lined.
484 			 *
485 			 * If the SVSKILL is effective, the KILL will be
486 			 * dropped.
487 			 */
488 		}
489 		sts(":%s KILL %s :%s!%s (%s)", CLIENT_NAME(killer), id, killer->host, killer->nick, reason);
490 	}
491 	else
492 		sts(":%s KILL %s :%s (%s)", ME, id, me.name, reason);
493 }
494 
495 /* PART wrapper */
unreal_part_sts(channel_t * c,user_t * u)496 static void unreal_part_sts(channel_t *c, user_t *u)
497 {
498 	sts(":%s PART %s", CLIENT_NAME(u), c->name);
499 }
500 
501 /* server-to-server KLINE wrapper */
unreal_kline_sts(const char * server,const char * user,const char * host,long duration,const char * reason)502 static void unreal_kline_sts(const char *server, const char *user, const char *host, long duration, const char *reason)
503 {
504 	service_t *svs;
505 
506 	svs = service_find("operserv");
507 	sts(":%s TKL + G %s %s %s %lu %lu :%s", ME, user, host, svs != NULL ? svs->nick : me.name, (unsigned long)(duration > 0 ? CURRTIME + duration : 0), (unsigned long)CURRTIME, reason);
508 }
509 
510 /* server-to-server UNKLINE wrapper */
unreal_unkline_sts(const char * server,const char * user,const char * host)511 static void unreal_unkline_sts(const char *server, const char *user, const char *host)
512 {
513 	service_t *svs;
514 
515 	svs = service_find("operserv");
516 	sts(":%s TKL - G %s %s %s", ME, user, host, svs != NULL ? svs->nick : me.name);
517 }
518 
unreal_xline_sts(const char * server,const char * realname,long duration,const char * reason)519 static void unreal_xline_sts(const char *server, const char *realname, long duration, const char *reason)
520 {
521 	char escapedreason[512], *p;
522 
523 	if (duration > 0)
524 	{
525 		slog(LG_INFO, "SGLINE: Could not set temporary SGLINE on \2%s\2, not supported by unrealircd.", realname);
526 		return;
527 	}
528 
529 	mowgli_strlcpy(escapedreason, reason, sizeof escapedreason);
530 	for (p = escapedreason; *p != '\0'; p++)
531 		if (*p == ' ')
532 			*p = '_';
533 	if (*escapedreason == ':')
534 		*escapedreason = ';';
535 
536 	sts(":%s SVSNLINE + %s :%s", ME, escapedreason, realname);
537 }
538 
unreal_unxline_sts(const char * server,const char * realname)539 static void unreal_unxline_sts(const char *server, const char *realname)
540 {
541 	sts(":%s SVSNLINE - :%s", ME, realname);
542 }
543 
unreal_qline_sts(const char * server,const char * name,long duration,const char * reason)544 static void unreal_qline_sts(const char *server, const char *name, long duration, const char *reason)
545 {
546 	service_t *svs;
547 
548 	if (*name == '#' || *name == '&')
549 	{
550 		slog(LG_INFO, "SQLINE: Could not set SQLINE on \2%s\2, not supported by unrealircd.", name);
551 		return;
552 	}
553 
554 	svs = service_find("operserv");
555 	sts(":%s TKL + Q * %s %s %lu %lu :%s", ME, name, svs != NULL ? svs->nick : me.name, (unsigned long)(duration > 0 ? CURRTIME + duration : 0), (unsigned long)CURRTIME, reason);
556 }
557 
unreal_unqline_sts(const char * server,const char * name)558 static void unreal_unqline_sts(const char *server, const char *name)
559 {
560 	service_t *svs;
561 
562 	svs = service_find("operserv");
563 	sts(":%s TKL - Q * %s %s", ME, name, svs != NULL ? svs->nick : me.name);
564 }
565 
566 /* topic wrapper */
unreal_topic_sts(channel_t * c,user_t * source,const char * setter,time_t ts,time_t prevts,const char * topic)567 static void unreal_topic_sts(channel_t *c, user_t *source, const char *setter, time_t ts, time_t prevts, const char *topic)
568 {
569 	return_if_fail(c != NULL);
570 	return_if_fail(source != NULL);
571 
572 	sts(":%s TOPIC %s %s %lu :%s", source->nick, c->name, setter, (unsigned long)ts, topic);
573 }
574 
575 /* mode wrapper */
unreal_mode_sts(char * sender,channel_t * target,char * modes)576 static void unreal_mode_sts(char *sender, channel_t *target, char *modes)
577 {
578 	return_if_fail(sender != NULL);
579 	return_if_fail(target != NULL);
580 	return_if_fail(modes != NULL);
581 
582 	sts(":%s MODE %s %s", sender, target->name, modes);
583 }
584 
585 /* ping wrapper */
unreal_ping_sts(void)586 static void unreal_ping_sts(void)
587 {
588 	sts("PING :%s", ME);
589 }
590 
591 /* protocol-specific stuff to do on login */
unreal_on_login(user_t * u,myuser_t * account,const char * wantedhost)592 static void unreal_on_login(user_t *u, myuser_t *account, const char *wantedhost)
593 {
594 	return_if_fail(u != NULL);
595 	return_if_fail(account != NULL);
596 
597 	if (!use_esvid)
598 	{
599 		/* Can only do this for nickserv, and can only record identified
600 		 * state if logged in to correct nick, sorry -- jilles
601 		 */
602 		/* imo, we should be using SVS2MODE to show the modechange here and on logout --w00t */
603 		if (should_reg_umode(u))
604 			sts(":%s SVS2MODE %s +rd %lu", nicksvs.nick, u->nick, (unsigned long)u->ts);
605 
606 		return;
607 	}
608 
609 	if (should_reg_umode(u))
610 		sts(":%s SVS2MODE %s +rd %s", nicksvs.nick, u->nick, entity(account)->name);
611 	else
612 		sts(":%s SVS2MODE %s +d %s", nicksvs.nick, u->nick, entity(account)->name);
613 }
614 
615 /* protocol-specific stuff to do on logout */
unreal_on_logout(user_t * u,const char * account)616 static bool unreal_on_logout(user_t *u, const char *account)
617 {
618 	return_val_if_fail(u != NULL, false);
619 
620 	if (use_esvid || !nicksvs.no_nick_ownership)
621 		sts(":%s SVS2MODE %s -r+d 0", nicksvs.nick, u->nick);
622 
623 	return false;
624 }
625 
unreal_jupe(const char * server,const char * reason)626 static void unreal_jupe(const char *server, const char *reason)
627 {
628 	service_t *svs;
629 
630 	server_delete(server);
631 
632 	svs = service_find("operserv");
633 	sts(":%s SQUIT %s :%s", svs != NULL ? svs->nick : ME, server, reason);
634 	sts(":%s SERVER %s 2 :%s", me.name, server, reason);
635 }
636 
unreal_sethost_sts(user_t * source,user_t * target,const char * host)637 static void unreal_sethost_sts(user_t *source, user_t *target, const char *host)
638 {
639 	sts(":%s CHGHOST %s :%s", source->nick, target->nick, host);
640 
641 	if (irccasecmp(target->host, host))
642 		numeric_sts(me.me, 396, target, "%s :is now your hidden host (set by %s)", host, source->nick);
643 	else
644 	{
645 		numeric_sts(me.me, 396, target, "%s :hostname reset by %s", host, source->nick);
646 		sts(":%s SVS2MODE %s +x", source->nick, target->nick);
647 	}
648 }
649 
unreal_fnc_sts(user_t * source,user_t * u,const char * newnick,int type)650 static void unreal_fnc_sts(user_t *source, user_t *u, const char *newnick, int type)
651 {
652 	sts(":%s SVSNICK %s %s %lu", ME, CLIENT_NAME(u), newnick,
653 			(unsigned long)(CURRTIME - 60));
654 }
655 
unreal_holdnick_sts(user_t * source,int duration,const char * nick,myuser_t * mu)656 static void unreal_holdnick_sts(user_t *source, int duration, const char *nick, myuser_t *mu)
657 {
658 	if (duration > 0)
659 		sts(":%s TKL + Q H %s %s %lu %lu :Reserved by %s for nickname owner (%s)",
660 				ME, nick, source->nick,
661 				(unsigned long)(CURRTIME + duration),
662 				(unsigned long)CURRTIME,
663 				source->nick,
664 				mu ? entity(mu)->name : nick);
665 	else
666 		sts(":%s TKL - Q H %s %s", ME, nick, source->nick);
667 }
668 
unreal_quarantine_sts(user_t * source,user_t * victim,long duration,const char * reason)669 static void unreal_quarantine_sts(user_t *source, user_t *victim, long duration, const char *reason)
670 {
671 	sts(":%s SHUN +*@%s %ld :%s", source->nick, victim->host, duration, reason);
672 }
673 
unreal_sasl_sts(char * target,char mode,char * data)674 static void unreal_sasl_sts(char *target, char mode, char *data)
675 {
676 	char servermask[BUFSIZE], *p;
677 	service_t *saslserv;
678 
679 	saslserv = service_find("saslserv");
680 	if (saslserv == NULL)
681 		return;
682 
683 	/* extract the servername from the target. */
684 	mowgli_strlcpy(servermask, target, sizeof servermask);
685 	p = strchr(servermask, '!');
686 	if (p != NULL)
687 		*p = '\0';
688 
689 	sts(":%s SASL %s %s %c %s", saslserv->me->nick, servermask, target, mode, data);
690 }
691 
unreal_svslogin_sts(char * target,char * nick,char * user,char * host,myuser_t * account)692 static void unreal_svslogin_sts(char *target, char *nick, char *user, char *host, myuser_t *account)
693 {
694 	char servermask[BUFSIZE], *p;
695 	service_t *saslserv;
696 
697 	saslserv = service_find("saslserv");
698 	if (saslserv == NULL)
699 		return;
700 
701 	/* extract the servername from the target. */
702 	mowgli_strlcpy(servermask, target, sizeof servermask);
703 	p = strchr(servermask, '!');
704 	if (p != NULL)
705 		*p = '\0';
706 
707 	sts(":%s SVSLOGIN %s %s %s", saslserv->me->nick, servermask, target, entity(account)->name);
708 }
709 
unreal_mlock_sts(channel_t * c)710 static void unreal_mlock_sts(channel_t *c)
711 {
712 	mychan_t *mc = mychan_from(c);
713 
714 	if (use_mlock == false)
715 		return;
716 
717 	if (mc == NULL)
718 		return;
719 
720 	sts(":%s MLOCK %lu %s :%s", ME, (unsigned long)c->ts, c->name,
721 				    mychan_get_sts_mlock(mc));
722 }
723 
m_mlock(sourceinfo_t * si,int parc,char * parv[])724 static void m_mlock(sourceinfo_t *si, int parc, char *parv[])
725 {
726 	channel_t *c;
727 	mychan_t *mc;
728 	const char *mlock;
729 
730 	/* Ignore MLOCK if the server isn't bursting, to avoid 'war' conditions */
731 	if (si->s->flags & SF_EOB)
732 		return;
733 
734 	if (!(c = channel_find(parv[1])))
735 		return;
736 
737 	if (!(mc = mychan_from(c)))
738 	{
739 		/* Unregistered channel. Clear the MLOCK. */
740 		sts(":%s MLOCK %lu %s :", ME, (unsigned long)c->ts, c->name);
741 		return;
742 	}
743 
744 	time_t ts = atol(parv[0]);
745 	if (ts > c->ts)
746 		return;
747 
748 	mlock = mychan_get_sts_mlock(mc);
749 	if (0 != strcmp(parv[2], mlock))
750 	{
751 		/* MLOCK is changing, with the same TS. Bounce back the correct one. */
752 		sts(":%s MLOCK %lu %s :%s", ME, (unsigned long)c->ts, c->name, mlock);
753 	}
754 }
755 
m_sasl(sourceinfo_t * si,int parc,char * parv[])756 static void m_sasl(sourceinfo_t *si, int parc, char *parv[])
757 {
758 	sasl_message_t smsg;
759 
760 	/* :irc.loldongs.eu SASL * irc.goatse.cx!42 S d29vTklOSkFTAGRhdGEgaW4gZmlyc3QgbGluZQ== */
761 	if (parc < 4)
762 		return;
763 
764 	smsg.uid = parv[1];
765 	smsg.mode = *parv[2];
766 	smsg.buf = parv[3];
767 	smsg.ext = parc >= 4 ? parv[4] : NULL;
768 	smsg.server = si->s ? si->s : NULL;
769 
770 	hook_call_sasl_input(&smsg);
771 }
772 
m_topic(sourceinfo_t * si,int parc,char * parv[])773 static void m_topic(sourceinfo_t *si, int parc, char *parv[])
774 {
775 	channel_t *c = channel_find(parv[0]);
776 
777 	if (!c)
778 		return;
779 
780 	/* Our uplink is trying to change the topic during burst,
781 	 * and we have already set a topic. Assume our change won.
782 	 * -- jilles */
783 	if (si->s != NULL && si->s->uplink == me.me &&
784 			!(si->s->flags & SF_EOB) && c->topic != NULL)
785 		return;
786 
787 	handle_topic_from(si, c, parv[1], atol(parv[2]), parv[3]);
788 }
789 
m_ping(sourceinfo_t * si,int parc,char * parv[])790 static void m_ping(sourceinfo_t *si, int parc, char *parv[])
791 {
792 	/* reply to PING's */
793 	sts(":%s PONG %s %s", ME, me.name, parv[0]);
794 }
795 
m_pong(sourceinfo_t * si,int parc,char * parv[])796 static void m_pong(sourceinfo_t *si, int parc, char *parv[])
797 {
798 	server_t *s;
799 
800 	/* someone replied to our PING */
801 	if (!parv[0])
802 		return;
803 	s = server_find(parv[0]);
804 	if (s == NULL)
805 		return;
806 	handle_eob(s);
807 
808 	if (s != si->s)
809 		return;
810 
811 	me.uplinkpong = CURRTIME;
812 
813 	/* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
814 	if (me.bursting)
815 	{
816 #ifdef HAVE_GETTIMEOFDAY
817 		e_time(burstime, &burstime);
818 
819 		slog(LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms(&burstime) > 1000) ? (tv2ms(&burstime) / 1000) : tv2ms(&burstime), (tv2ms(&burstime) > 1000) ? "s" : "ms");
820 
821 		wallops("Finished synchronizing with network in %d %s.", (tv2ms(&burstime) > 1000) ? (tv2ms(&burstime) / 1000) : tv2ms(&burstime), (tv2ms(&burstime) > 1000) ? "s" : "ms");
822 #else
823 		slog(LG_INFO, "m_pong(): finished synching with uplink");
824 		wallops("Finished synchronizing with network.");
825 #endif
826 
827 		me.bursting = false;
828 	}
829 }
830 
m_privmsg(sourceinfo_t * si,int parc,char * parv[])831 static void m_privmsg(sourceinfo_t *si, int parc, char *parv[])
832 {
833 	if (parc != 2)
834 		return;
835 
836 	handle_message(si, parv[0], false, parv[1]);
837 }
838 
m_notice(sourceinfo_t * si,int parc,char * parv[])839 static void m_notice(sourceinfo_t *si, int parc, char *parv[])
840 {
841 	if (parc != 2)
842 		return;
843 
844 	handle_message(si, parv[0], true, parv[1]);
845 }
846 
remove_our_modes(channel_t * c)847 static void remove_our_modes(channel_t *c)
848 {
849 	/* the TS changed.  a TS change requires the following things
850 	 * to be done to the channel:  reset all modes to nothing, remove
851 	 * all status modes on known users on the channel (including ours),
852 	 * and set the new TS.
853 	 */
854 	chanuser_t *cu;
855 	mowgli_node_t *n;
856 
857 	clear_simple_modes(c);
858 
859 	MOWGLI_ITER_FOREACH(n, c->members.head)
860 	{
861 		cu = (chanuser_t *)n->data;
862 		cu->modes = 0;
863 	}
864 }
865 
m_sjoin(sourceinfo_t * si,int parc,char * parv[])866 static void m_sjoin(sourceinfo_t *si, int parc, char *parv[])
867 {
868 	/*
869 	 *  -> :proteus.malkier.net SJOIN 1073516550 #shrike +tn :@sycobuny @+rakaur
870 	 *	also:
871 	 *  -> :nenolod_ SJOIN 1117334567 #chat
872 	 *	also:
873 	 *  -> SJOIN 1117334567 #chat :@nenolod
874 	 */
875 
876 	channel_t *c;
877 	unsigned int userc;
878 	char *userv[256];
879 	unsigned int i;
880 	time_t ts;
881 
882 	if (parc >= 4)
883 	{
884 		/* :origin SJOIN ts chan modestr [key or limits] :users */
885 		c = channel_find(parv[1]);
886 		ts = atol(parv[0]);
887 
888 		if (!c)
889 		{
890 			slog(LG_DEBUG, "m_sjoin(): new channel: %s", parv[1]);
891 			c = channel_add(parv[1], ts, si->s);
892 		}
893 
894 		if (ts < c->ts)
895 		{
896 			remove_our_modes(c);
897 			slog(LG_DEBUG, "m_sjoin(): TS changed for %s (%lu -> %lu)", c->name, (unsigned long)c->ts, (unsigned long)ts);
898 			c->ts = ts;
899 			hook_call_channel_tschange(c);
900 		}
901 
902 		channel_mode(NULL, c, parc - 3, parv + 2);
903 		userc = sjtoken(parv[parc - 1], ' ', userv);
904 
905 		for (i = 0; i < userc; i++)
906 			if (*userv[i] == '&')	/* channel ban */
907 				chanban_add(c, userv[i] + 1, 'b');
908 			else if (*userv[i] == '"')	/* exception */
909 				chanban_add(c, userv[i] + 1, 'e');
910 			else if (*userv[i] == '\'')	/* invex */
911 				chanban_add(c, userv[i] + 1, 'I');
912 			else
913 				chanuser_add(c, userv[i]);
914 	}
915 	else if (parc == 3)
916 	{
917 		/* :origin SJOIN ts chan :users */
918 		c = channel_find(parv[1]);
919 		ts = atol(parv[0]);
920 
921 		if (!c)
922 		{
923 			slog(LG_DEBUG, "m_sjoin(): new channel: %s (modes lost)", parv[1]);
924 			c = channel_add(parv[1], ts, si->s);
925 		}
926 
927 		if (ts < c->ts)
928 		{
929 			remove_our_modes(c);
930 			slog(LG_DEBUG, "m_sjoin(): TS changed for %s (%lu -> %lu)", c->name, (unsigned long)c->ts, (unsigned long)ts);
931 			c->ts = ts;
932 			hook_call_channel_tschange(c);
933 		}
934 
935 		channel_mode_va(NULL, c, 1, "+");
936 
937 		userc = sjtoken(parv[parc - 1], ' ', userv);
938 
939 		for (i = 0; i < userc; i++)
940 			if (*userv[i] == '&')	/* channel ban */
941 				chanban_add(c, userv[i] + 1, 'b');
942 			else if (*userv[i] == '"')	/* exception */
943 				chanban_add(c, userv[i] + 1, 'e');
944 			else if (*userv[i] == '\'')	/* invex */
945 				chanban_add(c, userv[i] + 1, 'I');
946 			else
947 				chanuser_add(c, userv[i]);
948 	}
949 	else if (parc == 2)
950 	{
951 		c = channel_find(parv[1]);
952 		ts = atol(parv[0]);
953 		if (!c)
954 		{
955 			slog(LG_DEBUG, "m_sjoin(): new channel: %s (modes lost)", parv[1]);
956 			c = channel_add(parv[1], ts, si->su->server);
957 		}
958 
959 		if (ts < c->ts)
960 		{
961 			remove_our_modes(c);
962 			slog(LG_DEBUG, "m_sjoin(): TS changed for %s (%lu -> %lu)", c->name, (unsigned long)c->ts, (unsigned long)ts);
963 			c->ts = ts;
964 			/* XXX lost modes! -- XXX - pardon? why do we worry about this? TS reset requires modes reset.. */
965 			hook_call_channel_tschange(c);
966 		}
967 
968 		channel_mode_va(NULL, c, 1, "+");
969 
970 		chanuser_add(c, si->su->nick);
971 	}
972 	else
973 		return;
974 
975 	if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
976 		channel_delete(c);
977 }
978 
m_part(sourceinfo_t * si,int parc,char * parv[])979 static void m_part(sourceinfo_t *si, int parc, char *parv[])
980 {
981 	int chanc;
982 	char *chanv[256];
983 	int i;
984 
985 	chanc = sjtoken(parv[0], ',', chanv);
986 	for (i = 0; i < chanc; i++)
987 	{
988 		slog(LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
989 
990 		chanuser_delete(channel_find(chanv[i]), si->su);
991 	}
992 }
993 
m_uid(sourceinfo_t * si,int parc,char * parv[])994 static void m_uid(sourceinfo_t *si, int parc, char *parv[])
995 {
996 	server_t *s;
997 	user_t *u;
998 	bool realchange;
999 	const char *vhost;
1000 	const char *ipb64;
1001 	char ipstring[64];
1002 	int af;
1003 	size_t iplen;
1004 	char ipdata[16];
1005 
1006 	if (parc == 12)
1007 	{
1008 		s = si->s;
1009 		if (!s)
1010 		{
1011 			slog(LG_DEBUG, "m_uid(): new user on nonexistant server: %s", parv[0]);
1012 			return;
1013 		}
1014 
1015 		slog(LG_DEBUG, "m_uid(): new user on `%s': %s", s->name, si->s->name);
1016 
1017 		vhost = strcmp(parv[8], "*") ? parv[8] : NULL;
1018 		iplen = 0;
1019 		if (parc == 11 && strcmp(parv[parc - 2], "*"))
1020 		{
1021 			ipb64 = parv[parc - 2];
1022 			af = AF_INET;
1023 			if (strlen(ipb64) == 8)
1024 			{
1025 				iplen = 4;
1026 				if (base64_decode(ipb64, ipdata, iplen) != iplen)
1027 					iplen = 0;
1028 				af = AF_INET;
1029 			}
1030 #ifdef AF_INET6
1031 			else if (strlen(ipb64) == 24)
1032 			{
1033 				iplen = 16;
1034 				if (base64_decode(ipb64, ipdata, iplen) != iplen)
1035 					iplen = 0;
1036 				af = AF_INET6;
1037 			}
1038 #endif
1039 			if (iplen != 0)
1040 				if (!inet_ntop(af, ipdata, ipstring, sizeof ipstring))
1041 					iplen = 0;
1042 		}
1043 		u = user_add(parv[0], parv[3], parv[4], vhost, iplen != 0 ? ipstring : NULL, parv[5], parv[parc - 1], s, atoi(parv[2]));
1044 		if (u == NULL)
1045 			return;
1046 
1047 		user_mode(u, parv[7]);
1048 
1049 		/*
1050 		 * with ESVID:
1051 		 * If the user's SVID is equal to their accountname,
1052 		 * they're properly logged in.	Alternatively, the
1053 		 * 'without ESVID' criteria is used. --nenolod
1054 		 *
1055 		 * without ESVID:
1056 		 * If the user's SVID is equal to their nick TS,
1057 		 * they're properly logged in --jilles
1058 		 */
1059 		if (use_esvid && !IsDigit(*parv[6]))
1060 		{
1061 			handle_burstlogin(u, parv[6], 0);
1062 
1063 			if (authservice_loaded && should_reg_umode(u))
1064 				sts(":%s SVS2MODE %s +r", nicksvs.nick, u->nick);
1065 		}
1066 		else if (u->ts > 100 && (time_t)atoi(parv[6]) == u->ts)
1067 			handle_burstlogin(u, NULL, 0);
1068 
1069 		handle_nickchange(u);
1070 	}
1071 	else
1072 	{
1073 		int i;
1074 		slog(LG_DEBUG, "m_uid(): got UID with wrong number of params");
1075 
1076 		for (i = 0; i < parc; i++)
1077 			slog(LG_DEBUG, "m_uid():   parv[%d] = %s", i, parv[i]);
1078 	}
1079 }
1080 
m_nick(sourceinfo_t * si,int parc,char * parv[])1081 static void m_nick(sourceinfo_t *si, int parc, char *parv[])
1082 {
1083 	server_t *s;
1084 	user_t *u;
1085 	bool realchange;
1086 	const char *vhost;
1087 	const char *ipb64;
1088 	char ipstring[64];
1089 	int af;
1090 	size_t iplen;
1091 	char ipdata[16];
1092 
1093 	if (parc > 10)
1094 	{
1095 		s = server_find(parv[5]);
1096 		if (!s)
1097 		{
1098 			slog(LG_DEBUG, "m_nick(): new user on nonexistant server: %s", parv[5]);
1099 			return;
1100 		}
1101 
1102 		slog(LG_DEBUG, "m_nick(): new user on `%s': %s", s->name, parv[0]);
1103 
1104 		vhost = strcmp(parv[8], "*") ? parv[8] : NULL;
1105 		iplen = 0;
1106 		if (parc == 11 && strcmp(parv[parc - 2], "*"))
1107 		{
1108 			ipb64 = parv[parc - 2];
1109 			af = AF_INET;
1110 			if (strlen(ipb64) == 8)
1111 			{
1112 				iplen = 4;
1113 				if (base64_decode(ipb64, ipdata, iplen) != iplen)
1114 					iplen = 0;
1115 				af = AF_INET;
1116 			}
1117 #ifdef AF_INET6
1118 			else if (strlen(ipb64) == 24)
1119 			{
1120 				iplen = 16;
1121 				if (base64_decode(ipb64, ipdata, iplen) != iplen)
1122 					iplen = 0;
1123 				af = AF_INET6;
1124 			}
1125 #endif
1126 			if (iplen != 0)
1127 				if (!inet_ntop(af, ipdata, ipstring, sizeof ipstring))
1128 					iplen = 0;
1129 		}
1130 		u = user_add(parv[0], parv[3], parv[4], vhost, iplen != 0 ? ipstring : NULL, NULL, parv[parc - 1], s, atoi(parv[2]));
1131 		if (u == NULL)
1132 			return;
1133 
1134 		user_mode(u, parv[7]);
1135 
1136 		/*
1137 		 * with ESVID:
1138 		 * If the user's SVID is equal to their accountname,
1139 		 * they're properly logged in.	Alternatively, the
1140 		 * 'without ESVID' criteria is used. --nenolod
1141 		 *
1142 		 * without ESVID:
1143 		 * If the user's SVID is equal to their nick TS,
1144 		 * they're properly logged in --jilles
1145 		 */
1146 		if (use_esvid && !IsDigit(*parv[6]))
1147 		{
1148 			handle_burstlogin(u, parv[6], 0);
1149 
1150 			if (authservice_loaded && should_reg_umode(u))
1151 				sts(":%s SVS2MODE %s +r", nicksvs.nick, u->nick);
1152 		}
1153 		else if (u->ts > 100 && (time_t)atoi(parv[6]) == u->ts)
1154 			handle_burstlogin(u, NULL, 0);
1155 
1156 		handle_nickchange(u);
1157 	}
1158 
1159 	/* if it's only 2 then it's a nickname change */
1160 	else if (parc == 2)
1161 	{
1162 		if (!si->su)
1163 		{
1164 			slog(LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
1165 			return;
1166 		}
1167 
1168 		slog(LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
1169 
1170 		realchange = irccasecmp(si->su->nick, parv[0]);
1171 
1172 		if (user_changenick(si->su, parv[0], atoi(parv[1])))
1173 			return;
1174 
1175 		/* fix up +r if necessary -- jilles */
1176 		if (realchange && !nicksvs.no_nick_ownership && !use_esvid)
1177 		{
1178 			if (should_reg_umode(si->su))
1179 				/* changed nick to registered one, reset +r */
1180 				sts(":%s SVS2MODE %s +rd %lu", nicksvs.nick, parv[0], atol(parv[1]));
1181 			else
1182 				/* changed from registered nick, remove +r */
1183 				sts(":%s SVS2MODE %s -r+d 0", nicksvs.nick, parv[0]);
1184 		}
1185 		else if (realchange && !nicksvs.no_nick_ownership && use_esvid)
1186 		{
1187 			if (should_reg_umode(si->su))
1188 				sts(":%s SVS2MODE %s +r", nicksvs.nick, parv[0]);
1189 			else
1190 				sts(":%s SVS2MODE %s -r", nicksvs.nick, parv[0]);
1191 		}
1192 
1193 		handle_nickchange(si->su);
1194 	}
1195 	else
1196 	{
1197 		int i;
1198 		slog(LG_DEBUG, "m_nick(): got NICK with wrong number of params");
1199 
1200 		for (i = 0; i < parc; i++)
1201 			slog(LG_DEBUG, "m_nick():   parv[%d] = %s", i, parv[i]);
1202 	}
1203 }
1204 
m_quit(sourceinfo_t * si,int parc,char * parv[])1205 static void m_quit(sourceinfo_t *si, int parc, char *parv[])
1206 {
1207 	slog(LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
1208 
1209 	/* user_delete() takes care of removing channels and so forth */
1210 	user_delete(si->su, parv[0]);
1211 }
1212 
unreal_user_mode(user_t * u,const char * changes)1213 static void unreal_user_mode(user_t *u, const char *changes)
1214 {
1215 	const char *p;
1216 	int dir;
1217 
1218 	if (u == NULL)
1219 		return;
1220 	user_mode(u, changes);
1221 	dir = 0;
1222 	for (p = changes; *p != '\0'; p++)
1223 		switch (*p)
1224 		{
1225 			case '-': dir = MTYPE_DEL; break;
1226 			case '+': dir = MTYPE_ADD; break;
1227 			case 'x':
1228 				/* If +x is set then the users vhost is set to their cloaked host - Adam */
1229 				if (dir == MTYPE_ADD)
1230 				{
1231 					/* It is possible for the users vhost to not be their cloaked host after +x.
1232 					 * This only occurs when a user is introduced after a netmerge with their
1233 					 * vhost instead of their cloaked host. - Adam
1234 					 */
1235 					if (strcmp(u->vhost, u->chost))
1236 					{
1237 						strshare_unref(u->chost);
1238 						u->chost = strshare_get(u->vhost);
1239 					}
1240 				}
1241 				else if (dir == MTYPE_DEL)
1242 				{
1243 					strshare_unref(u->vhost);
1244 					u->vhost = strshare_get(u->host);
1245 				}
1246 				break;
1247 		}
1248 }
1249 
unreal_is_extban(const char * mask)1250 static bool unreal_is_extban(const char *mask)
1251 {
1252 	const char mask_len = strlen(mask);
1253 	unsigned char offset = 0;
1254 
1255 	if (mask_len < 4 || mask[0] != '~' || mask[2] != ':' || strchr(mask, ' '))
1256 		return false;
1257 
1258 	if ((mask[1] < 'a' || mask[1] > 'z') && (mask[1] < 'A' || mask[1] > 'Z'))
1259 		return false;
1260 
1261 	return true;
1262 }
1263 
m_mode(sourceinfo_t * si,int parc,char * parv[])1264 static void m_mode(sourceinfo_t *si, int parc, char *parv[])
1265 {
1266 	if (*parv[0] == '#')
1267 		channel_mode(NULL, channel_find(parv[0]), parc - 1, &parv[1]);
1268 	else
1269 		unreal_user_mode(user_find(parv[0]), parv[1]);
1270 }
1271 
m_umode(sourceinfo_t * si,int parc,char * parv[])1272 static void m_umode(sourceinfo_t *si, int parc, char *parv[])
1273 {
1274 	unreal_user_mode(si->su, parv[0]);
1275 }
1276 
m_kick(sourceinfo_t * si,int parc,char * parv[])1277 static void m_kick(sourceinfo_t *si, int parc, char *parv[])
1278 {
1279 	user_t *u = user_find(parv[1]);
1280 	channel_t *c = channel_find(parv[0]);
1281 
1282 	/* -> :rakaur KICK #shrike rintaun :test */
1283 	slog(LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
1284 
1285 	if (!u)
1286 	{
1287 		slog(LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
1288 		return;
1289 	}
1290 
1291 	if (!c)
1292 	{
1293 		slog(LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
1294 		return;
1295 	}
1296 
1297 	if (!chanuser_find(c, u))
1298 	{
1299 		slog(LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
1300 		return;
1301 	}
1302 
1303 	chanuser_delete(c, u);
1304 
1305 	/* if they kicked us, let's rejoin */
1306 	if (is_internal_client(u))
1307 	{
1308 		slog(LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
1309 		join(parv[0], u->nick);
1310 	}
1311 }
1312 
m_kill(sourceinfo_t * si,int parc,char * parv[])1313 static void m_kill(sourceinfo_t *si, int parc, char *parv[])
1314 {
1315 	handle_kill(si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
1316 }
1317 
m_squit(sourceinfo_t * si,int parc,char * parv[])1318 static void m_squit(sourceinfo_t *si, int parc, char *parv[])
1319 {
1320 	slog(LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
1321 	server_delete(parv[0]);
1322 }
1323 
m_server(sourceinfo_t * si,int parc,char * parv[])1324 static void m_server(sourceinfo_t *si, int parc, char *parv[])
1325 {
1326 	server_t *s;
1327 	const char *inf;
1328 
1329 	/* because multiple PROTOCTL messages are allowed without a PROTOCTL END,
1330 	 * and even if we added a PROTOCTL END in unreal 3.4, we'd still have to do
1331 	 * this hack in order to support 3.2... -- kaniini
1332 	 */
1333 	if (has_protoctl)
1334 	{
1335 		if (ts6sid[0] == '\0')
1336 		{
1337 			ircd->uses_uid = false;
1338 			if (me.me->sid)
1339 			{
1340 				/* XXX: this is a hack that fixes *most*, though not
1341 				 * all, of the brokenness when linking to Unreal 3.2
1342 				 *
1343 				 * (the EOB is sent before this, therefore still with
1344 				 * a SID, but apparently still works) --grawity */
1345 				slog(LG_DEBUG, "m_server(): erasing our SID");
1346 				free(me.me->sid);
1347 				me.me->sid = NULL;
1348 			}
1349 		}
1350 
1351 		services_init();
1352 		has_protoctl = false;	/* only once after PROTOCTL message. */
1353 	}
1354 
1355 	slog(LG_DEBUG, "m_server(): new server: %s", parv[0]);
1356 	if (si->s == NULL && (inf = strchr(parv[2], ' ')) != NULL)
1357 		inf++;
1358 	else
1359 		inf = parv[2];
1360 	s = handle_server(si, parv[0], si->s || !ircd->uses_uid ? NULL : ts6sid, atoi(parv[1]), inf);
1361 
1362 	if (s != NULL && s->uplink != me.me)
1363 	{
1364 		/* elicit PONG for EOB detection; pinging uplink is
1365 		 * already done elsewhere -- jilles
1366 		 */
1367 		sts(":%s PING %s %s", ME, me.name, s->name);
1368 	}
1369 }
1370 
m_sid(sourceinfo_t * si,int parc,char * parv[])1371 static void m_sid(sourceinfo_t *si, int parc, char *parv[])
1372 {
1373 	server_t *s;
1374 	const char *inf;
1375 
1376 	s = handle_server(si, parv[0], parv[2], atoi(parv[1]), parv[3]);
1377 
1378 	if (s != NULL && s->uplink != me.me)
1379 	{
1380 		/* elicit PONG for EOB detection; pinging uplink is
1381 		 * already done elsewhere -- jilles
1382 		 */
1383 		sts(":%s PING %s %s", ME, me.name, s->sid);
1384 	}
1385 }
1386 
m_stats(sourceinfo_t * si,int parc,char * parv[])1387 static void m_stats(sourceinfo_t *si, int parc, char *parv[])
1388 {
1389 	handle_stats(si->su, parv[0][0]);
1390 }
1391 
m_admin(sourceinfo_t * si,int parc,char * parv[])1392 static void m_admin(sourceinfo_t *si, int parc, char *parv[])
1393 {
1394 	handle_admin(si->su);
1395 }
1396 
m_version(sourceinfo_t * si,int parc,char * parv[])1397 static void m_version(sourceinfo_t *si, int parc, char *parv[])
1398 {
1399 	handle_version(si->su);
1400 }
1401 
m_info(sourceinfo_t * si,int parc,char * parv[])1402 static void m_info(sourceinfo_t *si, int parc, char *parv[])
1403 {
1404 	handle_info(si->su);
1405 }
1406 
m_whois(sourceinfo_t * si,int parc,char * parv[])1407 static void m_whois(sourceinfo_t *si, int parc, char *parv[])
1408 {
1409 	handle_whois(si->su, parv[1]);
1410 }
1411 
m_trace(sourceinfo_t * si,int parc,char * parv[])1412 static void m_trace(sourceinfo_t *si, int parc, char *parv[])
1413 {
1414 	handle_trace(si->su, parv[0], parc >= 2 ? parv[1] : NULL);
1415 }
1416 
m_away(sourceinfo_t * si,int parc,char * parv[])1417 static void m_away(sourceinfo_t *si, int parc, char *parv[])
1418 {
1419 	handle_away(si->su, parc >= 1 ? parv[0] : NULL);
1420 }
1421 
m_join(sourceinfo_t * si,int parc,char * parv[])1422 static void m_join(sourceinfo_t *si, int parc, char *parv[])
1423 {
1424 	chanuser_t *cu;
1425 	mowgli_node_t *n, *tn;
1426 
1427 	/* JOIN 0 is really a part from all channels */
1428 	if (parv[0][0] == '0')
1429 	{
1430 		MOWGLI_ITER_FOREACH_SAFE(n, tn, si->su->channels.head)
1431 		{
1432 			cu = (chanuser_t *)n->data;
1433 			chanuser_delete(cu->chan, si->su);
1434 		}
1435 	}
1436 }
1437 
m_pass(sourceinfo_t * si,int parc,char * parv[])1438 static void m_pass(sourceinfo_t *si, int parc, char *parv[])
1439 {
1440 	if (strcmp(curr_uplink->receive_pass, parv[0]))
1441 	{
1442 		slog(LG_INFO, "m_pass(): password mismatch from uplink; aborting");
1443 		runflags |= RF_SHUTDOWN;
1444 	}
1445 }
1446 
m_error(sourceinfo_t * si,int parc,char * parv[])1447 static void m_error(sourceinfo_t *si, int parc, char *parv[])
1448 {
1449 	slog(LG_INFO, "m_error(): error from server: %s", parv[0]);
1450 }
1451 
m_sethost(sourceinfo_t * si,int parc,char * parv[])1452 static void m_sethost(sourceinfo_t *si, int parc, char *parv[])
1453 {
1454 	strshare_unref(si->su->vhost);
1455 	si->su->vhost = strshare_get(parv[0]);
1456 }
1457 
m_chghost(sourceinfo_t * si,int parc,char * parv[])1458 static void m_chghost(sourceinfo_t *si, int parc, char *parv[])
1459 {
1460 	user_t *u = user_find(parv[0]);
1461 
1462 	if (!u)
1463 		return;
1464 
1465 	strshare_unref(u->vhost);
1466 	u->vhost = strshare_get(parv[1]);
1467 }
1468 
m_motd(sourceinfo_t * si,int parc,char * parv[])1469 static void m_motd(sourceinfo_t *si, int parc, char *parv[])
1470 {
1471 	handle_motd(si->su);
1472 }
1473 
nick_group(hook_user_req_t * hdata)1474 static void nick_group(hook_user_req_t *hdata)
1475 {
1476 	user_t *u;
1477 
1478 	u = hdata->si->su != NULL && !irccasecmp(hdata->si->su->nick, hdata->mn->nick) ? hdata->si->su : user_find_named(hdata->mn->nick);
1479 	if (!use_esvid && u != NULL && should_reg_umode(u))
1480 		sts(":%s SVS2MODE %s +rd %lu", nicksvs.nick, u->nick,
1481 				(unsigned long)u->ts);
1482 }
1483 
nick_ungroup(hook_user_req_t * hdata)1484 static void nick_ungroup(hook_user_req_t *hdata)
1485 {
1486 	user_t *u;
1487 
1488 	u = hdata->si->su != NULL && !irccasecmp(hdata->si->su->nick, hdata->mn->nick) ? hdata->si->su : user_find_named(hdata->mn->nick);
1489 	if (u != NULL && (!use_esvid || !nicksvs.no_nick_ownership))
1490 		sts(":%s SVS2MODE %s -r+d 0", nicksvs.nick, u->nick);
1491 }
1492 
m_protoctl(sourceinfo_t * si,int parc,char * parv[])1493 static void m_protoctl(sourceinfo_t *si, int parc, char *parv[])
1494 {
1495 	int i;
1496 
1497 	has_protoctl = true;
1498 
1499 	for (i = 0; i < parc; i++)
1500 	{
1501 		if (!irccasecmp(parv[i], "ESVID"))
1502 			use_esvid = true;
1503 		else if (!irccasecmp(parv[i], "MLOCK"))
1504 			use_mlock = true;
1505 		else if (!strncmp(parv[i], "SID=", 4))
1506 		{
1507 			ircd->uses_uid = true;
1508 			mowgli_strlcpy(ts6sid, (parv[i] + 4), sizeof(ts6sid));
1509 		}
1510 	}
1511 }
1512 
_modinit(module_t * m)1513 void _modinit(module_t * m)
1514 {
1515 	MODULE_TRY_REQUEST_DEPENDENCY(m, "transport/rfc1459");
1516 	MODULE_TRY_REQUEST_DEPENDENCY(m, "protocol/base36uid");
1517 
1518 	/* Symbol relocation voodoo. */
1519 	server_login = &unreal_server_login;
1520 	introduce_nick = &unreal_introduce_nick;
1521 	quit_sts = &unreal_quit_sts;
1522 	wallops_sts = &unreal_wallops_sts;
1523 	join_sts = &unreal_join_sts;
1524 	kick = &unreal_kick;
1525 	msg = &unreal_msg;
1526 	msg_global_sts = &unreal_msg_global_sts;
1527 	notice_user_sts = &unreal_notice_user_sts;
1528 	notice_global_sts = &unreal_notice_global_sts;
1529 	notice_channel_sts = &unreal_notice_channel_sts;
1530 	numeric_sts = &unreal_numeric_sts;
1531 	kill_id_sts = &unreal_kill_id_sts;
1532 	part_sts = &unreal_part_sts;
1533 	kline_sts = &unreal_kline_sts;
1534 	unkline_sts = &unreal_unkline_sts;
1535 	xline_sts = &unreal_xline_sts;
1536 	unxline_sts = &unreal_unxline_sts;
1537 	qline_sts = &unreal_qline_sts;
1538 	unqline_sts = &unreal_unqline_sts;
1539 	topic_sts = &unreal_topic_sts;
1540 	mode_sts = &unreal_mode_sts;
1541 	ping_sts = &unreal_ping_sts;
1542 	ircd_on_login = &unreal_on_login;
1543 	ircd_on_logout = &unreal_on_logout;
1544 	jupe = &unreal_jupe;
1545 	sethost_sts = &unreal_sethost_sts;
1546 	fnc_sts = &unreal_fnc_sts;
1547 	invite_sts = &unreal_invite_sts;
1548 	holdnick_sts = &unreal_holdnick_sts;
1549 	chan_lowerts = &unreal_chan_lowerts;
1550 	sasl_sts = &unreal_sasl_sts;
1551 	svslogin_sts = &unreal_svslogin_sts;
1552 	quarantine_sts = &unreal_quarantine_sts;
1553 	mlock_sts = &unreal_mlock_sts;
1554 	is_extban = &unreal_is_extban;
1555 
1556 	next_matching_ban = &unreal_next_matching_ban;
1557 	mode_list = unreal_mode_list;
1558 	ignore_mode_list = unreal_ignore_mode_list;
1559 	status_mode_list = unreal_status_mode_list;
1560 	prefix_mode_list = unreal_prefix_mode_list;
1561 	user_mode_list = unreal_user_mode_list;
1562 	ignore_mode_list_size = ARRAY_SIZE(unreal_ignore_mode_list);
1563 
1564 	ircd = &Unreal;
1565 
1566 	pcommand_add("PING", m_ping, 1, MSRC_USER | MSRC_SERVER);
1567 	pcommand_add("PONG", m_pong, 1, MSRC_SERVER);
1568 	pcommand_add("PRIVMSG", m_privmsg, 2, MSRC_USER);
1569 	pcommand_add("NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER);
1570 	pcommand_add("SJOIN", m_sjoin, 2, MSRC_USER | MSRC_SERVER);
1571 	pcommand_add("PART", m_part, 1, MSRC_USER);
1572 	pcommand_add("NICK", m_nick, 2, MSRC_USER | MSRC_SERVER);
1573 	pcommand_add("UID", m_uid, 10, MSRC_SERVER);
1574 	pcommand_add("QUIT", m_quit, 1, MSRC_USER);
1575 	pcommand_add("UMODE2", m_umode, 1, MSRC_USER);
1576 	pcommand_add("MODE", m_mode, 2, MSRC_USER | MSRC_SERVER);
1577 	pcommand_add("KICK", m_kick, 2, MSRC_USER | MSRC_SERVER);
1578 	pcommand_add("KILL", m_kill, 1, MSRC_USER | MSRC_SERVER);
1579 	pcommand_add("SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER);
1580 	pcommand_add("SERVER", m_server, 3, MSRC_UNREG | MSRC_SERVER);
1581 	pcommand_add("SID", m_sid, 4, MSRC_UNREG | MSRC_SERVER);
1582 	pcommand_add("STATS", m_stats, 2, MSRC_USER);
1583 	pcommand_add("ADMIN", m_admin, 1, MSRC_USER);
1584 	pcommand_add("VERSION", m_version, 1, MSRC_USER);
1585 	pcommand_add("INFO", m_info, 1, MSRC_USER);
1586 	pcommand_add("WHOIS", m_whois, 2, MSRC_USER);
1587 	pcommand_add("TRACE", m_trace, 1, MSRC_USER);
1588 	pcommand_add("AWAY", m_away, 0, MSRC_USER);
1589 	pcommand_add("JOIN", m_join, 1, MSRC_USER);
1590 	pcommand_add("PASS", m_pass, 1, MSRC_UNREG);
1591 	pcommand_add("ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER);
1592 	pcommand_add("TOPIC", m_topic, 4, MSRC_USER | MSRC_SERVER);
1593 	pcommand_add("SETHOST", m_sethost, 1, MSRC_USER);
1594 	pcommand_add("CHGHOST", m_chghost, 2, MSRC_USER | MSRC_SERVER);
1595 	pcommand_add("MOTD", m_motd, 1, MSRC_USER);
1596 	pcommand_add("PROTOCTL", m_protoctl, 1, MSRC_UNREG);
1597 	pcommand_add("SASL", m_sasl, 4, MSRC_SERVER);
1598 	pcommand_add("MLOCK", m_mlock, 3, MSRC_SERVER);
1599 
1600 	hook_add_event("nick_group");
1601 	hook_add_nick_group(nick_group);
1602 	hook_add_event("nick_ungroup");
1603 	hook_add_nick_ungroup(nick_ungroup);
1604 
1605 	m->mflags = MODTYPE_CORE;
1606 
1607 	pmodule_loaded = true;
1608 }
1609 
1610 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
1611  * vim:ts=8
1612  * vim:sw=8
1613  * vim:noexpandtab
1614  */
1615