]> jfr.im git - irc/evilnet/x3.git/blame - src/proto-p10.c
Added back missing comment to example config
[irc/evilnet/x3.git] / src / proto-p10.c
CommitLineData
d76ed9a9 1/* proto-p10.c - IRC protocol output
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20
3fdd6a74 21#include "nickserv.h"
23475fc6 22#include "chanserv.h"
d76ed9a9 23#include "proto-common.c"
24
25/* Full commands. */
26#define CMD_ACCOUNT "ACCOUNT"
27#define CMD_ADMIN "ADMIN"
28#define CMD_ASLL "ASLL"
29#define CMD_AWAY "AWAY"
30#define CMD_BURST "BURST"
31#define CMD_CLEARMODE "CLEARMODE"
32#define CMD_CLOSE "CLOSE"
33#define CMD_CNOTICE "CNOTICE"
34#define CMD_CONNECT "CONNECT"
35#define CMD_CPRIVMSG "CPRIVMSG"
36#define CMD_CREATE "CREATE"
37#define CMD_DESTRUCT "DESTRUCT"
38#define CMD_DESYNCH "DESYNCH"
39#define CMD_DIE "DIE"
40#define CMD_DNS "DNS"
41#define CMD_EOB "END_OF_BURST"
42#define CMD_EOB_ACK "EOB_ACK"
43#define CMD_ERROR "ERROR"
44#define CMD_FAKEHOST "FAKE"
45#define CMD_GET "GET"
46#define CMD_GLINE "GLINE"
47#define CMD_HASH "HASH"
48#define CMD_HELP "HELP"
49#define CMD_INFO "INFO"
50#define CMD_INVITE "INVITE"
51#define CMD_ISON "ISON"
52#define CMD_JOIN "JOIN"
53#define CMD_JUPE "JUPE"
54#define CMD_KICK "KICK"
55#define CMD_KILL "KILL"
56#define CMD_LINKS "LINKS"
57#define CMD_LIST "LIST"
58#define CMD_LUSERS "LUSERS"
59#define CMD_MAP "MAP"
23475fc6 60#define CMD_MARK "MARK"
d76ed9a9 61#define CMD_MODE "MODE"
62#define CMD_MOTD "MOTD"
63#define CMD_NAMES "NAMES"
64#define CMD_NICK "NICK"
65#define CMD_NOTICE "NOTICE"
66#define CMD_OPER "OPER"
67#define CMD_OPMODE "OPMODE"
68#define CMD_PART "PART"
69#define CMD_PASS "PASS"
70#define CMD_PING "PING"
71#define CMD_PONG "PONG"
72#define CMD_POST "POST"
73#define CMD_PRIVMSG "PRIVMSG"
74#define CMD_PRIVS "PRIVS"
75#define CMD_PROTO "PROTO"
76#define CMD_QUIT "QUIT"
77#define CMD_REHASH "REHASH"
78#define CMD_RESET "RESET"
79#define CMD_RESTART "RESTART"
80#define CMD_RPING "RPING"
81#define CMD_RPONG "RPONG"
82#define CMD_SERVER "SERVER"
83#define CMD_SERVLIST "SERVLIST"
84#define CMD_SERVSET "SERVSET"
85#define CMD_SET "SET"
86#define CMD_SETTIME "SETTIME"
d914d1cb 87#define CMD_SHUN "SHUN"
d76ed9a9 88#define CMD_SILENCE "SILENCE"
89#define CMD_SQUERY "SQUERY"
90#define CMD_SQUIT "SQUIT"
91#define CMD_STATS "STATS"
92#define CMD_SVSNICK "SVSNICK"
93#define CMD_TIME "TIME"
94#define CMD_TOPIC "TOPIC"
95#define CMD_TRACE "TRACE"
96#define CMD_UPING "UPING"
97#define CMD_USER "USER"
98#define CMD_USERHOST "USERHOST"
99#define CMD_USERIP "USERIP"
100#define CMD_VERSION "VERSION"
101#define CMD_WALLCHOPS "WALLCHOPS"
102#define CMD_WALLOPS "WALLOPS"
55342ce8 103#define CMD_WALLHOPS "WALLHOPS"
d76ed9a9 104#define CMD_WALLUSERS "WALLUSERS"
105#define CMD_WALLVOICES "WALLVOICES"
55342ce8 106#define CMD_WALLHOPS "WALLHOPS"
d76ed9a9 107#define CMD_WHO "WHO"
108#define CMD_WHOIS "WHOIS"
109#define CMD_WHOWAS "WHOWAS"
110
111/* Tokenized commands. */
112#define TOK_ACCOUNT "AC"
113#define TOK_ADMIN "AD"
114#define TOK_ASLL "LL"
115#define TOK_AWAY "A"
116#define TOK_BURST "B"
117#define TOK_CLEARMODE "CM"
118#define TOK_CLOSE "CLOSE"
119#define TOK_CNOTICE "CN"
120#define TOK_CONNECT "CO"
121#define TOK_CPRIVMSG "CP"
122#define TOK_CREATE "C"
123#define TOK_DESTRUCT "DE"
124#define TOK_DESYNCH "DS"
125#define TOK_DIE "DIE"
126#define TOK_DNS "DNS"
127#define TOK_EOB "EB"
128#define TOK_EOB_ACK "EA"
129#define TOK_ERROR "Y"
fede8b64 130#define TOK_EXEMPT "EX"
d76ed9a9 131#define TOK_FAKEHOST "FA"
132#define TOK_GET "GET"
133#define TOK_GLINE "GL"
134#define TOK_HASH "HASH"
135#define TOK_HELP "HELP"
136#define TOK_INFO "F"
137#define TOK_INVITE "I"
138#define TOK_ISON "ISON"
139#define TOK_JOIN "J"
140#define TOK_JUPE "JU"
141#define TOK_KICK "K"
142#define TOK_KILL "D"
143#define TOK_LINKS "LI"
144#define TOK_LIST "LIST"
145#define TOK_LUSERS "LU"
146#define TOK_MAP "MAP"
23475fc6 147#define TOK_MARK "MK"
d76ed9a9 148#define TOK_MODE "M"
149#define TOK_MOTD "MO"
150#define TOK_NAMES "E"
151#define TOK_NICK "N"
152#define TOK_NOTICE "O"
153#define TOK_OPER "OPER"
154#define TOK_OPMODE "OM"
155#define TOK_PART "L"
156#define TOK_PASS "PA"
157#define TOK_PING "G"
158#define TOK_PONG "Z"
159#define TOK_POST "POST"
160#define TOK_PRIVMSG "P"
161#define TOK_PRIVS "PRIVS"
162#define TOK_PROTO "PROTO"
163#define TOK_QUIT "Q"
164#define TOK_REHASH "REHASH"
165#define TOK_RESET "RESET"
166#define TOK_RESTART "RESTART"
167#define TOK_RPING "RI"
168#define TOK_RPONG "RO"
169#define TOK_SERVER "S"
170#define TOK_SERVLIST "SERVSET"
171#define TOK_SERVSET "SERVSET"
172#define TOK_SET "SET"
173#define TOK_SETTIME "SE"
d914d1cb 174#define TOK_SHUN "SU"
d76ed9a9 175#define TOK_SILENCE "U"
176#define TOK_SQUERY "SQUERY"
177#define TOK_SQUIT "SQ"
178#define TOK_STATS "R"
179#define TOK_SVSNICK "SN"
180#define TOK_TIME "TI"
181#define TOK_TOPIC "T"
182#define TOK_TRACE "TR"
183#define TOK_UPING "UP"
184#define TOK_USER "USER"
185#define TOK_USERHOST "USERHOST"
186#define TOK_USERIP "USERIP"
187#define TOK_VERSION "V"
188#define TOK_WALLCHOPS "WC"
189#define TOK_WALLOPS "WA"
55342ce8 190#define TOK_WALLHOPS "WH"
d76ed9a9 191#define TOK_WALLUSERS "WU"
192#define TOK_WALLVOICES "WV"
55342ce8 193#define TOK_WALLHOPS "WH"
d76ed9a9 194#define TOK_WHO "H"
195#define TOK_WHOIS "W"
196#define TOK_WHOWAS "X"
197
198/* Protocol messages; aliased to full commands or tokens depending
199 on compile-time configuration. ircu prefers tokens WITH THE
200 EXCEPTION OF THE SERVER AND PASS COMMANDS, which cannot be
201 tokenized, because clients' (ie. a linking server) commands are
202 only checked against the full command list.
203*/
204#if defined(ENABLE_TOKENS)
205#define TYPE(NAME) TOK_ ## NAME
206#else /* !ENABLE_TOKENS */
207#define TYPE(NAME) CMD_ ## NAME
208#endif /* ENABLE_TOKENS */
209
210#define P10_ACCOUNT TYPE(ACCOUNT)
211#define P10_ADMIN TYPE(ADMIN)
212#define P10_ASLL TYPE(ASLL)
213#define P10_AWAY TYPE(AWAY)
214#define P10_BURST TYPE(BURST)
215#define P10_CLEARMODE TYPE(CLEARMODE)
216#define P10_CLOSE TYPE(CLOSE)
217#define P10_CNOTICE TYPE(CNOTICE)
218#define P10_CONNECT TYPE(CONNECT)
219#define P10_CPRIVMSG TYPE(CPRIVMSG)
220#define P10_CREATE TYPE(CREATE)
221#define P10_DESTRUCT TYPE(DESTRUCT)
222#define P10_DESYNCH TYPE(DESYNCH)
223#define P10_DIE TYPE(DIE)
224#define P10_DNS TYPE(DNS)
225#define P10_EOB TYPE(EOB)
226#define P10_EOB_ACK TYPE(EOB_ACK)
227#define P10_ERROR TYPE(ERROR)
228#define P10_FAKEHOST TYPE(FAKEHOST)
229#define P10_GET TYPE(GET)
230#define P10_GLINE TYPE(GLINE)
231#define P10_HASH TYPE(HASH)
232#define P10_HELP TYPE(HELP)
233#define P10_INFO TYPE(INFO)
234#define P10_INVITE TYPE(INVITE)
235#define P10_ISON TYPE(ISON)
236#define P10_JOIN TYPE(JOIN)
237#define P10_JUPE TYPE(JUPE)
238#define P10_KICK TYPE(KICK)
239#define P10_KILL TYPE(KILL)
240#define P10_LINKS TYPE(LINKS)
241#define P10_LIST TYPE(LIST)
242#define P10_LUSERS TYPE(LUSERS)
243#define P10_MAP TYPE(MAP)
23475fc6 244#define P10_MARK TYPE(MARK)
d76ed9a9 245#define P10_MODE TYPE(MODE)
246#define P10_MOTD TYPE(MOTD)
247#define P10_NAMES TYPE(NAMES)
248#define P10_NICK TYPE(NICK)
249#define P10_NOTICE TYPE(NOTICE)
250#define P10_OPER TYPE(OPER)
251#define P10_OPMODE TYPE(OPMODE)
252#define P10_PART TYPE(PART)
253#define P10_PASS CMD_PASS
254#define P10_PING TYPE(PING)
255#define P10_PONG TYPE(PONG)
256#define P10_POST TYPE(POST)
257#define P10_PRIVMSG TYPE(PRIVMSG)
258#define P10_PRIVS TYPE(PRIVS)
259#define P10_PROTO TYPE(PROTO)
260#define P10_QUIT TYPE(QUIT)
261#define P10_REHASH TYPE(REHASH)
262#define P10_RESET TYPE(RESET)
263#define P10_RESTART TYPE(RESTART)
264#define P10_RPING TYPE(RPING)
265#define P10_RPONG TYPE(RPONG)
266#define P10_SERVER CMD_SERVER
267#define P10_SERVLIST TYPE(SERVLIST)
268#define P10_SERVSET TYPE(SERVSET)
269#define P10_SET TYPE(SET)
270#define P10_SETTIME TYPE(SETTIME)
d914d1cb 271#define P10_SHUN TYPE(SHUN)
d76ed9a9 272#define P10_SILENCE TYPE(SILENCE)
273#define P10_SQUERY TYPE(SQUERY)
274#define P10_SQUIT TYPE(SQUIT)
275#define P10_STATS TYPE(STATS)
276#define P10_SVSNICK TYPE(SVSNICK)
277#define P10_TIME TYPE(TIME)
278#define P10_TOPIC TYPE(TOPIC)
279#define P10_TRACE TYPE(TRACE)
280#define P10_UPING TYPE(UPING)
281#define P10_USER TYPE(USER)
282#define P10_USERHOST TYPE(USERHOST)
283#define P10_USERIP TYPE(USERIP)
284#define P10_VERSION TYPE(VERSION)
285#define P10_WALLCHOPS TYPE(WALLCHOPS)
286#define P10_WALLOPS TYPE(WALLOPS)
55342ce8 287#define P10_WALLHOPS TYPE(WALLHOPS)
d76ed9a9 288#define P10_WALLUSERS TYPE(WALLUSERS)
289#define P10_WALLVOICES TYPE(WALLVOICES)
290#define P10_WHO TYPE(WHO)
291#define P10_WHOIS TYPE(WHOIS)
292#define P10_WHOWAS TYPE(WHOWAS)
850bf0cf 293#define P10_EXEMPT TYPE(EXEMPT)
d76ed9a9 294
295/* Servers claiming to have a boot or link time before PREHISTORY
296 * trigger errors to the log. We hope no server has been running
297 * constantly since September 1994. :)
298 */
299#define PREHISTORY 780000000
300
301static struct server *servers_num[64*64];
302static privmsg_func_t *privmsg_funcs;
303static unsigned int num_privmsg_funcs;
304static privmsg_func_t *notice_funcs;
305static unsigned int num_notice_funcs;
306static struct dict *unbursted_channels;
307static char *his_servername;
308static char *his_servercomment;
805e7c7a 309static int extended_accounts;
d76ed9a9 310
311static struct userNode *AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *numeric, const char *userinfo, time_t timestamp, const char *realip);
312
313extern int off_channel;
314
315/* Numerics can be XYY, XYYY, or XXYYY; with X's identifying the
316 * server and Y's indentifying the client on that server. */
317struct server*
318GetServerN(const char *numeric)
319{
320 switch (strlen(numeric)) {
321 default:
322 return servers_num[base64toint(numeric, 2)];
323 case 4:
324 case 3:
325 case 1:
326 return servers_num[base64toint(numeric, 1)];
327 case 0:
328 return NULL;
329 }
330}
331
332struct userNode*
333GetUserN(const char *numeric) /* using numeric */
334{
335 struct userNode *un;
336 struct server *s;
337 int n, slen, ulen;
338
339 switch (strlen(numeric)) {
340 default:
341 log_module(MAIN_LOG, LOG_WARNING, "GetUserN(%s): numeric too long!", numeric);
342 return NULL;
343 case 5: slen = 2; ulen = 3; break;
344 case 4: slen = 1; ulen = 3; break;
345 case 3: slen = 1; ulen = 2; break;
346 case 2: case 1: case 0:
347 log_module(MAIN_LOG, LOG_WARNING, "GetUserN(%s): numeric too short!", numeric);
348 return NULL;
349 }
350 if (!(s = servers_num[base64toint(numeric, slen)])) {
351 log_module(MAIN_LOG, LOG_WARNING, "GetUserN(%s): couldn't find server (len=%d)!", numeric, slen);
352 return NULL;
353 }
354 n = base64toint(numeric+slen, ulen) & s->num_mask;
355 if (!(un = s->users[n])) {
356 log_module(MAIN_LOG, LOG_WARNING, "GetUserN(%s) couldn't find user!", numeric);
357 }
358 return un;
359}
360
361static void
362privmsg_user_helper(struct userNode *un, void *data)
363{
364 struct privmsg_desc *pd = data;
365 unsigned int num = un->num_local;
366 if (!pd->is_notice) {
367 if ((num < num_privmsg_funcs) && privmsg_funcs[num]) {
368 privmsg_funcs[num](pd->user, un, pd->text, pd->is_qualified);
369 }
370 } else {
371 if ((num < num_notice_funcs) && notice_funcs[num]) {
372 notice_funcs[num](pd->user, un, pd->text, pd->is_qualified);
373 }
374 }
375}
376
377void
378irc_server(struct server *srv)
379{
380 char extranum[COMBO_NUMERIC_LEN+1];
381
382 inttobase64(extranum, srv->num_mask, (srv->numeric[1] || (srv->num_mask >= 64*64)) ? 3 : 2);
383 if (srv == self) {
384 /* The +s, ignored by Run's ircu, means "service" to Undernet's ircu */
385 putsock(P10_SERVER " %s %d %li %li J10 %s%s +s :%s",
386 srv->name, srv->hops+1, srv->boot, srv->link, srv->numeric, extranum, srv->description);
387 } else {
388 putsock("%s " P10_SERVER " %s %d %li %li %c10 %s%s +s :%s",
389 self->numeric, srv->name, srv->hops+1, srv->boot, srv->link, (srv->self_burst ? 'J' : 'P'), srv->numeric, extranum, srv->description);
390 }
391}
392
393void
394irc_user(struct userNode *user)
395{
396 char b64ip[7];
397 if (!user)
398 return;
399 inttobase64(b64ip, ntohl(user->ip.s_addr), 6);
400 if (user->modes) {
401 int modelen;
402 char modes[32];
403
404 modelen = 0;
405 if (IsOper(user))
406 modes[modelen++] = 'o';
407 if (IsInvisible(user))
408 modes[modelen++] = 'i';
409 if (IsWallOp(user))
410 modes[modelen++] = 'w';
411 if (IsService(user))
412 modes[modelen++] = 'k';
413 if (IsServNotice(user))
414 modes[modelen++] = 's';
415 if (IsDeaf(user))
416 modes[modelen++] = 'd';
417 if (IsGlobal(user))
418 modes[modelen++] = 'g';
182dd032 419 // sethost - reed/apples
420 // if (IsHelperIrcu(user))
421 if (IsSetHost(user))
d76ed9a9 422 modes[modelen++] = 'h';
182dd032 423 if (IsFakeHost(user))
424 modes[modelen++] = 'f';
d76ed9a9 425 if (IsHiddenHost(user))
426 modes[modelen++] = 'x';
427 modes[modelen] = 0;
428
429 /* we don't need to put the + in modes because it's in the format string. */
430 putsock("%s " P10_NICK " %s %d %li %s %s +%s %s %s :%s",
431 user->uplink->numeric, user->nick, user->uplink->hops+1, user->timestamp, user->ident, user->hostname, modes, b64ip, user->numeric, user->info);
432 } else {
433 putsock("%s " P10_NICK " %s %d %li %s %s %s %s :%s",
434 user->uplink->numeric, user->nick, user->uplink->hops+1, user->timestamp, user->ident, user->hostname, b64ip, user->numeric, user->info);
435 }
436}
437
a45e6ec7 438void
439irc_rename(struct userNode *user, const char *new_handle)
440{
805e7c7a 441 if(extended_accounts)
442 putsock("%s " P10_ACCOUNT " %s M %s", self->numeric, user->numeric, new_handle);
a45e6ec7 443}
444
445void
446irc_delete(struct userNode *user)
447{
805e7c7a 448 if(extended_accounts)
449 putsock("%s " P10_ACCOUNT " %s U", self->numeric, user->numeric);
a45e6ec7 450}
451
d76ed9a9 452void
b21e2cfe 453irc_account(struct userNode *user, const char *stamp, time_t timestamp)
d76ed9a9 454{
805e7c7a 455 if(extended_accounts)
456 putsock("%s " P10_ACCOUNT " %s R %s %lu", self->numeric, user->numeric, stamp, timestamp);
457 else
458 putsock("%s " P10_ACCOUNT " %s %s %lu", self->numeric, user->numeric, stamp, timestamp);
d76ed9a9 459}
460
461void
462irc_fakehost(struct userNode *user, const char *host)
463{
464 putsock("%s " P10_FAKEHOST " %s %s", self->numeric, user->numeric, host);
465}
466
467void
468irc_regnick(UNUSED_ARG(struct userNode *user))
469{
470 /* no operation here */
471}
472
473void
474irc_nick(struct userNode *user, UNUSED_ARG(const char *old_nick))
475{
476 putsock("%s " P10_NICK " %s "FMT_TIME_T, user->numeric, user->nick, now);
477}
478
479void
480irc_fetchtopic(struct userNode *from, const char *to)
481{
482 if (!from || !to)
483 return;
484 putsock("%s " P10_TOPIC " %s", from->numeric, to);
485}
486
487void
488irc_squit(struct server *srv, const char *message, const char *service_message)
489{
490 if (!service_message)
491 service_message = message;
492
493 /* Are we leaving the network? */
494 if (srv == self && cManager.uplink->state == CONNECTED) {
495 unsigned int i;
496
497 /* Quit all clients linked to me. */
498 for (i = 0; i <= self->num_mask; i++) {
499 if (!self->users[i])
500 continue;
501 irc_quit(self->users[i], service_message);
502 }
503 }
504
505 putsock("%s " P10_SQUIT " %s %d :%s", self->numeric, srv->name, 0, message);
506
507 if (srv == self) {
508 /* Force a reconnect to the currently selected server. */
509 cManager.uplink->tries = 0;
510 log_module(MAIN_LOG, LOG_INFO, "Squitting from uplink: %s", message);
511 close_socket();
512 }
513}
514
515void
516irc_wallchops(struct userNode *from, const char *to, const char *message)
517{
518 putsock("%s " P10_WALLCHOPS " %s :%s", from->numeric, to, message);
519}
520
521void
522irc_notice(struct userNode *from, const char *to, const char *message)
523{
524 putsock("%s " P10_NOTICE " %s :%s", from->numeric, to, message);
525}
526
527void
528irc_notice_user(struct userNode *from, struct userNode *to, const char *message)
529{
530 putsock("%s " P10_NOTICE " %s :%s", from->numeric, to->numeric, message);
531}
532
533void
534irc_privmsg(struct userNode *from, const char *to, const char *message)
535{
536 putsock("%s " P10_PRIVMSG " %s :%s", from->numeric, to, message);
537}
538
539void
540irc_eob(void)
541{
542 putsock("%s " P10_EOB, self->numeric);
543}
544
545void
546irc_eob_ack(void)
547{
548 putsock("%s " P10_EOB_ACK, self->numeric);
549}
550
551void
552irc_ping(const char *payload)
553{
554 putsock("%s " P10_PING " :%s", self->numeric, payload);
555}
556
557void
558irc_pong(const char *who, const char *data)
559{
560 putsock("%s " P10_PONG " %s :%s", self->numeric, who, data);
561}
562
563void
564irc_pass(const char *passwd)
565{
566 putsock(P10_PASS " :%s", passwd);
567}
568
569void
570irc_introduce(const char *passwd)
571{
572 void timed_send_ping(void *data);
573
574 self->self_burst = self->burst = 1;
575 irc_pass(passwd);
576 irc_server(self);
577 burst_length = 0;
578 timeq_add(now + ping_freq, timed_send_ping, 0);
579}
580
581void
9a75756e 582irc_gline(struct server *srv, struct gline *gline, int silent)
d76ed9a9 583{
9a75756e 584 putsock("%s " P10_GLINE " %s +%s %ld :%s<%s> %s",
585 self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, silent ? "AUTO " : "", gline->issuer, gline->reason);
d76ed9a9 586}
587
d914d1cb 588void
589irc_shun(struct server *srv, struct shun *shun)
590{
591 putsock("%s " P10_SHUN " %s +%s %ld :<%s> %s",
592 self->numeric, (srv ? srv->numeric : "*"), shun->target, shun->expires-now, shun->issuer, shun->reason);
593}
594
d76ed9a9 595void
596irc_settime(const char *srv_name_mask, time_t new_time)
597{
598 ioset_set_time(new_time);
599 if (!strcmp(srv_name_mask, "*"))
600 srv_name_mask = "";
601 putsock("%s " P10_SETTIME " " FMT_TIME_T " %s", self->numeric, new_time, srv_name_mask);
602}
603
604void
605irc_ungline(const char *mask)
606{
607 putsock("%s " P10_GLINE " * -%s", self->numeric, mask);
608}
609
d914d1cb 610void
611irc_unshun(const char *mask)
612{
613 putsock("%s " P10_SHUN " * -%s", self->numeric, mask);
614}
615
d76ed9a9 616static void
617irc_burst(struct chanNode *chan)
618{
619 char burst_line[512];
620 int pos, base_len, len;
621 struct modeNode *mn;
622 struct banNode *bn;
2aef5f4b 623 struct exemptNode *en;
d76ed9a9 624 long last_mode=-1;
625 unsigned int n;
626
627 base_len = sprintf(burst_line, "%s " P10_BURST " %s " FMT_TIME_T " ",
628 self->numeric, chan->name, chan->timestamp);
629 len = irc_make_chanmode(chan, burst_line+base_len);
630 pos = base_len + len;
631 if (len)
632 burst_line[pos++] = ' ';
633
634 /* dump the users */
635 for (n=0; n<chan->members.used; n++) {
636 mn = chan->members.list[n];
637 if (pos > 500) {
638 burst_line[pos-1] = 0; /* -1 to back up over the space or comma */
639 putsock("%s", burst_line);
640 pos = base_len;
641 last_mode = -1;
642 }
643 memcpy(burst_line+pos, mn->user->numeric, strlen(mn->user->numeric));
644 pos += strlen(mn->user->numeric);
645 if (mn->modes && (mn->modes != last_mode)) {
646 last_mode = mn->modes;
647 burst_line[pos++] = ':';
648 if (last_mode & MODE_CHANOP)
649 burst_line[pos++] = 'o';
55342ce8 650 if (last_mode & MODE_HALFOP)
651 burst_line[pos++] = 'h';
d76ed9a9 652 if (last_mode & MODE_VOICE)
653 burst_line[pos++] = 'v';
654 }
655 if ((n+1)<chan->members.used)
656 burst_line[pos++] = ',';
657 }
658 if (chan->banlist.used) {
659 /* dump the bans */
660 if (pos+2+strlen(chan->banlist.list[0]->ban) > 505) {
661 burst_line[pos-1] = 0;
662 putsock("%s", burst_line);
663 pos = base_len;
664 } else {
665 burst_line[pos++] = ' ';
666 }
667
668 burst_line[pos++] = ':';
669 burst_line[pos++] = '%';
670 base_len = pos;
671 for (n=0; n<chan->banlist.used; n++) {
672 bn = chan->banlist.list[n];
673 len = strlen(bn->ban);
674 if (pos+len+1 > 510) {
675 burst_line[pos-1] = 0; /* -1 to back up over the space or comma */
676 putsock("%s", burst_line);
677 pos = base_len;
678 }
679 memcpy(burst_line+pos, bn->ban, len);
680 pos += len;
681 burst_line[pos++] = ' ';
682 }
683 }
2aef5f4b 684 if (chan->exemptlist.used) {
685 /* dump the exempt */
686 if (pos+2+strlen(chan->exemptlist.list[0]->exempt) > 505) {
687 burst_line[pos-1] = 0;
688 putsock("%s", burst_line);
689 pos = base_len;
690 } else {
691 burst_line[pos++] = ' ';
692 }
693
694 burst_line[pos++] = ' ';
695 burst_line[pos++] = '~';
696 burst_line[pos++] = ' ';
697 base_len = pos;
698 for (n=0; n<chan->exemptlist.used; n++) {
699 en = chan->exemptlist.list[n];
700 len = strlen(en->exempt);
701 if (pos+len+1 > 510) {
702 burst_line[pos-1] = 0; /* -1 to back up over the space or comma */
703 putsock("%s", burst_line);
704 pos = base_len;
705 }
706 memcpy(burst_line+pos, en->exempt, len);
707 pos += len;
708 burst_line[pos++] = ' ';
709 }
710 }
d76ed9a9 711 /* print the last line */
712 burst_line[pos] = 0;
713 putsock("%s", burst_line);
714}
715
716void
717irc_quit(struct userNode *user, const char *message)
718{
719 putsock("%s " P10_QUIT " :%s", user->numeric, message);
720}
721
722void
723irc_error(const char *to, const char *message)
724{
725 if (to) {
726 putsock("%s " P10_ERROR " :%s", to, message);
727 } else {
728 putsock(":%s " P10_ERROR " :%s", self->name, message);
729 }
730}
731
732void
733irc_kill(struct userNode *from, struct userNode *target, const char *message)
734{
735 if (from) {
736 putsock("%s " P10_KILL " %s :%s!%s (%s)",
737 from->numeric, target->numeric, self->name, from->nick, message);
738 } else {
739 putsock("%s " P10_KILL " %s :%s (%s)",
740 self->numeric, target->numeric, self->name, message);
741 }
742}
743
744void
745irc_mode(struct userNode *from, struct chanNode *target, const char *modes)
746{
747 putsock("%s " P10_MODE " %s %s "FMT_TIME_T,
748 (from ? from->numeric : self->numeric),
749 target->name, modes, target->timestamp);
750}
751
5a1daaab 752/* Added to allow services to mode users
753 2005 - 8 - 10 by Life4Christ
754*/
755void
756irc_umode(struct userNode *target, const char *modes)
757{
758 putsock("%s " P10_MODE " %s %s ",self->numeric,target->nick, modes);
759}
760
761
d76ed9a9 762void
763irc_invite(struct userNode *from, struct userNode *who, struct chanNode *to)
764{
765 putsock("%s " P10_INVITE " %s %s", from->numeric, who->nick, to->name);
766}
767
768void
769irc_join(struct userNode *who, struct chanNode *what)
770{
771 if (what->members.used == 1) {
772 putsock("%s " P10_CREATE " %s %lu",
773 who->numeric, what->name, what->timestamp);
774 } else {
775 putsock("%s " P10_JOIN " %s %lu", who->numeric, what->name, what->timestamp);
776 }
777}
778
779void
780irc_kick(struct userNode *who, struct userNode *target, struct chanNode *channel, const char *msg)
781{
782 const char *numeric;
783 struct modeNode *mn = GetUserMode(channel, who);
784 numeric = ((mn && (mn->modes & MODE_CHANOP)) || off_channel) ? who->numeric : self->numeric;
785 putsock("%s " P10_KICK " %s %s :%s",
786 numeric, channel->name, target->numeric, msg);
787}
788
789void
790irc_stats(struct userNode *from, struct server *target, char type)
791{
792 putsock("%s " P10_STATS " %c :%s", from->numeric, type, target->numeric);
793}
794
795void
796irc_svsnick(struct userNode *from, struct userNode *target, const char *newnick)
797{
798 putsock("%s " P10_SVSNICK " %s %s "FMT_TIME_T, from->uplink->numeric, target->numeric, newnick, now);
799}
800
801void
802irc_part(struct userNode *who, struct chanNode *what, const char *reason)
803{
804 if (reason) {
805 putsock("%s " P10_PART " %s :%s", who->numeric, what->name, reason);
806 } else {
807 putsock("%s " P10_PART " %s", who->numeric, what->name);
808 }
809}
810
811void
7fda2b52 812irc_topic(struct userNode *service, struct userNode *who, struct chanNode *what, const char *topic)
d76ed9a9 813{
7fda2b52 814/* UNCOMMENT FOR NEFARIOUS 0.5.0 TOPIC SUPPORT
815 * putsock("%s " P10_TOPIC " %s %s " FMT_TIME_T " " FMT_TIME_T " :%s", service->numeric, what->name, who->nick, what->timestamp, now, topic);
816 * UNCOMMENT FOR NEFARIOUS 0.5.0 TOPIC SUPPORT */
817
818 who = service; /* REMOVE LINE FOR NEFARIOUS 0.5.0 */
819
d76ed9a9 820 putsock("%s " P10_TOPIC " %s :%s", who->numeric, what->name, topic);
821}
822
823void
824irc_raw(const char *what)
825{
826 putsock("%s", what);
827}
828
829void
830irc_numeric(struct userNode *user, unsigned int num, const char *format, ...)
831{
832 va_list arg_list;
833 char buffer[MAXLEN];
834 va_start(arg_list, format);
835 vsnprintf(buffer, MAXLEN-2, format, arg_list);
836 buffer[MAXLEN-1] = 0;
837 putsock(":%s %03d %s %s", self->name, num, user->nick, buffer);
838}
839
840static void send_burst(void);
841
842static void
843change_nicklen(int new_nicklen)
844{
845 unsigned int nn;
846 char new_nick[NICKLEN+1];
847 struct userNode *user;
848
849 nicklen = new_nicklen;
850 /* fix up any users we have here */
851 for (nn=0; nn<=self->num_mask; nn++) {
852 if (!(user = self->users[nn]))
853 continue;
854 safestrncpy(new_nick, user->nick, sizeof(new_nick));
855 new_nick[nicklen] = 0;
856 NickChange(user, new_nick, 1);
857 }
858}
859
860static CMD_FUNC(cmd_whois)
861{
862 struct userNode *from;
863 struct userNode *who;
864
865 if (argc < 3)
866 return 0;
867 if (!(from = GetUserH(origin))) {
868 log_module(MAIN_LOG, LOG_ERROR, "Could not find WHOIS origin user %s", origin);
869 return 0;
870 }
871 if(!(who = GetUserH(argv[2]))) {
872 irc_numeric(from, ERR_NOSUCHNICK, "%s@%s :No such nick", argv[2], self->name);
873 return 1;
874 }
875 if (IsHiddenHost(who) && !IsOper(from)) {
876 /* Just stay quiet. */
877 return 1;
878 }
879 irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
880 if (his_servername && his_servercomment)
a32da4c7 881 irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, his_servername, his_servercomment);
d76ed9a9 882 else
a32da4c7 883 irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
884 if (IsOper(who))
d76ed9a9 885 irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick);
d76ed9a9 886 irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
887 return 1;
888}
889
890static CMD_FUNC(cmd_server)
891{
892 struct server *srv;
893 const char *str;
894
895 if (argc < 8)
896 return 0;
a32da4c7 897 if (self->uplink) {
d76ed9a9 898 /* another server introduced us */
899 srv = AddServer(GetServerH(origin), argv[1], atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), argv[6], argv[argc-1]);
900 if (!srv)
901 return 0;
902 srv->self_burst = argv[5][0] == 'J';
903 srv->burst = 1;
904 } else {
905 /* this must be our uplink */
906 srv = self->uplink = AddServer(self, argv[1], atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), argv[6], argv[argc-1]);
907 if (!srv)
908 return 0;
909 srv->self_burst = argv[5][0] == 'J';
910 srv->burst = 1;
911 if ((argv[7][0] == '+') && !force_n2k) {
912 log_module(MAIN_LOG, LOG_WARNING, "Got Undernet-style SERVER message but \"force_n2k\" not on.");
913 }
914 send_burst();
915 }
916
917 /* Fix up our timestamps if necessary. */
918 if (srv->boot <= PREHISTORY) {
919 /* Server from the mists of time.. */
920 if (srv->hops == 1) {
921 log_module(MAIN_LOG, LOG_ERROR, "Server %s claims to have booted at time "FMT_TIME_T". This is absurd.", srv->name, srv->boot);
922 }
923 } else if ((str = conf_get_data("server/reliable_clock", RECDB_QSTRING))
924 && enabled_string(str)) {
925 /* If we have a reliable clock, we just keep our current time. */
926 } else {
927 if (srv->boot <= self->boot) {
928 /* The other server is older than us. Accept their timestamp.
929 * Alternately, we are same age, but we accept their time
930 * since we are linking to them. */
931 self->boot = srv->boot;
932 ioset_set_time(srv->link);
933 }
934 }
935 if (srv == self->uplink) {
936 extern time_t burst_begin;
937 burst_begin = now;
938 }
939 return 1;
940}
941
942static CMD_FUNC(cmd_eob)
943{
944 struct server *sender;
945 dict_iterator_t it;
946 unsigned int ii;
947
948 if (!(sender = GetServerH(origin)))
949 return 0;
950 if (sender == self->uplink) {
951 cManager.uplink->state = CONNECTED;
952 for (it = dict_first(unbursted_channels); it; it = iter_next(it))
953 irc_burst(iter_data(it));
954 dict_delete(unbursted_channels);
955 unbursted_channels = NULL;
956 irc_eob();
957 irc_eob_ack();
958 }
959 sender->self_burst = 0;
960 recalc_bursts(sender);
961 for (ii=0; ii<slf_used; ii++)
962 slf_list[ii](sender);
963 return 1;
964}
965
966static CMD_FUNC(cmd_eob_ack)
967{
968 extern time_t burst_begin;
969
970 if (GetServerH(origin) == self->uplink) {
971 burst_length = now - burst_begin;
972 self->self_burst = self->burst = 0;
973 }
974 cManager.uplink->state = CONNECTED;
975 return 1;
976}
977
978static CMD_FUNC(cmd_ping)
979{
980 struct server *srv;
981 struct userNode *un;
982
983 if(argc > 3)
984 irc_pong(argv[2], argv[1]);
985 else if ((srv = GetServerH(origin)))
986 irc_pong(self->name, srv->numeric);
987 else if ((un = GetUserH(origin)))
988 irc_pong(self->name, un->numeric);
989 else
990 irc_pong(self->name, origin);
991
992 timeq_del(0, timed_send_ping, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
993 timeq_del(0, timed_ping_timeout, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
994 timeq_add(now + ping_freq, timed_send_ping, 0);
995 received_ping();
996 return 1;
997}
998
999static CMD_FUNC(cmd_error_nick)
1000{
1001 /* Go back to original IRC length .. and try to reconnect :/ */
1002 change_nicklen(9);
1003 irc_squit(self, "Got erroneous nickname, truncating nicks.", NULL);
1004 return 1;
1005}
1006
1007struct create_desc {
1008 struct userNode *user;
1009 time_t when;
1010};
1011
1012static void
1013join_helper(struct chanNode *chan, void *data)
1014{
1015 struct create_desc *cd = data;
1016 AddChannelUser(cd->user, chan);
1017}
1018
1019static void
1020create_helper(char *name, void *data)
1021{
1022 struct create_desc *cd = data;
1023
1024 if (!strcmp(name, "0")) {
1025 while (cd->user->channels.used > 0)
1026 DelChannelUser(cd->user, cd->user->channels.list[0]->channel, 0, 0);
1027 return;
1028 }
1029
2aef5f4b 1030 AddChannelUser(cd->user, AddChannel(name, cd->when, NULL, NULL, NULL));
d76ed9a9 1031}
1032
1033static CMD_FUNC(cmd_create)
1034{
1035 struct create_desc cd;
1036 struct userNode *user;
1037
1038 if ((argc < 3) || !(user = GetUserH(origin)))
1039 return 0;
1040 cd.user = user;
1041 cd.when = atoi(argv[2]);
1042 parse_foreach(argv[1], join_helper, create_helper, NULL, NULL, &cd);
1043 return 1;
1044}
1045
1046static CMD_FUNC(cmd_join)
1047{
1048 struct create_desc cd;
1049
1050 if (!(cd.user = GetUserH(origin)))
1051 return 0;
1052 if (argc < 2)
1053 return 0;
1054 else if (argc < 3)
1055 cd.when = now;
1056 else
1057 cd.when = atoi(argv[2]);
1058 parse_foreach(argv[1], join_helper, create_helper, NULL, NULL, &cd);
1059 return 1;
1060}
1061
1062static CMD_FUNC(cmd_pong)
1063{
1064 if (argc < 3)
1065 return 0;
1066 if (!strcmp(argv[2], self->name)) {
1067 timeq_del(0, timed_send_ping, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
1068 timeq_del(0, timed_ping_timeout, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
1069 timeq_add(now + ping_freq, timed_send_ping, 0);
1070 received_ping();
1071 }
1072 return 1;
1073}
1074
1075static CMD_FUNC(cmd_nick)
1076{
1077 struct userNode *user;
1078 if ((user = GetUserH(origin))) {
1079 /* nick change (since the source is a user numeric) */
1080 if (argc < 2)
1081 return 0;
1082 NickChange(user, argv[1], 1);
1083 } else {
1084 struct server *serv;
1085 char modes[MAXLEN];
1086 /* new nick */
1087 if (argc < 9)
1088 return 0;
1089 serv = GetServerH(origin);
1090 if (argc > 9)
1091 unsplit_string(argv+6, argc-9, modes);
1092 else
1093 strcpy(modes, "+");
1094 AddUser(serv, argv[1], argv[4], argv[5], modes, argv[argc-2], argv[argc-1], atoi(argv[3]), argv[argc-3]);
1095 }
1096 return 1;
1097}
1098
1099static CMD_FUNC(cmd_account)
1100{
1101 struct userNode *user;
d9cd0e9d 1102 struct server *server;
b21e2cfe 1103 struct handle_info *hi;
d76ed9a9 1104
d9cd0e9d 1105 if ((argc < 3) || !origin || !(server = GetServerH(origin)))
d76ed9a9 1106 return 0; /* Origin must be server. */
d9cd0e9d 1107
1108 /* This next line appears to tremple origin.. why? */
d76ed9a9 1109 user = GetUserN(argv[1]);
1110 if (!user)
1111 return 1; /* A QUIT probably passed the ACCOUNT. */
805e7c7a 1112
1113 if(!extended_accounts) /* any need for this function without? */
1114 return 1;
d9cd0e9d 1115
1116 if(!strcmp(argv[2],"C"))
1117 {
b21e2cfe 1118 if((hi = loc_auth(argv[4], argv[5])))
d9cd0e9d 1119 {
1120 /* Return a AC A */
b21e2cfe 1121 putsock("%s " P10_ACCOUNT " %s A %s %lu", self->numeric, server->numeric , argv[3], hi->registered);
d9cd0e9d 1122
1123 }
1124 else
1125 {
1126 /* Return a AC D */
1127 putsock("%s " P10_ACCOUNT " %s D %s", self->numeric, server->numeric , argv[3]);
1128 }
1129 return 1;
1130 }
1131 else if(!strcmp(argv[2],"R"))
1132 call_account_func(user, argv[3]);
1133 else
1134 call_account_func(user, argv[2]); /* For backward compatability */
d76ed9a9 1135 return 1;
1136}
1137
1138static CMD_FUNC(cmd_fakehost)
1139{
1140 struct userNode *user;
1141
1142 if ((argc < 3) || !origin || !GetServerH(origin))
1143 return 0;
1144 if (!(user = GetUserN(argv[1])))
1145 return 1;
1146 assign_fakehost(user, argv[2], 0);
1147 return 1;
1148}
1149
1150static CMD_FUNC(cmd_burst)
1151{
1152 extern int rel_age;
2aef5f4b 1153 char modes[MAXLEN], *members = "";
1154 static char exemptlist[MAXLEN], banlist[MAXLEN];
a32da4c7 1155 unsigned int next = 3, res = 1;
2aef5f4b 1156 int ctype = 0, echeck = 0, bcheck = 0;
d76ed9a9 1157 struct chanNode *cNode;
1158 struct userNode *un;
1159 struct modeNode *mNode;
1160 long mode;
1161 char *user, *end, sep;
1162 time_t in_timestamp;
2aef5f4b 1163 char* parm = NULL;
d76ed9a9 1164
1165 if (argc < 3)
1166 return 0;
1167 modes[0] = 0;
2aef5f4b 1168
1169 exemptlist[0] = 0;
1170 banlist[0] = 0;
1171
d76ed9a9 1172 while (next < argc) {
1173 switch (argv[next][0]) {
1174 case '+': {
1175 const char *pos;
1176 int n_modes;
a32da4c7 1177 for (pos=argv[next], n_modes = 1; *pos; pos++)
d76ed9a9 1178 if ((*pos == 'k') || (*pos == 'l'))
1179 n_modes++;
1180 unsplit_string(argv+next, n_modes, modes);
1181 next += n_modes;
1182 break;
1183 }
2aef5f4b 1184 case '%': {
1185 for(parm = mysep(&argv[next], " "); /* parm = first param */
1186 parm; /* While param is not null */
1187 parm = mysep(&argv[next], " ") /* parm = next param */
1188 )
1189 {
1190 switch (parm[0]) {
1191 case '%': {
1192 ctype = 1;
1193 break;
1194 }
1195 case '~': {
1196 ctype = 2;
1197 break;
1198 }
1199 default: {
1200 break;
1201 }
1202 }
1203 if (ctype == 1) {
1204 if (bcheck == 0) {
1205 /* strip % char off start of very first ban */
23475fc6 1206 if (strlen(parm) > 1) {
1207 strncat(banlist, strtok(parm, "%"), sizeof(banlist) - 1 - strlen(banlist));
1208 strncat(banlist, " ", sizeof(banlist) - 1 - strlen(banlist));
1209 }
2aef5f4b 1210 bcheck = 1;
1211 } else {
1212 strncat(banlist, parm, sizeof(banlist) - 1 - strlen(banlist));
1213 strncat(banlist, " ", sizeof(banlist) - 1 - strlen(banlist));
1214 }
1215 } else if (ctype == 2) {
1216 if (echeck == 0) {
1217 echeck = 1;
1218 } else {
1219 strncat(exemptlist, parm, sizeof(exemptlist) - 1 - strlen(exemptlist));
1220 strncat(exemptlist, " ", sizeof(exemptlist) - 1 - strlen(exemptlist));
1221 }
1222 }
1223 }
1224 next++;
1225 break;
1226 }
a32da4c7 1227 default: members = argv[next++]; break;
2aef5f4b 1228 }
d76ed9a9 1229 }
1230
1231 in_timestamp = atoi(argv[2]);
1232 if ((cNode = dict_find(unbursted_channels, argv[1], NULL))) {
1233 cNode->timestamp = in_timestamp;
1234 dict_remove(unbursted_channels, cNode->name);
1235 irc_burst(cNode);
1236 }
2aef5f4b 1237 cNode = AddChannel(argv[1], in_timestamp, modes, banlist, exemptlist);
1238
a32da4c7 1239 /* Burst channel members in now. */
d76ed9a9 1240 for (user = members, sep = *members, mode = 0; sep; user = end) {
1241 for (end = user + 3; isalnum(*end) || *end == '[' || *end == ']'; end++) ;
1242 sep = *end++; end[-1] = 0;
1243 if (sep == ':') {
1244 mode = 0;
1245 while ((sep = *end++)) {
1246 if (sep == 'o')
1247 mode |= MODE_CHANOP;
55342ce8 1248 else if (sep == 'h')
1249 mode |= MODE_HALFOP;
d76ed9a9 1250 else if (sep == 'v')
1251 mode |= MODE_VOICE;
1117fc5a 1252 else if (isdigit(sep)) {
1253 mode |= MODE_CHANOP;
1254 while (isdigit(*end)) end++;
1255 } else
d76ed9a9 1256 break;
1257 }
1258 if (rel_age < 0)
1259 mode = 0;
1260 }
1261 if (!(un = GetUserN(user))) {
1262 res = 0;
1263 continue;
1264 }
1265 if ((mNode = AddChannelUser(un, cNode)))
1266 mNode->modes = mode;
1267 }
1268
1269 return res;
1270}
1271
1272static CMD_FUNC(cmd_mode)
1273{
1274 struct chanNode *cn;
1275 struct userNode *un;
182dd032 1276 char *sethost; // sethost - reed/apples
1277 int i; // sethost - reed/apples
d76ed9a9 1278
1279 if (argc < 3)
1280 return 0;
1281 if (!IsChannelName(argv[1])) {
1282 un = GetUserH(argv[1]);
1283 if (!un) {
1284 log_module(MAIN_LOG, LOG_ERROR, "Unable to find user %s whose mode is changing.", argv[1]);
1285 return 0;
1286 }
182dd032 1287 // sethost - reed/apples
1288 if (argc == 3)
d76ed9a9 1289 mod_usermode(un, argv[2]);
182dd032 1290 else {
1291 sethost = malloc(strlen(argv[2]) + 1 + strlen(argv[3]) + 1);
1292 i = 0;
1293 while((sethost[i++] = *argv[2]++));
1294 i--;
1295 sethost[i++] = ' ';
1296 while((sethost[i++] = *argv[3]++));
1297 mod_usermode(un, sethost); // sethost - reed/apples
1298 }
1299
d76ed9a9 1300 return 1;
1301 }
1302
1303 if (!(cn = GetChannel(argv[1]))) {
1304 log_module(MAIN_LOG, LOG_ERROR, "Unable to find channel %s whose mode is changing.", argv[1]);
1305 return 0;
1306 }
1307 if ((un = GetUserH(origin))) {
1308 struct modeNode *mn;
1309 /* Update idle time for person setting the mode */
1310 if ((mn = GetUserMode(cn, un)))
1311 mn->idle_since = now;
1312 } else {
1313 /* If it came from a server, reset timestamp to re-sync. */
1314 cn->timestamp = atoi(argv[argc-1]);
1315 }
1316
1317 return mod_chanmode(un, cn, argv+2, argc-2, MCP_ALLOW_OVB|MCP_FROM_SERVER|(un ? MC_NOTIFY : 0));
1318}
1319
1320static CMD_FUNC(cmd_opmode)
1321{
1322 struct chanNode *cn;
1323 struct userNode *un;
1324
1325 if (argc < 3)
1326 return 0;
1327
1328 if (!(cn = GetChannel(argv[1]))) {
1329 log_module(MAIN_LOG, LOG_ERROR, "Unable to find channel %s whose mode is changing.", argv[1]);
1330 return 0;
1331 }
1332 if (!(un = GetUserH(origin))) {
1333 log_module(MAIN_LOG, LOG_ERROR, "Unable to find user %s requesting OPMODE.", origin);
1334 return 0;
1335 }
1336 if (!IsOper(un)) {
1337 log_module(MAIN_LOG, LOG_ERROR, "Non-privileged user %s using OPMODE.", un->nick);
1338 return 0;
1339 }
1340
1341 return mod_chanmode(un, cn, argv+2, argc-2, MCP_ALLOW_OVB|MCP_FROM_SERVER); /* do NOT announce opmode locally */
1342}
1343
1344static int clear_chanmode(struct chanNode *channel, const char *modes);
1345
1346static CMD_FUNC(cmd_clearmode)
1347{
1348 struct chanNode *cn;
1349 struct userNode *un;
1350
1351 if (argc < 3)
1352 return 0;
1353
1354 if (!(cn = GetChannel(argv[1]))) {
1355 log_module(MAIN_LOG, LOG_ERROR, "Unable to find channel %s whose mode is changing.", argv[1]);
1356 return 0;
1357 }
1358 if (!(un = GetUserH(origin))) {
1359 log_module(MAIN_LOG, LOG_ERROR, "Unable to find user %s requesting CLEARMODE.", origin);
1360 return 0;
1361 }
1362 if (!IsOper(un)) {
1363 log_module(MAIN_LOG, LOG_ERROR, "Non-privileged user %s using CLEARMODE.", un->nick);
1364 return 0;
1365 }
1366
1367 return clear_chanmode(cn, argv[2]);
1368}
1369
1370static CMD_FUNC(cmd_topic)
1371{
1372 struct chanNode *cn;
1373 time_t chan_ts, topic_ts;
7fda2b52 1374 struct userNode *user;
d76ed9a9 1375
1376 if (argc < 3)
1377 return 0;
1378 if (!(cn = GetChannel(argv[1]))) {
1379 log_module(MAIN_LOG, LOG_ERROR, "Unable to find channel %s whose topic is being set", argv[1]);
1380 return 0;
1381 }
7fda2b52 1382
1383
1384 if (argc == 5) { /* Asuka / Topic Bursting IRCu's */
1385 user = GetUserH(origin);
d76ed9a9 1386 chan_ts = atoi(argv[2]);
1387 topic_ts = atoi(argv[3]);
7fda2b52 1388 } else if (argc >= 6) { /* Nefarious 0.5.0 */
1389 user = GetUserH(strtok(argv[2], "!"));
1390 chan_ts = atoi(argv[3]);
1391 topic_ts = atoi(argv[4]);
1392 } else { /* Regular IRCu (No Topic Bursting)*/
1393 user = GetUserH(origin);
d76ed9a9 1394 chan_ts = cn->timestamp;
1395 topic_ts = now;
1396 }
7fda2b52 1397
1398 SetChannelTopic(cn, user, user, argv[argc-1], 0);
d76ed9a9 1399 cn->topic_time = topic_ts;
1400 return 1;
1401}
1402
1403static CMD_FUNC(cmd_num_topic)
1404{
1405 struct chanNode *cn;
1406
1407 if (!argv[0])
1408 return 0; /* huh? */
1409 if (argv[2]) {
1410 cn = GetChannel(argv[2]);
1411 if (!cn) {
1412 log_module(MAIN_LOG, LOG_ERROR, "Unable to find channel %s in topic reply", argv[2]);
1413 return 0;
1414 }
1415 } else
1416 return 0;
1417
1418 switch (atoi(argv[0])) {
1419 case 331:
1420 cn->topic_time = 0;
1421 break; /* no topic */
1422 case 332:
1423 if (argc < 4)
1424 return 0;
1425 safestrncpy(cn->topic, unsplit_string(argv+3, argc-3, NULL), sizeof(cn->topic));
1426 break;
1427 case 333:
1428 if (argc < 5)
1429 return 0;
1430 safestrncpy(cn->topic_nick, argv[3], sizeof(cn->topic_nick));
1431 cn->topic_time = atoi(argv[4]);
1432 break;
1433 default:
1434 return 0; /* should never happen */
1435 }
1436 return 1;
1437}
1438
1439static CMD_FUNC(cmd_num_gline)
1440{
1441 if (argc < 6)
1442 return 0;
9a75756e 1443 gline_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0, 0);
d76ed9a9 1444 return 1;
1445}
1446
d914d1cb 1447static CMD_FUNC(cmd_num_shun)
1448{
1449 if (argc < 6)
1450 return 0;
1451 shun_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0);
1452 return 1;
1453}
1454
d76ed9a9 1455static CMD_FUNC(cmd_quit)
1456{
1457 struct userNode *user;
1458 if (argc < 2)
1459 return 0;
1460 /* Sometimes we get a KILL then a QUIT or the like, so we don't want to
1461 * call DelUser unless we have the user in our grasp. */
1462 if ((user = GetUserH(origin)))
1463 DelUser(user, NULL, false, argv[1]);
1464 return 1;
1465}
1466
1467static CMD_FUNC(cmd_kill)
1468{
1469 struct userNode *user;
1470 if (argc < 2)
1471 return 0;
1472 user = GetUserN(argv[1]);
1473 if (!user) {
1474 /* If we get a KILL for a non-existent user, it could be a
1475 * Ghost response to a KILL we sent out earlier. So we only
1476 * whine if the target is local.
1477 */
1478 if (!strncmp(argv[1], self->numeric, strlen(self->numeric)))
1479 log_module(MAIN_LOG, LOG_ERROR, "Unable to find kill victim %s", argv[1]);
1480 return 0;
1481 }
1482
1483 if (IsLocal(user) && IsService(user)) {
1484 /* TODO: rate limit this so silly things don't happen. */
1485 ReintroduceUser(user);
1486 return 1;
1487 }
1488
1489 DelUser(user, NULL, false, argv[2]);
1490 return 1;
1491}
1492
1493static CMD_FUNC(cmd_part)
1494{
1495 struct userNode *user;
1496
1497 if (argc < 2)
1498 return 0;
1499 user = GetUserH(origin);
1500 if (!user)
1501 return 0;
1502 parse_foreach(argv[1], part_helper, NULL, NULL, NULL, user);
1503 return 1;
1504}
1505
1506static CMD_FUNC(cmd_kick)
1507{
1508 if (argc < 3)
1509 return 0;
1510 ChannelUserKicked(GetUserH(origin), GetUserN(argv[2]), GetChannel(argv[1]));
1511 return 1;
1512}
1513
1514static CMD_FUNC(cmd_squit)
1515{
1516 struct server *server;
1517
1518 if (argc < 4)
1519 return 0;
1520 if (!(server = GetServerH(argv[1])))
1521 return 0;
1522
1523 if (server == self->uplink) {
1524 /* Force a reconnect to the currently selected server. */
1525 cManager.uplink->tries = 0;
1526 log_module(MAIN_LOG, LOG_INFO, "Squitting from uplink: %s", argv[3]);
1527 close_socket();
1528 return 1;
1529 }
1530
1531 DelServer(server, 0, argv[3]);
1532 return 1;
1533}
1534
1535static CMD_FUNC(cmd_privmsg)
1536{
1537 struct privmsg_desc pd;
1538 if (argc != 3)
1539 return 0;
1540 pd.user = GetUserH(origin);
1541 if (!pd.user || (IsGagged(pd.user) && !IsOper(pd.user)))
1542 return 1;
1543 pd.is_notice = 0;
1544 pd.text = argv[2];
1545 parse_foreach(argv[1], privmsg_chan_helper, NULL, privmsg_user_helper, privmsg_invalid, &pd);
1546 return 1;
1547}
1548
1549static CMD_FUNC(cmd_notice)
1550{
1551 struct privmsg_desc pd;
1552 if (argc != 3)
1553 return 0;
1554 pd.user = GetUserH(origin);
1555 if (!pd.user || (IsGagged(pd.user) && !IsOper(pd.user)))
1556 return 1;
1557 pd.is_notice = 1;
1558 pd.text = argv[2];
1559 parse_foreach(argv[1], privmsg_chan_helper, NULL, privmsg_user_helper, privmsg_invalid, &pd);
1560 return 1;
1561}
1562
1563static CMD_FUNC(cmd_away)
1564{
1565 struct userNode *uNode;
1566
1567 uNode = GetUserH(origin);
1568 if (!uNode)
1569 return 1;
1570 if (argc < 2)
1571 uNode->modes &= ~FLAGS_AWAY;
1572 else
1573 uNode->modes |= FLAGS_AWAY;
1574 return 1;
1575}
1576
1577static CMD_FUNC(cmd_gline)
1578{
1579 if (argc < 3)
1580 return 0;
1581 if (argv[2][0] == '+') {
1582 if (argc < 5)
1583 return 0;
9a75756e 1584 gline_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, 0, 0);
d76ed9a9 1585 return 1;
1586 } else if (argv[2][0] == '-') {
1587 gline_remove(argv[2]+1, 0);
1588 return 1;
1589 } else
1590 return 0;
1591}
1592
d914d1cb 1593static CMD_FUNC(cmd_shun)
1594{
1595 if (argc < 3)
1596 return 0;
1597 if (argv[2][0] == '+') {
1598 if (argc < 5)
1599 return 0;
1600 shun_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, 0);
1601 return 1;
1602 } else if (argv[2][0] == '-') {
1603 shun_remove(argv[2]+1, 0);
1604 return 1;
1605 } else
1606 return 0;
1607}
1608
d76ed9a9 1609static CMD_FUNC(cmd_svsnick)
1610{
1611 struct userNode *target, *dest;
1612 if ((argc < 4)
1613 || !(target = GetUserN(argv[1]))
1614 || !IsLocal(target)
1615 || (dest = GetUserH(argv[2])))
1616 return 0;
1617 NickChange(target, argv[2], 0);
1618 return 1;
1619}
1620
1621static oper_func_t *of_list;
1622static unsigned int of_size = 0, of_used = 0;
1623
1624void
1625free_user(struct userNode *user)
1626{
1627 free(user->nick);
1628 free(user);
1629}
1630
1631static void
1632parse_cleanup(void)
1633{
1634 unsigned int nn;
1635 free(of_list);
1636 free(privmsg_funcs);
1637 free(notice_funcs);
1638 free(mcf_list);
1639 dict_delete(irc_func_dict);
1640 for (nn=0; nn<dead_users.used; nn++)
1641 free_user(dead_users.list[nn]);
1642 userList_clean(&dead_users);
1643}
1644
1645static void
1646p10_conf_reload(void) {
1647 hidden_host_suffix = conf_get_data("server/hidden_host", RECDB_QSTRING);
1648}
1649
1650static void
1651remove_unbursted_channel(struct chanNode *cNode) {
1652 if (unbursted_channels)
1653 dict_remove(unbursted_channels, cNode->name);
1654}
1655
1656void
1657init_parse(void)
1658{
1659 const char *str, *desc;
1660 int numnick, usermask, max_users;
1661 char numer[COMBO_NUMERIC_LEN+1];
1662
1663 /* read config items */
805e7c7a 1664 str = conf_get_data("server/extended_accounts", RECDB_QSTRING);
1665 extended_accounts = str ? enabled_string(str) : 1;
d76ed9a9 1666 str = conf_get_data("server/ping_freq", RECDB_QSTRING);
1667 ping_freq = str ? ParseInterval(str) : 120;
1668 str = conf_get_data("server/ping_timeout", RECDB_QSTRING);
1669 ping_timeout = str ? ParseInterval(str) : 30;
1670 str = conf_get_data("server/force_n2k", RECDB_QSTRING);
1671 force_n2k = str ? enabled_string(str) : 1;
1672 str = conf_get_data("server/numeric", RECDB_QSTRING);
1673 if (!str) {
1674 log_module(MAIN_LOG, LOG_ERROR, "No server/numeric entry in config file.");
1675 exit(1);
1676 }
1677 numnick = atoi(str);
1678 str = conf_get_data("server/max_users", RECDB_QSTRING);
1679 max_users = str ? atoi(str) : 4096;
1680 for (usermask = 4; usermask < max_users; usermask <<= 1) ;
1681 usermask--;
1682 if ((numnick < 64) && (usermask < 4096) && !force_n2k)
1683 inttobase64(numer, (numnick << 12) + (usermask & 0x00fff), 3);
1684 else
1685 inttobase64(numer, (numnick << 18) + (usermask & 0x3ffff), 5);
1686
1687 str = conf_get_data("server/his_servername", RECDB_QSTRING);
1688 his_servername = str ? strdup(str) : NULL;
1689 str = conf_get_data("server/his_servercomment", RECDB_QSTRING);
1690 his_servercomment = str ? strdup(str) : NULL;
1691
1692 str = conf_get_data("server/hostname", RECDB_QSTRING);
1693 desc = conf_get_data("server/description", RECDB_QSTRING);
1694 if (!str || !desc) {
1695 log_module(MAIN_LOG, LOG_ERROR, "No server/hostname entry in config file.");
1696 exit(1);
1697 }
1698 self = AddServer(NULL, str, 0, boot_time, now, numer, desc);
1699 conf_register_reload(p10_conf_reload);
1700
1701 irc_func_dict = dict_new();
1702 dict_insert(irc_func_dict, CMD_BURST, cmd_burst);
1703 dict_insert(irc_func_dict, TOK_BURST, cmd_burst);
1704 dict_insert(irc_func_dict, CMD_CREATE, cmd_create);
1705 dict_insert(irc_func_dict, TOK_CREATE, cmd_create);
1706 dict_insert(irc_func_dict, CMD_EOB, cmd_eob);
1707 dict_insert(irc_func_dict, TOK_EOB, cmd_eob);
1708 dict_insert(irc_func_dict, CMD_EOB_ACK, cmd_eob_ack);
1709 dict_insert(irc_func_dict, TOK_EOB_ACK, cmd_eob_ack);
1710 dict_insert(irc_func_dict, CMD_MODE, cmd_mode);
1711 dict_insert(irc_func_dict, TOK_MODE, cmd_mode);
1712 dict_insert(irc_func_dict, CMD_NICK, cmd_nick);
1713 dict_insert(irc_func_dict, TOK_NICK, cmd_nick);
1714 dict_insert(irc_func_dict, CMD_ACCOUNT, cmd_account);
1715 dict_insert(irc_func_dict, TOK_ACCOUNT, cmd_account);
1716 dict_insert(irc_func_dict, CMD_FAKEHOST, cmd_fakehost);
1717 dict_insert(irc_func_dict, TOK_FAKEHOST, cmd_fakehost);
1718 dict_insert(irc_func_dict, CMD_PASS, cmd_pass);
1719 dict_insert(irc_func_dict, TOK_PASS, cmd_pass);
1720 dict_insert(irc_func_dict, CMD_PING, cmd_ping);
1721 dict_insert(irc_func_dict, TOK_PING, cmd_ping);
1722 dict_insert(irc_func_dict, CMD_PRIVMSG, cmd_privmsg);
1723 dict_insert(irc_func_dict, TOK_PRIVMSG, cmd_privmsg);
1724 dict_insert(irc_func_dict, CMD_PONG, cmd_pong);
1725 dict_insert(irc_func_dict, TOK_PONG, cmd_pong);
1726 dict_insert(irc_func_dict, CMD_QUIT, cmd_quit);
1727 dict_insert(irc_func_dict, TOK_QUIT, cmd_quit);
1728 dict_insert(irc_func_dict, CMD_SERVER, cmd_server);
1729 dict_insert(irc_func_dict, TOK_SERVER, cmd_server);
1730 dict_insert(irc_func_dict, CMD_JOIN, cmd_join);
1731 dict_insert(irc_func_dict, TOK_JOIN, cmd_join);
1732 dict_insert(irc_func_dict, CMD_PART, cmd_part);
1733 dict_insert(irc_func_dict, TOK_PART, cmd_part);
1734 dict_insert(irc_func_dict, CMD_ERROR, cmd_error);
1735 dict_insert(irc_func_dict, TOK_ERROR, cmd_error);
1736 dict_insert(irc_func_dict, CMD_TOPIC, cmd_topic);
1737 dict_insert(irc_func_dict, TOK_TOPIC, cmd_topic);
1738 dict_insert(irc_func_dict, CMD_AWAY, cmd_away);
1739 dict_insert(irc_func_dict, TOK_AWAY, cmd_away);
1740 dict_insert(irc_func_dict, CMD_SILENCE, cmd_dummy);
1741 dict_insert(irc_func_dict, TOK_SILENCE, cmd_dummy);
1742 dict_insert(irc_func_dict, CMD_KICK, cmd_kick);
1743 dict_insert(irc_func_dict, TOK_KICK, cmd_kick);
1744 dict_insert(irc_func_dict, CMD_SQUIT, cmd_squit);
1745 dict_insert(irc_func_dict, TOK_SQUIT, cmd_squit);
1746 dict_insert(irc_func_dict, CMD_KILL, cmd_kill);
1747 dict_insert(irc_func_dict, TOK_KILL, cmd_kill);
1748 dict_insert(irc_func_dict, CMD_NOTICE, cmd_notice);
1749 dict_insert(irc_func_dict, TOK_NOTICE, cmd_notice);
1750 dict_insert(irc_func_dict, CMD_STATS, cmd_stats);
1751 dict_insert(irc_func_dict, TOK_STATS, cmd_stats);
1752 dict_insert(irc_func_dict, CMD_SVSNICK, cmd_svsnick);
1753 dict_insert(irc_func_dict, TOK_SVSNICK, cmd_svsnick);
1754 dict_insert(irc_func_dict, CMD_WHOIS, cmd_whois);
1755 dict_insert(irc_func_dict, TOK_WHOIS, cmd_whois);
1756 dict_insert(irc_func_dict, CMD_GLINE, cmd_gline);
1757 dict_insert(irc_func_dict, TOK_GLINE, cmd_gline);
d914d1cb 1758 dict_insert(irc_func_dict, CMD_SHUN, cmd_shun);
1759 dict_insert(irc_func_dict, TOK_SHUN, cmd_shun);
d76ed9a9 1760 dict_insert(irc_func_dict, CMD_OPMODE, cmd_opmode);
1761 dict_insert(irc_func_dict, TOK_OPMODE, cmd_opmode);
1762 dict_insert(irc_func_dict, CMD_CLEARMODE, cmd_clearmode);
1763 dict_insert(irc_func_dict, TOK_CLEARMODE, cmd_clearmode);
1764 dict_insert(irc_func_dict, CMD_VERSION, cmd_version);
1765 dict_insert(irc_func_dict, TOK_VERSION, cmd_version);
1766 dict_insert(irc_func_dict, CMD_ADMIN, cmd_admin);
1767 dict_insert(irc_func_dict, TOK_ADMIN, cmd_admin);
1768
1769 /* In P10, DESTRUCT doesn't do anything except be broadcast to servers.
1770 * Apparently to obliterate channels from any servers that think they
1771 * exist?
1772 */
1773 dict_insert(irc_func_dict, CMD_DESTRUCT, cmd_dummy);
1774 dict_insert(irc_func_dict, TOK_DESTRUCT, cmd_dummy);
1775 /* Ignore invites */
1776 dict_insert(irc_func_dict, CMD_INVITE, cmd_dummy);
1777 dict_insert(irc_func_dict, TOK_INVITE, cmd_dummy);
1778 /* DESYNCH is just informational, so ignore it */
1779 dict_insert(irc_func_dict, CMD_DESYNCH, cmd_dummy);
1780 dict_insert(irc_func_dict, TOK_DESYNCH, cmd_dummy);
1781 /* Ignore channel operator notices. */
1782 dict_insert(irc_func_dict, CMD_WALLCHOPS, cmd_dummy);
1783 dict_insert(irc_func_dict, TOK_WALLCHOPS, cmd_dummy);
1784 dict_insert(irc_func_dict, CMD_WALLVOICES, cmd_dummy);
1785 dict_insert(irc_func_dict, TOK_WALLVOICES, cmd_dummy);
55342ce8 1786 dict_insert(irc_func_dict, CMD_WALLHOPS, cmd_dummy);
1787 dict_insert(irc_func_dict, TOK_WALLHOPS, cmd_dummy);
d76ed9a9 1788 /* Ignore opers being silly. */
1789 dict_insert(irc_func_dict, CMD_WALLOPS, cmd_dummy);
1790 dict_insert(irc_func_dict, TOK_WALLOPS, cmd_dummy);
55342ce8 1791 dict_insert(irc_func_dict, CMD_WALLHOPS, cmd_dummy);
1792 dict_insert(irc_func_dict, TOK_WALLHOPS, cmd_dummy);
41fadebe 1793 dict_insert(irc_func_dict, TOK_WALLUSERS, cmd_dummy);
850bf0cf 1794 /* Ignore dnsbl exemptions */
1795 dict_insert(irc_func_dict, TOK_EXEMPT, cmd_dummy);
fede8b64 1796 dict_insert(irc_func_dict, TOK_MARK, cmd_dummy);
1797 /* Ignore privs */
1798 dict_insert(irc_func_dict, TOK_PRIVS, cmd_dummy);
a74c9eac 1799 /* Ignore remote luser */
0c835e0f 1800 dict_insert(irc_func_dict, TOK_LUSERS, cmd_dummy);
d76ed9a9 1801 /* We have reliable clock! Always! Wraaa! */
1802 dict_insert(irc_func_dict, CMD_SETTIME, cmd_dummy);
1803 dict_insert(irc_func_dict, TOK_SETTIME, cmd_dummy);
1804 /* handle topics */
1805 dict_insert(irc_func_dict, "331", cmd_num_topic);
1806 dict_insert(irc_func_dict, "332", cmd_num_topic);
1807 dict_insert(irc_func_dict, "333", cmd_num_topic);
1808 dict_insert(irc_func_dict, "345", cmd_dummy); /* blah has been invited to blah */
1809 dict_insert(irc_func_dict, "432", cmd_error_nick); /* Erroneus [sic] nickname */
1810 /* ban list resetting */
1811 /* "stats g" responses */
1812 dict_insert(irc_func_dict, "247", cmd_num_gline);
d914d1cb 1813 dict_insert(irc_func_dict, "542", cmd_num_shun);
d76ed9a9 1814 dict_insert(irc_func_dict, "219", cmd_dummy); /* "End of /STATS report" */
1815 /* other numeric responses we might get */
1816 dict_insert(irc_func_dict, "401", cmd_dummy); /* target left network */
1817 dict_insert(irc_func_dict, "403", cmd_dummy); /* no such channel */
1818 dict_insert(irc_func_dict, "404", cmd_dummy); /* cannot send to channel */
0d16e639 1819 dict_insert(irc_func_dict, "439", cmd_dummy); /* target change too fast */
d76ed9a9 1820 dict_insert(irc_func_dict, "441", cmd_dummy); /* target isn't on that channel */
1821 dict_insert(irc_func_dict, "442", cmd_dummy); /* you aren't on that channel */
1822 dict_insert(irc_func_dict, "443", cmd_dummy); /* is already on channel (after invite?) */
1823 dict_insert(irc_func_dict, "461", cmd_dummy); /* Not enough parameters (after TOPIC w/ 0 args) */
1824 dict_insert(irc_func_dict, "467", cmd_dummy); /* Channel key already set */
1825
1826 num_privmsg_funcs = 16;
1827 privmsg_funcs = malloc(sizeof(privmsg_func_t)*num_privmsg_funcs);
1828 memset(privmsg_funcs, 0, sizeof(privmsg_func_t)*num_privmsg_funcs);
1829
1830 num_notice_funcs = 16;
1831 notice_funcs = malloc(sizeof(privmsg_func_t)*num_notice_funcs);
1832 memset(notice_funcs, 0, sizeof(privmsg_func_t)*num_notice_funcs);
1833
1834 userList_init(&dead_users);
1835 reg_del_channel_func(remove_unbursted_channel);
1836 reg_exit_func(parse_cleanup);
1837}
1838
1839int
1840parse_line(char *line, int recursive)
1841{
1842 char *argv[MAXNUMPARAMS], *origin;
1843 int argc, cmd, res=0;
1844 cmd_func_t *func;
1845
1846 argc = split_line(line, true, MAXNUMPARAMS, argv);
1847 cmd = self->uplink || !argv[0][1] || !argv[0][2];
1848 if (argc > cmd) {
1849 if (cmd) {
1850 if (argv[0][0] == ':') {
1851 origin = argv[0]+1;
1852 } else if (!argv[0][1] || !argv[0][2]) {
1853 struct server *sNode = GetServerN(argv[0]);
1854 origin = sNode ? sNode->name : 0;
1855 } else {
1856 struct userNode *uNode = GetUserN(argv[0]);
1857 origin = uNode ? uNode->nick : 0;
1858 }
1859 } else
1860 origin = 0;
1861 if ((func = dict_find(irc_func_dict, argv[cmd], NULL)))
1862 res = func(origin, argc-cmd, argv+cmd);
1863 }
1864 if (!res) {
1865 log_module(MAIN_LOG, LOG_ERROR, "PARSE ERROR on line: %s", unsplit_string(argv, argc, NULL));
1866 } else if (!recursive) {
1867 unsigned int i;
1868 for (i=0; i<dead_users.used; i++)
1869 free_user(dead_users.list[i]);
1870 dead_users.used = 0;
1871 }
1872 return res;
1873}
1874
1875static void
1876parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data)
1877{
1878 char *j, old;
1879 do {
1880 j = target_list;
1881 while (*j != 0 && *j != ',')
1882 j++;
1883 old = *j;
1884 *j = 0;
1885 if (IsChannelName(target_list)
1886 || (target_list[0] == '0' && target_list[1] == '\0')) {
1887 struct chanNode *chan = GetChannel(target_list);
1888 if (chan) {
1889 if (cf)
1890 cf(chan, data);
1891 } else {
1892 if (nc)
1893 nc(target_list, data);
1894 }
1895 } else {
1896 struct userNode *user;
1897 struct privmsg_desc *pd = data;
1898
1899 pd->is_qualified = 0;
1900 if (*target_list == '@') {
1901 user = NULL;
1902 } else if (strchr(target_list, '@')) {
1903 struct server *server;
1904
1905 pd->is_qualified = 1;
1906 user = GetUserH(strtok(target_list, "@"));
1907 server = GetServerH(strtok(NULL, "@"));
1908
1909 if (user && (user->uplink != server)) {
1910 /* Don't attempt to index into any arrays
1911 using a user's numeric on another server. */
1912 user = NULL;
1913 }
1914 } else {
1915 user = GetUserN(target_list);
1916 }
1917
1918 if (user) {
1919 if (uf)
1920 uf(user, data);
1921 } else {
1922 if (nu)
1923 nu(target_list, data);
1924 }
1925 }
1926 target_list = j+1;
1927 } while (old == ',');
1928}
1929
1930static int
1931get_local_numeric(void)
1932{
1933 static unsigned int next_numeric = 0;
1934 if (self->clients > self->num_mask)
1935 return -1;
1936 while (self->users[next_numeric])
1937 if (++next_numeric > self->num_mask)
1938 next_numeric = 0;
1939 return next_numeric;
1940}
1941
1942static void
1943make_numeric(struct server *svr, int local_num, char *outbuf)
1944{
1945 int slen, llen;
1946
1947 if (force_n2k || svr->numeric[1]) {
1948 slen = 2;
1949 llen = 3;
1950 } else {
1951 slen = 1;
1952 llen = (local_num < 64*64) ? 2 : 3;
1953 }
1954 strncpy(outbuf, svr->numeric, slen);
1955 inttobase64(outbuf+slen, local_num, llen);
1956 outbuf[slen+llen] = 0;
1957}
1958
1959struct server *
1960AddServer(struct server *uplink, const char *name, int hops, time_t boot, time_t link, const char *numeric, const char *description)
1961{
1962 struct server* sNode;
1963 int slen, mlen;
1964
1965 if ((sNode = GetServerN(numeric))) {
1966 /* This means we're trying to re-add an existant server.
1967 * To be safe, we should forget the previous incarnation.
1968 * (And all its linked servers.)
1969 *
1970 * It usually only happens in replays when the original
1971 * had a ping timeout and the replay didn't (because
1972 * replaying a ping timeout invariably gets things wrong).
1973 */
1974 DelServer(sNode, 0, NULL);
1975 }
1976
1977 switch (strlen(numeric)) {
1978 case 5: slen = 2; mlen = 3; break;
1979 case 4: slen = 1; mlen = 3; break;
1980 case 3: slen = 1; mlen = 2; break;
1981 default:
1982 log_module(MAIN_LOG, LOG_ERROR, "AddServer(\"%s\", \"%s\", ...): Numeric %s has invalid length.", uplink->name, name, numeric);
1983 return NULL;
1984 }
1985
1986 sNode = calloc(1, sizeof(*sNode));
1987 sNode->uplink = uplink;
1988 safestrncpy(sNode->name, name, sizeof(sNode->name));
1989 sNode->num_mask = base64toint(numeric+slen, mlen);
1990 sNode->hops = hops;
1991 sNode->boot = boot;
1992 sNode->link = link;
1993 strncpy(sNode->numeric, numeric, slen);
1994 safestrncpy(sNode->description, description, sizeof(sNode->description));
1995 sNode->users = calloc(sNode->num_mask+1, sizeof(*sNode->users));
1996 serverList_init(&sNode->children);
1997 if (sNode->uplink) {
1998 /* uplink may be NULL if we're just building ourself */
1999 serverList_append(&sNode->uplink->children, sNode);
2000 }
2001 servers_num[base64toint(numeric, slen)] = sNode;
2002 dict_insert(servers, sNode->name, sNode);
2003 return sNode;
2004}
2005
2006void DelServer(struct server* serv, int announce, const char *message)
2007{
2008 unsigned int i;
2009
2010 /* If we receive an ERROR command before the SERVER
2011 * command a NULL server can be passed */
2012 if (!serv)
2013 return;
2014
2015 /* Hrm, what's the right way to SQUIT some other server?
2016 * (This code is only to handle killing juped servers.) */
2017 if (announce && (serv->uplink == self) && (serv != self->uplink))
2018 irc_squit(serv, message, NULL);
2019
2020 /* must recursively remove servers linked to this one first */
2021 for (i=serv->children.used;i>0;)
2022 if (serv->children.list[--i] != self)
2023 DelServer(serv->children.list[i], false, NULL);
2024
2025 /* clean up server's user hash tables */
2026 for (i=0;i<=serv->num_mask;i++)
2027 if (serv->users[i])
2028 DelUser(serv->users[i], NULL, false, "server delinked");
2029
2030 /* delete server */
2031 if (serv->uplink)
2032 serverList_remove(&serv->uplink->children, serv);
2033 if (serv == self->uplink)
2034 self->uplink = NULL;
2035 servers_num[base64toint(serv->numeric, strlen(serv->numeric))] = NULL;
2036 dict_remove(servers, serv->name);
2037 serverList_clean(&serv->children);
2038 free(serv->users);
2039 free(serv);
2040}
2041
2042struct userNode *
a32da4c7 2043AddService(const char *nick, const char *modes, const char *desc, const char *hostname)
d76ed9a9 2044{
2045 char numeric[COMBO_NUMERIC_LEN+1];
2046 int local_num = get_local_numeric();
2047 time_t timestamp = now;
2048 struct userNode *old_user = GetUserH(nick);
2049
2050 if (old_user) {
2051 if (IsLocal(old_user))
2052 return old_user;
2053 timestamp = old_user->timestamp - 1;
2054 }
2055 if (local_num == -1) {
2056 log_module(MAIN_LOG, LOG_ERROR, "Unable to allocate numnick for service %s", nick);
2057 return 0;
2058 }
2059 if (!hostname)
2060 hostname = self->name;
2061 make_numeric(self, local_num, numeric);
180e0971 2062 /* TODO: Make these modes part of the conf file */
2063 return AddUser(self, nick, nick, hostname, modes ? modes : "+oik", numeric, desc, now, "AAAAAA");
d76ed9a9 2064}
2065
2066struct userNode *
2067AddClone(const char *nick, const char *ident, const char *hostname, const char *desc)
2068{
2069 char numeric[COMBO_NUMERIC_LEN+1];
2070 int local_num = get_local_numeric();
2071 time_t timestamp = now;
2072 struct userNode *old_user = GetUserH(nick);
2073
2074 if (old_user) {
2075 if (IsLocal(old_user))
2076 return old_user;
2077 timestamp = old_user->timestamp - 1;
2078 }
2079 if (local_num == -1) {
2080 log_module(MAIN_LOG, LOG_ERROR, "Unable to allocate numnick for clone %s", nick);
2081 return 0;
2082 }
2083 make_numeric(self, local_num, numeric);
2084 return AddUser(self, nick, ident, hostname, "+i", numeric, desc, timestamp, "AAAAAA");
2085}
2086
2087int
2088is_valid_nick(const char *nick) {
ec1a68c8 2089 unsigned int ii;
d76ed9a9 2090 /* IRC has some of The Most Fucked-Up ideas about character sets
2091 * in the world.. */
2092 if (!isalpha(*nick) && !strchr("{|}~[\\]^_`", *nick))
2093 return 0;
ec1a68c8 2094 for (ii = 0; nick[ii]; ++ii)
2095 if (!isalnum(nick[ii]) && !strchr("{|}~[\\]^-_`", nick[ii]))
d76ed9a9 2096 return 0;
2097 if (strlen(nick) > nicklen)
2098 return 0;
2099 return 1;
2100}
2101
2102static struct userNode*
2103AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *numeric, const char *userinfo, time_t timestamp, const char *realip)
2104{
2105 struct userNode *oldUser, *uNode;
2106 unsigned int n, ignore_user;
2107
2108 if ((strlen(numeric) < 3) || (strlen(numeric) > 5)) {
2109 log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): numeric %s wrong length!", uplink, nick, numeric);
2110 return NULL;
2111 }
2112
2113 if (!uplink) {
2114 log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): server for numeric %s doesn't exist!", uplink, nick, numeric);
2115 return NULL;
2116 }
2117
2118 if (uplink != GetServerN(numeric)) {
2119 log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): server for numeric %s differs from nominal uplink %s.", uplink, nick, numeric, uplink->name);
2120 return NULL;
2121 }
2122
2123 if (!is_valid_nick(nick)) {
2124 log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): invalid nickname detected.", uplink, nick);
2125 return NULL;
2126 }
2127
2128 ignore_user = 0;
2129 if ((oldUser = GetUserH(nick))) {
2130 if (IsLocal(oldUser) && (IsService(oldUser) || IsPersistent(oldUser))) {
2131 /* The service should collide the new user off. */
2132 oldUser->timestamp = timestamp - 1;
2133 irc_user(oldUser);
2134 }
2135 if (oldUser->timestamp > timestamp) {
2136 /* "Old" user is really newer; remove them */
2137 DelUser(oldUser, 0, 1, "Overruled by older nick");
2138 } else {
2139 /* User being added is too new; do not add them to
2140 * clients, but do add them to the server's list, since it
2141 * will send a KILL and QUIT soon. */
2142 ignore_user = 1;
2143 }
2144 }
2145
2146 /* create new usernode and set all values */
2147 uNode = calloc(1, sizeof(*uNode));
2148 uNode->nick = strdup(nick);
2149 safestrncpy(uNode->ident, ident, sizeof(uNode->ident));
2150 safestrncpy(uNode->info, userinfo, sizeof(uNode->info));
2151 safestrncpy(uNode->hostname, hostname, sizeof(uNode->hostname));
2152 safestrncpy(uNode->numeric, numeric, sizeof(uNode->numeric));
2153 uNode->ip.s_addr = htonl(base64toint(realip, 6));
2154 uNode->timestamp = timestamp;
2155 modeList_init(&uNode->channels);
2156 uNode->uplink = uplink;
2157 if (++uNode->uplink->clients > uNode->uplink->max_clients) {
2158 uNode->uplink->max_clients = uNode->uplink->clients;
2159 }
2160 uNode->num_local = base64toint(numeric+strlen(uNode->uplink->numeric), 3) & uNode->uplink->num_mask;
2161 uNode->uplink->users[uNode->num_local] = uNode;
2162 mod_usermode(uNode, modes);
2163 if (ignore_user)
2164 return uNode;
2165
2166 dict_insert(clients, uNode->nick, uNode);
2167 if (dict_size(clients) > max_clients) {
2168 max_clients = dict_size(clients);
2169 max_clients_time = now;
2170 }
2171 if (IsLocal(uNode))
2172 irc_user(uNode);
2173 for (n=0; n<nuf_used; n++)
2174 if (nuf_list[n](uNode))
2175 break;
3fdd6a74 2176
2177 if ((uNode->loc == 1) && (uNode->handle_info))
2178 send_func_list(uNode);
2179
d76ed9a9 2180 return uNode;
2181}
2182
2183/* removes user from it's server's hash table and nick hash table */
2184void
2185DelUser(struct userNode* user, struct userNode *killer, int announce, const char *why)
2186{
2187 unsigned int n;
2188
ec1a68c8 2189 verify(user);
2190
d76ed9a9 2191 /* mark them as dead, in case anybody cares */
2192 user->dead = 1;
2193
ac3bdc8d 2194 /* remove pending adduser commands */
2195 wipe_adduser_pending(NULL, user);
2196
d76ed9a9 2197 /* remove user from all channels */
2198 while (user->channels.used > 0)
2199 DelChannelUser(user, user->channels.list[user->channels.used-1]->channel, false, 0);
2200
2201 /* Call these in reverse order so ChanServ can update presence
2202 information before NickServ nukes the handle_info. */
2203 for (n = duf_used; n > 0; )
2204 duf_list[--n](user, killer, why);
2205
2206 user->uplink->clients--;
2207 user->uplink->users[user->num_local] = NULL;
2208 if (IsOper(user))
2209 userList_remove(&curr_opers, user);
2210 /* remove from global dictionary, but not if after a collide */
2211 if (user == dict_find(clients, user->nick, NULL))
2212 dict_remove(clients, user->nick);
2213
2214 if (IsInvisible(user))
2215 invis_clients--;
2216
2217 if (announce) {
2218 if (IsLocal(user))
2219 irc_quit(user, why);
2220 else
2221 irc_kill(killer, user, why);
2222 }
2223
2224 modeList_clean(&user->channels);
2225 /* We don't free them, in case we try to privmsg them or something
2226 * (like when a stupid oper kills themself). We just put them onto
2227 * a list of clients that get freed after processing each line.
2228 */
2229 if (dead_users.size)
2230 userList_append(&dead_users, user);
2231 else
2232 free_user(user);
2233}
2234
697f4c9a 2235static void call_oper_funcs(struct userNode *user);
2236
d76ed9a9 2237void mod_usermode(struct userNode *user, const char *mode_change) {
d76ed9a9 2238 int add = 1;
2239 const char *word = mode_change;
2240
2241 if (!user || !mode_change)
2242 return;
a32da4c7 2243 while (*word != ' ' && *word) word++;
2244 while (*word == ' ') word++;
d76ed9a9 2245 while (1) {
2246#define do_user_mode(FLAG) do { if (add) user->modes |= FLAG; else user->modes &= ~FLAG; } while (0)
2247 switch (*mode_change++) {
2248 case 0: case ' ': return;
2249 case '+': add = 1; break;
2250 case '-': add = 0; break;
2251 case 'o':
d76ed9a9 2252 if (add) {
ec0120d1 2253 if(!IsOper(user)) { /* Dont re-oper an oper */
2254 userList_append(&curr_opers, user);
2255 call_oper_funcs(user);
2256 }
d76ed9a9 2257 } else {
2258 userList_remove(&curr_opers, user);
2259 }
3d0b24ce 2260 do_user_mode(FLAGS_OPER);
d76ed9a9 2261 break;
2262 case 'O': do_user_mode(FLAGS_LOCOP); break;
2263 case 'i': do_user_mode(FLAGS_INVISIBLE);
2264 if (add)
2265 invis_clients++;
2266 else
2267 invis_clients--;
2268 break;
2269 case 'w': do_user_mode(FLAGS_WALLOP); break;
2270 case 's': do_user_mode(FLAGS_SERVNOTICE); break;
2271 case 'd': do_user_mode(FLAGS_DEAF); break;
2272 case 'k': do_user_mode(FLAGS_SERVICE); break;
2273 case 'g': do_user_mode(FLAGS_GLOBAL); break;
182dd032 2274 // sethost - reed/apples
2275 // case 'h': do_user_mode(FLAGS_HELPER); break;
2276 // I check if there's an 'h' in the first part, and if there,
2277 // then everything after the space becomes their new host.
2278 case 'h': do_user_mode(FLAGS_SETHOST);
2279 if (*word) {
2280 char sethost[MAXLEN];
2281 unsigned int ii;
2282 for (ii=0; (*word != ' ') && (*word != '\0'); )
2283 sethost[ii++] = *word++;
2284 sethost[ii] = 0;
2285 while (*word == ' ')
2286 word++;
2287 safestrncpy(user->sethost, sethost, sizeof(user->sethost));
2288 }
2289 break;
d76ed9a9 2290 case 'x': do_user_mode(FLAGS_HIDDEN_HOST); break;
2291 case 'r':
2292 if (*word) {
2293 char tag[MAXLEN];
2294 unsigned int ii;
2295 for (ii=0; (*word != ' ') && (*word != '\0'); )
2296 tag[ii++] = *word++;
2297 tag[ii] = 0;
2298 while (*word == ' ')
2299 word++;
2300 call_account_func(user, tag);
2301 }
2302 break;
2303 case 'f':
2304 if (*word) {
2305 char host[MAXLEN];
2306 unsigned int ii;
2307 for (ii=0; (*word != ' ') && (*word != '\0'); )
2308 host[ii++] = *word++;
2309 host[ii] = 0;
2310 while (*word == ' ')
2311 word++;
2312 assign_fakehost(user, host, 0);
2313 }
2314 break;
2315 }
2316#undef do_user_mode
2317 }
2318}
2319
2320struct mod_chanmode *
2321mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
2322{
2323 struct mod_chanmode *change;
2324 unsigned int ii, in_arg, ch_arg, add;
2325
2326 if (argc == 0)
2327 return NULL;
2328 if (!(change = mod_chanmode_alloc(argc - 1)))
2329 return NULL;
2330
2331 for (ii = ch_arg = 0, in_arg = add = 1;
2332 (modes[0][ii] != '\0') && (modes[0][ii] != ' ');
2333 ++ii) {
2334 switch (modes[0][ii]) {
2335 case '+':
2336 add = 1;
2337 break;
2338 case '-':
2339 add = 0;
2340 break;
2341#define do_chan_mode(FLAG) do { if (add) change->modes_set |= (FLAG), change->modes_clear &= ~(FLAG); else change->modes_clear |= (FLAG), change->modes_set &= ~(FLAG); } while(0)
2342 case 'C': do_chan_mode(MODE_NOCTCPS); break;
2343 case 'D': do_chan_mode(MODE_DELAYJOINS); break;
2344 case 'c': do_chan_mode(MODE_NOCOLORS); break;
2345 case 'i': do_chan_mode(MODE_INVITEONLY); break;
2346 case 'm': do_chan_mode(MODE_MODERATED); break;
2347 case 'n': do_chan_mode(MODE_NOPRIVMSGS); break;
2348 case 'p': do_chan_mode(MODE_PRIVATE); break;
2349 case 'r': do_chan_mode(MODE_REGONLY); break;
2350 case 's': do_chan_mode(MODE_SECRET); break;
2351 case 't': do_chan_mode(MODE_TOPICLIMIT); break;
d0cb2fb6 2352 case 'S': do_chan_mode(MODE_STRIPCOLOR); break;
2353 case 'M': do_chan_mode(MODE_MODUNREG); break;
2354 case 'N': do_chan_mode(MODE_NONOTICE); break;
2355 case 'Q': do_chan_mode(MODE_NOQUITMSGS); break;
2356 case 'T': do_chan_mode(MODE_NOAMSG); break;
2357 case 'O': do_chan_mode(MODE_OPERSONLY); break;
c8ca69a0 2358 case 'Z': do_chan_mode(MODE_SSLONLY); break;
88c7cb10 2359 case 'L': do_chan_mode(MODE_HIDEMODE); break;
ec1a68c8 2360 case 'z':
2361 if (!(flags & MCP_REGISTERED)) {
2362 do_chan_mode(MODE_REGISTERED);
2363 } else {
2364 mod_chanmode_free(change);
2365 return NULL;
2366 }
2367 break;
d76ed9a9 2368#undef do_chan_mode
2369 case 'l':
2370 if (add) {
2371 if (in_arg >= argc)
2372 goto error;
2373 change->modes_set |= MODE_LIMIT;
2374 change->new_limit = atoi(modes[in_arg++]);
2375 } else {
2376 change->modes_set &= ~MODE_LIMIT;
2377 change->modes_clear |= MODE_LIMIT;
2378 }
2379 break;
2380 case 'k':
2381 if (add) {
2382 if (in_arg >= argc)
2383 goto error;
2384 change->modes_set |= MODE_KEY;
2385 safestrncpy(change->new_key, modes[in_arg++], sizeof(change->new_key));
2386 } else {
2387 change->modes_clear |= MODE_KEY;
2388 if (!(flags & MCP_KEY_FREE)) {
2389 if (in_arg >= argc)
2390 goto error;
2391 in_arg++;
2392 }
2393 }
2394 break;
2395 case 'b':
2396 if (!(flags & MCP_ALLOW_OVB))
2397 goto error;
2398 if (in_arg >= argc)
2399 goto error;
2400 change->args[ch_arg].mode = MODE_BAN;
2401 if (!add)
2402 change->args[ch_arg].mode |= MODE_REMOVE;
a32da4c7 2403 change->args[ch_arg++].u.hostmask = modes[in_arg++];
d76ed9a9 2404 break;
2aef5f4b 2405 case 'e':
2406 if (!(flags & MCP_ALLOW_OVB))
2407 goto error;
2408 if (in_arg >= argc)
2409 goto error;
2410 change->args[ch_arg].mode = MODE_EXEMPT;
2411 if (!add)
2412 change->args[ch_arg].mode |= MODE_REMOVE;
a32da4c7 2413 change->args[ch_arg++].u.hostmask = modes[in_arg++];
2aef5f4b 2414 break;
55342ce8 2415 case 'o': case 'h': case 'v':
d76ed9a9 2416 {
2417 struct userNode *victim;
2418 if (!(flags & MCP_ALLOW_OVB))
2419 goto error;
2420 if (in_arg >= argc)
2421 goto error;
55342ce8 2422
2423 if (modes[0][ii] == 'o')
2424 change->args[ch_arg].mode = MODE_CHANOP;
2425 else if (modes[0][ii] == 'h')
2426 change->args[ch_arg].mode = MODE_HALFOP;
2427 else if (modes[0][ii] == 'v')
2428 change->args[ch_arg].mode = MODE_VOICE;
2429
d76ed9a9 2430 if (!add)
2431 change->args[ch_arg].mode |= MODE_REMOVE;
2432 if (flags & MCP_FROM_SERVER)
2433 victim = GetUserN(modes[in_arg++]);
2434 else
2435 victim = GetUserH(modes[in_arg++]);
2436 if (!victim)
2437 continue;
a32da4c7 2438 if ((change->args[ch_arg].u.member = GetUserMode(channel, victim)))
d76ed9a9 2439 ch_arg++;
2440 break;
2441 }
2442 default:
2443 if (!(flags & MCP_FROM_SERVER))
2444 goto error;
2445 break;
2446 }
2447 }
2448 change->argc = ch_arg; /* in case any turned out to be ignored */
2449 if (change->modes_set & MODE_SECRET) {
2450 change->modes_set &= ~(MODE_PRIVATE);
2451 change->modes_clear |= MODE_PRIVATE;
2452 } else if (change->modes_set & MODE_PRIVATE) {
2453 change->modes_set &= ~(MODE_SECRET);
2454 change->modes_clear |= MODE_SECRET;
2455 }
2456 return change;
2457 error:
2458 mod_chanmode_free(change);
2459 return NULL;
2460}
2461
2462struct chanmode_buffer {
2463 char modes[MAXLEN];
2464 char args[MAXLEN];
2465 struct chanNode *channel;
2466 struct userNode *actor;
2467 unsigned int modes_used;
2468 unsigned int args_used;
2469 size_t chname_len;
2470 unsigned int is_add : 1;
2471 unsigned int is_chanop : 1;
2472};
2473
2474static void
2475mod_chanmode_append(struct chanmode_buffer *buf, char ch, const char *arg)
2476{
2477 size_t arg_len = strlen(arg);
2478 if (buf->modes_used > (MAXMODEPARAMS) ||
2479 buf->modes_used + buf->args_used + buf->chname_len + arg_len > 450) {
2480 memcpy(buf->modes + buf->modes_used, buf->args, buf->args_used);
2481 buf->modes[buf->modes_used + buf->args_used] = '\0';
2482 irc_mode((buf->is_chanop ? buf->actor : NULL), buf->channel, buf->modes);
2483 buf->modes[0] = buf->is_add ? '+' : '-';
2484 buf->modes_used = 1;
2485 buf->args_used = 0;
2486 }
2487 buf->modes[buf->modes_used++] = ch;
2488 buf->args[buf->args_used++] = ' ';
2489 memcpy(buf->args + buf->args_used, arg, arg_len);
2490 buf->args_used += arg_len;
2491}
2492
2493void
2494mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
2495{
2496 struct chanmode_buffer chbuf;
2497 unsigned int arg;
2498 struct modeNode *mn;
2499 char int_buff[32], mode = '\0';
2500
2501 assert(change->argc <= change->alloc_argc);
2502 memset(&chbuf, 0, sizeof(chbuf));
2503 chbuf.channel = channel;
2504 chbuf.actor = who;
2505 chbuf.chname_len = strlen(channel->name);
2506
2507 mn = GetUserMode(channel, who);
2508 if ((mn && (mn->modes & MODE_CHANOP)) || off_channel)
2509 chbuf.is_chanop = 1;
2510
2511 /* First remove modes */
2512 chbuf.is_add = 0;
2513 if (change->modes_clear) {
2514 if (mode != '-')
2515 chbuf.modes[chbuf.modes_used++] = mode = '-';
2516#define DO_MODE_CHAR(BIT, CHAR) if (change->modes_clear & MODE_##BIT) chbuf.modes[chbuf.modes_used++] = CHAR
2517 DO_MODE_CHAR(PRIVATE, 'p');
2518 DO_MODE_CHAR(SECRET, 's');
2519 DO_MODE_CHAR(MODERATED, 'm');
2520 DO_MODE_CHAR(TOPICLIMIT, 't');
2521 DO_MODE_CHAR(INVITEONLY, 'i');
2522 DO_MODE_CHAR(NOPRIVMSGS, 'n');
2523 DO_MODE_CHAR(LIMIT, 'l');
2524 DO_MODE_CHAR(DELAYJOINS, 'D');
2525 DO_MODE_CHAR(REGONLY, 'r');
2526 DO_MODE_CHAR(NOCOLORS, 'c');
2527 DO_MODE_CHAR(NOCTCPS, 'C');
d0cb2fb6 2528 DO_MODE_CHAR(STRIPCOLOR, 'S');
2529 DO_MODE_CHAR(MODUNREG, 'M');
2530 DO_MODE_CHAR(NONOTICE, 'N');
2531 DO_MODE_CHAR(NOQUITMSGS, 'Q');
2532 DO_MODE_CHAR(NOAMSG, 'T');
2533 DO_MODE_CHAR(OPERSONLY, 'O');
ec1a68c8 2534 DO_MODE_CHAR(REGISTERED, 'z');
c8ca69a0 2535 DO_MODE_CHAR(SSLONLY, 'Z');
88c7cb10 2536 DO_MODE_CHAR(HIDEMODE, 'L');
d76ed9a9 2537#undef DO_MODE_CHAR
2538 if (change->modes_clear & channel->modes & MODE_KEY)
2539 mod_chanmode_append(&chbuf, 'k', channel->key);
2540 }
2541 for (arg = 0; arg < change->argc; ++arg) {
2542 if (!(change->args[arg].mode & MODE_REMOVE))
2543 continue;
2544 if (mode != '-')
2545 chbuf.modes[chbuf.modes_used++] = mode = '-';
2546 switch (change->args[arg].mode & ~MODE_REMOVE) {
2547 case MODE_BAN:
a32da4c7 2548 mod_chanmode_append(&chbuf, 'b', change->args[arg].u.hostmask);
d76ed9a9 2549 break;
2aef5f4b 2550 case MODE_EXEMPT:
a32da4c7 2551 mod_chanmode_append(&chbuf, 'e', change->args[arg].u.hostmask);
2aef5f4b 2552 break;
d76ed9a9 2553 default:
2554 if (change->args[arg].mode & MODE_CHANOP)
a32da4c7 2555 mod_chanmode_append(&chbuf, 'o', change->args[arg].u.member->user->numeric);
55342ce8 2556 if (change->args[arg].mode & MODE_HALFOP)
a32da4c7 2557 mod_chanmode_append(&chbuf, 'h', change->args[arg].u.member->user->numeric);
d76ed9a9 2558 if (change->args[arg].mode & MODE_VOICE)
a32da4c7 2559 mod_chanmode_append(&chbuf, 'v', change->args[arg].u.member->user->numeric);
d76ed9a9 2560 break;
2561 }
2562 }
2563
2564 /* Then set them */
2565 chbuf.is_add = 1;
2566 if (change->modes_set) {
2567 if (mode != '+')
2568 chbuf.modes[chbuf.modes_used++] = mode = '+';
2569#define DO_MODE_CHAR(BIT, CHAR) if (change->modes_set & MODE_##BIT) chbuf.modes[chbuf.modes_used++] = CHAR
2570 DO_MODE_CHAR(PRIVATE, 'p');
2571 DO_MODE_CHAR(SECRET, 's');
2572 DO_MODE_CHAR(MODERATED, 'm');
2573 DO_MODE_CHAR(TOPICLIMIT, 't');
2574 DO_MODE_CHAR(INVITEONLY, 'i');
2575 DO_MODE_CHAR(NOPRIVMSGS, 'n');
2576 DO_MODE_CHAR(DELAYJOINS, 'D');
2577 DO_MODE_CHAR(REGONLY, 'r');
2578 DO_MODE_CHAR(NOCOLORS, 'c');
2579 DO_MODE_CHAR(NOCTCPS, 'C');
d0cb2fb6 2580 DO_MODE_CHAR(STRIPCOLOR, 'S');
2581 DO_MODE_CHAR(MODUNREG, 'M');
2582 DO_MODE_CHAR(NONOTICE, 'N');
2583 DO_MODE_CHAR(NOQUITMSGS, 'Q');
2584 DO_MODE_CHAR(NOAMSG, 'T');
2585 DO_MODE_CHAR(OPERSONLY, 'O');
ec1a68c8 2586 DO_MODE_CHAR(REGISTERED, 'z');
c8ca69a0 2587 DO_MODE_CHAR(SSLONLY, 'Z');
88c7cb10 2588 DO_MODE_CHAR(HIDEMODE, 'L');
d76ed9a9 2589#undef DO_MODE_CHAR
2590 if(change->modes_set & MODE_KEY)
2591 mod_chanmode_append(&chbuf, 'k', change->new_key);
2592 if(change->modes_set & MODE_LIMIT) {
2593 sprintf(int_buff, "%d", change->new_limit);
2594 mod_chanmode_append(&chbuf, 'l', int_buff);
2595 }
2596 }
2597 for (arg = 0; arg < change->argc; ++arg) {
2598 if (change->args[arg].mode & MODE_REMOVE)
2599 continue;
2600 if (mode != '+')
2601 chbuf.modes[chbuf.modes_used++] = mode = '+';
2602 switch (change->args[arg].mode) {
2603 case MODE_BAN:
a32da4c7 2604 mod_chanmode_append(&chbuf, 'b', change->args[arg].u.hostmask);
d76ed9a9 2605 break;
2aef5f4b 2606 case MODE_EXEMPT:
a32da4c7 2607 mod_chanmode_append(&chbuf, 'e', change->args[arg].u.hostmask);
2aef5f4b 2608 break;
d76ed9a9 2609 default:
2610 if (change->args[arg].mode & MODE_CHANOP)
a32da4c7 2611 mod_chanmode_append(&chbuf, 'o', change->args[arg].u.member->user->numeric);
55342ce8 2612 if (change->args[arg].mode & MODE_HALFOP)
a32da4c7 2613 mod_chanmode_append(&chbuf, 'h', change->args[arg].u.member->user->numeric);
d76ed9a9 2614 if (change->args[arg].mode & MODE_VOICE)
a32da4c7 2615 mod_chanmode_append(&chbuf, 'v', change->args[arg].u.member->user->numeric);
d76ed9a9 2616 break;
2617 }
2618 }
2619
2620 /* Flush the buffer and apply changes locally */
2621 if (chbuf.modes_used > 0) {
2622 memcpy(chbuf.modes + chbuf.modes_used, chbuf.args, chbuf.args_used);
2623 chbuf.modes[chbuf.modes_used + chbuf.args_used] = '\0';
2624 irc_mode((chbuf.is_chanop ? chbuf.actor : NULL), chbuf.channel, chbuf.modes);
2625 }
2626 mod_chanmode_apply(who, channel, change);
2627}
2628
2629char *
2630mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
2631{
2632 unsigned int used = 0;
2633 assert(change->argc <= change->alloc_argc);
2634 if (change->modes_clear) {
2635 outbuff[used++] = '-';
2636#define DO_MODE_CHAR(BIT, CHAR) if (change->modes_clear & MODE_##BIT) outbuff[used++] = CHAR
2637 DO_MODE_CHAR(PRIVATE, 'p');
2638 DO_MODE_CHAR(SECRET, 's');
2639 DO_MODE_CHAR(MODERATED, 'm');
2640 DO_MODE_CHAR(TOPICLIMIT, 't');
2641 DO_MODE_CHAR(INVITEONLY, 'i');
2642 DO_MODE_CHAR(NOPRIVMSGS, 'n');
2643 DO_MODE_CHAR(LIMIT, 'l');
2644 DO_MODE_CHAR(KEY, 'k');
2645 DO_MODE_CHAR(DELAYJOINS, 'D');
2646 DO_MODE_CHAR(REGONLY, 'r');
2647 DO_MODE_CHAR(NOCOLORS, 'c');
2648 DO_MODE_CHAR(NOCTCPS, 'C');
d0cb2fb6 2649 DO_MODE_CHAR(STRIPCOLOR, 'S');
2650 DO_MODE_CHAR(MODUNREG, 'M');
2651 DO_MODE_CHAR(NONOTICE, 'N');
2652 DO_MODE_CHAR(NOQUITMSGS, 'Q');
2653 DO_MODE_CHAR(NOAMSG, 'T');
2654 DO_MODE_CHAR(OPERSONLY, 'O');
ec1a68c8 2655 DO_MODE_CHAR(REGISTERED, 'z');
c8ca69a0 2656 DO_MODE_CHAR(SSLONLY, 'Z');
88c7cb10 2657 DO_MODE_CHAR(HIDEMODE, 'L');
d76ed9a9 2658#undef DO_MODE_CHAR
2659 }
2660 if (change->modes_set) {
2661 outbuff[used++] = '+';
2662#define DO_MODE_CHAR(BIT, CHAR) if (change->modes_set & MODE_##BIT) outbuff[used++] = CHAR
2663 DO_MODE_CHAR(PRIVATE, 'p');
2664 DO_MODE_CHAR(SECRET, 's');
2665 DO_MODE_CHAR(MODERATED, 'm');
2666 DO_MODE_CHAR(TOPICLIMIT, 't');
2667 DO_MODE_CHAR(INVITEONLY, 'i');
2668 DO_MODE_CHAR(NOPRIVMSGS, 'n');
2669 DO_MODE_CHAR(DELAYJOINS, 'D');
2670 DO_MODE_CHAR(REGONLY, 'r');
2671 DO_MODE_CHAR(NOCOLORS, 'c');
2672 DO_MODE_CHAR(NOCTCPS, 'C');
d0cb2fb6 2673 DO_MODE_CHAR(STRIPCOLOR, 'S');
2674 DO_MODE_CHAR(MODUNREG, 'M');
2675 DO_MODE_CHAR(NONOTICE, 'N');
2676 DO_MODE_CHAR(NOQUITMSGS, 'Q');
2677 DO_MODE_CHAR(NOAMSG, 'T');
2678 DO_MODE_CHAR(OPERSONLY, 'O');
ec1a68c8 2679 DO_MODE_CHAR(REGISTERED, 'z');
c8ca69a0 2680 DO_MODE_CHAR(SSLONLY, 'Z');
88c7cb10 2681 DO_MODE_CHAR(HIDEMODE, 'L');
d76ed9a9 2682#undef DO_MODE_CHAR
2683 switch (change->modes_set & (MODE_KEY|MODE_LIMIT)) {
2684 case MODE_KEY|MODE_LIMIT:
2685 used += sprintf(outbuff+used, "lk %d %s", change->new_limit, change->new_key);
2686 break;
2687 case MODE_KEY:
2688 used += sprintf(outbuff+used, "k %s", change->new_key);
2689 break;
2690 case MODE_LIMIT:
2691 used += sprintf(outbuff+used, "l %d", change->new_limit);
2692 break;
2693 }
2694 }
2695 outbuff[used] = 0;
2696 return outbuff;
2697}
2698
2699static int
2700clear_chanmode(struct chanNode *channel, const char *modes)
2701{
2702 unsigned int remove;
2703
2704 for (remove = 0; *modes; modes++) {
2705 switch (*modes) {
2706 case 'o': remove |= MODE_CHANOP; break;
55342ce8 2707 case 'h': remove |= MODE_HALFOP; break;
d76ed9a9 2708 case 'v': remove |= MODE_VOICE; break;
2709 case 'p': remove |= MODE_PRIVATE; break;
2710 case 's': remove |= MODE_SECRET; break;
2711 case 'm': remove |= MODE_MODERATED; break;
2712 case 't': remove |= MODE_TOPICLIMIT; break;
2713 case 'i': remove |= MODE_INVITEONLY; break;
2714 case 'n': remove |= MODE_NOPRIVMSGS; break;
2715 case 'k':
2716 remove |= MODE_KEY;
2717 channel->key[0] = '\0';
2718 break;
2719 case 'l':
2720 remove |= MODE_LIMIT;
2721 channel->limit = 0;
2722 break;
2723 case 'b': remove |= MODE_BAN; break;
2aef5f4b 2724 case 'e': remove |= MODE_EXEMPT; break;
d76ed9a9 2725 case 'D': remove |= MODE_DELAYJOINS; break;
2726 case 'r': remove |= MODE_REGONLY; break;
850bf0cf 2727 case 'c': remove |= MODE_NOCOLORS; break;
d76ed9a9 2728 case 'C': remove |= MODE_NOCTCPS; break;
d0cb2fb6 2729 case 'S': remove |= MODE_STRIPCOLOR; break;
2730 case 'M': remove |= MODE_MODUNREG; break;
2731 case 'N': remove |= MODE_NONOTICE; break;
2732 case 'Q': remove |= MODE_NOQUITMSGS; break;
2733 case 'T': remove |= MODE_NOAMSG; break;
2734 case 'O': remove |= MODE_OPERSONLY; break;
ec1a68c8 2735 case 'z': remove |= MODE_REGISTERED; break;
c8ca69a0 2736 case 'Z': remove |= MODE_SSLONLY; break;
88c7cb10 2737 case 'L': remove |= MODE_HIDEMODE; break;
d76ed9a9 2738 }
2739 }
2740
2741 if (!remove)
2742 return 1;
2743
2744 /* Remove simple modes. */
2745 channel->modes &= ~remove;
2746
2747 /* If removing bans, kill 'em all. */
2748 if ((remove & MODE_BAN) && channel->banlist.used) {
2749 unsigned int i;
2750 for (i=0; i<channel->banlist.used; i++)
2751 free(channel->banlist.list[i]);
2752 channel->banlist.used = 0;
2753 }
2754
2aef5f4b 2755 /* If removing exempts, kill 'em all. */
2756 if ((remove & MODE_EXEMPT) && channel->exemptlist.used) {
2757 unsigned int i;
2758 for (i=0; i<channel->exemptlist.used; i++)
2759 free(channel->exemptlist.list[i]);
2760 channel->exemptlist.used = 0;
2761 }
2762
d76ed9a9 2763 /* Remove member modes. */
55342ce8 2764 if ((remove & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE)) && channel->members.used) {
2765 int mask = ~(remove & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE));
d76ed9a9 2766 unsigned int i;
2767
2768 for (i = 0; i < channel->members.used; i++)
2769 channel->members.list[i]->modes &= mask;
2770 }
2771
2772 return 1;
2773}
2774
2775void
2776reg_privmsg_func(struct userNode *user, privmsg_func_t handler)
2777{
2778 unsigned int numeric = user->num_local;
2779 if (numeric >= num_privmsg_funcs) {
2780 int newnum = numeric + 8;
2781 privmsg_funcs = realloc(privmsg_funcs, newnum*sizeof(privmsg_func_t));
2782 memset(privmsg_funcs+num_privmsg_funcs, 0, (newnum-num_privmsg_funcs)*sizeof(privmsg_func_t));
2783 num_privmsg_funcs = newnum;
2784 }
2785 if (privmsg_funcs[numeric])
2786 log_module(MAIN_LOG, LOG_WARNING, "re-registering new privmsg handler for numeric %d", numeric);
2787 privmsg_funcs[numeric] = handler;
2788}
2789
2790void
2791unreg_privmsg_func(struct userNode *user, privmsg_func_t handler)
2792{
2793 unsigned int x;
2794
2795 if (!user || handler)
2796 return; /* this really only works with users */
2797
2798 memset(privmsg_funcs+user->num_local, 0, sizeof(privmsg_func_t));
2799
2800 for (x = user->num_local+1; x < num_privmsg_funcs; x++)
2801 memmove(privmsg_funcs+x-1, privmsg_funcs+x, sizeof(privmsg_func_t));
2802
2803 privmsg_funcs = realloc(privmsg_funcs, num_privmsg_funcs*sizeof(privmsg_func_t));
2804 num_privmsg_funcs--;
2805}
2806
2807
2808void
2809reg_notice_func(struct userNode *user, privmsg_func_t handler)
2810{
2811 unsigned int numeric = user->num_local;
2812 if (numeric >= num_notice_funcs) {
2813 int newnum = numeric + 8;
2814 notice_funcs = realloc(notice_funcs, newnum*sizeof(privmsg_func_t));
2815 memset(notice_funcs+num_notice_funcs, 0, (newnum-num_notice_funcs)*sizeof(privmsg_func_t));
2816 num_notice_funcs = newnum;
2817 }
2818 if (notice_funcs[numeric])
2819 log_module(MAIN_LOG, LOG_WARNING, "re-registering new notice handler for numeric %d", numeric);
2820 notice_funcs[numeric] = handler;
2821}
2822
2823void
2824unreg_notice_func(struct userNode *user, privmsg_func_t handler)
2825{
2826 unsigned int x;
2827
2828 if (!user || handler)
2829 return; /* this really only works with users */
2830
2831 memset(notice_funcs+user->num_local, 0, sizeof(privmsg_func_t));
2832
2833 for (x = user->num_local+1; x < num_notice_funcs; x++)
2834 memmove(notice_funcs+x-1, notice_funcs+x, sizeof(privmsg_func_t));
2835
2836 memset(notice_funcs+user->num_local, 0, sizeof(privmsg_func_t));
2837 notice_funcs = realloc(notice_funcs, num_notice_funcs*sizeof(privmsg_func_t));
2838 num_notice_funcs--;
2839}
2840
2841void
2842reg_oper_func(oper_func_t handler)
2843{
2844 if (of_used == of_size) {
2845 if (of_size) {
2846 of_size <<= 1;
2847 of_list = realloc(of_list, of_size*sizeof(oper_func_t));
2848 } else {
2849 of_size = 8;
2850 of_list = malloc(of_size*sizeof(oper_func_t));
2851 }
2852 }
2853 of_list[of_used++] = handler;
2854}
2855
2856static void
2857call_oper_funcs(struct userNode *user)
2858{
2859 unsigned int n;
2860 if (IsLocal(user))
2861 return;
2862 for (n=0; n<of_used; n++)
2863 of_list[n](user);
2864}
2865
2866static void
2867send_burst(void)
2868{
2869 unsigned int i, hop, max_hop=1;
2870 dict_iterator_t it;
2871
2872 /* burst (juped) servers, closest first (except self, which is sent already) */
2873 for (i=0; i<ArrayLength(servers_num); i++)
2874 if (servers_num[i] && servers_num[i]->hops > max_hop)
2875 max_hop = servers_num[i]->hops;
2876 for (hop=1; hop<=max_hop; hop++) {
2877 for (i=0;i<ArrayLength(servers_num);i++) {
2878 if (servers_num[i]
2879 && (servers_num[i]->hops == hop)
2880 && (servers_num[i] != self->uplink))
2881 irc_server(servers_num[i]);
2882 }
2883 }
2884
2885 /* burst local nicks */
2886 for (i=0; i<=self->num_mask; i++)
2887 if (self->users[i])
2888 irc_user(self->users[i]);
2889
2890 /* build dict of unbursted channel names (just copy existing channels) */
2891 unbursted_channels = dict_new();
2892 for (it = dict_first(channels); it; it = iter_next(it))
2893 dict_insert(unbursted_channels, iter_key(it), iter_data(it));
2894}