1 /*
2  * Copyright (c) 2005-2006 William Pitcock, et al.
3  * Rights to this code are documented in doc/LICENSE.
4  *
5  * This file contains protocol support for P10 ircd's.
6  * Some sources used: Run's documentation, beware's description,
7  * raw data sent by nefarious.
8  *
9  */
10 
11 #include "atheme.h"
12 #include "uplink.h"
13 #include "pmodule.h"
14 #include "protocol/nefarious.h"
15 
16 DECLARE_MODULE_V1("protocol/nefarious", true, _modinit, NULL, PACKAGE_STRING, VENDOR_STRING);
17 
18 /* *INDENT-OFF* */
19 
20 ircd_t Nefarious = {
21 	.ircdname = "Nefarious IRCU 0.4.0 or later",
22 	.tldprefix = "$",
23 	.uses_uid = true,
24 	.uses_rcommand = false,
25 	.uses_owner = false,
26 	.uses_protect = false,
27 	.uses_halfops = true,
28 	.uses_p10 = true,
29 	.uses_vhost = true,
30 	.oper_only_modes = CMODE_PERM | CMODE_OPERONLY | CMODE_ADMONLY,
31 	.owner_mode = 0,
32 	.protect_mode = 0,
33 	.halfops_mode = CSTATUS_HALFOP,
34 	.owner_mchar = "+",
35 	.protect_mchar = "+",
36 	.halfops_mchar = "+",
37 	.type = PROTOCOL_NEFARIOUS,
38 	.perm_mode = CMODE_PERM,
39 	.oimmune_mode = 0,
40 	.ban_like_modes = "be",
41 	.except_mchar = 'e',
42 	.invex_mchar = 'e',
43 	.flags = IRCD_CIDR_BANS,
44 };
45 
46 struct cmode_ nefarious_mode_list[] = {
47   { 'a', CMODE_ADMONLY },
48   { 'i', CMODE_INVITE },
49   { 'm', CMODE_MOD    },
50   { 'n', CMODE_NOEXT  },
51   { 'p', CMODE_PRIV   },
52   { 'r', CMODE_REGONLY },
53   { 's', CMODE_SEC    },
54   { 't', CMODE_TOPIC  },
55   { 'z', CMODE_PERM   },
56   { 'c', CMODE_NOCOLOR },
57   { 'C', CMODE_NOCTCP },
58   { 'D', CMODE_DELAYED },
59   { 'Q', CMODE_NOQUIT },
60   { 'N', CMODE_NONOTICE },
61   { 'M', CMODE_SOFTMOD },
62   { 'C', CMODE_NOCTCP },
63   { 'S', CMODE_STRIP },
64   { 'T', CMODE_NOAMSG },
65   { 'O', CMODE_OPERONLY },
66   { 'L', CMODE_SOFTPRIV },
67   { 'Z', CMODE_SSLONLY },
68   { '\0', 0 }
69 };
70 
71 struct extmode nefarious_ignore_mode_list[] = {
72   { '\0', 0 }
73 };
74 
75 struct cmode_ nefarious_status_mode_list[] = {
76   { 'o', CSTATUS_OP	},
77   { 'h', CSTATUS_HALFOP },
78   { 'v', CSTATUS_VOICE	},
79   { '\0', 0 }
80 };
81 
82 struct cmode_ nefarious_prefix_mode_list[] = {
83   { '@', CSTATUS_OP	},
84   { '%', CSTATUS_HALFOP },
85   { '+', CSTATUS_VOICE	},
86   { '\0', 0 }
87 };
88 
89 struct cmode_ nefarious_user_mode_list[] = {
90   { 'a', UF_ADMIN    },
91   { 'i', UF_INVIS    },
92   { 'o', UF_IRCOP    },
93   { 'd', UF_DEAF     },
94   { 'k', UF_IMMUNE   },
95   { '\0', 0 }
96 };
97 
98 static void check_hidehost(user_t *u);
99 
100 /* *INDENT-ON* */
101 
102 /* join a channel */
nefarious_join_sts(channel_t * c,user_t * u,bool isnew,char * modes)103 static void nefarious_join_sts(channel_t *c, user_t *u, bool isnew, char *modes)
104 {
105 	/* If the channel doesn't exist, we need to create it. */
106 	if (isnew)
107 	{
108 		sts("%s C %s %lu", u->uid, c->name, (unsigned long)c->ts);
109 		if (modes[0] && modes[1])
110 			sts("%s M %s %s", u->uid, c->name, modes);
111 	}
112 	else
113 	{
114 		sts("%s J %s %lu", u->uid, c->name, (unsigned long)c->ts);
115 		sts("%s M %s +o %s", u->uid, c->name, u->uid);
116 	}
117 }
118 
119 /* kicks a user from a channel */
nefarious_kick(user_t * source,channel_t * c,user_t * u,const char * reason)120 static void nefarious_kick(user_t *source, channel_t *c, user_t *u, const char *reason)
121 {
122 	sts("%s K %s %s :%s", source->uid, c->name, u->uid, reason);
123 
124 	chanuser_delete(c, u);
125 }
126 
127 /* NOTICE wrapper */
nefarious_notice_channel_sts(user_t * from,channel_t * target,const char * text)128 static void nefarious_notice_channel_sts(user_t *from, channel_t *target, const char *text)
129 {
130 	sts("%s O %s :%s", from ? from->uid : me.numeric, target->name, text);
131 }
132 
133 /* topic wrapper */
nefarious_topic_sts(channel_t * c,user_t * source,const char * setter,time_t ts,time_t prevts,const char * topic)134 static void nefarious_topic_sts(channel_t *c, user_t *source, const char *setter, time_t ts, time_t prevts, const char *topic)
135 {
136 	return_if_fail(c != NULL);
137 
138 	/* for nefarious, set topicsetter iff we can set the proper topicTS */
139 	if (ts > prevts || prevts == 0)
140 		sts("%s T %s %s %lu %lu :%s", source->uid, c->name, setter, (unsigned long)c->ts, (unsigned long)ts, topic);
141 	else
142 	{
143 		ts = CURRTIME;
144 		if (ts < prevts)
145 			ts = prevts + 1;
146 		sts("%s T %s %lu %lu :%s", source->uid, c->name, (unsigned long)c->ts, (unsigned long)ts, topic);
147 		c->topicts = ts;
148 	}
149 }
150 
151 /* protocol-specific stuff to do on login */
nefarious_on_login(user_t * u,myuser_t * mu,const char * wantedhost)152 static void nefarious_on_login(user_t *u, myuser_t *mu, const char *wantedhost)
153 {
154 	return_if_fail(u != NULL);
155 	return_if_fail(mu != NULL);
156 
157 	sts("%s AC %s R %s %lu", me.numeric, u->uid, entity(mu)->name,
158 			(unsigned long)mu->registered);
159 	check_hidehost(u);
160 }
161 
162 /* P10 does not support logout, so kill the user
163  * we can't keep track of which logins are stale and which aren't -- jilles
164  * Except we can in Nefarious --nenolod
165  */
nefarious_on_logout(user_t * u,const char * account)166 static bool nefarious_on_logout(user_t *u, const char *account)
167 {
168 	return_val_if_fail(u != NULL, false);
169 
170 	sts("%s AC %s U", me.numeric, u->uid);
171 	if (u->flags & UF_HIDEHOSTREQ && me.hidehostsuffix != NULL &&
172 			!strcmp(u->vhost + strlen(u->vhost) - strlen(me.hidehostsuffix), me.hidehostsuffix))
173 	{
174 		slog(LG_DEBUG, "nefarious_on_logout(): removing +x vhost for %s: %s -> %s",
175 				u->nick, u->vhost, u->host);
176 
177 		strshare_unref(u->vhost);
178 		u->vhost = strshare_get(u->host);
179 	}
180 
181 	return false;
182 }
183 
nefarious_sasl_sts(char * target,char mode,char * data)184 static void nefarious_sasl_sts(char *target, char mode, char *data)
185 {
186 	sts("%s SASL %c%c %s %c %s", me.numeric, target[0], target[1], target, mode, data);
187 }
188 
nefarious_svslogin_sts(char * target,char * nick,char * user,char * host,myuser_t * account)189 static void nefarious_svslogin_sts(char *target, char *nick, char *user, char *host, myuser_t *account)
190 {
191 	sts("%s SASL %c%c %s L %s %lu", me.numeric, target[0], target[1], target,
192 			entity(account)->name, (unsigned long)account->registered);
193 }
194 
nefarious_sethost_sts(user_t * source,user_t * target,const char * host)195 static void nefarious_sethost_sts(user_t *source, user_t *target, const char *host)
196 {
197 	sts("%s FA %s %s", me.numeric, target->uid, host);
198 	/* need to set +x; this will be echoed */
199 	if (!(target->flags & UF_HIDEHOSTREQ))
200 		sts("%s M %s +x", me.numeric, target->uid);
201 }
202 
nefarious_quarantine_sts(user_t * source,user_t * victim,long duration,const char * reason)203 static void nefarious_quarantine_sts(user_t *source, user_t *victim, long duration, const char *reason)
204 {
205 	sts("%s SU * +*@%s %lu :%s", me.numeric, victim->host, CURRTIME + duration, reason);
206 }
207 
m_topic(sourceinfo_t * si,int parc,char * parv[])208 static void m_topic(sourceinfo_t *si, int parc, char *parv[])
209 {
210 	channel_t *c = channel_find(parv[0]);
211 	const char *source;
212 	time_t ts = 0;
213 
214 	if (!c)
215 		return;
216 
217 	if (si->s != NULL)
218 		source = si->s->name;
219 	else
220 		source = si->su->nick;
221 
222 	if (parc > 2)
223 		ts = atoi(parv[parc - 2]);
224 	if (ts == 0)
225 		ts = CURRTIME;
226 	else if (c->topic != NULL && ts < c->topicts)
227 		return;
228 	handle_topic_from(si, c, parc > 4 ? parv[parc - 4] : source, ts, parv[parc - 1]);
229 }
230 
m_burst(sourceinfo_t * si,int parc,char * parv[])231 static void m_burst(sourceinfo_t *si, int parc, char *parv[])
232 {
233 	channel_t *c;
234 	unsigned int modec;
235 	char *modev[16];
236 	unsigned int userc;
237 	char *userv[256];
238 	unsigned int i;
239 	int j;
240 	char prefix[16];
241 	char newnick[16+NICKLEN];
242 	char *p;
243 	time_t ts;
244 	int bantype;
245 
246 	/* S BURST <channel> <ts> [parameters]
247 	 * parameters can be:
248 	 * +<simple mode>
249 	 * %<bans separated with spaces>
250 	 * <nicks>
251 	 */
252 	ts = atoi(parv[1]);
253 
254 	c = channel_find(parv[0]);
255 
256 	if (c == NULL)
257 	{
258 		slog(LG_DEBUG, "m_burst(): new channel: %s", parv[0]);
259 		c = channel_add(parv[0], ts, si->s);
260 	}
261 	else if (ts < c->ts)
262 	{
263 		chanuser_t *cu;
264 		mowgli_node_t *n;
265 
266 		clear_simple_modes(c);
267 		chanban_clear(c);
268 		handle_topic_from(si, c, "", 0, "");
269 		MOWGLI_ITER_FOREACH(n, c->members.head)
270 		{
271 			cu = (chanuser_t *)n->data;
272 			if (cu->user->server == me.me)
273 			{
274 				/* it's a service, reop */
275 				sts("%s M %s +o %s", me.numeric, c->name, CLIENT_NAME(cu->user));
276 				cu->modes = CSTATUS_OP;
277 			}
278 			else
279 				cu->modes = 0;
280 		}
281 
282 		slog(LG_DEBUG, "m_burst(): TS changed for %s (%lu -> %lu)", c->name, (unsigned long)c->ts, (unsigned long)ts);
283 		c->ts = ts;
284 		hook_call_channel_tschange(c);
285 	}
286 	if (parc < 3 || parv[2][0] != '+')
287 	{
288 		/* Tell the core to check mode locks now,
289 		 * otherwise it may only happen after the next
290 		 * mode change. -- jilles */
291 		channel_mode_va(NULL, c, 1, "+");
292 	}
293 
294 	bantype = 'b';
295 	j = 2;
296 	while (j < parc)
297 	{
298 		if (parv[j][0] == '+')
299 		{
300 			modec = 0;
301 			modev[modec++] = parv[j++];
302 			if (strchr(modev[0], 'k') && j < parc)
303 				modev[modec++] = parv[j++];
304 			if (strchr(modev[0], 'l') && j < parc)
305 				modev[modec++] = parv[j++];
306 			channel_mode(NULL, c, modec, modev);
307 		}
308 		else if (parv[j][0] == '%')
309 		{
310 			userc = sjtoken(parv[j++] + 1, ' ', userv);
311 			for (i = 0; i < userc; i++)
312 				if (!strcmp(userv[i], "~"))
313 					/* A ban "~" means exceptions are
314 					 * following */
315 					bantype = 'e';
316 				else
317 					chanban_add(c, userv[i], bantype);
318 		}
319 		else
320 		{
321 			userc = sjtoken(parv[j++], ',', userv);
322 
323 			prefix[0] = '\0';
324 			for (i = 0; i < userc; i++)
325 			{
326 				p = strchr(userv[i], ':');
327 				if (p != NULL)
328 				{
329 					*p = '\0';
330 					prefix[0] = '\0';
331 					prefix[1] = '\0';
332 					prefix[2] = '\0';
333 					p++;
334 					while (*p)
335 					{
336 						if (*p == 'o' || (*p >= '0' && *p <= '9' && !prefix[0]))
337 							prefix[prefix[0] ? 1 : 0] = '@';
338 						else if (*p == 'h')
339 							prefix[prefix[0] ? 1 : 0] = '%';
340 						else if (*p == 'v')
341 							prefix[prefix[0] ? 1 : 0] = '+';
342 						p++;
343 					}
344 				}
345 				mowgli_strlcpy(newnick, prefix, sizeof newnick);
346 				mowgli_strlcat(newnick, userv[i], sizeof newnick);
347 				chanuser_add(c, newnick);
348 			}
349 		}
350 	}
351 
352 	if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
353 		channel_delete(c);
354 }
355 
m_nick(sourceinfo_t * si,int parc,char * parv[])356 static void m_nick(sourceinfo_t *si, int parc, char *parv[])
357 {
358 	user_t *u;
359 	char ipstring[HOSTIPLEN];
360 	char *p;
361 	int i;
362 
363 	/* got the right number of args for an introduction? */
364 	if (parc >= 8)
365 	{
366 		/*
367 		 * -> Mh N jilles 1 1460435852 jilles 127.0.0.1 +ixCc 1A130C.572B1.6F53B5.8DD3B8.IP 1A130C.572B1.6F53B5.8DD3B8.IP DSBHPW Mhw2O :Real Name
368 		 */
369 		slog(LG_DEBUG, "m_nick(): new user on `%s': %s@%s (%s)", si->s->name, parv[0],parv[4],parv[7]);
370 
371 		decode_p10_ip(parv[parc - 3], ipstring);
372 		u = user_add(parv[0], parv[3], parv[4], parv[7], ipstring, parv[parc - 2], parv[parc - 1], si->s, atoi(parv[2]));
373 		if (u == NULL)
374 			return;
375 
376 		if (parv[5][0] == '+')
377 		{
378 			user_mode(u, parv[5]);
379 			i = 1;
380 			if (strchr(parv[5], 'r'))
381 			{
382 				p = strchr(parv[5+i], ':');
383 				if (p != NULL)
384 					*p++ = '\0';
385 				handle_burstlogin(u, parv[5+i], p ? atol(p) : 0);
386 				/* killed to force logout? */
387 				if (user_find(parv[parc - 2]) == NULL)
388 					return;
389 				i++;
390 			}
391 			if (strchr(parv[5], 'h'))
392 			{
393 				p = strchr(parv[5+i], '@');
394 				if (p == NULL)
395 				{
396 					strshare_unref(u->vhost);
397 					u->vhost = strshare_get(parv[5 + i]);
398 				}
399 				else
400 				{
401 					char userbuf[USERLEN];
402 
403 					strshare_unref(u->vhost);
404 					u->vhost = strshare_get(p + 1);
405 
406 					mowgli_strlcpy(userbuf, parv[5+i], sizeof userbuf);
407 
408 					p = strchr(userbuf, '@');
409 					if (p != NULL)
410 						*p = '\0';
411 
412 					strshare_unref(u->user);
413 					u->user = strshare_get(userbuf);
414 				}
415 				i++;
416 			}
417 			if (strchr(parv[5], 'f'))
418 			{
419 				strshare_unref(u->vhost);
420 				u->vhost = strshare_get(parv[5 + i]);
421 
422 				i++;
423 			}
424 			if (strchr(parv[5], 'x'))
425 			{
426 				u->flags |= UF_HIDEHOSTREQ;
427 				/* this must be after setting the account name */
428 				check_hidehost(u);
429 			}
430 		}
431 
432 		handle_nickchange(u);
433 	}
434 	/* if it's only 2 then it's a nickname change */
435 	else if (parc == 2)
436 	{
437 		if (!si->su)
438 		{
439 			slog(LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
440 			return;
441 		}
442 
443 		slog(LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
444 
445 		if (user_changenick(si->su, parv[0], atoi(parv[1])))
446 			return;
447 
448 		handle_nickchange(si->su);
449 	}
450 	else
451 	{
452 		slog(LG_DEBUG, "m_nick(): got NICK with wrong (%d) number of params", parc);
453 
454 		for (i = 0; i < parc; i++)
455 			slog(LG_DEBUG, "m_nick():   parv[%d] = %s", i, parv[i]);
456 	}
457 }
458 
m_mode(sourceinfo_t * si,int parc,char * parv[])459 static void m_mode(sourceinfo_t *si, int parc, char *parv[])
460 {
461 	user_t *u;
462 	char *p;
463 
464 	if (*parv[0] == '#')
465 		channel_mode(NULL, channel_find(parv[0]), parc - 1, &parv[1]);
466 	else
467 	{
468 		/* Yes this is a nick and not a UID -- jilles */
469 		u = user_find_named(parv[0]);
470 		if (u == NULL)
471 		{
472 			slog(LG_DEBUG, "m_mode(): user mode for unknown user %s", parv[0]);
473 			return;
474 		}
475 		user_mode(u, parv[1]);
476 		if (strchr(parv[1], 'x'))
477 		{
478 			u->flags |= UF_HIDEHOSTREQ;
479 			check_hidehost(u);
480 		}
481 		if (strchr(parv[1], 'h'))
482 		{
483 			if (parc > 2)
484 			{
485 				/* assume +h */
486 				p = strchr(parv[2], '@');
487 				if (p == NULL)
488 				{
489 					strshare_unref(u->vhost);
490 					u->vhost = strshare_get(parv[2]);
491 				}
492 				else
493 				{
494 					char userbuf[USERLEN];
495 
496 					strshare_unref(u->vhost);
497 					u->vhost = strshare_get(p + 1);
498 
499 					mowgli_strlcpy(userbuf, parv[2], sizeof userbuf);
500 					p = strchr(userbuf, '@');
501 					if (p != NULL)
502 						*p = '\0';
503 
504 					strshare_unref(u->user);
505 					u->user = strshare_get(userbuf);
506 				}
507 				slog(LG_DEBUG, "m_mode(): user %s setting vhost %s@%s", u->nick, u->user, u->vhost);
508 			}
509 			else
510 			{
511 				/* must be -h */
512 				/* XXX we don't know the original ident */
513 				slog(LG_DEBUG, "m_mode(): user %s turning off vhost", u->nick);
514 
515 				strshare_unref(u->vhost);
516 				u->vhost = strshare_get(u->host);
517 
518 				/* revert to +x vhost if applicable */
519 				check_hidehost(u);
520 			}
521 		}
522 	}
523 }
524 
m_clearmode(sourceinfo_t * si,int parc,char * parv[])525 static void m_clearmode(sourceinfo_t *si, int parc, char *parv[])
526 {
527 	channel_t *chan;
528 	char *p, c;
529 	mowgli_node_t *n, *tn;
530 	chanuser_t *cu;
531 	int i;
532 
533 	/* -> ABAAA CM # b */
534 	/* Note: this is an IRCop command, do not enforce mode locks. */
535 	chan = channel_find(parv[0]);
536 	if (chan == NULL)
537 	{
538 		slog(LG_DEBUG, "m_clearmode(): unknown channel %s", parv[0]);
539 		return;
540 	}
541 	p = parv[1];
542 	while ((c = *p++))
543 	{
544 		if (c == 'b')
545 		{
546 			MOWGLI_ITER_FOREACH_SAFE(n, tn, chan->bans.head)
547 			{
548 				if (((chanban_t *)n->data)->type == 'b')
549 					chanban_delete(n->data);
550 			}
551 		}
552 		else if (c == 'e')
553 		{
554 			MOWGLI_ITER_FOREACH_SAFE(n, tn, chan->bans.head)
555 			{
556 				if (((chanban_t *)n->data)->type == 'e')
557 					chanban_delete(n->data);
558 			}
559 		}
560 		else if (c == 'k')
561 		{
562 			if (chan->key)
563 				free(chan->key);
564 			chan->key = NULL;
565 		}
566 		else if (c == 'l')
567 			chan->limit = 0;
568 		else if (c == 'o')
569 		{
570 			MOWGLI_ITER_FOREACH(n, chan->members.head)
571 			{
572 				cu = (chanuser_t *)n->data;
573 				if (cu->user->server == me.me)
574 				{
575 					/* it's a service, reop */
576 					sts("%s M %s +o %s", me.numeric,
577 							chan->name,
578 							cu->user->uid);
579 				}
580 				else
581 					cu->modes &= ~CSTATUS_OP;
582 			}
583 		}
584 		else if (c == 'h')
585 		{
586 			MOWGLI_ITER_FOREACH(n, chan->members.head)
587 			{
588 				cu = (chanuser_t *)n->data;
589 				cu->modes &= ~CSTATUS_HALFOP;
590 			}
591 		}
592 		else if (c == 'v')
593 		{
594 			MOWGLI_ITER_FOREACH(n, chan->members.head)
595 			{
596 				cu = (chanuser_t *)n->data;
597 				cu->modes &= ~CSTATUS_VOICE;
598 			}
599 		}
600 		else
601 			for (i = 0; mode_list[i].mode != '\0'; i++)
602 			{
603 				if (c == mode_list[i].mode)
604 					chan->modes &= ~mode_list[i].value;
605 			}
606 	}
607 }
608 
m_account(sourceinfo_t * si,int parc,char * parv[])609 static void m_account(sourceinfo_t *si, int parc, char *parv[])
610 {
611 	user_t *u;
612 	static bool warned = false;
613 
614 	u = user_find(parv[0]);
615 	if (u == NULL)
616 		return;
617 	if (strlen(parv[1]) != 1 || (parv[1][0] != 'U' && parc < 3))
618 	{
619 		if (!warned)
620 		{
621 			slog(LG_ERROR, "m_account(): got account with second parameter %s, %u parameters, Atheme requires F:EXTENDED_ACCOUNTS:TRUE", parv[1], parc);
622 			wallops("Invalid ACCOUNT syntax, check F:EXTENDED_ACCOUNTS:TRUE");
623 			warned = true;
624 		}
625 		return;
626 	}
627 	switch (parv[1][0])
628 	{
629 		case 'R':
630 			handle_setlogin(si, u, parv[2], parc > 3 ? atol(parv[3]) : 0);
631 			break;
632 		case 'M':
633 			if (!u->myuser)
634 				slog(LG_INFO, "Account rename (%s) for not logged in user %s, processing anyway",
635 						parv[2], u->nick);
636 			handle_setlogin(si, u, parv[2], 0);
637 			break;
638 		case 'U':
639 			handle_clearlogin(si, u);
640 			break;
641 		default:
642 			slog(LG_INFO, "Unrecognized ACCOUNT type %s", parv[1]);
643 	}
644 }
645 
m_sasl(sourceinfo_t * si,int parc,char * parv[])646 static void m_sasl(sourceinfo_t *si, int parc, char *parv[])
647 {
648 	sasl_message_t smsg;
649 
650 	if (parc < 4)
651 		return;
652 
653 	smsg.uid = parv[1];
654 	smsg.mode = *parv[2];
655 	smsg.buf = parv[3];
656 	smsg.ext = parc >= 4 ? parv[4] : NULL;
657 	smsg.server = si->s ? si->s : NULL;
658 
659 	hook_call_sasl_input(&smsg);
660 }
661 
check_hidehost(user_t * u)662 static void check_hidehost(user_t *u)
663 {
664 	static bool warned = false;
665 	char buf[HOSTLEN + 1];
666 
667 	/* do they qualify? */
668 	if (!(u->flags & UF_HIDEHOSTREQ) || u->myuser == NULL || (u->myuser->flags & MU_WAITAUTH))
669 		return;
670 	/* don't use this if they have some other kind of vhost */
671 	if (strcmp(u->host, u->vhost))
672 	{
673 		slog(LG_DEBUG, "check_hidehost(): +x overruled by other vhost for %s", u->nick);
674 		return;
675 	}
676 	if (me.hidehostsuffix == NULL)
677 	{
678 		if (!warned)
679 		{
680 			wallops("Misconfiguration: serverinfo::hidehostsuffix not set");
681 			warned = true;
682 		}
683 		return;
684 	}
685 
686 	snprintf(buf, sizeof buf, "%s.%s", entity(u->myuser)->name, me.hidehostsuffix);
687 
688 	strshare_unref(u->vhost);
689 	u->vhost = strshare_get(buf);
690 
691 	slog(LG_DEBUG, "check_hidehost(): %s -> %s", u->nick, u->vhost);
692 }
693 
p10_kline_sts(const char * server,const char * user,const char * host,long duration,const char * reason)694 static void p10_kline_sts(const char *server, const char *user, const char *host, long duration, const char *reason)
695 {
696 	/* hold permanent akills for four weeks -- jilles
697 	 *  This was changed in Nefarious 2.
698 	 */
699 	sts("%s GL * +%s@%s %ld %lu :%s", me.numeric, user, host, duration > 0 ? duration : 2419200, (unsigned long)CURRTIME, reason);
700 }
701 
_modinit(module_t * m)702 void _modinit(module_t * m)
703 {
704 	MODULE_TRY_REQUEST_DEPENDENCY(m, "protocol/p10-generic");
705 
706 	/* Symbol relocation voodoo. */
707 	join_sts = &nefarious_join_sts;
708 	kick = &nefarious_kick;
709 	notice_channel_sts = &nefarious_notice_channel_sts;
710 	topic_sts = &nefarious_topic_sts;
711 	ircd_on_login = &nefarious_on_login;
712 	ircd_on_logout = &nefarious_on_logout;
713 	sasl_sts = &nefarious_sasl_sts;
714 	sethost_sts = &nefarious_sethost_sts;
715 	svslogin_sts = &nefarious_svslogin_sts;
716 	quarantine_sts = &nefarious_quarantine_sts;
717 	kline_sts = &p10_kline_sts;
718 	mode_list = nefarious_mode_list;
719 	ignore_mode_list = nefarious_ignore_mode_list;
720 	status_mode_list = nefarious_status_mode_list;
721 	prefix_mode_list = nefarious_prefix_mode_list;
722 	user_mode_list = nefarious_user_mode_list;
723 	ignore_mode_list_size = ARRAY_SIZE(nefarious_ignore_mode_list);
724 
725 	ircd = &Nefarious;
726 
727 	pcommand_add("SASL", m_sasl, 4, MSRC_SERVER);
728 
729 	/* override these */
730 	pcommand_delete("B");
731 	pcommand_delete("N");
732 	pcommand_delete("M");
733 	pcommand_delete("OM");
734 	pcommand_delete("CM");
735 	pcommand_delete("T");
736 	pcommand_delete("AC");
737 	pcommand_add("B", m_burst, 2, MSRC_SERVER);
738 	pcommand_add("N", m_nick, 2, MSRC_USER | MSRC_SERVER);
739 	pcommand_add("M", m_mode, 2, MSRC_USER | MSRC_SERVER);
740 	pcommand_add("OM", m_mode, 2, MSRC_USER); /* OPMODE, treat as MODE */
741 	pcommand_add("CM", m_clearmode, 2, MSRC_USER);
742 	pcommand_add("T", m_topic, 2, MSRC_USER | MSRC_SERVER);
743 	pcommand_add("AC", m_account, 2, MSRC_SERVER);
744 
745 	m->mflags = MODTYPE_CORE;
746 
747 	pmodule_loaded = true;
748 }
749 
750 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
751  * vim:ts=8
752  * vim:sw=8
753  * vim:noexpandtab
754  */