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 asuka.
8  *
9  */
10 
11 #include "atheme.h"
12 #include "uplink.h"
13 #include "pmodule.h"
14 #include "protocol/asuka.h"
15 
16 DECLARE_MODULE_V1("protocol/asuka", true, _modinit, NULL, PACKAGE_STRING, VENDOR_STRING);
17 
18 /* *INDENT-OFF* */
19 
20 ircd_t Asuka = {
21 	.ircdname = "Asuka 1.2.1 and later",
22 	.tldprefix = "$",
23 	.uses_uid = true,
24 	.uses_rcommand = false,
25 	.uses_owner = false,
26 	.uses_protect = false,
27 	.uses_halfops = false,
28 	.uses_p10 = true,
29 	.uses_vhost = true,
30 	.oper_only_modes = 0,
31 	.owner_mode = 0,
32 	.protect_mode = 0,
33 	.halfops_mode = 0,
34 	.owner_mchar = "+",
35 	.protect_mchar = "+",
36 	.halfops_mchar = "+",
37 	.type = PROTOCOL_ASUKA,
38 	.perm_mode = 0,
39 	.oimmune_mode = 0,
40 	.ban_like_modes = "b",
41 	.except_mchar = 0,
42 	.invex_mchar = 0,
43 	.flags = IRCD_CIDR_BANS,
44 };
45 
46 struct cmode_ asuka_mode_list[] = {
47   { 'i', CMODE_INVITE },
48   { 'm', CMODE_MOD    },
49   { 'n', CMODE_NOEXT  },
50   { 'p', CMODE_PRIV   },
51   { 's', CMODE_SEC    },
52   { 't', CMODE_TOPIC  },
53   { 'c', CMODE_NOCOLOR },
54   { 'C', CMODE_NOCTCP },
55   { 'D', CMODE_DELAYED },
56   { 'u', CMODE_NOQUIT },
57   { 'N', CMODE_NONOTICE },
58   { '\0', 0 }
59 };
60 
61 struct extmode asuka_ignore_mode_list[] = {
62   { '\0', 0 }
63 };
64 
65 struct cmode_ asuka_status_mode_list[] = {
66   { 'o', CSTATUS_OP    },
67   { 'v', CSTATUS_VOICE },
68   { '\0', 0 }
69 };
70 
71 struct cmode_ asuka_prefix_mode_list[] = {
72   { '@', CSTATUS_OP    },
73   { '+', CSTATUS_VOICE },
74   { '\0', 0 }
75 };
76 
77 struct cmode_ asuka_user_mode_list[] = {
78   { 'i', UF_INVIS    },
79   { 'o', UF_IRCOP    },
80   { 'd', UF_DEAF     },
81   { 'k', UF_IMMUNE   },
82   { '\0', 0 }
83 };
84 
85 static void check_hidehost(user_t *u);
86 
87 /* *INDENT-ON* */
88 
89 /* NOTICE wrapper */
asuka_notice_channel_sts(user_t * from,channel_t * target,const char * text)90 static void asuka_notice_channel_sts(user_t *from, channel_t *target, const char *text)
91 {
92 	if (target->modes & CMODE_NONOTICE)
93 	{
94 		/* asuka sucks */
95 		/* remove that stupid +N mode before it blocks our notice
96 		 * -- jilles */
97 		sts("%s M %s -N", from ? from->uid : me.numeric, target->name);
98 		target->modes &= ~CMODE_NONOTICE;
99 	}
100 	if (from == NULL || chanuser_find(target, from))
101 		sts("%s O %s :%s", from ? from->uid : me.numeric, target->name, text);
102 	else
103 		sts("%s O %s :[%s:%s] %s", me.numeric, target->name, from->nick, target->name, text);
104 }
105 
asuka_wallchops(user_t * sender,channel_t * channel,const char * message)106 static void asuka_wallchops(user_t *sender, channel_t *channel, const char *message)
107 {
108 	if (channel->modes & CMODE_NONOTICE)
109 	{
110 		/* asuka sucks */
111 		/* remove that stupid +N mode before it blocks our notice
112 		 * -- jilles */
113 		sts("%s M %s -N", sender->uid, channel->name);
114 		channel->modes &= ~CMODE_NONOTICE;
115 	}
116 	sts("%s WC %s :%s", sender->uid, channel->name, message);
117 }
118 
119 /* protocol-specific stuff to do on login */
asuka_on_login(user_t * u,myuser_t * account,const char * wantedhost)120 static void asuka_on_login(user_t *u, myuser_t *account, const char *wantedhost)
121 {
122 	return_if_fail(u != NULL);
123 
124 	sts("%s AC %s %s %lu", me.numeric, u->uid, entity(u->myuser)->name,
125 			(unsigned long)account->registered);
126 	check_hidehost(u);
127 }
128 
129 /* P10 does not support logout, so kill the user
130  * we can't keep track of which logins are stale and which aren't -- jilles */
asuka_on_logout(user_t * u,const char * account)131 static bool asuka_on_logout(user_t *u, const char *account)
132 {
133 	return_val_if_fail(u != NULL, false);
134 
135 	kill_user(NULL, u, "Forcing logout %s -> %s", u->nick, account);
136 	return true;
137 }
138 
m_nick(sourceinfo_t * si,int parc,char * parv[])139 static void m_nick(sourceinfo_t *si, int parc, char *parv[])
140 {
141 	user_t *u;
142 	char ipstring[HOSTIPLEN];
143 	char *p;
144 	int i;
145 
146 	/* got the right number of args for an introduction? */
147 	if (parc >= 8)
148 	{
149 		/* -> AB N jilles 1 1137687480 jilles jaguar.test +oiwgrx jilles B]AAAB ABAAE :Jilles Tjoelker */
150 		/* -> AB N test4 1 1137690148 jilles jaguar.test +iw B]AAAB ABAAG :Jilles Tjoelker */
151 		slog(LG_DEBUG, "m_nick(): new user on `%s': %s", si->s->name, parv[0]);
152 
153 		decode_p10_ip(parv[parc - 3], ipstring);
154 		u = user_add(parv[0], parv[3], parv[4], NULL, ipstring, parv[parc - 2], parv[parc - 1], si->s, atoi(parv[2]));
155 		if (u == NULL)
156 			return;
157 
158 		if (parv[5][0] == '+')
159 		{
160 			user_mode(u, parv[5]);
161 			i = 1;
162 			if (strchr(parv[5], 'r'))
163 			{
164 				p = strchr(parv[5+i], ':');
165 				if (p != NULL)
166 					*p++ = '\0';
167 				handle_burstlogin(u, parv[5+i], p ? atol(p) : 0);
168 				/* killed to force logout? */
169 				if (user_find(parv[parc - 2]) == NULL)
170 					return;
171 				i++;
172 			}
173 			if (strchr(parv[5], 'h'))
174 			{
175 				p = strchr(parv[5+i], '@');
176 				if (p == NULL)
177 				{
178 					strshare_unref(u->vhost);
179 					u->vhost = strshare_get(parv[5 + i]);
180 				}
181 				else
182 				{
183 					char userbuf[USERLEN];
184 
185 					strshare_unref(u->vhost);
186 					u->vhost = strshare_get(p + 1);
187 
188 					mowgli_strlcpy(userbuf, parv[5+i], sizeof userbuf);
189 					p = strchr(userbuf, '@');
190 					if (p != NULL)
191 						*p = '\0';
192 
193 					strshare_unref(u->user);
194 					u->user = strshare_get(userbuf);
195 				}
196 				i++;
197 			}
198 			if (strchr(parv[5], 'x'))
199 			{
200 				u->flags |= UF_HIDEHOSTREQ;
201 				/* this must be after setting the account name */
202 				check_hidehost(u);
203 			}
204 		}
205 
206 		handle_nickchange(u);
207 	}
208 	/* if it's only 2 then it's a nickname change */
209 	else if (parc == 2)
210 	{
211 		if (!si->su)
212 		{
213 			slog(LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
214 			return;
215 		}
216 
217 		slog(LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
218 
219 		if (user_changenick(si->su, parv[0], atoi(parv[1])))
220 			return;
221 
222 		handle_nickchange(si->su);
223 	}
224 	else
225 	{
226 		slog(LG_DEBUG, "m_nick(): got NICK with wrong (%d) number of params", parc);
227 
228 		for (i = 0; i < parc; i++)
229 			slog(LG_DEBUG, "m_nick():   parv[%d] = %s", i, parv[i]);
230 	}
231 }
232 
m_mode(sourceinfo_t * si,int parc,char * parv[])233 static void m_mode(sourceinfo_t *si, int parc, char *parv[])
234 {
235 	user_t *u;
236 	char *p;
237 
238 	if (*parv[0] == '#')
239 		channel_mode(NULL, channel_find(parv[0]), parc - 1, &parv[1]);
240 	else
241 	{
242 		/* Yes this is a nick and not a UID -- jilles */
243 		u = user_find_named(parv[0]);
244 		if (u == NULL)
245 		{
246 			slog(LG_DEBUG, "m_mode(): user mode for unknown user %s", parv[0]);
247 			return;
248 		}
249 		user_mode(u, parv[1]);
250 		if (strchr(parv[1], 'x'))
251 		{
252 			u->flags |= UF_HIDEHOSTREQ;
253 			check_hidehost(u);
254 		}
255 		if (strchr(parv[1], 'h'))
256 		{
257 			if (parc > 2)
258 			{
259 				/* assume +h */
260 				p = strchr(parv[2], '@');
261 				if (p == NULL)
262 				{
263 					strshare_unref(u->vhost);
264 					u->vhost = strshare_get(parv[2]);
265 				}
266 				else
267 				{
268 					char userbuf[USERLEN];
269 
270 					strshare_unref(u->vhost);
271 					u->vhost = strshare_get(p + 1);
272 
273 					mowgli_strlcpy(userbuf, parv[2], sizeof userbuf);
274 
275 					p = strchr(userbuf, '@');
276 					if (p != NULL)
277 						*p = '\0';
278 
279 					strshare_unref(u->user);
280 					u->user = strshare_get(userbuf);
281 				}
282 				slog(LG_DEBUG, "m_mode(): user %s setting vhost %s@%s", u->nick, u->user, u->vhost);
283 			}
284 			else
285 			{
286 				/* must be -h */
287 				/* XXX we don't know the original ident */
288 				slog(LG_DEBUG, "m_mode(): user %s turning off vhost", u->nick);
289 
290 				strshare_unref(u->vhost);
291 				u->vhost = strshare_get(u->host);
292 
293 				/* revert to +x vhost if applicable */
294 				check_hidehost(u);
295 			}
296 		}
297 	}
298 }
299 
check_hidehost(user_t * u)300 static void check_hidehost(user_t *u)
301 {
302 	static bool warned = false;
303 	char buf[HOSTLEN + 1];
304 
305 	/* do they qualify? */
306 	if (!(u->flags & UF_HIDEHOSTREQ) || u->myuser == NULL || (u->myuser->flags & MU_WAITAUTH))
307 		return;
308 	/* don't use this if they have some other kind of vhost */
309 	if (strcmp(u->host, u->vhost))
310 	{
311 		slog(LG_DEBUG, "check_hidehost(): +x overruled by other vhost for %s", u->nick);
312 		return;
313 	}
314 	if (me.hidehostsuffix == NULL)
315 	{
316 		if (!warned)
317 		{
318 			wallops("Misconfiguration: serverinfo::hidehostsuffix not set");
319 			warned = true;
320 		}
321 		return;
322 	}
323 
324 	snprintf(buf, sizeof buf, "%s.%s", entity(u->myuser)->name, me.hidehostsuffix);
325 
326 	strshare_unref(u->vhost);
327 	u->vhost = strshare_get(buf);
328 
329 	slog(LG_DEBUG, "check_hidehost(): %s -> %s", u->nick, u->vhost);
330 }
331 
_modinit(module_t * m)332 void _modinit(module_t * m)
333 {
334 	MODULE_TRY_REQUEST_DEPENDENCY(m, "protocol/p10-generic");
335 
336 	/* Symbol relocation voodoo. */
337 	notice_channel_sts = &asuka_notice_channel_sts;
338 	wallchops = &asuka_wallchops;
339 	ircd_on_login = &asuka_on_login;
340 	ircd_on_logout = &asuka_on_logout;
341 
342 	mode_list = asuka_mode_list;
343 	ignore_mode_list = asuka_ignore_mode_list;
344 	status_mode_list = asuka_status_mode_list;
345 	prefix_mode_list = asuka_prefix_mode_list;
346 	user_mode_list = asuka_user_mode_list;
347 	ignore_mode_list_size = ARRAY_SIZE(asuka_ignore_mode_list);
348 
349 	ircd = &Asuka;
350 
351 	/* override these */
352 	pcommand_delete("N");
353 	pcommand_delete("M");
354 	pcommand_delete("OM");
355 	pcommand_add("N", m_nick, 2, MSRC_USER | MSRC_SERVER);
356 	pcommand_add("M", m_mode, 2, MSRC_USER | MSRC_SERVER);
357 	pcommand_add("OM", m_mode, 2, MSRC_USER); /* OPMODE, treat as MODE */
358 
359 	m->mflags = MODTYPE_CORE;
360 
361 	pmodule_loaded = true;
362 }
363 
364 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
365  * vim:ts=8
366  * vim:sw=8
367  * vim:noexpandtab
368  */
369