32
32
#include <sys/stat.h>
33
33
#include <unistd.h>
36
37
#include "tandem.h"
37
38
#include "modules.h"
39
41
# include <sys/utsname.h>
43
extern struct dcc_t *dcc;
44
extern struct chanset_t *chanset;
45
extern char helpdir[], version[], origbotname[], botname[],
46
admin[], network[], motdfile[], ver[], botnetnick[],
47
bannerfile[], logfile_suffix[], textdir[];
48
extern int backgrd, con_chan, term_z, use_stderr, dcc_total,
49
keep_all_logs, quick_logs, strict_host;
51
extern Tcl_Interp *interp;
54
int shtime = 1; /* Whether or not to display the time
55
with console output */
56
log_t *logs = 0; /* Logfiles */
57
int max_logs = 5; /* Current maximum log files */
58
int max_logsize = 0; /* Maximum logfile size, 0 for no limit */
59
int conmask = LOG_MODES | LOG_CMDS | LOG_MISC; /* Console mask */
60
int debug_output = 0; /* Disply output to server to LOG_SERVEROUT */
46
extern struct dcc_t *dcc;
47
extern struct chanset_t *chanset;
49
extern char helpdir[], version[], origbotname[], botname[], admin[], network[],
50
motdfile[], ver[], botnetnick[], bannerfile[], logfile_suffix[],
52
extern int backgrd, con_chan, term_z, use_stderr, dcc_total, keep_all_logs,
53
quick_logs, strict_host;
56
extern Tcl_Interp *interp;
59
int shtime = 1; /* Display the time with console output */
60
log_t *logs = 0; /* Logfiles */
61
int max_logs = 5; /* Current maximum log files */
62
int max_logsize = 0; /* Maximum logfile size, 0 for no limit */
63
int raw_log = 0; /* Disply output to server to LOG_SERVEROUT */
65
int conmask = LOG_MODES | LOG_CMDS | LOG_MISC; /* Console mask */
62
67
struct help_list_t {
63
68
struct help_list_t *next;
133
/* This implementation wont overrun dst - 'max' is the max bytes that dst
134
* can be, including the null terminator. So if 'dst' is a 128 byte buffer,
135
* pass 128 as 'max'. The function will _always_ null-terminate 'dst'.
137
* Returns: The number of characters appended to 'dst'.
142
* size_t bufsize = sizeof(buf);
144
* buf[0] = 0, bufsize--;
146
* while (blah && bufsize) {
147
* bufsize -= egg_strcatn(buf, <some-long-string>, sizeof(buf));
138
/* This implementation wont overrun dst - 'max' is the max bytes that dst
139
* can be, including the null terminator. So if 'dst' is a 128 byte buffer,
140
* pass 128 as 'max'. The function will _always_ null-terminate 'dst'.
142
* Returns: The number of characters appended to 'dst'.
147
* size_t bufsize = sizeof(buf);
149
* buf[0] = 0, bufsize--;
151
* while (blah && bufsize) {
152
* bufsize -= egg_strcatn(buf, <some-long-string>, sizeof(buf));
152
157
int egg_strcatn(char *dst, const char *src, size_t max)
342
361
for (f = e; *f; f++);
344
if (*f >= '0' && *f <= '9') { /* Numeric IP address */
347
strncpy(nw, q, f - q);
348
/* No need to nw[f-q] = 0 here, as the strcpy below will
349
* terminate the string for us.
353
} else { /* Normal host >= 3 parts */
355
* a.b.c.d -> *.b.c.d if tld is a country (2 chars)
356
* OR *.c.d if tld is com/edu/etc (3 chars)
357
* a.b.c.d.e -> *.c.d.e etc
359
const char *x = strchr(e + 1, '.');
363
if (*f >= '0' && *f <= '9') { /* Numeric IP address */
366
strncpy(nw, q, f - q);
367
/* No need to nw[f-q] = 0 here, as the strcpy below will
368
* terminate the string for us.
372
} else { /* Normal host >= 3 parts */
374
* a.b.c.d -> *.b.c.d if tld is a country (2 chars)
375
* OR *.c.d if tld is com/edu/etc (3 chars)
376
* a.b.c.d.e -> *.c.d.e etc
378
const char *x = strchr(e + 1, '.');
363
else if (strchr(x + 1, '.'))
365
else if (strlen(x) == 3)
369
sprintf(nw, "*%s", x);
382
else if (strchr(x + 1, '.'))
384
else if (strlen(x) == 3)
388
sprintf(nw, "*%s", x);
482
501
void putlog EGG_VARARGS_DEF(int, arg1)
485
char *format, *chname, s[LOGLINELEN], s1[256], *out;
488
struct tm *t = localtime(&now);
503
int i, type, tsl = 0;
504
char *format, *chname, s[LOGLINELEN], s1[256], *out, ct[81], *s2, stamp[34];
506
time_t now2 = time(NULL);
507
struct tm *t = localtime(&now2);
491
509
type = EGG_VARARGS_START(int, arg1, va);
492
510
chname = va_arg(va, char *);
493
511
format = va_arg(va, char *);
495
/* Format log entry at offset 8, then i can prepend the timestamp */
513
/* Create the timestamp */
514
t = localtime(&now2);
516
egg_strftime(stamp, sizeof(stamp) - 2, LOG_TS, t);
523
/* Format log entry at offset 'tsl,' then i can prepend the timestamp */
497
525
/* No need to check if out should be null-terminated here,
498
526
* just do it! <cybah>
500
egg_vsnprintf(out, LOGLINEMAX - 8, format, va);
501
out[LOGLINEMAX - 8] = 0;
528
egg_vsnprintf(out, LOGLINEMAX - tsl, format, va);
529
out[LOGLINEMAX - tsl] = 0;
503
530
if (keep_all_logs) {
504
531
if (!logfile_suffix[0])
505
egg_strftime(ct, 12, ".%d%b%Y", localtime(&tt));
532
egg_strftime(ct, 12, ".%d%b%Y", t);
507
egg_strftime(ct, 80, logfile_suffix, localtime(&tt));
534
egg_strftime(ct, 80, logfile_suffix, t);
510
537
/* replace spaces by underscores */
518
if ((out[0]) && (shtime)) {
519
egg_strftime(s1, 9, "[%H:%M] ", localtime(&tt));
520
strncpy(&s[0], s1, 8);
545
/* Place the timestamp in the string to be printed */
546
if (out[0] && shtime) {
547
strncpy(s, stamp, tsl);
523
550
strcat(out, "\n");
524
551
if (!use_stderr) {
525
552
for (i = 0; i < max_logs; i++) {
526
553
if ((logs[i].filename != NULL) && (logs[i].mask & type) &&
527
((chname[0] == '*') || (logs[i].chname[0] == '*') ||
528
(!rfc_casecmp(chname, logs[i].chname)))) {
529
if (logs[i].f == NULL) {
530
/* Open this logfile */
532
egg_snprintf(s1, 256, "%s%s", logs[i].filename, ct);
533
logs[i].f = fopen(s1, "a+");
535
logs[i].f = fopen(logs[i].filename, "a+");
537
if (logs[i].f != NULL) {
538
/* Check if this is the same as the last line added to
541
if (!egg_strcasecmp(out + 8, logs[i].szlast)) {
542
/* It is a repeat, so increment repeats */
545
/* Not a repeat, check if there were any repeat
546
* lines previously...
548
if (logs[i].repeats > 0) {
549
/* Yep.. so display 'last message repeated x times'
550
* then reset repeats. We want the current time here,
551
* so put that in the file first.
554
fprintf(logs[i].f, "[%2.2d:%2.2d] ", t->tm_hour, t->tm_min);
555
fprintf(logs[i].f, MISC_LOGREPEAT, logs[i].repeats);
557
fprintf(logs[i].f, "[??:??] ");
558
fprintf(logs[i].f, MISC_LOGREPEAT, logs[i].repeats);
561
/* No need to reset logs[i].szlast here
562
* because we update it later on...
565
fputs(out, logs[i].f);
566
strncpyz(logs[i].szlast, out + 8, LOGLINEMAX);
554
((chname[0] == '*') || (logs[i].chname[0] == '*') ||
555
(!rfc_casecmp(chname, logs[i].chname)))) {
556
if (logs[i].f == NULL) {
557
/* Open this logfile */
559
egg_snprintf(s1, 256, "%s%s", logs[i].filename, ct);
560
logs[i].f = fopen(s1, "a+");
562
logs[i].f = fopen(logs[i].filename, "a+");
564
if (logs[i].f != NULL) {
565
/* Check if this is the same as the last line added to
568
if (!egg_strcasecmp(out + tsl, logs[i].szlast))
569
/* It is a repeat, so increment repeats */
572
/* Not a repeat, check if there were any repeat
573
* lines previously...
575
if (logs[i].repeats > 0) {
576
/* Yep.. so display 'last message repeated x times'
577
* then reset repeats. We want the current time here,
578
* so put that in the file first.
580
fprintf(logs[i].f, stamp);
581
fprintf(logs[i].f, MISC_LOGREPEAT, logs[i].repeats);
583
/* No need to reset logs[i].szlast here
584
* because we update it later on...
587
fputs(out, logs[i].f);
588
strncpyz(logs[i].szlast, out + tsl, LOGLINEMAX);
572
for (i = 0; i < dcc_total; i++)
594
for (i = 0; i < dcc_total; i++) {
573
595
if ((dcc[i].type == &DCC_CHAT) && (dcc[i].u.chat->con_flags & type)) {
574
596
if ((chname[0] == '*') || (dcc[i].u.chat->con_chan[0] == '*') ||
575
(!rfc_casecmp(chname, dcc[i].u.chat->con_chan)))
576
dprintf(i, "%s", out);
597
!rfc_casecmp(chname, dcc[i].u.chat->con_chan)) {
598
dprintf(i, "%s", out);
578
if ((!backgrd) && (!con_chan) && (!term_z))
602
if (!backgrd && !con_chan && !term_z)
579
603
dprintf(DP_STDOUT, "%s", out);
580
604
else if ((type & LOG_MISC) && use_stderr) {
583
607
dprintf(DP_STDERR, "%s", s);
617
char buf[1024]; /* Should be plenty */
646
char buf[1024]; /* Should be plenty */
619
648
if (!keep_all_logs && max_logsize > 0) {
620
649
for (i = 0; i < max_logs; i++) {
621
650
if (logs[i].filename) {
622
if (stat(logs[i].filename, &ss) != 0) {
625
if ((ss.st_size >> 10) > max_logsize) {
627
/* write to the log before closing it huh.. */
628
putlog(LOG_MISC, "*", MISC_CLOGS, logs[i].filename, ss.st_size);
651
if (stat(logs[i].filename, &ss) != 0) {
654
if ((ss.st_size >> 10) > max_logsize) {
656
/* write to the log before closing it huh.. */
657
putlog(LOG_MISC, "*", MISC_CLOGS, logs[i].filename, ss.st_size);
634
egg_snprintf(buf, sizeof buf, "%s.yesterday", logs[i].filename);
637
movefile(logs[i].filename, buf);
663
egg_snprintf(buf, sizeof buf, "%s.yesterday", logs[i].filename);
666
movefile(logs[i].filename, buf);
658
686
for (i = 0; i < max_logs; i++) {
659
687
if (logs[i].f != NULL) {
660
if ((logs[i].repeats > 0) && quick_logs) {
661
/* Repeat.. if quicklogs used then display 'last message
662
* repeated x times' and reset repeats.
665
fprintf(logs[i].f, "[%2.2d:%2.2d] ", t->tm_hour, t->tm_min);
666
fprintf(logs[i].f, MISC_LOGREPEAT, logs[i].repeats);
668
fprintf(logs[i].f, "[??:??] ");
669
fprintf(logs[i].f, MISC_LOGREPEAT, logs[i].repeats);
688
if ((logs[i].repeats > 0) && quick_logs) {
689
/* Repeat.. if quicklogs used then display 'last message
690
* repeated x times' and reset repeats.
694
egg_strftime(stamp, sizeof(stamp) - 1, LOG_TS, localtime(&now));
695
fprintf(logs[i].f, "%s ", stamp);
696
fprintf(logs[i].f, MISC_LOGREPEAT, logs[i].repeats);
674
700
fflush(logs[i].f);
796
823
if (glob_hilite(*flags)) {
797
if (help_flags & HELP_IRC) {
799
} else if (help_flags & HELP_BOLD) {
800
help_flags &= ~HELP_BOLD;
803
help_flags |= HELP_BOLD;
824
if (help_flags & HELP_IRC) {
826
} else if (help_flags & HELP_BOLD) {
827
help_flags &= ~HELP_BOLD;
830
help_flags |= HELP_BOLD;
809
836
if (glob_hilite(*flags)) {
810
if (help_flags & HELP_IRC) {
812
} else if (help_flags & HELP_REV) {
813
help_flags &= ~HELP_REV;
816
help_flags |= HELP_REV;
837
if (help_flags & HELP_IRC) {
839
} else if (help_flags & HELP_REV) {
840
help_flags &= ~HELP_REV;
843
help_flags |= HELP_REV;
822
849
if (glob_hilite(*flags)) {
823
if (help_flags & HELP_IRC) {
825
} else if (help_flags & HELP_UNDER) {
826
help_flags &= ~HELP_UNDER;
829
help_flags |= HELP_UNDER;
850
if (help_flags & HELP_IRC) {
852
} else if (help_flags & HELP_UNDER) {
853
help_flags &= ~HELP_UNDER;
856
help_flags |= HELP_UNDER;
835
862
if (glob_hilite(*flags)) {
836
if (help_flags & HELP_FLASH) {
837
if (help_flags & HELP_IRC) {
838
towrite = "\002\037";
842
help_flags &= ~HELP_FLASH;
844
help_flags |= HELP_FLASH;
845
if (help_flags & HELP_IRC) {
846
towrite = "\037\002";
863
if (help_flags & HELP_FLASH) {
864
if (help_flags & HELP_IRC)
865
towrite = "\002\037";
868
help_flags &= ~HELP_FLASH;
870
help_flags |= HELP_FLASH;
871
if (help_flags & HELP_IRC)
872
towrite = "\037\002";
854
879
#ifdef HAVE_UNAME
855
if (!uname(&uname_info)) {
856
egg_snprintf(sub, sizeof sub, "%s %s", uname_info.sysname,
880
if (uname(&uname_info) >= 0) {
881
egg_snprintf(sub, sizeof sub, "%s %s", uname_info.sysname,
861
towrite = "*UNKNOWN*";
886
towrite = "*UNKNOWN*";
864
889
towrite = (isdcc ? botnetnick : botname);
883
908
towrite = strchr(nick, ':');
891
for (chan = chanset; chan; chan = chan->next) {
892
if ((strlen(chan->dname) + writeidx + 2) >=
893
(s + HELP_BUF_LEN)) {
894
strncpy(writeidx, chan->dname, (s + HELP_BUF_LEN) - writeidx);
898
writeidx += my_strcpy(writeidx, chan->dname);
916
for (chan = chanset; chan; chan = chan->next) {
917
if ((strlen(chan->dname) + writeidx + 2) >= (s + HELP_BUF_LEN)) {
918
strncpy(writeidx, chan->dname, (s + HELP_BUF_LEN) - writeidx);
922
writeidx += my_strcpy(writeidx, chan->dname);
908
932
while ((*current != '}') && (*current))
914
/* Now q is the string and p is where the rest of the fcn expects */
915
if (!strncmp(q, "help=", 5)) {
916
if (topic && egg_strcasecmp(q + 5, topic))
920
} else if (!(blind & 2)) {
922
struct flag_record fr =
923
{FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0};
925
break_down_flags(q + 1, &fr, NULL);
926
if (!flagrec_ok(&fr, flags))
930
} else if (q[0] == '-') {
932
} else if (!egg_strcasecmp(q, "end")) {
937
subst_addcol(sub, "\377");
943
} else if (!egg_strcasecmp(q, "center"))
945
else if (!strncmp(q, "cols=", 5)) {
950
colstr = (char *) nmalloc(1);
952
r = strchr(q + 5, '/');
954
subwidth = atoi(r + 1);
938
/* Now q is the string and p is where the rest of the fcn expects */
939
if (!strncmp(q, "help=", 5)) {
940
if (topic && egg_strcasecmp(q + 5, topic))
944
} else if (!(blind & 2)) {
946
struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
948
break_down_flags(q + 1, &fr, NULL);
949
if (!flagrec_ok(&fr, flags))
953
} else if (q[0] == '-')
955
else if (!egg_strcasecmp(q, "end")) {
960
subst_addcol(sub, "\377");
966
} else if (!egg_strcasecmp(q, "center"))
968
else if (!strncmp(q, "cols=", 5)) {
973
colstr = (char *) nmalloc(1);
975
r = strchr(q + 5, '/');
977
subwidth = atoi(r + 1);
958
current = q; /* no } so ignore */
981
current = q; /* no } so ignore */
963
if (writeidx >= (s + HELP_BUF_LEN)) {
986
if (writeidx >= (s + HELP_BUF_LEN)) {
969
992
if (towrite && !blind) {
970
993
if ((writeidx + strlen(towrite)) >= (s + HELP_BUF_LEN)) {
971
strncpy(writeidx, towrite, (s + HELP_BUF_LEN) - writeidx);
994
strncpy(writeidx, towrite, (s + HELP_BUF_LEN) - writeidx);
975
998
writeidx += my_strcpy(writeidx, towrite);
1129
1152
struct help_list_t *item;
1131
1154
/* Somewhere here goes the eventual substituation */
1132
if (!(dcc & HELP_TEXT))
1155
if (!(dcc & HELP_TEXT)) {
1134
1156
for (current = help_list; current; current = current->next)
1135
1157
for (item = current->first; item; item = item->next)
1136
if (!strcmp(item->name, file)) {
1137
if (!item->type && !dcc) {
1138
egg_snprintf(s, sizeof s, "%smsg/%s", helpdir, current->name);
1139
if ((f = fopen(s, "r")))
1141
} else if (dcc && item->type) {
1142
if (item->type == 1)
1143
egg_snprintf(s, sizeof s, "%s%s", helpdir, current->name);
1145
egg_snprintf(s, sizeof s, "%sset/%s", helpdir, current->name);
1146
if ((f = fopen(s, "r")))
1158
if (!strcmp(item->name, file)) {
1159
if (!item->type && !dcc) {
1160
egg_snprintf(s, sizeof s, "%smsg/%s", helpdir, current->name);
1161
if ((f = fopen(s, "r")))
1163
} else if (dcc && item->type) {
1164
if (item->type == 1)
1165
egg_snprintf(s, sizeof s, "%s%s", helpdir, current->name);
1167
egg_snprintf(s, sizeof s, "%sset/%s", helpdir, current->name);
1168
if ((f = fopen(s, "r")))
1150
1172
/* No match was found, so we better return NULL */
1165
1187
FILE *f = resolve_help(fl, file);
1168
help_subst(NULL, NULL, 0, HELP_IRC, NULL); /* Clear flags */
1190
help_subst(NULL, NULL, 0, HELP_IRC, NULL); /* Clear flags */
1169
1191
while (!feof(f)) {
1170
1192
fgets(s, HELP_BUF_LEN, f);
1171
1193
if (!feof(f)) {
1172
if (s[strlen(s) - 1] == '\n')
1173
s[strlen(s) - 1] = 0;
1176
help_subst(s, who, flags, 0, file);
1177
if ((s[0]) && (strlen(s) > 1)) {
1178
dprintf(DP_HELP, "NOTICE %s :%s\n", who, s);
1194
if (s[strlen(s) - 1] == '\n')
1195
s[strlen(s) - 1] = 0;
1198
help_subst(s, who, flags, 0, file);
1199
if ((s[0]) && (strlen(s) > 1)) {
1200
dprintf(DP_HELP, "NOTICE %s :%s\n", who, s);
1189
1211
static int display_tellhelp(int idx, char *file, FILE *f,
1190
struct flag_record *flags)
1212
struct flag_record *flags)
1192
1214
char s[HELP_BUF_LEN + 1];
1196
1218
help_subst(NULL, NULL, 0,
1197
(dcc[idx].status & STAT_TELNET) ? 0 : HELP_IRC, NULL);
1219
(dcc[idx].status & STAT_TELNET) ? 0 : HELP_IRC, NULL);
1198
1220
while (!feof(f)) {
1199
1221
fgets(s, HELP_BUF_LEN, f);
1200
1222
if (!feof(f)) {
1201
if (s[strlen(s) - 1] == '\n')
1202
s[strlen(s) - 1] = 0;
1205
help_subst(s, dcc[idx].nick, flags, 1, file);
1207
dprintf(idx, "%s\n", s);
1223
if (s[strlen(s) - 1] == '\n')
1224
s[strlen(s) - 1] = 0;
1227
help_subst(s, dcc[idx].nick, flags, 1, file);
1229
dprintf(idx, "%s\n", s);
1238
1260
for (current = help_list; current; current = current->next)
1239
1261
for (item = current->first; item; item = item->next)
1240
1262
if (wild_match(match, item->name) && item->type) {
1241
if (item->type == 1)
1242
egg_snprintf(s, sizeof s, "%s%s", helpdir, current->name);
1244
egg_snprintf(s, sizeof s, "%sset/%s", helpdir, current->name);
1245
if ((f = fopen(s, "r")))
1246
display_tellhelp(idx, item->name, f, flags);
1263
if (item->type == 1)
1264
egg_snprintf(s, sizeof s, "%s%s", helpdir, current->name);
1266
egg_snprintf(s, sizeof s, "%sset/%s", helpdir, current->name);
1267
if ((f = fopen(s, "r")))
1268
display_tellhelp(idx, item->name, f, flags);
1249
1271
dprintf(idx, "%s\n", IRC_NOHELP2);
1263
1285
for (item = current->first; item; item = item->next)
1264
1286
if (!strcmp(match, item->name) && item->type) {
1266
if (item->type == 1)
1267
egg_snprintf(s, sizeof s, "%s%s", helpdir, current->name);
1269
egg_snprintf(s, sizeof s, "%sset/%s", helpdir, current->name);
1270
if ((f = fopen(s, "r")))
1271
display_tellhelp(idx, item->name, f, flags);
1288
if (item->type == 1)
1289
egg_snprintf(s, sizeof s, "%s%s", helpdir, current->name);
1291
egg_snprintf(s, sizeof s, "%sset/%s", helpdir, current->name);
1292
if ((f = fopen(s, "r")))
1293
display_tellhelp(idx, item->name, f, flags);
1274
1296
dprintf(idx, "%s\n", IRC_NOHELP2);
1323
1346
dprintf(idx, "\n");
1324
1347
/* reset the help_subst variables to their defaults */
1325
1348
help_subst(NULL, NULL, 0,
1326
(dcc[idx].status & STAT_TELNET) ? 0 : HELP_IRC, NULL);
1349
(dcc[idx].status & STAT_TELNET) ? 0 : HELP_IRC, NULL);
1327
1350
while (!feof(vv)) {
1328
1351
fgets(s, 120, vv);
1329
1352
if (!feof(vv)) {
1330
1353
if (s[strlen(s) - 1] == '\n')
1331
s[strlen(s) - 1] = 0;
1354
s[strlen(s) - 1] = 0;
1334
1357
help_subst(s, dcc[idx].nick, &fr, 1, botnetnick);
1336
dprintf(idx, "%s\n", s);
1359
dprintf(idx, "%s\n", s);
1340
1363
dprintf(idx, "\n");
1343
/* Show banner to telnet user
1366
/* Show banner to telnet user
1345
void show_banner(int idx) {
1368
void show_banner(int idx)
1348
struct flag_record fr = {FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0};
1372
struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
1350
1374
if (!is_file(bannerfile))