1 |
/* |
2 |
* chanprog.c -- handles: |
3 |
* rmspace() |
4 |
* maintaining the server list |
5 |
* revenge punishment |
6 |
* timers, utimers |
7 |
* telling the current programmed settings |
8 |
* initializing a lot of stuff and loading the tcl scripts |
9 |
* |
10 |
* $Id: chanprog.c,v 1.67 2010/06/29 15:52:24 thommey Exp $ |
11 |
*/ |
12 |
/* |
13 |
* Copyright (C) 1997 Robey Pointer |
14 |
* Copyright (C) 1999 - 2010 Eggheads Development Team |
15 |
* |
16 |
* This program is free software; you can redistribute it and/or |
17 |
* modify it under the terms of the GNU General Public License |
18 |
* as published by the Free Software Foundation; either version 2 |
19 |
* of the License, or (at your option) any later version. |
20 |
* |
21 |
* This program is distributed in the hope that it will be useful, |
22 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
23 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
24 |
* GNU General Public License for more details. |
25 |
* |
26 |
* You should have received a copy of the GNU General Public License |
27 |
* along with this program; if not, write to the Free Software |
28 |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
29 |
*/ |
30 |
|
31 |
#include "main.h" |
32 |
|
33 |
#ifdef HAVE_GETRUSAGE |
34 |
# include <sys/resource.h> |
35 |
# ifdef HAVE_SYS_RUSAGE_H |
36 |
# include <sys/rusage.h> |
37 |
# endif |
38 |
#endif |
39 |
|
40 |
#ifdef HAVE_UNAME |
41 |
# include <sys/utsname.h> |
42 |
#endif |
43 |
|
44 |
#include "modules.h" |
45 |
|
46 |
extern struct userrec *userlist; |
47 |
extern log_t *logs; |
48 |
extern Tcl_Interp *interp; |
49 |
extern char ver[], botnetnick[], firewall[], motdfile[], userfile[], helpdir[], |
50 |
tempdir[], moddir[], notify_new[], owner[], configfile[]; |
51 |
extern time_t now, online_since; |
52 |
extern int backgrd, term_z, con_chan, cache_hit, cache_miss, firewallport, |
53 |
default_flags, max_logs, conmask, protect_readonly, make_userfile, |
54 |
noshare, ignore_time, max_socks; |
55 |
|
56 |
tcl_timer_t *timer = NULL; /* Minutely timer */ |
57 |
tcl_timer_t *utimer = NULL; /* Secondly timer */ |
58 |
unsigned long timer_id = 1; /* Next timer of any sort will |
59 |
* have this number */ |
60 |
struct chanset_t *chanset = NULL; /* Channel list */ |
61 |
char admin[121] = ""; /* Admin info */ |
62 |
char origbotname[NICKLEN + 1]; |
63 |
char botname[NICKLEN + 1]; /* Primary botname */ |
64 |
|
65 |
|
66 |
/* Remove leading and trailing whitespaces. |
67 |
*/ |
68 |
void rmspace(char *s) |
69 |
{ |
70 |
register char *p = NULL, *q = NULL; |
71 |
|
72 |
if (!s || !*s) |
73 |
return; |
74 |
|
75 |
/* Remove trailing whitespaces. */ |
76 |
for (q = s + strlen(s) - 1; q >= s && egg_isspace(*q); q--); |
77 |
*(q + 1) = 0; |
78 |
|
79 |
/* Remove leading whitespaces. */ |
80 |
for (p = s; egg_isspace(*p); p++); |
81 |
|
82 |
if (p != s) |
83 |
memmove(s, p, q - p + 2); |
84 |
} |
85 |
|
86 |
|
87 |
/* Returns memberfields if the nick is in the member list. |
88 |
*/ |
89 |
memberlist *ismember(struct chanset_t *chan, char *nick) |
90 |
{ |
91 |
register memberlist *x; |
92 |
|
93 |
for (x = chan->channel.member; x && x->nick[0]; x = x->next) |
94 |
if (!rfc_casecmp(x->nick, nick)) |
95 |
return x; |
96 |
return NULL; |
97 |
} |
98 |
|
99 |
/* Find a chanset by channel name as the server knows it (ie !ABCDEchannel) |
100 |
*/ |
101 |
struct chanset_t *findchan(const char *name) |
102 |
{ |
103 |
register struct chanset_t *chan; |
104 |
|
105 |
for (chan = chanset; chan; chan = chan->next) |
106 |
if (!rfc_casecmp(chan->name, name)) |
107 |
return chan; |
108 |
return NULL; |
109 |
} |
110 |
|
111 |
/* Find a chanset by display name (ie !channel) |
112 |
*/ |
113 |
struct chanset_t *findchan_by_dname(const char *name) |
114 |
{ |
115 |
register struct chanset_t *chan; |
116 |
|
117 |
for (chan = chanset; chan; chan = chan->next) |
118 |
if (!rfc_casecmp(chan->dname, name)) |
119 |
return chan; |
120 |
return NULL; |
121 |
} |
122 |
|
123 |
|
124 |
/* |
125 |
* "caching" functions |
126 |
*/ |
127 |
|
128 |
/* Shortcut for get_user_by_host -- might have user record in one |
129 |
* of the channel caches. |
130 |
*/ |
131 |
struct userrec *check_chanlist(const char *host) |
132 |
{ |
133 |
char *nick, *uhost, buf[UHOSTLEN]; |
134 |
register memberlist *m; |
135 |
register struct chanset_t *chan; |
136 |
|
137 |
strncpyz(buf, host, sizeof buf); |
138 |
uhost = buf; |
139 |
nick = splitnick(&uhost); |
140 |
for (chan = chanset; chan; chan = chan->next) |
141 |
for (m = chan->channel.member; m && m->nick[0]; m = m->next) |
142 |
if (!rfc_casecmp(nick, m->nick) && !egg_strcasecmp(uhost, m->userhost)) |
143 |
return m->user; |
144 |
return NULL; |
145 |
} |
146 |
|
147 |
/* Shortcut for get_user_by_handle -- might have user record in channels |
148 |
*/ |
149 |
struct userrec *check_chanlist_hand(const char *hand) |
150 |
{ |
151 |
register struct chanset_t *chan; |
152 |
register memberlist *m; |
153 |
|
154 |
for (chan = chanset; chan; chan = chan->next) |
155 |
for (m = chan->channel.member; m && m->nick[0]; m = m->next) |
156 |
if (m->user && !egg_strcasecmp(m->user->handle, hand)) |
157 |
return m->user; |
158 |
return NULL; |
159 |
} |
160 |
|
161 |
/* Clear the user pointers in the chanlists. |
162 |
* |
163 |
* Necessary when a hostmask is added/removed, a user is added or a new |
164 |
* userfile is loaded. |
165 |
*/ |
166 |
void clear_chanlist(void) |
167 |
{ |
168 |
register memberlist *m; |
169 |
register struct chanset_t *chan; |
170 |
|
171 |
for (chan = chanset; chan; chan = chan->next) |
172 |
for (m = chan->channel.member; m && m->nick[0]; m = m->next) { |
173 |
m->user = NULL; |
174 |
m->tried_getuser = 0; |
175 |
} |
176 |
} |
177 |
|
178 |
/* Clear the user pointer of a specific nick in the chanlists. |
179 |
* |
180 |
* Necessary when a hostmask is added/removed, a nick changes, etc. |
181 |
* Does not completely invalidate the channel cache like clear_chanlist(). |
182 |
*/ |
183 |
void clear_chanlist_member(const char *nick) |
184 |
{ |
185 |
register memberlist *m; |
186 |
register struct chanset_t *chan; |
187 |
|
188 |
for (chan = chanset; chan; chan = chan->next) |
189 |
for (m = chan->channel.member; m && m->nick[0]; m = m->next) |
190 |
if (!rfc_casecmp(m->nick, nick)) { |
191 |
m->user = NULL; |
192 |
m->tried_getuser = 0; |
193 |
break; |
194 |
} |
195 |
} |
196 |
|
197 |
/* If this user@host is in a channel, set it (it was null) |
198 |
*/ |
199 |
void set_chanlist(const char *host, struct userrec *rec) |
200 |
{ |
201 |
char *nick, *uhost, buf[UHOSTLEN]; |
202 |
register memberlist *m; |
203 |
register struct chanset_t *chan; |
204 |
|
205 |
strncpyz(buf, host, sizeof buf); |
206 |
uhost = buf; |
207 |
nick = splitnick(&uhost); |
208 |
for (chan = chanset; chan; chan = chan->next) |
209 |
for (m = chan->channel.member; m && m->nick[0]; m = m->next) |
210 |
if (!rfc_casecmp(nick, m->nick) && !egg_strcasecmp(uhost, m->userhost)) |
211 |
m->user = rec; |
212 |
} |
213 |
|
214 |
/* Calculate the memory we should be using |
215 |
*/ |
216 |
int expmem_chanprog() |
217 |
{ |
218 |
register int tot = 0; |
219 |
register tcl_timer_t *t; |
220 |
|
221 |
for (t = timer; t; t = t->next) |
222 |
tot += sizeof(tcl_timer_t) + strlen(t->cmd) + 1; |
223 |
for (t = utimer; t; t = t->next) |
224 |
tot += sizeof(tcl_timer_t) + strlen(t->cmd) + 1; |
225 |
return tot; |
226 |
} |
227 |
|
228 |
/* Dump uptime info out to dcc (guppy 9Jan99) |
229 |
*/ |
230 |
void tell_verbose_uptime(int idx) |
231 |
{ |
232 |
char s[256], s1[121]; |
233 |
time_t now2, hr, min; |
234 |
|
235 |
now2 = now - online_since; |
236 |
s[0] = 0; |
237 |
if (now2 > 86400) { |
238 |
/* days */ |
239 |
sprintf(s, "%d day", (int) (now2 / 86400)); |
240 |
if ((int) (now2 / 86400) >= 2) |
241 |
strcat(s, "s"); |
242 |
strcat(s, ", "); |
243 |
now2 -= (((int) (now2 / 86400)) * 86400); |
244 |
} |
245 |
hr = (time_t) ((int) now2 / 3600); |
246 |
now2 -= (hr * 3600); |
247 |
min = (time_t) ((int) now2 / 60); |
248 |
sprintf(&s[strlen(s)], "%02d:%02d", (int) hr, (int) min); |
249 |
s1[0] = 0; |
250 |
if (backgrd) |
251 |
strcpy(s1, MISC_BACKGROUND); |
252 |
else { |
253 |
if (term_z) |
254 |
strcpy(s1, MISC_TERMMODE); |
255 |
else if (con_chan) |
256 |
strcpy(s1, MISC_STATMODE); |
257 |
else |
258 |
strcpy(s1, MISC_LOGMODE); |
259 |
} |
260 |
dprintf(idx, "%s %s (%s)\n", MISC_ONLINEFOR, s, s1); |
261 |
} |
262 |
|
263 |
/* Dump status info out to dcc |
264 |
*/ |
265 |
void tell_verbose_status(int idx) |
266 |
{ |
267 |
char s[256], s1[121], s2[81]; |
268 |
char *vers_t, *uni_t; |
269 |
int i; |
270 |
time_t now2 = now - online_since, hr, min; |
271 |
#ifdef HAVE_GETRUSAGE |
272 |
struct rusage ru; |
273 |
#else |
274 |
# ifdef HAVE_CLOCK |
275 |
clock_t cl; |
276 |
# endif |
277 |
#endif |
278 |
#ifdef HAVE_UNAME |
279 |
struct utsname un; |
280 |
|
281 |
if (!uname(&un) < 0) { |
282 |
#endif |
283 |
vers_t = " "; |
284 |
uni_t = "*unknown*"; |
285 |
#ifdef HAVE_UNAME |
286 |
} else { |
287 |
vers_t = un.release; |
288 |
uni_t = un.sysname; |
289 |
} |
290 |
#endif |
291 |
|
292 |
i = count_users(userlist); |
293 |
dprintf(idx, "I am %s, running %s: %d user%s (mem: %uk).\n", |
294 |
botnetnick, ver, i, i == 1 ? "" : "s", |
295 |
(int) (expected_memory() / 1024)); |
296 |
|
297 |
s[0] = 0; |
298 |
if (now2 > 86400) { |
299 |
/* days */ |
300 |
sprintf(s, "%d day", (int) (now2 / 86400)); |
301 |
if ((int) (now2 / 86400) >= 2) |
302 |
strcat(s, "s"); |
303 |
strcat(s, ", "); |
304 |
now2 -= (((int) (now2 / 86400)) * 86400); |
305 |
} |
306 |
hr = (time_t) ((int) now2 / 3600); |
307 |
now2 -= (hr * 3600); |
308 |
min = (time_t) ((int) now2 / 60); |
309 |
sprintf(&s[strlen(s)], "%02d:%02d", (int) hr, (int) min); |
310 |
s1[0] = 0; |
311 |
if (backgrd) |
312 |
strcpy(s1, MISC_BACKGROUND); |
313 |
else { |
314 |
if (term_z) |
315 |
strcpy(s1, MISC_TERMMODE); |
316 |
else if (con_chan) |
317 |
strcpy(s1, MISC_STATMODE); |
318 |
else |
319 |
strcpy(s1, MISC_LOGMODE); |
320 |
} |
321 |
#ifdef HAVE_GETRUSAGE |
322 |
getrusage(RUSAGE_SELF, &ru); |
323 |
hr = (int) ((ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) / 60); |
324 |
min = (int) ((ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) - (hr * 60)); |
325 |
sprintf(s2, "CPU: %02d:%02d", (int) hr, (int) min); /* Actally min/sec */ |
326 |
#else |
327 |
# ifdef HAVE_CLOCK |
328 |
cl = (clock() / CLOCKS_PER_SEC); |
329 |
hr = (int) (cl / 60); |
330 |
min = (int) (cl - (hr * 60)); |
331 |
sprintf(s2, "CPU: %02d:%02d", (int) hr, (int) min); /* Actually min/sec */ |
332 |
# else |
333 |
sprintf(s2, "CPU: unknown"); |
334 |
# endif |
335 |
#endif |
336 |
dprintf(idx, "%s %s (%s) - %s - %s: %4.1f%%\n", MISC_ONLINEFOR, |
337 |
s, s1, s2, MISC_CACHEHIT, |
338 |
100.0 * ((float) cache_hit) / ((float) (cache_hit + cache_miss))); |
339 |
|
340 |
if (admin[0]) |
341 |
dprintf(idx, "Admin: %s\n", admin); |
342 |
|
343 |
dprintf(idx, "Config file: %s\n", configfile); |
344 |
dprintf(idx, "OS: %s %s\n", uni_t, vers_t); |
345 |
|
346 |
/* info library */ |
347 |
dprintf(idx, "%s %s\n", MISC_TCLLIBRARY, |
348 |
((interp) && (Tcl_Eval(interp, "info library") == TCL_OK)) ? |
349 |
tcl_resultstring() : "*unknown*"); |
350 |
|
351 |
/* info tclversion/patchlevel */ |
352 |
dprintf(idx, "%s %s (%s %s)\n", MISC_TCLVERSION, |
353 |
((interp) && (Tcl_Eval(interp, "info patchlevel") == TCL_OK)) ? |
354 |
tcl_resultstring() : (Tcl_Eval(interp, "info tclversion") == TCL_OK) ? |
355 |
tcl_resultstring() : "*unknown*", MISC_TCLHVERSION, |
356 |
TCL_PATCH_LEVEL ? TCL_PATCH_LEVEL : "*unknown*"); |
357 |
|
358 |
if (tcl_threaded()) |
359 |
dprintf(idx, "Tcl is threaded.\n"); |
360 |
dprintf(idx, "Socket table: %d/%d\n", threaddata()->MAXSOCKS, max_socks); |
361 |
} |
362 |
|
363 |
/* Show all internal state variables |
364 |
*/ |
365 |
void tell_settings(int idx) |
366 |
{ |
367 |
char s[1024]; |
368 |
int i; |
369 |
struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 }; |
370 |
|
371 |
dprintf(idx, "Botnet nickname: %s\n", botnetnick); |
372 |
if (firewall[0]) |
373 |
dprintf(idx, "Firewall: %s:%d\n", firewall, firewallport); |
374 |
dprintf(idx, "Userfile: %s\n", userfile); |
375 |
dprintf(idx, "Motd: %s\n", motdfile); |
376 |
dprintf(idx, "Directories:\n"); |
377 |
#ifndef STATIC |
378 |
dprintf(idx, " Help : %s\n", helpdir); |
379 |
dprintf(idx, " Temp : %s\n", tempdir); |
380 |
dprintf(idx, " Modules: %s\n", moddir); |
381 |
#else |
382 |
dprintf(idx, " Help: %s\n", helpdir); |
383 |
dprintf(idx, " Temp: %s\n", tempdir); |
384 |
#endif |
385 |
fr.global = default_flags; |
386 |
|
387 |
build_flags(s, &fr, NULL); |
388 |
dprintf(idx, "%s [%s], %s: %s\n", MISC_NEWUSERFLAGS, s, |
389 |
MISC_NOTIFY, notify_new); |
390 |
if (owner[0]) |
391 |
dprintf(idx, "%s: %s\n", MISC_PERMOWNER, owner); |
392 |
for (i = 0; i < max_logs; i++) |
393 |
if (logs[i].filename != NULL) { |
394 |
dprintf(idx, "Logfile #%d: %s on %s (%s: %s)\n", i + 1, |
395 |
logs[i].filename, logs[i].chname, |
396 |
masktype(logs[i].mask), maskname(logs[i].mask)); |
397 |
} |
398 |
dprintf(idx, "Ignores last %d minute%s.\n", ignore_time, |
399 |
(ignore_time != 1) ? "s" : ""); |
400 |
} |
401 |
|
402 |
void reaffirm_owners() |
403 |
{ |
404 |
char *p, *q, s[121]; |
405 |
struct userrec *u; |
406 |
|
407 |
/* Please stop breaking this function. */ |
408 |
if (owner[0]) { |
409 |
q = owner; |
410 |
p = strchr(q, ','); |
411 |
while (p) { |
412 |
strncpyz(s, q, (p - q) + 1); |
413 |
rmspace(s); |
414 |
u = get_user_by_handle(userlist, s); |
415 |
if (u) |
416 |
u->flags = sanity_check(u->flags | USER_OWNER); |
417 |
q = p + 1; |
418 |
p = strchr(q, ','); |
419 |
} |
420 |
strcpy(s, q); |
421 |
rmspace(s); |
422 |
u = get_user_by_handle(userlist, s); |
423 |
if (u) |
424 |
u->flags = sanity_check(u->flags | USER_OWNER); |
425 |
} |
426 |
} |
427 |
|
428 |
void chanprog() |
429 |
{ |
430 |
int i; |
431 |
FILE *f; |
432 |
char s[161], rands[8]; |
433 |
|
434 |
admin[0] = 0; |
435 |
helpdir[0] = 0; |
436 |
tempdir[0] = 0; |
437 |
conmask = 0; |
438 |
|
439 |
for (i = 0; i < max_logs; i++) |
440 |
logs[i].flags |= LF_EXPIRING; |
441 |
|
442 |
/* Turn off read-only variables (make them write-able) for rehash */ |
443 |
protect_readonly = 0; |
444 |
|
445 |
/* Now read it */ |
446 |
if (!readtclprog(configfile)) |
447 |
fatal(MISC_NOCONFIGFILE, 0); |
448 |
|
449 |
for (i = 0; i < max_logs; i++) { |
450 |
if (logs[i].flags & LF_EXPIRING) { |
451 |
if (logs[i].filename != NULL) { |
452 |
nfree(logs[i].filename); |
453 |
logs[i].filename = NULL; |
454 |
} |
455 |
if (logs[i].chname != NULL) { |
456 |
nfree(logs[i].chname); |
457 |
logs[i].chname = NULL; |
458 |
} |
459 |
if (logs[i].f != NULL) { |
460 |
fclose(logs[i].f); |
461 |
logs[i].f = NULL; |
462 |
} |
463 |
logs[i].mask = 0; |
464 |
logs[i].flags = 0; |
465 |
} |
466 |
} |
467 |
|
468 |
/* We should be safe now */ |
469 |
call_hook(HOOK_REHASH); |
470 |
protect_readonly = 1; |
471 |
|
472 |
if (!botnetnick[0]) |
473 |
strncpyz(botnetnick, origbotname, HANDLEN + 1); |
474 |
|
475 |
if (!botnetnick[0]) |
476 |
fatal("I don't have a botnet nick!!\n", 0); |
477 |
|
478 |
if (!userfile[0]) |
479 |
fatal(MISC_NOUSERFILE2, 0); |
480 |
|
481 |
if (!readuserfile(userfile, &userlist)) { |
482 |
if (!make_userfile) { |
483 |
char tmp[178]; |
484 |
|
485 |
egg_snprintf(tmp, sizeof tmp, MISC_NOUSERFILE, configfile); |
486 |
fatal(tmp, 0); |
487 |
} |
488 |
printf("\n\n%s\n", MISC_NOUSERFILE2); |
489 |
if (module_find("server", 0, 0)) |
490 |
printf(MISC_USERFCREATE1, origbotname); |
491 |
printf("%s\n\n", MISC_USERFCREATE2); |
492 |
} else if (make_userfile) { |
493 |
make_userfile = 0; |
494 |
printf("%s\n", MISC_USERFEXISTS); |
495 |
} |
496 |
|
497 |
if (helpdir[0]) |
498 |
if (helpdir[strlen(helpdir) - 1] != '/') |
499 |
strcat(helpdir, "/"); |
500 |
|
501 |
if (tempdir[0]) |
502 |
if (tempdir[strlen(tempdir) - 1] != '/') |
503 |
strcat(tempdir, "/"); |
504 |
|
505 |
/* Test tempdir: it's vital. */ |
506 |
|
507 |
/* Possible file race condition solved by using a random string |
508 |
* and the process id in the filename. |
509 |
* FIXME: This race is only partitially fixed. We could still be |
510 |
* overwriting an existing file / following a malicious |
511 |
* link. |
512 |
*/ |
513 |
make_rand_str(rands, 7); /* create random string */ |
514 |
sprintf(s, "%s.test-%u-%s", tempdir, getpid(), rands); |
515 |
f = fopen(s, "w"); |
516 |
if (f == NULL) |
517 |
fatal(MISC_CANTWRITETEMP, 0); |
518 |
fclose(f); |
519 |
unlink(s); |
520 |
reaffirm_owners(); |
521 |
check_tcl_event("userfile-loaded"); |
522 |
} |
523 |
|
524 |
/* Reload the user file from disk |
525 |
*/ |
526 |
void reload() |
527 |
{ |
528 |
if (!file_readable(userfile)) { |
529 |
putlog(LOG_MISC, "*", MISC_CANTRELOADUSER); |
530 |
return; |
531 |
} |
532 |
|
533 |
noshare = 1; |
534 |
clear_userlist(userlist); |
535 |
noshare = 0; |
536 |
userlist = NULL; |
537 |
if (!readuserfile(userfile, &userlist)) |
538 |
fatal(MISC_MISSINGUSERF, 0); |
539 |
reaffirm_owners(); |
540 |
check_tcl_event("userfile-loaded"); |
541 |
call_hook(HOOK_READ_USERFILE); |
542 |
} |
543 |
|
544 |
void rehash() |
545 |
{ |
546 |
call_hook(HOOK_PRE_REHASH); |
547 |
noshare = 1; |
548 |
clear_userlist(userlist); |
549 |
noshare = 0; |
550 |
userlist = NULL; |
551 |
chanprog(); |
552 |
} |
553 |
|
554 |
/* |
555 |
* Brief venture into timers |
556 |
*/ |
557 |
|
558 |
/* Add a timer |
559 |
*/ |
560 |
unsigned long add_timer(tcl_timer_t ** stack, int elapse, char *cmd, |
561 |
unsigned long prev_id) |
562 |
{ |
563 |
tcl_timer_t *old = (*stack); |
564 |
|
565 |
*stack = nmalloc(sizeof **stack); |
566 |
(*stack)->next = old; |
567 |
(*stack)->mins = elapse; |
568 |
(*stack)->cmd = nmalloc(strlen(cmd) + 1); |
569 |
strcpy((*stack)->cmd, cmd); |
570 |
/* If it's just being added back and already had an id, |
571 |
* don't create a new one. |
572 |
*/ |
573 |
if (prev_id > 0) |
574 |
(*stack)->id = prev_id; |
575 |
else |
576 |
(*stack)->id = timer_id++; |
577 |
return (*stack)->id; |
578 |
} |
579 |
|
580 |
/* Remove a timer, by id |
581 |
*/ |
582 |
int remove_timer(tcl_timer_t ** stack, unsigned long id) |
583 |
{ |
584 |
tcl_timer_t *old; |
585 |
int ok = 0; |
586 |
|
587 |
while (*stack) { |
588 |
if ((*stack)->id == id) { |
589 |
ok++; |
590 |
old = *stack; |
591 |
*stack = ((*stack)->next); |
592 |
nfree(old->cmd); |
593 |
nfree(old); |
594 |
} else |
595 |
stack = &((*stack)->next); |
596 |
} |
597 |
return ok; |
598 |
} |
599 |
|
600 |
/* Check timers, execute the ones that have expired. |
601 |
*/ |
602 |
void do_check_timers(tcl_timer_t ** stack) |
603 |
{ |
604 |
tcl_timer_t *mark = *stack, *old = NULL; |
605 |
char x[16]; |
606 |
|
607 |
/* New timers could be added by a Tcl script inside a current timer |
608 |
* so i'll just clear out the timer list completely, and add any |
609 |
* unexpired timers back on. |
610 |
*/ |
611 |
*stack = NULL; |
612 |
while (mark) { |
613 |
if (mark->mins > 0) |
614 |
mark->mins--; |
615 |
old = mark; |
616 |
mark = mark->next; |
617 |
if (!old->mins) { |
618 |
egg_snprintf(x, sizeof x, "timer%lu", old->id); |
619 |
do_tcl(x, old->cmd); |
620 |
nfree(old->cmd); |
621 |
nfree(old); |
622 |
} else { |
623 |
old->next = *stack; |
624 |
*stack = old; |
625 |
} |
626 |
} |
627 |
} |
628 |
|
629 |
/* Wipe all timers. |
630 |
*/ |
631 |
void wipe_timers(Tcl_Interp *irp, tcl_timer_t **stack) |
632 |
{ |
633 |
tcl_timer_t *mark = *stack, *old; |
634 |
|
635 |
while (mark) { |
636 |
old = mark; |
637 |
mark = mark->next; |
638 |
nfree(old->cmd); |
639 |
nfree(old); |
640 |
} |
641 |
*stack = NULL; |
642 |
} |
643 |
|
644 |
/* Return list of timers |
645 |
*/ |
646 |
void list_timers(Tcl_Interp *irp, tcl_timer_t *stack) |
647 |
{ |
648 |
char mins[10], id[16], *x; |
649 |
EGG_CONST char *argv[3]; |
650 |
tcl_timer_t *mark; |
651 |
|
652 |
for (mark = stack; mark; mark = mark->next) { |
653 |
egg_snprintf(mins, sizeof mins, "%u", mark->mins); |
654 |
egg_snprintf(id, sizeof id, "timer%lu", mark->id); |
655 |
argv[0] = mins; |
656 |
argv[1] = mark->cmd; |
657 |
argv[2] = id; |
658 |
x = Tcl_Merge(3, argv); |
659 |
Tcl_AppendElement(irp, x); |
660 |
Tcl_Free((char *) x); |
661 |
} |
662 |
} |
663 |
|
664 |
/* Oddly enough, written by Sup (former(?) Eggdrop coder) |
665 |
*/ |
666 |
int isowner(char *name) |
667 |
{ |
668 |
register char *ptr = NULL, *s = NULL, *n = NULL; |
669 |
|
670 |
if (!name) |
671 |
return 0; |
672 |
|
673 |
ptr = owner - 1; |
674 |
|
675 |
do { |
676 |
ptr++; |
677 |
if (*ptr && !egg_isspace(*ptr) && *ptr != ',') { |
678 |
if (!s) |
679 |
s = ptr; |
680 |
} else if (s) { |
681 |
for (n = name; *n && *s && s < ptr && |
682 |
tolower((unsigned) *n) == tolower((unsigned) *s); n++, s++); |
683 |
|
684 |
if (s == ptr && !*n) |
685 |
return 1; |
686 |
|
687 |
s = NULL; |
688 |
} |
689 |
} while (*ptr); |
690 |
|
691 |
return 0; |
692 |
} |