1 /*
2  * assoc.c -- part of assoc.mod
3  *   the assoc code, moved here mainly from botnet.c for module work
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 "assoc"
25 #define MAKING_ASSOC
26 
27 #include "src/mod/module.h"
28 #include "src/tandem.h"
29 #include <stdlib.h>
30 #include "assoc.h"
31 
32 #undef global
33 static Function *global = NULL;
34 
35 /* Keep track of channel associations */
36 typedef struct assoc_t_ {
37   char name[21];
38   unsigned int channel;
39   struct assoc_t_ *next;
40 } assoc_t;
41 
42 /* Channel name-number associations */
43 static assoc_t *assoc;
44 
botnet_send_assoc(int idx,int chan,char * nick,char * buf)45 static void botnet_send_assoc(int idx, int chan, char *nick, char *buf)
46 {
47   char x[1024];
48   int idx2;
49 
50   simple_sprintf(x, "assoc %D %s %s", chan, nick, buf);
51   for (idx2 = 0; idx2 < dcc_total; idx2++)
52     if ((dcc[idx2].type == &DCC_BOT) && (idx2 != idx) &&
53         (b_numver(idx2) >= NEAT_BOTNET) &&
54         !(bot_flags(dcc[idx2].user) & BOT_ISOLATE))
55       botnet_send_zapf(idx2, botnetnick, dcc[idx2].nick, x);
56 }
57 
assoc_expmem()58 static int assoc_expmem()
59 {
60   assoc_t *a;
61   int size = 0;
62 
63   for (a = assoc; a; a = a->next)
64     size += sizeof(assoc_t);
65   return size;
66 }
67 
link_assoc(char * bot,char * via)68 static void link_assoc(char *bot, char *via)
69 {
70   char x[1024];
71 
72   if (!strcasecmp(via, botnetnick)) {
73     int idx = nextbot(bot);
74     assoc_t *a;
75 
76     if (!(bot_flags(dcc[idx].user) & BOT_ISOLATE)) {
77       for (a = assoc; a && a->name[0]; a = a->next) {
78         simple_sprintf(x, "assoc %D %s %s", (int) a->channel, botnetnick,
79                        a->name);
80         botnet_send_zapf(idx, botnetnick, dcc[idx].nick, x);
81       }
82     }
83   }
84 }
85 
kill_assoc(int chan)86 static void kill_assoc(int chan)
87 {
88   assoc_t *a = assoc, *last = NULL;
89 
90   while (a) {
91     if (a->channel == chan) {
92       if (last != NULL)
93         last->next = a->next;
94       else
95         assoc = a->next;
96       nfree(a);
97       a = NULL;
98     } else {
99       last = a;
100       a = a->next;
101     }
102   }
103 }
104 
kill_all_assoc()105 static void kill_all_assoc()
106 {
107   assoc_t *a, *x;
108 
109   for (a = assoc; a; a = x) {
110     x = a->next;
111     nfree(a);
112   }
113   assoc = NULL;
114 }
115 
add_assoc(char * name,int chan)116 static void add_assoc(char *name, int chan)
117 {
118   assoc_t *a, *b, *old = NULL;
119 
120   for (a = assoc; a; a = a->next) {
121     if (name[0] != 0 && !strcasecmp(a->name, name)) {
122       kill_assoc(a->channel);
123       add_assoc(name, chan);
124       return;
125     }
126     if (a->channel == chan) {
127       strlcpy(a->name, name, sizeof a->name);
128       return;
129     }
130   }
131   /* Add in numerical order */
132   for (a = assoc; a; old = a, a = a->next) {
133     if (a->channel > chan) {
134       b = nmalloc(sizeof *b);
135       b->next = a;
136       b->channel = chan;
137       strlcpy(b->name, name, sizeof b->name);
138       if (old == NULL)
139         assoc = b;
140       else
141         old->next = b;
142       return;
143     }
144   }
145   /* Add at the end */
146   b = nmalloc(sizeof *b);
147   b->next = NULL;
148   b->channel = chan;
149   strlcpy(b->name, name, sizeof b->name);
150   if (old == NULL)
151     assoc = b;
152   else
153     old->next = b;
154 }
155 
get_assoc(char * name)156 static int get_assoc(char *name)
157 {
158   assoc_t *a;
159 
160   for (a = assoc; a; a = a->next)
161     if (!strcasecmp(a->name, name))
162       return a->channel;
163   return -1;
164 }
165 
get_assoc_name(int chan)166 static char *get_assoc_name(int chan)
167 {
168   assoc_t *a;
169 
170   for (a = assoc; a; a = a->next)
171     if (a->channel == chan)
172       return a->name;
173   return NULL;
174 }
175 
dump_assoc(int idx)176 static void dump_assoc(int idx)
177 {
178   assoc_t *a = assoc;
179 
180   if (a == NULL) {
181     dprintf(idx, "%s\n", ASSOC_NOCHNAMES);
182     return;
183   }
184   dprintf(idx, " %s  %s\n", ASSOC_CHAN, ASSOC_NAME);
185   for (; a && a->name[0]; a = a->next)
186     dprintf(idx, "%c%5d %s\n", (a->channel < GLOBAL_CHANS) ? ' ' : '*',
187             a->channel % GLOBAL_CHANS, a->name);
188   return;
189 }
190 
cmd_assoc(struct userrec * u,int idx,char * par)191 static int cmd_assoc(struct userrec *u, int idx, char *par)
192 {
193   char *num;
194   int chan;
195 
196   if (!par[0]) {
197     putlog(LOG_CMDS, "*", "#%s# assoc", dcc[idx].nick);
198     dump_assoc(idx);
199   } else if (!u || !(u->flags & USER_BOTMAST))
200     dprintf(idx, "%s", MISC_NOSUCHCMD);
201   else {
202     num = newsplit(&par);
203     if (num[0] == '*') {
204       chan = GLOBAL_CHANS + atoi(num + 1);
205       if ((chan < GLOBAL_CHANS) || (chan > 199999)) {
206         dprintf(idx, "%s\n", ASSOC_LCHAN_RANGE);
207         return 0;
208       }
209     } else {
210       chan = atoi(num);
211       if (chan == 0) {
212         dprintf(idx, "%s\n", ASSOC_PARTYLINE);
213         return 0;
214       } else if ((chan < 1) || (chan >= GLOBAL_CHANS)) {
215         dprintf(idx, "%s\n", ASSOC_CHAN_RANGE);
216         return 0;
217       }
218     }
219     if (!par[0]) {
220       /* Remove an association */
221       if (get_assoc_name(chan) == NULL) {
222         dprintf(idx, ASSOC_NONAME_CHAN, (chan < GLOBAL_CHANS) ? "" : "*",
223                 chan % GLOBAL_CHANS);
224         return 0;
225       }
226       kill_assoc(chan);
227       putlog(LOG_CMDS, "*", "#%s# assoc %d", dcc[idx].nick, chan);
228       dprintf(idx, ASSOC_REMNAME_CHAN, (chan < GLOBAL_CHANS) ? "" : "*",
229               chan % GLOBAL_CHANS);
230       chanout_but(-1, chan, ASSOC_REMOUT_CHAN, dcc[idx].nick);
231       if (chan < GLOBAL_CHANS)
232         botnet_send_assoc(-1, chan, dcc[idx].nick, "0");
233       return 0;
234     }
235     if (strlen(par) > 20) {
236       dprintf(idx, "%s\n", ASSOC_CHNAME_TOOLONG);
237       return 0;
238     }
239     if ((par[0] >= '0') && (par[0] <= '9')) {
240       dprintf(idx, "%s\n", ASSOC_CHNAME_FIRSTCHAR);
241       return 0;
242     }
243     add_assoc(par, chan);
244     putlog(LOG_CMDS, "*", "#%s# assoc %d %s", dcc[idx].nick, chan, par);
245     dprintf(idx, ASSOC_NEWNAME_CHAN, (chan < GLOBAL_CHANS) ? "" : "*",
246             chan % GLOBAL_CHANS, par);
247     chanout_but(-1, chan, ASSOC_NEWOUT_CHAN, dcc[idx].nick, par);
248     if (chan < GLOBAL_CHANS)
249       botnet_send_assoc(-1, chan, dcc[idx].nick, par);
250   }
251   return 0;
252 }
253 
254 static int tcl_killassoc STDVAR
255 {
256   int chan;
257 
258   BADARGS(2, 2, " chan");
259 
260   if (argv[1][0] == '&')
261     kill_all_assoc();
262   else {
263     chan = atoi(argv[1]);
264     if ((chan < 1) || (chan > 199999)) {
265       Tcl_AppendResult(irp, "invalid channel #", NULL);
266       return TCL_ERROR;
267     }
268     kill_assoc(chan);
269 
270     botnet_send_assoc(-1, chan, "*script*", "0");
271   }
272   return TCL_OK;
273 }
274 
275 static int tcl_assoc STDVAR
276 {
277   int chan;
278   char name[21], *p;
279 
280   BADARGS(2, 3, " chan ?name?");
281 
282   if ((argc == 2) && ((argv[1][0] < '0') || (argv[1][0] > '9'))) {
283     chan = get_assoc(argv[1]);
284     if (chan == -1)
285       Tcl_AppendResult(irp, "", NULL);
286     else {
287       simple_sprintf(name, "%d", chan);
288       Tcl_AppendResult(irp, name, NULL);
289     }
290     return TCL_OK;
291   }
292   chan = atoi(argv[1]);
293   if ((chan < 1) || (chan > 199999)) {
294     Tcl_AppendResult(irp, "invalid channel #", NULL);
295     return TCL_ERROR;
296   }
297   if (argc == 3) {
298     strlcpy(name, argv[2], sizeof name);
299     add_assoc(name, chan);
300     botnet_send_assoc(-1, chan, "*script*", name);
301   }
302   p = get_assoc_name(chan);
303   if (p == NULL)
304     name[0] = 0;
305   else
306     strcpy(name, p);
307   Tcl_AppendResult(irp, name, NULL);
308   return TCL_OK;
309 }
310 
zapf_assoc(char * botnick,char * code,char * par)311 static void zapf_assoc(char *botnick, char *code, char *par)
312 {
313   int idx = nextbot(botnick);
314   char *s, *s1, *nick;
315   int linking = 0, chan;
316 
317   if ((idx >= 0) && !(bot_flags(dcc[idx].user) & BOT_ISOLATE)) {
318     if (!strcasecmp(dcc[idx].nick, botnick))
319       linking = b_status(idx) & STAT_LINKING;
320     s = newsplit(&par);
321     chan = base64_to_int(s);
322     if ((chan > 0) && (chan < GLOBAL_CHANS)) {
323       nick = newsplit(&par);
324       s1 = get_assoc_name(chan);
325       if (linking && ((s1 == NULL) || (s1[0] == 0) ||
326           (((intptr_t) get_user(find_entry_type("BOTFL"),
327           dcc[idx].user) & BOT_HUB)))) {
328         add_assoc(par, chan);
329         botnet_send_assoc(idx, chan, nick, par);
330         chanout_but(-1, chan, ASSOC_CHNAME_NAMED, nick, par);
331       } else if (par[0] == '0') {
332         kill_assoc(chan);
333         chanout_but(-1, chan, ASSOC_CHNAME_REM, botnick, nick);
334       } else if (get_assoc(par) != chan) {
335         /* New one i didn't know about -- pass it on */
336         add_assoc(par, chan);
337         chanout_but(-1, chan, ASSOC_CHNAME_NAMED2, botnick, nick, par);
338       }
339     } else {
340       dprintf(LOG_DEBUG, "ASSOC: Channel number outside bounds\n");
341     }
342   }
343 }
344 
assoc_report(int idx,int details)345 static void assoc_report(int idx, int details)
346 {
347   if (details) {
348     assoc_t *a;
349     int size = 0, count = 0;
350 
351     for (a = assoc; a; a = a->next) {
352       count++;
353       size += sizeof(assoc_t);
354     }
355 
356     dprintf(idx, "    %d current association%s\n", count,
357             (count != 1) ? "s" : "");
358     dprintf(idx, "    Using %d byte%s of memory\n", size,
359             (size != 1) ? "s" : "");
360   }
361 }
362 
363 static cmd_t mydcc[] = {
364   {"assoc", "",   cmd_assoc, NULL},
365   {NULL,    NULL, NULL,      NULL}
366 };
367 
368 static cmd_t mybot[] = {
369   {"assoc", "",   (IntFunc) zapf_assoc, NULL},
370   {NULL,    NULL, NULL,                  NULL}
371 };
372 
373 static cmd_t mylink[] = {
374   {"*",  "",   (IntFunc) link_assoc, "assoc"},
375   {NULL, NULL, NULL,                     NULL}
376 };
377 
378 static tcl_cmds mytcl[] = {
379   {"assoc",         tcl_assoc},
380   {"killassoc", tcl_killassoc},
381   {NULL,                 NULL}
382 };
383 
assoc_close()384 static char *assoc_close()
385 {
386   kill_all_assoc();
387   rem_builtins(H_dcc, mydcc);
388   rem_builtins(H_bot, mybot);
389   rem_builtins(H_link, mylink);
390   rem_tcl_commands(mytcl);
391   rem_help_reference("assoc.help");
392   del_lang_section("assoc");
393   module_undepend(MODULE_NAME);
394   return NULL;
395 }
396 
397 EXPORT_SCOPE char *assoc_start();
398 
399 static Function assoc_table[] = {
400   (Function) assoc_start,
401   (Function) assoc_close,
402   (Function) assoc_expmem,
403   (Function) assoc_report,
404 };
405 
assoc_start(Function * global_funcs)406 char *assoc_start(Function *global_funcs)
407 {
408   global = global_funcs;
409 
410   module_register(MODULE_NAME, assoc_table, 2, 1);
411   if (!module_depend(MODULE_NAME, "eggdrop", 108, 0)) {
412     module_undepend(MODULE_NAME);
413     return "This module requires Eggdrop 1.8.0 or later.";
414   }
415   assoc = NULL;
416   add_builtins(H_dcc, mydcc);
417   add_builtins(H_bot, mybot);
418   add_builtins(H_link, mylink);
419   add_lang_section("assoc");
420   add_tcl_commands(mytcl);
421   add_help_reference("assoc.help");
422   return NULL;
423 }
424