From 721c71677f8736dfcf9ee7b566c0dd0a07964f85 Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Sun, 25 Mar 2018 03:47:00 +0000
Subject: [PATCH] Changed user' message pointer/last-read/scan-config storage
 to a new file (data/user/*.subs - in familar .ini syntax) indexed by the
 sub-board internal rather than the old flat binary file using the troublesome
 "pointer index" (ptridx) as an offset. The old files (data/user/ptrs/*.ixb)
 will still be read in when the new file doesn't exist, for upgrading users
 automatically to the new method of pointer storage. This should resolve
 long-standing issues around users' new-scan pointers and configurations
 getting corrupted when the sysop inserts/remove sub-boards. However, if you
 change a sub-board's internal code, that invalidates the users' pointers and
 scan configurations - so that may need to be addrsesed via SCFG. The subs'
 ptridx values are still be used for storing QWKnet hub pointers, for now, but
 that too will soon be addressed with a new file format for those ptrs too.

---
 src/sbbs3/userdat.c | 87 +++++++++++++++++++++++----------------------
 1 file changed, 45 insertions(+), 42 deletions(-)

diff --git a/src/sbbs3/userdat.c b/src/sbbs3/userdat.c
index 70ccf45d96..383a79aced 100644
--- a/src/sbbs3/userdat.c
+++ b/src/sbbs3/userdat.c
@@ -3045,7 +3045,30 @@ BOOL DLLCALL getmsgptrs(scfg_t* cfg, user_t* user, subscan_t* subscan, void (*pr
 
 	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);
+			subscan[i].ptr	= iniGetLongInt(ini, cfg->sub[i]->code, "ptr"	, subscan[i].ptr);
+			subscan[i].last	= iniGetLongInt(ini, cfg->sub[i]->code, "last"	, subscan[i].last);
+			subscan[i].cfg	= iniGetShortInt(ini, cfg->sub[i]->code, "cfg"	, subscan[i].cfg);
+			subscan[i].sav_ptr	= subscan[i].ptr;
+			subscan[i].sav_last	= subscan[i].last;
+			subscan[i].sav_cfg	= subscan[i].cfg; 
+		}
+		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))
@@ -3074,61 +3097,41 @@ BOOL DLLCALL getmsgptrs(scfg_t* cfg, user_t* user, subscan_t* subscan, void (*pr
 }
 
 /****************************************************************************/
-/* Writes to data/user/ptrs/####.ixb the msgptr array for the current user	*/
+/* Writes to data/user/*.subs the msgptr array for the current user			*/
 /* Pass usernumber value of 0 to indicate "Guest" login						*/
 /****************************************************************************/
 BOOL DLLCALL putmsgptrs(scfg_t* cfg, user_t* user, subscan_t* subscan)
 {
 	char		path[MAX_PATH+1];
-	ushort		idx;
-	uint16_t	scancfg;
-	uint		i,j;
-	int 		file;
-	ulong		length;
-	uint32_t	l=0L;
+	uint		i;
+	time_t		now = time(NULL);
 
 	if(user->number==0 || (user->rest&FLAG('G')))	/* Guest */
 		return(TRUE);
-	SAFEPRINTF2(path,"%suser/ptrs/%4.4u.ixb", cfg->data_dir, user->number);
-	if((file=nopen(path,O_WRONLY|O_CREAT))==-1) {
-		return(FALSE); 
-	}
-	fixmsgptrs(cfg, subscan);
-	length=(ulong)filelength(file);
-	for(i=0;i<cfg->total_subs;i++) {
+
+	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 ini = iniReadFile(fp);
+	ini_style_t ini_style = { .key_prefix = "\t", .section_separator = "\n" };
+	for(i=0; i < cfg->total_subs; i++) {
+		BOOL exists = iniSectionExists(ini, cfg->sub[i]->code);
 		if(subscan[i].sav_ptr==subscan[i].ptr 
 			&& subscan[i].sav_last==subscan[i].last
-			&& length>=((cfg->sub[i]->ptridx+1)*10UL)
-			&& subscan[i].sav_cfg==subscan[i].cfg)
+			&& subscan[i].sav_cfg==subscan[i].cfg
+			&& exists)
 			continue;
-		while(filelength(file)<(long)(cfg->sub[i]->ptridx)*10) {
-			lseek(file,0L,SEEK_END);
-			idx=(ushort)(tell(file)/10);
-			for(j=0;j<cfg->total_subs;j++)
-				if(cfg->sub[j]->ptridx==idx)
-					break;
-			write(file,&l,sizeof(l));
-			write(file,&l,sizeof(l));
-			scancfg=0xff;					
-			if(j<cfg->total_subs) {
-				if(!(cfg->sub[j]->misc&SUB_NSDEF))
-					scancfg&=~SUB_CFG_NSCAN;
-				if(!(cfg->sub[j]->misc&SUB_SSDEF))
-					scancfg&=~SUB_CFG_SSCAN; 
-			} else	/* default to scan OFF for unknown sub */
-				scancfg&=~(SUB_CFG_NSCAN|SUB_CFG_SSCAN);
-			write(file,&scancfg,sizeof(scancfg)); 
-		}
-		lseek(file,(long)((long)(cfg->sub[i]->ptridx)*10),SEEK_SET);
-		write(file,&(subscan[i].ptr),sizeof(subscan[i].ptr));
-		write(file,&(subscan[i].last),sizeof(subscan[i].last));
-		write(file,&(subscan[i].cfg),sizeof(subscan[i].cfg));
+		iniSetLongInt(&ini, cfg->sub[i]->code, "ptr", subscan[i].ptr, &ini_style);
+		iniSetLongInt(&ini, cfg->sub[i]->code, "last", subscan[i].last, &ini_style);
+		iniSetHexInt(&ini, cfg->sub[i]->code, "cfg", subscan[i].cfg, &ini_style);
+		iniSetDateTime(&ini, cfg->sub[i]->code, "updated", /* include_time: */TRUE, now, &ini_style);
 	}
-	close(file);
-	if(!flength(path))			/* Don't leave 0 byte files */
-		remove(path);
+	iniWriteFile(fp, ini);
+	iniFreeStringList(ini);
+	fclose(fp);
 
-	return(TRUE);
+	return TRUE;
 }
 
 /****************************************************************************/
-- 
GitLab