mirror of
https://github.com/EnergyMech/energymech.git
synced 2025-12-29 16:14:43 +00:00
755 lines
14 KiB
C
755 lines
14 KiB
C
/*
|
|
|
|
EnergyMech, IRC bot software
|
|
Copyright (c) 2001-2004 proton
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
#define NOTIFY_C
|
|
#include "config.h"
|
|
|
|
#ifdef NOTIFY
|
|
|
|
#include "defines.h"
|
|
#include "structs.h"
|
|
#include "global.h"
|
|
#include "h.h"
|
|
#include "text.h"
|
|
|
|
#define CHOOSE_NUMBER 25
|
|
#define CHOOSE_MOVETO (CHOOSE_NUMBER - 2)
|
|
|
|
#define LOGFILENAMEFMT "notify-guid%i.log"
|
|
#define LOGFILENAMEBUF 32 /* need to recalculate this if LOGFILENAMEFMT is changed */
|
|
|
|
LS Notify **endoflist;
|
|
LS int lock_ison = FALSE;
|
|
|
|
void purge_notify(void)
|
|
{
|
|
Notify *nf;
|
|
nfLog *nlog;
|
|
|
|
/*
|
|
* empty the notifylist
|
|
*/
|
|
while(current->notifylist)
|
|
{
|
|
nf = current->notifylist;
|
|
current->notifylist = nf->next;
|
|
while(nf->log)
|
|
{
|
|
nlog = nf->log;
|
|
nf->log = nlog->next;
|
|
Free((char**)&nlog);
|
|
}
|
|
Free((char**)&nf);
|
|
}
|
|
}
|
|
|
|
int mask_check(Notify *nf, char *userhost)
|
|
{
|
|
char *mask,*rest;
|
|
int ret;
|
|
|
|
/*
|
|
* no mask (NULL)
|
|
*/
|
|
if (!nf->mask)
|
|
return(NF_MASKONLINE);
|
|
|
|
ret = NF_NOMATCH;
|
|
if (nf->endofmask)
|
|
{
|
|
/*
|
|
* multiple masks separated by spaces
|
|
*/
|
|
mask = rest = nf->mask;
|
|
chop(&rest);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* single mask
|
|
*/
|
|
if (!matches(nf->mask,userhost))
|
|
ret = NF_MASKONLINE;
|
|
return(ret);
|
|
}
|
|
while(mask)
|
|
{
|
|
if (!matches(mask,userhost))
|
|
{
|
|
ret = NF_MASKONLINE;
|
|
break;
|
|
}
|
|
mask = chop(&rest);
|
|
}
|
|
if (nf->endofmask)
|
|
{
|
|
/*
|
|
* undo the chop()'s
|
|
*/
|
|
for(mask=nf->mask;mask<nf->endofmask;mask++)
|
|
{
|
|
if (*mask == 0)
|
|
*mask = ' ';
|
|
}
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
void send_ison(void)
|
|
{
|
|
Notify *nf,*chosen[CHOOSE_NUMBER];
|
|
char isonmsg[MSGLEN];
|
|
char *p,*src;
|
|
int i,x,period;
|
|
|
|
/*
|
|
* dont send nicks to ISON too often
|
|
*/
|
|
period = current->setting[INT_ISONDELAY].int_var;
|
|
x = now - current->lastnotify;
|
|
if ((x < period) || (lock_ison && (x < 600)))
|
|
return;
|
|
|
|
current->lastnotify = now;
|
|
|
|
/*
|
|
* the nature of the code makes it so that the first NULL is
|
|
* pushed further down the list as more entries are added,
|
|
* so no need to blank the whole list
|
|
*/
|
|
chosen[0] = NULL;
|
|
|
|
/*
|
|
* select the oldest (CHOOSE_NUMBER) nicks for an update
|
|
*/
|
|
for(nf=current->notifylist;nf;nf=nf->next)
|
|
{
|
|
for(i=0;i<CHOOSE_NUMBER;i++)
|
|
{
|
|
if (!chosen[i] || (chosen[i]->checked > nf->checked))
|
|
{
|
|
for(x=CHOOSE_MOVETO;x>=i;x--)
|
|
chosen[x+1] = chosen[x];
|
|
chosen[i] = nf;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (chosen[0])
|
|
{
|
|
p = isonmsg;
|
|
for(i=0;i<CHOOSE_NUMBER;i++)
|
|
{
|
|
/*
|
|
* drop out once the end-of-chosen-list NULL is found
|
|
*/
|
|
if (!chosen[i])
|
|
break;
|
|
chosen[i]->checked = 1;
|
|
src = chosen[i]->nick;
|
|
if (i) *(p++) = ' ';
|
|
while((*p = *(src++))) p++;
|
|
}
|
|
to_server("ISON :%s\n",isonmsg);
|
|
lock_ison = TRUE;
|
|
}
|
|
}
|
|
|
|
void catch_ison(char *rest)
|
|
{
|
|
Notify *nf;
|
|
char whoismsg[MSGLEN];
|
|
char *nick,*dst;
|
|
|
|
lock_ison = FALSE;
|
|
*whoismsg = 0;
|
|
dst = whoismsg;
|
|
while((nick = chop(&rest)))
|
|
{
|
|
for(nf=current->notifylist;nf;nf=nf->next)
|
|
{
|
|
if (!nickcmp(nf->nick,nick))
|
|
{
|
|
nf->checked = now;
|
|
/*
|
|
* /whois user to get user@host + realname
|
|
*/
|
|
if (nf->status == NF_OFFLINE)
|
|
{
|
|
if (*whoismsg) *(dst++) = ',';
|
|
*dst = 0;
|
|
dst = stringcat(dst,nf->nick);
|
|
nf->status = NF_WHOIS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*whoismsg)
|
|
to_server("WHOIS %s\n",whoismsg);
|
|
|
|
for(nf=current->notifylist;nf;nf=nf->next)
|
|
{
|
|
if (nf->checked == 1)
|
|
{
|
|
nf->checked = now;
|
|
if (nf->status >= NF_WHOIS)
|
|
{
|
|
/*
|
|
* close the log entry for this online period
|
|
*/
|
|
if (nf->log && nf->log->signon && !nf->log->signoff)
|
|
nf->log->signoff = now;
|
|
/*
|
|
* announce that the user is offline if its a mask match
|
|
*/
|
|
if (nf->status == NF_MASKONLINE)
|
|
send_spy(SPYSTR_STATUS,"Notify: %s is offline",nf->nick);
|
|
nf->status = NF_OFFLINE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void catch_whois(char *nick, char *userhost, char *realname)
|
|
{
|
|
Notify *nf;
|
|
nfLog *nlog;
|
|
|
|
if (!realname || !*realname)
|
|
realname = "unknown";
|
|
|
|
for(nf=current->notifylist;nf;nf=nf->next)
|
|
{
|
|
if ((nf->status == NF_WHOIS) && !nickcmp(nf->nick,nick))
|
|
{
|
|
/*
|
|
* put in a new log entry
|
|
*/
|
|
set_mallocdoer(catch_whois);
|
|
nlog = (nfLog*)Calloc(sizeof(nfLog) + Strlen2(userhost,realname)); // realname is never NULL
|
|
nlog->signon = now;
|
|
nlog->next = nf->log;
|
|
nf->log = nlog;
|
|
nlog->realname = stringcat(nlog->userhost,userhost) + 1;
|
|
stringcpy(nlog->realname,realname);
|
|
/*
|
|
* if there is a mask, we need a match to announce the online status
|
|
*/
|
|
nf->status = mask_check(nf,userhost);
|
|
if (nf->status == NF_MASKONLINE)
|
|
send_spy(SPYSTR_STATUS,"Notify: %s (%s) is online",nf->nick,userhost);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
* saving and loading notify information
|
|
*
|
|
*/
|
|
|
|
int notifylog_callback(char *rest)
|
|
{
|
|
Notify *nf;
|
|
nfLog *nlog,**pp;
|
|
time_t on,off;
|
|
char *nick,*userhost;
|
|
|
|
if (*rest == COMMENT_CHAR)
|
|
return(FALSE);
|
|
|
|
nick = chop(&rest);
|
|
|
|
on = asc2int(chop(&rest));
|
|
if (errno)
|
|
return(FALSE);
|
|
|
|
off = asc2int(chop(&rest));
|
|
if (errno)
|
|
return(FALSE);
|
|
|
|
userhost = chop(&rest);
|
|
|
|
if (rest && *rest == ':')
|
|
rest++;
|
|
if (!*rest)
|
|
return(FALSE);
|
|
|
|
for(nf=current->notifylist;nf;nf=nf->next)
|
|
{
|
|
if (!nickcmp(nick,nf->nick))
|
|
{
|
|
pp = &nf->log;
|
|
while(*pp)
|
|
{
|
|
if ((*pp)->signon < on)
|
|
break;
|
|
pp = &(*pp)->next;
|
|
}
|
|
set_mallocdoer(notifylog_callback);
|
|
nlog = (nfLog*)Calloc(sizeof(nfLog) + Strlen2(userhost,rest)); // rest is never NULL
|
|
nlog->signon = on;
|
|
nlog->signoff = off;
|
|
nlog->next = *pp;
|
|
*pp = nlog;
|
|
nlog->realname = stringcat(nlog->userhost,userhost) + 1;
|
|
stringcpy(nlog->realname,rest);
|
|
break;
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
void read_notifylog(void)
|
|
{
|
|
char fname[LOGFILENAMEBUF];
|
|
int fd;
|
|
#ifdef DEBUG
|
|
int dd;
|
|
#endif /* DEBUG */
|
|
|
|
sprintf(fname,LOGFILENAMEFMT,current->guid);
|
|
if ((fd = open(fname,O_RDONLY)) < 0)
|
|
return;
|
|
#ifdef DEBUG
|
|
dd = dodebug;
|
|
dodebug = FALSE;
|
|
#endif /* DEBUG */
|
|
|
|
readline(fd,¬ifylog_callback); /* readline closes fd */
|
|
|
|
#ifdef DEBUG
|
|
dodebug = dd;
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
void write_notifylog(void)
|
|
{
|
|
Notify *nf;
|
|
nfLog *nlog;
|
|
char fname[LOGFILENAMEBUF];
|
|
int fd;
|
|
#ifdef DEBUG
|
|
int dd;
|
|
#endif /* DEBUG */
|
|
|
|
sprintf(fname,LOGFILENAMEFMT,current->guid);
|
|
if ((fd = open(fname,O_WRONLY|O_CREAT|O_TRUNC,NEWFILEMODE)) < 0)
|
|
return;
|
|
|
|
#ifdef DEBUG
|
|
dd = dodebug;
|
|
dodebug = FALSE;
|
|
#endif /* DEBUG */
|
|
for(nf=current->notifylist;nf;nf=nf->next)
|
|
{
|
|
to_file(fd,COMMENT_STRCHR "\n" COMMENT_STRCHR " Nick: %s\n",nf->nick);
|
|
if (nf->info)
|
|
to_file(fd,COMMENT_STRCHR " Note: %s\n",nf->info);
|
|
if (nf->mask)
|
|
to_file(fd,COMMENT_STRCHR " Mask: %s\n",nf->mask);
|
|
to_file(fd,COMMENT_STRCHR "\n");
|
|
for(nlog=nf->log;nlog;nlog=nlog->next)
|
|
{
|
|
to_file(fd,"%s %lu %lu %s :%s\n",nf->nick,nlog->signon,
|
|
(nlog->signoff) ? nlog->signoff : now,
|
|
nlog->userhost,nlog->realname);
|
|
}
|
|
}
|
|
close(fd);
|
|
#ifdef DEBUG
|
|
dodebug = dd;
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
int notify_callback(char *rest)
|
|
{
|
|
Notify *nf;
|
|
char *nick;
|
|
char *src,*dst;
|
|
char *lspace;
|
|
|
|
errno = EINVAL;
|
|
|
|
if (!rest || *rest == COMMENT_CHAR)
|
|
return(FALSE);
|
|
fix_config_line(rest);
|
|
if ((nick = chop(&rest)) == NULL)
|
|
return(FALSE);
|
|
|
|
#ifdef DEBUG
|
|
debug("(notify_callback) parsing %s `%s'\n",nick,nullstr(rest));
|
|
#endif /* DEBUG */
|
|
|
|
lspace = rest - 1;
|
|
src = dst = rest;
|
|
while(*src)
|
|
{
|
|
if (*src == ' ')
|
|
{
|
|
if (!lspace)
|
|
{
|
|
lspace = dst;
|
|
*(dst++) = *src;
|
|
}
|
|
src++;
|
|
}
|
|
else
|
|
if (*src == ':' && lspace)
|
|
{
|
|
*lspace = 0;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
lspace = NULL;
|
|
*(dst++) = *(src++);
|
|
}
|
|
}
|
|
|
|
if (*src == ':')
|
|
*(src++) = 0;
|
|
if (!*src)
|
|
src = NULL;
|
|
|
|
#ifdef DEBUG
|
|
debug("(notify_callback) creating struct\n");
|
|
#endif /* DEBUG */
|
|
|
|
/*
|
|
* nick = nick
|
|
* rest = mask(s) or *rest == 0
|
|
* src = description or NULL
|
|
*/
|
|
set_mallocdoer(notify_callback);
|
|
nf = (Notify*)Calloc(sizeof(Notify) + StrlenX(nick,rest,src,NULL));
|
|
dst = stringcat(nf->nick,nick);
|
|
if (*rest)
|
|
{
|
|
nf->mask = dst + 1;
|
|
dst = stringcat(nf->mask,rest);
|
|
if (STRCHR(nf->mask,' '))
|
|
nf->endofmask = dst;
|
|
}
|
|
if (src)
|
|
{
|
|
nf->info = dst + 1;
|
|
stringcpy(nf->info,src);
|
|
}
|
|
/* put it at the end of notifylist */
|
|
*endoflist = nf;
|
|
endoflist = &nf->next;
|
|
|
|
errno = 0;
|
|
return(FALSE);
|
|
}
|
|
|
|
int read_notify(char *filename)
|
|
{
|
|
int fd;
|
|
|
|
if (!filename)
|
|
return(FALSE);
|
|
|
|
if ((fd = open(filename,O_RDONLY)) < 0)
|
|
return(FALSE);
|
|
|
|
/*
|
|
* save online logs
|
|
*/
|
|
if (current->notifylist)
|
|
write_notifylog();
|
|
|
|
/*
|
|
* delete the whole list
|
|
*/
|
|
purge_notify();
|
|
|
|
endoflist = ¤t->notifylist;
|
|
readline(fd,¬ify_callback); /* readline closes fd */
|
|
|
|
/*
|
|
* read back online logs
|
|
*/
|
|
read_notifylog();
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/*
|
|
*
|
|
* commands...
|
|
*
|
|
*/
|
|
|
|
#define NF_OPTIONS 7
|
|
|
|
LS const char notify_opt[NF_OPTIONS][10] =
|
|
{
|
|
"-ALL",
|
|
"-NOMATCH",
|
|
"-RELOAD",
|
|
"-FULL",
|
|
"-SEEN",
|
|
};
|
|
|
|
#define NFF_ALL 1
|
|
#define NFF_NOMATCH 2
|
|
#define NFF_RELOAD 4
|
|
#define NFF_FULL 8
|
|
#define NFF_SEEN 16
|
|
|
|
LS char *nf_from;
|
|
LS int nf_header;
|
|
|
|
void nfshow_brief(Notify *nf)
|
|
{
|
|
time_t when;
|
|
char mem[40];
|
|
char *s;
|
|
int d,h,m;
|
|
|
|
if (nf->status == NF_NOMATCH)
|
|
s = " nickname in use";
|
|
else
|
|
if (nf->status >= NF_WHOIS)
|
|
s = " online now";
|
|
else
|
|
if (nf->log && nf->log->signoff)
|
|
{
|
|
s = mem;
|
|
when = now - nf->log->signoff;
|
|
d = when / 86400;
|
|
h = (when -= d * 86400) / 3600;
|
|
m = (when -= h * 3600) / 60;
|
|
sprintf(mem,"%2i day%1s %02i:%02i ago",d,EXTRA_CHAR(d),h,m);
|
|
}
|
|
else
|
|
s = " never seen";
|
|
|
|
if (!nf_header)
|
|
to_user(nf_from,"\037nick\037 \037last seen\037 \037note\037");
|
|
to_user(nf_from,(nf->info) ? "%-9s %-22s %s" : "%-9s %s",
|
|
nf->nick,s,nf->info);
|
|
nf_header++;
|
|
}
|
|
|
|
void nfshow_full(Notify *nf)
|
|
{
|
|
char mem[MSGLEN];
|
|
nfLog *nlog;
|
|
char *s,*opt;
|
|
|
|
if (nf_header)
|
|
to_user(nf_from," ");
|
|
to_user(nf_from,(nf->status == NF_MASKONLINE) ? "Nick: \037%s\037" : "Nick: %s",nf->nick);
|
|
if (nf->info)
|
|
to_user(nf_from,"Note: %s",nf->info);
|
|
if (nf->mask)
|
|
to_user(nf_from,"Mask: %s",nf->mask);
|
|
if (nf->log)
|
|
{
|
|
to_user(nf_from,"Online history:");
|
|
for(nlog=nf->log;nlog;nlog=nlog->next)
|
|
{
|
|
opt = mem;
|
|
s = time2away(nlog->signon);
|
|
if (s[1] == ':')
|
|
*(opt++) = ' ';
|
|
*opt = 0;
|
|
opt = stringcat(opt,s);
|
|
while(opt < (mem+18))
|
|
*(opt++) = ' ';
|
|
*opt = 0;
|
|
opt = stringcat(opt," -- ");
|
|
if (nlog->signoff)
|
|
{
|
|
s = time2away(nlog->signoff);
|
|
if (s[1] == ':')
|
|
*(opt++) = ' ';
|
|
*opt = 0;
|
|
}
|
|
else
|
|
{
|
|
s = " online now";
|
|
}
|
|
opt = stringcat(opt,s);
|
|
while(opt < (mem+41))
|
|
*(opt++) = ' ';
|
|
*opt = 0;
|
|
s = (nlog->realname) ? "%s: %s (%s)" : "%s: %s";
|
|
to_user(nf_from,s,mem,nlog->userhost,nlog->realname);
|
|
}
|
|
}
|
|
nf_header++;
|
|
}
|
|
|
|
void sub_notifynick(char *from, char *rest)
|
|
{
|
|
Notify *nf,**pp;
|
|
char *nick;
|
|
int i;
|
|
|
|
nick = chop(&rest);
|
|
if (!nick)
|
|
{
|
|
usage(from);
|
|
return;
|
|
}
|
|
i = 0;
|
|
pp = ¤t->notifylist;
|
|
while(*pp)
|
|
{
|
|
nf = *pp;
|
|
if (!nickcmp(nick,nf->nick))
|
|
{
|
|
*pp = nf->next;
|
|
Free((char**)&nf);
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
pp = &nf->next;
|
|
}
|
|
}
|
|
if (!i)
|
|
to_user(from,"Nick not found: %s",nick);
|
|
else
|
|
to_user(from,"Nick removed from notify: %s",nick);
|
|
}
|
|
|
|
void do_notify(COMMAND_ARGS)
|
|
{
|
|
char message[MSGLEN];
|
|
Notify *nf;
|
|
nfLog *nlog;
|
|
char *opt;
|
|
int n,flags;
|
|
|
|
nf_from = from;
|
|
flags = nf_header = 0;
|
|
*message = 0;
|
|
|
|
if (*rest)
|
|
{
|
|
while((opt = chop(&rest)))
|
|
{
|
|
if (!stringcmp(opt,"+"))
|
|
{
|
|
endoflist = ¤t->notifylist;
|
|
while(*endoflist)
|
|
endoflist = &(*endoflist)->next;
|
|
notify_callback(rest);
|
|
return;
|
|
}
|
|
if (!stringcmp(opt,"-"))
|
|
{
|
|
sub_notifynick(from,rest);
|
|
return;
|
|
}
|
|
for(n=0;n<NF_OPTIONS;n++)
|
|
{
|
|
if (!stringcasecmp(notify_opt[n],opt))
|
|
{
|
|
flags |= (1 << n);
|
|
break;
|
|
}
|
|
}
|
|
if (n>=NF_OPTIONS)
|
|
{
|
|
for(nf=current->notifylist;nf;nf=nf->next)
|
|
{
|
|
if (!nickcmp(opt,nf->nick))
|
|
{
|
|
if (flags & NFF_FULL)
|
|
nfshow_full(nf);
|
|
else
|
|
nfshow_brief(nf);
|
|
break;
|
|
}
|
|
}
|
|
if (!nf)
|
|
{
|
|
if (*message)
|
|
stringcat(message,", ");
|
|
stringcat(message,opt);
|
|
}
|
|
nf_header++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*message)
|
|
{
|
|
#ifdef DEBUG
|
|
debug("(do_notify) dumping errnames\n");
|
|
#endif /* DEBUG */
|
|
to_user(from,"User%s not found: %s",(STRCHR(message,',')) ? "s" : "",message);
|
|
}
|
|
|
|
if (nf_header)
|
|
return;
|
|
|
|
if (flags & NFF_RELOAD)
|
|
{
|
|
opt = current->setting[STR_NOTIFYFILE].str_var;
|
|
if (opt && read_notify(opt))
|
|
{
|
|
flags = get_useraccess(from,"");
|
|
send_spy(SPYSTR_STATUS,"Notify: %s reloaded by %s[%i]",
|
|
opt,CurrentNick,flags);
|
|
to_user(from,"notify list read from file %s",opt);
|
|
}
|
|
else
|
|
{
|
|
to_user(from,"notify list could not be read from file %s",nullstr(opt));
|
|
}
|
|
return;
|
|
}
|
|
|
|
for(nf=current->notifylist;nf;nf=nf->next)
|
|
{
|
|
if ((nf->status == NF_MASKONLINE) || (flags & NFF_ALL) || ((flags & NFF_NOMATCH) && (nf->status == NF_NOMATCH)))
|
|
goto show_it;
|
|
if ((flags & NFF_SEEN) && nf->log)
|
|
{
|
|
for(nlog=nf->log;nlog;nlog=nlog->next)
|
|
{
|
|
if (mask_check(nf,nlog->userhost) == NF_MASKONLINE)
|
|
goto show_it;
|
|
}
|
|
}
|
|
continue;
|
|
show_it:
|
|
if (flags & NFF_FULL)
|
|
nfshow_full(nf);
|
|
else
|
|
nfshow_brief(nf);
|
|
}
|
|
if (!nf_header)
|
|
{
|
|
to_user(from,"no notify users are online");
|
|
}
|
|
}
|
|
|
|
#endif /* NOTIFY */
|