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