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