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