energymech/src/function.c
2025-12-08 13:51:07 +01:00

960 lines
16 KiB
C

/*
EnergyMech, IRC bot software
Parts Copyright (c) 1997-2025 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 FUNCTION_C
#include "config.h"
#include "defines.h"
#include "structs.h"
#ifdef TEST
#define MAIN_C
#endif
#include "global.h"
#include "h.h"
#include "text.h"
char timebuf[64]; /* max format lentgh == 20+1 */
char idlestr[64]; /* max format lentgh == 24+1 */
const char monlist[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
const char daylist[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
#ifndef TEST
/*
* memory allocation routines
*/
#ifdef DEBUG
void *Calloc(int size)
{
aME *mmep;
aMEA *mp;
int i;
#ifdef __GNUC__
if (mallocdoer == NULL)
{
mallocdoer = __builtin_return_address(0);
debug("(Calloc) mallocdoer = "mx_pfmt"\n",(mx_ptr)mallocdoer);
mallocdoer = NULL;
}
#endif /* GNUC */
mmep = NULL;
mp = mrrec;
while(!mmep)
{
for(i=0;i<MRSIZE;i++)
{
if (mp->mme[i].area == NULL)
{
mmep = &mp->mme[i];
break;
}
}
if (!mmep)
{
if (mp->next == NULL)
{
mp->next = calloc(sizeof(aMEA),1);
mmep = &mp->next->mme[0];
}
else
mp = mp->next;
}
}
/*
* heap check +4 bytes
*/
if ((mmep->area = (void*)calloc(size+4,1)) == NULL)
{
run_debug();
exit(1);
}
mmep->size = size;
mmep->when = cx.now;
mmep->doer = mallocdoer;
mallocdoer = NULL;
return((void*)mmep->area+4);
}
void Free(char **mem)
{
aME *mmep;
aMEA *mp;
int *src;
int i;
if (*mem == NULL)
return;
src = (int*)((*mem)-4);
mmep = NULL;
mp = mrrec;
while(!mmep)
{
for(i=0;i<MRSIZE;i++)
{
if (mp->mme[i].area == src)
{
mmep = &mp->mme[i];
break;
}
}
if (!mmep)
{
if (mp->next == NULL)
{
debug("(Free) PANIC: Free(0x"mx_pfmt"); Unregistered memory block\n",(mx_ptr)src);
run_debug();
/*exit(1);*/
/* overreacting. just ignore it and accept the leak. */
return;
}
mp = mp->next;
}
}
if (*src)
debug("Free: Heap corruption at 0x"mx_pfmt"\n",(mx_ptr)(*mem)-4);
mmep->area = NULL;
mmep->size = 0;
mmep->when = (time_t)0;
free((*mem)-4);
*mem = NULL;
}
#else /* DEBUG */
void *Calloc(int size)
{
void *tmp;
if ((tmp = (void*)calloc(size,1)) == NULL)
exit(1);
return((void*)tmp);
}
/*
* Free() can be called with NULL's
*/
void Free(char **mem)
{
if (*mem)
{
free(*mem);
*mem = NULL;
}
}
#endif /* DEBUG */
void set_mix16(Mix16 *mix, const char *str)
{
int sz;
if (mix->opt >= 16)
{
Free(&mix->x.ptr);
}
sz = strlen(str);
if (sz < 16)
{
stringcpy(mix->x.string,str);
}
else
{
set_mallocdoer(set_mix16);
mix->x.ptr = stringdup(str);
}
mix->opt = sz;
}
void set_mix64(Mix64 *mix, const char *str)
{
int sz;
if (mix->opt >= 64)
{
Free(&mix->x.ptr);
}
sz = strlen(str);
if (sz < 64)
{
stringcpy(mix->x.string,str);
}
else
{
set_mallocdoer(set_mix64);
mix->x.ptr = stringdup(str);
}
mix->opt = sz;
}
Strp *make_strp(Strp **pp, const char *string)
{
set_mallocdoer(make_strp);
*pp = (Strp*)Calloc(sizeof(Strp) + strlen(string));
stringcpy((*pp)->p,string);
return(*pp);
}
Strp *append_strp(Strp **pp, const char *string)
{
while((*pp) != NULL)
pp = &((*pp)->next);
make_strp(pp,string);
return(*pp);
}
Strp *prepend_strp(Strp **pp, const char *string)
{
Strp *sp;
make_strp(&sp,string);
sp->next = *pp;
*pp = sp;
return(sp);
}
/*
* Free() a whole linked list of any suitable type (Strp, Banlist)
*/
void purge_linklist(void **list)
{
Strp *sp,*nxt;
sp = *list;
while(sp)
{
nxt = sp->next;
Free((char**)&sp);
sp = nxt;
}
*list = NULL;
}
/*
* duplicate a list of Strp
*/
void dupe_strp(Strp *sp, Strp **pp)
{
while(sp)
{
make_strp(pp,sp->p);
pp = &((*pp)->next);
sp = sp->next;
}
}
char *getuh(char *nuh)
{
char *s;
s = nuh;
while(*s)
{
if (*s == '!')
{
/*
* We have to grab everything from the first '!' since some
* braindamaged ircds allow '!' in the "user" part of the nuh
*/
return(s + 1);
}
s++;
}
return(nuh);
}
/*
* caller is responsible for:
*
* src != NULL
* *src != NULL
*
*/
char *get_token(char **src, const char *token_sep)
{
const char *s;
char *token = NULL;
/*
* skip past any token_sep chars in the beginning
*/
if (0) /* is this legal C? */
a: ++(*src);
s = token_sep;
while(**src && *s)
{
if (*(s++) == **src)
goto a;
}
if (token || **src == 0)
return(token);
token = *src;
/*
* find the next token_sep char
*/
do {
s = token_sep;
do {
if (*s == **src)
{
**src = 0;
goto a;
}
++s;
}
while(*s);
(*src)++;
}
while(**src);
return(token);
}
#endif /* ifndef TEST */
/*
* time to string routines
*/
char *maketimestr(time_t when, int format)
{
struct tm *btime;
char *dest,*f,ampm;
int option;
btime = localtime(&when);
dest = timebuf;
do
{
option = format & 0x7;
format = format >> 4;
f = "%02i:%02i:%02i";
switch(option)
{
case 0:
*(dest++) = ' ';
break;
case 1:/* HH:mm */
f += 5;
case 2:/* HH:mm:ss */
dest += sprintf(dest,f,btime->tm_hour,btime->tm_min,btime->tm_sec);
break;
case 3:/* WeekDay */
/* stringcpy return a pointer to the last char, not how many chars was copied */
dest = stringcpy(dest,daylist[btime->tm_wday]);
break;
case 4:/* ascii-Month Day */
dest += sprintf(dest,"%s %i",monlist[btime->tm_mon],btime->tm_mday);
break;
case 5:/* num-Month Day */
dest += sprintf(dest,"%02i %02i",btime->tm_mon+1,btime->tm_mday);
break;
case 6:/* Year */
dest += sprintf(dest,"%i",btime->tm_year+1900);
break;
case 7:/* am/pm */
unsigned char a,b;
a = ((unsigned char)btime->tm_hour - 1);
a >>= 7;
b = ((unsigned char)btime->tm_hour - 12);
b >>= 7;
btime->tm_hour += (a * 12) - 12 * (b^1);
ampm = 'p' - (b * ('p' - 'a'));
dest += sprintf(dest,"%i:%02i%cm",btime->tm_hour,btime->tm_min,ampm);
break;
}
}
while(format);
return(timebuf);
}
char *idle2str(time_t when, int small)
{
char *dst;
int n,z[4];
when = cx.now - when;
z[0] = when / 86400;
z[1] = (when -= z[0] * 86400) / 3600;
z[2] = (when -= z[1] * 3600) / 60;
z[3] = when % 60;
#ifdef DEBUG
{
struct tm *btime;
btime = localtime(&when);
debug("(idle2str) days %i, hours %i, minutes %i, seconds %i\n"
"(idle2str) d %i, h %i, m %i, s %i\n",
z[0],z[1],z[2],z[3],
btime->tm_yday,btime->tm_hour,btime->tm_min,btime->tm_sec);
}
#endif /* DEBUG*/
/* 32 : "9999 days, 24 hours, 59 minutes" */
/* xx : "24 hours, 59 minutes" */
/* xx : "59 minutes, 59 seconds" */
/* xx : "59 seconds" */
if (small)
{
const char *f[] = {"day","hour","minute","second"};
*idlestr = 0;
for(n=0;n<4;n++)
{
if (*idlestr || z[n])
{
dst = STREND(idlestr);
sprintf(dst,"%s%i %s%s",(*idlestr) ? ", " : "",z[n],f[n],(z[n]==1) ? "" : "s");
}
}
}
else
/* 18+1 (up to 9999 days) */
sprintf(idlestr,"%i day%s %02i:%02i:%02i",z[0],EXTRA_CHAR(z[0]),z[1],z[2],z[3]);
return(idlestr);
}
#ifndef TEST
const char *get_channel(const char *to, char **rest)
{
const char *channel;
if (*rest && ischannel(*rest))
{
channel = chop(rest);
}
else
{
if (!ischannel(to) && current->activechan)
channel = current->activechan->name;
else
channel = to;
}
return(channel);
}
const char *get_channel2(const char *to, char **rest)
{
const char *channel;
if (*rest && (**rest == '*' || ischannel(*rest)))
{
channel = chop(rest);
}
else
{
if (!ischannel(to) && current->activechan)
channel = current->activechan->name;
else
channel = to;
}
return(channel);
}
char *cluster(char *hostname)
{
char mask[NUHLEN];
char *p,*host;
char num,dot;
host = p = hostname;
num = dot = 0;
while(*p)
{
if (*p == '@')
{
host = p + 1;
num = dot = 0;
}
else
if (*p == '.')
dot++;
else
if (*p < '0' || *p > '9')
num++;
p++;
}
if (!num && (dot == 3))
{
/*
* its a numeric IP address
* 1.2.3.4 --> 1.2.*.*
*/
p = mask;
while(*host)
{
if (*host == '.')
{
if (num)
break;
num++;
}
*(p++) = *(host++);
}
stringcpy(p,".*.*");
}
else
{
/*
* its not a numeric mask
*/
p = mask;
*(p++) = '*';
num = (dot >= 4) ? 2 : 1;
while(*host)
{
if (*host == '.')
dot--;
if (dot <= num)
break;
host++;
}
stringcpy(p,host);
}
stringcpy(hostname,mask);
return(hostname);
}
/*
* type output
* ~~~~ ~~~~~~
* 0,1 *!*user@*.host.com
* 2 *!*@*.host.com
*/
char *format_uh(char *userhost, int type)
{
char tmpmask[NUHLEN];
char *u,*h;
if (stringchr(userhost,'*'))
return(userhost);
stringcpy(tmpmask,userhost);
h = tmpmask;
get_token(&h,"!"); /* discard nickname */
u = get_token(&h,"@");
if (*h == 0)
return(userhost);
if (u && (type < 2))
{
if ((type = strlen(u)) > 9)
u += (type - 9);
else
if (*u == '~')
u++;
}
sprintf(userhost,"*!*%s@%s",(u) ? u : "",cluster(h));
return(userhost);
}
int is_nick(const char *nick)
{
uchar *p;
p = (uchar*)nick;
if ((attrtab[*p] & FNICK) != FNICK)
return(FALSE);
while(*p)
{
if ((attrtab[*p] & NICK) != NICK)
return(FALSE);
p++;
}
return(TRUE);
}
#endif /* ifndef TEST */
/* asc2int requires the whole string *anum to be a valid numeric */
int asc2int(const char *anum)
{
int res = 0, neg;
errno = EINVAL;
if (!anum || !*anum)
return(-1);
neg = (*anum == '-') ? 1 : 0;
anum += neg;
while(*anum)
{
if (0 == (attrtab[(uchar)*anum] & NUM))
return(-1);
res = (res * 10) + *(anum++) - '0';
}
errno = 0;
return((neg) ? -res : res);
}
/* get_number returns as soon as a non numeric character is encountered */
int get_number(const char *rest)
{
const char *src = NULL;
int n = 0;
while(*rest)
{
if (0 == (attrtab[(uchar)*rest] & NUM))
n = (n * 10) + (*(src = rest) - '0');
else
if (src)
return(n);
rest++;
}
return(n);
}
#define checkifdigit(xcx) (xcx >= '0' && xcx <= '9')
void parse_range(char **s, int *a)
{
int v;
a[0] = -1;
a[1] = -1;
a[2] = 0;
if (0 == checkifdigit(**s))
return;
v = 0;
while(checkifdigit(**s))
{
v = (v * 10) + **s - '0';
(*s)++;
}
a[0] = v;
/* optional second number */
if (**s == '-')
{
a[2] = 1;
(*s)++;
}
if (0 == checkifdigit(**s))
return;
v = 0;
while(checkifdigit(**s))
{
v = (v * 10) + **s - '0';
(*s)++;
}
a[1] = v;
}
void fix_config_line(char *text)
{
char *s,*space;
space = NULL;
for(s=text;*s;s++)
{
if (*s == '\t')
*s = ' ';
if (!space && *s == ' ')
space = s;
if (space && *s != ' ')
space = NULL;
}
if (space)
*space = 0;
}
/*
* mask matching
* returns 0 for match
* returns 1 for non-match
*/
int matches(const char *mask, const char *text)
{
const uchar *m = (uchar*)mask;
const uchar *n = (uchar*)text;
const uchar *orig = (uchar*)mask;
int wild,q;
q = wild = 0;
if (!m || !n)
return(TRUE);
loop:
if (!*m)
{
if (!*n)
return(FALSE);
for (m--;((*m == '?') && (m > orig));m--)
;
if ((*m == '*') && (m > orig) && (m[-1] != '\\'))
return(FALSE);
if (wild)
{
m = (uchar *)mask;
n = (uchar *)++text;
}
else
return(TRUE);
}
else
if (!*n)
{
while(*m == '*')
m++;
return(*m != 0);
}
if (*m == '*')
{
while (*m == '*')
m++;
wild = 1;
mask = (char *)m;
text = (char *)n;
}
if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
{
m++;
q = 1;
}
else
q = 0;
if ((tolowertab[(uchar)*m] != tolowertab[(uchar)*n]) && ((*m != '?') || q))
{
if (wild)
{
m = (uchar *)mask;
n = (uchar *)++text;
}
else
return(TRUE);
}
else
{
if (*m)
m++;
if (*n)
n++;
}
goto loop;
}
int num_matches(const char *mask, const char *text)
{
const char *p = mask;
int n;
n = !matches(mask,text);
if (n)
{
do {
if (*p != '*')
n++;
}
while(*++p);
}
return(n);
}
int is_safepath(const char *path, int filemustexist)
{
struct stat st;
char tmp[PATH_MAX];
const char *src;
char *dst;
int mo,dir_r,orr,oerrno;
#ifdef TEST
memset(&st,0,sizeof(st));
#endif
if (*(src = path) == '/') /* dont allow starting at root, only allow relative paths */
return(-1);
if (strlen(path) >= PATH_MAX)
return(-6);
orr = lstat(path,&st);
oerrno = errno;
if (filemustexist == FILE_MUST_EXIST && orr == -1 && errno == ENOENT)
return(-2);
if (filemustexist == FILE_MUST_NOTEXIST && orr == 0)
return(-3);
mo = st.st_mode; /* save mode for later */
dir_r = -1;
for(dst=tmp;*src;)
{
if (*src == '/')
{
*dst = 0;
if ((dir_r = lstat(tmp,&st)) == -1 && errno == ENOENT)
return(-7);
if (!(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))) /* disallow all except regular files and directories */
return(-4);
if (st.st_ino == parent_inode) /* disallow traversing below bots homedir */
return(-5);
}
if (dst == tmp + PATH_MAX-1)
return(-6);
*dst++ = *src++;
}
if (filemustexist != FILE_MUST_EXIST && orr == -1 && oerrno == ENOENT)
return(TRUE);
return (S_ISREG(mo)) ? TRUE : FALSE;
}
#ifdef TEST
#include "debug.c"
#define P0 "verylongexcessivelengthfilenamegibberish"
#define P1 P0 "/" P0 "/" P0 "/" P0 "/" P0 "/" P0 "/" P0 "/" P0 "/" P0 "/" P0 "/"
#define P2 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P1 P0
char *tostr[] = { "ZERO", "FILE_MUST_EXIST", "FILE_MAY_EXIST", "FILE_MUST_NOTEXIST" };
void *Calloc(int size)
{
return(calloc(1,size));
}
void testcase(const char *str, int expected, int filemustexist)
{
int r;
r = is_safepath(str,filemustexist);
if (r == expected)
{
debug("testcase SUCCESS: testpath %s %s -> result %i\n",
(strlen(str)>50) ? "(very long string)" : str,tostr[filemustexist],r);
}
else
{
debug("testcase FAIL: testpath %s(%i) %s -> result %i, expected %i\n",
(strlen(str)>50) ? "(very long string)" : str,
strlen(str),tostr[filemustexist],r,expected);/*(r) ? "TRUE" : "FALSE",(expected) ? "TRUE" : "FALSE"); */
}
}
const char teststr[] = "dingchat";
void teststring(void)
{
char str[100];
stringcpy(str,teststr);
if (stringcmp(str,teststr))
debug("teststring FAIL: stringcpy(str,%s) != %s (%s)\n",teststr,teststr,str);
else
debug("teststring SUCCESS: stringcpy(str,%s) == %s\n",teststr,teststr);
stringcpy_n(str+4,"xoom",2);
if (stringcmp(str,"dingxo"))
debug("teststring FAIL: stringcpy_n(str+4,%s,2) != %s (%s)\n","xoom","dingxo",str);
else
debug("teststring SUCCESS: stringcpy_n(str+4,%s,2) == %s\n","xoom","dingxo");
stringcat(str,"free");
if (stringcmp(str,"dingxofree"))
debug("teststring FAIL: stringcat(str,%s) != %s (%s)\n","free","dingxofree",str);
else
debug("teststring SUCCESS: stringcat(str,%s) == %s\n","free","dingxofree");
}
int main(int argc, char **argv, char **envp)
{
char mybuffer[64];
struct stat st;
time_t when;
int r;
dodebug = 1;
stat("../..",&st);
parent_inode = st.st_ino; /* used for is_safepath() */
debug("PATH_MAX = %i\n",PATH_MAX);
if (argv[1] == NULL)
{
testcase("/etc/passwd",-1,FILE_MAY_EXIST);
testcase("../../../../../../../../../../../etc/passwd",-5,FILE_MAY_EXIST);
testcase("../anyparentfile",1,FILE_MAY_EXIST);
testcase("../../anyparentparentfile",-5,FILE_MAY_EXIST);
testcase("function.c",TRUE,FILE_MUST_EXIST);
testcase("./function.c",TRUE,FILE_MUST_EXIST);
testcase("function.c",-3,FILE_MUST_NOTEXIST);
testcase("./function.c",-3,FILE_MUST_NOTEXIST);
testcase("nosuchfile",1,FILE_MAY_EXIST);
testcase("nosuchfile",1,FILE_MUST_NOTEXIST);
testcase("./nosuchfile",1,FILE_MUST_NOTEXIST);
testcase("../../nosuchfile",-5,FILE_MUST_NOTEXIST);
testcase(P2,-6,FILE_MAY_EXIST);
teststring();
}
if (argv[1])
{
r = is_safepath(argv[1],FILE_MAY_EXIST);
debug("testpath %s -> result %s\n",argv[1],(r) ? "TRUE" : "FALSE");
}
time(&cx.now);
for(r=0;r<10;r++)
{
when = cx.now - (int[]){100000,888,534569,999999,99,9000,84600,7777777,56565656+3600,78987654}[r];
debug("\nmaketimestr %s\n",maketimestr(when,TFMT_LOG));
debug("maketimestr %s\n",maketimestr(when,TFMT_FULL));
debug("maketimestr %s\n",maketimestr(when,TFMT_AWAY));
debug("maketimestr %s\n",maketimestr(when,TFMT_CLOCK));
debug("maketimestr %s\n",maketimestr(when,TFMT_DATE));
}
}
#endif /* TEST */