1 /*
2 * Copyright (c) 2005-2006 William Pitcock, et al.
3 * Rights to this code are documented in doc/LICENSE.
4 *
5 * This file contains protocol support for P10 ircd's.
6 * Some sources used: Run's documentation, beware's description,
7 * raw data sent by asuka.
8 *
9 */
10
11 #include "atheme.h"
12 #include "uplink.h"
13 #include "pmodule.h"
14
15 DECLARE_MODULE_V1("protocol/p10-generic", true, _modinit, NULL, PACKAGE_STRING, VENDOR_STRING);
16
17 static void check_hidehost(user_t *u);
18
19 /* login to our uplink */
p10_server_login(void)20 static unsigned int p10_server_login(void)
21 {
22 int ret;
23
24 ret = sts("PASS :%s", curr_uplink->send_pass);
25 if (ret == 1)
26 return 1;
27
28 me.bursting = true;
29
30 /* SERVER irc.p10.org 1 933022556 947908144 J10 AA]]] :[127.0.0.1] A Undernet Server */
31 sts("SERVER %s 1 %lu %lu J10 %s]]] +s6 :%s", me.name, (unsigned long)me.start, (unsigned long)CURRTIME, me.numeric, me.desc);
32
33 services_init();
34
35 sts("%s EB", me.numeric);
36
37 return 0;
38 }
39
40 /* introduce a client */
p10_introduce_nick(user_t * u)41 static void p10_introduce_nick(user_t *u)
42 {
43 const char *umode = user_get_umodestr(u);
44
45 sts("%s N %s 1 %lu %s %s %sk ]]]]]] %s :%s", me.numeric, u->nick, (unsigned long)u->ts, u->user, u->host, umode, u->uid, u->gecos);
46 }
47
48 /* invite a user to a channel */
p10_invite_sts(user_t * sender,user_t * target,channel_t * channel)49 static void p10_invite_sts(user_t *sender, user_t *target, channel_t *channel)
50 {
51 /* target is a nick, weird eh? -- jilles */
52 sts("%s I %s %s", sender->uid, target->nick, channel->name);
53 }
54
p10_quit_sts(user_t * u,const char * reason)55 static void p10_quit_sts(user_t *u, const char *reason)
56 {
57 sts("%s Q :%s", u->uid, reason);
58 }
59
60 /* WALLOPS wrapper */
p10_wallops_sts(const char * text)61 static void p10_wallops_sts(const char *text)
62 {
63 sts("%s WA :%s", me.numeric, text);
64 }
65
66 /* join a channel */
p10_join_sts(channel_t * c,user_t * u,bool isnew,char * modes)67 static void p10_join_sts(channel_t *c, user_t *u, bool isnew, char *modes)
68 {
69 /* If the channel doesn't exist, we need to create it. */
70 if (isnew)
71 {
72 sts("%s C %s %lu", u->uid, c->name, (unsigned long)c->ts);
73 if (modes[0] && modes[1])
74 sts("%s M %s %s", u->uid, c->name, modes);
75 }
76 else
77 {
78 sts("%s J %s %lu", u->uid, c->name, (unsigned long)c->ts);
79 sts("%s M %s +o %s", me.numeric, c->name, u->uid);
80 }
81 }
82
p10_chan_lowerts(channel_t * c,user_t * u)83 static void p10_chan_lowerts(channel_t *c, user_t *u)
84 {
85 slog(LG_DEBUG, "p10_chan_lowerts(): lowering TS for %s to %lu",
86 c->name, (unsigned long)c->ts);
87 sts("%s B %s %lu %s %s:o", me.numeric, c->name, (unsigned long)c->ts,
88 channel_modes(c, true), u->uid);
89 chanban_clear(c);
90 }
91
92 /* kicks a user from a channel */
p10_kick(user_t * source,channel_t * c,user_t * u,const char * reason)93 static void p10_kick(user_t *source, channel_t *c, user_t *u, const char *reason)
94 {
95 if (chanuser_find(c, source))
96 sts("%s K %s %s :%s", source->uid, c->name, u->uid, reason);
97 else
98 sts("%s K %s %s :%s", me.numeric, c->name, u->uid, reason);
99
100 chanuser_delete(c, u);
101 }
102
103 /* PRIVMSG wrapper */
p10_msg(const char * from,const char * target,const char * fmt,...)104 static void p10_msg(const char *from, const char *target, const char *fmt, ...)
105 {
106 va_list ap;
107 user_t *u = user_find_named(from);
108 char buf[BUFSIZE];
109
110 if (!u)
111 return;
112
113 va_start(ap, fmt);
114 vsnprintf(buf, BUFSIZE, fmt, ap);
115 va_end(ap);
116
117 sts("%s P %s :%s", u->uid, target, buf);
118 }
119
p10_msg_global_sts(user_t * from,const char * mask,const char * text)120 static void p10_msg_global_sts(user_t *from, const char *mask, const char *text)
121 {
122 mowgli_node_t *n;
123 tld_t *tld;
124
125 if (!strcmp(mask, "*"))
126 {
127 MOWGLI_ITER_FOREACH(n, tldlist.head)
128 {
129 tld = n->data;
130 sts("%s P %s*%s :%s", from ? from->uid : me.numeric, ircd->tldprefix, tld->name, text);
131 }
132 }
133 else
134 sts("%s P %s%s :%s", from ? from->uid : me.numeric, ircd->tldprefix, mask, text);
135 }
136
137 /* NOTICE wrapper */
p10_notice_user_sts(user_t * from,user_t * target,const char * text)138 static void p10_notice_user_sts(user_t *from, user_t *target, const char *text)
139 {
140 sts("%s O %s :%s", from ? from->uid : me.numeric, target->uid, text);
141 }
142
p10_notice_global_sts(user_t * from,const char * mask,const char * text)143 static void p10_notice_global_sts(user_t *from, const char *mask, const char *text)
144 {
145 mowgli_node_t *n;
146 tld_t *tld;
147
148 if (!strcmp(mask, "*"))
149 {
150 MOWGLI_ITER_FOREACH(n, tldlist.head)
151 {
152 tld = n->data;
153 sts("%s O %s*%s :%s", from ? from->uid : me.numeric, ircd->tldprefix, tld->name, text);
154 }
155 }
156 else
157 sts("%s O %s%s :%s", from ? from->uid : me.numeric, ircd->tldprefix, mask, text);
158 }
159
p10_notice_channel_sts(user_t * from,channel_t * target,const char * text)160 static void p10_notice_channel_sts(user_t *from, channel_t *target, const char *text)
161 {
162 if (from == NULL || chanuser_find(target, from))
163 sts("%s O %s :%s", from ? from->uid : me.numeric, target->name, text);
164 else
165 sts("%s O %s :[%s:%s] %s", me.numeric, target->name, from->nick, target->name, text);
166 }
167
p10_wallchops(user_t * sender,channel_t * channel,const char * message)168 static void p10_wallchops(user_t *sender, channel_t *channel, const char *message)
169 {
170 sts("%s WC %s :%s", sender->uid, channel->name, message);
171 }
172
173 /* numeric wrapper */
p10_numeric_sts(server_t * from,int numeric,user_t * target,const char * fmt,...)174 static void p10_numeric_sts(server_t *from, int numeric, user_t *target, const char *fmt, ...)
175 {
176 va_list ap;
177 char buf[BUFSIZE];
178
179 va_start(ap, fmt);
180 vsnprintf(buf, BUFSIZE, fmt, ap);
181 va_end(ap);
182
183 sts("%s %d %s %s", from->sid, numeric, target->uid, buf);
184 }
185
186 /* KILL wrapper */
p10_kill_id_sts(user_t * killer,const char * id,const char * reason)187 static void p10_kill_id_sts(user_t *killer, const char *id, const char *reason)
188 {
189 if (killer != NULL)
190 sts("%s D %s :%s!%s (%s)", killer->uid, id, killer->host, killer->nick, reason);
191 else
192 sts("%s D %s :%s (%s)", me.numeric, id, me.name, reason);
193 }
194
195 /* PART wrapper */
p10_part_sts(channel_t * c,user_t * u)196 static void p10_part_sts(channel_t *c, user_t *u)
197 {
198 sts("%s L %s", u->uid, c->name);
199 }
200
201 /* server-to-server KLINE wrapper */
p10_kline_sts(const char * server,const char * user,const char * host,long duration,const char * reason)202 static void p10_kline_sts(const char *server, const char *user, const char *host, long duration, const char *reason)
203 {
204 /* hold permanent akills for four weeks -- jilles */
205 sts("%s GL * +%s@%s %ld :%s", me.numeric, user, host, duration > 0 ? duration : 2419200, reason);
206 }
207
208 /* server-to-server UNKLINE wrapper */
p10_unkline_sts(const char * server,const char * user,const char * host)209 static void p10_unkline_sts(const char *server, const char *user, const char *host)
210 {
211 sts("%s GL * -%s@%s", me.numeric, user, host);
212 }
213
p10_xline_sts(const char * server,const char * realname,long duration,const char * reason)214 static void p10_xline_sts(const char *server, const char *realname, long duration, const char *reason)
215 {
216 /* hold permanent sglines for four weeks -- jilles */
217 sts("%s GL * +$R%s %ld :%s", me.numeric, realname, duration > 0 ? duration : 2419200, reason);
218 }
219
p10_unxline_sts(const char * server,const char * realname)220 static void p10_unxline_sts(const char *server, const char *realname)
221 {
222 sts("%s GL * -$R%s", me.numeric, realname);
223 }
224
p10_qline_sts(const char * server,const char * name,long duration,const char * reason)225 static void p10_qline_sts(const char *server, const char *name, long duration, const char *reason)
226 {
227 if (!VALID_CHANNEL_PFX(name))
228 {
229 slog(LG_INFO, "SQLINE: Could not set SQLINE on \2%s\2, not supported by ircu.", name);
230 return;
231 }
232
233 /* hold permanent sqlines for four weeks -- jilles */
234 sts("%s GL * +%s %ld :%s", me.numeric, name, duration > 0 ? duration : 2419200, reason);
235 }
236
p10_unqline_sts(const char * server,const char * name)237 static void p10_unqline_sts(const char *server, const char *name)
238 {
239 if (!VALID_CHANNEL_PFX(name))
240 {
241 slog(LG_INFO, "SQLINE: Could not remove SQLINE on \2%s\2, not supported by ircu.", name);
242 return;
243 }
244
245 sts("%s GL * -%s", me.numeric, name);
246 }
247
248 /* topic wrapper */
p10_topic_sts(channel_t * c,user_t * source,const char * setter,time_t ts,time_t prevts,const char * topic)249 static void p10_topic_sts(channel_t *c, user_t *source, const char *setter, time_t ts, time_t prevts, const char *topic)
250 {
251 if (ts > prevts || prevts == 0)
252 sts("%s T %s %lu %lu :%s", source->uid, c->name, (unsigned long)c->ts, (unsigned long)ts, topic);
253 else
254 {
255 ts = CURRTIME;
256 if (ts < prevts)
257 ts = prevts + 1;
258 sts("%s T %s %lu %lu :%s", source->uid, c->name, (unsigned long)c->ts, (unsigned long)ts, topic);
259 c->topicts = ts;
260 }
261 }
262
263 /* mode wrapper */
p10_mode_sts(char * sender,channel_t * target,char * modes)264 static void p10_mode_sts(char *sender, channel_t *target, char *modes)
265 {
266 user_t *fptr;
267
268 return_if_fail(sender != NULL);
269 return_if_fail(target != NULL);
270 return_if_fail(modes != NULL);
271
272 fptr = user_find_named(sender);
273
274 return_if_fail(fptr != NULL);
275
276 if (chanuser_find(target, fptr))
277 sts("%s M %s %s", fptr->uid, target->name, modes);
278 else
279 sts("%s M %s %s", me.numeric, target->name, modes);
280 }
281
282 /* ping wrapper */
p10_ping_sts(void)283 static void p10_ping_sts(void)
284 {
285 sts("%s G !%lu %s %lu", me.numeric, (unsigned long)CURRTIME, me.name, (unsigned long)CURRTIME);
286 }
287
288 /* protocol-specific stuff to do on login */
p10_on_login(user_t * u,myuser_t * mu,const char * wantedhost)289 static void p10_on_login(user_t *u, myuser_t *mu, const char *wantedhost)
290 {
291 return_if_fail(u != NULL);
292
293 sts("%s AC %s %s %lu", me.numeric, u->uid, entity(mu)->name,
294 (unsigned long)mu->registered);
295 check_hidehost(u);
296 }
297
298 /* P10 does not support logout, so kill the user
299 * we can't keep track of which logins are stale and which aren't -- jilles */
p10_on_logout(user_t * u,const char * account)300 static bool p10_on_logout(user_t *u, const char *account)
301 {
302 return_val_if_fail(u != NULL, false);
303
304 kill_user(NULL, u, "Forcing logout %s -> %s", u->nick, account);
305 return true;
306 }
307
p10_jupe(const char * server,const char * reason)308 static void p10_jupe(const char *server, const char *reason)
309 {
310 server_t *s;
311
312 /* hold it for a day (arbitrary) -- jilles */
313 /* get rid of local deactivation too */
314 s = server_find(server);
315 if (s != NULL && s->uplink != NULL)
316 sts("%s JU %s +%s %d %lu :%s", me.numeric, s->uplink->sid, server, 86400, (unsigned long)CURRTIME, reason);
317 sts("%s JU * +%s %d %lu :%s", me.numeric, server, 86400, (unsigned long)CURRTIME, reason);
318 }
319
p10_sasl_sts(char * target,char mode,char * data)320 static void p10_sasl_sts(char *target, char mode, char *data)
321 {
322 sts("%s XR %c%c %s :SASL:%c:%s", me.numeric, target[0], target[1], target, mode, data);
323 }
324
p10_svslogin_sts(char * target,char * nick,char * user,char * host,myuser_t * account)325 static void p10_svslogin_sts(char *target, char *nick, char *user, char *host, myuser_t *account)
326 {
327 sts("%s XR %c%c %s :SASL:L:%s", me.numeric, target[0], target[1], target, entity(account)->name);
328 }
329
m_topic(sourceinfo_t * si,int parc,char * parv[])330 static void m_topic(sourceinfo_t *si, int parc, char *parv[])
331 {
332 channel_t *c = channel_find(parv[0]);
333 const char *source;
334 time_t ts = 0;
335
336 if (!c)
337 return;
338
339 if (si->s != NULL)
340 source = si->s->name;
341 else
342 source = si->su->nick;
343
344 if (parc > 2)
345 ts = atoi(parv[parc - 2]);
346 if (ts == 0)
347 ts = CURRTIME;
348 else if (c->topic != NULL && ts < c->topicts)
349 return;
350 handle_topic_from(si, c, source, ts, parv[parc - 1]);
351 }
352
353 /* AB G !1119920789.573932 services.atheme.org 1119920789.573932 */
m_ping(sourceinfo_t * si,int parc,char * parv[])354 static void m_ping(sourceinfo_t *si, int parc, char *parv[])
355 {
356 /* reply to PING's */
357 if (si->su != NULL)
358 sts("%s Z %s %s", me.numeric, me.numeric, si->su->nick);
359 else if (parv[0][0] != '!')
360 sts("%s Z %s %s", me.numeric, me.numeric, si->s->name);
361 else
362 sts("%s Z %s %s", me.numeric, me.numeric, parv[0]);
363 }
364
m_pong(sourceinfo_t * si,int parc,char * parv[])365 static void m_pong(sourceinfo_t *si, int parc, char *parv[])
366 {
367 me.uplinkpong = CURRTIME;
368
369 /* -> :test.projectxero.net PONG test.projectxero.net :shrike.malkier.net */
370 if (me.bursting)
371 {
372 #ifdef HAVE_GETTIMEOFDAY
373 e_time(burstime, &burstime);
374
375 slog(LG_INFO, "m_pong(): finished synching with uplink (%d %s)", (tv2ms(&burstime) > 1000) ? (tv2ms(&burstime) / 1000) : tv2ms(&burstime), (tv2ms(&burstime) > 1000) ? "s" : "ms");
376
377 wallops("Finished synchronizing with network in %d %s.", (tv2ms(&burstime) > 1000) ? (tv2ms(&burstime) / 1000) : tv2ms(&burstime), (tv2ms(&burstime) > 1000) ? "s" : "ms");
378 #else
379 slog(LG_INFO, "m_pong(): finished synching with uplink");
380 wallops("Finished synchronizing with network.");
381 #endif
382
383 me.bursting = false;
384 }
385 }
386
m_privmsg(sourceinfo_t * si,int parc,char * parv[])387 static void m_privmsg(sourceinfo_t *si, int parc, char *parv[])
388 {
389 if (parc != 2)
390 return;
391
392 handle_message(si, parv[0], false, parv[1]);
393 }
394
m_notice(sourceinfo_t * si,int parc,char * parv[])395 static void m_notice(sourceinfo_t *si, int parc, char *parv[])
396 {
397 if (parc != 2)
398 return;
399
400 handle_message(si, parv[0], true, parv[1]);
401 }
402
m_create(sourceinfo_t * si,int parc,char * parv[])403 static void m_create(sourceinfo_t *si, int parc, char *parv[])
404 {
405 char buf[BUFSIZE];
406 int chanc;
407 char *chanv[256];
408 int i;
409 time_t ts;
410
411 chanc = sjtoken(parv[0], ',', chanv);
412
413 for (i = 0; i < chanc; i++)
414 {
415 channel_t *c = channel_add(chanv[i], ts = atoi(parv[1]), si->su->server);
416
417 /* Tell the core to check mode locks now,
418 * otherwise it may only happen after the next
419 * mode change.
420 * P10 does not allow any redundant modes
421 * so this will not look ugly. -- jilles */
422 channel_mode_va(NULL, c, 1, "+");
423
424 if (ts <= c->ts)
425 {
426 buf[0] = '@';
427 buf[1] = '\0';
428 }
429 else
430 buf[0] = '\0';
431
432 mowgli_strlcat(buf, si->su->uid, BUFSIZE);
433
434 chanuser_add(c, buf);
435 }
436 }
437
m_join(sourceinfo_t * si,int parc,char * parv[])438 static void m_join(sourceinfo_t *si, int parc, char *parv[])
439 {
440 int chanc;
441 char *chanv[256];
442 int i;
443 mowgli_node_t *n, *tn;
444 chanuser_t *cu;
445
446 /* JOIN 0 is really a part from all channels */
447 if (!strcmp(parv[0], "0"))
448 {
449 MOWGLI_ITER_FOREACH_SAFE(n, tn, si->su->channels.head)
450 {
451 cu = (chanuser_t *) n->data;
452 chanuser_delete(cu->chan, si->su);
453 }
454 return;
455 }
456 if (parc < 2)
457 return;
458
459 chanc = sjtoken(parv[0], ',', chanv);
460
461 for (i = 0; i < chanc; i++)
462 {
463 channel_t *c = channel_find(chanv[i]);
464
465 if (!c)
466 {
467 c = channel_add(chanv[i], atoi(parv[1]), si->su->server);
468 channel_mode_va(NULL, c, 1, "+");
469 }
470
471 chanuser_add(c, si->su->uid);
472 }
473 }
474
m_burst(sourceinfo_t * si,int parc,char * parv[])475 static void m_burst(sourceinfo_t *si, int parc, char *parv[])
476 {
477 channel_t *c;
478 unsigned int modec;
479 char *modev[16];
480 unsigned int userc;
481 char *userv[256];
482 unsigned int i;
483 int j;
484 char prefix[16];
485 char newnick[16+NICKLEN];
486 char *p;
487 time_t ts;
488 bool keep_new_modes = true;
489
490 /* S BURST <channel> <ts> [parameters]
491 * parameters can be:
492 * +<simple mode>
493 * %<bans separated with spaces>
494 * <nicks>
495 */
496 ts = atoi(parv[1]);
497
498 c = channel_find(parv[0]);
499
500 if (c == NULL)
501 {
502 slog(LG_DEBUG, "m_burst(): new channel: %s", parv[0]);
503 c = channel_add(parv[0], ts, si->s);
504 }
505 if (ts < c->ts)
506 {
507 chanuser_t *cu;
508 mowgli_node_t *n;
509
510 clear_simple_modes(c);
511 chanban_clear(c);
512 handle_topic_from(si, c, "", 0, "");
513 MOWGLI_ITER_FOREACH(n, c->members.head)
514 {
515 cu = (chanuser_t *)n->data;
516 if (cu->user->server == me.me)
517 {
518 /* it's a service, reop */
519 sts("%s M %s +o %s", me.numeric, c->name, CLIENT_NAME(cu->user));
520 cu->modes = CSTATUS_OP;
521 }
522 else
523 cu->modes = 0;
524 }
525
526 slog(LG_DEBUG, "m_burst(): TS changed for %s (%lu -> %lu)", c->name, (unsigned long)c->ts, (unsigned long)ts);
527 c->ts = ts;
528 hook_call_channel_tschange(c);
529 }
530 else if (ts > c->ts)
531 keep_new_modes = false;
532 if (parc < 3 || parv[2][0] != '+')
533 {
534 /* Tell the core to check mode locks now,
535 * otherwise it may only happen after the next
536 * mode change. -- jilles */
537 channel_mode_va(NULL, c, 1, "+");
538 }
539
540 j = 2;
541 while (j < parc)
542 {
543 if (parv[j][0] == '+')
544 {
545 modec = 0;
546 modev[modec++] = parv[j++];
547 if (strchr(modev[0], 'k') && j < parc)
548 modev[modec++] = parv[j++];
549 if (strchr(modev[0], 'l') && j < parc)
550 modev[modec++] = parv[j++];
551 if (keep_new_modes)
552 channel_mode(NULL, c, modec, modev);
553 }
554 else if (parv[j][0] == '%')
555 {
556 userc = sjtoken(parv[j++] + 1, ' ', userv);
557 if (keep_new_modes)
558 for (i = 0; i < userc; i++)
559 chanban_add(c, userv[i], 'b');
560 }
561 else
562 {
563 userc = sjtoken(parv[j++], ',', userv);
564
565 prefix[0] = '\0';
566 for (i = 0; i < userc; i++)
567 {
568 p = strchr(userv[i], ':');
569 if (p != NULL)
570 {
571 *p = '\0';
572 prefix[0] = '\0';
573 prefix[1] = '\0';
574 prefix[2] = '\0';
575 p++;
576 if (keep_new_modes)
577 while (*p)
578 {
579 if (*p == 'o' || (*p >= '0' && *p <= '9' && !prefix[0]))
580 prefix[prefix[0] ? 1 : 0] = '@';
581 else if (*p == 'v')
582 prefix[prefix[0] ? 1 : 0] = '+';
583 p++;
584 }
585 }
586 mowgli_strlcpy(newnick, prefix, sizeof newnick);
587 mowgli_strlcat(newnick, userv[i], sizeof newnick);
588 chanuser_add(c, newnick);
589 }
590 }
591 }
592
593 if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
594 channel_delete(c);
595 }
596
m_part(sourceinfo_t * si,int parc,char * parv[])597 static void m_part(sourceinfo_t *si, int parc, char *parv[])
598 {
599 int chanc;
600 char *chanv[256];
601 int i;
602
603 chanc = sjtoken(parv[0], ',', chanv);
604 for (i = 0; i < chanc; i++)
605 {
606 slog(LG_DEBUG, "m_part(): user left channel: %s -> %s", si->su->nick, chanv[i]);
607
608 chanuser_delete(channel_find(chanv[i]), si->su);
609 }
610 }
611
m_nick(sourceinfo_t * si,int parc,char * parv[])612 static void m_nick(sourceinfo_t *si, int parc, char *parv[])
613 {
614 user_t *u;
615 char ipstring[HOSTIPLEN];
616 char *p;
617
618 /* got the right number of args for an introduction? */
619 if (parc >= 8)
620 {
621 /* -> AB N jilles 1 1137687480 jilles jaguar.test +oiwgrx jilles B]AAAB ABAAE :Jilles Tjoelker */
622 /* -> AB N test4 1 1137690148 jilles jaguar.test +iw B]AAAB ABAAG :Jilles Tjoelker */
623 slog(LG_DEBUG, "m_nick(): new user on `%s': %s", si->s->name, parv[0]);
624
625 decode_p10_ip(parv[parc - 3], ipstring);
626 u = user_add(parv[0], parv[3], parv[4], NULL, ipstring, parv[parc - 2], parv[parc - 1], si->s, atoi(parv[2]));
627 if (u == NULL)
628 return;
629
630 if (parv[5][0] == '+')
631 {
632 user_mode(u, parv[5]);
633 if (strchr(parv[5], 'r'))
634 {
635 p = strchr(parv[6], ':');
636 if (p != NULL)
637 *p++ = '\0';
638 handle_burstlogin(u, parv[6], p ? atol(p) : 0);
639 /* killed to force logout? */
640 if (user_find(parv[parc - 2]) == NULL)
641 return;
642 }
643 if (strchr(parv[5], 'x'))
644 {
645 u->flags |= UF_HIDEHOSTREQ;
646 /* this must be after setting the account name */
647 check_hidehost(u);
648 }
649 }
650
651 handle_nickchange(u);
652 }
653 /* if it's only 2 then it's a nickname change */
654 else if (parc == 2)
655 {
656 if (!si->su)
657 {
658 slog(LG_DEBUG, "m_nick(): server trying to change nick: %s", si->s != NULL ? si->s->name : "<none>");
659 return;
660 }
661
662 slog(LG_DEBUG, "m_nick(): nickname change from `%s': %s", si->su->nick, parv[0]);
663
664 if (user_changenick(si->su, parv[0], atoi(parv[1])))
665 return;
666
667 handle_nickchange(si->su);
668 }
669 else
670 {
671 int i;
672 slog(LG_DEBUG, "m_nick(): got NICK with wrong (%d) number of params", parc);
673
674 for (i = 0; i < parc; i++)
675 slog(LG_DEBUG, "m_nick(): parv[%d] = %s", i, parv[i]);
676 }
677 }
678
m_quit(sourceinfo_t * si,int parc,char * parv[])679 static void m_quit(sourceinfo_t *si, int parc, char *parv[])
680 {
681 slog(LG_DEBUG, "m_quit(): user leaving: %s", si->su->nick);
682
683 /* user_delete() takes care of removing channels and so forth */
684 user_delete(si->su, parv[0]);
685 }
686
m_mode(sourceinfo_t * si,int parc,char * parv[])687 static void m_mode(sourceinfo_t *si, int parc, char *parv[])
688 {
689 user_t *u;
690 channel_t *c;
691 int i;
692 char *p;
693 int dir = MTYPE_ADD;
694 time_t ts;
695
696 if (*parv[0] == '#')
697 {
698 c = channel_find(parv[0]);
699 if (c == NULL)
700 return;
701 i = 2;
702 for (p = parv[1]; *p != '\0'; p++)
703 {
704 switch (*p)
705 {
706 case '+': dir = MTYPE_ADD; break;
707 case '-': dir = MTYPE_DEL; break;
708 case 'l':
709 if (dir == MTYPE_DEL)
710 break;
711 /* FALLTHROUGH */
712 case 'b': case 'k': case 'o': case 'v':
713 i++;
714 }
715 }
716 if (i < parc && (ts = atoi(parv[i])) != 0)
717 {
718 if (ts > c->ts)
719 {
720 slog(LG_DEBUG, "m_mode(): ignoring mode on %s (%lu > %lu)", c->name, (unsigned long)ts, (unsigned long)c->ts);
721 return;
722 }
723 }
724 channel_mode(NULL, c, parc - 1, &parv[1]);
725 }
726 else
727 {
728 /* Yes this is a nick and not a UID -- jilles */
729 u = user_find_named(parv[0]);
730 if (u == NULL)
731 {
732 slog(LG_DEBUG, "m_mode(): user mode for unknown user %s", parv[0]);
733 return;
734 }
735 user_mode(u, parv[1]);
736 if (strchr(parv[1], 'x'))
737 {
738 u->flags |= UF_HIDEHOSTREQ;
739 check_hidehost(u);
740 }
741 }
742 }
743
m_clearmode(sourceinfo_t * si,int parc,char * parv[])744 static void m_clearmode(sourceinfo_t *si, int parc, char *parv[])
745 {
746 channel_t *chan;
747 char *p, c;
748 mowgli_node_t *n;
749 chanuser_t *cu;
750 int i;
751
752 /* -> ABAAA CM # b */
753 /* Note: this is an IRCop command, do not enforce mode locks. */
754 chan = channel_find(parv[0]);
755 if (chan == NULL)
756 {
757 slog(LG_DEBUG, "m_clearmode(): unknown channel %s", parv[0]);
758 return;
759 }
760 p = parv[1];
761 while ((c = *p++))
762 {
763 if (c == 'b')
764 chanban_clear(chan);
765 else if (c == 'k')
766 {
767 if (chan->key)
768 free(chan->key);
769 chan->key = NULL;
770 }
771 else if (c == 'l')
772 chan->limit = 0;
773 else if (c == 'o')
774 {
775 MOWGLI_ITER_FOREACH(n, chan->members.head)
776 {
777 cu = (chanuser_t *)n->data;
778 if (cu->user->server == me.me)
779 {
780 /* it's a service, reop */
781 sts("%s M %s +o %s", me.numeric,
782 chan->name,
783 cu->user->uid);
784 }
785 else
786 cu->modes &= ~CSTATUS_OP;
787 }
788 }
789 else if (c == 'v')
790 {
791 MOWGLI_ITER_FOREACH(n, chan->members.head)
792 {
793 cu = (chanuser_t *)n->data;
794 cu->modes &= ~CSTATUS_VOICE;
795 }
796 }
797 else
798 for (i = 0; mode_list[i].mode != '\0'; i++)
799 {
800 if (c == mode_list[i].mode)
801 chan->modes &= ~mode_list[i].value;
802 }
803 }
804 }
805
m_kick(sourceinfo_t * si,int parc,char * parv[])806 static void m_kick(sourceinfo_t *si, int parc, char *parv[])
807 {
808 user_t *u = user_find(parv[1]);
809 channel_t *c = channel_find(parv[0]);
810
811 /* -> :rakaur KICK #shrike rintaun :test */
812 slog(LG_DEBUG, "m_kick(): user was kicked: %s -> %s", parv[1], parv[0]);
813
814 if (!u)
815 {
816 slog(LG_DEBUG, "m_kick(): got kick for nonexistant user %s", parv[1]);
817 return;
818 }
819
820 if (!c)
821 {
822 slog(LG_DEBUG, "m_kick(): got kick in nonexistant channel: %s", parv[0]);
823 return;
824 }
825
826 if (!chanuser_find(c, u))
827 {
828 slog(LG_DEBUG, "m_kick(): got kick for %s not in %s", u->nick, c->name);
829 return;
830 }
831
832 chanuser_delete(c, u);
833
834 /* if they kicked us, let's rejoin */
835 if (is_internal_client(u))
836 {
837 slog(LG_DEBUG, "m_kick(): %s got kicked from %s; rejoining", u->nick, parv[0]);
838 join(parv[0], u->nick);
839 }
840 }
841
m_kill(sourceinfo_t * si,int parc,char * parv[])842 static void m_kill(sourceinfo_t *si, int parc, char *parv[])
843 {
844 handle_kill(si, parv[0], parc > 1 ? parv[1] : "<No reason given>");
845 }
846
m_squit(sourceinfo_t * si,int parc,char * parv[])847 static void m_squit(sourceinfo_t *si, int parc, char *parv[])
848 {
849 slog(LG_DEBUG, "m_squit(): server leaving: %s from %s", parv[0], parv[1]);
850 server_delete(parv[0]);
851 }
852
853 /* SERVER ircu.devel.atheme.org 1 1119902586 1119908830 J10 ABAP] + :lets lol */
m_server(sourceinfo_t * si,int parc,char * parv[])854 static void m_server(sourceinfo_t *si, int parc, char *parv[])
855 {
856 server_t *s;
857
858 /* We dont care about the max connections. */
859 parv[5][2] = '\0';
860
861 slog(LG_DEBUG, "m_server(): new server: %s, id %s, %s",
862 parv[0], parv[5],
863 parv[4][0] == 'P' ? "eob" : "bursting");
864 s = handle_server(si, parv[0], parv[5], atoi(parv[1]), parv[7]);
865
866 /* SF_EOB may only be set when we have all users on the server.
867 * so store the fact that they are EOB in another flag.
868 * handle_eob() will set SF_EOB when the uplink has finished bursting.
869 * -- jilles */
870 if (s != NULL && parv[4][0] == 'P')
871 s->flags |= SF_EOB2;
872 }
873
m_stats(sourceinfo_t * si,int parc,char * parv[])874 static void m_stats(sourceinfo_t *si, int parc, char *parv[])
875 {
876 handle_stats(si->su, parv[0][0]);
877 }
878
m_admin(sourceinfo_t * si,int parc,char * parv[])879 static void m_admin(sourceinfo_t *si, int parc, char *parv[])
880 {
881 handle_admin(si->su);
882 }
883
m_version(sourceinfo_t * si,int parc,char * parv[])884 static void m_version(sourceinfo_t *si, int parc, char *parv[])
885 {
886 handle_version(si->su);
887 }
888
m_info(sourceinfo_t * si,int parc,char * parv[])889 static void m_info(sourceinfo_t *si, int parc, char *parv[])
890 {
891 handle_info(si->su);
892 }
893
m_motd(sourceinfo_t * si,int parc,char * parv[])894 static void m_motd(sourceinfo_t *si, int parc, char *parv[])
895 {
896 handle_motd(si->su);
897 }
898
m_whois(sourceinfo_t * si,int parc,char * parv[])899 static void m_whois(sourceinfo_t *si, int parc, char *parv[])
900 {
901 handle_whois(si->su, parv[1]);
902 }
903
m_trace(sourceinfo_t * si,int parc,char * parv[])904 static void m_trace(sourceinfo_t *si, int parc, char *parv[])
905 {
906 handle_trace(si->su, parv[0], parc >= 2 ? parv[1] : NULL);
907 }
908
m_away(sourceinfo_t * si,int parc,char * parv[])909 static void m_away(sourceinfo_t *si, int parc, char *parv[])
910 {
911 handle_away(si->su, parc >= 1 ? parv[0] : NULL);
912 }
913
m_pass(sourceinfo_t * si,int parc,char * parv[])914 static void m_pass(sourceinfo_t *si, int parc, char *parv[])
915 {
916 if (strcmp(curr_uplink->receive_pass, parv[0]))
917 {
918 slog(LG_INFO, "m_pass(): password mismatch from uplink; aborting");
919 runflags |= RF_SHUTDOWN;
920 }
921 }
922
m_error(sourceinfo_t * si,int parc,char * parv[])923 static void m_error(sourceinfo_t *si, int parc, char *parv[])
924 {
925 slog(LG_INFO, "m_error(): error from server: %s", parv[0]);
926 }
927
m_eos(sourceinfo_t * si,int parc,char * parv[])928 static void m_eos(sourceinfo_t *si, int parc, char *parv[])
929 {
930 handle_eob(si->s);
931
932 /* acknowledge a local END_OF_BURST */
933 if (si->s->uplink == me.me)
934 sts("%s EA", me.numeric);
935 }
936
m_account(sourceinfo_t * si,int parc,char * parv[])937 static void m_account(sourceinfo_t *si, int parc, char *parv[])
938 {
939 user_t *u;
940
941 u = user_find(parv[0]);
942 if (u == NULL)
943 return;
944 handle_setlogin(si, u, parv[1], parc > 2 ? atol(parv[2]) : 0);
945 }
946
check_hidehost(user_t * u)947 static void check_hidehost(user_t *u)
948 {
949 static bool warned = false;
950 char buf[HOSTLEN + 1];
951
952 /* do they qualify? */
953 if (!(u->flags & UF_HIDEHOSTREQ) || u->myuser == NULL || (u->myuser->flags & MU_WAITAUTH))
954 return;
955 /* don't use this if they have some other kind of vhost */
956 if (strcmp(u->host, u->vhost))
957 {
958 slog(LG_DEBUG, "check_hidehost(): +x overruled by other vhost for %s", u->nick);
959 return;
960 }
961 if (me.hidehostsuffix == NULL)
962 {
963 if (!warned)
964 {
965 wallops("Misconfiguration: serverinfo::hidehostsuffix not set");
966 warned = true;
967 }
968 return;
969 }
970
971 snprintf(buf, sizeof buf, "%s.%s", entity(u->myuser)->name, me.hidehostsuffix);
972
973 strshare_unref(u->vhost);
974 u->vhost = strshare_get(buf);
975
976 slog(LG_DEBUG, "check_hidehost(): %s -> %s", u->nick, u->vhost);
977 }
978
_modinit(module_t * m)979 void _modinit(module_t * m)
980 {
981 MODULE_TRY_REQUEST_DEPENDENCY(m, "transport/p10");
982 MODULE_TRY_REQUEST_DEPENDENCY(m, "protocol/base36uid");
983
984 /* Symbol relocation voodoo. */
985 server_login = &p10_server_login;
986 introduce_nick = &p10_introduce_nick;
987 quit_sts = &p10_quit_sts;
988 wallops_sts = &p10_wallops_sts;
989 join_sts = &p10_join_sts;
990 chan_lowerts = &p10_chan_lowerts;
991 kick = &p10_kick;
992 msg = &p10_msg;
993 msg_global_sts = &p10_msg_global_sts;
994 notice_user_sts = &p10_notice_user_sts;
995 notice_global_sts = &p10_notice_global_sts;
996 notice_channel_sts = &p10_notice_channel_sts;
997 wallchops = &p10_wallchops;
998 numeric_sts = &p10_numeric_sts;
999 kill_id_sts = &p10_kill_id_sts;
1000 part_sts = &p10_part_sts;
1001 kline_sts = &p10_kline_sts;
1002 unkline_sts = &p10_unkline_sts;
1003 xline_sts = &p10_xline_sts;
1004 unxline_sts = &p10_unxline_sts;
1005 qline_sts = &p10_qline_sts;
1006 unqline_sts = &p10_unqline_sts;
1007 topic_sts = &p10_topic_sts;
1008 mode_sts = &p10_mode_sts;
1009 ping_sts = &p10_ping_sts;
1010 ircd_on_login = &p10_on_login;
1011 ircd_on_logout = &p10_on_logout;
1012 jupe = &p10_jupe;
1013 invite_sts = &p10_invite_sts;
1014 sasl_sts = &p10_sasl_sts;
1015 svslogin_sts = &p10_svslogin_sts;
1016
1017 pcommand_add("G", m_ping, 1, MSRC_USER | MSRC_SERVER);
1018 pcommand_add("Z", m_pong, 1, MSRC_SERVER);
1019 pcommand_add("P", m_privmsg, 2, MSRC_USER);
1020 pcommand_add("O", m_notice, 2, MSRC_USER | MSRC_SERVER);
1021 pcommand_add("NOTICE", m_notice, 2, MSRC_UNREG);
1022 pcommand_add("C", m_create, 1, MSRC_USER);
1023 pcommand_add("J", m_join, 1, MSRC_USER);
1024 pcommand_add("EB", m_eos, 0, MSRC_SERVER);
1025 pcommand_add("B", m_burst, 2, MSRC_SERVER);
1026 pcommand_add("L", m_part, 1, MSRC_USER);
1027 pcommand_add("N", m_nick, 2, MSRC_USER | MSRC_SERVER);
1028 pcommand_add("Q", m_quit, 1, MSRC_USER);
1029 pcommand_add("M", m_mode, 2, MSRC_USER | MSRC_SERVER);
1030 pcommand_add("OM", m_mode, 2, MSRC_USER); /* OPMODE, treat as MODE */
1031 pcommand_add("CM", m_clearmode, 2, MSRC_USER);
1032 pcommand_add("K", m_kick, 2, MSRC_USER | MSRC_SERVER);
1033 pcommand_add("OK", m_kick, 2, MSRC_USER); /* OPKICK, treat as KICK */
1034 pcommand_add("D", m_kill, 1, MSRC_USER | MSRC_SERVER);
1035 pcommand_add("SQ", m_squit, 1, MSRC_USER | MSRC_SERVER);
1036 pcommand_add("S", m_server, 8, MSRC_SERVER);
1037 pcommand_add("SERVER", m_server, 8, MSRC_UNREG);
1038 pcommand_add("R", m_stats, 2, MSRC_USER);
1039 pcommand_add("AD", m_admin, 1, MSRC_USER);
1040 pcommand_add("V", m_version, 1, MSRC_USER);
1041 pcommand_add("F", m_info, 1, MSRC_USER);
1042 pcommand_add("W", m_whois, 2, MSRC_USER);
1043 pcommand_add("TR", m_trace, 1, MSRC_USER);
1044 pcommand_add("A", m_away, 0, MSRC_USER);
1045 pcommand_add("PASS", m_pass, 1, MSRC_UNREG);
1046 pcommand_add("Y", m_error, 1, MSRC_UNREG | MSRC_SERVER);
1047 pcommand_add("ERROR", m_error, 1, MSRC_UNREG | MSRC_SERVER);
1048 pcommand_add("T", m_topic, 2, MSRC_USER | MSRC_SERVER);
1049 pcommand_add("MO", m_motd, 1, MSRC_USER);
1050 pcommand_add("AC", m_account, 2, MSRC_SERVER);
1051
1052 m->mflags = MODTYPE_CORE;
1053 }
1054
1055 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
1056 * vim:ts=8
1057 * vim:sw=8
1058 * vim:noexpandtab
1059 */
1060