/* userdat.c */ /* Synchronet user data-related routines (exported) */ /* $Id$ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * * Copyright 2000 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 * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * See the GNU General Public License for more details: gpl.txt or * * http://www.fsf.org/copyleft/gpl.html * * * * Anonymous FTP access to the most recent released source is available at * * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * * * * Anonymous CVS access to the development source and modification history * * is available at cvs.synchro.net:/cvsroot/sbbs, example: * * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * * (just hit return, no password is necessary) * * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * * You are encouraged to submit any modifications (preferably in Unix diff * * format) via e-mail to mods@synchro.net * * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ #include "sbbs.h" #include "cmdshell.h" /* convenient space-saving global variables */ char* crlf="\r\n"; char* nulstr=""; #define REPLACE_CHARS(str,ch1,ch2) for(c=0;str[c];c++) if(str[c]==ch1) str[c]=ch2; /****************************************************************************/ /* Looks for a perfect match amoung all usernames (not deleted users) */ /* 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) { int file,c; char dat[LEN_ALIAS+2]; char str[256]; ulong l,length; FILE *stream; if(sysop_alias && (!stricmp(name,"SYSOP") || !stricmp(name,"POSTMASTER") || !stricmp(name,cfg->sys_id))) return(1); sprintf(str,"%suser/name.dat",cfg->data_dir); if((file=nopen(str,O_RDONLY))==-1) return(0); length=filelength(file); if((stream=fdopen(file,"rb"))==NULL) return(0); for(l=0;l<length;l+=LEN_ALIAS+2) { fread(dat,sizeof(dat),1,stream); for(c=0;c<LEN_ALIAS;c++) if(dat[c]==ETX) break; dat[c]=0; if(!stricmp(dat,name)) break; /* convert dots to spaces */ strcpy(str,dat); REPLACE_CHARS(str,'.',' '); if(!stricmp(str,name)) break; /* convert spaces to dots */ strcpy(str,dat); REPLACE_CHARS(str,' ','.'); if(!stricmp(str,name)) break; /* convert dots to underscores */ strcpy(str,dat); REPLACE_CHARS(str,'.','_'); if(!stricmp(str,name)) break; /* convert underscores to dots */ strcpy(str,dat); REPLACE_CHARS(str,'_','.'); if(!stricmp(str,name)) break; /* convert spaces to underscores */ strcpy(str,dat); REPLACE_CHARS(str,' ','_'); if(!stricmp(str,name)) break; /* convert underscores to spaces */ strcpy(str,dat); REPLACE_CHARS(str,'_',' '); if(!stricmp(str,name)) break; } fclose(stream); if(l<length) return((l/(LEN_ALIAS+2))+1); return(0); } /****************************************************************************/ /* Returns the number of the last user in user.dat (deleted ones too) */ /****************************************************************************/ uint DLLCALL lastuser(scfg_t* cfg) { char str[256]; long length; sprintf(str,"%suser/user.dat", cfg->data_dir); if((length=flength(str))>0) return((uint)(length/U_LEN)); return(0); } /****************************************************************************/ /* Deletes last user record in user.dat */ /****************************************************************************/ BOOL DLLCALL del_lastuser(scfg_t* cfg) { char str[256]; int file; long length; sprintf(str,"%suser/user.dat", cfg->data_dir); if((file=nopen(str,O_RDWR|O_DENYNONE))==-1) return(FALSE); length=filelength(file); if(length<U_LEN) { close(file); return(FALSE); } chsize(file,length-U_LEN); close(file); return(TRUE); } /****************************************************************************/ /* Fills the structure 'user' with info for user.number from user.dat */ /* Called from functions useredit, waitforcall and main_sec */ /****************************************************************************/ int DLLCALL getuserdat(scfg_t* cfg, user_t *user) { char userdat[U_LEN+1],str[U_LEN+1],tmp[64]; int i,file; if(!user->number) { memset(user,0,sizeof(user_t)); return(-1); } sprintf(userdat,"%suser/user.dat",cfg->data_dir); if((file=nopen(userdat,O_RDONLY|O_DENYNONE))==-1) { memset(user,0,sizeof(user_t)); return(errno); } if(user->number > (filelength(file)/U_LEN)) { close(file); return(-1); /* no such user record */ } lseek(file,(long)((long)(user->number-1)*U_LEN),SEEK_SET); i=0; while(i<LOOP_NODEDAB && lock(file,(long)((long)(user->number-1)*U_LEN),U_LEN)==-1) { if(i) mswait(100); i++; } if(i>=LOOP_NODEDAB) { close(file); memset(user,0L,sizeof(user_t)); return(-2); } if(read(file,userdat,U_LEN)!=U_LEN) { unlock(file,(long)((long)(user->number-1)*U_LEN),U_LEN); close(file); memset(user,0L,sizeof(user_t)); return(-3); } unlock(file,(long)((long)(user->number-1)*U_LEN),U_LEN); close(file); /* order of these function calls is irrelevant */ getrec(userdat,U_ALIAS,LEN_ALIAS,user->alias); getrec(userdat,U_NAME,LEN_NAME,user->name); getrec(userdat,U_HANDLE,LEN_HANDLE,user->handle); getrec(userdat,U_NOTE,LEN_NOTE,user->note); getrec(userdat,U_COMP,LEN_COMP,user->comp); getrec(userdat,U_COMMENT,LEN_COMMENT,user->comment); getrec(userdat,U_NETMAIL,LEN_NETMAIL,user->netmail); getrec(userdat,U_ADDRESS,LEN_ADDRESS,user->address); getrec(userdat,U_LOCATION,LEN_LOCATION,user->location); getrec(userdat,U_ZIPCODE,LEN_ZIPCODE,user->zipcode); getrec(userdat,U_PASS,LEN_PASS,user->pass); getrec(userdat,U_PHONE,LEN_PHONE,user->phone); getrec(userdat,U_BIRTH,LEN_BIRTH,user->birth); getrec(userdat,U_MODEM,LEN_MODEM,user->modem); getrec(userdat,U_LASTON,8,str); user->laston=ahtoul(str); getrec(userdat,U_FIRSTON,8,str); user->firston=ahtoul(str); getrec(userdat,U_EXPIRE,8,str); user->expire=ahtoul(str); getrec(userdat,U_PWMOD,8,str); user->pwmod=ahtoul(str); getrec(userdat,U_NS_TIME,8,str); user->ns_time=ahtoul(str); if(user->ns_time<0x20000000L) user->ns_time=user->laston; /* Fix for v2.00->v2.10 */ getrec(userdat,U_LOGONTIME,8,str); user->logontime=ahtoul(str); getrec(userdat,U_LOGONS,5,str); user->logons=atoi(str); getrec(userdat,U_LTODAY,5,str); user->ltoday=atoi(str); getrec(userdat,U_TIMEON,5,str); user->timeon=atoi(str); getrec(userdat,U_TEXTRA,5,str); user->textra=atoi(str); getrec(userdat,U_TTODAY,5,str); user->ttoday=atoi(str); getrec(userdat,U_TLAST,5,str); user->tlast=atoi(str); getrec(userdat,U_POSTS,5,str); user->posts=atoi(str); getrec(userdat,U_EMAILS,5,str); user->emails=atoi(str); getrec(userdat,U_FBACKS,5,str); user->fbacks=atoi(str); getrec(userdat,U_ETODAY,5,str); user->etoday=atoi(str); getrec(userdat,U_PTODAY,5,str); user->ptoday=atoi(str); getrec(userdat,U_ULB,10,str); user->ulb=atol(str); getrec(userdat,U_ULS,5,str); user->uls=atoi(str); getrec(userdat,U_DLB,10,str); user->dlb=atol(str); getrec(userdat,U_DLS,5,str); user->dls=atoi(str); getrec(userdat,U_CDT,10,str); user->cdt=atol(str); getrec(userdat,U_MIN,10,str); user->min=atol(str); getrec(userdat,U_LEVEL,2,str); user->level=atoi(str); getrec(userdat,U_FLAGS1,8,str); user->flags1=ahtoul(str); getrec(userdat,U_FLAGS2,8,str); user->flags2=ahtoul(str); getrec(userdat,U_FLAGS3,8,str); user->flags3=ahtoul(str); getrec(userdat,U_FLAGS4,8,str); user->flags4=ahtoul(str); getrec(userdat,U_EXEMPT,8,str); user->exempt=ahtoul(str); getrec(userdat,U_REST,8,str); user->rest=ahtoul(str); getrec(userdat,U_ROWS,2,str); user->rows=atoi(str); if(user->rows && user->rows<10) user->rows=10; user->sex=userdat[U_SEX]; if(!user->sex) user->sex=SP; /* fix for v1b04 that could save as 0 */ user->prot=userdat[U_PROT]; if(user->prot<SP) user->prot=SP; getrec(userdat,U_MISC,8,str); user->misc=ahtoul(str); if(user->rest&FLAG('Q')) user->misc&=~SPIN; getrec(userdat,U_LEECH,2,str); user->leech=(uchar)ahtoul(str); getrec(userdat,U_CURSUB,8,user->cursub); getrec(userdat,U_CURDIR,8,user->curdir); getrec(userdat,U_CURXTRN,8,user->curxtrn); getrec(userdat,U_FREECDT,10,str); user->freecdt=atol(str); getrec(userdat,U_XEDIT,8,str); for(i=0;i<cfg->total_xedits;i++) if(!stricmp(str,cfg->xedit[i]->code) && chk_ar(cfg,cfg->xedit[i]->ar,user)) break; user->xedit=i+1; if(user->xedit>cfg->total_xedits) user->xedit=0; getrec(userdat,U_SHELL,8,str); for(i=0;i<cfg->total_shells;i++) if(!stricmp(str,cfg->shell[i]->code)) break; if(i==cfg->total_shells) i=0; user->shell=i; getrec(userdat,U_QWK,8,str); if(str[0]<SP) { /* v1c, so set defaults */ if(user->rest&FLAG('Q')) user->qwk=(QWK_RETCTLA); else user->qwk=(QWK_FILES|QWK_ATTACH|QWK_EMAIL|QWK_DELMAIL); } else user->qwk=ahtoul(str); getrec(userdat,U_TMPEXT,3,user->tmpext); if((!user->tmpext[0] || !strcmp(user->tmpext,"0")) && cfg->total_fcomps) strcpy(user->tmpext,cfg->fcomp[0]->ext); /* For v1x to v2x conversion */ 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 if(useron.number==user->number) { if(user!=&useron) useron=*user; if(online) { #if 0 /* legacy? */ getusrdirs(); getusrsubs(); #endif if(user->misc&AUTOTERM) { // was useron.misc (01/19/00) user->misc&=~(ANSI|RIP|WIP); user->misc|=autoterm; } } } #endif return(0); } /****************************************************************************/ /* Writes into user.number's slot in user.dat data in structure 'user' */ /* Called from functions newuser, useredit and main */ /****************************************************************************/ int DLLCALL putuserdat(scfg_t* cfg, user_t* user) { int i,file; char userdat[U_LEN],str[MAX_PATH+1]; node_t node; if(!user->number) return(-1); memset(userdat,ETX,U_LEN); putrec(userdat,U_ALIAS,LEN_ALIAS+5,user->alias); putrec(userdat,U_NAME,LEN_NAME,user->name); putrec(userdat,U_HANDLE,LEN_HANDLE,user->handle); putrec(userdat,U_HANDLE+LEN_HANDLE,2,crlf); putrec(userdat,U_NOTE,LEN_NOTE,user->note); putrec(userdat,U_COMP,LEN_COMP,user->comp); putrec(userdat,U_COMP+LEN_COMP,2,crlf); putrec(userdat,U_COMMENT,LEN_COMMENT,user->comment); putrec(userdat,U_COMMENT+LEN_COMMENT,2,crlf); putrec(userdat,U_NETMAIL,LEN_NETMAIL,user->netmail); putrec(userdat,U_NETMAIL+LEN_NETMAIL,2,crlf); putrec(userdat,U_ADDRESS,LEN_ADDRESS,user->address); putrec(userdat,U_LOCATION,LEN_LOCATION,user->location); putrec(userdat,U_ZIPCODE,LEN_ZIPCODE,user->zipcode); putrec(userdat,U_ZIPCODE+LEN_ZIPCODE,2,crlf); putrec(userdat,U_PASS,LEN_PASS,user->pass); putrec(userdat,U_PHONE,LEN_PHONE,user->phone); putrec(userdat,U_BIRTH,LEN_BIRTH,user->birth); putrec(userdat,U_MODEM,LEN_MODEM,user->modem); putrec(userdat,U_LASTON,8,ultoa(user->laston,str,16)); putrec(userdat,U_FIRSTON,8,ultoa(user->firston,str,16)); putrec(userdat,U_EXPIRE,8,ultoa(user->expire,str,16)); putrec(userdat,U_PWMOD,8,ultoa(user->pwmod,str,16)); putrec(userdat,U_PWMOD+8,2,crlf); putrec(userdat,U_LOGONS,5,ultoa(user->logons,str,10)); putrec(userdat,U_LTODAY,5,ultoa(user->ltoday,str,10)); putrec(userdat,U_TIMEON,5,ultoa(user->timeon,str,10)); putrec(userdat,U_TEXTRA,5,ultoa(user->textra,str,10)); putrec(userdat,U_TTODAY,5,ultoa(user->ttoday,str,10)); putrec(userdat,U_TLAST,5,ultoa(user->tlast,str,10)); putrec(userdat,U_POSTS,5,ultoa(user->posts,str,10)); putrec(userdat,U_EMAILS,5,ultoa(user->emails,str,10)); putrec(userdat,U_FBACKS,5,ultoa(user->fbacks,str,10)); putrec(userdat,U_ETODAY,5,ultoa(user->etoday,str,10)); putrec(userdat,U_PTODAY,5,ultoa(user->ptoday,str,10)); putrec(userdat,U_PTODAY+5,2,crlf); putrec(userdat,U_ULB,10,ultoa(user->ulb,str,10)); putrec(userdat,U_ULS,5,ultoa(user->uls,str,10)); putrec(userdat,U_DLB,10,ultoa(user->dlb,str,10)); putrec(userdat,U_DLS,5,ultoa(user->dls,str,10)); putrec(userdat,U_CDT,10,ultoa(user->cdt,str,10)); putrec(userdat,U_MIN,10,ultoa(user->min,str,10)); putrec(userdat,U_MIN+10,2,crlf); putrec(userdat,U_LEVEL,2,ultoa(user->level,str,10)); putrec(userdat,U_FLAGS1,8,ultoa(user->flags1,str,16)); putrec(userdat,U_TL,2,nulstr); /* unused */ putrec(userdat,U_FLAGS2,8,ultoa(user->flags2,str,16)); putrec(userdat,U_EXEMPT,8,ultoa(user->exempt,str,16)); putrec(userdat,U_REST,8,ultoa(user->rest,str,16)); putrec(userdat,U_REST+8,2,crlf); putrec(userdat,U_ROWS,2,ultoa(user->rows,str,10)); userdat[U_SEX]=user->sex; userdat[U_PROT]=user->prot; putrec(userdat,U_MISC,8,ultoa(user->misc,str,16)); putrec(userdat,U_LEECH,2,ultoa(user->leech,str,16)); putrec(userdat,U_CURSUB,8,user->cursub); putrec(userdat,U_CURDIR,8,user->curdir); putrec(userdat,U_CURXTRN,8,user->curxtrn); putrec(userdat,U_CURXTRN+8,2,crlf); putrec(userdat,U_XFER_CMD+LEN_XFER_CMD,2,crlf); putrec(userdat,U_MAIL_CMD+LEN_MAIL_CMD,2,crlf); putrec(userdat,U_FREECDT,10,ultoa(user->freecdt,str,10)); putrec(userdat,U_FLAGS3,8,ultoa(user->flags3,str,16)); putrec(userdat,U_FLAGS4,8,ultoa(user->flags4,str,16)); if(user->xedit) putrec(userdat,U_XEDIT,8,cfg->xedit[user->xedit-1]->code); else putrec(userdat,U_XEDIT,8,nulstr); putrec(userdat,U_SHELL,8,cfg->shell[user->shell]->code); putrec(userdat,U_QWK,8,ultoa(user->qwk,str,16)); putrec(userdat,U_TMPEXT,3,user->tmpext); putrec(userdat,U_CHAT,8,ultoa(user->chat,str,16)); putrec(userdat,U_NS_TIME,8,ultoa(user->ns_time,str,16)); putrec(userdat,U_LOGONTIME,8,ultoa(user->logontime,str,16)); 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); if((file=nopen(str,O_RDWR|O_CREAT|O_DENYNONE))==-1) { return(errno); } if(filelength(file)<((long)user->number-1)*U_LEN) { close(file); return(-4); } lseek(file,(long)((long)((long)user->number-1)*U_LEN),SEEK_SET); i=0; while(i<LOOP_NODEDAB && lock(file,(long)((long)(user->number-1)*U_LEN),U_LEN)==-1) { if(i) mswait(100); i++; } if(i>=LOOP_NODEDAB) { close(file); return(-2); } if(write(file,userdat,U_LEN)!=U_LEN) { unlock(file,(long)((long)(user->number-1)*U_LEN),U_LEN); close(file); return(-3); } 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; } } return(0); } /****************************************************************************/ /* Returns the username in 'str' that corresponds to the 'usernumber' */ /* Called from functions everywhere */ /****************************************************************************/ char* DLLCALL username(scfg_t* cfg, int usernumber,char *strin) { char str[256]; int c; int file; if(usernumber<1) { strin[0]=0; return(strin); } sprintf(str,"%suser/name.dat",cfg->data_dir); if(flength(str)<1L) { strin[0]=0; return(strin); } if((file=nopen(str,O_RDONLY))==-1) { strin[0]=0; return(strin); } if(filelength(file)<(long)((long)usernumber*(LEN_ALIAS+2))) { close(file); strin[0]=0; return(strin); } lseek(file,(long)((long)(usernumber-1)*(LEN_ALIAS+2)),SEEK_SET); read(file,strin,LEN_ALIAS); close(file); for(c=0;c<LEN_ALIAS;c++) if(strin[c]==ETX) break; strin[c]=0; if(!c) strcpy(strin,"DELETED USER"); return(strin); } /****************************************************************************/ /* Puts 'name' into slot 'number' in user/name.dat */ /****************************************************************************/ int DLLCALL putusername(scfg_t* cfg, int number, char *name) { char str[256]; int file; long length; uint total_users; if (number<1) return(-1); sprintf(str,"%suser/name.dat", cfg->data_dir); if((file=nopen(str,O_RDWR|O_CREAT))==-1) return(-2); length=filelength(file); /* Truncate corrupted name.dat */ total_users=lastuser(cfg); if((uint)(length/(LEN_ALIAS+2))>total_users) chsize(file,total_users*(LEN_ALIAS+2)); if(length && length%(LEN_ALIAS+2)) { close(file); return(-3); } if(length<(((long)number-1)*(LEN_ALIAS+2))) { sprintf(str,"%*s",LEN_ALIAS,nulstr); memset(str,ETX,LEN_ALIAS); strcat(str,crlf); lseek(file,0L,SEEK_END); while(filelength(file)<((long)number*(LEN_ALIAS+2))) write(file,str,(LEN_ALIAS+2)); } lseek(file,(long)(((long)number-1)*(LEN_ALIAS+2)),SEEK_SET); putrec(str,0,LEN_ALIAS,name); putrec(str,LEN_ALIAS,2,crlf); write(file,str,LEN_ALIAS+2); close(file); return(0); } /****************************************************************************/ /* Returns the age derived from the string 'birth' in the format MM/DD/YY */ /* Called from functions statusline, main_sec, xfer_sec, useredit and */ /* text files */ /****************************************************************************/ char DLLCALL getage(scfg_t* cfg, char *birth) { char age; struct tm * tm; time_t now; if(!atoi(birth) || !atoi(birth+3)) /* Invalid */ return(0); now=time(NULL); tm=localtime(&now); if(tm==NULL) return(0); age=(tm->tm_year)-(((birth[6]&0xf)*10)+(birth[7]&0xf)); if(age>105) age-=105; 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) return(0); if(((birth[3]&0xf)*10)+(birth[4]&0xf)>tm->tm_mon || (((birth[3]&0xf)*10)+(birth[4]&0xf)==tm->tm_mon && ((birth[0]&0xf)*10)+(birth[1]&0xf)>tm->tm_mday)) age--; } else { /* MM/DD/YY format */ if(atoi(birth)>12 || atoi(birth+3)>31) return(0); if(((birth[0]&0xf)*10)+(birth[1]&0xf)>tm->tm_mon || (((birth[0]&0xf)*10)+(birth[1]&0xf)==tm->tm_mon && ((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 */ /* if lockit is non-zero, locks this node's record. putnodedat() unlocks it */ /****************************************************************************/ int DLLCALL getnodedat(scfg_t* cfg, uint number, node_t *node, int* fp) { char str[MAX_PATH+1]; int count; int file; if(!number || number>cfg->sys_nodes) return(-1); sprintf(str,"%snode.dab",cfg->ctrl_dir); if((file=nopen(str,O_RDWR|O_DENYNONE))==-1) { memset(node,0,sizeof(node_t)); if(fp!=NULL) *fp=file; return(errno); } number--; /* make zero based */ for(count=0;count<LOOP_NODEDAB;count++) { if(count) mswait(100); lseek(file,(long)number*sizeof(node_t),SEEK_SET); if(fp!=NULL && lock(file,(long)number*sizeof(node_t),sizeof(node_t))==-1) continue; if(read(file,node,sizeof(node_t))==sizeof(node_t)) break; } if(fp==NULL) close(file); else *fp=file; if(count==LOOP_NODEDAB) return(-2); return(0); } /****************************************************************************/ /* Write the data from the structure 'node' into node.dab */ /****************************************************************************/ int DLLCALL putnodedat(scfg_t* cfg, uint number, node_t* node, int file) { size_t wr=0; int wrerr=0; int attempts; if(!number || number>cfg->sys_nodes || file<0) { close(file); return(-1); } number--; /* make zero based */ for(attempts=0;attempts<10;attempts++) { lseek(file,(long)number*sizeof(node_t),SEEK_SET); if((wr=write(file,node,sizeof(node_t)))==sizeof(node_t)) break; wrerr=errno; /* save write error */ mswait(100); } unlock(file,(long)number*sizeof(node_t),sizeof(node_t)); close(file); if(wr!=sizeof(node_t)) return(wrerr); return(0); } /****************************************************************************/ /* Packs the password 'pass' into 5bit ASCII inside node_t. 32bits in */ /* node.extaux, and the other 8bits in the upper byte of node.aux */ /****************************************************************************/ void DLLCALL packchatpass(char *pass, node_t *node) { char bits; int i,j; node->aux&=~0xff00; /* clear the password */ node->extaux=0L; if((j=strlen(pass))==0) /* there isn't a password */ return; node->aux|=(int)((pass[0]-64)<<8); /* 1st char goes in low 5bits of aux */ if(j==1) /* password is only one char, we're done */ return; node->aux|=(int)((pass[1]-64)<<13); /* low 3bits of 2nd char go in aux */ node->extaux|=(long)((pass[1]-64)>>3); /* high 2bits of 2nd char go extaux */ bits=2; for(i=2;i<j;i++) { /* now process the 3rd char through the last */ node->extaux|=(long)((long)(pass[i]-64)<<bits); bits+=5; } } /****************************************************************************/ /* Unpacks the password 'pass' from the 5bit ASCII inside node_t. 32bits in */ /* node.extaux, and the other 8bits in the upper byte of node.aux */ /****************************************************************************/ char* DLLCALL unpackchatpass(char *pass, node_t* node) { char bits; int i; pass[0]=(node->aux&0x1f00)>>8; pass[1]=(char)(((node->aux&0xe000)>>13)|((node->extaux&0x3)<<3)); bits=2; for(i=2;i<8;i++) { pass[i]=(char)((node->extaux>>bits)&0x1f); bits+=5; } pass[8]=0; for(i=0;i<8;i++) if(pass[i]) pass[i]+=64; return(pass); } /****************************************************************************/ /* Displays the information for node number 'number' contained in 'node' */ /****************************************************************************/ void DLLCALL printnodedat(scfg_t* cfg, uint number, node_t* node) { char mer[3]; char tmp[128]; int hour; printf("Node %2d: ",number); switch(node->status) { case NODE_WFC: printf("Waiting for call"); break; case NODE_OFFLINE: printf("Offline"); break; case NODE_NETTING: printf("Networking"); break; case NODE_LOGON: printf("At logon prompt"); break; case NODE_EVENT_WAITING: printf("Waiting for all nodes to become inactive"); break; case NODE_EVENT_LIMBO: printf("Waiting for node %d to finish external event",node->aux); break; case NODE_EVENT_RUNNING: printf("Running external event"); break; case NODE_NEWUSER: printf("New user"); printf(" applying for access "); if(!node->connection) printf("locally"); else if(node->connection==0xffff) printf("via telnet"); else printf("at %ubps",node->connection); break; case NODE_QUIET: case NODE_INUSE: printf("%s ",username(cfg, node->useron, tmp)); switch(node->action) { case NODE_MAIN: printf("at main menu"); break; case NODE_RMSG: printf("reading messages"); break; case NODE_RMAL: printf("reading mail"); break; case NODE_RSML: printf("reading sent mail"); break; case NODE_RTXT: printf("reading text files"); break; case NODE_PMSG: printf("posting message"); break; case NODE_SMAL: printf("sending mail"); break; case NODE_AMSG: printf("posting auto-message"); break; case NODE_XTRN: if(!node->aux) printf("at external program menu"); else printf("running external program #%d",node->aux); break; case NODE_DFLT: printf("changing defaults"); break; case NODE_XFER: printf("at transfer menu"); break; case NODE_RFSD: printf("retrieving from device #%d",node->aux); break; case NODE_DLNG: printf("downloading"); break; case NODE_ULNG: printf("uploading"); break; case NODE_BXFR: printf("transferring bidirectional"); break; case NODE_LFIL: printf("listing files"); break; case NODE_LOGN: printf("logging on"); break; case NODE_LCHT: printf("in local chat with sysop"); break; case NODE_MCHT: if(node->aux) { printf("in multinode chat channel %d",node->aux&0xff); if(node->aux&0x1f00) { /* password */ putchar('*'); printf(" %s",unpackchatpass(tmp,node)); } } else printf("in multinode global chat channel"); break; case NODE_PAGE: printf("paging node %u for private chat",node->aux); break; case NODE_PCHT: printf("in private chat with node %u",node->aux); break; case NODE_GCHT: printf("chatting with The Guru"); break; case NODE_CHAT: printf("in chat section"); break; case NODE_TQWK: printf("transferring QWK packet"); break; case NODE_SYSP: printf("performing sysop activities"); break; default: printf(ultoa(node->action,tmp,10)); break; } if(!node->connection) printf(" locally"); else if(node->connection==0xffff) printf(" via telnet"); else printf(" at %ubps",node->connection); if(node->action==NODE_DLNG) { if((node->aux/60)>=12) { if(node->aux/60==12) hour=12; else hour=(node->aux/60)-12; strcpy(mer,"pm"); } else { if((node->aux/60)==0) /* 12 midnite */ hour=12; else hour=node->aux/60; strcpy(mer,"am"); } printf(" ETA %02d:%02d %s" ,hour,node->aux-((node->aux/60)*60),mer); } break; } if(node->misc&(NODE_LOCK|NODE_POFF|NODE_AOFF|NODE_MSGW|NODE_NMSG)) { printf(" ("); if(node->misc&NODE_AOFF) putchar('A'); if(node->misc&NODE_LOCK) putchar('L'); if(node->misc&(NODE_MSGW|NODE_NMSG)) putchar('M'); if(node->misc&NODE_POFF) putchar('P'); putchar(')'); } if(((node->misc &(NODE_ANON|NODE_UDAT|NODE_INTR|NODE_RRUN|NODE_EVENT|NODE_DOWN)) || node->status==NODE_QUIET)) { printf(" ["); if(node->misc&NODE_ANON) putchar('A'); if(node->misc&NODE_INTR) putchar('I'); if(node->misc&NODE_RRUN) putchar('R'); if(node->misc&NODE_UDAT) putchar('U'); if(node->status==NODE_QUIET) putchar('Q'); if(node->misc&NODE_EVENT) putchar('E'); if(node->misc&NODE_DOWN) putchar('D'); if(node->misc&NODE_LCHAT) putchar('C'); putchar(']'); } if(node->errors) printf(" %d error%c",node->errors, node->errors>1 ? 's' : '\0' ); printf("\n"); } /****************************************************************************/ uint DLLCALL userdatdupe(scfg_t* cfg, uint usernumber, uint offset, uint datlen, char *dat ,BOOL del) { char str[MAX_PATH+1]; uint i; int file; long l,length; truncsp(dat); sprintf(str,"%suser/user.dat", cfg->data_dir); if((file=nopen(str,O_RDONLY|O_DENYNONE))==-1) return(0); length=filelength(file); for(l=0;l<length;l+=U_LEN) { if(usernumber && l/U_LEN==(long)usernumber-1) continue; lseek(file,l+offset,SEEK_SET); i=0; while(i<LOOP_NODEDAB && lock(file,l,U_LEN)==-1) { if(i) mswait(100); i++; } if(i>=LOOP_NODEDAB) { close(file); return(0); } read(file,str,datlen); for(i=0;i<datlen;i++) if(str[i]==ETX) break; str[i]=0; truncsp(str); if(!stricmp(str,dat)) { if(!del) { /* Don't include deleted users in search */ lseek(file,l+U_MISC,SEEK_SET); read(file,str,8); getrec(str,0,8,str); if(ahtoul(str)&(DELETED|INACTIVE)) { unlock(file,l,U_LEN); continue; } } unlock(file,l,U_LEN); close(file); return((l/U_LEN)+1); } else unlock(file,l,U_LEN); } close(file); return(0); } /****************************************************************************/ /* Creates a short message for 'usernumber' that contains 'strin' */ /****************************************************************************/ int DLLCALL putsmsg(scfg_t* cfg, int usernumber, char *strin) { char str[256]; int file,i; node_t node; sprintf(str,"%smsgs/%4.4u.msg",cfg->data_dir,usernumber); if((file=nopen(str,O_WRONLY|O_CREAT|O_APPEND))==-1) { return(errno); } i=strlen(strin); if(write(file,strin,i)!=i) { close(file); return(errno); } close(file); for(i=1;i<=cfg->sys_nodes;i++) { /* flag node if user on that msg waiting */ 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(0); } /****************************************************************************/ /* Creates a short message for node 'num' that contains 'strin' */ /****************************************************************************/ int DLLCALL putnmsg(scfg_t* cfg, int num, char *strin) { char str[256]; int file,i; node_t node; sprintf(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 i=strlen(strin); if(write(file,strin,i)!=i) { close(file); return(errno); } close(file); getnodedat(cfg,num,&node,NULL); if((node.status==NODE_INUSE || node.status==NODE_QUIET) && !(node.misc&NODE_NMSG)) { getnodedat(cfg,num,&node,&file); node.misc|=NODE_NMSG; putnodedat(cfg,num,&node,file); } return(0); } static BOOL ar_exp(scfg_t* cfg, uchar **ptrptr, user_t* user) { BOOL result,not,or,equal; uint i,n,artype=AR_LEVEL,age; ulong l; time_t now; struct tm * tm; result = TRUE; for(;(**ptrptr);(*ptrptr)++) { if((**ptrptr)==AR_ENDNEST) break; not=or=equal = FALSE; if((**ptrptr)==AR_OR) { or=1; (*ptrptr)++; } if((**ptrptr)==AR_NOT) { not=1; (*ptrptr)++; } if((**ptrptr)==AR_EQUAL) { equal=1; (*ptrptr)++; } if((result && or) || (!result && !or)) break; if((**ptrptr)==AR_BEGNEST) { (*ptrptr)++; if(ar_exp(cfg,ptrptr,user)) result=!not; else result=not; while((**ptrptr)!=AR_ENDNEST && (**ptrptr)) /* in case of early exit */ (*ptrptr)++; if(!(**ptrptr)) break; continue; } artype=(**ptrptr); switch(artype) { case AR_ANSI: /* No arguments */ case AR_RIP: case AR_WIP: case AR_LOCAL: case AR_EXPERT: case AR_SYSOP: case AR_QUIET: case AR_OS2: case AR_DOS: break; default: (*ptrptr)++; break; } n=(**ptrptr); i=(*(short *)*ptrptr); switch(artype) { case AR_LEVEL: if((equal && user->level!=n) || (!equal && user->level<n)) result=not; else result=!not; break; case AR_AGE: age=getage(cfg,user->birth); if((equal && age!=n) || (!equal && age<n)) result=not; else result=!not; break; case AR_BPS: result=!not; (*ptrptr)++; break; case AR_ANSI: if(!(user->misc&ANSI)) result=not; else result=!not; break; case AR_RIP: if(!(user->misc&RIP)) result=not; else result=!not; break; case AR_WIP: if(!(user->misc&WIP)) result=not; else result=!not; break; case AR_OS2: #ifndef __OS2__ result=not; #else result=!not; #endif break; case AR_DOS: #ifdef __FLAT__ result=not; #else result=!not; #endif break; case AR_WIN32: #ifndef _WIN32 result=not; #else result=!not; #endif break; case AR_UNIX: #ifndef __unix__ result=not; #else result=!not; #endif break; case AR_LINUX: #ifndef __linux__ result=not; #else result=!not; #endif break; case AR_EXPERT: if(!(user->misc&EXPERT)) result=not; else result=!not; break; case AR_SYSOP: if(user->level<SYSOP_LEVEL) result=not; else result=!not; break; case AR_QUIET: result=not; break; case AR_LOCAL: result=not; break; case AR_DAY: now=time(NULL); tm=localtime(&now); if(tm==NULL || (equal && tm->tm_wday!=(int)n) || (!equal && tm->tm_wday<(int)n)) result=not; else result=!not; break; case AR_CREDIT: l=(ulong)i*1024UL; if((equal && user->cdt+user->freecdt!=l) || (!equal && user->cdt+user->freecdt<l)) result=not; else result=!not; (*ptrptr)++; break; case AR_NODE: if((equal && cfg->node_num!=n) || (!equal && cfg->node_num<n)) result=not; else result=!not; break; case AR_USER: if((equal && user->number!=i) || (!equal && user->number<i)) result=not; else result=!not; (*ptrptr)++; break; case AR_GROUP: result=not; (*ptrptr)++; break; case AR_SUB: result=not; (*ptrptr)++; break; case AR_SUBCODE: result=not; while(*(*ptrptr)) (*ptrptr)++; break; case AR_LIB: result=not; (*ptrptr)++; break; case AR_DIR: result=not; (*ptrptr)++; break; case AR_DIRCODE: result=not; while(*(*ptrptr)) (*ptrptr)++; break; case AR_EXPIRE: now=time(NULL); if(!user->expire || now+((long)i*24L*60L*60L)>user->expire) result=not; else result=!not; (*ptrptr)++; break; case AR_RANDOM: n=sbbs_random(i+1); if((equal && n!=i) || (!equal && n<i)) result=not; else result=!not; (*ptrptr)++; break; case AR_LASTON: now=time(NULL); if((now-user->laston)/(24L*60L*60L)<(long)i) result=not; else result=!not; (*ptrptr)++; break; case AR_LOGONS: if((equal && user->logons!=i) || (!equal && user->logons<i)) result=not; else result=!not; (*ptrptr)++; break; case AR_MAIN_CMDS: result=not; (*ptrptr)++; break; case AR_FILE_CMDS: result=not; (*ptrptr)++; break; case AR_TLEFT: result=not; break; case AR_TUSED: result=not; break; case AR_TIME: now=time(NULL); tm=localtime(&now); if(tm==NULL || (tm->tm_hour*60)+tm->tm_min<(int)i) result=not; else result=!not; (*ptrptr)++; break; case AR_PCR: if(user->logons>user->posts && (!user->posts || 100/(user->logons/user->posts)<(long)n)) result=not; else result=!not; break; case AR_UDR: /* up/download byte ratio */ l=user->dlb; if(!l) l=1; if(user->dlb>user->ulb && (!user->ulb || 100/(l/user->ulb)<n)) result=not; else result=!not; break; case AR_UDFR: /* up/download file ratio */ i=user->dls; if(!i) i=1; if(user->dls>user->uls && (!user->uls || 100/(i/user->uls)<n)) result=not; else result=!not; break; case AR_FLAG1: if((!equal && !(user->flags1&FLAG(n))) || (equal && user->flags1!=FLAG(n))) result=not; else result=!not; break; case AR_FLAG2: if((!equal && !(user->flags2&FLAG(n))) || (equal && user->flags2!=FLAG(n))) result=not; else result=!not; break; case AR_FLAG3: if((!equal && !(user->flags3&FLAG(n))) || (equal && user->flags3!=FLAG(n))) result=not; else result=!not; break; case AR_FLAG4: if((!equal && !(user->flags4&FLAG(n))) || (equal && user->flags4!=FLAG(n))) result=not; else result=!not; break; case AR_REST: if((!equal && !(user->rest&FLAG(n))) || (equal && user->rest!=FLAG(n))) result=not; else result=!not; break; case AR_EXEMPT: if((!equal && !(user->exempt&FLAG(n))) || (equal && user->exempt!=FLAG(n))) result=not; else result=!not; break; case AR_SEX: if(user->sex!=n) result=not; else result=!not; break; case AR_SHELL: if(user->shell>=cfg->total_shells || stricmp(cfg->shell[user->shell]->code,(char*)*ptrptr)) result=not; else result=!not; while(*(*ptrptr)) (*ptrptr)++; break; } } return(result); } BOOL DLLCALL chk_ar(scfg_t* cfg, uchar *ar, user_t* user) { uchar *p; if(ar==NULL) return(TRUE); p=ar; return(ar_exp(cfg,&p,user)); } /****************************************************************************/ /* Fills 'str' with record for usernumber starting at start for length bytes*/ /* Called from function ??? */ /****************************************************************************/ int DLLCALL getuserrec(scfg_t* cfg, int usernumber,int start, int length, char *str) { char path[256]; int i,c,file; if(!usernumber) return(-1); sprintf(path,"%suser/user.dat",cfg->data_dir); if((file=nopen(path,O_RDONLY|O_DENYNONE))==-1) return(errno); if(usernumber<1 || filelength(file)<(long)((long)(usernumber-1L)*U_LEN)+(long)start) { close(file); return(-2); } lseek(file,(long)((long)(usernumber-1)*U_LEN)+start,SEEK_SET); i=0; while(i<LOOP_NODEDAB && lock(file,(long)((long)(usernumber-1)*U_LEN)+start,length)==-1) { if(i) mswait(100); i++; } if(i>=LOOP_NODEDAB) { close(file); return(-3); } if(read(file,str,length)!=length) { unlock(file,(long)((long)(usernumber-1)*U_LEN)+start,length); close(file); return(-4); } unlock(file,(long)((long)(usernumber-1)*U_LEN)+start,length); close(file); for(c=0;c<length;c++) if(str[c]==ETX || str[c]==CR) break; str[c]=0; return(0); } /****************************************************************************/ /* 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) { char str2[256]; int file; uint c,i; node_t node; if(usernumber<1) return(-1); sprintf(str2,"%suser/user.dat",cfg->data_dir); if((file=nopen(str2,O_RDWR|O_DENYNONE))==-1) return(errno); if(filelength(file)<((long)usernumber-1)*U_LEN) { close(file); return(-4); } strcpy(str2,str); if(strlen(str2)<length) { for(c=strlen(str2);c<length;c++) str2[c]=ETX; str2[c]=0; } lseek(file,(long)((long)((long)((long)usernumber-1)*U_LEN)+start),SEEK_SET); i=0; while(i<LOOP_NODEDAB && lock(file,(long)((long)(usernumber-1)*U_LEN)+start,length)==-1) { if(i) mswait(100); i++; } if(i>=LOOP_NODEDAB) return(-3); 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; } } return(0); } /****************************************************************************/ /* Updates user 'usernumber's record (numeric string) by adding 'adj' to it */ /* returns the new value. */ /****************************************************************************/ ulong DLLCALL adjustuserrec(scfg_t* cfg, int usernumber, int start, int length, long adj) { char str[256],path[256]; char tmp[32]; int i,c,file; long val; node_t node; if(usernumber<1) return(0UL); sprintf(path,"%suser/user.dat",cfg->data_dir); if((file=nopen(path,O_RDWR|O_DENYNONE))==-1) return(0UL); if(filelength(file)<((long)usernumber-1)*U_LEN) { close(file); return(0); } lseek(file,(long)((long)(usernumber-1)*U_LEN)+start,SEEK_SET); i=0; while(i<LOOP_NODEDAB && lock(file,(long)((long)(usernumber-1)*U_LEN)+start,length)==-1) { if(i) mswait(100); i++; } if(i>=LOOP_NODEDAB) { close(file); return(0); } if(read(file,str,length)!=length) { unlock(file,(long)((long)(usernumber-1)*U_LEN)+start,length); close(file); return(0UL); } for(c=0;c<length;c++) if(str[c]==ETX || str[c]==CR) break; str[c]=0; val=atol(str); if(adj<0L && val<-adj) /* don't go negative */ val=0UL; else val+=adj; lseek(file,(long)((long)(usernumber-1)*U_LEN)+start,SEEK_SET); putrec(str,0,length,ultoa(val,tmp,10)); if(write(file,str,length)!=length) { unlock(file,(long)((long)(usernumber-1)*U_LEN)+start,length); close(file); return(val); } 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; } } return(val); } /****************************************************************************/ /* Subtract credits from the current user online, accounting for the new */ /* "free credits" field. */ /****************************************************************************/ void DLLCALL subtract_cdt(scfg_t* cfg, user_t* user, long amt) { char tmp[64]; long mod; if(!amt) return; if(user->freecdt) { if((ulong)amt>user->freecdt) { /* subtract both credits and */ mod=amt-user->freecdt; /* free credits */ putuserrec(cfg, user->number,U_FREECDT,10,"0"); user->freecdt=0; user->cdt=adjustuserrec(cfg, user->number,U_CDT,10,-mod); } else { /* subtract just free credits */ user->freecdt-=amt; putuserrec(cfg, user->number,U_FREECDT,10 ,ultoa(user->freecdt,tmp,10)); } } else /* no free credits */ user->cdt=adjustuserrec(cfg, user->number,U_CDT,10,-amt); } /****************************************************************************/ /****************************************************************************/ BOOL DLLCALL logoutuserdat(scfg_t* cfg, user_t* user, time_t now, time_t logontime) { char str[128]; struct tm* tm, tm_now; user->tlast=(now-logontime)/60; putuserrec(cfg,user->number,U_LASTON,8,ultoa(now,str,16)); putuserrec(cfg,user->number,U_TLAST,5,ultoa(user->tlast,str,10)); adjustuserrec(cfg,user->number,U_TIMEON,5,user->tlast); adjustuserrec(cfg,user->number,U_TTODAY,5,user->tlast); /* Convert time_t to struct tm */ tm=localtime(&now); if(tm==NULL) return(FALSE); tm_now=*tm; tm=localtime(&logontime); if(tm==NULL) return(FALSE); /* Reset daily stats if new day */ if(tm->tm_mday!=tm_now.tm_mday) resetdailyuserdat(cfg, user); return(TRUE); } /****************************************************************************/ /****************************************************************************/ void DLLCALL resetdailyuserdat(scfg_t* cfg, user_t* user) { char str[128]; /* logons today */ user->ltoday=0; putuserrec(cfg,user->number,U_LTODAY,5,"0"); /* e-mails today */ user->etoday=0; putuserrec(cfg,user->number,U_ETODAY,5,"0"); /* posts today */ user->ptoday=0; 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 ,ultoa(user->freecdt,str,10)); /* time used today */ user->ttoday=0; putuserrec(cfg,user->number,U_TTODAY,5,"0"); /* extra time today */ user->textra=0; putuserrec(cfg,user->number,U_TEXTRA,5,"0"); } /****************************************************************************/ /****************************************************************************/ char* DLLCALL usermailaddr(scfg_t* cfg, char* addr, char* name) { int i; if(strchr(name,'@')!=NULL) { /* Avoid double-@ */ strcpy(addr,name); return(addr); } if(strchr(name,'.') && strchr(name,' ')) sprintf(addr,"\"%s\"@",name); else { sprintf(addr,"%s@",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,cfg->sys_inetaddr); return(addr); } char* DLLCALL alias(scfg_t* cfg, char* name, char* buf) { int file; char line[128]; char* p; char* np; char* tp; char fname[MAX_PATH+1]; size_t namelen; size_t cmplen; FILE* fp; p=name; sprintf(fname,"%salias.cfg",cfg->ctrl_dir); if((file=sopen(fname,O_RDONLY|O_BINARY,SH_DENYNO))==-1) return(name); if((fp=fdopen(file,"rb"))==NULL) { close(file); return(name); } while(!feof(fp)) { if(!fgets(line,sizeof(line),fp)) break; np=line; while(*np && *np<=' ') np++; if(*np==';') continue; tp=np; while(*tp && *tp>' ') tp++; if(*tp) *tp=0; if(*np=='*') { np++; cmplen=strlen(np); namelen=strlen(name); if(namelen<cmplen) continue; if(strnicmp(np,name+(namelen-cmplen),cmplen)) continue; np=tp+1; while(*np && *np<=' ') np++; truncsp(np); if(*np=='*') sprintf(buf,"%.*s%s",namelen-cmplen,name,np+1); else strcpy(buf,np); p=buf; break; } if(!stricmp(np,name)) { np=tp+1; while(*np && *np<=' ') np++; truncsp(np); strcpy(buf,np); p=buf; break; } } fclose(fp); return(p); }