Newer
Older
user->freecdt=cfg->level_freecdtperday[user->level];
if(cfg->new_expire)
user->expire=user->firston+((long)cfg->new_expire*24L*60L*60L);
else
user->expire=0;
/* settings */
if(cfg->total_fcomps)
SAFECOPY(user->tmpext,cfg->fcomp[0]->ext);
else
SAFECOPY(user->tmpext,"zip");
user->shell = cfg->new_shell;
user->misc = cfg->new_misc|(AUTOTERM|COLOR);
user->misc &= ~(DELETED|INACTIVE|QUIET|NETMAIL);
user->prot = cfg->new_prot;
user->qwk = cfg->new_qwk;
user->chat = cfg->new_chat;
for(i=0;i<cfg->total_xedits;i++)
if(!stricmp(cfg->xedit[i]->code,cfg->new_xedit) && chk_ar(cfg,cfg->xedit[i]->ar, user, /* client: */NULL))
break;
if(i<cfg->total_xedits)
user->xedit=i+1;
return 0;
}
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;
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 */
unum=last+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);
}
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
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
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_DLCPS: return sizeof(user.dlcps);
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
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;
}
}
/****************************************************************************/
/* 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=CantPostOnSub;
if(!can_user_access_sub(cfg, subnum, user, client))
return FALSE;
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;
}
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
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
/****************************************************************************/
// 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
}
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
/****************************************************************************/
/* 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=CantUploadHere;
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->lib[cfg->dir[dirnum]->lib]->ul_ar, user, client))
return FALSE;
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=CantDownloadFromDir;

Rob Swindell
committed
if(dirnum != cfg->user_dir && !can_user_access_dir(cfg, dirnum, user, client))
if(!chk_ar(cfg,cfg->lib[cfg->dir[dirnum]->lib]->dl_ar,user,client))
return FALSE;
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 system operator */
/****************************************************************************/
BOOL is_user_sysop(user_t* user)
{
if(user == NULL)
return FALSE;
return user->level >= SYSOP_LEVEL;
}
/****************************************************************************/
/* 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;
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;
return (cfg->dir[dirnum]->op_ar[0]!=0 && chk_ar(cfg,cfg->dir[dirnum]->op_ar,user,client))
|| (cfg->lib[cfg->dir[dirnum]->lib]->op_ar[0]!=0 && chk_ar(cfg,cfg->lib[cfg->dir[dirnum]->lib]->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);
if(cfg->lib[cfg->dir[dirnum]->lib]->ex_ar[0] != 0
&& chk_ar(cfg,cfg->lib[cfg->dir[dirnum]->lib]->ex_ar,user,client))
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;
/*************************************************************************/
/* Check a supplied real name and see if it's valid by our standards. */
/*************************************************************************/
BOOL check_realname(scfg_t* cfg, const char* name)
{
if(name == NULL)
return FALSE;
return (uchar)name[0]<0x7f && name[1] && IS_ALPHA(name[0]) && strchr(name,' ');
}
/****************************************************************************/
/* 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);
fixmsgptrs(cfg, subscan);
SAFEPRINTF2(path,"%suser/%4.4u.subs", cfg->data_dir, user->number);
FILE* fp = fnopen(NULL, path, O_RDWR|O_CREAT|O_TEXT);
if (fp == NULL)
return FALSE;
str_list_t new = strListInit();
str_list_t ini = iniReadFile(fp);
ini_style_t ini_style = { .key_prefix = "\t", .section_separator = "" };
BOOL modified = FALSE;
for(i=0; i < cfg->total_subs; i++) {
str_list_t keys = iniGetSection(ini, cfg->sub[i]->code);
if(subscan[i].sav_ptr==subscan[i].ptr
&& subscan[i].sav_cfg==subscan[i].cfg
&& keys != NULL && *keys != NULL)
iniAppendSectionWithKeys(&new, cfg->sub[i]->code, keys, &ini_style);
else {
iniSetUInt32(&new, cfg->sub[i]->code, "ptr", subscan[i].ptr, &ini_style);
iniSetUInt32(&new, cfg->sub[i]->code, "last", subscan[i].last, &ini_style);
iniSetHexInt(&new, cfg->sub[i]->code, "cfg", subscan[i].cfg, &ini_style);
iniSetDateTime(&new, cfg->sub[i]->code, "updated", /* include_time: */TRUE, now, &ini_style);
modified = TRUE;
}
if(keys != NULL) {
iniRemoveSection(&ini, cfg->sub[i]->code);
iniFreeStringList(keys);
}
}
if(modified || strListCount(ini))
result = iniWriteFile(fp, new);
strListFree(&new);
iniFreeStringList(ini);
fclose(fp);
return result;
BOOL newmsgs(smb_t* smb, time_t t)
{
char index_fname[MAX_PATH + 1];
SAFEPRINTF(index_fname, "%s.sid", smb->file);
return fdate(index_fname) >= t;
}
/****************************************************************************/
/* Initialize new-msg-scan pointers (e.g. for new users) */
/* If 'days' is specified as 0, just set pointer to last message (faster) */
/****************************************************************************/
BOOL initmsgptrs(scfg_t* cfg, subscan_t* subscan, unsigned days, void (*progress)(void*, int, int), void* cbdata)
{
uint i;
smb_t smb;
idxrec_t idx;
time_t t = time(NULL) - (days * 24 * 60 * 60);
for(i=0;i<cfg->total_subs;i++) {
if(progress != NULL)
progress(cbdata, i, cfg->total_subs);
/* This value will be "fixed" (changed to the last msg) when saving */
subscan[i].ptr = ~0;
if(days == 0)
continue;
ZERO_VAR(smb);
SAFEPRINTF2(smb.file,"%s%s",cfg->sub[i]->data_dir,cfg->sub[i]->code);
if(!newmsgs(&smb, t))
continue;
smb.retry_time=cfg->smb_retry_time;
smb.subnum=i;
if(smb_open_index(&smb) != SMB_SUCCESS)
memset(&idx, 0, sizeof(idx));
smb_getlastidx(&smb, &idx);
subscan[i].ptr = idx.number;
if(idx.time >= t && smb_getmsgidx_by_time(&smb, &idx, t) >= SMB_SUCCESS)
subscan[i].ptr = idx.number - 1;
smb_close(&smb);
}
if(progress != NULL)
progress(cbdata, i, cfg->total_subs);