1 /*
2 * server.c -- part of server.mod
3 * basic irc server support
4 */
5 /*
6 * Copyright (C) 1997 Robey Pointer
7 * Copyright (C) 1999 - 2021 Eggheads Development Team
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23
24 #define MODULE_NAME "server"
25 #define MAKING_SERVER
26
27 #include <errno.h>
28 #include "src/mod/module.h"
29 #include "server.h"
30
31 static Function *global = NULL;
32
33 static int ctcp_mode;
34 static int serv; /* sock # of server currently */
35 static char newserver[121]; /* new server? */
36 static int newserverport; /* new server port? */
37 static char newserverpass[121]; /* new server password? */
38 static time_t trying_server; /* trying to connect to a server right now? */
39 static int server_lag; /* how lagged (in seconds) is the server? */
40 static char altnick[NICKLEN]; /* possible alternate nickname to use */
41 static char raltnick[NICKLEN]; /* random nick created from altnick */
42 static int curserv; /* current position in server list: */
43 static int flud_thr; /* msg flood threshold */
44 static int flud_time; /* msg flood time */
45 static int flud_ctcp_thr; /* ctcp flood threshold */
46 static int flud_ctcp_time; /* ctcp flood time */
47 static char initserver[121]; /* what, if anything, to send to the
48 * server on connection */
49 static char botuserhost[121]; /* bot's user@host (refreshed whenever the
50 * bot joins a channel) */
51 /* may not be correct user@host BUT it's
52 * how the server sees it */
53 static int keepnick; /* keep trying to regain my intended
54 * nickname? */
55 static int nick_juped = 0; /* True if origbotname is juped(RPL437) (dw) */
56 static int check_stoned; /* Check for a stoned server? */
57 static int serverror_quit; /* Disconnect from server if ERROR
58 * messages received? */
59 static time_t lastpingcheck; /* set when i unidle myself, cleared when
60 * i get the response */
61 static time_t server_online; /* server connection time */
62 static time_t server_cycle_wait; /* seconds to wait before
63 * re-beginning the server list */
64 static char botrealname[81]; /* realname of bot */
65 static int server_timeout; /* server timeout for connecting */
66 static struct server_list *serverlist; /* old-style queue, still used by
67 * server list */
68 static int cycle_time; /* cycle time till next server connect */
69 static int default_port; /* default IRC port */
70 static char oldnick[NICKLEN]; /* previous nickname *before* rehash */
71 static int trigger_on_ignore; /* trigger bindings if user is ignored ? */
72 static int exclusive_binds; /* configures PUBM and MSGM binds to be
73 * exclusive of PUB and MSG binds. */
74 static int answer_ctcp; /* answer how many stacked ctcp's ? */
75 static int lowercase_ctcp; /* answer lowercase CTCP's (non-standard) */
76 static int check_mode_r; /* check for IRCnet +r modes */
77 static char net_type[9];
78 static int net_type_int;
79 static char connectserver[121]; /* what, if anything, to do before connect
80 * to the server */
81 static int resolvserv; /* in the process of resolving a server host */
82 static int double_mode; /* allow a msgs to be twice in a queue? */
83 static int double_server;
84 static int double_help;
85 static int double_warned;
86 static int lastpingtime; /* IRCnet LAGmeter support -- drummer */
87 static char stackablecmds[MSGMAX];
88 static char stackable2cmds[MSGMAX];
89 static time_t last_time;
90 static int use_penalties;
91 static int use_fastdeq;
92 static int nick_len; /* Maximal nick length allowed on the
93 * network. */
94 static int kick_method;
95 static int optimize_kicks;
96 static int msgrate; /* Number of seconds between sending
97 * queued lines to server. */
98 static int msgtag; /* Enable IRCv3 message-tags capability */
99 #ifdef TLS
100 static int use_ssl; /* Use SSL for the next server connection? */
101 static int tls_vfyserver; /* Certificate validation mode for servrs */
102 #endif
103
104 #ifndef TLS
105 static char sslserver = 0;
106 #endif
107
108 static p_tcl_bind_list H_wall, H_raw, H_notc, H_msgm, H_msg, H_flud, H_ctcr,
109 H_ctcp, H_out, H_rawt, H_account;
110
111 static void empty_msgq(void);
112 static void next_server(int *, char *, unsigned int *, char *);
113 static void disconnect_server(int);
114 static char *get_altbotnick(void);
115 static int calc_penalty(char *);
116 static int fast_deq(int);
117 static char *splitnicks(char **);
118 static void check_queues(char *, char *);
119 static void parse_q(struct msgq_head *, char *, char *);
120 static void purge_kicks(struct msgq_head *);
121 static int deq_kick(int);
122 static void msgq_clear(struct msgq_head *qh);
123 static int stack_limit;
124 static char *realservername;
125 static int add_server(const char *, const char *, const char *);
126 static int del_server(const char *, const char *);
127 static void free_server(struct server_list *);
128
129 static int sasl = 0;
130 static int away_notify = 0;
131 static int invite_notify = 0;
132 static int message_tags = 0;
133
134 static char cap_request[CAPMAX - 9];
135 static int sasl_mechanism = 0;
136 static char sasl_username[NICKMAX + 1];
137 static char sasl_password[81];
138 static int sasl_continue = 1;
139 static char sasl_ecdsa_key[121];
140 static int sasl_timeout = 15;
141 static int sasl_timeout_time = 0;
142
143 #include "isupport.c"
144 #include "tclisupport.c"
145 #include "servmsg.c"
146
147 #define MAXPENALTY 10
148
149 /* Maximum messages to store in each queue. */
150 static int maxqmsg;
151 static struct msgq_head mq, hq, modeq;
152 static int burst;
153
154 #include "cmdsserv.c"
155 #include "tclserv.c"
156
157 /* Available sasl mechanisms. */
158 char const *SASL_MECHANISMS[SASL_MECHANISM_NUM] = {
159 [SASL_MECHANISM_PLAIN] = "PLAIN",
160 [SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE] = "ECDSA-NIST256P-CHALLENGE",
161 [SASL_MECHANISM_EXTERNAL] = "EXTERNAL"
162 };
163
write_to_server(char * s,unsigned int len)164 static void write_to_server(char *s, unsigned int len) {
165 char *s2 = nmalloc(len + 2);
166
167 memcpy(s2, s, len);
168 s2[len] = '\r';
169 s2[len + 1] = '\n';
170 tputs(serv, s2, len + 2);
171 nfree(s2);
172 }
173
174 /*
175 * Bot server queues
176 */
177
178 /* Called periodically to shove out another queued item.
179 *
180 * 'mode' queue gets priority now.
181 *
182 * Most servers will allow 'bursts' of up to 5 msgs, so let's put something
183 * in to support flushing modeq a little faster if possible.
184 * Will send up to 4 msgs from modeq, and then send 1 msg every time
185 * it will *not* send anything from hq until the 'burst' value drops
186 * down to 0 again (allowing a sudden mq flood to sneak through).
187 */
deq_msg()188 static void deq_msg()
189 {
190 struct msgq *q;
191 int ok = 0;
192
193 /* now < last_time tested 'cause clock adjustments could mess it up */
194 if ((now - last_time) >= msgrate || now < (last_time - 90)) {
195 last_time = now;
196 if (burst > 0)
197 burst--;
198 ok = 1;
199 }
200
201 if (serv < 0)
202 return;
203
204 /* Send up to 4 msgs to server if the *critical queue* has anything in it */
205 if (modeq.head) {
206 while (modeq.head && (burst < 4) && ((last_time - now) < MAXPENALTY)) {
207 if (deq_kick(DP_MODE)) {
208 burst++;
209 continue;
210 }
211 if (!modeq.head)
212 break;
213 if (fast_deq(DP_MODE)) {
214 burst++;
215 continue;
216 }
217 check_tcl_out(DP_MODE, modeq.head->msg, 1);
218 write_to_server(modeq.head->msg, modeq.head->len);
219 if (raw_log)
220 putlog(LOG_SRVOUT, "*", "[m->] %s", modeq.head->msg);
221 modeq.tot--;
222 last_time += calc_penalty(modeq.head->msg);
223 q = modeq.head->next;
224 nfree(modeq.head->msg);
225 nfree(modeq.head);
226 modeq.head = q;
227 burst++;
228 }
229 if (!modeq.head)
230 modeq.last = 0;
231 return;
232 }
233
234 /* Send something from the normal msg q even if we're slightly bursting */
235 if (burst > 1)
236 return;
237
238 if (mq.head) {
239 burst++;
240
241 if (deq_kick(DP_SERVER))
242 return;
243
244 if (fast_deq(DP_SERVER))
245 return;
246
247 check_tcl_out(DP_SERVER, mq.head->msg, 1);
248 write_to_server(mq.head->msg, mq.head->len);
249 if (raw_log)
250 putlog(LOG_SRVOUT, "*", "[s->] %s", mq.head->msg);
251 mq.tot--;
252 last_time += calc_penalty(mq.head->msg);
253 q = mq.head->next;
254 nfree(mq.head->msg);
255 nfree(mq.head);
256 mq.head = q;
257 if (!mq.head)
258 mq.last = NULL;
259 return;
260 }
261
262 /* Never send anything from the help queue unless everything else is
263 * finished.
264 */
265 if (!hq.head || burst || !ok)
266 return;
267
268 if (deq_kick(DP_HELP))
269 return;
270
271 if (fast_deq(DP_HELP))
272 return;
273
274 check_tcl_out(DP_HELP, hq.head->msg, 1);
275 write_to_server(hq.head->msg, hq.head->len);
276 if (raw_log)
277 putlog(LOG_SRVOUT, "*", "[h->] %s", hq.head->msg);
278 hq.tot--;
279 last_time += calc_penalty(hq.head->msg);
280 q = hq.head->next;
281 nfree(hq.head->msg);
282 nfree(hq.head);
283 hq.head = q;
284 if (!hq.head)
285 hq.last = NULL;
286 }
287
calc_penalty(char * msg)288 static int calc_penalty(char *msg)
289 {
290 char *cmd, *par1, *par2, *par3;
291 int penalty, i, ii;
292
293 if (!use_penalties && net_type_int != NETT_UNDERNET &&
294 net_type_int != NETT_QUAKENET && net_type_int != NETT_HYBRID_EFNET)
295 return 0;
296
297 cmd = newsplit(&msg);
298 if (msg)
299 i = strlen(msg);
300 else
301 i = strlen(cmd);
302 last_time -= 2; /* undo eggdrop standard flood prot */
303 if (net_type_int == NETT_UNDERNET || net_type_int == NETT_QUAKENET ||
304 net_type_int == NETT_HYBRID_EFNET) {
305 last_time += (2 + i / 120);
306 return 0;
307 }
308 penalty = (1 + i / 100);
309 if (!strcasecmp(cmd, "KICK")) {
310 par1 = newsplit(&msg); /* channel */
311 par2 = newsplit(&msg); /* victim(s) */
312 par3 = splitnicks(&par2);
313 penalty++;
314 while (strlen(par3) > 0) {
315 par3 = splitnicks(&par2);
316 penalty++;
317 }
318 ii = penalty;
319 par3 = splitnicks(&par1);
320 while (strlen(par1) > 0) {
321 par3 = splitnicks(&par1);
322 penalty += ii;
323 }
324 } else if (!strcasecmp(cmd, "MODE")) {
325 i = 0;
326 par1 = newsplit(&msg); /* channel */
327 par2 = newsplit(&msg); /* mode(s) */
328 if (!strlen(par2))
329 i++;
330 while (strlen(par2) > 0) {
331 if (strchr("ntimps", par2[0]))
332 i += 3;
333 else if (!strchr("+-", par2[0]))
334 i += 1;
335 par2++;
336 }
337 while (strlen(msg) > 0) {
338 newsplit(&msg);
339 i += 2;
340 }
341 ii = 0;
342 while (strlen(par1) > 0) {
343 splitnicks(&par1);
344 ii++;
345 }
346 penalty += (ii * i);
347 } else if (!strcasecmp(cmd, "TOPIC")) {
348 penalty++;
349 par1 = newsplit(&msg); /* channel */
350 par2 = newsplit(&msg); /* topic */
351 if (strlen(par2) > 0) { /* topic manipulation => 2 penalty points */
352 penalty += 2;
353 par3 = splitnicks(&par1);
354 while (strlen(par1) > 0) {
355 par3 = splitnicks(&par1);
356 penalty += 2;
357 }
358 }
359 } else if (!strcasecmp(cmd, "PRIVMSG") ||
360 !strcasecmp(cmd, "NOTICE")) {
361 par1 = newsplit(&msg); /* channel(s)/nick(s) */
362 /* Add one sec penalty for each recipient */
363 while (strlen(par1) > 0) {
364 splitnicks(&par1);
365 penalty++;
366 }
367 } else if (!strcasecmp(cmd, "WHO")) {
368 par1 = newsplit(&msg); /* masks */
369 par2 = par1;
370 while (strlen(par1) > 0) {
371 par2 = splitnicks(&par1);
372 if (strlen(par2) > 4) /* long WHO-masks receive less penalty */
373 penalty += 3;
374 else
375 penalty += 5;
376 }
377 } else if (!strcasecmp(cmd, "AWAY")) {
378 if (strlen(msg) > 0)
379 penalty += 2;
380 else
381 penalty += 1;
382 } else if (!strcasecmp(cmd, "INVITE")) {
383 /* Successful invite receives 2 or 3 penalty points. Let's go
384 * with the maximum.
385 */
386 penalty += 3;
387 } else if (!strcasecmp(cmd, "JOIN")) {
388 penalty += 2;
389 } else if (!strcasecmp(cmd, "PART")) {
390 penalty += 4;
391 } else if (!strcasecmp(cmd, "VERSION")) {
392 penalty += 2;
393 } else if (!strcasecmp(cmd, "TIME")) {
394 penalty += 2;
395 } else if (!strcasecmp(cmd, "TRACE")) {
396 penalty += 2;
397 } else if (!strcasecmp(cmd, "NICK")) {
398 penalty += 3;
399 } else if (!strcasecmp(cmd, "ISON")) {
400 penalty += 1;
401 } else if (!strcasecmp(cmd, "WHOIS")) {
402 penalty += 2;
403 } else if (!strcasecmp(cmd, "DNS")) {
404 penalty += 2;
405 } else
406 penalty++; /* just add standard-penalty */
407 /* Shouldn't happen, but you never know... */
408 if (penalty > 99)
409 penalty = 99;
410 if (penalty < 2) {
411 putlog(LOG_SRVOUT, "*", "Penalty < 2sec; that's impossible!");
412 penalty = 2;
413 }
414 if (raw_log && penalty != 0)
415 putlog(LOG_SRVOUT, "*", "Adding penalty: %i", penalty);
416 return penalty;
417 }
418
splitnicks(char ** rest)419 static char *splitnicks(char **rest)
420 {
421 char *o, *r;
422
423 if (!rest)
424 return *rest = "";
425 o = *rest;
426 while (*o == ' ')
427 o++;
428 r = o;
429 while (*o && *o != ',')
430 o++;
431 if (*o)
432 *o++ = 0;
433 *rest = o;
434 return r;
435 }
436
fast_deq(int which)437 static int fast_deq(int which)
438 {
439 struct msgq_head *h;
440 struct msgq *m, *nm;
441 char msgstr[SENDLINEMAX], nextmsgstr[SENDLINEMAX], tosend[SENDLINEMAX],
442 victims[SENDLINEMAX], stackable[SENDLINEMAX], *msg, *nextmsg, *cmd,
443 *nextcmd, *to, *nextto, *stckbl;
444 int len, doit = 0, found = 0, cmd_count = 0, stack_method = 1;
445
446 if (!use_fastdeq)
447 return 0;
448
449 switch (which) {
450 case DP_MODE:
451 h = &modeq;
452 break;
453 case DP_SERVER:
454 h = &mq;
455 break;
456 case DP_HELP:
457 h = &hq;
458 break;
459 default:
460 return 0;
461 }
462
463 m = h->head;
464 strlcpy(msgstr, m->msg, sizeof msgstr);
465 msg = msgstr;
466 cmd = newsplit(&msg);
467 if (use_fastdeq > 1) {
468 strlcpy(stackable, stackablecmds, sizeof stackable);
469 stckbl = stackable;
470 while (strlen(stckbl) > 0) {
471 if (!strcasecmp(newsplit(&stckbl), cmd)) {
472 found = 1;
473 break;
474 }
475 }
476
477 /* If use_fastdeq is 2, only commands in the list should be stacked. */
478 if (use_fastdeq == 2 && !found)
479 return 0;
480
481 /* If use_fastdeq is 3, only commands _not_ in the list should be stacked. */
482 if (use_fastdeq == 3 && found)
483 return 0;
484
485 /* we check for the stacking method (default=1) */
486 strlcpy(stackable, stackable2cmds, sizeof stackable);
487 stckbl = stackable;
488 while (strlen(stckbl) > 0)
489 if (!strcasecmp(newsplit(&stckbl), cmd)) {
490 stack_method = 2;
491 break;
492 }
493 }
494 to = newsplit(&msg);
495 simple_sprintf(victims, "%s", to);
496 while (m) {
497 nm = m->next;
498 if (!nm)
499 break;
500 strlcpy(nextmsgstr, nm->msg, sizeof nextmsgstr);
501 nextmsg = nextmsgstr;
502 nextcmd = newsplit(&nextmsg);
503 nextto = newsplit(&nextmsg);
504 if (strcmp(to, nextto) && !strcmp(cmd, nextcmd) && !strcmp(msg, nextmsg) &&
505 ((strlen(cmd) + strlen(victims) + strlen(nextto) + strlen(msg) + 2) <
506 SENDLINEMAX-2) && (!stack_limit || cmd_count < stack_limit - 1)) {
507 cmd_count++;
508 if (stack_method == 1)
509 simple_sprintf(victims, "%s,%s", victims, nextto);
510 else
511 simple_sprintf(victims, "%s %s", victims, nextto);
512 doit = 1;
513 m->next = nm->next;
514 if (!nm->next)
515 h->last = m;
516 nfree(nm->msg);
517 nfree(nm);
518 h->tot--;
519 } else
520 m = m->next;
521 }
522 if (doit) {
523 simple_sprintf(tosend, "%s %s %s", cmd, victims, msg);
524 len = strlen(tosend);
525 check_tcl_out(which, tosend, 1);
526 write_to_server(tosend, len);
527 if (raw_log) {
528 switch (which) {
529 case DP_MODE:
530 putlog(LOG_SRVOUT, "*", "[m=>] %s", tosend);
531 break;
532 case DP_SERVER:
533 putlog(LOG_SRVOUT, "*", "[s=>] %s", tosend);
534 break;
535 case DP_HELP:
536 putlog(LOG_SRVOUT, "*", "[h=>] %s", tosend);
537 break;
538 }
539 }
540 m = h->head->next;
541 nfree(h->head->msg);
542 nfree(h->head);
543 h->head = m;
544 if (!h->head)
545 h->last = 0;
546 h->tot--;
547 last_time += calc_penalty(tosend);
548 return 1;
549 }
550 return 0;
551 }
552
check_queues(char * oldnick,char * newnick)553 static void check_queues(char *oldnick, char *newnick)
554 {
555 if (optimize_kicks != 2)
556 return;
557 if (modeq.head)
558 parse_q(&modeq, oldnick, newnick);
559 if (mq.head)
560 parse_q(&mq, oldnick, newnick);
561 if (hq.head)
562 parse_q(&hq, oldnick, newnick);
563 }
564
parse_q(struct msgq_head * q,char * oldnick,char * newnick)565 static void parse_q(struct msgq_head *q, char *oldnick, char *newnick)
566 {
567 struct msgq *m, *lm = NULL;
568 char buf[SENDLINEMAX], *msg, *nicks, *nick, *chan, newnicks[SENDLINEMAX], newmsg[SENDLINEMAX];
569 int changed;
570
571 for (m = q->head; m;) {
572 changed = 0;
573 if (optimize_kicks == 2 && !strncasecmp(m->msg, "KICK ", 5)) {
574 newnicks[0] = 0;
575 strlcpy(buf, m->msg, sizeof buf);
576 msg = buf;
577 newsplit(&msg);
578 chan = newsplit(&msg);
579 nicks = newsplit(&msg);
580 while (strlen(nicks) > 0) {
581 nick = splitnicks(&nicks);
582 if (!strcasecmp(nick, oldnick) &&
583 ((9 + strlen(chan) + strlen(newnicks) + strlen(newnick) +
584 strlen(nicks) + strlen(msg)) < SENDLINEMAX-1)) {
585 if (newnick)
586 egg_snprintf(newnicks, sizeof newnicks, "%s,%s", newnicks, newnick);
587 changed = 1;
588 } else
589 egg_snprintf(newnicks, sizeof newnicks, ",%s", nick);
590 }
591 egg_snprintf(newmsg, sizeof newmsg, "KICK %s %s %s", chan,
592 newnicks + 1, msg);
593 }
594 if (changed) {
595 if (newnicks[0] == 0) {
596 if (!lm)
597 q->head = m->next;
598 else
599 lm->next = m->next;
600 nfree(m->msg);
601 nfree(m);
602 m = lm;
603 q->tot--;
604 if (!q->head)
605 q->last = 0;
606 } else {
607 nfree(m->msg);
608 m->msg = nmalloc(strlen(newmsg) + 1);
609 m->len = strlen(newmsg);
610 strcpy(m->msg, newmsg);
611 }
612 }
613 lm = m;
614 if (m)
615 m = m->next;
616 else
617 m = q->head;
618 }
619 }
620
purge_kicks(struct msgq_head * q)621 static void purge_kicks(struct msgq_head *q)
622 {
623 struct msgq *m, *lm = NULL;
624 char buf[MSGMAX], *reason, *nicks, *nick, *chan, newnicks[MSGMAX],
625 newmsg[MSGMAX], chans[MSGMAX], *chns, *ch;
626 int changed, found;
627 struct chanset_t *cs;
628
629 for (m = q->head; m;) {
630 if (!strncasecmp(m->msg, "KICK", 4)) {
631 newnicks[0] = 0;
632 changed = 0;
633 strlcpy(buf, m->msg, sizeof buf);
634 reason = buf;
635 newsplit(&reason);
636 chan = newsplit(&reason);
637 nicks = newsplit(&reason);
638 while (strlen(nicks) > 0) {
639 found = 0;
640 nick = splitnicks(&nicks);
641 strlcpy(chans, chan, sizeof chans);
642 chns = chans;
643 while (strlen(chns) > 0) {
644 ch = newsplit(&chns);
645 cs = findchan(ch);
646 if (!cs)
647 continue;
648 if (ismember(cs, nick))
649 found = 1;
650 }
651 if (found)
652 egg_snprintf(newnicks, sizeof newnicks, "%s,%s", newnicks, nick);
653 else {
654 putlog(LOG_SRVOUT, "*", "%s isn't on any target channel; removing "
655 "kick.", nick);
656 changed = 1;
657 }
658 }
659 if (changed) {
660 if (newnicks[0] == 0) {
661 if (!lm)
662 q->head = m->next;
663 else
664 lm->next = m->next;
665 nfree(m->msg);
666 nfree(m);
667 m = lm;
668 q->tot--;
669 if (!q->head)
670 q->last = 0;
671 } else {
672 nfree(m->msg);
673 egg_snprintf(newmsg, sizeof newmsg, "KICK %s %s %s", chan,
674 newnicks + 1, reason);
675 m->msg = nmalloc(strlen(newmsg) + 1);
676 m->len = strlen(newmsg);
677 strcpy(m->msg, newmsg);
678 }
679 }
680 }
681 lm = m;
682 if (m)
683 m = m->next;
684 else
685 m = q->head;
686 }
687 }
688
deq_kick(int which)689 static int deq_kick(int which)
690 {
691 struct msgq_head *h;
692 struct msgq *msg, *m, *lm;
693 char buf[MSGMAX], buf2[MSGMAX], *reason2, *nicks, *chan, *chan2, *reason, *nick,
694 newnicks[MSGMAX], newnicks2[MSGMAX], newmsg[MSGMAX];
695 int changed = 0, nr = 0;
696
697 if (!optimize_kicks)
698 return 0;
699
700 newnicks[0] = 0;
701 switch (which) {
702 case DP_MODE:
703 h = &modeq;
704 break;
705 case DP_SERVER:
706 h = &mq;
707 break;
708 case DP_HELP:
709 h = &hq;
710 break;
711 default:
712 return 0;
713 }
714
715 if (strncasecmp(h->head->msg, "KICK", 4))
716 return 0;
717
718 if (optimize_kicks == 2) {
719 purge_kicks(h);
720 if (!h->head)
721 return 1;
722 }
723
724 if (strncasecmp(h->head->msg, "KICK", 4))
725 return 0;
726
727 msg = h->head;
728 strlcpy(buf, msg->msg, sizeof buf);
729 reason = buf;
730 newsplit(&reason);
731 chan = newsplit(&reason);
732 nicks = newsplit(&reason);
733 while (strlen(nicks) > 0) {
734 egg_snprintf(newnicks, sizeof newnicks, "%s,%s", newnicks,
735 newsplit(&nicks));
736 nr++;
737 }
738 for (m = msg->next, lm = NULL; m && (nr < kick_method);) {
739 if (!strncasecmp(m->msg, "KICK", 4)) {
740 changed = 0;
741 newnicks2[0] = 0;
742 strlcpy(buf2, m->msg, sizeof buf2);
743 reason2 = buf2;
744 newsplit(&reason2);
745 chan2 = newsplit(&reason2);
746 nicks = newsplit(&reason2);
747 if (!strcasecmp(chan, chan2) && !strcasecmp(reason, reason2)) {
748 while (strlen(nicks) > 0) {
749 nick = splitnicks(&nicks);
750 if ((nr < kick_method) && ((9 + strlen(chan) + strlen(newnicks) +
751 strlen(nick) + strlen(reason)) < 510)) {
752 egg_snprintf(newnicks, sizeof newnicks, "%s,%s", newnicks, nick);
753 nr++;
754 changed = 1;
755 } else
756 egg_snprintf(newnicks2, sizeof newnicks2, "%s,%s", newnicks2, nick);
757 }
758 }
759 if (changed) {
760 if (newnicks2[0] == 0) {
761 if (!lm)
762 h->head->next = m->next;
763 else
764 lm->next = m->next;
765 nfree(m->msg);
766 nfree(m);
767 m = lm;
768 h->tot--;
769 if (!h->head)
770 h->last = 0;
771 } else {
772 nfree(m->msg);
773 egg_snprintf(newmsg, sizeof newmsg, "KICK %s %s %s", chan2,
774 newnicks2 + 1, reason);
775 m->msg = nmalloc(strlen(newmsg) + 1);
776 m->len = strlen(newmsg);
777 strcpy(m->msg, newmsg);
778 }
779 }
780 }
781 lm = m;
782 if (m)
783 m = m->next;
784 else
785 m = h->head->next;
786 }
787 egg_snprintf(newmsg, sizeof newmsg, "KICK %s %s %s", chan, newnicks + 1,
788 reason);
789 check_tcl_out(which, newmsg, 1);
790 write_to_server(newmsg, strlen(newmsg));
791 if (raw_log) {
792 switch (which) {
793 case DP_MODE:
794 putlog(LOG_SRVOUT, "*", "[m->] %s", newmsg);
795 break;
796 case DP_SERVER:
797 putlog(LOG_SRVOUT, "*", "[s->] %s", newmsg);
798 break;
799 case DP_HELP:
800 putlog(LOG_SRVOUT, "*", "[h->] %s", newmsg);
801 break;
802 }
803 debug3("Changed: %d, kick-method: %d, nr: %d", changed, kick_method, nr);
804 }
805 h->tot--;
806 last_time += calc_penalty(newmsg);
807 m = h->head->next;
808 nfree(h->head->msg);
809 nfree(h->head);
810 h->head = m;
811 if (!h->head)
812 h->last = 0;
813 return 1;
814 }
815
816 /* Clean out the msg queues (like when changing servers).
817 */
empty_msgq()818 static void empty_msgq()
819 {
820 msgq_clear(&modeq);
821 msgq_clear(&mq);
822 msgq_clear(&hq);
823 burst = 0;
824 }
825
826 /* Queues outgoing messages so there's no flooding.
827 */
queue_server(int which,char * msg,int len)828 static void queue_server(int which, char *msg, int len)
829 {
830 struct msgq_head *h = NULL, tempq;
831 struct msgq *q, *tq, *tqq;
832 int doublemsg = 0, qnext = 0;
833 char buf[SENDLINEMAX];
834
835 /* Don't even BOTHER if there's no server online. */
836 if (serv < 0)
837 return;
838
839 /* Remove \r\n. We will add these back when we send the text to the server.
840 * - Wcc [01/09/2004]
841 */
842 strlcpy(buf, msg, sizeof buf);
843 msg = buf;
844 remove_crlf(&msg);
845 len = strlen(buf);
846
847 /* No queue for PING, PONG and AUTHENTICATE */
848 #define PING "PING"
849 #define PONG "PONG"
850 #define AUTHENTICATE "AUTHENTICATE"
851 if (!strncasecmp(buf, PING, sizeof PING - 1) ||
852 !strncasecmp(buf, PONG, sizeof PONG - 1) ||
853 !strncasecmp(buf, AUTHENTICATE, sizeof AUTHENTICATE - 1)) {
854 if (buf[1] == 'I' || buf[1] == 'i')
855 lastpingtime = now;
856 check_tcl_out(which, buf, 1);
857 write_to_server(buf, len);
858 if (raw_log)
859 putlog(LOG_SRVOUT, "*", "[m->] %s", buf);
860 return;
861 }
862
863 switch (which) {
864 case DP_MODE_NEXT:
865 qnext = 1;
866 /* Fallthrough */
867
868 case DP_MODE:
869 h = &modeq;
870 tempq = modeq;
871 if (double_mode)
872 doublemsg = 1;
873 break;
874
875 case DP_SERVER_NEXT:
876 qnext = 1;
877 /* Fallthrough */
878
879 case DP_SERVER:
880 h = &mq;
881 tempq = mq;
882 if (double_server)
883 doublemsg = 1;
884 break;
885
886 case DP_HELP_NEXT:
887 qnext = 1;
888 /* Fallthrough */
889
890 case DP_HELP:
891 h = &hq;
892 tempq = hq;
893 if (double_help)
894 doublemsg = 1;
895 break;
896
897 default:
898 putlog(LOG_MISC, "*", "Warning: queuing unknown type to server!");
899 return;
900 }
901
902 if (h->tot < maxqmsg) {
903 /* Don't queue msg if it's already queued? */
904 if (!doublemsg) {
905 for (tq = tempq.head; tq; tq = tqq) {
906 tqq = tq->next;
907 if (!strcasecmp(tq->msg, buf)) {
908 if (!double_warned) {
909 debug1("Message already queued; skipping: %s", buf);
910 double_warned = 1;
911 }
912 return;
913 }
914 }
915 }
916
917 if (check_tcl_out(which, buf, 0))
918 return; /* a Tcl proc requested not to send the message */
919
920 q = nmalloc(sizeof(struct msgq));
921
922 /* Insert into queue. */
923 if (qnext) {
924 q->next = h->head;
925 h->head = q;
926 if (!h->last)
927 h->last = q;
928 }
929 else {
930 q->next = NULL;
931 if (h->last)
932 h->last->next = q;
933 else
934 h->head = q;
935 h->last = q;
936 }
937
938 q->len = len;
939 q->msg = nmalloc(len + 1);
940 memcpy(q->msg, buf, len);
941 q->msg[len] = 0;
942 h->tot++;
943 h->warned = 0;
944 double_warned = 0;
945
946 if (raw_log) {
947 switch (which) {
948 case DP_MODE:
949 putlog(LOG_SRVOUT, "*", "[!m] %s", buf);
950 break;
951 case DP_SERVER:
952 putlog(LOG_SRVOUT, "*", "[!s] %s", buf);
953 break;
954 case DP_HELP:
955 putlog(LOG_SRVOUT, "*", "[!h] %s", buf);
956 break;
957 case DP_MODE_NEXT:
958 putlog(LOG_SRVOUT, "*", "[!!m] %s", buf);
959 break;
960 case DP_SERVER_NEXT:
961 putlog(LOG_SRVOUT, "*", "[!!s] %s", buf);
962 break;
963 case DP_HELP_NEXT:
964 putlog(LOG_SRVOUT, "*", "[!!h] %s", buf);
965 break;
966 }
967 }
968 } else {
969 if (!h->warned) {
970 switch (which) {
971 case DP_MODE_NEXT:
972 /* Fallthrough */
973 case DP_MODE:
974 putlog(LOG_MISC, "*", "Warning: over maximum mode queue!");
975 break;
976
977 case DP_SERVER_NEXT:
978 /* Fallthrough */
979 case DP_SERVER:
980 putlog(LOG_MISC, "*", "Warning: over maximum server queue!");
981 break;
982
983 case DP_HELP_NEXT:
984 /* Fallthrough */
985 case DP_HELP:
986 putlog(LOG_MISC, "*", "Warning: over maximum help queue!");
987 break;
988 }
989 }
990 h->warned = 1;
991 }
992
993 if (which == DP_MODE || which == DP_MODE_NEXT)
994 deq_msg(); /* DP_MODE needs to be sent ASAP, flush if possible. */
995 }
996
997
998 /* This is used to split the 'old' server lists prior to sending to the new
999 add_server as of v1.9.0. It can be removed if the 'old' server method is
1000 removed from Eggdrop.
1001 */
old_add_server(const char * ss)1002 static void old_add_server(const char *ss) {
1003 char name[256] = "";
1004 char port[7] = "";
1005 char pass[121] = "";
1006 if (!sscanf(ss, "[%255[0-9.A-F:a-f]]:%6[+0-9]:%120[^\r\n]", name, port, pass) &&
1007 !sscanf(ss, "%255[^:]:%6[+0-9]:%120[^\r\n]", name, port, pass))
1008 return;
1009 add_server(name, port, pass);
1010 }
1011
1012 /* Add a new server to the server_list.
1013 * Don't return '3' from here, that is used by del_server() for tcl_server()
1014 */
add_server(const char * name,const char * port,const char * pass)1015 static int add_server(const char *name, const char *port, const char *pass)
1016 {
1017 struct server_list *x, *z;
1018 char *ret;
1019
1020 for (z = serverlist; z && z->next; z = z->next);
1021
1022 if ((ret = strchr(name, ':'))) {
1023 if (!strchr(ret+1, ':')) {
1024 return 1;
1025 }
1026 }
1027
1028 #ifndef TLS
1029 if (port[0] == '+') {
1030 sslserver = 1;
1031 return 2;
1032 }
1033 #endif
1034
1035 x = nmalloc(sizeof(struct server_list));
1036 x->next = 0;
1037 x->realname = 0;
1038 x->port = default_port;
1039 if (z)
1040 z->next = x;
1041 else
1042 serverlist = x;
1043
1044 x->name = nmalloc(strlen(name) + 1);
1045 strcpy(x->name, name);
1046 if (pass[0]) {
1047 x->pass = nmalloc(strlen(pass) + 1);
1048 strcpy(x->pass, pass);
1049 } else
1050 x->pass = NULL;
1051 if (port[0])
1052 x->port = atoi(port);
1053 #ifdef TLS
1054 x->ssl = (port[0] == '+') ? 1 : 0;
1055 #endif
1056 return 0;
1057 }
1058
1059 /* Remove a server from the server list.
1060 * Checks based on IP and then the port, if one is provided. If no port is
1061 * provided, remove only the first matching host.
1062 */
del_server(const char * name,const char * port)1063 static int del_server(const char *name, const char *port)
1064 {
1065 struct server_list *z, *curr, *prev;
1066 char *ret;
1067 int found = 0;
1068
1069 if (!serverlist) {
1070 return 2;
1071 }
1072 if ((ret = strchr(name, ':'))) {
1073 if (!strchr(ret+1, ':')) {
1074 return 1;
1075 }
1076 }
1077 /* Check if server to be deleted is first node in list */
1078 if (!strcasecmp(name, serverlist->name)) {
1079 z = serverlist;
1080 if (strlen(port)) {
1081 if ((atoi(port) != serverlist->port)
1082 #ifdef TLS
1083 || ((port[0] != '+') && serverlist->ssl )) {
1084 #else
1085 ) {
1086 #endif
1087 serverlist = serverlist->next;
1088 free_server(z);
1089 }
1090 } else {
1091 serverlist = serverlist->next;
1092 free_server(z);
1093 }
1094 found = 1;
1095 }
1096 curr = serverlist->next;
1097 prev = serverlist;
1098 /* Check the remaining nodes in list */
1099 while (curr != NULL && prev != NULL) {
1100 if (!strcasecmp(name, curr->name)) {
1101 if (port[0] != '\0') {
1102 if ((atoi(port) != curr->port)
1103 #ifdef TLS
1104 || ((port[0] != '+') && curr->ssl )) {
1105 #else
1106 ) {
1107 #endif
1108 prev = curr;
1109 curr = curr->next;
1110 continue;
1111 }
1112 }
1113 z = curr;
1114 prev->next = curr->next;
1115 curr = curr->next;
1116 free_server(z);
1117 found = 1;
1118 } else {
1119 prev = curr;
1120 curr = curr->next;
1121 }
1122 }
1123 return found ? 0 : 3;
1124 }
1125
1126 /* Free a single removed server from server link list */
1127 static void free_server(struct server_list *z) {
1128 if (z->name)
1129 nfree(z->name);
1130 if (z->pass)
1131 nfree(z->pass);
1132 if (z->realname)
1133 nfree(z->realname);
1134 nfree(z);
1135 return;
1136 }
1137
1138
1139 /* Clear out the given server_list.
1140 */
1141 static void clearq(struct server_list *xx)
1142 {
1143 struct server_list *x;
1144
1145 while (xx) {
1146 x = xx->next;
1147 if (xx->name)
1148 nfree(xx->name);
1149 if (xx->pass)
1150 nfree(xx->pass);
1151 if (xx->realname)
1152 nfree(xx->realname);
1153 nfree(xx);
1154 xx = x;
1155 }
1156 }
1157
1158 /* Set botserver to the next available server.
1159 *
1160 * -> if (*ptr == -1) then jump to that particular server
1161 */
1162 static void next_server(int *ptr, char *serv, unsigned int *port, char *pass)
1163 {
1164 struct server_list *x = serverlist;
1165 int i = 0;
1166
1167 /* -1 --> Go to specified server */
1168 if (*ptr == -1) {
1169 for (; x; x = x->next) {
1170 if (x->port == *port) {
1171 if (!strcasecmp(x->name, serv)) {
1172 *ptr = i;
1173 #ifdef TLS
1174 x->ssl = use_ssl;
1175 #endif
1176 return;
1177 } else if (x->realname && !strcasecmp(x->realname, serv)) {
1178 *ptr = i;
1179 strlcpy(serv, x->realname, UHOSTLEN);
1180 #ifdef TLS
1181 use_ssl = x->ssl;
1182 #endif
1183 return;
1184 }
1185 }
1186 i++;
1187 }
1188 /* Gotta add it: */
1189 x = nmalloc(sizeof(struct server_list));
1190
1191 x->next = 0;
1192 x->realname = 0;
1193 x->name = nmalloc(strlen(serv) + 1);
1194 strcpy(x->name, serv);
1195 x->port = *port ? *port : default_port;
1196 if (pass && pass[0]) {
1197 x->pass = nmalloc(strlen(pass) + 1);
1198 strcpy(x->pass, pass);
1199 } else
1200 x->pass = NULL;
1201 #ifdef TLS
1202 x->ssl = use_ssl;
1203 #endif
1204 egg_list_append((struct list_type **) (&serverlist),
1205 (struct list_type *) x);
1206 *ptr = i;
1207 return;
1208 }
1209 /* Find where i am and boogie */
1210 if (x == NULL)
1211 return;
1212 i = (*ptr);
1213 while (i > 0 && x != NULL) {
1214 x = x->next;
1215 i--;
1216 }
1217 if (x != NULL) {
1218 x = x->next;
1219 (*ptr)++;
1220 } /* Go to next server */
1221 if (x == NULL) {
1222 x = serverlist;
1223 *ptr = 0;
1224 } /* Start over at the beginning */
1225 #ifdef TLS
1226 use_ssl = x->ssl;
1227 #endif
1228 strcpy(serv, x->name);
1229 *port = x->port ? x->port : default_port;
1230 if (x->pass)
1231 strcpy(pass, x->pass);
1232 else
1233 pass[0] = 0;
1234 }
1235
1236 static int server_6char STDVAR
1237 {
1238 IntFunc F = (IntFunc) cd;
1239 char x[20];
1240
1241 BADARGS(7, 7, " nick user@host handle dest/chan keyword text");
1242
1243 CHECKVALIDITY(server_6char);
1244 snprintf(x, sizeof x, "%d",
1245 F(argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]));
1246 Tcl_AppendResult(irp, x, NULL);
1247 return TCL_OK;
1248 }
1249
1250 static int server_5char STDVAR
1251 {
1252 Function F = (Function) cd;
1253
1254 BADARGS(6, 6, " nick user@host handle dest/channel text");
1255
1256 CHECKVALIDITY(server_5char);
1257 F(argv[1], argv[2], argv[3], argv[4], argv[5]);
1258 return TCL_OK;
1259 }
1260
1261 static int server_2char STDVAR
1262 {
1263 Function F = (Function) cd;
1264
1265 BADARGS(3, 3, " nick msg");
1266
1267 CHECKVALIDITY(server_2char);
1268 F(argv[1], argv[2]);
1269 return TCL_OK;
1270 }
1271
1272 static int server_msg STDVAR
1273 {
1274 Function F = (Function) cd;
1275
1276 BADARGS(5, 5, " nick uhost hand buffer");
1277
1278 CHECKVALIDITY(server_msg);
1279 F(argv[1], argv[2], get_user_by_handle(userlist, argv[3]), argv[4]);
1280 return TCL_OK;
1281 }
1282
1283 static int server_account STDVAR
1284 {
1285 Function F = (Function) cd;
1286
1287 BADARGS(6, 6, " nick uhost hand chan account");
1288
1289 CHECKVALIDITY(server_account);
1290 F(argv[1], argv[2], get_user_by_handle(userlist, argv[3]), argv[4], argv[5]);
1291 return TCL_OK;
1292 }
1293
1294 static int server_raw STDVAR
1295 {
1296 Function F = (Function) cd;
1297
1298 BADARGS(4, 4, " from code args");
1299
1300 CHECKVALIDITY(server_raw);
1301 Tcl_AppendResult(irp, int_to_base10(F(argv[1], argv[3])), NULL);
1302 return TCL_OK;
1303 }
1304
1305 static int server_tag STDVAR
1306 {
1307 Function F = (Function) cd;
1308
1309 BADARGS(5, 5, " from code args tag");
1310
1311 CHECKVALIDITY(server_tag);
1312 Tcl_AppendResult(irp, int_to_base10(F(argv[1], argv[3], argv[4])), NULL);
1313 return TCL_OK;
1314 }
1315
1316 static int server_out STDVAR
1317 {
1318 Function F = (Function) cd;
1319
1320 BADARGS(4, 4, " queue message sent");
1321
1322 CHECKVALIDITY(server_out);
1323 F(argv[1], argv[2], argv[3]);
1324 return TCL_OK;
1325 }
1326
1327 /* Read/write normal string variable.
1328 */
1329 static char *nick_change(ClientData cdata, Tcl_Interp *irp,
1330 EGG_CONST char *name1,
1331 EGG_CONST char *name2, int flags)
1332 {
1333 EGG_CONST char *new;
1334
1335 if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
1336 Tcl_SetVar2(interp, name1, name2, origbotname, TCL_GLOBAL_ONLY);
1337 if (flags & TCL_TRACE_UNSETS)
1338 Tcl_TraceVar(irp, name1, TCL_TRACE_READS | TCL_TRACE_WRITES |
1339 TCL_TRACE_UNSETS, nick_change, cdata);
1340 } else { /* writes */
1341 new = Tcl_GetVar2(interp, name1, name2, TCL_GLOBAL_ONLY);
1342 if (strcmp(origbotname, (char *) new)) {
1343 if (origbotname[0]) {
1344 putlog(LOG_MISC, "*", "* IRC NICK CHANGE: %s -> %s", origbotname, new);
1345 nick_juped = 0;
1346 }
1347 strlcpy(origbotname, new, NICKLEN);
1348 if (server_online)
1349 dprintf(DP_SERVER, "NICK %s\n", origbotname);
1350 }
1351 }
1352 return NULL;
1353 }
1354
1355 /* Replace all '?'s in s with a random number.
1356 */
1357 static void rand_nick(char *nick)
1358 {
1359 char *p = nick;
1360
1361 while ((p = strchr(p, '?')) != NULL) {
1362 *p = '0' + randint(10);
1363 p++;
1364 }
1365 }
1366
1367 /* Return the alternative bot nick.
1368 */
1369 static char *get_altbotnick(void)
1370 {
1371 /* A random-number nick? */
1372 if (strchr(altnick, '?')) {
1373 if (!raltnick[0] && !wild_match(altnick, botname)) {
1374 strlcpy(raltnick, altnick, NICKLEN);
1375 rand_nick(raltnick);
1376 }
1377 return raltnick;
1378 } else
1379 return altnick;
1380 }
1381
1382 static char *altnick_change(ClientData cdata, Tcl_Interp *irp,
1383 EGG_CONST char *name1,
1384 EGG_CONST char *name2, int flags)
1385 {
1386 /* Always unset raltnick. Will be regenerated when needed. */
1387 raltnick[0] = 0;
1388 return NULL;
1389 }
1390
1391 static char *traced_serveraddress(ClientData cdata, Tcl_Interp *irp,
1392 EGG_CONST char *name1,
1393 EGG_CONST char *name2, int flags)
1394 {
1395 char s[1024];
1396
1397 if (server_online) {
1398 int servidx = findanyidx(serv);
1399
1400 #ifdef TLS
1401 simple_sprintf(s, "%s:%s%u", dcc[servidx].host, dcc[servidx].ssl ? "+" : "",
1402 dcc[servidx].port);
1403 #else
1404 simple_sprintf(s, "%s:%u", dcc[servidx].host, dcc[servidx].port);
1405 #endif
1406 } else
1407 s[0] = 0;
1408 Tcl_SetVar2(interp, name1, name2, s, TCL_GLOBAL_ONLY);
1409 if (flags & TCL_TRACE_UNSETS)
1410 Tcl_TraceVar(irp, name1, TCL_TRACE_READS | TCL_TRACE_WRITES |
1411 TCL_TRACE_UNSETS, traced_serveraddress, cdata);
1412 if (flags & TCL_TRACE_WRITES)
1413 return "read-only variable";
1414 return NULL;
1415 }
1416
1417 static char *traced_server(ClientData cdata, Tcl_Interp *irp,
1418 EGG_CONST char *name1,
1419 EGG_CONST char *name2, int flags)
1420 {
1421 char s[1024];
1422
1423 if (server_online && realservername) {
1424 int servidx = findanyidx(serv);
1425
1426 /* return real server name */
1427 #ifdef TLS
1428 simple_sprintf(s, "%s:%s%u", realservername, dcc[servidx].ssl ? "+" : "",
1429 dcc[servidx].port);
1430 #else
1431 simple_sprintf(s, "%s:%u", realservername, dcc[servidx].port);
1432 #endif
1433 } else
1434 s[0] = 0;
1435 Tcl_SetVar2(interp, name1, name2, s, TCL_GLOBAL_ONLY);
1436 if (flags & TCL_TRACE_UNSETS)
1437 Tcl_TraceVar(irp, name1, TCL_TRACE_READS | TCL_TRACE_WRITES |
1438 TCL_TRACE_UNSETS, traced_server, cdata);
1439 if (flags & TCL_TRACE_WRITES)
1440 return "read-only variable";
1441 return NULL;
1442 }
1443
1444 static char *traced_botname(ClientData cdata, Tcl_Interp *irp,
1445 EGG_CONST char *name1,
1446 EGG_CONST char *name2, int flags)
1447 {
1448 char s[1024];
1449
1450 simple_sprintf(s, "%s%s%s", botname,
1451 botuserhost[0] ? "!" : "", botuserhost[0] ? botuserhost : "");
1452 Tcl_SetVar2(interp, name1, name2, s, TCL_GLOBAL_ONLY);
1453 if (flags & TCL_TRACE_UNSETS)
1454 Tcl_TraceVar(irp, name1, TCL_TRACE_READS | TCL_TRACE_WRITES |
1455 TCL_TRACE_UNSETS, traced_botname, cdata);
1456 if (flags & TCL_TRACE_WRITES)
1457 return "read-only variable";
1458 return NULL;
1459 }
1460
1461 static void do_nettype(void)
1462 {
1463 switch (net_type_int) {
1464 case NETT_EFNET:
1465 case NETT_HYBRID_EFNET:
1466 check_mode_r = 0;
1467 nick_len = 9;
1468 break;
1469 case NETT_IRCNET:
1470 check_mode_r = 1;
1471 use_penalties = 1;
1472 use_fastdeq = 3;
1473 nick_len = 15;
1474 simple_sprintf(stackablecmds, "INVITE AWAY VERSION NICK");
1475 kick_method = 4;
1476 break;
1477 case NETT_UNDERNET:
1478 check_mode_r = 0;
1479 use_fastdeq = 2;
1480 nick_len = 12;
1481 simple_sprintf(stackablecmds,
1482 "PRIVMSG NOTICE TOPIC PART WHOIS USERHOST USERIP ISON");
1483 simple_sprintf(stackable2cmds, "USERHOST USERIP ISON");
1484 break;
1485 case NETT_DALNET:
1486 check_mode_r = 0;
1487 use_fastdeq = 2;
1488 nick_len = 30;
1489 simple_sprintf(stackablecmds,
1490 "PRIVMSG NOTICE PART WHOIS WHOWAS USERHOST ISON WATCH DCCALLOW");
1491 simple_sprintf(stackable2cmds, "USERHOST ISON WATCH");
1492 stack_limit = 20;
1493 kick_method = 4;
1494 break;
1495 case NETT_FREENODE:
1496 nick_len = 16;
1497 break;
1498 case NETT_LIBERA:
1499 check_mode_r = 0;
1500 nick_len = 16;
1501 kick_method = 1;
1502 break;
1503 case NETT_QUAKENET:
1504 check_mode_r = 0;
1505 use_fastdeq = 2;
1506 nick_len = 15;
1507 simple_sprintf(stackablecmds,
1508 "PRIVMSG NOTICE TOPIC PART WHOIS USERHOST USERIP ISON");
1509 simple_sprintf(stackable2cmds, "USERHOST USERIP ISON");
1510 break;
1511 case NETT_RIZON:
1512 check_mode_r = 0;
1513 nick_len = 30;
1514 break;
1515 }
1516 }
1517
1518 static char *traced_nettype(ClientData cdata, Tcl_Interp *irp,
1519 EGG_CONST char *name1,
1520 EGG_CONST char *name2, int flags)
1521 {
1522 int warn = 0;
1523
1524 if (!strcasecmp(net_type, "DALnet"))
1525 net_type_int = NETT_DALNET;
1526 else if (!strcasecmp(net_type, "EFnet"))
1527 net_type_int = NETT_EFNET;
1528 else if (!strcasecmp(net_type, "freenode"))
1529 net_type_int = NETT_FREENODE;
1530 else if (!strcasecmp(net_type, "IRCnet"))
1531 net_type_int = NETT_IRCNET;
1532 else if (!strcasecmp(net_type, "Libera"))
1533 net_type_int = NETT_LIBERA;
1534 else if (!strcasecmp(net_type, "QuakeNet"))
1535 net_type_int = NETT_QUAKENET;
1536 else if (!strcasecmp(net_type, "Rizon"))
1537 net_type_int = NETT_RIZON;
1538 else if (!strcasecmp(net_type, "Undernet"))
1539 net_type_int = NETT_UNDERNET;
1540 else if (!strcasecmp(net_type, "Twitch"))
1541 net_type_int = NETT_TWITCH;
1542 else if (!strcasecmp(net_type, "Other"))
1543 net_type_int = NETT_OTHER;
1544 else if (!strcasecmp(net_type, "0")) { /* For backwards compatibility */
1545 net_type_int = NETT_EFNET;
1546 warn = 1;
1547 }
1548 else if (!strcasecmp(net_type, "1")) { /* For backwards compatibility */
1549 net_type_int = NETT_IRCNET;
1550 warn = 1;
1551 }
1552 else if (!strcasecmp(net_type, "2")) { /* For backwards compatibility */
1553 net_type_int = NETT_UNDERNET;
1554 warn = 1;
1555 }
1556 else if (!strcasecmp(net_type, "3")) { /* For backwards compatibility */
1557 net_type_int = NETT_DALNET;
1558 warn = 1;
1559 }
1560 else if (!strcasecmp(net_type, "4")) { /* For backwards compatibility */
1561 net_type_int = NETT_HYBRID_EFNET;
1562 warn = 1;
1563 }
1564 else if (!strcasecmp(net_type, "5")) { /* For backwards compatibility */
1565 net_type_int = NETT_OTHER;
1566 warn = 1;
1567 } else {
1568 fatal("ERROR: NET-TYPE NOT SET.\n Must be one of DALNet, EFnet, freenode, "
1569 "Libera, IRCnet, Quakenet, Rizon, Undernet, Other.", 0);
1570 }
1571 if (warn) {
1572 putlog(LOG_MISC, "*",
1573 "INFO: The config setting for \"net-type\" has transitioned from a number\n"
1574 "to a text string. Please update your choice to one of the allowed values\n"
1575 "listed in the current configuration file from the source directory\n");
1576 }
1577 do_nettype();
1578 return NULL;
1579 }
1580
1581 static char *traced_nicklen(ClientData cdata, Tcl_Interp *irp,
1582 EGG_CONST char *name1,
1583 EGG_CONST char *name2, int flags)
1584 {
1585 if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
1586 char s[40];
1587
1588 egg_snprintf(s, sizeof s, "%d", nick_len);
1589 Tcl_SetVar2(interp, name1, name2, s, TCL_GLOBAL_ONLY);
1590 if (flags & TCL_TRACE_UNSETS)
1591 Tcl_TraceVar(irp, name1, TCL_TRACE_READS | TCL_TRACE_WRITES |
1592 TCL_TRACE_UNSETS, traced_nicklen, cdata);
1593 } else {
1594 EGG_CONST char *cval = Tcl_GetVar2(interp, name1, name2, TCL_GLOBAL_ONLY);
1595 long lval = 0;
1596
1597 if (cval && Tcl_ExprLong(interp, cval, &lval) != TCL_ERROR) {
1598 if (lval > NICKMAX)
1599 lval = NICKMAX;
1600 nick_len = (int) lval;
1601 }
1602 }
1603 return NULL;
1604 }
1605
1606 static tcl_strings my_tcl_strings[] = {
1607 {"botnick", NULL, 0, STR_PROTECT},
1608 {"altnick", altnick, NICKMAX, 0},
1609 {"realname", botrealname, 80, 0},
1610 {"init-server", initserver, 120, 0},
1611 {"connect-server", connectserver, 120, 0},
1612 {"stackable-commands", stackablecmds, 510, 0},
1613 {"stackable2-commands", stackable2cmds, 510, 0},
1614 {"cap-request", cap_request, CAPMAX - 9, 0},
1615 {"sasl-username", sasl_username, NICKMAX, 0},
1616 {"sasl-password", sasl_password, 80, 0},
1617 {"sasl-ecdsa-key", sasl_ecdsa_key, 120, 0},
1618 {"net-type", net_type, 8, 0},
1619 {NULL, NULL, 0, 0}
1620 };
1621
1622 static tcl_coups my_tcl_coups[] = {
1623 {"flood-ctcp", &flud_ctcp_thr, &flud_ctcp_time},
1624 {"flood-msg", &flud_thr, &flud_time},
1625 {NULL, NULL, NULL}
1626 };
1627
1628 static tcl_ints my_tcl_ints[] = {
1629 {"server-timeout", &server_timeout, 0},
1630 {"lowercase-ctcp", &lowercase_ctcp, 0},
1631 {"server-online", (int *) &server_online, 2},
1632 {"keep-nick", &keepnick, 0},
1633 {"check-stoned", &check_stoned, 0},
1634 {"serverror-quit", &serverror_quit, 0},
1635 {"max-queue-msg", &maxqmsg, 0},
1636 {"trigger-on-ignore", &trigger_on_ignore, 0},
1637 {"answer-ctcp", &answer_ctcp, 0},
1638 {"server-cycle-wait", (int *) &server_cycle_wait, 0},
1639 {"default-port", &default_port, 0},
1640 {"check-mode-r", &check_mode_r, 0},
1641 {"ctcp-mode", &ctcp_mode, 0},
1642 {"double-mode", &double_mode, 0},
1643 {"double-server", &double_server, 0},
1644 {"double-help", &double_help, 0},
1645 {"use-penalties", &use_penalties, 0},
1646 {"use-fastdeq", &use_fastdeq, 0},
1647 {"nicklen", &nick_len, 0},
1648 {"nick-len", &nick_len, 0},
1649 {"optimize-kicks", &optimize_kicks, 0},
1650 {"isjuped", &nick_juped, 0},
1651 {"stack-limit", &stack_limit, 0},
1652 {"exclusive-binds", &exclusive_binds, 0},
1653 {"msg-rate", &msgrate, 0},
1654 #ifdef TLS
1655 {"ssl-verify-server", &tls_vfyserver, 0},
1656 #endif
1657 {"sasl", &sasl, 0},
1658 {"sasl-mechanism", &sasl_mechanism, 0},
1659 {"sasl-continue", &sasl_continue, 0},
1660 {"sasl-timeout", &sasl_timeout, 0},
1661 {"away-notify", &away_notify, 0},
1662 {"invite-notify", &invite_notify, 0},
1663 {"message-tags", &message_tags, 0},
1664 {"extended-join", &extended_join, 0},
1665 {"account-notify", &account_notify, 0},
1666 {NULL, NULL, 0}
1667 };
1668
1669
1670 /*
1671 * Tcl variable trace functions
1672 */
1673
1674 /* Read or write the server list.
1675 */
1676
1677 static char *tcl_eggserver(ClientData cdata, Tcl_Interp *irp,
1678 EGG_CONST char *name1,
1679 EGG_CONST char *name2, int flags)
1680 {
1681 int lc, code, i;
1682 char x[1024];
1683 EGG_CONST char **list, *slist;
1684 struct server_list *q;
1685 Tcl_DString ds;
1686
1687 if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
1688 /* Create server list */
1689 Tcl_DStringInit(&ds);
1690 for (q = serverlist; q; q = q->next) {
1691 #ifdef TLS
1692 egg_snprintf(x, sizeof x, "%s%s%s:%s%d%s%s %s", strchr(q->name, ':') ?
1693 "[" : "", q->name, strchr(q->name, ':') ? "]" : "",
1694 q->ssl ? "+" : "", q->port ? q->port : default_port,
1695 q->pass ? ":" : "", q->pass ? q->pass : "",
1696 q->realname ? q->realname : "");
1697 #else
1698 egg_snprintf(x, sizeof x, "%s%s%s:%d%s%s %s", strchr(q->name, ':') ?
1699 "[" : "", q->name, strchr(q->name, ':') ? "]" : "",
1700 q->port ? q->port : default_port, q->pass ? ":" : "",
1701 q->pass ? q->pass : "", q->realname ? q->realname : "");
1702 #endif
1703 Tcl_DStringAppendElement(&ds, x);
1704 }
1705 slist = Tcl_DStringValue(&ds);
1706 Tcl_SetVar2(interp, name1, name2, slist, TCL_GLOBAL_ONLY);
1707 Tcl_DStringFree(&ds);
1708 } else { /* TCL_TRACE_WRITES */
1709 if (serverlist) {
1710 clearq(serverlist);
1711 serverlist = NULL;
1712 }
1713 slist = Tcl_GetVar2(interp, name1, name2, TCL_GLOBAL_ONLY);
1714 if (slist != NULL) {
1715 code = Tcl_SplitList(interp, slist, &lc, &list);
1716 if (code == TCL_ERROR)
1717 return "variable must be a list";
1718 for (i = 0; i < lc && i < 50; i++)
1719 old_add_server((char *) list[i]);
1720
1721 /* Tricky way to make the bot reset its server pointers
1722 * perform part of a '.jump <current-server>':
1723 */
1724 if (server_online) {
1725 int servidx = findanyidx(serv);
1726
1727 curserv = -1;
1728 if (serverlist)
1729 next_server(&curserv, dcc[servidx].host, &dcc[servidx].port, "");
1730 }
1731 Tcl_Free((char *) list);
1732 }
1733 }
1734 return NULL;
1735 }
1736
1737 /* Trace the servers */
1738 #define tcl_traceserver(name, ptr) \
1739 Tcl_TraceVar(interp, name, TCL_TRACE_READS | TCL_TRACE_WRITES | \
1740 TCL_TRACE_UNSETS, tcl_eggserver, (ClientData) ptr)
1741
1742 #define tcl_untraceserver(name, ptr) \
1743 Tcl_UntraceVar(interp, name, TCL_TRACE_READS | TCL_TRACE_WRITES | \
1744 TCL_TRACE_UNSETS, tcl_eggserver, (ClientData) ptr)
1745
1746
1747 /*
1748 * CTCP DCC CHAT functions
1749 */
1750
1751 static void dcc_chat_hostresolved(int);
1752
1753 /* This only handles CHAT requests, otherwise it's handled in filesys.
1754 */
1755 static int ctcp_DCC_CHAT(char *nick, char *from, char *handle,
1756 char *object, char *keyword, char *text)
1757 {
1758 char *action, *param, *ip, *prt, buf[512], *msg = buf;
1759 int i;
1760 #ifdef TLS
1761 int ssl = 0;
1762 #endif
1763 struct userrec *u = get_user_by_handle(userlist, handle);
1764 struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 };
1765
1766 strlcpy(buf, text, sizeof buf);
1767 action = newsplit(&msg);
1768 param = newsplit(&msg);
1769 ip = newsplit(&msg);
1770 prt = newsplit(&msg);
1771 if (strcasecmp(action, "CHAT"))
1772 {
1773 #ifdef TLS
1774 if (!strcasecmp(action, "SCHAT"))
1775 ssl = 1;
1776 else
1777 #endif
1778 return 0;
1779 }
1780 if (strcasecmp(object, botname) || !u)
1781 return 0;
1782 get_user_flagrec(u, &fr, 0);
1783 if (dcc_total == max_dcc && increase_socks_max()) {
1784 if (!quiet_reject)
1785 dprintf(DP_HELP, "NOTICE %s :%s\n", nick, DCC_TOOMANYDCCS1);
1786 putlog(LOG_MISC, "*", DCC_TOOMANYDCCS2, "CHAT", param, nick, from);
1787 } else if (!(glob_party(fr) || (!require_p && chan_op(fr)))) {
1788 if (glob_xfer(fr))
1789 return 0; /* Allow filesys to pick up the chat */
1790 if (!quiet_reject)
1791 dprintf(DP_HELP, "NOTICE %s :%s\n", nick, DCC_REFUSED2);
1792 putlog(LOG_MISC, "*", "%s: %s!%s", DCC_REFUSED, nick, from);
1793 } else if (u_pass_match(u, "-")) {
1794 if (!quiet_reject)
1795 dprintf(DP_HELP, "NOTICE %s :%s\n", nick, DCC_REFUSED3);
1796 putlog(LOG_MISC, "*", "%s: %s!%s", DCC_REFUSED4, nick, from);
1797 } else if (atoi(prt) < 1024 || atoi(prt) > 65535) {
1798 /* Invalid port */
1799 if (!quiet_reject)
1800 dprintf(DP_HELP, "NOTICE %s :%s (invalid port)\n", nick,
1801 DCC_CONNECTFAILED1);
1802 putlog(LOG_MISC, "*", "%s: CHAT (%s!%s)", DCC_CONNECTFAILED3, nick, from);
1803 } else {
1804 if (!sanitycheck_dcc(nick, from, ip, prt))
1805 return 1;
1806 i = new_dcc(&DCC_DNSWAIT, sizeof(struct dns_info));
1807 if (i < 0) {
1808 putlog(LOG_MISC, "*", "DCC connection: CHAT (%s!%s)", nick, ip);
1809 return 1;
1810 }
1811 #ifdef TLS
1812 dcc[i].ssl = ssl;
1813 #endif
1814 dcc[i].port = atoi(prt);
1815 (void) setsockname(&dcc[i].sockname, ip, dcc[i].port, 0);
1816 dcc[i].u.dns->ip = &dcc[i].sockname;
1817 dcc[i].sock = -1;
1818 strcpy(dcc[i].nick, u->handle);
1819 strcpy(dcc[i].host, from);
1820 dcc[i].timeval = now;
1821 dcc[i].user = u;
1822 dcc[i].u.dns->dns_type = RES_HOSTBYIP;
1823 dcc[i].u.dns->dns_success = dcc_chat_hostresolved;
1824 dcc[i].u.dns->dns_failure = dcc_chat_hostresolved;
1825 dcc[i].u.dns->type = &DCC_CHAT_PASS;
1826 dcc_dnshostbyip(&dcc[i].sockname);
1827 }
1828 return 1;
1829 }
1830
1831 #ifdef TLS
1832 int dcc_chat_sslcb(int sock)
1833 {
1834 int idx;
1835
1836 idx = findanyidx(sock);
1837 if ((idx >= 0) && dcc_fingerprint(idx))
1838 dprintf(idx, "%s\n", DCC_ENTERPASS);
1839 return 0;
1840 }
1841 #endif
1842
1843 static void dcc_chat_hostresolved(int i)
1844 {
1845 char buf[512];
1846 struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 };
1847
1848 egg_snprintf(buf, sizeof buf, "%d", dcc[i].port);
1849 if (!hostsanitycheck_dcc(dcc[i].nick, dcc[i].host, &dcc[i].sockname,
1850 dcc[i].u.dns->host, buf)) {
1851 lostdcc(i);
1852 return;
1853 }
1854 buf[0] = 0;
1855 dcc[i].sock = getsock(dcc[i].sockname.family, 0);
1856 if (dcc[i].sock < 0 || open_telnet_raw(dcc[i].sock, &dcc[i].sockname) < 0)
1857 egg_snprintf(buf, sizeof buf, "%s", strerror(errno));
1858 #ifdef TLS
1859 else if (dcc[i].ssl && ssl_handshake(dcc[i].sock, TLS_CONNECT, tls_vfydcc,
1860 LOG_MISC, dcc[i].host, &dcc_chat_sslcb))
1861 egg_snprintf(buf, sizeof buf, "TLS negotiation error");
1862 #endif
1863 if (buf[0]) {
1864 if (!quiet_reject)
1865 dprintf(DP_HELP, "NOTICE %s :%s (%s)\n", dcc[i].nick,
1866 DCC_CONNECTFAILED1, buf);
1867 putlog(LOG_MISC, "*", "%s: CHAT (%s!%s)", DCC_CONNECTFAILED2,
1868 dcc[i].nick, dcc[i].host);
1869 putlog(LOG_MISC, "*", " (%s)", buf);
1870 killsock(dcc[i].sock);
1871 lostdcc(i);
1872 } else {
1873 changeover_dcc(i, &DCC_CHAT_PASS, sizeof(struct chat_info));
1874 dcc[i].status = STAT_ECHO;
1875 get_user_flagrec(dcc[i].user, &fr, 0);
1876 if (glob_party(fr))
1877 dcc[i].status |= STAT_PARTY;
1878 strcpy(dcc[i].u.chat->con_chan, (chanset) ? chanset->dname : "*");
1879 dcc[i].timeval = now;
1880 /* Ok, we're satisfied with them now: attempt the connect */
1881 putlog(LOG_MISC, "*", "DCC connection: CHAT (%s!%s)", dcc[i].nick,
1882 dcc[i].host);
1883 #ifdef TLS
1884 /* For SSL connections, the handshake callback will determine
1885 if we should request a password */
1886 if (!dcc[i].ssl)
1887 #endif
1888 dprintf(i, "%s\n", DCC_ENTERPASS);
1889 }
1890 return;
1891 }
1892
1893
1894 /*
1895 * Server timer functions
1896 */
1897
1898 static void server_secondly()
1899 {
1900 if (cycle_time)
1901 cycle_time--;
1902 deq_msg();
1903 if (!resolvserv && serv < 0)
1904 connect_server();
1905 if (!--sasl_timeout_time)
1906 handle_sasl_timeout();
1907 }
1908
1909 static void server_5minutely()
1910 {
1911 if (check_stoned && server_online) {
1912 if (lastpingcheck && (now - lastpingcheck >= 240)) {
1913 /* Still waiting for activity, requested longer than 4 minutes ago.
1914 * Server is probably stoned.
1915 */
1916 int servidx = findanyidx(serv);
1917
1918 disconnect_server(servidx);
1919 lostdcc(servidx);
1920 putlog(LOG_SERV, "*", IRC_SERVERSTONED);
1921 } else if (!trying_server) {
1922 /* Check for server being stoned. */
1923 dprintf(DP_MODE, "PING :%li\n", now);
1924 lastpingcheck = now;
1925 }
1926 }
1927 }
1928
1929 static void server_prerehash()
1930 {
1931 struct server_list *x;
1932
1933 strlcpy(oldnick, botname, sizeof oldnick);
1934 /* Clear out servers, any addservers in config file are about to be re-run */
1935 while (serverlist != NULL) {
1936 x = serverlist;
1937 serverlist = serverlist->next;
1938 free_server(x);
1939 }
1940 }
1941
1942 static void server_postrehash()
1943 {
1944 strlcpy(botname, origbotname, NICKLEN);
1945 if (!botname[0])
1946 fatal("NO BOT NAME.", 0);
1947 #ifndef TLS
1948 if ((serverlist == NULL) && sslserver)
1949 fatal("NO NON-SSL SERVERS ADDED (TLS IS DISABLED).", 0);
1950 #endif
1951 if (serverlist == NULL)
1952 fatal("NO SERVERS ADDED.", 0);
1953 if (oldnick[0] && !rfc_casecmp(oldnick, botname) &&
1954 !rfc_casecmp(oldnick, get_altbotnick())) {
1955 /* Change botname back, don't be premature. */
1956 strcpy(botname, oldnick);
1957 dprintf(DP_SERVER, "NICK %s\n", origbotname);
1958 }
1959 /* Change botname back in case we were using altnick previous to rehash. */
1960 else if (oldnick[0])
1961 strcpy(botname, oldnick);
1962 }
1963
1964 static void server_die()
1965 {
1966 cycle_time = 100;
1967 if (server_online) {
1968 dprintf(-serv, "QUIT :%s\n", quit_msg[0] ? quit_msg : "");
1969 sleep(3); /* Give the server time to understand */
1970 }
1971 nuke_server(NULL);
1972 }
1973
1974
1975 static void msgq_clear(struct msgq_head *qh)
1976 {
1977 struct msgq *q, *qq;
1978
1979 for (q = qh->head; q; q = qq) {
1980 qq = q->next;
1981 nfree(q->msg);
1982 nfree(q);
1983 }
1984 qh->head = qh->last = NULL;
1985 qh->tot = qh->warned = 0;
1986 }
1987
1988 static int msgq_expmem(struct msgq_head *qh)
1989 {
1990 int tot = 0;
1991 struct msgq *m;
1992
1993 for (m = qh->head; m; m = m->next) {
1994 tot += m->len + 1;
1995 tot += sizeof(struct msgq);
1996 }
1997 return tot;
1998 }
1999
2000 static int server_expmem()
2001 {
2002 int tot = 0;
2003 struct server_list *s = serverlist;
2004
2005 for (; s; s = s->next) {
2006 if (s->name)
2007 tot += strlen(s->name) + 1;
2008 if (s->pass)
2009 tot += strlen(s->pass) + 1;
2010 if (s->realname)
2011 tot += strlen(s->realname) + 1;
2012 tot += sizeof(struct server_list);
2013 }
2014
2015 if (realservername)
2016 tot += strlen(realservername) + 1;
2017 tot += msgq_expmem(&mq) + msgq_expmem(&hq) + msgq_expmem(&modeq);
2018
2019 tot += isupport_expmem();
2020 return tot;
2021 }
2022
2023 static void server_report(int idx, int details)
2024 {
2025 char s1[64], s[128];
2026 int servidx;
2027
2028 if (server_online) {
2029 dprintf(idx, " Online as: %s%s%s (%s)\n", botname, botuserhost[0] ?
2030 "!" : "", botuserhost[0] ? botuserhost : "", botrealname);
2031 if (nick_juped)
2032 dprintf(idx, " NICK IS JUPED: %s%s\n", origbotname,
2033 keepnick ? " (trying)" : "");
2034 daysdur(now, server_online, s1);
2035 egg_snprintf(s, sizeof s, "(connected %s)", s1);
2036 if (server_lag && !lastpingcheck) {
2037 if (server_lag == -1)
2038 egg_snprintf(s1, sizeof s1, " (bad pong replies)");
2039 else
2040 egg_snprintf(s1, sizeof s1, " (lag: %ds)", server_lag);
2041 strcat(s, s1);
2042 }
2043 }
2044
2045 if ((trying_server || server_online) &&
2046 ((servidx = findanyidx(serv)) != -1)) {
2047 const char *networkname = server_online ? isupport_get("NETWORK", strlen("NETWORK")) : "unknown network";
2048 #ifdef TLS
2049 dprintf(idx, " Connected to %s [%s]:%s%d %s\n", networkname, dcc[servidx].host,
2050 dcc[servidx].ssl ? "+" : "", dcc[servidx].port, trying_server ?
2051 "(trying)" : s);
2052 #else
2053 dprintf(idx, " Connected to %s [%s]:%d %s\n", networkname, dcc[servidx].host,
2054 dcc[servidx].port, trying_server ? "(trying)" : s);
2055 #endif
2056 } else
2057 dprintf(idx, " %s\n", IRC_NOSERVER);
2058
2059 if (modeq.tot)
2060 dprintf(idx, " %s %d%% (%d msgs)\n", IRC_MODEQUEUE,
2061 (int) ((float) (modeq.tot * 100.0) / (float) maxqmsg),
2062 (int) modeq.tot);
2063 if (mq.tot)
2064 dprintf(idx, " %s %d%% (%d msgs)\n", IRC_SERVERQUEUE,
2065 (int) ((float) (mq.tot * 100.0) / (float) maxqmsg), (int) mq.tot);
2066 if (hq.tot)
2067 dprintf(idx, " %s %d%% (%d msgs)\n", IRC_HELPQUEUE,
2068 (int) ((float) (hq.tot * 100.0) / (float) maxqmsg), (int) hq.tot);
2069 dprintf(idx, " Active CAP negotiations: %s\n", (strlen(cap.negotiated) > 0) ?
2070 cap.negotiated : "None" );
2071 if (details) {
2072 int size = server_expmem();
2073
2074 if (initserver[0])
2075 dprintf(idx, " On connect, I do: %s\n", initserver);
2076 if (connectserver[0])
2077 dprintf(idx, " Before connect, I do: %s\n", connectserver);
2078
2079 isupport_report(idx, " ", details);
2080
2081 dprintf(idx, " Msg flood: %d msg%s/%d second%s\n", flud_thr,
2082 (flud_thr != 1) ? "s" : "", flud_time,
2083 (flud_time != 1) ? "s" : "");
2084 dprintf(idx, " CTCP flood: %d msg%s/%d second%s\n", flud_ctcp_thr,
2085 (flud_ctcp_thr != 1) ? "s" : "", flud_ctcp_time,
2086 (flud_ctcp_time != 1) ? "s" : "");
2087 dprintf(idx, " Using %d byte%s of memory\n", size,
2088 (size != 1) ? "s" : "");
2089 }
2090 }
2091
2092 static cmd_t my_ctcps[] = {
2093 {"DCC", "", ctcp_DCC_CHAT, "server:DCC"},
2094 {NULL, NULL, NULL, NULL}
2095 };
2096
2097 static char *server_close()
2098 {
2099 cycle_time = 100;
2100 nuke_server("Connection reset by peer");
2101 clearq(serverlist);
2102 rem_builtins(H_dcc, C_dcc_serv);
2103 rem_builtins(H_raw, my_raw_binds);
2104 rem_builtins(H_rawt, my_rawt_binds);
2105 rem_builtins(H_ctcp, my_ctcps);
2106 rem_builtins(H_isupport, my_isupport_binds);
2107 isupport_fini();
2108 /* Restore original commands. */
2109 del_bind_table(H_wall);
2110 del_bind_table(H_account);
2111 del_bind_table(H_raw);
2112 del_bind_table(H_rawt);
2113 del_bind_table(H_notc);
2114 del_bind_table(H_msgm);
2115 del_bind_table(H_msg);
2116 del_bind_table(H_flud);
2117 del_bind_table(H_ctcr);
2118 del_bind_table(H_ctcp);
2119 del_bind_table(H_out);
2120 rem_tcl_coups(my_tcl_coups);
2121 rem_tcl_strings(my_tcl_strings);
2122 rem_tcl_ints(my_tcl_ints);
2123 rem_help_reference("server.help");
2124 rem_tcl_commands(my_tcl_cmds);
2125 Tcl_UntraceVar(interp, "nick",
2126 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2127 nick_change, NULL);
2128 Tcl_UntraceVar(interp, "altnick",
2129 TCL_TRACE_WRITES | TCL_TRACE_UNSETS, altnick_change, NULL);
2130 Tcl_UntraceVar(interp, "botname",
2131 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2132 traced_botname, NULL);
2133 Tcl_UntraceVar(interp, "server",
2134 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2135 traced_server, NULL);
2136 Tcl_UntraceVar(interp, "serveraddress",
2137 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2138 traced_serveraddress, NULL);
2139 Tcl_UntraceVar(interp, "net-type",
2140 TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2141 traced_nettype, NULL);
2142 Tcl_UntraceVar(interp, "nick-len",
2143 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2144 traced_nicklen, NULL);
2145 tcl_untraceserver("servers", NULL);
2146 empty_msgq();
2147 del_hook(HOOK_SECONDLY, (Function) server_secondly);
2148 del_hook(HOOK_5MINUTELY, (Function) server_5minutely);
2149 del_hook(HOOK_QSERV, (Function) queue_server);
2150 del_hook(HOOK_MINUTELY, (Function) minutely_checks);
2151 del_hook(HOOK_PRE_REHASH, (Function) server_prerehash);
2152 del_hook(HOOK_REHASH, (Function) server_postrehash);
2153 del_hook(HOOK_DIE, (Function) server_die);
2154 module_undepend(MODULE_NAME);
2155 return NULL;
2156 }
2157
2158 EXPORT_SCOPE char *server_start();
2159
2160 static Function server_table[] = {
2161 (Function) server_start,
2162 (Function) server_close,
2163 (Function) server_expmem,
2164 (Function) server_report,
2165 /* 4 - 7 */
2166 (Function) NULL, /* char * (points to botname later on) */
2167 (Function) botuserhost, /* char * */
2168 #ifdef TLS
2169 (Function) & use_ssl, /* int */
2170 #else
2171 (Function) NULL, /* Was quiet_reject <Wcc[01/21/03]>. */
2172 #endif
2173 (Function) & serv, /* int */
2174 /* 8 - 11 */
2175 (Function) & flud_thr, /* int */
2176 (Function) & flud_time, /* int */
2177 (Function) & flud_ctcp_thr, /* int */
2178 (Function) & flud_ctcp_time, /* int */
2179 /* 12 - 15 */
2180 (Function) match_my_nick,
2181 (Function) check_tcl_flud,
2182 (Function) & msgtag, /* int */
2183 (Function) & answer_ctcp, /* int */
2184 /* 16 - 19 */
2185 (Function) & trigger_on_ignore, /* int */
2186 (Function) check_tcl_ctcpr,
2187 (Function) NULL,
2188 (Function) nuke_server,
2189 /* 20 - 23 */
2190 (Function) newserver, /* char * */
2191 (Function) & newserverport, /* int */
2192 (Function) newserverpass, /* char * */
2193 (Function) & cycle_time, /* int */
2194 /* 24 - 27 */
2195 (Function) & default_port, /* int */
2196 (Function) & server_online, /* int */
2197 (Function) & H_rawt, /* p_tcl_bind_list */
2198 (Function) & H_raw, /* p_tcl_bind_list */
2199 /* 28 - 31 */
2200 (Function) & H_wall, /* p_tcl_bind_list */
2201 (Function) & H_msg, /* p_tcl_bind_list */
2202 (Function) & H_msgm, /* p_tcl_bind_list */
2203 (Function) & H_notc, /* p_tcl_bind_list */
2204 /* 32 - 35 */
2205 (Function) & H_flud, /* p_tcl_bind_list */
2206 (Function) & H_ctcp, /* p_tcl_bind_list */
2207 (Function) & H_ctcr, /* p_tcl_bind_list */
2208 (Function) ctcp_reply,
2209 /* 36 - 39 */
2210 (Function) get_altbotnick, /* char * */
2211 (Function) & nick_len, /* int */
2212 (Function) check_tcl_notc,
2213 (Function) & exclusive_binds, /* int */
2214 /* 40 - 43 */
2215 (Function) & H_out, /* p_tcl_bind_list */
2216 (Function) & net_type_int, /* int */
2217 (Function) & H_account, /* p_tcl_bind)list */
2218 (Function) & cap, /* cap_list */
2219 /* 44 - 47 */
2220 (Function) & extended_join, /* int */
2221 (Function) & account_notify, /* int */
2222 (Function) & H_isupport, /* p_tcl_bind_list */
2223 (Function) & isupport_get, /* */
2224 /* 48 - 52 */
2225 (Function) & isupport_parseint/* */
2226 };
2227
2228 char *server_start(Function *global_funcs)
2229 {
2230 EGG_CONST char *s;
2231
2232 global = global_funcs;
2233
2234 /*
2235 * Init of all the variables *must* be done in _start rather than
2236 * globally.
2237 */
2238 serv = -1;
2239 botname[0] = 0;
2240 trying_server = 0L;
2241 server_lag = 0;
2242 altnick[0] = 0;
2243 raltnick[0] = 0;
2244 curserv = 0;
2245 flud_thr = 5;
2246 flud_time = 60;
2247 flud_ctcp_thr = 3;
2248 flud_ctcp_time = 60;
2249 initserver[0] = 0;
2250 connectserver[0] = 0; /* drummer */
2251 botuserhost[0] = 0;
2252 keepnick = 1;
2253 check_stoned = 1;
2254 serverror_quit = 1;
2255 lastpingcheck = 0;
2256 server_online = 0;
2257 server_cycle_wait = 60;
2258 strcpy(botrealname, "A deranged product of evil coders");
2259 server_timeout = 60;
2260 serverlist = NULL;
2261 cycle_time = 0;
2262 default_port = 6667;
2263 oldnick[0] = 0;
2264 trigger_on_ignore = 0;
2265 exclusive_binds = 0;
2266 answer_ctcp = 1;
2267 lowercase_ctcp = 0;
2268 check_mode_r = 0;
2269 maxqmsg = 300;
2270 burst = 0;
2271 strlcpy(net_type, "EFnet", sizeof net_type);
2272 double_mode = 0;
2273 double_server = 0;
2274 double_help = 0;
2275 use_penalties = 0;
2276 use_fastdeq = 0;
2277 stackablecmds[0] = 0;
2278 strcpy(stackable2cmds, "USERHOST ISON");
2279 resolvserv = 0;
2280 lastpingtime = 0;
2281 last_time = 0;
2282 nick_len = 9;
2283 kick_method = 1;
2284 optimize_kicks = 0;
2285 stack_limit = 4;
2286 realservername = 0;
2287 msgrate = 2;
2288 #ifdef TLS
2289 tls_vfyserver = 0;
2290 #endif
2291
2292 server_table[4] = (Function) botname;
2293 module_register(MODULE_NAME, server_table, 1, 5);
2294 if (!module_depend(MODULE_NAME, "eggdrop", 108, 0)) {
2295 module_undepend(MODULE_NAME);
2296 return "This module requires Eggdrop 1.8.0 or later.";
2297 }
2298
2299 /* Fool bot in reading the values. */
2300 tcl_eggserver(NULL, interp, "servers", NULL, 0);
2301 tcl_traceserver("servers", NULL);
2302 s = Tcl_GetVar(interp, "nick", TCL_GLOBAL_ONLY);
2303 if (s)
2304 strlcpy(origbotname, s, NICKLEN);
2305 Tcl_TraceVar(interp, "nick",
2306 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2307 nick_change, NULL);
2308 Tcl_TraceVar(interp, "altnick",
2309 TCL_TRACE_WRITES | TCL_TRACE_UNSETS, altnick_change, NULL);
2310 Tcl_TraceVar(interp, "botname",
2311 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2312 traced_botname, NULL);
2313 Tcl_TraceVar(interp, "server",
2314 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2315 traced_server, NULL);
2316 Tcl_TraceVar(interp, "serveraddress",
2317 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2318 traced_serveraddress, NULL);
2319 Tcl_TraceVar(interp, "net-type",
2320 TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2321 traced_nettype, NULL);
2322 Tcl_TraceVar(interp, "nick-len",
2323 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2324 traced_nicklen, NULL);
2325 H_wall = add_bind_table("wall", HT_STACKABLE, server_2char);
2326 H_account = add_bind_table("account", HT_STACKABLE, server_account);
2327 H_raw = add_bind_table("raw", HT_STACKABLE, server_raw);
2328 H_rawt = add_bind_table("rawt", HT_STACKABLE, server_tag);
2329 H_notc = add_bind_table("notc", HT_STACKABLE, server_5char);
2330 H_msgm = add_bind_table("msgm", HT_STACKABLE, server_msg);
2331 H_msg = add_bind_table("msg", 0, server_msg);
2332 H_flud = add_bind_table("flud", HT_STACKABLE, server_5char);
2333 H_ctcr = add_bind_table("ctcr", HT_STACKABLE, server_6char);
2334 H_ctcp = add_bind_table("ctcp", HT_STACKABLE, server_6char);
2335 H_out = add_bind_table("out", HT_STACKABLE, server_out);
2336 isupport_init();
2337 add_builtins(H_raw, my_raw_binds);
2338 add_builtins(H_rawt, my_rawt_binds);
2339 add_builtins(H_dcc, C_dcc_serv);
2340 add_builtins(H_ctcp, my_ctcps);
2341 add_builtins(H_isupport, my_isupport_binds);
2342 add_help_reference("server.help");
2343 my_tcl_strings[0].buf = botname;
2344 add_tcl_strings(my_tcl_strings);
2345 add_tcl_ints(my_tcl_ints);
2346 if (sasl) {
2347 if ((sasl_mechanism < 0) || (sasl_mechanism >= SASL_MECHANISM_NUM)) {
2348 fatal("ERROR: sasl-mechanism is not set to an allowed value, please check"
2349 " it and try again", 0);
2350 }
2351 #ifdef TLS
2352 #ifndef HAVE_EVP_PKEY_GET1_EC_KEY
2353 if (sasl_mechanism == SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE) {
2354 fatal("ERROR: NIST256 functionality missing from your TLS libs, please "
2355 "choose a different SASL method", 0);
2356 }
2357 #endif /* HAVE_EVP_PKEY_GET1_EC_KEY */
2358 #else /* TLS */
2359 if ((sasl_mechanism == SASL_MECHANISM_ECDSA_NIST256P_CHALLENGE) ||
2360 (sasl_mechanism == SASL_MECHANISM_EXTERNAL)) {
2361 fatal("ERROR: The selected SASL authentication method requires TLS "
2362 "libraries which are not installed on this machine. Please "
2363 "choose the PLAIN method in your config.", 0);
2364 }
2365 #endif /* TLS */
2366 }
2367 add_tcl_commands(my_tcl_cmds);
2368 add_tcl_coups(my_tcl_coups);
2369 add_hook(HOOK_SECONDLY, (Function) server_secondly);
2370 add_hook(HOOK_5MINUTELY, (Function) server_5minutely);
2371 add_hook(HOOK_MINUTELY, (Function) minutely_checks);
2372 add_hook(HOOK_QSERV, (Function) queue_server);
2373 add_hook(HOOK_PRE_REHASH, (Function) server_prerehash);
2374 add_hook(HOOK_REHASH, (Function) server_postrehash);
2375 add_hook(HOOK_DIE, (Function) server_die);
2376 mq.head = hq.head = modeq.head = NULL;
2377 mq.last = hq.last = modeq.last = NULL;
2378 mq.tot = hq.tot = modeq.tot = 0;
2379 mq.warned = hq.warned = modeq.warned = 0;
2380 double_warned = 0;
2381 newserver[0] = 0;
2382 newserverport = 0;
2383 curserv = 999;
2384 /* Because this reads the interp variable, the read trace MUST be after */
2385 do_nettype();
2386 return NULL;
2387 }
2388