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