1 /*
2 * Copyright (c) 2005-2006 William Pitcock, et al.
3 * Rights to this code are documented in doc/LICENSE.
4 *
5 * This file contains protocol support for IRCnet ircd's.
6 * Derived mainly from the documentation (or lack thereof)
7 * in my protocol bridge.
8 *
9 */
10
11 #include "atheme.h"
12 #include "uplink.h"
13 #include "pmodule.h"
14 #include "protocol/ircnet.h"
15
16 DECLARE_MODULE_V1("protocol/ircnet", true, _modinit, NULL, PACKAGE_STRING, VENDOR_STRING);
17
18 /* *INDENT-OFF* */
19
20 ircd_t IRCNet = {
21 .ircdname = "ircd 2.11.1p1 or later",
22 .tldprefix = "$$",
23 .uses_uid = true,
24 .uses_rcommand = false,
25 .uses_owner = false,
26 .uses_protect = false,
27 .uses_halfops = false,
28 .uses_p10 = false,
29 .uses_vhost = false,
30 .oper_only_modes = 0,
31 .owner_mode = 0,
32 .protect_mode = 0,
33 .halfops_mode = 0,
34 .owner_mchar = "+",
35 .protect_mchar = "+",
36 .halfops_mchar = "+",
37 .type = PROTOCOL_IRCNET,
38 .perm_mode = 0,
39 .oimmune_mode = 0,
40 .ban_like_modes = "beIR",
41 .except_mchar = 'e',
42 .invex_mchar = 'I',
43 .flags = IRCD_CIDR_BANS,
44 };
45
46 struct cmode_ ircnet_mode_list[] = {
47 { 'i', CMODE_INVITE },
48 { 'm', CMODE_MOD },
49 { 'n', CMODE_NOEXT },
50 { 'p', CMODE_PRIV },
51 { 's', CMODE_SEC },
52 { 't', CMODE_TOPIC },
53 { '\0', 0 }
54 };
55
56 struct extmode ircnet_ignore_mode_list[] = {
57 { '\0', 0 }
58 };
59
60 struct cmode_ ircnet_status_mode_list[] = {
61 { 'o', CSTATUS_OP },
62 { 'v', CSTATUS_VOICE },
63 { '\0', 0 }
64 };
65
66 struct cmode_ ircnet_prefix_mode_list[] = {
67 { '@', CSTATUS_OP },
68 { '+', CSTATUS_VOICE },
69 { '\0', 0 }
70 };
71
72 struct cmode_ ircnet_user_mode_list[] = {
73 { 'i', UF_INVIS },
74 { 'o', UF_IRCOP },
75 { '\0', 0 }
76 };
77
78 /* *INDENT-ON* */
79
80 /* login to our uplink */
ircnet_server_login(void)81 static unsigned int ircnet_server_login(void)
82 {
83 int ret;
84
85 ret = sts("PASS %s 0211010000 IRC|aDEFiIJMuw P", curr_uplink->send_pass);
86 if (ret == 1)
87 return 1;
88
89 me.bursting = true;
90
91 sts("SERVER %s 1 %s :%s", me.name, me.numeric, me.desc);
92
93 services_init();
94
95 sts(":%s EOB", me.numeric);
96
97 return 0;
98 }
99
100 /* introduce a client */
ircnet_introduce_nick(user_t * u)101 static void ircnet_introduce_nick(user_t *u)
102 {
103 const char *umode = user_get_umodestr(u);
104
105 sts(":%s UNICK %s %s %s %s 0.0.0.0 %s :%s", me.numeric, u->nick, u->uid, u->user, u->host, umode, u->gecos);
106 }
107
108 /* invite a user to a channel */
ircnet_invite_sts(user_t * sender,user_t * target,channel_t * channel)109 static void ircnet_invite_sts(user_t *sender, user_t *target, channel_t *channel)
110 {
111 int joined = 0;
112
113 /* Need to join to invite -- jilles */
114 if (!chanuser_find(channel, sender))
115 {
116 sts(":%s NJOIN %s :@%s", ME, channel->name, CLIENT_NAME(sender));
117 joined = 1;
118 }
119 /* ircnet's UID implementation is incomplete, in many places,
120 * like this one, it does not accept UIDs -- jilles */
121 sts(":%s INVITE %s %s", CLIENT_NAME(sender), target->nick, channel->name);
122 if (joined)
123 sts(":%s PART %s :Invited %s", CLIENT_NAME(sender), channel->name, target->nick);
124 }
125
ircnet_quit_sts(user_t * u,const char * reason)126 static void ircnet_quit_sts(user_t *u, const char *reason)
127 {
128 sts(":%s QUIT :%s", u->nick, reason);
129 }
130
131 /* join a channel */
ircnet_join_sts(channel_t * c,user_t * u,bool isnew,char * modes)132 static void ircnet_join_sts(channel_t *c, user_t *u, bool isnew, char *modes)
133 {
134 sts(":%s NJOIN %s :@%s", me.numeric, c->name, u->uid);
135 if (isnew && modes[0] && modes[1])
136 sts(":%s MODE %s %s", me.numeric, c->name, modes);
137 }
138
139 /* kicks a user from a channel */
ircnet_kick(user_t * source,channel_t * c,user_t * u,const char * reason)140 static void ircnet_kick(user_t *source, channel_t *c, user_t *u, const char *reason)
141 {
142 /* sigh server kicks will generate snotes
143 * but let's avoid joining N times for N kicks */
144 sts(":%s KICK %s %s :%s", source != NULL && chanuser_find(c, source) ? CLIENT_NAME(source) : ME, c->name, CLIENT_NAME(u), reason);
145
146 chanuser_delete(c, u);
147 }
148
149 /* PRIVMSG wrapper */
ircnet_msg(const char * from,const char * target,const char * fmt,...)150 static void ircnet_msg(const char *from, const char *target, const char *fmt, ...)
151 {
152 va_list ap;
153 char buf[BUFSIZE];
154
155 va_start(ap, fmt);
156 vsnprintf(buf, BUFSIZE, fmt, ap);
157 va_end(ap);
158
159 sts(":%s PRIVMSG %s :%s", from, target, buf);
160 }
161
ircnet_msg_global_sts(user_t * from,const char * mask,const char * text)162 static void ircnet_msg_global_sts(user_t *from, const char *mask, const char *text)
163 {
164 mowgli_node_t *n;
165 tld_t *tld;
166
167 if (!strcmp(mask, "*"))
168 {
169 MOWGLI_ITER_FOREACH(n, tldlist.head)
170 {
171 tld = n->data;
172 sts(":%s PRIVMSG %s*%s :%s", from ? CLIENT_NAME(from) : ME, ircd->tldprefix, tld->name, text);
173 }
174 }
175 else
176 sts(":%s PRIVMSG %s%s :%s", from ? CLIENT_NAME(from) : ME, ircd->tldprefix, mask, text);
177 }
178
179 /* NOTICE wrapper */
ircnet_notice_user_sts(user_t * from,user_t * target,const char * text)180 static void ircnet_notice_user_sts(user_t *from, user_t *target, const char *text)
181 {
182 sts(":%s NOTICE %s :%s", from ? CLIENT_NAME(from) : ME, CLIENT_NAME(target), text);
183 }
184
ircnet_notice_global_sts(user_t * from,const char * mask,const char * text)185 static void ircnet_notice_global_sts(user_t *from, const char *mask, const char *text)
186 {
187 mowgli_node_t *n;
188 tld_t *tld;
189
190 if (!strcmp(mask, "*"))
191 {
192 MOWGLI_ITER_FOREACH(n, tldlist.head)
193 {
194 tld = n->data;
195 sts(":%s NOTICE %s*%s :%s", from ? CLIENT_NAME(from) : ME, ircd->tldprefix, tld->name, text);
196 }
197 }
198 else
199 sts(":%s NOTICE %s%s :%s", from ? CLIENT_NAME(from) : ME, ircd->tldprefix, mask, text);
200 }
201
ircnet_notice_channel_sts(user_t * from,channel_t * target,const char * text)202 static void ircnet_notice_channel_sts(user_t *from, channel_t *target, const char *text)
203 {
204 if (from == NULL || chanuser_find(target, from))
205 sts(":%s NOTICE %s :%s", from ? CLIENT_NAME(from) : ME, target->name, text);
206 else
207 sts(":%s NOTICE %s :[%s:%s] %s", ME, target->name, from->nick, target->name, text);
208 }
209
210 /* numeric wrapper */
ircnet_numeric_sts(server_t * from,int numeric,user_t * target,const char * fmt,...)211 static void ircnet_numeric_sts(server_t *from, int numeric, user_t *target, const char *fmt, ...)
212 {
213 va_list ap;
214 char buf[BUFSIZE];
215
216 va_start(ap, fmt);
217 vsnprintf(buf, BUFSIZE, fmt, ap);
218 va_end(ap);
219
220 /* if we were to use SID/UID here, the user would see SID/UID :( */
221 sts(":%s %d %s %s", from->name, numeric, target->nick, buf);
222 }
223
224 /* KILL wrapper */
ircnet_kill_id_sts(user_t * killer,const char * id,const char * reason)225 static void ircnet_kill_id_sts(user_t *killer, const char *id, const char *reason)
226 {
227 if (killer != NULL)
228 sts(":%s KILL %s :%s!%s (%s)", CLIENT_NAME(killer), id, killer->host, killer->nick, reason);
229 else
230 sts(":%s KILL %s :%s (%s)", ME, id, me.name, reason);
231 }
232
233 /* PART wrapper */
ircnet_part_sts(channel_t * c,user_t * u)234 static void ircnet_part_sts(channel_t *c, user_t *u)
235 {
236 sts(":%s PART %s", u->nick, c->name);
237 }
238
239 /* server-to-server KLINE wrapper */
ircnet_kline_sts(const char * server,const char * user,const char * host,long duration,const char * reason)240 static void ircnet_kline_sts(const char *server, const char *user, const char *host, long duration, const char *reason)
241 {
242 service_t *svs;
243
244 /* this won't propagate!
245 * you'll need some bot/service on each server to do that */
246 if (irccasecmp(server, me.actual) && cnt.server > 2)
247 wallops("Missed a tkline");
248
249 svs = service_find("operserv");
250 sts(":%s TKLINE %lds %s@%s :%s", svs != NULL ? CLIENT_NAME(svs->me) : me.actual, duration, user, host, reason);
251 }
252
253 /* server-to-server UNKLINE wrapper */
ircnet_unkline_sts(const char * server,const char * user,const char * host)254 static void ircnet_unkline_sts(const char *server, const char *user, const char *host)
255 {
256 service_t *svs;
257
258 if (irccasecmp(server, me.actual) && cnt.server > 2)
259 wallops("Missed an untkline");
260
261 svs = service_find("operserv");
262 sts(":%s UNTKLINE %s@%s", svs != NULL ? CLIENT_NAME(svs->me) : me.actual, user, host);
263 }
264
265 /* topic wrapper */
ircnet_topic_sts(channel_t * c,user_t * source,const char * setter,time_t ts,time_t prevts,const char * topic)266 static void ircnet_topic_sts(channel_t *c, user_t *source, const char *setter, time_t ts, time_t prevts, const char *topic)
267 {
268 int joined = 0;
269
270 return_if_fail(c != NULL);
271
272 /* Need to join to set topic -- jilles */
273 if (!chanuser_find(c, source))
274 {
275 sts(":%s NJOIN %s :@%s", ME, c->name, CLIENT_NAME(source));
276 joined = 1;
277 }
278 sts(":%s TOPIC %s :%s", CLIENT_NAME(source), c->name, topic);
279 if (joined)
280 sts(":%s PART %s :Topic set for %s",
281 CLIENT_NAME(source), c->name, setter);
282 }
283
284 /* mode wrapper */
ircnet_mode_sts(char * sender,channel_t * target,char * modes)285 static void ircnet_mode_sts(char *sender, channel_t *target, char *modes)
286 {
287 user_t *u;
288
289 return_if_fail(sender != NULL);
290 return_if_fail(target != NULL);
291 return_if_fail(modes != NULL);
292
293 u = user_find(sender);
294
295 /* send it from the server if that service isn't on channel
296 * -- jilles */
297 sts(":%s MODE %s %s", chanuser_find(target, u) ? CLIENT_NAME(u) : ME, target->name, modes);
298 }
299
300 /* ping wrapper */
ircnet_ping_sts(void)301 static void ircnet_ping_sts(void)
302 {
303 sts("PING :%s", me.name);
304 }
305
306 /* protocol-specific stuff to do on login */
ircnet_on_login(user_t * u,myuser_t * account,const char * wantedhost)307 static void ircnet_on_login(user_t *u, myuser_t *account, const char *wantedhost)
308 {
309 /* nothing to do on ratbox */
310 return;
311 }
312
313 /* protocol-specific stuff to do on login */
ircnet_on_logout(user_t * u,const char * account)314 static bool ircnet_on_logout(user_t *u, const char *account)
315 {
316 /* nothing to do on ratbox */
317 return false;
318 }
319
ircnet_jupe(const char * server,const char * reason)320 static void ircnet_jupe(const char *server, const char *reason)
321 {
322 service_t *svs;
323 static char sid[4+1];
324 int i;
325 server_t *s;
326
327 svs = service_find("operserv");
328 sts(":%s SQUIT %s :%s", svs != NULL ? CLIENT_NAME(svs->me) : me.actual, server, reason);
329
330 s = server_find(server);
331 /* We need to wait for the SQUIT to be processed -- jilles */
332 if (s != NULL)
333 {
334 s->flags |= SF_JUPE_PENDING;
335 return;
336 }
337
338 /* dirty dirty make up some sid */
339 if (sid[0] == '\0')
340 mowgli_strlcpy(sid, me.numeric, sizeof sid);
341 do
342 {
343 i = 3;
344 for (;;)
345 {
346 if (sid[i] == 'Z')
347 {
348 sid[i] = '0';
349 i--;
350 /* eek, no more sids */
351 if (i < 0)
352 return;
353 continue;
354 }
355 else if (sid[i] == '9')
356 sid[i] = 'A';
357 else sid[i]++;
358 break;
359 }
360 } while (server_find(sid));
361
362 sts(":%s SERVER %s 2 %s 0211010000 :%s", me.name, server, sid, reason);
363 }
364
m_topic(sourceinfo_t * si,int parc,char * parv[])365 static void m_topic(sourceinfo_t *si, int parc, char *parv[])
366 {
367 channel_t *c = channel_find(parv[0]);
368
369 if (!c)
370 return;
371
372 handle_topic_from(si, c, si->su->nick, CURRTIME, parv[1]);
373 }
374
m_ping(sourceinfo_t * si,int parc,char * parv[])375 static void m_ping(sourceinfo_t *si, int parc, char *parv[])
376 {
377 /* reply to PING's */
378 sts(":%s PONG %s %s", me.name, me.name, parv[0]);
379 }
380
m_pong(sourceinfo_t * si,int parc,char * parv[])381 static void m_pong(sourceinfo_t *si, int parc, char *parv[])
382 {
383 /* someone replied to our PING */
384 if ((!parv[0]) || (strcasecmp(me.actual, parv[0])))
385 return;
386
387 me.uplinkpong = CURRTIME;
388
389 /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
390 }
391
m_eob(sourceinfo_t * si,int parc,char * parv[])392 static void m_eob(sourceinfo_t *si, int parc, char *parv[])
393 {
394 server_t *serv;
395 char sidbuf[4+1], *p;
396
397 handle_eob(si->s);
398 if (parc >= 1)
399 {
400 sidbuf[4] = '\0';
401 p = parv[0];
402 while (p[0] && p[1] && p[2] && p[3])
403 {
404 memcpy(sidbuf, p, 4);
405 serv = server_find(sidbuf);
406 handle_eob(serv);
407 if (p[4] != ',')
408 break;
409 p += 5;
410 }
411 }
412
413 if (me.bursting)
414 {
415 sts(":%s EOBACK", me.numeric);
416 #ifdef HAVE_GETTIMEOFDAY
417 e_time(burstime, &burstime);
418
419 slog(LG_INFO, "m_eob(): finished synching with uplink (%d %s)", (tv2ms(&burstime) > 1000) ? (tv2ms(&burstime) / 1000) : tv2ms(&burstime), (tv2ms(&burstime) > 1000) ? "s" : "ms");
420
421 wallops("Finished synchronizing with network in %d %s.", (tv2ms(&burstime) > 1000) ? (tv2ms(&burstime) / 1000) : tv2ms(&burstime), (tv2ms(&burstime) > 1000) ? "s" : "ms");
422 #else
423 slog(LG_INFO, "m_eob(): finished synching with uplink");
424 wallops("Finished synchronizing with network.");
425 #endif
426
427 me.bursting = false;
428 }
429 }
430
m_privmsg(sourceinfo_t * si,int parc,char * parv[])431 static void m_privmsg(sourceinfo_t *si, int parc, char *parv[])
432 {
433 if (parc != 2)
434 return;
435
436 handle_message(si, parv[0], false, parv[1]);
437 }
438
m_notice(sourceinfo_t * si,int parc,char * parv[])439 static void m_notice(sourceinfo_t *si, int parc, char *parv[])
440 {
441 if (parc != 2)
442 return;
443
444 handle_message(si, parv[0], true, parv[1]);
445 }
446
m_njoin(sourceinfo_t * si,int parc,char * parv[])447 static void m_njoin(sourceinfo_t *si, int parc, char *parv[])
448 {
449 channel_t *c;
450 unsigned int userc;
451 char *userv[256];
452 unsigned int i;
453
454 c = channel_find(parv[0]);
455
456 if (!c)
457 {
458 slog(LG_DEBUG, "m_njoin(): new channel: %s", parv[0]);
459 /* Give channels created during burst an older "TS"
460 * so they won't be deopped -- jilles */
461 c = channel_add(parv[0], si->s->flags & SF_EOB ? CURRTIME : CURRTIME - 601, si->s);
462 /* if !/+ channel, we don't want to do anything with it */
463 if (c == NULL)
464 return;
465 /* Check mode locks */
466 channel_mode_va(NULL, c, 1, "+");
467 }
468
469 userc = sjtoken(parv[parc - 1], ',', userv);
470
471 for (i = 0; i < userc; i++)
472 chanuser_add(c, userv[i]);
473
474 if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
475 channel_delete(c);
476 }
477
m_part(sourceinfo_t * si,int parc,char * parv[])478 static void m_part(sourceinfo_t *si, int parc, char *parv[])
479 {
480 int chanc;
481 char *chanv[256];
482 int i;
483
484 chanc = sjtoken(parv[0], ',', chanv);
485 for (i = 0; i < chanc; i++)
486 {
487 slog(LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
488
489 chanuser_delete(channel_find(chanv[i]), si->su);
490 }
491 }
492
m_nick(sourceinfo_t * si,int parc,char * parv[])493 static void m_nick(sourceinfo_t *si, int parc, char *parv[])
494 {
495 user_t *u;
496
497 /* got the right number of args for an introduction? */
498 if (parc == 7)
499 {
500 slog(LG_DEBUG, "m_nick(): new user on `%s': %s", si->s->name, parv[0]);
501
502 u = user_add(parv[0], parv[2], parv[3], NULL, parv[4], parv[1], parv[6], si->s, 0);
503 if (u == NULL)
504 return;
505
506 user_mode(u, parv[5]);
507 if (strchr(parv[5], 'a'))
508 handle_away(u, "Gone");
509
510 handle_nickchange(u);
511 }
512
513 /* if it's only 1 then it's a nickname change */
514 else if (parc == 1)
515 {
516 if (!si->su)
517 {
518 slog(LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
519 return;
520 }
521
522 slog(LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
523
524 if (user_changenick(si->su, parv[0], 0))
525 return;
526
527 handle_nickchange(si->su);
528 }
529 else
530 {
531 int i;
532 slog(LG_DEBUG, "m_nick(): got NICK with wrong number of params");
533
534 for (i = 0; i < parc; i++)
535 slog(LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
536 }
537 }
538
m_save(sourceinfo_t * si,int parc,char * parv[])539 static void m_save(sourceinfo_t *si, int parc, char *parv[])
540 {
541 user_t *u;
542
543 u = user_find(parv[0]);
544 if (u == NULL)
545 return;
546 if (!strcmp(u->nick, u->uid))
547 {
548 slog(LG_DEBUG, "m_save(): ignoring noop SAVE message for %s", u->nick);
549 return;
550 }
551 if (is_internal_client(u))
552 {
553 slog(LG_INFO, "m_save(): service %s got hit, changing back", u->nick);
554 sts(":%s NICK %s", u->uid, u->nick);
555 /* XXX services wars */
556 }
557 else
558 {
559 slog(LG_DEBUG, "m_save(): nickname change for `%s': %s", u->nick, u->uid);
560
561 if (user_changenick(u, u->uid, 0))
562 return;
563
564 handle_nickchange(u);
565 }
566 }
567
m_quit(sourceinfo_t * si,int parc,char * parv[])568 static void m_quit(sourceinfo_t *si, int parc, char *parv[])
569 {
570 slog(LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
571
572 /* user_delete() takes care of removing channels and so forth */
573 user_delete(si->su, parv[0]);
574 }
575
m_mode(sourceinfo_t * si,int parc,char * parv[])576 static void m_mode(sourceinfo_t *si, int parc, char *parv[])
577 {
578 /* The following is hackish, but it works, because user MODE
579 * is not used in bursts and users are not allowed to change away
580 * status using MODE.
581 * -- jilles */
582 if (*parv[0] == '#')
583 channel_mode(NULL, channel_find(parv[0]), parc - 1, &parv[1]);
584 else if (!strcmp(parv[1], "-a"))
585 handle_away(user_find(parv[0]), NULL);
586 else if (!strcmp(parv[1], "+a"))
587 handle_away(user_find(parv[0]), "Gone");
588 else
589 user_mode(user_find(parv[0]), parv[1]);
590 }
591
m_kick(sourceinfo_t * si,int parc,char * parv[])592 static void m_kick(sourceinfo_t *si, int parc, char *parv[])
593 {
594 user_t *u = user_find(parv[1]);
595 channel_t *c = channel_find(parv[0]);
596
597 /* -> :rakaur KICK #shrike rintaun :test */
598 slog(LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
599
600 if (!u)
601 {
602 slog(LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
603 return;
604 }
605
606 if (!c)
607 {
608 slog(LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
609 return;
610 }
611
612 if (!chanuser_find(c, u))
613 {
614 slog(LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
615 return;
616 }
617
618 chanuser_delete(c, u);
619
620 /* if they kicked us, let's rejoin */
621 if (is_internal_client(u))
622 {
623 slog(LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
624 join(parv[0], u->nick);
625 }
626 }
627
m_kill(sourceinfo_t * si,int parc,char * parv[])628 static void m_kill(sourceinfo_t *si, int parc, char *parv[])
629 {
630 handle_kill(si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
631 }
632
m_squit(sourceinfo_t * si,int parc,char * parv[])633 static void m_squit(sourceinfo_t *si, int parc, char *parv[])
634 {
635 slog(LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
636 if (server_find(parv[0]))
637 server_delete(parv[0]);
638 else if (si->su != NULL)
639 {
640 /* XXX we don't have a list of jupes, so let's just
641 * assume it is one if we don't know it */
642 slog(LG_INFO, "m_squit(): accepting SQUIT for jupe %s from %s", parv[0], si->su->nick);
643 sts(":%s WALLOPS :Received SQUIT %s from %s (%s)", me.numeric, parv[0], si->su->nick, parv[1]);
644 sts(":%s SQUIT %s :%s", me.numeric, parv[0], parv[1]);
645 }
646 }
647
m_server(sourceinfo_t * si,int parc,char * parv[])648 static void m_server(sourceinfo_t *si, int parc, char *parv[])
649 {
650 slog(LG_DEBUG, "m_server(): new server: %s", parv[0]);
651 handle_server(si, parv[0], parv[2], atoi(parv[1]), parv[parc - 1]);
652 }
653
m_smask(sourceinfo_t * si,int parc,char * parv[])654 static void m_smask(sourceinfo_t *si, int parc, char *parv[])
655 {
656 slog(LG_DEBUG, "m_smask(): new masked server: %s (%s)",
657 si->s->name, parv[0]);
658 handle_server(si, NULL, parv[0], si->s->hops + 1, si->s->desc);
659 }
660
m_stats(sourceinfo_t * si,int parc,char * parv[])661 static void m_stats(sourceinfo_t *si, int parc, char *parv[])
662 {
663 handle_stats(si->su, parv[0][0]);
664 }
665
m_admin(sourceinfo_t * si,int parc,char * parv[])666 static void m_admin(sourceinfo_t *si, int parc, char *parv[])
667 {
668 handle_admin(si->su);
669 }
670
m_version(sourceinfo_t * si,int parc,char * parv[])671 static void m_version(sourceinfo_t *si, int parc, char *parv[])
672 {
673 handle_version(si->su);
674 }
675
m_info(sourceinfo_t * si,int parc,char * parv[])676 static void m_info(sourceinfo_t *si, int parc, char *parv[])
677 {
678 handle_info(si->su);
679 }
680
m_whois(sourceinfo_t * si,int parc,char * parv[])681 static void m_whois(sourceinfo_t *si, int parc, char *parv[])
682 {
683 handle_whois(si->su, parv[1]);
684 }
685
m_trace(sourceinfo_t * si,int parc,char * parv[])686 static void m_trace(sourceinfo_t *si, int parc, char *parv[])
687 {
688 handle_trace(si->su, parv[0], parc >= 2 ? parv[1] : NULL);
689 }
690
m_join(sourceinfo_t * si,int parc,char * parv[])691 static void m_join(sourceinfo_t *si, int parc, char *parv[])
692 {
693 chanuser_t *cu;
694 mowgli_node_t *n, *tn;
695
696 /* JOIN 0 is really a part from all channels */
697 if (parv[0][0] == '0')
698 {
699 MOWGLI_ITER_FOREACH_SAFE(n, tn, si->su->channels.head)
700 {
701 cu = (chanuser_t *)n->data;
702 chanuser_delete(cu->chan, si->su);
703 }
704 }
705 }
706
m_pass(sourceinfo_t * si,int parc,char * parv[])707 static void m_pass(sourceinfo_t *si, int parc, char *parv[])
708 {
709 if (strcmp(curr_uplink->receive_pass, parv[0]))
710 {
711 slog(LG_INFO, "m_pass(): password mismatch from uplink; aborting");
712 runflags |= RF_SHUTDOWN;
713 }
714 }
715
m_error(sourceinfo_t * si,int parc,char * parv[])716 static void m_error(sourceinfo_t *si, int parc, char *parv[])
717 {
718 slog(LG_INFO, "m_error(): error from server: %s", parv[0]);
719 }
720
m_motd(sourceinfo_t * si,int parc,char * parv[])721 static void m_motd(sourceinfo_t *si, int parc, char *parv[])
722 {
723 handle_motd(si->su);
724 }
725
_modinit(module_t * m)726 void _modinit(module_t * m)
727 {
728 MODULE_TRY_REQUEST_DEPENDENCY(m, "transport/rfc1459");
729 MODULE_TRY_REQUEST_DEPENDENCY(m, "protocol/base36uid");
730
731 /* Symbol relocation voodoo. */
732 server_login = &ircnet_server_login;
733 introduce_nick = &ircnet_introduce_nick;
734 quit_sts = &ircnet_quit_sts;
735 join_sts = &ircnet_join_sts;
736 kick = &ircnet_kick;
737 msg = &ircnet_msg;
738 msg_global_sts = &ircnet_msg_global_sts;
739 notice_user_sts = &ircnet_notice_user_sts;
740 notice_global_sts = &ircnet_notice_global_sts;
741 notice_channel_sts = &ircnet_notice_channel_sts;
742 /* no wallchops, ircnet ircd does not support this */
743 numeric_sts = &ircnet_numeric_sts;
744 kill_id_sts = &ircnet_kill_id_sts;
745 part_sts = &ircnet_part_sts;
746 kline_sts = &ircnet_kline_sts;
747 unkline_sts = &ircnet_unkline_sts;
748 topic_sts = &ircnet_topic_sts;
749 mode_sts = &ircnet_mode_sts;
750 ping_sts = &ircnet_ping_sts;
751 ircd_on_login = &ircnet_on_login;
752 ircd_on_logout = &ircnet_on_logout;
753 jupe = &ircnet_jupe;
754 invite_sts = &ircnet_invite_sts;
755
756 mode_list = ircnet_mode_list;
757 ignore_mode_list = ircnet_ignore_mode_list;
758 status_mode_list = ircnet_status_mode_list;
759 prefix_mode_list = ircnet_prefix_mode_list;
760 user_mode_list = ircnet_user_mode_list;
761 ignore_mode_list_size = ARRAY_SIZE(ircnet_ignore_mode_list);
762
763 ircd = &IRCNet;
764
765 pcommand_add("PING", m_ping, 1, MSRC_USER | MSRC_SERVER);
766 pcommand_add("PONG", m_pong, 1, MSRC_SERVER);
767 pcommand_add("EOB", m_eob, 0, MSRC_SERVER);
768 pcommand_add("PRIVMSG", m_privmsg, 2, MSRC_USER);
769 pcommand_add("NOTICE", m_notice, 2, MSRC_UNREG | MSRC_USER | MSRC_SERVER);
770 pcommand_add("NJOIN", m_njoin, 2, MSRC_SERVER);
771 pcommand_add("PART", m_part, 1, MSRC_USER);
772 pcommand_add("NICK", m_nick, 1, MSRC_USER);
773 pcommand_add("UNICK", m_nick, 7, MSRC_SERVER);
774 pcommand_add("SAVE", m_save, 1, MSRC_SERVER);
775 pcommand_add("QUIT", m_quit, 1, MSRC_USER);
776 pcommand_add("MODE", m_mode, 2, MSRC_USER | MSRC_SERVER);
777 pcommand_add("KICK", m_kick, 2, MSRC_USER | MSRC_SERVER);
778 pcommand_add("KILL", m_kill, 1, MSRC_USER | MSRC_SERVER);
779 pcommand_add("SQUIT", m_squit, 1, MSRC_USER | MSRC_SERVER);
780 pcommand_add("SERVER", m_server, 4, MSRC_UNREG | MSRC_SERVER);
781 pcommand_add("SMASK", m_smask, 2, MSRC_SERVER);
782 pcommand_add("STATS", m_stats, 2, MSRC_USER);
783 pcommand_add("ADMIN", m_admin, 1, MSRC_USER);
784 pcommand_add("VERSION", m_version, 1, MSRC_USER);
785 pcommand_add("INFO", m_info, 1, MSRC_USER);
786 pcommand_add("WHOIS", m_whois, 2, MSRC_USER);
787 pcommand_add("TRACE", m_trace, 1, MSRC_USER);
788 pcommand_add("JOIN", m_join, 1, MSRC_USER);
789 pcommand_add("PASS", m_pass, 1, MSRC_UNREG);
790 pcommand_add("ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER);
791 pcommand_add("TOPIC", m_topic, 2, MSRC_USER);
792 pcommand_add("MOTD", m_motd, 1, MSRC_USER);
793
794 m->mflags = MODTYPE_CORE;
795
796 pmodule_loaded = true;
797 }
798
799 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
800 * vim:ts=8
801 * vim:sw=8
802 * vim:noexpandtab
803 */
804