Skip to content
Snippets Groups Projects
userdat.c 122 KiB
Newer Older
					result=not;
				else
					result=!not;
				break;
			case AR_AGE:
				else {
					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==NULL || !(user->misc&ANSI))
					result=not;
				else result=!not;
				break;
rswindell's avatar
rswindell committed
			case AR_PETSCII:
				if(user==NULL || (user->misc&CHARSET_FLAGS) != CHARSET_PETSCII)
					result=not;
				else result=!not;
				break;
			case AR_ASCII:
				if(user==NULL || (user->misc&CHARSET_FLAGS) != CHARSET_ASCII)
					result=not;
				else result=!not;
				break;
			case AR_UTF8:
				if(user==NULL || (user->misc&CHARSET_FLAGS) != CHARSET_UTF8)
					result=not;
				else result=!not;
				break;
			case AR_CP437:
				if(user==NULL || (user->misc&CHARSET_FLAGS) != CHARSET_CP437)
rswindell's avatar
rswindell committed
					result=not;
				else result=!not;
				break;
					result=not;
				else result=!not;
				break;
			case AR_WIP:
				break;
			case AR_OS2:
				#ifndef __OS2__
					result=not;
				#else
					result=!not;
				#endif
				break;
			case AR_DOS:
				#if defined(_WIN32) || defined(__linux__) || defined(__FreeBSD__)
			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_ACTIVE:
				if(user==NULL || user->misc&(DELETED|INACTIVE))
					result=not;
				else result=!not;
				break;
			case AR_INACTIVE:
				if(user==NULL || !(user->misc&INACTIVE))
					result=not;
				else result=!not;
				break;
			case AR_DELETED:
				if(user==NULL || !(user->misc&DELETED))
					result=not;
				else result=!not;
				break;
			case AR_EXPERT:
				if(user==NULL || !(user->misc&EXPERT))
					result=not;
				else result=!not;
				break;
			case AR_SYSOP:
				if(!is_user_sysop(user))
					result=not;
				else result=!not;
				break;
			case AR_GUEST:
				if(user==NULL || !(user->rest&FLAG('G')))
					result=not;
				else result=!not;
				break;
			case AR_QNODE:
				if(user==NULL || !(user->rest&FLAG('Q')))
					result=not;
				else result=!not;
				break;
			case AR_QUIET:
				result=not;
				break;
			case AR_LOCAL:
				result=not;
				break;
			case AR_DAY:
				now=time(NULL);
				if((equal && tm.tm_wday!=(int)n)
					result=not;
				else
					result=!not;
				break;
			case AR_CREDIT:
					|| (equal && user_available_credits(user)!=l)
					|| (!equal && user_available_credits(user)<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:
					|| (equal && user->number!=i)
					result=not;
				else
					result=!not;
				(*ptrptr)++;
				break;
			case AR_GROUP:
				if(user==NULL)
					result=not;
				else {
					l=getgrpnum(cfg,user->cursub);
					if((equal && l!=i) || (!equal && l<i))
						result=not;
					else
						result=!not;
				}
				(*ptrptr)++;
				break;
			case AR_SUB:
				if(user==NULL)
					result=not;
				else {
					l=getsubnum(cfg,user->cursub);
					if((equal && l!=i) || (!equal && l<i))
						result=not;
					else
						result=!not;
				}
				(*ptrptr)++;
				break;
			case AR_SUBCODE:
				if(user!=NULL && findstr_in_string(user->cursub,(char *)*ptrptr)==0)
				while(*(*ptrptr))
					(*ptrptr)++;
				break;
			case AR_LIB:
				if(user==NULL)
					result=not;
				else {
					l=getlibnum(cfg,user->curdir);
					if((equal && l!=i) || (!equal && l<i))
						result=not;
					else
						result=!not;
				}
				(*ptrptr)++;
				break;
			case AR_DIR:
				if(user==NULL)
					result=not;
				else {
					l=getdirnum(cfg,user->curdir);
					if((equal && l!=i) || (!equal && l<i))
						result=not;
					else
						result=!not;
				}
				(*ptrptr)++;
				break;
			case AR_DIRCODE:
				if(user!=NULL && findstr_in_string(user->curdir,(char *)*ptrptr)==0)
				while(*(*ptrptr))
					(*ptrptr)++;
				break;
			case AR_EXPIRE:
				now=time(NULL);
					|| user->expire==0
					|| now+((long)i*24L*60L*60L)>user->expire)
					result=not;
				else
					result=!not;
				(*ptrptr)++;
				break;
			case AR_RANDOM:
				if((equal && n!=i) || (!equal && n<i))
					result=not;
				else
					result=!not;
				(*ptrptr)++;
				break;
			case AR_LASTON:
				now=time(NULL);
				if(user==NULL || (now-user->laston)/(24L*60L*60L)<(long)i)
					result=not;
				else
					result=!not;
				(*ptrptr)++;
				break;
			case AR_LOGONS:
				if(user==NULL
					|| (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);
				localtime_r(&now,&tm);
				if((tm.tm_hour*60)+tm.tm_min<(int)i)
					result=not;
				else
					result=!not;
				(*ptrptr)++;
				break;
			case AR_PCR:
				if(user==NULL)
					result=not;
				else if(user->logons>user->posts
					&& (!user->posts || 100/((float)user->logons/user->posts)<(long)n))
					result=not;
				else
					result=!not;
				break;
			case AR_UDR:	/* up/download byte ratio */
				else {
					l=user->dlb;
					if(!l) l=1;
					if(user->dlb>user->ulb
						&& (!user->ulb || 100/((float)l/user->ulb)<n))
				break;
			case AR_UDFR:	/* up/download file ratio */
				else {
					i=user->dls;
					if(!i) i=1;
					if(user->dls>user->uls
						&& (!user->uls || 100/((float)i/user->uls)<n))
rswindell's avatar
rswindell committed
			case AR_ULS:
				if(user==NULL || (equal && user->uls!=i) || (!equal && user->uls<i))
rswindell's avatar
rswindell committed
					result=not;
				else
					result=!not;
				(*ptrptr)++;
				break;
				if(user==NULL || (equal && user->ulb/1024!=i) || (!equal && user->ulb/1024<i))
					result=not;
				else
					result=!not;
				(*ptrptr)++;
				break;
			case AR_ULM:
				if(user==NULL || (equal && user->ulb/(1024*1024)!=i) || (!equal && user->ulb/(1024*1024)<i))
rswindell's avatar
rswindell committed
					result=not;
				else
					result=!not;
				(*ptrptr)++;
				break;
			case AR_DLS:
				if(user==NULL || (equal && user->dls!=i) || (!equal && user->dls<i))
rswindell's avatar
rswindell committed
					result=not;
				else
					result=!not;
				(*ptrptr)++;
				break;
				if(user==NULL || (equal && user->dlb/1024!=i) || (!equal && user->dlb/1024<i))
					result=not;
				else
					result=!not;
				(*ptrptr)++;
				break;
			case AR_DLM:
				if(user==NULL || (equal && user->dlb/(1024*1024)!=i) || (!equal && user->dlb/(1024*1024)<i))
rswindell's avatar
rswindell committed
					result=not;
				else
					result=!not;
				(*ptrptr)++;
				break;
			case AR_FLAG1:
				if(user==NULL
					|| (!equal && !(user->flags1&FLAG(n)))
					|| (equal && user->flags1!=FLAG(n)))
					result=not;
				else
					result=!not;
				break;
			case AR_FLAG2:
				if(user==NULL
					|| (!equal && !(user->flags2&FLAG(n)))
					|| (equal && user->flags2!=FLAG(n)))
					result=not;
				else
					result=!not;
				break;
			case AR_FLAG3:
				if(user==NULL
					|| (!equal && !(user->flags3&FLAG(n)))
					|| (equal && user->flags3!=FLAG(n)))
					result=not;
				else
					result=!not;
				break;
			case AR_FLAG4:
				if(user==NULL
					|| (!equal && !(user->flags4&FLAG(n)))
					|| (equal && user->flags4!=FLAG(n)))
					result=not;
				else
					result=!not;
				break;
			case AR_REST:
				if(user==NULL
					|| (!equal && !(user->rest&FLAG(n)))
					|| (equal && user->rest!=FLAG(n)))
					result=not;
				else
					result=!not;
				break;
			case AR_EXEMPT:
				if(user==NULL
					|| (!equal && !(user->exempt&FLAG(n)))
					|| (equal && user->exempt!=FLAG(n)))
					result=not;
				else
					result=!not;
				break;
			case AR_SEX:
					result=not;
				else
					result=!not;
rswindell's avatar
rswindell committed
					|| !findstr_in_string(cfg->shell[user->shell]->code,(char*)*ptrptr))
				while(*(*ptrptr))
					(*ptrptr)++;
				break;
			case AR_PROT:
rswindell's avatar
rswindell committed
				if(client!=NULL)
					p=client->protocol;
				else if(user!=NULL)
					p=user->modem;
				else
					p=NULL;
				if(!findstr_in_string(p,(char*)*ptrptr))
					result=not;
				else
					result=!not;
				while(*(*ptrptr))
					(*ptrptr)++;
				break;
			case AR_HOST:
				if(client!=NULL)
					p=client->host;
				else if(user!=NULL)
					p=user->comp;
				else
					p=NULL;
				if(!findstr_in_string(p,(char*)*ptrptr))
					result=not;
				else
					result=!not;
				while(*(*ptrptr))
					(*ptrptr)++;
				break;
			case AR_IP:
				if(client!=NULL)
					p=client->addr;
				else if(user!=NULL)
deuce's avatar
deuce committed
					p=user->ipaddr;
rswindell's avatar
rswindell committed
				else
					p=NULL;
				if(!findstr_in_string(p,(char*)*ptrptr))
rswindell's avatar
rswindell committed
			case AR_TERM:
				result=!not;
				while(*(*ptrptr))
					(*ptrptr)++;
				break;
			case AR_ROWS:
			case AR_COLS:
				result=!not;
				break;
bool chk_ar(scfg_t* cfg, uchar *ar, user_t* user, client_t* client)
rswindell's avatar
rswindell committed
	return(ar_exp(cfg,&p,user,client));
/****************************************************************************/
/* Does the Access Requirement String (ARS) parse and check					*/
/****************************************************************************/
bool chk_ars(scfg_t* cfg, char *ars, user_t* user, client_t* client)
{
	uchar ar_buf[LEN_ARSTR + 1];

	return chk_ar(cfg, arstr(NULL, ars, cfg, ar_buf), user, client);
}

/****************************************************************************/
/* Returns 0 on success, non-zero on failure.								*/
/****************************************************************************/
char* getuserstr(scfg_t* cfg, int usernumber, enum user_field fnum, char *str, size_t size)
	char*	field[USER_FIELD_COUNT];
	char	userdat[USER_RECORD_LEN + 1];
	int		file;
	if(!VALID_CFG(cfg) || !VALID_USER_NUMBER(usernumber) || !VALID_USER_FIELD(fnum) || str == NULL)
		return NULL;
	memset(str, 0, size);
	if((file = openuserdat(cfg, /* for_modify: */false)) == -1)
		return str;
	if(readuserdat(cfg, usernumber, userdat, sizeof(userdat), file, /* leave_locked: */false) == 0) {
		split_userdat(userdat, field);
		safe_snprintf(str, size, "%s", field[fnum]);
	dirtyuserdat(cfg, usernumber);
	return str;
uint32_t getuserhex32(scfg_t* cfg, int usernumber, enum user_field fnum)
	if(getuserstr(cfg, usernumber, fnum, str, sizeof(str)) == NULL)
		return 0;
uint32_t getuserdec32(scfg_t* cfg, int usernumber, enum user_field fnum)
{
	char str[32];
	if(getuserstr(cfg, usernumber, fnum, str, sizeof(str)) == NULL)
		return 0;
	return strtoul(str, NULL, 10);
}
uint64_t getuserdec64(scfg_t* cfg, int usernumber, enum user_field fnum)
{
	char str[32];
	if(getuserstr(cfg, usernumber, fnum, str, sizeof(str)) == NULL)
		return 0;
	return strtoull(str, NULL, 10);
uint32_t getusermisc(scfg_t* cfg, int usernumber)
	return getuserhex32(cfg, usernumber, USER_MISC);
}
uint32_t getuserchat(scfg_t* cfg, int usernumber)
{
	return getuserhex32(cfg, usernumber, USER_CHAT);
}
uint32_t getusermail(scfg_t* cfg, int usernumber)
{
	return getuserhex32(cfg, usernumber, USER_MAIL);
}

uint32_t getuserqwk(scfg_t* cfg, int usernumber)
{
	return getuserhex32(cfg, usernumber, USER_QWK);
}
uint32_t getuserflags(scfg_t* cfg, int usernumber, enum user_field fnum)
{
	char str[LEN_FLAGSTR + 1];
	if(getuserstr(cfg, usernumber, fnum, str, sizeof(str)) == NULL)
		return 0;

	return aftou32(str);
}

/****************************************************************************/
/* Returns 0 on success, non-zero on failure.								*/
/****************************************************************************/
int putuserstr(scfg_t* cfg, int usernumber, enum user_field fnum, const char *str)
	char	userdat[USER_RECORD_LEN + 1];
	int		file;
	int		retval;

	if(!VALID_CFG(cfg) || !VALID_USER_NUMBER(usernumber) || !VALID_USER_FIELD(fnum) || str == NULL)
		return -1;

	if(strchr(str, USER_FIELD_SEPARATOR) != NULL)
		return -2;

	if((file = openuserdat(cfg, /* for_modify: */true)) == -1)
		return errno;

	retval = readuserdat(cfg, usernumber, userdat, sizeof(userdat), file, /* leave_locked: */true);
	if(retval == 0) {
		split_userdat(userdat, field);
		field[fnum] = (char*)str;
		if(!seekuserdat(file, usernumber))
			retval = -4;
		unlockuserdat(file, usernumber);
	if(retval == 0)
		dirtyuserdat(cfg, usernumber);
int putuserdatetime(scfg_t* cfg, int usernumber, enum user_field fnum, time32_t t)
	return putuserstr(cfg, usernumber, fnum, format_datetime(t, str, sizeof(str)));
}
int putuserflags(scfg_t* cfg, int usernumber, enum user_field fnum, uint32_t flags)
{
	char str[128];
	return putuserstr(cfg, usernumber, fnum, u32toaf(flags, str));
}
int putuserhex32(scfg_t* cfg, int usernumber, enum user_field fnum, uint32_t value)
{
	char str[128];
	return putuserstr(cfg, usernumber, fnum, ultoa(value, str, 16));
}
int putuserdec32(scfg_t* cfg, int usernumber, enum user_field fnum, uint32_t value)
{
	char str[128];
	return putuserstr(cfg, usernumber, fnum, ultoa(value, str, 10));
}
int putuserdec64(scfg_t* cfg, int usernumber, enum user_field fnum, uint64_t value)
{
	char str[128];

	SAFEPRINTF(str, "%" PRIu64, value);

	return putuserstr(cfg, usernumber, fnum, str);
}

int putusermisc(scfg_t* cfg, int usernumber, uint32_t value)
{
	return putuserhex32(cfg, usernumber, USER_MISC, value);
}

int putuserchat(scfg_t* cfg, int usernumber, uint32_t value)
{
	return putuserhex32(cfg, usernumber, USER_CHAT, value);
}

int putusermail(scfg_t* cfg, int usernumber, uint32_t value)
{
	return putuserhex32(cfg, usernumber, USER_MAIL, value);
}

int putuserqwk(scfg_t* cfg, int usernumber, uint32_t value)
{
	return putuserhex32(cfg, usernumber, USER_QWK, value);
/****************************************************************************/
/* Updates user 'usernumber's numeric field value by adding 'adj' to it		*/
/* returns the new value.													*/
/****************************************************************************/
uint64_t adjustuserval(scfg_t* cfg, int usernumber, enum user_field fnum, int64_t adj)
	char userdat[USER_RECORD_LEN + 1];
	bool valid = true;
	uint64_t val = 0;

	if(!VALID_CFG(cfg) || !VALID_USER_NUMBER(usernumber) || !VALID_USER_FIELD(fnum))
		return 0;

	if((file = openuserdat(cfg, /* for_modify: */true)) == -1)
	if(readuserdat(cfg, usernumber, userdat, sizeof(userdat), file, /* leave_locked: */true) == 0) {
		char* field[USER_FIELD_COUNT];
		split_userdat(userdat, field);
		val = strtoull(field[fnum], NULL, 10);
		switch(user_field_len(fnum)) {
			case sizeof(uint64_t):
				if(adj < 0 && val < (uint64_t)-adj)		// don't go negative
					val = 0;
				else if(adj > 0 && val + adj < val)
					val = UINT64_MAX;
				else
					val += (uint64_t)adj;
				SAFEPRINTF(value, "%" PRIu64, val);
				break;
			case sizeof(uint32_t):
				if(adj < 0 && val < (uint32_t)-adj)		// don't go negative
					val = 0;
				else if(adj > 0 && val + adj < val)
					val = UINT32_MAX;
				else
					val += (uint32_t)adj;
Rob Swindell's avatar
Rob Swindell committed
				SAFEPRINTF(value, "%" PRIu32, (uint)val);
				break;
			case sizeof(uint16_t):
				if(adj < 0 && val < (uint16_t)-adj)		// don't go negative
					val = 0;
				else if(adj > 0 && val + adj < val)
					val = UINT16_MAX;
				else
					val += (uint16_t)adj;
				SAFEPRINTF(value, "%u", (uint)val);
				break;
			case sizeof(uint8_t):
				if(adj < 0 && val < (uint8_t)-adj)		// don't go negative
					val = 0;
				else if(adj > 0 && val + adj < val)
					val = UINT8_MAX;
				else
					val += (uint8_t)adj;
				SAFEPRINTF(value, "%u", (uint)val);
				break;
			default:
				valid = false;
				break;
		}
		if(valid) {
			field[fnum] = value;
			if(seekuserdat(file, usernumber))
				writeuserfields(cfg, field, file);
		}
	}
	unlockuserdat(file, usernumber);
	close(file);
	dirtyuserdat(cfg, usernumber);
/****************************************************************************/
/* Returns user's available credits, handling integer overflow				*/
/****************************************************************************/
uint64_t user_available_credits(user_t* user)
{
	uint64_t result;
	if(user == NULL)
		return 0;
	result = user->cdt + user->freecdt;
	if(result < user->cdt)
		return UINT64_MAX;
	return result;
}

/****************************************************************************/
/* Subtract credits from the current user online, accounting for the new    */
/* "free credits" field.                                                    */
/****************************************************************************/
void subtract_cdt(scfg_t* cfg, user_t* user, uint64_t amt)
	if(!amt || user==NULL)
		return;
	if(user->freecdt) {
		if(amt > user->freecdt) {      /* subtract both credits and */
			mod=amt-user->freecdt;   /* free credits */
			putuserstr(cfg, user->number, USER_FREECDT, "0");
			user->freecdt=0;
			user->cdt = adjustuserval(cfg, user->number, USER_FREECDT, -mod);
		} else {                          /* subtract just free credits */
			user->freecdt-=amt;
			putuserstr(cfg, user->number, USER_FREECDT, _ui64toa(user->freecdt, tmp, 10));
	else {  /* no free credits */
		if(amt > INT64_MAX)
			amt = INT64_MAX;
		user->cdt = adjustuserval(cfg, user->number, USER_CDT, -(int64_t)amt);
bool user_posted_msg(scfg_t* cfg, user_t* user, int count)
	user->posts	=(ushort)adjustuserval(cfg, user->number, USER_POSTS, count);
	user->ptoday=(ushort)adjustuserval(cfg, user->number, USER_PTODAY, count);
Rob Swindell's avatar
Rob Swindell committed
	return inc_post_stats(cfg, count);
bool user_sent_email(scfg_t* cfg, user_t* user, int count, bool feedback)
		user->fbacks=(ushort)adjustuserval(cfg, user->number, USER_FBACKS, count);
		user->emails=(ushort)adjustuserval(cfg, user->number, USER_EMAILS, count);
	user->etoday=(ushort)adjustuserval(cfg, user->number, USER_ETODAY, count);
Rob Swindell's avatar
Rob Swindell committed
	return inc_email_stats(cfg, count, feedback);
bool user_downloaded(scfg_t* cfg, user_t* user, int files, off_t bytes)
	user->dls=(ushort)adjustuserval(cfg, user->number, USER_DLS, files);
	user->dlb=adjustuserval(cfg, user->number, USER_DLB, bytes);
bool user_downloaded_file(scfg_t* cfg, user_t* user, client_t* client,
	int dirnum, const char* filename, off_t bytes)
	bool removed = false;
	if(!loadfile(cfg, dirnum, filename, &f, file_detail_normal))
		bytes = getfilesize(cfg, &f);
	if(dirnum == cfg->user_dir) {

		str_list_t dest_user_list = strListSplitCopy(NULL, f.to_list, ",");
		char usernum[16];
		SAFEPRINTF(usernum, "%u", user->number);
		int i = strListFind(dest_user_list, usernum, /* case-sensitive: */true);
		if(i >= 0) {
			strListFastDelete(dest_user_list, i, /* count: */1);
			char tmp[512];
			smb_hfield_replace_str(&f, RECIPIENTLIST, strListCombine(dest_user_list, tmp, sizeof(tmp), ","));
		}
		if(strListCount(dest_user_list) < 1) {
			char path[MAX_PATH + 1];
			if(remove(getfilepath(cfg, &f, path)) == 0)
				removed = removefile(cfg, dirnum, f.name);
		}
		strListFree(&dest_user_list);
	}

	f.hdr.times_downloaded++;
	f.hdr.last_downloaded = time32(NULL);
	if(!removed && !updatefile(cfg, &f)) {

	/**************************/
	/* Update Uploader's Info */
	/**************************/
	user_t uploader = {0};
	uploader.number=matchuser(cfg, f.from, true /*sysop_alias*/);
	if(uploader.number
		&& uploader.number != user->number 
		&& getuserdat(cfg, &uploader) == 0
		&& (uint32_t)uploader.firston < f.hdr.when_imported.time) {
		if(!(cfg->dir[dirnum]->misc&DIR_CDTDL))	/* Don't give credits on d/l */
		uint64_t mod=(uint64_t)(l*(cfg->dir[dirnum]->dn_pct/100.0));
		adjustuserval(cfg, uploader.number, USER_CDT, mod);
		if(cfg->text != NULL && !(cfg->dir[dirnum]->misc&DIR_QUIET)) {
			const char* alias = user->alias[0] ? user->alias : cfg->text[UNKNOWN_USER];
			if(client != NULL && is_user_sysop(&uploader)) {
				if(client->host[0] != '\0' && strcmp(client->host, STR_NO_HOSTNAME) != 0)
					SAFEPRINTF2(username,"%s [%s]", alias, client->host);
					SAFEPRINTF2(username,"%s [%s]", alias, client->addr);
				SAFECOPY(username, alias);
			if(strcmp(cfg->dir[dirnum]->code, "TEMP") == 0 || bytes < getfilesize(cfg, &f))
				SAFECOPY(prefix, cfg->text[Partially]);
			if(client != NULL) {
				SAFECAT(prefix, client->protocol);
				SAFECAT(prefix, "-");
			}
			/* Inform uploader of downloaded file */
			if(mod == 0)
				SAFEPRINTF3(str, cfg->text[FreeDownloadUserMsg]
					,getfname(filename)
					,prefix
					,username);
			else
				SAFEPRINTF4(str, cfg->text[DownloadUserMsg]
					,getfname(filename)
					,prefix
					,username, tmp);
			putsmsg(cfg, uploader.number, str);
		}
	}
	/****************************/
	/* Update Downloader's Info */
	/****************************/
	user_downloaded(cfg, user, /* files: */1, bytes);
	if(!is_download_free(cfg, dirnum, user, client))
		subtract_cdt(cfg, user, f.cost);
	if(!(cfg->dir[dirnum]->misc&DIR_NOSTAT))
		result = inc_download_stats(cfg, /* files: */1, bytes);
bool user_uploaded(scfg_t* cfg, user_t* user, int files, off_t bytes)
	user->uls=(ushort)adjustuserval(cfg, user->number, USER_ULS, files);
	user->ulb=adjustuserval(cfg, user->number, USER_ULB, bytes);
bool user_adjust_credits(scfg_t* cfg, user_t* user, int64_t amount)

	if(amount<0)	/* subtract */
		subtract_cdt(cfg, user, -amount);
	else			/* add */
		user->cdt=adjustuserval(cfg, user->number, USER_CDT, amount);
bool user_adjust_minutes(scfg_t* cfg, user_t* user, long amount)
	user->min=(uint32_t)adjustuserval(cfg, user->number, USER_MIN, amount);
/****************************************************************************/
/****************************************************************************/
bool logoutuserdat(scfg_t* cfg, user_t* user, time_t now, time_t logontime)
	time_t tused;
	if(user==NULL)
	tused=(now-logontime)/60;
	user->tlast=(ushort)(tused > USHRT_MAX ? USHRT_MAX : tused);
	putuserdatetime(cfg,user->number, USER_LASTON, (time32_t)now);
	putuserstr(cfg,user->number, USER_TLAST, ultoa(user->tlast,str,10));
	adjustuserval(cfg,user->number, USER_TIMEON, user->tlast);
	adjustuserval(cfg,user->number, USER_TTODAY, user->tlast);