/* misc.c -- handles: stristr() split() maskhost() copyfile() movefile() fixfrom() dumplots() daysago() days() daysdur() logging things queueing output for the bot (msg and help) resync buffers for sharebots help system motd display and %var substitution dprintf'ized, 12dec95 */ /* This file is part of the eggdrop source code copyright (c) 1997 Robey Pointer and is distributed according to the GNU general public license. For full details, read the top of 'main.c' or the file called COPYING that was distributed with this code. */ #include "main.h" #include #include #include #include "chan.h" extern int serv; extern char notefile[]; extern int dcc_total; extern struct dcc_t * dcc; extern char helpdir[]; extern char version[]; extern char botname[]; extern char admin[]; extern int require_p; extern int backgrd; extern int con_chan; extern int term_z; extern int use_stderr; extern char motdfile[]; extern char ver[]; extern char textdir[]; extern int strict_host; extern int keep_all_logs; extern char botnetnick[]; extern struct chanset_t *chanset; extern time_t now; /* whether or not to display the time with console output */ int shtime = 1; /* logfiles */ log_t * logs = 0; /* current maximum log files */ int max_logs = 5; /* console mask */ int conmask = LOG_MODES | LOG_CMDS | LOG_MISC; /* total messages queued on main queue */ int mtot = 0; /* total messages queued on help queue */ int htot = 0; /* maximum messages to store in each queue */ int maxqmsg = 300; struct msgq { int sock; char *msg; struct msgq *next; } *mq = NULL, *hq = NULL; /* expected memory usage */ int expmem_misc() { int tot = max_logs * sizeof(log_t); struct msgq *m = mq, *h = hq; context; while (m != NULL) { tot += strlen(m->msg) + 1; tot += sizeof(struct msgq); m = m->next; } while (h != NULL) { tot += strlen(h->msg) + 1; tot += sizeof(struct msgq); h = h->next; } return tot; } void init_misc() { static int last = 0; if (max_logs < 1) max_logs = 1; if (logs) logs = nrealloc(logs,max_logs * sizeof(log_t)); else logs = nmalloc(max_logs * sizeof(log_t)); for (; last < max_logs; last++) { logs[last].filename = logs[last].chname = NULL; logs[last].mask = 0; logs[last].f = NULL; } } /***** MISC FUNCTIONS *****/ /* low-level stuff for other modules */ static int is_file (char * s) { struct stat ss; int i = stat(s, &ss); if (i < 0) return 0; if ((ss.st_mode & S_IFREG) || (ss.st_mode & S_IFLNK)) return 1; return 0; } #define upcase(c) (((c)>='a' && (c)<='z') ? (c)-'a'+'A' : (c)) /* determine if littles is contained in bigs (ignoring case) */ /* if so: return pointer to the littles in bigs */ /* if not: return NULL */ char *stristr (char * bigs, char * littles) { char *st = bigs, *p, *q; while (1) { if (!*st) return NULL; p = littles; q = st; while ((*p) && (*q) && (upcase(*p) == upcase(*q))) { p++; q++; } if ((!*q) && (*p)) return NULL; /* premature end of bigs */ if (!*p) return st; /* found it! */ st++; /* try again */ } } #if !HAVE_STRCASECMP /* unixware has no strcasecmp() without linking in a hefty library */ int strcasecmp (char * s1, char * s2) { while ((*s1) && (*s2) && (upcase(*s1) == upcase(*s2))) { s1++; s2++; } return upcase(*s1) - upcase(*s2); } #endif /* split first word off of rest and put it in first */ void splitc (char * first, char * rest, char divider) { char *p; p = strchr(rest, divider); if (p == NULL) { if ((first != rest) && (first != NULL)) first[0] = 0; return; } *p = 0; if (first != NULL) strcpy(first, rest); if (first != rest) strcpy(rest, p + 1); } #ifdef EBUG /* return the index'd word without changing 'rest' */ void stridx (char * first, char * rest, int index) { char s[510]; int i; context; strcpy(s, rest); for (i = 0; i < index; i++) { splitc(first, s, ' '); rmspace(s); } } #endif void nsplit (char * first, char * rest) { split(first, rest); if (first != NULL) if (!first[0]) { strcpy(first, rest); rest[0] = 0; } } /* convert "abc!user@a.b.host" into "*!user@*.b.host" or "abc!user@1.2.3.4" into "*!user@1.2.3.*" */ void maskhost (char * s, char * nw) { char *p, *q, xx[150]; strcpy(xx, s); p = strchr(s, '!'); if (p != NULL) { /* copy username over, quoting '?' and '*' */ char *dest = xx, *src = p + 1; while (*src) { if ((*src == '*') || (*src == '?')) *dest++ = '\\'; *dest++ = *src++; } *dest = 0; if (strlen(dest) > 10) { /* truncate */ p = strchr(s, '@'); if (p != NULL) { if (*(dest + 8) == '\\') { *(dest + 8) = '*'; strcpy(dest + 9, p); } else { *(dest + 9) = '*'; strcpy(dest + 10, p); } } } } p = strchr(xx, '@'); if (p != NULL) { q = strchr(p, '.'); if (q == NULL) { /* form xx@yy -> very bizarre */ sprintf(nw, "*!%s", xx); return; } if (strchr(q + 1, '.') == NULL) { /* form xx@yy.com -> don't truncate */ sprintf(nw, "*!%s", xx); return; } if ((xx[strlen(xx) - 1] >= '0') && (xx[strlen(xx) - 1] <= '9')) { /* ip number -> xx@#.#.#.* */ q = strrchr(p, '.'); if (q != NULL) strcpy(q, ".*"); sprintf(nw, "*!%s", xx); return; } /* form xx@yy.zz.etc.edu or whatever -> xx@*.zz.etc.edu */ if (q != NULL) { *(p + 1) = '*'; strcpy(p + 2, q); } sprintf(nw, "*!%s", xx); } else strcpy(nw, "*"); } /* copy a file from one place to another (possibly erasing old copy) */ /* returns 0 if OK, 1 if can't open original file, 2 if can't open new */ /* file, 3 if original file isn't normal, 4 if ran out of disk space */ int copyfile (char * oldpath, char * newpath) { int fi, fo, x; char buf[512]; struct stat st; fi = open(oldpath, O_RDONLY, 0); if (fi < 0) return 1; fstat(fi, &st); if (!(st.st_mode & S_IFREG)) return 3; fo = creat(newpath, (int) (st.st_mode & 0777)); if (fo < 0) { close(fi); return 2; } for (x = 1; x > 0;) { x = read(fi, buf, 512); if (x > 0) { if (write(fo, buf, x) < x) { /* couldn't write */ close(fo); close(fi); unlink(newpath); return 4; } } } close(fo); close(fi); return 0; } int movefile (char * oldpath, char * newpath) { int x = copyfile(oldpath, newpath); if (x == 0) unlink(oldpath); return x; } /* make nick!~user@host into nick!user@host if necessary */ /* also the new form: nick!+user@host or nick!-user@host */ void fixfrom (char * s) { char nick[NICKLEN], from[UHOSTLEN]; if (strict_host) return; if (s == NULL) return; if (strchr(s, '@') == NULL) return; strcpy(from, s); splitnick(nick, from); /* these are ludicrous. */ if (strchr("~+-^=", from[0]) != NULL) strcpy(from, &from[1]); sprintf(s, "%s!%s", nick, from); } /* dump a potentially super-long string of text */ /* assume prefix 20 chars or less */ void dumplots (int idx, char * prefix, char * data) { char *p = data, *q, *n, c; if (!(*data)) { dprintf(idx, "%s\n", prefix); return; } while (strlen(p) > 480) { q = p + 480; /* search for embedded linefeed first */ n = strchr(p, '\n'); if ((n != NULL) && (n < q)) { /* great! dump that first line then start over */ *n = 0; dprintf(idx, "%s%s\n", prefix, p); *n = '\n'; p = n + 1; } else { /* search backwards for the last space */ while ((*q != ' ') && (q != p)) q--; if (q == p) q = p + 480; /* ^ 1 char will get squashed cos there was no space -- too bad */ c = *q; *q = 0; dprintf(idx, "%s%s\n", prefix, p); *q = c; p = q + 1; } } /* last trailing bit: split by linefeeds if possible */ n = strchr(p, '\n'); while (n != NULL) { *n = 0; dprintf(idx, "%s%s\n", prefix, p); *n = '\n'; p = n + 1; n = strchr(p, '\n'); } if (*p) dprintf(idx, "%s%s\n", prefix, p); /* last trailing bit */ } /* convert an interval (in seconds) to one of: */ /* "19 days ago", "1 day ago", "18:12" */ void daysago (time_t now, time_t then, char * out) { char s[81]; if (now - then > 86400) { int days = (now - then) / 86400; sprintf(out, "%d day%s ago", days, (days == 1) ? "" : "s"); return; } strcpy(s, ctime(&then)); s[16] = 0; strcpy(out, &s[11]); } /* convert an interval (in seconds) to one of: */ /* "in 19 days", "in 1 day", "at 18:12" */ void days (time_t now, time_t then, char * out) { char s[81]; if (now - then > 86400) { int days = (now - then) / 86400; sprintf(out, "in %d day%s", days, (days == 1) ? "" : "s"); return; } strcpy(out, "at "); strcpy(s, ctime(&now)); s[16] = 0; strcpy(&out[3], &s[11]); } /* convert an interval (in seconds) to one of: */ /* "for 19 days", "for 1 day", "for 09:10" */ void daysdur (time_t now, time_t then, char * out) { char s[81]; int hrs, mins; if (now - then > 86400) { int days = (now - then) / 86400; sprintf(out, "for %d day%s", days, (days == 1) ? "" : "s"); return; } strcpy(out, "for "); now -= then; hrs = (int) (now / 3600); mins = (int) ((now - (hrs * 3600)) / 60); sprintf(s, "%02d:%02d", hrs, mins); strcat(out, s); } /***** LOGGING *****/ /* log something */ /* putlog(level,channel_name,format,...); */ void putlog(va_alist) va_dcl { va_list va; int i, type; char *format, *chname, s[768], s1[256], *out; time_t tt; char ct[81]; va_start(va); type = va_arg(va, int); chname = va_arg(va, char *); format = va_arg(va, char *); /* format log entry at offset 8, then i can prepend the timestamp */ out = &s[8]; vsprintf(out, format, va); tt = now; if (keep_all_logs) { strcpy(ct, ctime(&tt)); ct[10] = 0; strcpy(ct, &ct[8]); ct[7] = 0; strcpy(&ct[2], &ct[4]); ct[24] = 0; strcpy(&ct[5], &ct[22]); if (ct[0] == ' ') ct[0] = '0'; } if ((out[0]) && (shtime)) { strcpy(s1, ctime(&tt)); strcpy(s1, &s1[11]); s1[5] = 0; out = s; s[0] = '['; strncpy(&s[1], s1, 5); s[6] = ']'; s[7] = ' '; } strcat(out, "\n"); if (!use_stderr) { for (i = 0; i < max_logs; i++) { if ((logs[i].filename != NULL) && (logs[i].mask & type) && ((chname[0] == '*') || (logs[i].chname[0] == '*') || (strcasecmp(chname, logs[i].chname) == 0))) { if (logs[i].f == NULL) { /* open this logfile */ if (keep_all_logs) { sprintf(s1, "%s.%s", logs[i].filename, ct); logs[i].f = fopen(s1, "a+"); } else logs[i].f = fopen(logs[i].filename, "a+"); } if (logs[i].f != NULL) fputs(out, logs[i].f); } } } if ((!backgrd) && (!con_chan) && (!term_z)) printf("%s", out); for (i = 0; i < dcc_total; i++) if ((dcc[i].type == &DCC_CHAT) && (dcc[i].u.chat->con_flags & type)) { if ((chname[0] == '*') || (dcc[i].u.chat->con_chan[0] == '*') || (strcasecmp(chname, dcc[i].u.chat->con_chan) == 0)) dprintf(i, "%s", out); } if ((type & LOG_MISC) && (use_stderr)) { vsprintf(s, format, va); tprintf(STDERR, "%s\n", s); } va_end(va); } /* flush the logfiles to disk */ void flushlogs() { int i; context; for (i = 0; i < max_logs; i++) if (logs[i].f != NULL) fflush(logs[i].f); context; } /***** BOT AND HELPBOT SERVER QUEUES *****/ /* queue a msg on one of the msg queues */ static struct msgq *q_msg (struct msgq * qq, int sock, char * s) { struct msgq *q; int cnt; if (qq == NULL) { q = (struct msgq *) nmalloc(sizeof(struct msgq)); q->sock = sock; q->next = NULL; q->msg = (char *) nmalloc(strlen(s) + 1); strcpy(q->msg, s); return q; } cnt = 0; q = qq; while (q->next != NULL) { q = q->next; cnt++; } if (cnt > maxqmsg) return NULL; /* return null: did not alter queue */ q->next = (struct msgq *) nmalloc(sizeof(struct msgq)); q = q->next; q->sock = sock; q->next = NULL; q->msg = (char *) nmalloc(strlen(s) + 1); strcpy(q->msg, s); return qq; } /* use when sending msgs... will spread them out so there's no flooding */ void mprintf(va_alist) va_dcl { char s[1024]; int sock; char *format; va_list va; struct msgq *q; static int warned = 0; va_start(va); sock = va_arg(va, int); format = va_arg(va, char *); vsprintf(s, format, va); va_end(va); q = q_msg(mq, sock, s); #ifdef EBUG_OUTPUT if (s[strlen(s) - 1] == '\n') s[strlen(s) - 1] = 0; debug1("[!m] %s", s); #endif if (q != NULL) { mq = q; mtot++; warned = 0; } else { if (!warned) putlog(LOG_MISC, "*", "!!! OVER MAXIMUM MSG QUEUE"); warned = 1; } } /* use when sending help msgs (different queue) */ void hprintf(va_alist) va_dcl { char s[1024]; int sock; char *format; va_list va; struct msgq *q; static int warned = 0; va_start(va); sock = va_arg(va, int); format = va_arg(va, char *); vsprintf(s, format, va); va_end(va); q = q_msg(hq, sock, s); #ifdef EBUG_OUTPUT if (s[strlen(s) - 1] == '\n') s[strlen(s) - 1] = 0; debug1("[!h] %s", s); #endif if (q != NULL) { hq = q; htot++; warned = 0; } else { if (!warned) putlog(LOG_MISC, "*", "!!! OVER MAXIMUM HELP QUEUE"); warned = 1; } } /* called periodically to shove out another queued item */ /* mode queue gets priority now */ void deq_msg() { struct msgq *q = mq; if (q == NULL) q = hq; if (q == NULL) return; tputs(q->sock, q->msg, strlen(q->msg)); if (q == mq) { mq = mq->next; mtot--; } else { hq = hq->next; htot--; } nfree(q->msg); nfree(q); } /* clean out the msg queues (like when changing servers) */ void empty_msgq() { struct msgq *q, *qq; q = mq; while (q != NULL) { qq = q->next; nfree(q->msg); nfree(q); q = qq; } q = hq; while (q != NULL) { qq = q->next; nfree(q->msg); nfree(q); q = qq; } mtot = htot = 0; mq = hq = NULL; } /********** STRING SUBSTITUTION **********/ static int cols = 0; static int colsofar = 0; static int blind = 0; static int subwidth = 70; static char *colstr = NULL; /* add string to colstr */ static void subst_addcol (char * s, char * newcol) { char *p, *q; int i, colwidth; if ((newcol[0]) && (newcol[0] != '\377')) colsofar++; colstr = nrealloc(colstr, strlen(colstr) + strlen(newcol) + (colstr[0] ? 2 : 1)); if ((newcol[0]) && (newcol[0] != '\377')) { if (colstr[0]) strcat(colstr, "\377"); strcat(colstr, newcol); } if ((colsofar == cols) || ((newcol[0] == '\377') && (colstr[0]))) { colsofar = 0; strcpy(s, " "); colwidth = (subwidth - 5) / cols; q = colstr; p = strchr(colstr, '\377'); while (p != NULL) { *p = 0; strcat(s, q); for (i = strlen(q); i < colwidth; i++) strcat(s, " "); q = p + 1; p = strchr(q, '\377'); } strcat(s, q); nfree(colstr); colstr = (char *) nmalloc(1); colstr[0] = 0; } } /* substitute %x codes in help files */ /* %B = bot nickname */ /* %V = version */ /* %C = list of channels i monitor */ /* %E = eggdrop banner */ /* %A = admin line */ /* %T = current time ("14:15") */ /* %N = user's nickname */ /* %{+xy} require flags to read this section */ /* %{center} center this line */ /* %{cols=N} start of columnated section (indented) */ /* %{end} end of section */ void help_subst (char * s, char * nick, struct flag_record * flags, int isdcc) { char xx[512], sub[161], *p, *q, c; int i, j, center = 0; time_t tt; if (s == NULL) { /* used to reset substitutions */ blind = 0; cols = 0; subwidth = 70; if (colstr != NULL) { nfree(colstr); colstr = NULL; } return; } strcpy(xx, s); s[0] = 0; p = strchr(xx, '%'); while (p != NULL) { c = *(p + 1); sub[0] = 0; *p = 0; if (!blind) strcat(s, xx); switch (c) { case 'B': strcpy(sub, (isdcc ? botnetnick : botname)); break; case 'V': strcpy(sub, ver); break; case 'C': { struct chanset_t *chan = chanset; sub[0] = 0; while (chan != NULL) { if (strlen(s) + strlen(chan->name) + 1 > 160) break; if (s[0]) strcat(s, " "); strcat(s, chan->name); chan = chan->next; } } break; case 'E': strcpy(sub, version); break; case 'A': strcpy(sub, admin); break; case 'T': tt = now; strcpy(sub, ctime(&tt)); strcpy(sub, &sub[11]); sub[5] = 0; break; case 'N': { char * p = strchr(nick,':'); if (p) p++; else p = nick; strcpy(sub, p); } break; case '{': q = p; p++; while ((*p != '}') && (*p)) p++; if (*p) { *p = 0; p--; q += 2; /* now q is the string and p is where the rest of the fcn expects */ if (q[0] == '+') { char *r; struct flag_record fr = {0,0,FR_OR}; c = 0; if ((r = strchr(q,'|'))) { c = '|'; } else if ((r = strchr(q,'&'))) { fr.match = FR_AND; c = '&'; } if (r) { *r = 0; fr.chan = str2chflags(r+1); } fr.global = str2flags(q+1); if (r) { *r = c; } if (!flagrec_ok(&fr, flags)) blind = 1; else blind = 0; } if (q[0] == '-') blind = 0; if (strcasecmp(q, "end") == 0) { blind = 0; subwidth = 70; if (cols) { subst_addcol(s, "\377"); nfree(colstr); colstr = NULL; cols = 0; } } if (strcasecmp(q, "center") == 0) center = 1; if (strncmp(q, "cols=", 5) == 0) { char *r; cols = atoi(q + 5); colsofar = 0; colstr = (char *) nmalloc(1); colstr[0] = 0; r = strchr(q + 5, '/'); if (r != NULL) subwidth = atoi(r + 1); } } else p = q; /* no } so ignore */ break; case '%': strcpy(sub, "%"); break; default: strcpy(sub, "%_"); sub[1] = c; break; } i = strlen(sub); if (!blind) strcat(s, sub); if (c != 0) strcpy(xx, p + 2); else xx[0] = 0; p = strchr(xx, '%'); } if (!blind) strcat(s, xx); if (strlen(s) > 120) s[120] = 0; if (center) { strcpy(xx, s); i = 35 - (strlen(xx) / 2); if (i > 0) { s[0] = 0; for (j = 0; j < i; j++) s[j] = ' '; s[i] = 0; strcat(s, xx); } } if (cols) { strcpy(xx, s); s[0] = 0; subst_addcol(s, xx); } } void showhelp (char * who, char * file, struct flag_record * flags) { FILE *f; char s[1024], *p; int lines = 0; for (p = file; *p != 0; p++) { if ((*p == ' ') || (*p == '.')) *p = '/'; if (*p == '-') *p = '_'; if (*p == '+') *p = 'P'; } sprintf(s, "%s%s", helpdir, file); s[256] = 0; if (!is_file(s)) { strcat(s, "/"); strcat(s, file); if (!is_file(s)) { hprintf(serv, "NOTICE %s :%s\n", who, IRC_NOHELP2); return; } } f = fopen(s, "r"); if (f == NULL) { hprintf(serv, "NOTICE %s :%s\n", who, IRC_NOHELP2); return; } help_subst(NULL, NULL, 0, 0); /* clear flags */ while (!feof(f)) { fgets(s, 120, f); if (!feof(f)) { if (s[strlen(s) - 1] == '\n') s[strlen(s) - 1] = 0; if (!s[0]) strcpy(s, " "); help_subst(s, who, flags, 0); if (s[0]) { hprintf(serv, "NOTICE %s :%s\n", who, s); lines++; } } } fclose(f); if (!lines) hprintf(serv, "NOTICE %s :%s\n", who, IRC_NOHELP2); } void showtext (char * who, char * file, struct flag_record * flags) { FILE *f; char s[1024], *p; for (p = file; *p != 0; p++) if ((*p == ' ') || (*p == '.')) *p = '/'; sprintf(s, "%s%s", textdir, file); s[256] = 0; f = fopen(s, "r"); if (f == NULL) return; if (!is_file(s)) { fclose(f); hprintf(serv, "NOTICE %s :'%s' %s\n", who, s, IRC_NOTNORMFILE); return; } help_subst(NULL, NULL, 0, 0); while (!feof(f)) { fgets(s, 120, f); if (!feof(f)) { if (s[strlen(s) - 1] == '\n') s[strlen(s) - 1] = 0; if (!s[0]) strcpy(s, " "); help_subst(s, who, flags, 0); if (s[0]) hprintf(serv, "NOTICE %s :%s\n", who, s); } } fclose(f); } void tellhelp (int idx, char * file, struct flag_record * flags) { FILE *f; char s[1024], *p; int lines = 0; for (p = file; *p != 0; p++) { if ((*p == ' ') || (*p == '.')) *p = '/'; if (*p == '-') *p = '_'; if (*p == '+') *p = 'P'; } sprintf(s, "%sdcc/%s", helpdir, file); s[256] = 0; if (!is_file(s)) { strcat(s, "/"); strcat(s, file); if (!is_file(s)) { dprintf(idx, "%s\n", IRC_NOHELP2); return; } } f = fopen(s, "r"); if (f == NULL) { dprintf(idx, "%s\n", IRC_NOHELP2); return; } help_subst(NULL, NULL, 0, 1); while (!feof(f)) { fgets(s, 120, f); if (!feof(f)) { if (s[strlen(s) - 1] == '\n') s[strlen(s) - 1] = 0; if (!s[0]) strcpy(s, " "); help_subst(s, dcc[idx].nick, flags, 1); if (s[0]) { dprintf(idx, "%s\n", s); lines++; } } } if (!lines) dprintf(idx, "%s\n", IRC_NOHELP2); fclose(f); } void telltext (int idx, char * file, struct flag_record * flags) { FILE *f; char s[1024], *p; for (p = file; *p != 0; p++) if ((*p == ' ') || (*p == '.')) *p = '/'; sprintf(s, "%s%s", textdir, file); s[256] = 0; f = fopen(s, "r"); if (f == NULL) return; if (!is_file(s)) { fclose(f); dprintf(idx, "### '%s' %s\n", s, IRC_NOTNORMFILE); return; } help_subst(NULL, NULL, 0, 1); while (!feof(f)) { fgets(s, 120, f); if (!feof(f)) { if (s[strlen(s) - 1] == '\n') s[strlen(s) - 1] = 0; if (!s[0]) strcpy(s, " "); help_subst(s, dcc[idx].nick, flags, 1); if (s[0]) dprintf(idx, "%s\n", s); } } fclose(f); } /* show motd to dcc chatter */ void show_motd (int idx) { FILE *vv; char s[1024]; struct flag_record fr = {0,0,0}; fr.global = get_attr_handle(dcc[idx].nick); fr.chan = get_chanattr_handle(dcc[idx].nick,dcc[idx].u.chat->con_chan); vv = fopen(motdfile, "r"); if (vv != NULL) { if (!is_file(motdfile)) { fclose(vv); dprintf(idx, "### MOTD %s\n", IRC_NOTNORMFILE); return; } dprintf(idx, "\n"); help_subst(NULL, NULL, 0, 1); while (!feof(vv)) { fgets(s, 120, vv); if (!feof(vv)) { if (s[strlen(s) - 1] == '\n') s[strlen(s) - 1] = 0; if (!s[0]) strcpy(s, " "); help_subst(s, dcc[idx].nick, &fr, 1); if (s[0]) dprintf(idx, "%s\n", s); } } fclose(vv); dprintf(idx, "\n"); } }