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/bahamut.h"
14
15 DECLARE_MODULE_V1("protocol/bahamut", true, _modinit, NULL, PACKAGE_STRING, VENDOR_STRING);
16
17 /* *INDENT-OFF* */
18
19 ircd_t Bahamut = {
20 .ircdname = "Bahamut 1.8.x",
21 .tldprefix = "$",
22 .uses_uid = false,
23 .uses_rcommand = false,
24 .uses_owner = false,
25 .uses_protect = false,
26 .uses_halfops = false,
27 .uses_p10 = false,
28 .uses_vhost = false,
29 .oper_only_modes = CMODE_OPERONLY,
30 .owner_mode = 0,
31 .protect_mode = 0,
32 .halfops_mode = 0,
33 .owner_mchar = "+",
34 .protect_mchar = "+",
35 .halfops_mchar = "+",
36 .type = PROTOCOL_BAHAMUT,
37 .perm_mode = 0,
38 .oimmune_mode = 0,
39 .ban_like_modes = "beI",
40 .except_mchar = 'e',
41 .invex_mchar = 'I',
42 .flags = IRCD_HOLDNICK,
43 };
44
45 struct cmode_ bahamut_mode_list[] = {
46 { 'i', CMODE_INVITE },
47 { 'm', CMODE_MOD },
48 { 'n', CMODE_NOEXT },
49 { 'p', CMODE_PRIV },
50 { 's', CMODE_SEC },
51 { 't', CMODE_TOPIC },
52 { 'c', CMODE_NOCOLOR },
53 { 'M', CMODE_MODREG },
54 { 'R', CMODE_REGONLY },
55 { 'O', CMODE_OPERONLY },
56 { 'r', CMODE_CHANREG },
57 { '\0', 0 }
58 };
59
60 static bool check_jointhrottle(const char *, channel_t *, mychan_t *, user_t *, myuser_t *);
61
62 struct extmode bahamut_ignore_mode_list[] = {
63 { 'j', check_jointhrottle },
64 { '\0', 0 }
65 };
66
67 struct cmode_ bahamut_status_mode_list[] = {
68 { 'o', CSTATUS_OP },
69 { 'v', CSTATUS_VOICE },
70 { '\0', 0 }
71 };
72
73 struct cmode_ bahamut_prefix_mode_list[] = {
74 { '@', CSTATUS_OP },
75 { '+', CSTATUS_VOICE },
76 { '\0', 0 }
77 };
78
79 struct cmode_ bahamut_user_mode_list[] = {
80 { 'A', UF_ADMIN },
81 { 'i', UF_INVIS },
82 { 'o', UF_IRCOP },
83 { '\0', 0 }
84 };
85
86 /* *INDENT-ON* */
87
check_jointhrottle(const char * value,channel_t * c,mychan_t * mc,user_t * u,myuser_t * mu)88 static bool check_jointhrottle(const char *value, channel_t *c, mychan_t *mc, user_t *u, myuser_t *mu)
89 {
90 const char *p, *arg2;
91 int num, timeslice, v;
92
93 if (!strcmp(value, "0") && u == NULL && mu == NULL)
94 return true;
95 p = value, arg2 = NULL;
96 while (*p != '\0')
97 {
98 if (*p == ':')
99 {
100 if (arg2 != NULL)
101 return false;
102 arg2 = p + 1;
103 }
104 else if (!isdigit((unsigned char)*p))
105 return false;
106 p++;
107 }
108 if (arg2 == NULL)
109 return false;
110 if (p - arg2 > 3 || arg2 - value - 1 > 3)
111 return false;
112 num = atoi(value);
113 timeslice = atoi(arg2);
114 if (num <= 0 || num > 127 || timeslice <= 0 || timeslice > 127)
115 return false;
116 if (u != NULL || mu != NULL)
117 {
118 /* the following are the same restrictions bahamut
119 * applies to local clients
120 */
121 if (num < 2 || num > 20 || timeslice > 60)
122 return false;
123 v = (timeslice - 1) / 8 + 1;
124 if (num < v)
125 return false;
126 v = num / 2;
127 if (timeslice < v)
128 return false;
129 }
130 return true;
131 }
132
133 static bool use_nickipstr = false;
134
135 /* login to our uplink */
bahamut_server_login(void)136 static unsigned int bahamut_server_login(void)
137 {
138 int ret;
139
140 ret = sts("PASS %s :TS", curr_uplink->send_pass);
141 if (ret == 1)
142 return 1;
143
144 me.bursting = true;
145
146 sts("CAPAB SSJOIN NOQUIT BURST ZIP NICKIP TSMODE NICKIPSTR");
147 sts("SERVER %s 1 :%s", me.name, me.desc);
148 sts("SVINFO 5 3 0 :%lu", (unsigned long)CURRTIME);
149
150 return 0;
151 }
152
153 /* introduce a client */
bahamut_introduce_nick(user_t * u)154 static void bahamut_introduce_nick(user_t *u)
155 {
156 const char *umode = user_get_umodestr(u);
157
158 if (use_nickipstr)
159 sts("NICK %s 1 %lu %s %s %s %s 0 0.0.0.0 :%s", u->nick, (unsigned long)u->ts, umode, u->user, u->host, me.name, u->gecos);
160 else
161 sts("NICK %s 1 %lu %s %s %s %s 0 0 :%s", u->nick, (unsigned long)u->ts, umode, u->user, u->host, me.name, u->gecos);
162 }
163
164 /* invite a user to a channel */
bahamut_invite_sts(user_t * sender,user_t * target,channel_t * channel)165 static void bahamut_invite_sts(user_t *sender, user_t *target, channel_t *channel)
166 {
167 sts(":%s INVITE %s %s", sender->nick, target->nick, channel->name);
168 }
169
bahamut_quit_sts(user_t * u,const char * reason)170 static void bahamut_quit_sts(user_t *u, const char *reason)
171 {
172 sts(":%s QUIT :%s", u->nick, reason);
173 }
174
175 /* WALLOPS wrapper */
bahamut_wallops_sts(const char * text)176 static void bahamut_wallops_sts(const char *text)
177 {
178 sts(":%s GLOBOPS :%s", me.name, text);
179 }
180
181 /* join a channel */
bahamut_join_sts(channel_t * c,user_t * u,bool isnew,char * modes)182 static void bahamut_join_sts(channel_t *c, user_t *u, bool isnew, char *modes)
183 {
184 if (isnew)
185 sts(":%s SJOIN %lu %s %s :@%s", me.name, (unsigned long)c->ts,
186 c->name, modes, u->nick);
187 else
188 sts(":%s SJOIN %lu %s + :@%s", me.name, (unsigned long)c->ts,
189 c->name, u->nick);
190 }
191
bahamut_chan_lowerts(channel_t * c,user_t * u)192 static void bahamut_chan_lowerts(channel_t *c, user_t *u)
193 {
194 slog(LG_DEBUG, "bahamut_chan_lowerts(): lowering TS for %s to %lu",
195 c->name, (unsigned long)c->ts);
196 sts(":%s SJOIN %lu %s %s :@%s", me.name, (unsigned long)c->ts, c->name,
197 channel_modes(c, true), u->nick);
198 chanban_clear(c);
199 /*handle_topic(c, "", 0, "");*/
200 /* Don't destroy keeptopic info, I'll admit this is ugly -- jilles */
201 if (c->topic != NULL)
202 free(c->topic);
203 if (c->topic_setter != NULL)
204 free(c->topic_setter);
205 c->topic = c->topic_setter = NULL;
206 c->topicts = 0;
207 }
208
209 /* kicks a user from a channel */
bahamut_kick(user_t * source,channel_t * c,user_t * u,const char * reason)210 static void bahamut_kick(user_t *source, channel_t *c, user_t *u, const char *reason)
211 {
212 sts(":%s KICK %s %s :%s", source->nick, c->name, u->nick, reason);
213
214 chanuser_delete(c, u);
215 }
216
217 /* PRIVMSG wrapper */
bahamut_msg(const char * from,const char * target,const char * fmt,...)218 static void bahamut_msg(const char *from, const char *target, const char *fmt, ...)
219 {
220 va_list ap;
221 char buf[BUFSIZE];
222
223 va_start(ap, fmt);
224 vsnprintf(buf, BUFSIZE, fmt, ap);
225 va_end(ap);
226
227 sts(":%s PRIVMSG %s :%s", from, target, buf);
228 }
229
bahamut_msg_global_sts(user_t * from,const char * mask,const char * text)230 static void bahamut_msg_global_sts(user_t *from, const char *mask, const char *text)
231 {
232 mowgli_node_t *n;
233 tld_t *tld;
234 if (!strcmp(mask, "*"))
235 {
236 MOWGLI_ITER_FOREACH(n, tldlist.head)
237 {
238 tld = n->data;
239 sts(":%s PRIVMSG %s*%s :%s", from ? from->nick : me.name, ircd->tldprefix, tld->name, text);
240 }
241 }
242 else
243 sts(":%s PRIVMSG %s%s :%s", from ? from->nick : me.name, ircd->tldprefix, mask, text);
244 }
245
246 /* NOTICE wrapper */
bahamut_notice_user_sts(user_t * from,user_t * target,const char * text)247 static void bahamut_notice_user_sts(user_t *from, user_t *target, const char *text)
248 {
249 sts(":%s NOTICE %s :%s", from ? from->nick : me.name, target->nick, text);
250 }
251
bahamut_notice_global_sts(user_t * from,const char * mask,const char * text)252 static void bahamut_notice_global_sts(user_t *from, const char *mask, const char *text)
253 {
254 mowgli_node_t *n;
255 tld_t *tld;
256
257 if (!strcmp(mask, "*"))
258 {
259 MOWGLI_ITER_FOREACH(n, tldlist.head)
260 {
261 tld = n->data;
262 sts(":%s NOTICE %s*%s :%s", from ? from->nick : me.name, ircd->tldprefix, tld->name, text);
263 }
264 }
265 else
266 sts(":%s NOTICE %s%s :%s", from ? from->nick : me.name, ircd->tldprefix, mask, text);
267 }
268
bahamut_notice_channel_sts(user_t * from,channel_t * target,const char * text)269 static void bahamut_notice_channel_sts(user_t *from, channel_t *target, const char *text)
270 {
271 sts(":%s NOTICE %s :%s", from ? from->nick : me.name, target->name, text);
272 }
273
bahamut_wallchops(user_t * sender,channel_t * channel,const char * message)274 static void bahamut_wallchops(user_t *sender, channel_t *channel, const char *message)
275 {
276 sts(":%s NOTICE @%s :%s", sender->nick, channel->name, message);
277 }
278
bahamut_numeric_sts(server_t * from,int numeric,user_t * target,const char * fmt,...)279 static void bahamut_numeric_sts(server_t *from, int numeric, user_t *target, const char *fmt, ...)
280 {
281 va_list ap;
282 char buf[BUFSIZE];
283
284 va_start(ap, fmt);
285 vsnprintf(buf, BUFSIZE, fmt, ap);
286 va_end(ap);
287
288 sts(":%s %d %s %s", from->name, numeric, target->nick, buf);
289 }
290
291 /* KILL wrapper */
bahamut_kill_id_sts(user_t * killer,const char * id,const char * reason)292 static void bahamut_kill_id_sts(user_t *killer, const char *id, const char *reason)
293 {
294 if (killer != NULL)
295 sts(":%s KILL %s :%s!%s (%s)", killer->nick, id, killer->host, killer->nick, reason);
296 else
297 sts(":%s KILL %s :%s (%s)", me.name, id, me.name, reason);
298 }
299
300 /* PART wrapper */
bahamut_part_sts(channel_t * c,user_t * u)301 static void bahamut_part_sts(channel_t *c, user_t *u)
302 {
303 sts(":%s PART %s", u->nick, c->name);
304 }
305
306 /* server-to-server KLINE wrapper */
bahamut_kline_sts(const char * server,const char * user,const char * host,long duration,const char * reason)307 static void bahamut_kline_sts(const char *server, const char *user, const char *host, long duration, const char *reason)
308 {
309 service_t *svs;
310
311 svs = service_find("operserv");
312 sts(":%s AKILL %s %s %ld %s %lu :%s", me.name, host, user, duration, svs != NULL ? svs->nick : me.name, (unsigned long)CURRTIME, reason);
313 }
314
315 /* server-to-server UNKLINE wrapper */
bahamut_unkline_sts(const char * server,const char * user,const char * host)316 static void bahamut_unkline_sts(const char *server, const char *user, const char *host)
317 {
318 sts(":%s RAKILL %s %s", me.name, host, user);
319 }
320
321 /* topic wrapper */
bahamut_topic_sts(channel_t * c,user_t * source,const char * setter,time_t ts,time_t prevts,const char * topic)322 static void bahamut_topic_sts(channel_t *c, user_t *source, const char *setter, time_t ts, time_t prevts, const char *topic)
323 {
324 return_if_fail(c != NULL);
325
326 sts(":%s TOPIC %s %s %lu :%s", source->nick, c->name, setter, (unsigned long)ts, topic);
327 }
328
329 /* mode wrapper */
bahamut_mode_sts(char * sender,channel_t * target,char * modes)330 static void bahamut_mode_sts(char *sender, channel_t *target, char *modes)
331 {
332 return_if_fail(sender != NULL);
333 return_if_fail(target != NULL);
334 return_if_fail(modes != NULL);
335
336 sts(":%s MODE %s %lu %s", sender, target->name, (unsigned long)target->ts, modes);
337 }
338
339 /* ping wrapper */
bahamut_ping_sts(void)340 static void bahamut_ping_sts(void)
341 {
342 sts("PING :%s", me.name);
343 }
344
345 /* protocol-specific stuff to do on login */
bahamut_on_login(user_t * u,myuser_t * account,const char * wantedhost)346 static void bahamut_on_login(user_t *u, myuser_t *account, const char *wantedhost)
347 {
348 return_if_fail(u != NULL);
349
350 /* Can only do this for nickserv, and can only record identified
351 * state if logged in to correct nick, sorry -- jilles
352 */
353 if (should_reg_umode(u))
354 sts(":%s SVSMODE %s +rd %lu", nicksvs.nick, u->nick, (unsigned long)CURRTIME);
355 }
356
357 /* protocol-specific stuff to do on login */
bahamut_on_logout(user_t * u,const char * account)358 static bool bahamut_on_logout(user_t *u, const char *account)
359 {
360 return_val_if_fail(u != NULL, false);
361
362 if (!nicksvs.no_nick_ownership)
363 sts(":%s SVSMODE %s -r+d %lu", nicksvs.nick, u->nick, (unsigned long)CURRTIME);
364 return false;
365 }
366
bahamut_jupe(const char * server,const char * reason)367 static void bahamut_jupe(const char *server, const char *reason)
368 {
369 server_t *s;
370
371 sts(":%s SQUIT %s :%s", me.name, server, reason);
372 s = server_find(server);
373 /* If the server is not directly connected to our uplink, we
374 * need to wait for its uplink to process the SQUIT :(
375 * -- jilles */
376 if (s != NULL && s->uplink != NULL && s->uplink->uplink != me.me)
377 s->flags |= SF_JUPE_PENDING;
378 else
379 sts(":%s SERVER %s 2 :%s", me.name, server, reason);
380 }
381
bahamut_fnc_sts(user_t * source,user_t * u,const char * newnick,int type)382 static void bahamut_fnc_sts(user_t *source, user_t *u, const char *newnick, int type)
383 {
384 sts(":%s SVSNICK %s %s %lu", source->nick, u->nick, newnick,
385 (unsigned long)(CURRTIME - 60));
386 }
387
bahamut_holdnick_sts(user_t * source,int duration,const char * nick,myuser_t * mu)388 static void bahamut_holdnick_sts(user_t *source, int duration, const char *nick, myuser_t *mu)
389 {
390 sts(":%s SVSHOLD %s %d :Reserved by %s for nickname owner (%s)",
391 source->nick, nick, duration, source->nick,
392 mu ? entity(mu)->name : nick);
393 }
394
m_topic(sourceinfo_t * si,int parc,char * parv[])395 static void m_topic(sourceinfo_t *si, int parc, char *parv[])
396 {
397 channel_t *c = channel_find(parv[0]);
398
399 if (!c)
400 return;
401
402 /* Our uplink is trying to change the topic during burst,
403 * and we have already set a topic. Assume our change won.
404 * -- jilles */
405 if (si->s != NULL && si->s->uplink == me.me &&
406 !(si->s->flags & SF_EOB) && c->topic != NULL)
407 return;
408
409 handle_topic_from(si, c, parv[1], atol(parv[2]), parv[3]);
410 }
411
m_ping(sourceinfo_t * si,int parc,char * parv[])412 static void m_ping(sourceinfo_t *si, int parc, char *parv[])
413 {
414 /* reply to PING's */
415 sts(":%s PONG %s %s", me.name, me.name, parv[0]);
416 }
417
m_pong(sourceinfo_t * si,int parc,char * parv[])418 static void m_pong(sourceinfo_t *si, int parc, char *parv[])
419 {
420 server_t *s;
421
422 /* someone replied to our PING */
423 if (!parv[0])
424 return;
425 s = server_find(parv[0]);
426 if (s == NULL)
427 return;
428 /* Postpone EOB for our uplink until topic burst is also done */
429 if (s->uplink != me.me)
430 handle_eob(s);
431
432 if (s != si->s)
433 return;
434
435 me.uplinkpong = CURRTIME;
436
437 /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
438 }
439
m_burst(sourceinfo_t * si,int parc,char * parv[])440 static void m_burst(sourceinfo_t *si, int parc, char *parv[])
441 {
442 server_t *s;
443
444 /* Ignore "BURST" at start of burst */
445 if (parc != 1)
446 return;
447
448 s = server_find(me.actual);
449 if (s != NULL)
450 handle_eob(s);
451
452 if (me.bursting)
453 {
454 #ifdef HAVE_GETTIMEOFDAY
455 e_time(burstime, &burstime);
456
457 slog(LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms(&burstime) > 1000) ? (tv2ms(&burstime) / 1000) : tv2ms(&burstime), (tv2ms(&burstime) > 1000) ? "s" : "ms");
458
459 wallops("Finished synchronizing with network in %d %s.", (tv2ms(&burstime) > 1000) ? (tv2ms(&burstime) / 1000) : tv2ms(&burstime), (tv2ms(&burstime) > 1000) ? "s" : "ms");
460 #else
461 slog(LG_INFO, "m_pong(): finished synching with uplink");
462 wallops("Finished synchronizing with network.");
463 #endif
464
465 me.bursting = false;
466 }
467 }
468
m_privmsg(sourceinfo_t * si,int parc,char * parv[])469 static void m_privmsg(sourceinfo_t *si, int parc, char *parv[])
470 {
471 if (parc != 2)
472 return;
473
474 handle_message(si, parv[0], false, parv[1]);
475 }
476
m_notice(sourceinfo_t * si,int parc,char * parv[])477 static void m_notice(sourceinfo_t *si, int parc, char *parv[])
478 {
479 if (parc != 2)
480 return;
481
482 handle_message(si, parv[0], true, parv[1]);
483 }
484
m_sjoin(sourceinfo_t * si,int parc,char * parv[])485 static void m_sjoin(sourceinfo_t *si, int parc, char *parv[])
486 {
487 /*
488 * -> :proteus.malkier.net SJOIN 1073516550 #shrike +tn :@sycobuny @+rakaur
489 * also:
490 * -> :nenolod_ SJOIN 1117334567 #chat
491 */
492
493 channel_t *c;
494 bool keep_new_modes = true;
495 unsigned int userc;
496 char *userv[256];
497 unsigned int i;
498 time_t ts;
499 char *p;
500
501 if (parc >= 4 && si->s != NULL)
502 {
503 /* :origin SJOIN ts chan modestr [key or limits] :users */
504 c = channel_find(parv[1]);
505 ts = atol(parv[0]);
506
507 if (!c)
508 {
509 slog(LG_DEBUG, "m_sjoin(): new channel: %s", parv[1]);
510 c = channel_add(parv[1], ts, si->s);
511 }
512
513 if (ts == 0 || c->ts == 0)
514 {
515 if (c->ts != 0)
516 slog(LG_INFO, "m_sjoin(): server %s changing TS on %s from %lu to 0", si->s->name, c->name, (unsigned long)c->ts);
517 c->ts = 0;
518 hook_call_channel_tschange(c);
519 }
520 else if (ts < c->ts)
521 {
522 chanuser_t *cu;
523 mowgli_node_t *n;
524
525 /* the TS changed. a TS change requires the following things
526 * to be done to the channel: reset all modes to nothing, remove
527 * all status modes on known users on the channel (including ours),
528 * and set the new TS.
529 * also clear all bans and the topic
530 */
531
532 clear_simple_modes(c);
533 chanban_clear(c);
534 handle_topic_from(si, c, "", 0, "");
535
536 MOWGLI_ITER_FOREACH(n, c->members.head)
537 {
538 cu = (chanuser_t *)n->data;
539 if (cu->user->server == me.me)
540 {
541 /* it's a service, reop */
542 sts(":%s PART %s :Reop", cu->user->nick, c->name);
543 sts(":%s SJOIN %lu %s + :@%s", me.name, (unsigned long)ts, c->name, cu->user->nick);
544 cu->modes = CSTATUS_OP;
545 }
546 else
547 cu->modes = 0;
548 }
549
550 slog(LG_DEBUG, "m_sjoin(): TS changed for %s (%lu -> %lu)", c->name, (unsigned long)c->ts, (unsigned long)ts);
551
552 c->ts = ts;
553 hook_call_channel_tschange(c);
554 }
555 else if (ts > c->ts)
556 keep_new_modes = false;
557
558 if (keep_new_modes)
559 channel_mode(NULL, c, parc - 3, parv + 2);
560
561 userc = sjtoken(parv[parc - 1], ' ', userv);
562
563 if (keep_new_modes)
564 for (i = 0; i < userc; i++)
565 chanuser_add(c, userv[i]);
566 else
567 for (i = 0; i < userc; i++)
568 {
569 p = userv[i];
570 while (*p == '@' || *p == '%' || *p == '+')
571 p++;
572 chanuser_add(c, p);
573 }
574
575 if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
576 channel_delete(c);
577 }
578 else if (parc >= 2 && si->su != NULL)
579 {
580 c = channel_find(parv[1]);
581 ts = atol(parv[0]);
582
583 if (c == NULL || ts < c->ts)
584 {
585 /* just request a resynch, this will include
586 * the user joining -- jilles */
587 slog(LG_DEBUG, "m_sjoin(): requesting resynch for %s",
588 parv[1]);
589 sts("RESYNCH %s", parv[1]);
590 return;
591 }
592
593 chanuser_add(c, CLIENT_NAME(si->su));
594 }
595 else
596 {
597 slog(LG_DEBUG, "m_sjoin(): invalid source/parameters: origin %s parc %d",
598 si->su != NULL ? si->su->nick : (si->s != NULL ? si->s->name : "<none>"), parc);
599 }
600 }
601
m_part(sourceinfo_t * si,int parc,char * parv[])602 static void m_part(sourceinfo_t *si, int parc, char *parv[])
603 {
604 int chanc;
605 char *chanv[256];
606 int i;
607
608 chanc = sjtoken(parv[0], ',', chanv);
609 for (i = 0; i < chanc; i++)
610 {
611 slog(LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
612
613 chanuser_delete(channel_find(chanv[i]), si->su);
614 }
615 }
616
m_nick(sourceinfo_t * si,int parc,char * parv[])617 static void m_nick(sourceinfo_t *si, int parc, char *parv[])
618 {
619 server_t *s;
620 user_t *u;
621 char ipstring[64];
622 bool realchange;
623
624 /* -> NICK jilles 1 1136143909 +oi ~jilles 192.168.1.5 jaguar.test 0 3232235781 :Jilles Tjoelker */
625 if (parc == 10)
626 {
627 s = server_find(parv[6]);
628 if (!s)
629 {
630 slog(LG_DEBUG, "m_nick(): new user on nonexistant server: %s", parv[6]);
631 return;
632 }
633
634 slog(LG_DEBUG, "m_nick(): new user on `%s': %s", s->name, parv[0]);
635
636 if (!use_nickipstr)
637 {
638 struct in_addr ip;
639
640 ip.s_addr = ntohl(strtoul(parv[8], NULL, 10));
641 ipstring[0] = '\0';
642 if (!inet_ntop(AF_INET, &ip, ipstring, sizeof ipstring))
643 ipstring[0] = '\0';
644 }
645 else
646 mowgli_strlcpy(ipstring, parv[8], sizeof ipstring);
647
648 u = user_add(parv[0], parv[4], parv[5], NULL, ipstring, NULL, parv[9], s, atoi(parv[2]));
649 if (u == NULL)
650 return;
651
652 user_mode(u, parv[3]);
653
654 /* Ok, we have the user ready to go.
655 * Here's the deal -- if the user's SVID is before
656 * the start time, and not 0, then check to see
657 * if it's a registered account or not.
658 *
659 * If it IS registered, deal with that accordingly,
660 * via handle_burstlogin(). --nenolod
661 */
662 /* Changed to just check umode +r for now -- jilles */
663 /* This is ok because this ircd clears +r on nick changes
664 * -- jilles */
665 if (strchr(parv[3], 'r'))
666 handle_burstlogin(u, NULL, 0);
667
668 handle_nickchange(u);
669 }
670
671 /* if it's only 2 then it's a nickname change */
672 else if (parc == 2)
673 {
674 if (!si->su)
675 {
676 slog(LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
677 return;
678 }
679
680 slog(LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
681
682 realchange = irccasecmp(si->su->nick, parv[0]);
683
684 if (user_changenick(si->su, parv[0], atoi(parv[1])))
685 return;
686
687 /* fix up +r if necessary -- jilles */
688 if (realchange && should_reg_umode(si->su))
689 /* changed nick to registered one, reset +r */
690 sts(":%s SVSMODE %s +rd %lu", nicksvs.nick, parv[0], (unsigned long)CURRTIME);
691
692 handle_nickchange(si->su);
693 }
694 else
695 {
696 int i;
697 slog(LG_DEBUG, "m_nick(): got NICK with wrong number of params");
698
699 for (i = 0; i < parc; i++)
700 slog(LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
701 }
702 }
703
m_quit(sourceinfo_t * si,int parc,char * parv[])704 static void m_quit(sourceinfo_t *si, int parc, char *parv[])
705 {
706 slog(LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
707
708 /* user_delete() takes care of removing channels and so forth */
709 user_delete(si->su, parv[0]);
710 }
711
m_mode(sourceinfo_t * si,int parc,char * parv[])712 static void m_mode(sourceinfo_t *si, int parc, char *parv[])
713 {
714 channel_t *c;
715
716 if (*parv[0] == '#')
717 {
718 c = channel_find(parv[0]);
719 if (c == NULL)
720 {
721 slog(LG_DEBUG, "m_mode(): unknown channel %s", parv[0]);
722 return;
723 }
724 if (atol(parv[1]) > c->ts)
725 return;
726 channel_mode(NULL, channel_find(parv[0]), parc - 2, &parv[2]);
727 }
728 else
729 user_mode(user_find(parv[0]), parv[1]);
730 }
731
m_kick(sourceinfo_t * si,int parc,char * parv[])732 static void m_kick(sourceinfo_t *si, int parc, char *parv[])
733 {
734 user_t *u = user_find(parv[1]);
735 channel_t *c = channel_find(parv[0]);
736
737 /* -> :rakaur KICK #shrike rintaun :test */
738 slog(LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
739
740 if (!u)
741 {
742 slog(LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
743 return;
744 }
745
746 if (!c)
747 {
748 slog(LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
749 return;
750 }
751
752 if (!chanuser_find(c, u))
753 {
754 slog(LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
755 return;
756 }
757
758 chanuser_delete(c, u);
759
760 /* if they kicked us, let's rejoin */
761 if (is_internal_client(u))
762 {
763 slog(LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
764 join(parv[0], u->nick);
765 }
766 }
767
m_kill(sourceinfo_t * si,int parc,char * parv[])768 static void m_kill(sourceinfo_t *si, int parc, char *parv[])
769 {
770 handle_kill(si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
771 }
772
m_squit(sourceinfo_t * si,int parc,char * parv[])773 static void m_squit(sourceinfo_t *si, int parc, char *parv[])
774 {
775 slog(LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
776 server_delete(parv[0]);
777 }
778
m_server(sourceinfo_t * si,int parc,char * parv[])779 static void m_server(sourceinfo_t *si, int parc, char *parv[])
780 {
781 server_t *s;
782
783 slog(LG_DEBUG, "m_server(): new server: %s", parv[0]);
784 s = handle_server(si, parv[0], NULL, atoi(parv[1]), parv[2]);
785
786 if (s != NULL && s->uplink != me.me)
787 {
788 /* elicit PONG for EOB detection; pinging uplink is
789 * already done elsewhere -- jilles
790 */
791 sts(":%s PING %s %s", me.name, me.name, s->name);
792 }
793 }
794
m_stats(sourceinfo_t * si,int parc,char * parv[])795 static void m_stats(sourceinfo_t *si, int parc, char *parv[])
796 {
797 handle_stats(si->su, parv[0][0]);
798 }
799
m_admin(sourceinfo_t * si,int parc,char * parv[])800 static void m_admin(sourceinfo_t *si, int parc, char *parv[])
801 {
802 handle_admin(si->su);
803 }
804
m_version(sourceinfo_t * si,int parc,char * parv[])805 static void m_version(sourceinfo_t *si, int parc, char *parv[])
806 {
807 handle_version(si->su);
808 }
809
m_info(sourceinfo_t * si,int parc,char * parv[])810 static void m_info(sourceinfo_t *si, int parc, char *parv[])
811 {
812 handle_info(si->su);
813 }
814
m_whois(sourceinfo_t * si,int parc,char * parv[])815 static void m_whois(sourceinfo_t *si, int parc, char *parv[])
816 {
817 handle_whois(si->su, parv[1]);
818 }
819
m_trace(sourceinfo_t * si,int parc,char * parv[])820 static void m_trace(sourceinfo_t *si, int parc, char *parv[])
821 {
822 handle_trace(si->su, parv[0], parc >= 2 ? parv[1] : NULL);
823 }
824
m_away(sourceinfo_t * si,int parc,char * parv[])825 static void m_away(sourceinfo_t *si, int parc, char *parv[])
826 {
827 handle_away(si->su, parc >= 1 ? parv[0] : NULL);
828 }
829
m_join(sourceinfo_t * si,int parc,char * parv[])830 static void m_join(sourceinfo_t *si, int parc, char *parv[])
831 {
832 chanuser_t *cu;
833 mowgli_node_t *n, *tn;
834
835 /* JOIN 0 is really a part from all channels */
836 if (parv[0][0] == '0')
837 {
838 MOWGLI_ITER_FOREACH_SAFE(n, tn, si->su->channels.head)
839 {
840 cu = (chanuser_t *)n->data;
841 chanuser_delete(cu->chan, si->su);
842 }
843 }
844 }
845
m_pass(sourceinfo_t * si,int parc,char * parv[])846 static void m_pass(sourceinfo_t *si, int parc, char *parv[])
847 {
848 if (strcmp(curr_uplink->receive_pass, parv[0]))
849 {
850 slog(LG_INFO, "m_pass(): password mismatch from uplink; aborting");
851 runflags |= RF_SHUTDOWN;
852 }
853 }
854
m_error(sourceinfo_t * si,int parc,char * parv[])855 static void m_error(sourceinfo_t *si, int parc, char *parv[])
856 {
857 slog(LG_INFO, "m_error(): error from server: %s", parv[0]);
858 }
859
m_motd(sourceinfo_t * si,int parc,char * parv[])860 static void m_motd(sourceinfo_t *si, int parc, char *parv[])
861 {
862 handle_motd(si->su);
863 }
864
m_capab(sourceinfo_t * si,int parc,char * parv[])865 static void m_capab(sourceinfo_t *si, int parc, char *parv[])
866 {
867 int i;
868
869 use_nickipstr = false;
870
871 for (i = 0; i < parc; i++)
872 {
873 if (!irccasecmp(parv[i], "NICKIPSTR"))
874 {
875 slog(LG_DEBUG, "m_capab(): uplink supports string-based IP addresses, enabling support.");
876 use_nickipstr = true;
877 }
878 }
879
880 /* now burst services with NICKIP set correctly. */
881 services_init();
882 }
883
nick_group(hook_user_req_t * hdata)884 static void nick_group(hook_user_req_t *hdata)
885 {
886 user_t *u;
887
888 u = hdata->si->su != NULL && !irccasecmp(hdata->si->su->nick, hdata->mn->nick) ? hdata->si->su : user_find_named(hdata->mn->nick);
889 if (u != NULL && should_reg_umode(u))
890 sts(":%s SVSMODE %s +rd %lu", nicksvs.nick, u->nick, (unsigned long)CURRTIME);
891 }
892
nick_ungroup(hook_user_req_t * hdata)893 static void nick_ungroup(hook_user_req_t *hdata)
894 {
895 user_t *u;
896
897 u = hdata->si->su != NULL && !irccasecmp(hdata->si->su->nick, hdata->mn->nick) ? hdata->si->su : user_find_named(hdata->mn->nick);
898 if (u != NULL && !nicksvs.no_nick_ownership)
899 sts(":%s SVSMODE %s -r+d %lu", nicksvs.nick, u->nick, (unsigned long)CURRTIME);
900 }
901
_modinit(module_t * m)902 void _modinit(module_t * m)
903 {
904 MODULE_TRY_REQUEST_DEPENDENCY(m, "transport/rfc1459");
905
906 /* Symbol relocation voodoo. */
907 server_login = &bahamut_server_login;
908 introduce_nick = &bahamut_introduce_nick;
909 quit_sts = &bahamut_quit_sts;
910 wallops_sts = &bahamut_wallops_sts;
911 join_sts = &bahamut_join_sts;
912 chan_lowerts = &bahamut_chan_lowerts;
913 kick = &bahamut_kick;
914 msg = &bahamut_msg;
915 msg_global_sts = &bahamut_msg_global_sts;
916 notice_user_sts = &bahamut_notice_user_sts;
917 notice_global_sts = &bahamut_notice_global_sts;
918 notice_channel_sts = &bahamut_notice_channel_sts;
919 wallchops = &bahamut_wallchops;
920 numeric_sts = &bahamut_numeric_sts;
921 kill_id_sts = &bahamut_kill_id_sts;
922 part_sts = &bahamut_part_sts;
923 kline_sts = &bahamut_kline_sts;
924 unkline_sts = &bahamut_unkline_sts;
925 topic_sts = &bahamut_topic_sts;
926 mode_sts = &bahamut_mode_sts;
927 ping_sts = &bahamut_ping_sts;
928 ircd_on_login = &bahamut_on_login;
929 ircd_on_logout = &bahamut_on_logout;
930 jupe = &bahamut_jupe;
931 fnc_sts = &bahamut_fnc_sts;
932 invite_sts = &bahamut_invite_sts;
933 holdnick_sts = &bahamut_holdnick_sts;
934
935 mode_list = bahamut_mode_list;
936 ignore_mode_list = bahamut_ignore_mode_list;
937 status_mode_list = bahamut_status_mode_list;
938 prefix_mode_list = bahamut_prefix_mode_list;
939 user_mode_list = bahamut_user_mode_list;
940 ignore_mode_list_size = ARRAY_SIZE(bahamut_ignore_mode_list);
941
942 ircd = &Bahamut;
943
944 pcommand_add("CAPAB", m_capab, 0, MSRC_UNREG);
945 pcommand_add("PING", m_ping, 1, MSRC_USER | MSRC_SERVER);
946 pcommand_add("PONG", m_pong, 1, MSRC_SERVER);
947 pcommand_add("PRIVMSG", m_privmsg, 2, MSRC_USER);
948 pcommand_add("NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER);
949 pcommand_add("SJOIN", m_sjoin, 2, MSRC_USER | MSRC_SERVER);
950 pcommand_add("PART", m_part, 1, MSRC_USER);
951 pcommand_add("NICK", m_nick, 2, MSRC_USER | MSRC_SERVER);
952 pcommand_add("QUIT", m_quit, 1, MSRC_USER);
953 pcommand_add("MODE", m_mode, 2, MSRC_USER | MSRC_SERVER);
954 pcommand_add("KICK", m_kick, 2, MSRC_USER | MSRC_SERVER);
955 pcommand_add("KILL", m_kill, 1, MSRC_USER | MSRC_SERVER);
956 pcommand_add("SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER);
957 pcommand_add("SERVER", m_server, 3, MSRC_UNREG | MSRC_SERVER);
958 pcommand_add("STATS", m_stats, 2, MSRC_USER);
959 pcommand_add("ADMIN", m_admin, 1, MSRC_USER);
960 pcommand_add("VERSION", m_version, 1, MSRC_USER);
961 pcommand_add("INFO", m_info, 1, MSRC_USER);
962 pcommand_add("WHOIS", m_whois, 2, MSRC_USER);
963 pcommand_add("TRACE", m_trace, 1, MSRC_USER);
964 pcommand_add("AWAY", m_away, 0, MSRC_USER);
965 pcommand_add("JOIN", m_join, 1, MSRC_USER);
966 pcommand_add("PASS", m_pass, 1, MSRC_UNREG);
967 pcommand_add("ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER);
968 pcommand_add("TOPIC", m_topic, 4, MSRC_USER | MSRC_SERVER);
969 pcommand_add("MOTD", m_motd, 1, MSRC_USER);
970 pcommand_add("BURST", m_burst, 0, MSRC_SERVER);
971
972 hook_add_event("nick_group");
973 hook_add_nick_group(nick_group);
974 hook_add_event("nick_ungroup");
975 hook_add_nick_ungroup(nick_ungroup);
976
977 m->mflags = MODTYPE_CORE;
978
979 pmodule_loaded = true;
980 }
981
982 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
983 * vim:ts=8
984 * vim:sw=8
985 * vim:noexpandtab
986 */
987