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