/* 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 */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "eggdrop.h" #include "proto.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 origbotname[]; 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 cx_file[]; extern int cx_line; extern char ver[]; extern char textdir[]; extern int strict_host; extern int keep_all_logs; /* whether or not to display the time with console output */ int shtime=1; /* logfile names */ char *logfile[MAXLOGS]; /* logfile masks */ int logmask[MAXLOGS]; /* logfile channels */ char *logchan[MAXLOGS]; /* 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; struct msgq { int sock; char *msg; struct msgq *next; } *mq=NULL,*hq=NULL; /* store info for clone bots */ struct tandbuf { char bot[10]; time_t timer; struct msgq *q; } tbuf[5]; /* expected memory usage */ int expmem_misc() { int tot=0; struct msgq *m=mq,*h=hq; 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() { int i; for (i=0; i<5; i++) { tbuf[i].q=NULL; tbuf[i].bot[0]=0; } } /***** MISC FUNCTIONS *****/ #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(bigs,littles) char *bigs,*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(s1,s2) char *s1,*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(first,rest,divider) char *first,*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); } void split(first,rest) char *first,*rest; { splitc(first,rest,' '); } void splitnick(first,rest) char *first,*rest; { splitc(first,rest,'!'); } void nsplit(first,rest) char *first,*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(s,nw) 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(oldpath,newpath) char *oldpath,*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(oldpath,newpath) char *oldpath,*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(s) 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(idx,prefix,data) int idx; char *prefix,*data; { char *p=data,*q,*n,c; if (!(*data)) { dprintf(idx,"%s\n",prefix); return; } while (strlen(p)>490) { q=p+490; /* search for embedded linefeed first */ n=strchr(p,'\n'); if ((n!=NULL) && (n 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(now,then,out) time_t now,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(now,then,out) time_t now,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[1024],s1[161],s2[1024]; FILE *f; time_t tt; char ct[81]; va_start(va); type=va_arg(va,int); chname=va_arg(va,char *); format=va_arg(va,char *); vsprintf(s,format,va); tt=time(NULL); 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 (s[0]==0) strcpy(s,"\n"); else if (shtime) { strcpy(s1,ctime(&tt)); strcpy(s1,&s1[11]); s1[5]=0; sprintf(s2,"[%s] %s\n",s1,s); strcpy(s,s2); } else strcat(s,"\n"); if (!use_stderr) { for (i=0; icon_flags&type)) { if ((chname[0]=='*') || (dcc[i].u.chat->con_chan[0]=='*') || (strcasecmp(chname,dcc[i].u.chat->con_chan)==0)) dprintf(i,"%s",s); } if ((type&LOG_MISC) && (use_stderr)) { vsprintf(s,format,va); tprintf(STDERR,"%s\n",s); } va_end(va); } /***** BOT AND HELPBOT SERVER QUEUES *****/ /* queue a msg on one of the msg queues */ struct msgq *q_msg(qq,sock,s) 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); 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); 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 */ void deq_msg() { static which=0; /* to alternate which queue is pushed */ struct msgq *q; which=(which+1)%2; q=(which ? hq : mq); if (q==NULL) q=(which ? mq : hq); /* chosen one is empty? switch off */ if (q==NULL) return; /* both queues empty */ write(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; } /***** RESYNC BUFFERS *****/ /* create a tandem buffer for 'bot' */ void new_tbuf(bot) char *bot; { int i; for (i=0; i<5; i++) if (tbuf[i].bot[0]==0) { /* this one is empty */ strcpy(tbuf[i].bot,bot); tbuf[i].q=NULL; tbuf[i].timer=time(NULL); putlog(LOG_MISC,"*","Creating resync buffer for %s",bot); return; } } /* flush a certain bot's tbuf */ int flush_tbuf(bot) char *bot; { int i; struct msgq *q; for (i=0; i<5; i++) if (strcasecmp(tbuf[i].bot,bot)==0) { while (tbuf[i].q!=NULL) { q=tbuf[i].q; tbuf[i].q=tbuf[i].q->next; nfree(q->msg); nfree(q); } tbuf[i].bot[0]=0; return 1; } return 0; } /* flush all tbufs older than 15 minutes */ void check_expired_tbufs() { int i; time_t now=time(NULL); struct msgq *q; for (i=0; i<5; i++) if (tbuf[i].bot[0]) { if (now-tbuf[i].timer > 900) { /* EXPIRED */ while (tbuf[i].q!=NULL) { q=tbuf[i].q; tbuf[i].q=tbuf[i].q->next; nfree(q->msg); nfree(q); } putlog(LOG_MISC,"*","Flushing resync buffer for clonebot %s.", tbuf[i].bot); tbuf[i].bot[0]=0; } } } /* add stuff to a specific bot's tbuf */ void q_tbuf(bot,s) char *bot,*s; { int i; struct msgq *q; for (i=0; i<5; i++) if (strcasecmp(tbuf[i].bot,bot)==0) { q=q_msg(tbuf[i].q,0,s); if (q!=NULL) tbuf[i].q=q; } } /* add stuff to the resync buffers */ void q_resync(s) char *s; { int i; struct msgq *q; for (i=0; i<5; i++) if (tbuf[i].bot[0]) { q=q_msg(tbuf[i].q,0,s); if (q!=NULL) tbuf[i].q=q; } } /* is bot in resync list? */ int can_resync(bot) char *bot; { int i; for (i=0; i<5; i++) if (strcasecmp(bot,tbuf[i].bot)==0) return 1; return 0; } /* dump the resync buffer for a bot */ void dump_resync(z,bot) int z; char *bot; { int i; struct msgq *q; for (i=0; i<5; i++) if (strcasecmp(bot,tbuf[i].bot)==0) { while (tbuf[i].q!=NULL) { q=tbuf[i].q; tbuf[i].q=tbuf[i].q->next; tprintf(z,"%s",q->msg); nfree(q->msg); nfree(q); } tbuf[i].bot[0]=0; return; } } /* give status report on tbufs */ void status_tbufs(idx) int idx; { int i,count; struct msgq *q; char s[121]; s[0]=0; for (i=0; i<5; i++) if (tbuf[i].bot[0]) { strcat(s,tbuf[i].bot); count=0; q=tbuf[i].q; while (q!=NULL) { count++; q=q->next; } sprintf(&s[strlen(s)]," (%d), ",count); } if (s[0]) { s[strlen(s)-2]=0; dprintf(idx,"Pending sharebot buffers: %s\n",s); } } /* 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 */ void help_subst(s,nick,oper,master) char *s,*nick; int oper,master; { char xx[512],sub[161],*p,c; int i,blind=0; time_t tt; 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,botname); break; case 'V': strcpy(sub,ver); break; case 'C': getchanlist(sub,160); break; case 'E': strcpy(sub,version); break; case 'A': strcpy(sub,admin); break; case 'T': tt=time(NULL); strcpy(sub,ctime(&tt)); strcpy(sub,&sub[11]); sub[5]=0; break; case 'N': strcpy(sub,nick); break; case '(': if (!oper) blind=1; break; case ')': blind=0; break; case '[': if (!master) blind=1; break; case ']': blind=0; 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; } void showhelp(who,file,oper,master) char *who,*file; int oper,master; { FILE *f; char s[1024],*p; for (p=file; *p!=0; p++) if ((*p==' ') || (*p=='/')) *p='.'; sprintf(s,"%s%s",helpdir,file); s[256]=0; f=fopen(s,"r"); if (f==NULL) { hprintf(serv,"NOTICE %s :No help available on that.\n",who); return; } if (!is_file(s)) { fclose(f); hprintf(serv,"NOTICE %s :'%s' is not a normal file!\n",who,s); return; } if (!feof(f)) { fgets(s,120,f); if (!feof(f)) { if (s[strlen(s)-1]=='\n') s[strlen(s)-1]=0; if (((strcasecmp(s,"!master")==0) && (!master)) || ((strcasecmp(s,"!op")==0) && (!oper))) { hprintf(serv,"NOTICE %s :No help available on that.\n",who); return; } } } if (!feof(f)) if ((strcasecmp(s,"!master")!=0) && (strcasecmp(s,"!op")!=0)) { help_subst(s,who,oper,master); if (s[0]) hprintf(serv,"NOTICE %s :%s\n",who,s); } 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,oper,master); if (s[0]) hprintf(serv,"NOTICE %s :%s\n",who,s); } } fclose(f); } void showtext(who,file,oper,master) char *who,*file; int oper,master; { 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' is not a normal file!\n",who,s); return; } if (!feof(f)) { fgets(s,120,f); if (!feof(f)) { if (s[strlen(s)-1]=='\n') s[strlen(s)-1]=0; if (((strcasecmp(s,"!master")==0) && (!master)) || ((strcasecmp(s,"!op")==0) && (!oper))) return; } } if (!feof(f)) if ((strcasecmp(s,"!master")!=0) && (strcasecmp(s,"!op")!=0)) { help_subst(s,who,oper,master); if (s[0]) hprintf(serv,"NOTICE %s :%s\n",who,s); } 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,oper,master); if (s[0]) hprintf(serv,"NOTICE %s :%s\n",who,s); } } fclose(f); } void tellhelp(idx,file,oper,master) int idx,oper,master; char *file; { FILE *f; char s[1024],*p; for (p=file; *p!=0; p++) if ((*p==' ') || (*p=='/')) *p='.'; sprintf(s,"%sdcc.%s",helpdir,file); s[256]=0; f=fopen(s,"r"); if (f==NULL) { dprintf(idx,"No help available on that.\n"); return; } if (!is_file(s)) { fclose(f); dprintf(idx,"### '%s' is not a normal file!\n",s); return; } if (!feof(f)) { fgets(s,120,f); if (!feof(f)) { if (s[strlen(s)-1]=='\n') s[strlen(s)-1]=0; if (((strcasecmp(s,"!master")==0) && (!master)) || ((strcasecmp(s,"!op")==0) && (!oper))) { dprintf(idx,"No help available on that.\n"); return; } } } if (!feof(f)) if ((strcasecmp(s,"!master")!=0) && (strcasecmp(s,"!op")!=0)) { help_subst(s,dcc[idx].nick,oper,master); if (s[0]) dprintf(idx,"%s\n",s); } 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,oper,master); if (s[0]) dprintf(idx,"%s\n",s); } } fclose(f); } void telltext(idx,file,oper,master) int idx,oper,master; char *file; { 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' is not a normal file!\n",s); return; } if (!feof(f)) { fgets(s,120,f); if (!feof(f)) { if (s[strlen(s)-1]=='\n') s[strlen(s)-1]=0; if (((strcasecmp(s,"!master")==0) && (!master)) || ((strcasecmp(s,"!op")==0) && (!oper))) return; } } if (!feof(f)) if ((strcasecmp(s,"!master")!=0) && (strcasecmp(s,"!op")!=0)) { help_subst(s,dcc[idx].nick,oper,master); if (s[0]) dprintf(idx,"%s\n",s); } 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,oper,master); if (s[0]) dprintf(idx,"%s\n",s); } } fclose(f); } /* show motd to dcc chatter */ void show_motd(idx) int idx; { FILE *vv; char s[121]; vv=fopen(motdfile,"r"); if (vv!=NULL) { if (!is_file(motdfile)) { fclose(vv); dprintf(idx,"### MOTD is not a normal file!\n"); return; } dprintf(idx,"\n"); 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,!(dcc[idx].u.chat->status&STAT_PARTY), dcc[idx].u.chat->status&STAT_MASTER); if (s[0]) dprintf(idx,"%s\n",s); } } fclose(vv); dprintf(idx,"\n"); } }