Newer
Older
if(*np==';' || *np==0) /* no name value, or comment */
continue;
tp=np;
FIND_WHITESPACE(tp);
if(*tp==0) /* no alias value */
continue;
*tp=0;
vp=tp+1;
SKIP_WHITESPACE(vp);
truncsp(vp);
if(*vp==0) /* no value */
continue;
if(*np=='*') {
np++;
cmplen=strlen(np);
namelen=strlen(name);
if(namelen<cmplen)
continue;
if(strnicmp(np,name+(namelen-cmplen),cmplen))
continue;
sprintf(buf,"%.*s%s",(int)(namelen-cmplen),name,vp+1);
strcpy(buf,vp);
p=buf;
break;
}
if(!stricmp(np,name)) {
strcpy(buf,vp);
break;
}
}
fclose(fp);
return(p);
}
int newuserdat(scfg_t* cfg, user_t* user)
{
char str[MAX_PATH+1];
char tmp[128];
int c;
int i;
int err;
int file;
int unum=1;
int last;
long misc;
FILE* stream;
stats_t stats;
if(!VALID_CFG(cfg) || user==NULL)
return(-1);
SAFEPRINTF(str,"%suser/name.dat",cfg->data_dir);
if(fexist(str)) {
if((stream=fnopen(&file,str,O_RDONLY))==NULL) {
last=(long)filelength(file)/(LEN_ALIAS+2); /* total users */
while(unum<=last) {
fread(str,LEN_ALIAS+2,1,stream);
for(c=0;c<LEN_ALIAS;c++)
if(str[c]==ETX) break;
str[c]=0;
if(!c) { /* should be a deleted user */
user_t deluser;
deluser.number = unum;
if(getuserdat(cfg, &deluser) == 0) {
if(deluser.misc&DELETED) { /* deleted bit set too */
if((time(NULL)-deluser.laston)/86400>=cfg->sys_deldays)
break; /* deleted long enough ? */
}
}
last=lastuser(cfg); /* Check against data file */
if(unum>last+1) /* Corrupted name.dat? */
unum=last+1;
else if(unum<=last) { /* Overwriting existing user */
user_t deluser;
deluser.number = unum;
if(getuserdat(cfg, &deluser) == 0) {
if(!(deluser.misc&DELETED)) /* Not deleted? Set usernumber to end+1 */
}
user->number=unum; /* store the new user number */
if((err=putusername(cfg,user->number,user->alias))!=0)
return(err);
if((err=putuserdat(cfg,user))!=0)
return(err);
SAFEPRINTF2(str,"%sfile/%04u.in",cfg->data_dir,user->number); /* delete any files */
delfiles(str, ALLFILES, /* keep: */0); /* waiting for user */
SAFEPRINTF(tmp,"%04u.*",user->number);
SAFEPRINTF(str,"%sfile",cfg->data_dir);
delfiles(str,tmp, /* keep: */0);
SAFEPRINTF(str,"%suser",cfg->data_dir);
delfiles(str,tmp, /* keep: */0);
SAFEPRINTF(str,"%smsgs",cfg->data_dir);
delfiles(str,tmp, /* keep: */0);
SAFEPRINTF2(str,"%suser/%04u",cfg->data_dir,user->number);
delfiles(str,ALLFILES, /* keep: */0);
rmdir(str);
SAFEPRINTF2(str,"%suser/ptrs/%04u.ixb",cfg->data_dir,user->number); /* legacy msg ptrs */
/* Update daily statistics database (for system and node) */
for(i=0;i<2;i++) {
FILE* fp = fopen_dstats(cfg, i ? cfg->node_num : 0, /* for_write: */TRUE);
if(fp == NULL)
if(fread_dstats(fp, &stats)) {
stats.today.nusers++;
stats.total.nusers++;
fwrite_dstats(fp, &stats);
}
fclose_dstats(fp);
}
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
size_t user_field_len(enum user_field fnum)
{
user_t user;
switch(fnum) {
case USER_ALIAS: return sizeof(user.alias) - 1;
case USER_NAME: return sizeof(user.name) - 1;
case USER_HANDLE: return sizeof(user.handle) -1;
case USER_NOTE: return sizeof(user.note) - 1;
case USER_IPADDR: return sizeof(user.ipaddr) - 1;
case USER_HOST: return sizeof(user.comp) - 1;
case USER_NETMAIL: return sizeof(user.netmail) - 1;
case USER_ADDRESS: return sizeof(user.address) - 1;
case USER_LOCATION: return sizeof(user.location) - 1;
case USER_ZIPCODE: return sizeof(user.zipcode) - 1;
case USER_PHONE: return sizeof(user.phone) - 1;
case USER_BIRTH: return sizeof(user.birth) - 1;
case USER_GENDER: return sizeof(user.sex);
case USER_COMMENT: return sizeof(user.comment) - 1;
case USER_CONNECTION: return sizeof(user.modem) -1;
// Bit-fields:
case USER_MISC: return sizeof(user.misc);
case USER_QWK: return sizeof(user.qwk);
case USER_CHAT: return sizeof(user.chat);
// Settings:
case USER_ROWS: return sizeof(user.rows);
case USER_COLS: return sizeof(user.cols);
case USER_XEDIT: return sizeof(user.xedit);
case USER_SHELL: return sizeof(user.shell);
case USER_TMPEXT: return sizeof(user.tmpext) - 1;
case USER_PROT: return sizeof(user.prot);
case USER_CURSUB: return sizeof(user.cursub) - 1;
case USER_CURDIR: return sizeof(user.curdir) - 1;
case USER_CURXTRN: return sizeof(user.curxtrn) - 1;
// Date/times:
case USER_LOGONTIME: return sizeof(user.logontime);
case USER_NS_TIME: return sizeof(user.ns_time);
case USER_LASTON: return sizeof(user.laston);
case USER_FIRSTON: return sizeof(user.firston);
// Counting stats:
case USER_LOGONS: return sizeof(user.logons);
case USER_LTODAY: return sizeof(user.ltoday);
case USER_TIMEON: return sizeof(user.timeon);
case USER_TTODAY: return sizeof(user.ttoday);
case USER_TLAST: return sizeof(user.tlast);
case USER_POSTS: return sizeof(user.posts);
case USER_EMAILS: return sizeof(user.emails);
case USER_FBACKS: return sizeof(user.fbacks);
case USER_ETODAY: return sizeof(user.etoday);
case USER_PTODAY: return sizeof(user.ptoday);
// File xfer stats:
case USER_ULB: return sizeof(user.ulb);
case USER_ULS: return sizeof(user.uls);
case USER_DLB: return sizeof(user.dlb);
case USER_DLS: return sizeof(user.dls);
case USER_LEECH: return sizeof(user.leech);
// Security:
case USER_PASS: return sizeof(user.pass) - 1;
case USER_PWMOD: return sizeof(user.pwmod);
case USER_LEVEL: return sizeof(user.level);
case USER_FLAGS1: return sizeof(user.flags1);
case USER_FLAGS2: return sizeof(user.flags2);
case USER_FLAGS3: return sizeof(user.flags3);
case USER_FLAGS4: return sizeof(user.flags4);
case USER_EXEMPT: return sizeof(user.exempt);
case USER_REST: return sizeof(user.rest);
case USER_CDT: return sizeof(user.cdt);
case USER_FREECDT: return sizeof(user.freecdt);
case USER_MIN: return sizeof(user.min);
case USER_TEXTRA: return sizeof(user.textra);
case USER_EXPIRE: return sizeof(user.expire);
default: return 0;
}
}
/* Returns length of specified user record 'field', or -1 if invalid */
int user_rec_len(int offset)
{
switch(offset) {
/* Strings (of different lengths) */
case U_ALIAS: return(LEN_ALIAS);
case U_NAME: return(LEN_NAME);
case U_HANDLE: return(LEN_HANDLE);
case U_NOTE: return(LEN_NOTE);
case U_COMP: return(LEN_COMP);
case U_COMMENT: return(LEN_COMMENT);
case U_NETMAIL: return(LEN_NETMAIL);
case U_ADDRESS: return(LEN_ADDRESS);
case U_LOCATION: return(LEN_LOCATION);
case U_ZIPCODE: return(LEN_ZIPCODE);
case U_PASS: return(LEN_PASS);
case U_PHONE: return(LEN_PHONE);
case U_BIRTH: return(LEN_BIRTH);
case U_MODEM: return(LEN_MODEM);
/* Internal codes (16 chars) */
case U_CURSUB:
case U_CURDIR:
return (16);
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
/* Dates in time_t format (8 hex digits) */
case U_LASTON:
case U_FIRSTON:
case U_EXPIRE:
case U_PWMOD:
case U_NS_TIME:
case U_LOGONTIME:
/* 32-bit integers (8 hex digits) */
case U_FLAGS1:
case U_FLAGS2:
case U_FLAGS3:
case U_FLAGS4:
case U_EXEMPT:
case U_REST:
case U_MISC:
case U_QWK:
case U_CHAT:
/* Internal codes (8 chars) */
case U_CURXTRN:
case U_XEDIT:
case U_SHELL:
return(8);
/* 16-bit integers (5 decimal digits) */
case U_LOGONS:
case U_LTODAY:
case U_TIMEON:
case U_TEXTRA:
case U_TTODAY:
case U_TLAST:
case U_POSTS:
case U_EMAILS:
case U_FBACKS:
case U_ETODAY:
case U_PTODAY:
case U_ULS:
case U_DLS:
return(5);
/* 32-bit integers (10 decimal digits) */
case U_ULB:
case U_DLB:
case U_MIN:
return(10);
/* 64-bit integers (20 decimal digits) */
case U_CDT:
case U_FREECDT:
return 20;
/* 3 char strings */
case U_TMPEXT:
return(3);
case U_ROWS:
return LEN_ROWS;
case U_COLS:
return LEN_COLS;
/* 2 digit integers (0-99 or 00-FF) */
case U_LEVEL:
case U_TL:
return(2);
/* Single digits chars */
case U_SEX:
case U_PROT:
return(1);
}
return(-1);
/****************************************************************************/
/* Determine if the specified user can or cannot access the specified sub */
/****************************************************************************/
BOOL 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;
return FALSE;
return TRUE;
}
/****************************************************************************/
/* Determine if the specified user can or cannot read the specified sub */
/****************************************************************************/
BOOL 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 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;
return FALSE;
if(cfg->sub[subnum]->misc&(SUB_QNET|SUB_FIDO|SUB_PNET|SUB_INET)
&& user->rest&FLAG('N')) /* network restriction? */
return FALSE;
if((cfg->sub[subnum]->misc & SUB_NAME)
&& (user->rest & (FLAG('Q') | FLAG('O'))) == FLAG('O'))
return FALSE;
if(reason!=NULL)
*reason=R_Post;
if(user->rest&FLAG('P')) /* post restriction? */
if(reason!=NULL)
*reason=TooManyPostsToday;
if(user->ptoday>=cfg->level_postsperday[user->level])
return FALSE;
return TRUE;
}
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
/****************************************************************************/
// Determine if the specified user can access one or more directories of lib
/****************************************************************************/
BOOL can_user_access_lib(scfg_t* cfg, uint libnum, user_t* user, client_t* client)
{
uint count = 0;
for(uint dirnum = 0; dirnum < cfg->total_dirs; dirnum++) {
if(cfg->dir[dirnum]->lib != libnum)
continue;
if(can_user_access_dir(cfg, dirnum, user, client)) // checks lib's AR already
count++;
}
return count >= 1; // User has access to one or more directories of library
}
/****************************************************************************/
// Determine if the specified user can access ALL file libraries
/****************************************************************************/
BOOL can_user_access_all_libs(scfg_t* cfg, user_t* user, client_t* client)
{
for(uint libnum = 0; libnum < cfg->total_libs; libnum++) {
if(!can_user_access_lib(cfg, libnum, user, client))
return FALSE;
}
return TRUE;
}
/****************************************************************************/
// Determine if the specified user can all dirs of a lib
/****************************************************************************/
BOOL can_user_access_all_dirs(scfg_t* cfg, uint libnum, user_t* user, client_t* client)
{
uint count = 0;
for(uint dirnum = 0; dirnum < cfg->total_dirs; dirnum++) {
if(cfg->dir[dirnum]->lib != libnum)
continue;
if(can_user_access_dir(cfg, dirnum, user, client)) // checks lib's AR already
count++;
else
return FALSE;
}
return count >= 1; // User has access to one or more directories of library
}
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
/****************************************************************************/
/* Determine if the specified user can or cannot access the specified dir */
/****************************************************************************/
BOOL can_user_access_dir(scfg_t* cfg, uint dirnum, user_t* user, client_t* client)
{
if(!VALID_CFG(cfg))
return FALSE;
if(dirnum>=cfg->total_dirs)
return FALSE;
if(!chk_ar(cfg,cfg->lib[cfg->dir[dirnum]->lib]->ar,user,client))
return FALSE;
if(!chk_ar(cfg,cfg->dir[dirnum]->ar,user,client))
return FALSE;
return TRUE;
}
/****************************************************************************/
/* Determine if the specified user can or cannot upload files to the dirnum */
/* 'reason' is an (optional) pointer to a text.dat item number, indicating */
/* the reason the user cannot post, when returning FALSE. */
/****************************************************************************/
BOOL can_user_upload(scfg_t* cfg, uint dirnum, user_t* user, client_t* client, uint* reason)
{
if(reason!=NULL)
*reason=NoAccessDir;
if(!can_user_access_dir(cfg, dirnum, user, client))
return FALSE;
if(reason!=NULL)
*reason=R_Upload;
if(user->rest&FLAG('U')) /* upload restriction? */
return FALSE;
if(user->rest&FLAG('T')) /* transfer restriction? */
return FALSE;
if(!(user->exempt&FLAG('U')) /* upload exemption */
&& !is_user_dirop(cfg, dirnum, user, client)) {
if(reason!=NULL)
*reason=CantUploadHere;
if(!chk_ar(cfg, cfg->dir[dirnum]->ul_ar, user, client))
return FALSE;
}
return TRUE;
}
/****************************************************************************/
/* Determine if the specified user can or cannot download files from dirnum */
/* 'reason' is an (optional) pointer to a text.dat item number, indicating */
/* the reason the user cannot post, when returning FALSE. */
/****************************************************************************/
BOOL can_user_download(scfg_t* cfg, uint dirnum, user_t* user, client_t* client, uint* reason)
{
if(reason!=NULL)
*reason=NoAccessDir;
if(!can_user_access_dir(cfg, dirnum, user, client))
return FALSE;
if(reason!=NULL)
*reason=CantDownloadFromDir;
if(!chk_ar(cfg,cfg->dir[dirnum]->dl_ar,user,client))
return FALSE;
if(reason!=NULL)
*reason=R_Download;
if(user->rest&FLAG('D')) /* download restriction? */
return FALSE;
if(user->rest&FLAG('T')) /* transfer restriction? */
return FALSE;
return TRUE;
}
/****************************************************************************/
/* Determine if the specified user can or cannot send email */
/* 'reason' is an (optional) pointer to a text.dat item number */
/* usernumber==0 for netmail */
/****************************************************************************/
BOOL can_user_send_mail(scfg_t* cfg, enum smb_net_type net_type, uint usernumber, user_t* user, uint* reason)
{
if(reason!=NULL)
*reason=R_Email;
if(user==NULL || user->number==0)
return FALSE;
if(net_type==NET_NONE && usernumber>1 && user->rest&FLAG('E')) /* local mail restriction? */
return FALSE;
if(reason!=NULL)
*reason=NoNetMailAllowed;
if(net_type!=NET_NONE && user->rest&FLAG('M')) /* netmail restriction */
return FALSE;
if(net_type==NET_FIDO && !(cfg->netmail_misc&NMAIL_ALLOW)) /* Fido netmail globally disallowed */
return FALSE;
if(net_type==NET_INTERNET && !(cfg->inetmail_misc&NMAIL_ALLOW)) /* Internet mail globally disallowed */
return FALSE;
if(reason!=NULL)
*reason=R_Feedback;
if(net_type==NET_NONE && usernumber==1 && user->rest&FLAG('S')) /* feedback restriction? */
return FALSE;
if(reason!=NULL)
*reason=TooManyEmailsToday;
if(user->etoday>=cfg->level_emailperday[user->level] && !(user->exempt&FLAG('M')))
return FALSE;
return TRUE;
}
/****************************************************************************/
/* Determine if the specified user is a sub-board operator */
/****************************************************************************/
BOOL is_user_subop(scfg_t* cfg, uint subnum, user_t* user, client_t* client)
{
if(user==NULL)
return FALSE;
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 the specified user is a directory operator */
/****************************************************************************/
BOOL is_user_dirop(scfg_t* cfg, uint dirnum, user_t* user, client_t* client)
{
if(user==NULL)
return FALSE;
if(!can_user_access_dir(cfg, dirnum, user, client))
return FALSE;
if(user->level >= SYSOP_LEVEL)
return TRUE;
return cfg->dir[dirnum]->op_ar[0]!=0 && chk_ar(cfg,cfg->dir[dirnum]->op_ar,user,client);
/****************************************************************************/
/* Determine if downloads from the specified directory are free for the */
/* specified user */
/****************************************************************************/
BOOL is_download_free(scfg_t* cfg, uint dirnum, user_t* user, client_t* client)
{
if(!VALID_CFG(cfg))
return(FALSE);
if(dirnum>=cfg->total_dirs)
return(FALSE);
if(cfg->dir[dirnum]->misc&DIR_FREE)
return(TRUE);
if(user==NULL)
return(FALSE);
if(user->exempt&FLAG('D'))
return(TRUE);
return(FALSE);
BOOL is_host_exempt(scfg_t* cfg, const char* ip_addr, const char* host_name)
{
char exempt[MAX_PATH+1];
SAFEPRINTF2(exempt, "%s%s", cfg->ctrl_dir, strIpFilterExemptConfigFile);
return findstr(ip_addr, exempt) || findstr(host_name, exempt);
}
/****************************************************************************/
/* Add an IP address (with comment) to the IP filter/trashcan file */
/* ToDo: Move somewhere more appropriate (filter.c?) */
/****************************************************************************/
BOOL 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 exempt[MAX_PATH+1];
char tstr[64];
FILE* fp;
time_t now = time(NULL);
if(ip_addr==NULL)
return(FALSE);
SAFEPRINTF2(exempt, "%s%s", cfg->ctrl_dir, strIpFilterExemptConfigFile);
if(findstr(ip_addr, exempt))
return(FALSE);
if(findstr(host, exempt))
return(FALSE);
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 ", prot, reason);
if(username != NULL)
fprintf(fp, "by %s ", username);
fprintf(fp,"on %.24s\n", ctime_r(&now, tstr));
if(host!=NULL)
fprintf(fp,"; Hostname: %s\n",host);
fclose(fp);
return(TRUE);
}
/****************************************************************************/
/* Note: This function does not account for timed events! */
/****************************************************************************/
time_t 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*=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;
long tused = (long)MAX(now - starttime, 0);
tleft -= tused;
if(tleft < 0)
tleft = 0;
if(tleft>0x7fffL)
timeleft=0x7fff;
else
return(timeleft);
}
/*************************************************************************/
/* Check a supplied name/alias and see if it's valid by our standards. */
/*************************************************************************/
BOOL 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 */
|| !IS_ALPHA(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* loginAttemptListInit(link_list_t* list)
{
return listInit(list, LINK_LIST_MUTEX);
}
/****************************************************************************/
BOOL loginAttemptListFree(link_list_t* list)
{
return listFree(list);
}
/****************************************************************************/
/* Returns negative value on failure */
/****************************************************************************/
long loginAttemptListCount(link_list_t* list)
if(!listLock(list))
return -1;
count = listCountNodes(list);
listUnlock(list);
return count;
}
/****************************************************************************/
/* Returns number of items (attempts) removed from the list */
/* Returns negative value on failure */
/****************************************************************************/
long loginAttemptListClear(link_list_t* list)
long count;
if(!listLock(list))
return -1;
count=listCountNodes(list);
count-=listFreeNodes(list);
listUnlock(list);
return count;
}
/****************************************************************************/
static list_node_t* login_attempted(link_list_t* list, const union xp_sockaddr* addr)
{
list_node_t* node;
login_attempt_t* attempt;
if(list==NULL)
return NULL;
for(node=list->first; node!=NULL; node=node->next) {
attempt=node->data;
if(attempt->addr.addr.sa_family != addr->addr.sa_family)
continue;
if(memcmp(&attempt->addr.in.sin_addr, &addr->in.sin_addr, sizeof(addr->in.sin_addr)) == 0)
return node;
if(memcmp(&attempt->addr.in6.sin6_addr, &addr->in6.sin6_addr, sizeof(addr->in6.sin6_addr)) == 0)
return node;
}
return NULL;
}
/****************************************************************************/
/* Returns negative value on failure */
/****************************************************************************/
long loginAttempts(link_list_t* list, const union xp_sockaddr* addr)
long count=0;
if(addr->addr.sa_family != AF_INET && addr->addr.sa_family != AF_INET6)
return 0;
if(!listLock(list))
return -1;
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 loginSuccess(link_list_t* list, const union xp_sockaddr* addr)
{
list_node_t* node;
if(addr->addr.sa_family != AF_INET && addr->addr.sa_family != AF_INET6)
return;
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 loginFailure(link_list_t* list, const union xp_sockaddr* addr, const char* prot, const char* user, const char* pass)
{
list_node_t* node;
login_attempt_t* attempt=&first;
ulong count=0;
if(addr->addr.sa_family != AF_INET && addr->addr.sa_family != AF_INET6)
return 0;
if(list==NULL)
return 0;
if(!listLock(list))
return 0;
if((node=login_attempted(list, addr)) != NULL) {
attempt=node->data;
/* Don't count consecutive duplicate attempts (same name and password): */
if((user!=NULL && strcmp(attempt->user,user)==0) && (pass!=NULL && strcmp(attempt->pass,pass)==0))
}
SAFECOPY(attempt->prot,prot);
attempt->time=time32(NULL);
memcpy(&attempt->addr, addr, sizeof(*addr));
if(user != NULL)
SAFECOPY(attempt->user, user);
memset(attempt->pass, 0, sizeof(attempt->pass));
if(pass != NULL)
SAFECOPY(attempt->pass, pass);
attempt->count++;
if(node==NULL)
listPushNodeData(list, attempt, sizeof(login_attempt_t));
listUnlock(list);
return count;
}
#if !defined(NO_SOCKET_SUPPORT)
ulong loginBanned(scfg_t* cfg, link_list_t* list, SOCKET sock, const char* host_name
,struct login_attempt_settings settings, login_attempt_t* details)
char ip_addr[128];
list_node_t* node;
login_attempt_t* attempt;
BOOL result = FALSE;
time32_t now = time32(NULL);
union xp_sockaddr client_addr;
union xp_sockaddr server_addr;
socklen_t addr_len;
char exempt[MAX_PATH+1];
SAFEPRINTF2(exempt, "%s%s", cfg->ctrl_dir, strIpFilterExemptConfigFile);
if(list==NULL)
return 0;
addr_len=sizeof(server_addr);
if((result=getsockname(sock, &server_addr.addr, &addr_len)) != 0)
return 0;
addr_len=sizeof(client_addr);
if((result=getpeername(sock, &client_addr.addr, &addr_len)) != 0)
return 0;
/* Don't ban connections from the server back to itself */
if(inet_addrmatch(&server_addr, &client_addr))
return 0;
if(inet_addrtop(&client_addr, ip_addr, sizeof(ip_addr)) != NULL
&& findstr(ip_addr, exempt))
return 0;
if(host_name != NULL
&& findstr(host_name, exempt))
return 0;
if(!listLock(list))
return 0;
node = login_attempted(list, &client_addr);
listUnlock(list);
if(node == NULL)
return 0;
attempt = node->data;
if(((settings.tempban_threshold && (attempt->count - attempt->dupes) >= settings.tempban_threshold)
|| trashcan(cfg, attempt->user, "name")) && now < (time32_t)(attempt->time + settings.tempban_duration)) {
if(details != NULL)
*details = *attempt;
return settings.tempban_duration - (now - attempt->time);
/****************************************************************************/
/* Message-new-scan pointer/configuration functions */
/****************************************************************************/
BOOL getmsgptrs(scfg_t* cfg, user_t* user, subscan_t* subscan, void (*progress)(void*, int, int), void* cbdata)
{
char path[MAX_PATH+1];
uint i;
int file;
long length;
FILE* stream;
/* Initialize to configured defaults */
for(i=0;i<cfg->total_subs;i++) {
subscan[i].ptr=subscan[i].sav_ptr=0;
subscan[i].last=subscan[i].sav_last=0;
subscan[i].cfg=0xff;
if(!(cfg->sub[i]->misc&SUB_NSDEF))
subscan[i].cfg&=~SUB_CFG_NSCAN;
if(!(cfg->sub[i]->misc&SUB_SSDEF))
subscan[i].cfg&=~SUB_CFG_SSCAN;
subscan[i].sav_cfg=subscan[i].cfg;
if(user->number == 0)
return 0;
if(user->rest&FLAG('G'))
return initmsgptrs(cfg, subscan, cfg->guest_msgscan_init, progress, cbdata);
/* New way: */
SAFEPRINTF2(path,"%suser/%4.4u.subs", cfg->data_dir, user->number);
FILE* fp = fnopen(NULL, path, O_RDONLY|O_TEXT);
if (fp != NULL) {
str_list_t ini = iniReadFile(fp);
for(i = 0; i < cfg->total_subs; i++) {
if(progress != NULL)
progress(cbdata, i, cfg->total_subs);
str_list_t keys = iniGetSection(ini, cfg->sub[i]->code);
if(keys == NULL)
continue;
subscan[i].ptr = iniGetUInt32(keys, NULL, "ptr" , subscan[i].ptr);
subscan[i].last = iniGetUInt32(keys, NULL, "last" , subscan[i].last);
subscan[i].cfg = iniGetShortInt(keys, NULL, "cfg" , subscan[i].cfg);
subscan[i].cfg &= (SUB_CFG_NSCAN|SUB_CFG_SSCAN|SUB_CFG_YSCAN); // Sanitize the 'cfg' value
subscan[i].sav_ptr = subscan[i].ptr;
subscan[i].sav_last = subscan[i].last;
subscan[i].sav_cfg = subscan[i].cfg;
iniFreeStringList(keys);
iniRemoveSection(&ini, cfg->sub[i]->code);
}
iniFreeStringList(ini);
fclose(fp);
if(progress != NULL)
progress(cbdata, i, cfg->total_subs);
return TRUE;
}
/* Old way: */
SAFEPRINTF2(path,"%suser/ptrs/%4.4u.ixb", cfg->data_dir, user->number);
if((stream=fnopen(&file,path,O_RDONLY))==NULL) {
if(fexist(path))
return(FALSE); /* file exists, but couldn't be opened? */
return initmsgptrs(cfg, subscan, cfg->new_msgscan_init, progress, cbdata);
}
length=(long)filelength(file);
for(i=0;i<cfg->total_subs;i++) {
if(progress != NULL)
progress(cbdata, i, cfg->total_subs);
if(length>=(cfg->sub[i]->ptridx+1)*10L) {
fseek(stream,(long)cfg->sub[i]->ptridx*10L,SEEK_SET);
fread(&subscan[i].ptr,sizeof(subscan[i].ptr),1,stream);
fread(&subscan[i].last,sizeof(subscan[i].last),1,stream);
fread(&subscan[i].cfg,sizeof(subscan[i].cfg),1,stream);
}
subscan[i].sav_ptr=subscan[i].ptr;
subscan[i].sav_last=subscan[i].last;
subscan[i].sav_cfg=subscan[i].cfg;
if(progress != NULL)
progress(cbdata, i, cfg->total_subs);
fclose(stream);
return(TRUE);
}
/****************************************************************************/
/* Writes to data/user/####.subs the msgptr array for the current user */
/* Pass usernumber value of 0 to indicate "Guest" login */
/****************************************************************************/
BOOL putmsgptrs(scfg_t* cfg, user_t* user, subscan_t* subscan)
uint i;
time_t now = time(NULL);
BOOL result = TRUE;
if(user->number==0 || (user->rest&FLAG('G'))) /* Guest */
return(TRUE);