--- sbbs/src/sbbs3/userdat.c 2018/04/24 16:41:23 1.1 +++ sbbs/src/sbbs3/userdat.c 2018/04/24 16:42:46 1.1.1.2 @@ -2,13 +2,13 @@ /* Synchronet user data-related routines (exported) */ -/* $Id: userdat.c,v 1.1 2018/04/24 16:41:23 root Exp $ */ +/* $Id: userdat.c,v 1.1.1.2 2018/04/24 16:42:46 root Exp $ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * - * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html * + * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * @@ -52,12 +52,13 @@ char* nulstr=""; /* Makes dots and underscores synomynous with spaces for comparisions */ /* Returns the number of the perfect matched username or 0 if no match */ /****************************************************************************/ -uint DLLCALL matchuser(scfg_t* cfg, char *name, BOOL sysop_alias) +uint DLLCALL matchuser(scfg_t* cfg, const char *name, BOOL sysop_alias) { int file,c; char* p; char dat[LEN_ALIAS+2]; char str[256]; + char tmp[256]; ulong l,length; FILE* stream; @@ -68,10 +69,10 @@ uint DLLCALL matchuser(scfg_t* cfg, char (!stricmp(name,"SYSOP") || !stricmp(name,"POSTMASTER") || !stricmp(name,cfg->sys_id))) return(1); - sprintf(str,"%suser/name.dat",cfg->data_dir); + SAFEPRINTF(str,"%suser/name.dat",cfg->data_dir); if((file=nopen(str,O_RDONLY))==-1) return(0); - length=filelength(file); + length=(long)filelength(file); if((stream=fdopen(file,"rb"))==NULL) return(0); for(l=0;ldata_dir); + SAFEPRINTF(str,"%suser/user.dat", cfg->data_dir); if((file=nopen(str,O_RDONLY|O_DENYNONE))==-1) return(0); - length=filelength(file); + length=(long)filelength(file); for(l=0;ldata_dir); - if((length=flength(str))>0) + SAFEPRINTF(str,"%suser/user.dat", cfg->data_dir); + if((length=(long)flength(str))>0) return((uint)(length/U_LEN)); return(0); } @@ -176,10 +182,10 @@ BOOL DLLCALL del_lastuser(scfg_t* cfg) if(!VALID_CFG(cfg)) return(FALSE); - sprintf(str,"%suser/user.dat", cfg->data_dir); + SAFEPRINTF(str,"%suser/user.dat", cfg->data_dir); if((file=nopen(str,O_RDWR|O_DENYNONE))==-1) return(FALSE); - length=filelength(file); + length=(long)filelength(file); if(lengthdata_dir); + SAFEPRINTF(userdat,"%suser/user.dat",cfg->data_dir); if((file=nopen(userdat,O_RDONLY|O_DENYNONE))==-1) return(errno); @@ -312,7 +318,7 @@ int DLLCALL getuserdat(scfg_t* cfg, user getrec(userdat,U_XEDIT,8,str); for(i=0;itotal_xedits;i++) - if(!stricmp(str,cfg->xedit[i]->code) && chk_ar(cfg,cfg->xedit[i]->ar,user)) + if(!stricmp(str,cfg->xedit[i]->code) && chk_ar(cfg,cfg->xedit[i]->ar,user,/* client: */NULL)) break; user->xedit=i+1; if(user->xedit>cfg->total_xedits) @@ -343,36 +349,49 @@ int DLLCALL getuserdat(scfg_t* cfg, user getrec(userdat,U_CHAT,8,str); user->chat=ahtoul(str); - /* Reset daily stats if not logged on today */ - unixtodstr(cfg, time(NULL),str); - unixtodstr(cfg, user->laston,tmp); - if(strcmp(str,tmp) && user->ltoday) - resetdailyuserdat(cfg,user); - -#if 0 /* removed 01/19/00 Why? ToDo */ - if(useron.number==user_number) { - if(user!=&useron) - useron=*user; - - if(online) { - - #if 0 /* legacy? ToDo */ - getusrdirs(); - getusrsubs(); - #endif - if(user->misc&AUTOTERM) { /* was useron.misc (01/19/00) */ - user->misc&=~(ANSI|RIP|WIP|HTML); - user->misc|=autoterm; - } - } - } -#endif user->number=user_number; /* Signal of success */ + /* Reset daily stats if not already logged on today */ + if(user->ltoday || user->etoday || user->ptoday || user->ttoday) { + time_t now; + struct tm now_tm; + struct tm logon_tm; + + now=time(NULL); + if(localtime_r(&now, &now_tm)!=NULL + && localtime_r(&user->logontime, &logon_tm)!=NULL) { + if(now_tm.tm_year!=logon_tm.tm_year + || now_tm.tm_mon!=logon_tm.tm_mon + || now_tm.tm_mday!=logon_tm.tm_mday) + resetdailyuserdat(cfg,user,/* write: */FALSE); + } + } + return(0); } /****************************************************************************/ +/****************************************************************************/ +static void dirtyuserdat(scfg_t* cfg, uint usernumber) +{ + int i,file; + node_t node; + + for(i=1;i<=cfg->sys_nodes;i++) { /* instant user data update */ +// if(i==cfg->node_num) +// continue; + getnodedat(cfg, i,&node,NULL); + if(node.useron==usernumber && (node.status==NODE_INUSE + || node.status==NODE_QUIET)) { + getnodedat(cfg, i,&node,&file); + node.misc|=NODE_UDAT; + putnodedat(cfg, i,&node,file); + break; + } + } +} + +/****************************************************************************/ /* Writes into user.number's slot in user.dat data in structure 'user' */ /* Called from functions newuser, useredit and main */ /****************************************************************************/ @@ -380,7 +399,6 @@ int DLLCALL putuserdat(scfg_t* cfg, user { int i,file; char userdat[U_LEN],str[MAX_PATH+1]; - node_t node; if(user==NULL) return(-1); @@ -484,7 +502,7 @@ int DLLCALL putuserdat(scfg_t* cfg, user putrec(userdat,U_UNUSED,U_LEN-(U_UNUSED)-2,crlf); putrec(userdat,U_UNUSED+(U_LEN-(U_UNUSED)-2),2,crlf); - sprintf(str,"%suser/user.dat", cfg->data_dir); + SAFEPRINTF(str,"%suser/user.dat", cfg->data_dir); if((file=nopen(str,O_RDWR|O_CREAT|O_DENYNONE))==-1) { return(errno); } @@ -516,18 +534,7 @@ int DLLCALL putuserdat(scfg_t* cfg, user } unlock(file,(long)((long)(user->number-1)*U_LEN),U_LEN); close(file); - for(i=1;i<=cfg->sys_nodes;i++) { /* instant user data update */ - if(i==cfg->node_num) - continue; - getnodedat(cfg, i,&node,NULL); - if(node.useron==user->number && (node.status==NODE_INUSE - || node.status==NODE_QUIET)) { - getnodedat(cfg, i,&node,&file); - node.misc|=NODE_UDAT; - putnodedat(cfg, i,&node,file); - break; - } - } + dirtyuserdat(cfg,user->number); return(0); } @@ -549,7 +556,7 @@ char* DLLCALL username(scfg_t* cfg, int name[0]=0; return(name); } - sprintf(str,"%suser/name.dat",cfg->data_dir); + SAFEPRINTF(str,"%suser/name.dat",cfg->data_dir); if(flength(str)<1L) { name[0]=0; return(name); @@ -588,10 +595,10 @@ int DLLCALL putusername(scfg_t* cfg, int if(!VALID_CFG(cfg) || name==NULL || number<1) return(-1); - sprintf(str,"%suser/name.dat", cfg->data_dir); + SAFEPRINTF(str,"%suser/name.dat", cfg->data_dir); if((file=nopen(str,O_RDWR|O_CREAT))==-1) return(errno); - length=filelength(file); + length=(long)filelength(file); /* Truncate corrupted name.dat */ total_users=lastuser(cfg); @@ -603,7 +610,7 @@ int DLLCALL putusername(scfg_t* cfg, int return(-3); } if(length<(((long)number-1)*(LEN_ALIAS+2))) { - sprintf(str,"%*s",LEN_ALIAS,nulstr); + SAFEPRINTF2(str,"%*s",LEN_ALIAS,nulstr); memset(str,ETX,LEN_ALIAS); strcat(str,crlf); lseek(file,0L,SEEK_END); @@ -624,9 +631,9 @@ int DLLCALL putusername(scfg_t* cfg, int /****************************************************************************/ /* Returns the age derived from the string 'birth' in the format MM/DD/YY */ /****************************************************************************/ -char DLLCALL getage(scfg_t* cfg, char *birth) +uint DLLCALL getage(scfg_t* cfg, char *birth) { - char age; + uint age; struct tm tm; time_t now; @@ -641,7 +648,7 @@ char DLLCALL getage(scfg_t* cfg, char *b return(0); age=(tm.tm_year)-(((birth[6]&0xf)*10)+(birth[7]&0xf)); if(age>105) - age-=105; + age-=100; tm.tm_mon++; /* convert to 1 based */ if(cfg->sys_misc&SM_EURODATE) { /* DD/MM/YY format */ if(atoi(birth)>31 || atoi(birth+3)>12) @@ -658,12 +665,9 @@ char DLLCALL getage(scfg_t* cfg, char *b ((birth[3]&0xf)*10)+(birth[4]&0xf)>tm.tm_mday)) age--; } - if(age<0) - return(0); return(age); } - /****************************************************************************/ /* Reads the data for node number 'number' into the structure 'node' */ /* from node.dab */ @@ -683,7 +687,7 @@ int DLLCALL getnodedat(scfg_t* cfg, uint return(-1); memset(node,0,sizeof(node_t)); - sprintf(str,"%snode.dab",cfg->ctrl_dir); + SAFEPRINTF(str,"%snode.dab",cfg->ctrl_dir); if((file=nopen(str,O_RDWR|O_DENYNONE))==-1) return(errno); @@ -800,9 +804,33 @@ char* DLLCALL unpackchatpass(char *pass, return(pass); } +static char* node_connection_desc(ushort conn, char* str) +{ + switch(conn) { + case NODE_CONNECTION_LOCAL: + strcpy(str,"Locally"); + break; + case NODE_CONNECTION_TELNET: + strcpy(str,"via telnet"); + break; + case NODE_CONNECTION_RLOGIN: + strcpy(str,"via rlogin"); + break; + case NODE_CONNECTION_SSH: + strcpy(str,"via ssh"); + break; + default: + sprintf(str,"at %ubps",conn); + break; + } + + return str; +} + char* DLLCALL nodestatus(scfg_t* cfg, node_t* node, char* buf, size_t buflen) { char str[256]; + char tmp[128]; char* mer; int hour; @@ -813,35 +841,31 @@ char* DLLCALL nodestatus(scfg_t* cfg, no switch(node->status) { case NODE_WFC: - strcpy(str,"Waiting for call"); + strcpy(str,"Waiting for connection"); break; case NODE_OFFLINE: strcpy(str,"Offline"); break; - case NODE_NETTING: + case NODE_NETTING: /* Obsolete */ strcpy(str,"Networking"); break; case NODE_LOGON: - strcpy(str,"At logon prompt"); + SAFEPRINTF(str,"At logon prompt %s" + ,node_connection_desc(node->connection, tmp)); break; case NODE_EVENT_WAITING: strcpy(str,"Waiting for all nodes to become inactive"); break; case NODE_EVENT_LIMBO: - sprintf(str,"Waiting for node %d to finish external event" + SAFEPRINTF(str,"Waiting for node %d to finish external event" ,node->aux); break; case NODE_EVENT_RUNNING: strcpy(str,"Running external event"); break; case NODE_NEWUSER: - strcpy(str,"New user applying for access "); - if(!node->connection) - strcat(str,"Locally"); - else if(node->connection==0xffff) { - strcat(str,"via telnet"); - } else - sprintf(str+strlen(str),"at %ubps",node->connection); + SAFEPRINTF(str,"New user applying for access %s" + ,node_connection_desc(node->connection, tmp)); break; case NODE_QUIET: case NODE_INUSE: @@ -949,12 +973,7 @@ char* DLLCALL nodestatus(scfg_t* cfg, no sprintf(str+strlen(str),"%d",node->action); break; } - if(!node->connection) - strcat(str," locally"); - if(node->connection==0xffff) { - strcat(str," via telnet"); - } else - sprintf(str+strlen(str)," at %ubps",node->connection); + sprintf(str+strlen(str)," %s",node_connection_desc(node->connection, tmp)); if(node->action==NODE_DLNG) { if((node->aux/60)>=12) { if(node->aux/60==12) @@ -1028,7 +1047,7 @@ void DLLCALL printnodedat(scfg_t* cfg, u /****************************************************************************/ uint DLLCALL userdatdupe(scfg_t* cfg, uint usernumber, uint offset, uint datlen - ,char *dat, BOOL del) + ,char *dat, BOOL del, BOOL next) { char str[MAX_PATH+1]; uint i; @@ -1039,12 +1058,16 @@ uint DLLCALL userdatdupe(scfg_t* cfg, ui return(0); truncsp(dat); - sprintf(str,"%suser/user.dat", cfg->data_dir); + SAFEPRINTF(str,"%suser/user.dat", cfg->data_dir); if((file=nopen(str,O_RDONLY|O_DENYNONE))==-1) return(0); - length=filelength(file); - for(l=0;ldata_dir,usernumber); + SAFEPRINTF2(str,"%smsgs/%4.4u.msg",cfg->data_dir,usernumber); if((file=nopen(str,O_WRONLY|O_CREAT|O_APPEND))==-1) { return(errno); } @@ -1136,12 +1159,23 @@ char* DLLCALL getsmsg(scfg_t* cfg, int u if(!VALID_CFG(cfg) || usernumber<1) return(NULL); - sprintf(str,"%smsgs/%4.4u.msg",cfg->data_dir,usernumber); + for(i=1;i<=cfg->sys_nodes;i++) { /* clear msg waiting flag */ + getnodedat(cfg,i,&node,NULL); + if(node.useron==usernumber + && (node.status==NODE_INUSE || node.status==NODE_QUIET) + && node.misc&NODE_MSGW) { + getnodedat(cfg,i,&node,&file); + node.misc&=~NODE_MSGW; + putnodedat(cfg,i,&node,file); + } + } + + SAFEPRINTF2(str,"%smsgs/%4.4u.msg",cfg->data_dir,usernumber); if(flength(str)<1L) return(NULL); if((file=nopen(str,O_RDWR))==-1) return(NULL); - length=filelength(file); + length=(long)filelength(file); if((buf=(char *)malloc(length+1))==NULL) { close(file); return(NULL); @@ -1155,17 +1189,6 @@ char* DLLCALL getsmsg(scfg_t* cfg, int u close(file); buf[length]=0; - for(i=1;i<=cfg->sys_nodes;i++) { /* clear msg waiting flag */ - getnodedat(cfg,i,&node,NULL); - if(node.useron==usernumber - && (node.status==NODE_INUSE || node.status==NODE_QUIET) - && node.misc&NODE_MSGW) { - getnodedat(cfg,i,&node,&file); - node.misc&=~NODE_MSGW; - putnodedat(cfg,i,&node,file); - } - } - return(buf); /* caller must free */ } @@ -1184,12 +1207,12 @@ char* DLLCALL getnmsg(scfg_t* cfg, int n node.misc&=~NODE_NMSG; /* clear the NMSG flag */ putnodedat(cfg,node_num,&node,file); - sprintf(str,"%smsgs/n%3.3u.msg",cfg->data_dir,node_num); + SAFEPRINTF2(str,"%smsgs/n%3.3u.msg",cfg->data_dir,node_num); if(flength(str)<1L) return(NULL); if((file=nopen(str,O_RDWR))==-1) return(NULL); - length=filelength(file); + length=(long)filelength(file); if(!length) { close(file); return(NULL); @@ -1225,7 +1248,7 @@ int DLLCALL putnmsg(scfg_t* cfg, int num if(*strin==0) return(0); - sprintf(str,"%smsgs/n%3.3u.msg",cfg->data_dir,num); + SAFEPRINTF2(str,"%smsgs/n%3.3u.msg",cfg->data_dir,num); if((file=nopen(str,O_WRONLY|O_CREAT))==-1) return(errno); lseek(file,0L,SEEK_END); /* Instead of opening with O_APPEND */ @@ -1286,13 +1309,14 @@ static int getgrpnum(scfg_t* cfg, char* return(-1); } -static BOOL ar_exp(scfg_t* cfg, uchar **ptrptr, user_t* user) +static BOOL ar_exp(scfg_t* cfg, uchar **ptrptr, user_t* user, client_t* client) { BOOL result,not,or,equal; uint i,n,artype=AR_LEVEL,age; ulong l; time_t now; struct tm tm; + const char* p; result = TRUE; @@ -1323,7 +1347,7 @@ static BOOL ar_exp(scfg_t* cfg, uchar ** if((**ptrptr)==AR_BEGNEST) { (*ptrptr)++; - if(ar_exp(cfg,ptrptr,user)) + if(ar_exp(cfg,ptrptr,user,client)) result=!not; else result=not; @@ -1347,6 +1371,9 @@ static BOOL ar_exp(scfg_t* cfg, uchar ** case AR_QUIET: case AR_OS2: case AR_DOS: + case AR_WIN32: + case AR_UNIX: + case AR_LINUX: break; default: (*ptrptr)++; @@ -1525,11 +1552,10 @@ static BOOL ar_exp(scfg_t* cfg, uchar ** (*ptrptr)++; break; case AR_SUBCODE: - if(user!=NULL && stricmp(user->cursub,(char *)*ptrptr)==0) + if(user!=NULL && !findstr_in_string(user->cursub,(char *)*ptrptr)==0) result=!not; else result=not; - result=not; while(*(*ptrptr)) (*ptrptr)++; break; @@ -1558,7 +1584,7 @@ static BOOL ar_exp(scfg_t* cfg, uchar ** (*ptrptr)++; break; case AR_DIRCODE: - if(user!=NULL && stricmp(user->curdir,(char *)*ptrptr)==0) + if(user!=NULL && !findstr_in_string(user->curdir,(char *)*ptrptr)==0) result=!not; else result=not; @@ -1658,6 +1684,48 @@ static BOOL ar_exp(scfg_t* cfg, uchar ** result=!not; } break; + case AR_ULS: + if(user==NULL || (equal && user->uls!=i) || (!equal && user->ulsulb/1024!=i) || (!equal && user->ulb/1024ulb/(1024*1024)!=i) || (!equal && user->ulb/(1024*1024)dls!=i) || (!equal && user->dlsdlb/1024!=i) || (!equal && user->dlb/1024dlb/(1024*1024)!=i) || (!equal && user->dlb/(1024*1024)flags1&FLAG(n))) @@ -1715,7 +1783,7 @@ static BOOL ar_exp(scfg_t* cfg, uchar ** case AR_SHELL: if(user==NULL || user->shell>=cfg->total_shells - || stricmp(cfg->shell[user->shell]->code,(char*)*ptrptr)) + || !findstr_in_string(cfg->shell[user->shell]->code,(char*)*ptrptr)) result=not; else result=!not; @@ -1723,8 +1791,41 @@ static BOOL ar_exp(scfg_t* cfg, uchar ** (*ptrptr)++; break; case AR_PROT: - if(user==NULL - || stricmp(user->modem,(char*)*ptrptr)) /* should this be changed to client.prot? */ + if(client!=NULL) + p=client->protocol; + else if(user!=NULL) + p=user->modem; + else + p=NULL; + if(!findstr_in_string(p,(char*)*ptrptr)) + result=not; + else + result=!not; + while(*(*ptrptr)) + (*ptrptr)++; + break; + case AR_HOST: + if(client!=NULL) + p=client->host; + else if(user!=NULL) + p=user->comp; + else + p=NULL; + if(!findstr_in_string(p,(char*)*ptrptr)) + result=not; + else + result=!not; + while(*(*ptrptr)) + (*ptrptr)++; + break; + case AR_IP: + if(client!=NULL) + p=client->addr; + else if(user!=NULL) + p=user->note; + else + p=NULL; + if(!findstr_in_string(p,(char*)*ptrptr)) result=not; else result=!not; @@ -1736,7 +1837,7 @@ static BOOL ar_exp(scfg_t* cfg, uchar ** return(result); } -BOOL DLLCALL chk_ar(scfg_t* cfg, uchar *ar, user_t* user) +BOOL DLLCALL chk_ar(scfg_t* cfg, uchar *ar, user_t* user, client_t* client) { uchar *p; @@ -1745,7 +1846,7 @@ BOOL DLLCALL chk_ar(scfg_t* cfg, uchar * if(!VALID_CFG(cfg)) return(FALSE); p=ar; - return(ar_exp(cfg,&p,user)); + return(ar_exp(cfg,&p,user,client)); } /****************************************************************************/ @@ -1759,7 +1860,7 @@ int DLLCALL getuserrec(scfg_t* cfg, int if(!VALID_CFG(cfg) || usernumber<1 || str==NULL) return(-1); - sprintf(path,"%suser/user.dat",cfg->data_dir); + SAFEPRINTF(path,"%suser/user.dat",cfg->data_dir); if((file=nopen(path,O_RDONLY|O_DENYNONE))==-1) return(errno); if(usernumber<1 @@ -1804,17 +1905,16 @@ int DLLCALL getuserrec(scfg_t* cfg, int /* Places into user.dat at the offset for usernumber+start for length bytes */ /* Called from various locations */ /****************************************************************************/ -int DLLCALL putuserrec(scfg_t* cfg, int usernumber,int start, uint length, char *str) +int DLLCALL putuserrec(scfg_t* cfg, int usernumber,int start, uint length, const char *str) { char str2[256]; int file; uint c,i; - node_t node; if(!VALID_CFG(cfg) || usernumber<1 || str==NULL) return(-1); - sprintf(str2,"%suser/user.dat",cfg->data_dir); + SAFEPRINTF(str2,"%suser/user.dat",cfg->data_dir); if((file=nopen(str2,O_RDWR|O_DENYNONE))==-1) return(errno); @@ -1848,19 +1948,7 @@ int DLLCALL putuserrec(scfg_t* cfg, int write(file,str2,length); unlock(file,(long)((long)(usernumber-1)*U_LEN)+start,length); close(file); - for(i=1;i<=cfg->sys_nodes;i++) { /* instant user data update */ - if(i==cfg->node_num) - continue; - getnodedat(cfg, i,&node,NULL); - if(node.useron==usernumber && (node.status==NODE_INUSE - || node.status==NODE_QUIET)) { - getnodedat(cfg, i,&node,&file); - node.misc|=NODE_UDAT; - putnodedat(cfg, i,&node,file); - break; - } - } - + dirtyuserdat(cfg,usernumber); return(0); } @@ -1874,12 +1962,11 @@ ulong DLLCALL adjustuserrec(scfg_t* cfg, char tmp[32]; int i,c,file; long val; - node_t node; if(!VALID_CFG(cfg) || usernumber<1) return(0); - sprintf(path,"%suser/user.dat",cfg->data_dir); + SAFEPRINTF(path,"%suser/user.dat",cfg->data_dir); if((file=nopen(path,O_RDWR|O_DENYNONE))==-1) return(0); @@ -1927,18 +2014,7 @@ ulong DLLCALL adjustuserrec(scfg_t* cfg, } unlock(file,(long)((long)(usernumber-1)*U_LEN)+start,length); close(file); - for(i=1;i<=cfg->sys_nodes;i++) { /* instant user data update */ - if(i==cfg->node_num) - continue; - getnodedat(cfg, i,&node,NULL); - if(node.useron==usernumber && (node.status==NODE_INUSE - || node.status==NODE_QUIET)) { - getnodedat(cfg, i,&node,&file); - node.misc|=NODE_UDAT; - putnodedat(cfg, i,&node,file); - break; - } - } + dirtyuserdat(cfg,usernumber); return(val); } @@ -2070,14 +2146,14 @@ BOOL DLLCALL logoutuserdat(scfg_t* cfg, /* Reset daily stats if new day */ if(tm.tm_mday!=tm_now.tm_mday) - resetdailyuserdat(cfg, user); + resetdailyuserdat(cfg, user, /* write: */TRUE); return(TRUE); } /****************************************************************************/ /****************************************************************************/ -void DLLCALL resetdailyuserdat(scfg_t* cfg, user_t* user) +void DLLCALL resetdailyuserdat(scfg_t* cfg, user_t* user, BOOL write) { char str[128]; @@ -2086,28 +2162,28 @@ void DLLCALL resetdailyuserdat(scfg_t* c /* logons today */ user->ltoday=0; - putuserrec(cfg,user->number,U_LTODAY,5,"0"); + if(write) putuserrec(cfg,user->number,U_LTODAY,5,"0"); /* e-mails today */ user->etoday=0; - putuserrec(cfg,user->number,U_ETODAY,5,"0"); + if(write) putuserrec(cfg,user->number,U_ETODAY,5,"0"); /* posts today */ user->ptoday=0; - putuserrec(cfg,user->number,U_PTODAY,5,"0"); + if(write) putuserrec(cfg,user->number,U_PTODAY,5,"0"); /* free credits per day */ user->freecdt=cfg->level_freecdtperday[user->level]; - putuserrec(cfg,user->number,U_FREECDT,10 + if(write) putuserrec(cfg,user->number,U_FREECDT,10 ,ultoa(user->freecdt,str,10)); /* time used today */ user->ttoday=0; - putuserrec(cfg,user->number,U_TTODAY,5,"0"); + if(write) putuserrec(cfg,user->number,U_TTODAY,5,"0"); /* extra time today */ user->textra=0; - putuserrec(cfg,user->number,U_TEXTRA,5,"0"); + if(write) putuserrec(cfg,user->number,U_TEXTRA,5,"0"); } /****************************************************************************/ /****************************************************************************/ -char* DLLCALL usermailaddr(scfg_t* cfg, char* addr, char* name) +char* DLLCALL usermailaddr(scfg_t* cfg, char* addr, const char* name) { int i; @@ -2118,21 +2194,25 @@ char* DLLCALL usermailaddr(scfg_t* cfg, strcpy(addr,name); return(addr); } - if(strchr(name,'!') || (strchr(name,'.') && strchr(name,' '))) - sprintf(addr,"\"%s\"@",name); - else { - sprintf(addr,"%s@",name); - /* convert "first last@" to "first.last@" */ + if(strchr(name,'.') && strchr(name,' ')) { + /* convert "Dr. Seuss" to "Dr.Seuss" */ + strip_space(name,addr); + } else if(strchr(name,'!')) { + sprintf(addr,"\"%s\"",name); + } else { + strcpy(addr,name); + /* convert "first last" to "first.last" */ for(i=0;addr[i];i++) if(addr[i]==' ' || addr[i]&0x80) addr[i]='.'; strlwr(addr); } + strcat(addr,"@"); strcat(addr,cfg->sys_inetaddr); return(addr); } -char* DLLCALL alias(scfg_t* cfg, char* name, char* buf) +char* DLLCALL alias(scfg_t* cfg, const char* name, char* buf) { char line[128]; char* p; @@ -2147,11 +2227,11 @@ char* DLLCALL alias(scfg_t* cfg, char* n if(!VALID_CFG(cfg) || name==NULL || buf==NULL) return(NULL); - p=name; + p=(char*)name; - sprintf(fname,"%salias.cfg",cfg->ctrl_dir); + SAFEPRINTF(fname,"%salias.cfg",cfg->ctrl_dir); if((fp=fopen(fname,"r"))==NULL) - return(name); + return((char*)name); while(!feof(fp)) { if(!fgets(line,sizeof(line),fp)) @@ -2215,12 +2295,12 @@ int DLLCALL newuserdat(scfg_t* cfg, user if(!VALID_CFG(cfg) || user==NULL) return(-1); - sprintf(str,"%suser/name.dat",cfg->data_dir); + SAFEPRINTF(str,"%suser/name.dat",cfg->data_dir); if(fexist(str)) { if((stream=fnopen(&file,str,O_RDONLY))==NULL) { return(errno); } - last=filelength(file)/(LEN_ALIAS+2); /* total users */ + last=(long)filelength(file)/(LEN_ALIAS+2); /* total users */ while(unum<=last) { fread(str,LEN_ALIAS+2,1,stream); for(c=0;cdata_dir,user->number); /* delete any files */ + SAFEPRINTF2(str,"%sfile/%04u.in",cfg->data_dir,user->number); /* delete any files */ delfiles(str,ALLFILES); /* waiting for user */ rmdir(str); - sprintf(tmp,"%04u.*",user->number); - sprintf(str,"%sfile",cfg->data_dir); + SAFEPRINTF(tmp,"%04u.*",user->number); + SAFEPRINTF(str,"%sfile",cfg->data_dir); delfiles(str,tmp); - sprintf(str,"%suser",cfg->data_dir); + SAFEPRINTF(str,"%suser",cfg->data_dir); delfiles(str,tmp); + SAFEPRINTF2(str,"%suser/%04u",cfg->data_dir,user->number); + delfiles(str,ALLFILES); + rmdir(str); - sprintf(str,"%suser/ptrs/%04u.ixb",cfg->data_dir,user->number); /* msg ptrs */ + SAFEPRINTF2(str,"%suser/ptrs/%04u.ixb",cfg->data_dir,user->number); /* msg ptrs */ remove(str); - sprintf(str,"%smsgs/%04u.msg",cfg->data_dir,user->number); /* delete short msg */ + SAFEPRINTF2(str,"%smsgs/%04u.msg",cfg->data_dir,user->number); /* delete short msg */ remove(str); /* Update daily statistics database (for system and node) */ for(i=0;i<2;i++) { - sprintf(str,"%sdsts.dab",i ? cfg->ctrl_dir : cfg->node_dir); + SAFEPRINTF(str,"%sdsts.dab",i ? cfg->ctrl_dir : cfg->node_dir); if((file=nopen(str,O_RDWR))==-1) continue; memset(&stats,0,sizeof(stats)); @@ -2386,7 +2469,81 @@ int DLLCALL user_rec_len(int offset) return(-1); } -BOOL DLLCALL is_download_free(scfg_t* cfg, uint dirnum, user_t* user) +/****************************************************************************/ +/* Determine if the specified user can or cannot access the specified sub */ +/****************************************************************************/ +BOOL DLLCALL can_user_access_sub(scfg_t* cfg, uint subnum, user_t* user, client_t* client) +{ + if(!VALID_CFG(cfg)) + return FALSE; + if(subnum>=cfg->total_subs) + return FALSE; + if(!chk_ar(cfg,cfg->grp[cfg->sub[subnum]->grp]->ar,user,client)) + return FALSE; + if(!chk_ar(cfg,cfg->sub[subnum]->ar,user,client)) + return FALSE; + + return TRUE; +} + +/****************************************************************************/ +/* Determine if the specified user can or cannot read the specified sub */ +/****************************************************************************/ +BOOL DLLCALL can_user_read_sub(scfg_t* cfg, uint subnum, user_t* user, client_t* client) +{ + if(!can_user_access_sub(cfg, subnum, user, client)) + return FALSE; + return chk_ar(cfg,cfg->sub[subnum]->read_ar,user,client); +} + +/****************************************************************************/ +/* Determine if the specified user can or cannot post on the specified sub */ +/* 'reason' is an (optional) pointer to a text.dat item number, indicating */ +/* the reason the user cannot post, when returning FALSE. */ +/****************************************************************************/ +BOOL DLLCALL can_user_post(scfg_t* cfg, uint subnum, user_t* user, client_t* client, uint* reason) +{ + if(reason!=NULL) + *reason=NoAccessSub; + if(!can_user_access_sub(cfg, subnum, user, client)) + return FALSE; + if(reason!=NULL) + *reason=CantPostOnSub; + if(!chk_ar(cfg,cfg->sub[subnum]->post_ar,user,client)) + return FALSE; + if(cfg->sub[subnum]->misc&(SUB_QNET|SUB_FIDO|SUB_PNET|SUB_INET) + && user->rest&FLAG('N')) /* network restriction? */ + return FALSE; + if(reason!=NULL) + *reason=R_Post; + if(user->rest&FLAG('P')) /* post restriction? */ + return FALSE; + if(reason!=NULL) + *reason=TooManyPostsToday; + if(user->ptoday>=cfg->level_postsperday[user->level]) + return FALSE; + + return TRUE; +} + +/****************************************************************************/ +/* Determine if the specified user is a sub-board operator */ +/****************************************************************************/ +BOOL DLLCALL is_user_subop(scfg_t* cfg, uint subnum, user_t* user, client_t* client) +{ + if(!can_user_access_sub(cfg, subnum, user, client)) + return FALSE; + if(user->level>=SYSOP_LEVEL) + return TRUE; + + return cfg->sub[subnum]->op_ar[0]!=0 && chk_ar(cfg,cfg->sub[subnum]->op_ar,user,client); +} + +/****************************************************************************/ +/* Determine if downloads from the specified directory are free for the */ +/* specified user */ +/****************************************************************************/ +BOOL DLLCALL is_download_free(scfg_t* cfg, uint dirnum, user_t* user, client_t* client) { if(!VALID_CFG(cfg)) return(FALSE); @@ -2406,15 +2563,15 @@ BOOL DLLCALL is_download_free(scfg_t* cf if(cfg->dir[dirnum]->ex_ar==NULL || cfg->dir[dirnum]->ex_ar[0]==0) return(FALSE); - return(chk_ar(cfg,cfg->dir[dirnum]->ex_ar,user)); + return(chk_ar(cfg,cfg->dir[dirnum]->ex_ar,user,client)); } /****************************************************************************/ /* Add an IP address (with comment) to the IP filter/trashcan file */ /* ToDo: Move somewhere more appropriate (filter.c?) */ /****************************************************************************/ -BOOL DLLCALL filter_ip(scfg_t* cfg, char* prot, char* reason, char* host - ,char* ip_addr, char* username, char* fname) +BOOL DLLCALL filter_ip(scfg_t* cfg, const char* prot, const char* reason, const char* host + ,const char* ip_addr, const char* username, const char* fname) { char ip_can[MAX_PATH+1]; char tstr[64]; @@ -2424,15 +2581,18 @@ BOOL DLLCALL filter_ip(scfg_t* cfg, char if(ip_addr==NULL) return(FALSE); - sprintf(ip_can,"%sip.can",cfg->text_dir); + SAFEPRINTF(ip_can,"%sip.can",cfg->text_dir); if(fname==NULL) fname=ip_can; + if(findstr(ip_addr, fname)) /* Already filtered? */ + return(TRUE); + if((fp=fopen(fname,"a"))==NULL) return(FALSE); fprintf(fp,"\n; %s %s by %s on %s\n" - ,prot,reason,username,timestr(cfg,&now,tstr)); + ,prot,reason,username,timestr(cfg,now,tstr)); if(host!=NULL) fprintf(fp,"; Hostname: %s\n",host); @@ -2442,3 +2602,162 @@ BOOL DLLCALL filter_ip(scfg_t* cfg, char fclose(fp); return(TRUE); } + +/****************************************************************************/ +/* Note: This function does not account for timed events! */ +/****************************************************************************/ +time_t DLLCALL gettimeleft(scfg_t* cfg, user_t* user, time_t starttime) +{ + time_t now; + long tleft; + time_t timeleft; + + now=time(NULL); + + if(user->exempt&FLAG('T')) { /* Time online exemption */ + timeleft=cfg->level_timepercall[user->level]; + if(timeleft<10) /* never get below 10 minutes for exempt users */ + timeleft=10; + timeleft*=60; /* convert to seconds */ + } + else { + tleft=(((long)cfg->level_timeperday[user->level]-user->ttoday) + +user->textra)*60L; + if(tleft<0) tleft=0; + if(tleft>cfg->level_timepercall[user->level]*60) + tleft=cfg->level_timepercall[user->level]*60; + tleft+=user->min*60L; + tleft-=now-starttime; + if(tleft>0x7fffL) + timeleft=0x7fff; + else + timeleft=tleft; + } + + return(timeleft); +} + +/*************************************************************************/ +/* Check a supplied name/alias and see if it's valid by our standards. */ +/*************************************************************************/ +BOOL DLLCALL check_name(scfg_t* cfg, const char* name) +{ + char tmp[512]; + size_t len; + + len=strlen(name); + if(len<1) + return FALSE; + if ( name[0] <= ' ' /* begins with white-space? */ + || name[len-1] <= ' ' /* ends with white-space */ + || !isalpha(name[0]) + || !stricmp(name,cfg->sys_id) + || strchr(name,0xff) + || matchuser(cfg,name,TRUE /* sysop_alias */) + || trashcan(cfg,name,"name") + || alias(cfg,name,tmp)!=name + ) + return FALSE; + return TRUE; +} + +/****************************************************************************/ +/* Login attempt/hack tracking */ +/****************************************************************************/ + +/****************************************************************************/ +link_list_t* DLLCALL loginAttemptListInit(link_list_t* list) +{ + return listInit(list, LINK_LIST_MUTEX); +} + +/****************************************************************************/ +BOOL DLLCALL loginAttemptListFree(link_list_t* list) +{ + return listFree(list); +} + +/****************************************************************************/ +long DLLCALL loginAttemptListClear(link_list_t* list) +{ + long count; + + listLock(list); + count=listCountNodes(list); + count-=listFreeNodes(list); + listUnlock(list); + return count; +} + +/****************************************************************************/ +static list_node_t* login_attempted(link_list_t* list, SOCKADDR_IN* addr) +{ + list_node_t* node; + login_attempt_t* attempt; + + for(node=list->first; node!=NULL; node=node->next) { + attempt=node->data; + if(memcmp(&attempt->addr,&addr->sin_addr,sizeof(attempt->addr))==0) + break; + } + return node; +} + +/****************************************************************************/ +long DLLCALL loginAttempts(link_list_t* list, SOCKADDR_IN* addr) +{ + long count=0; + list_node_t* node; + + listLock(list); + if((node=login_attempted(list, addr))!=NULL) + count = ((login_attempt_t*)node->data)->count - ((login_attempt_t*)node->data)->dupes; + listUnlock(list); + + return count; +} + +/****************************************************************************/ +void DLLCALL loginSuccess(link_list_t* list, SOCKADDR_IN* addr) +{ + list_node_t* node; + + listLock(list); + if((node=login_attempted(list, addr)) != NULL) + listRemoveNode(list, node, /* freeData: */TRUE); + listUnlock(list); +} + +/****************************************************************************/ +/* Returns number of *unique* login attempts (excludes consecutive dupes) */ +/****************************************************************************/ +ulong DLLCALL loginFailure(link_list_t* list, SOCKADDR_IN* addr, const char* prot, const char* user, const char* pass) +{ + list_node_t* node; + login_attempt_t first={0}; + login_attempt_t* attempt=&first; + ulong count=0; + + if(list==NULL) + return 0; + + listLock(list); + if((node=login_attempted(list, addr)) != NULL) { + attempt=node->data; + /* Don't count consecutive duplicate attempts (same name and password): */ + if(strcmp(attempt->user,user)==0 && (pass==NULL || strcmp(attempt->pass,pass)==0)) + attempt->dupes++; + } + SAFECOPY(attempt->prot,prot); + attempt->time=time(NULL); + attempt->addr=addr->sin_addr; + SAFECOPY(attempt->user, user); + SAFECOPY(attempt->pass, pass); + attempt->count++; + count = attempt->count-attempt->dupes; + if(node==NULL) + listPushNodeData(list, attempt, sizeof(login_attempt_t)); + listUnlock(list); + + return count; +}