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