// ==++==
// 
//   
//    Copyright (c) 2002 Microsoft Corporation.  All rights reserved.
//   
//    The use and distribution terms for this software are contained in the file
//    named license.txt, which can be found in the root of this distribution.
//    By using this software in any fashion, you are agreeing to be bound by the
//    terms of this license.
//   
//    You must not remove this notice, or any other, from this software.
//   
// 
// ==--==
// ===========================================================================
// File: IniFile.CPP
// 
// A simple API for reading/writing .ini files
//
// It is not designed to be 100% replacement for Win32's Read[Private]ProfileString/
// Write[Private]ProfileString.
//
// It does not provide any smart caching nor locking. It is up to the caller
// to provide it. Also, the grammar for the .ini files probably differs 
// in corner cases.
// ===========================================================================
#include <windows.h>
#include <stdlib.h>
#include <TCHAR.H>
#include "inifile.h"
#include "wvglob.h"
#include "version.h"
#include "defaults.h"
#include "winvn.h"

//#define CONFIG_FILE_NAME   _T("\\rotor.ini")


BYTE* CBlobBuilder::Ensure(DWORD nNewData)
{
	if (m_fError)
		return NULL;

	// grow 2x for smaller sizes, 1.25x for bigger sizes
	DWORD n = m_nData;
	n = min(2 * n, n + n / 4 + 0x10000);

	// don't allocate tiny chunks
	n = max(n, 0x80);

	// compare with the hard limit
	nNewData = max(n, m_nSize + nNewData);

	BYTE * pNewData = (BYTE*)realloc(m_pData, nNewData);
	if (pNewData == NULL)
	{
		m_fError = true;
		return pNewData;
	}

	m_nData = nNewData;
	m_pData = pNewData;
	return pNewData;
}


	void CBlobBuilder::Init()
	{
		m_pData = 0;
		m_nSize = 0;
		m_nData = 0;
		m_fError = false;
	}

	void CBlobBuilder::Destroy()
	{
		free(m_pData);
	}

	void CBlobBuilder::Append(LPCVOID pData, ULONG nSize)
	{
		ULONG nNewSize = m_nSize + nSize;

		if (nNewSize > m_nData)
		{
			if (!Ensure(nNewSize))
				return;
		}

		BYTE *p = m_pData + m_nSize;
		m_nSize = nNewSize;
		memcpy(p, pData, nSize);
	}

	void CBlobBuilder::AppendChar(TCHAR ch)
	{
		Append(&ch, sizeof(TCHAR));
	}

	void CBlobBuilder::AppendString(const TCHAR* pString, DWORD dwLength)
	{
		Append((LPCVOID)pString, dwLength * sizeof(TCHAR));
	}

	void CBlobBuilder::AppendNewLine()
	{
		static const TCHAR newline[2] = { '\r', '\n' };
		Append(newline, sizeof(newline));
	}

	void CBlobBuilder::AppendNode(const ININODE* pNode)
	{
		Append((LPCVOID)&pNode->Value, pNode->dwSize);
	}




// The ini file implementation

// create a free floating ININODE with the given value
ININODE* IniFile::CreateNode(LPCVOID pValue, DWORD dwSize)
{
	ININODE* pNode = (ININODE*)malloc(FIELD_OFFSET(ININODE, Value) + dwSize);
	if (pNode == NULL)
	{
		SetLastError(ERROR_OUTOFMEMORY);
		return NULL;
	}
	m_fDirty = TRUE;

	pNode->pNext = NULL;
	pNode->pChild = NULL;
	pNode->dwSize = dwSize;

	memcpy(&pNode->Value, pValue, dwSize);

	return pNode;
}

// destroy ININODE and all its childs
void IniFile::DestroyNode(ININODE* pNode)
{
	while (pNode != NULL)
	{
		if (pNode->pChild != NULL)
			DestroyNode(pNode->pChild);

		ININODE* pTmp = pNode;
		pNode = pNode->pNext;
		free(pTmp);
	}
}

// does a ININODE hold the given value?
BOOL IniFile::Equal(const ININODE* pNode, LPCVOID pValue, DWORD dwSize)
{
	return (pNode->dwSize == dwSize) && 
		   (memcmp(&pNode->Value, pValue, dwSize) == 0);
}

 // find a ININODE with the given value, create one if it does not exist
 //  and fCreate is specified
ININODE* IniFile::GetNode(ININODE** ppNode, LPCVOID pValue, DWORD dwSize, Action action)
{
	ININODE* pNode = *ppNode;

	while (pNode != NULL)
	{
		if (Equal(pNode, pValue, dwSize))
			break;
		ppNode = &pNode->pNext;
		pNode = *ppNode;
	}

	switch (action)
	{
		case CREATE_NODE:
			if (pNode == NULL)
			{
				pNode = CreateNode(pValue, dwSize);
				*ppNode = pNode;
			}
		break;

		case DELETE_NODE:
			if (pNode != NULL)
			{
				*ppNode = pNode->pNext;
				pNode->pNext = NULL;
				DestroyNode(pNode);
			}
		break;

		default:
			//_ASSERTE(action == FIND_NODE);
			break;
	}

	return pNode;
}

 // find a ININODE with the given string value, create one if it does not exist
 //  and fCreate is specified
ININODE* IniFile::GetNode(ININODE** ppNode, LPCTSTR pszValue, Action action)
{
	return GetNode(ppNode, (LPCVOID)pszValue, sizeof(TCHAR) * _tcslen(pszValue), action);
}


IniFile::IniFile()
{
	m_pRoot = NULL;
	m_fDirty = FALSE;
}

IniFile::~IniFile()
{
	DestroyNode(m_pRoot);
}


BOOL IniFile::Load(LPCTSTR pszFileName)
{
	DWORD dwSize, dwRead;
	DWORD dwSizeHigh;
	BOOL fRet = FALSE;
	PVOID pReadBuffer = NULL;
	TCHAR* pBuffer = NULL;
	ININODE* pSection = NULL;
	ININODE* pKey;
	TCHAR* p;
	TCHAR* pEnd;

	// read the ini file
	HANDLE hFile = CreateFile(pszFileName, 
	 GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}

	dwSize = GetFileSize(hFile, &dwSizeHigh);
	if ((dwSizeHigh == INVALID_FILE_SIZE) && GetLastError() != NO_ERROR)
		goto Error;
	if (dwSizeHigh != 0)
	{
		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
		goto Error;
	}

	pReadBuffer = malloc(dwSize);
	if (pReadBuffer == NULL)
	{
		SetLastError(ERROR_OUTOFMEMORY);
		goto Error;
	}

	if (!ReadFile(hFile, pReadBuffer, dwSize, &dwRead, NULL))
		goto Error;
	if (dwRead != dwSize)
	{
		SetLastError(ERROR_READ_FAULT);
		goto Error;
	}
	
#ifdef _UNICODE

	pBuffer = (TCHAR*)malloc(dwSize * sizeof(TCHAR));
	if (pBuffer == NULL)
	{
		SetLastError(ERROR_OUTOFMEMORY);
		goto Error;
	}

	dwSize = MultiByteToWideChar(CP_UTF8, 0, 
			(LPCSTR)pReadBuffer, dwSize, pBuffer, dwSize);

#else
	pBuffer = (LPSTR) pReadBuffer;
#endif
	p = pBuffer;
	pEnd = pBuffer + dwSize;
	for (;;)
	{
		TCHAR *pValueStart;
		TCHAR *pValueEnd;

#define TEST(cond) ((p < pEnd) && (cond))
#define PARSE_UNTIL(ch) while (TEST((*p != ch) && !IsJunk(*p))) p++
#define PARSE_WHILE(pred) while (TEST(pred(*p))) p++

		 PARSE_WHILE(IsJunk);

		 // are we done?
		 if (p >= pEnd)
			 break;

		 if (TEST(*p == '['))
		 {
			 p++;
			 PARSE_WHILE(IsJunk);

			 // we've got the section name
			 pValueStart = p;
			 PARSE_UNTIL(']');
			 pValueEnd = p;

			 PARSE_WHILE(IsJunk);

			 // better have closing bracket now
			 if (!TEST(*p == ']'))
				 goto ParseError;
			 p++;

			 pSection = GetNode(&m_pRoot, 
				 (LPCVOID)pValueStart, (BYTE*)pValueEnd - (BYTE*)pValueStart, CREATE_NODE);
			 if (pSection == NULL)
				 goto Error;

			 continue;
		 }
		 else
		 if (TEST(*p == ';') || TEST(*p == '#') || TEST(*p == '!'))
		 {
			 p++;
			 // everything up to the end of the line is comment - ignored
			 PARSE_WHILE(!IsEndOfLine);
			 continue;
		 }
		 else
		 {                
			 // we've got the key value pair
			 pValueStart = p;
			 PARSE_UNTIL('=');
			 pValueEnd = p;

			 PARSE_WHILE(IsJunk);

			 // better have the equal sign now
			 if (!TEST(*p == '='))
				 goto ParseError;
			 p++;

			 // no section to throw this in
			 if (pSection == NULL)
				 goto ParseError;

			 pKey = GetNode(&(pSection->pChild), 
				 (LPCVOID)pValueStart, (BYTE*)pValueEnd - (BYTE*)pValueStart, CREATE_NODE);
			 if (pKey == NULL)
				 goto Error;

			 // the value is everything till end of the line
			 pValueStart = p;
			 PARSE_WHILE(!IsEndOfLine);
			 pValueEnd = p;

			 // duplicate ININODE
			 if (pKey->pChild != NULL)
				 goto ParseError;

			 pKey->pChild = CreateNode((LPCVOID)pValueStart, (BYTE*)pValueEnd - (BYTE*)pValueStart);
			 if (pKey->pChild == NULL)
				 goto Error;

			 continue;
		 }

	 ParseError:
		 //_ASSERTE(false);


		 SetLastError(ERROR_FILE_INVALID);
		 goto Error;
	 }

	 m_fDirty = FALSE;

	 fRet = TRUE;

Error:
	 if (hFile != INVALID_HANDLE_VALUE)
		 CloseHandle(hFile);
	 free(pReadBuffer);

#ifdef _UNICODE
	 free(pBuffer);
#endif

	 return fRet;
}

BOOL IniFile::Save(LPCTSTR pszFileName, BOOL fForce)
 {
	 HANDLE hFile = INVALID_HANDLE_VALUE;
	 CBlobBuilder builder;
	 BOOL fRet = FALSE;
	 PVOID pBuffer = NULL;
	 DWORD dwSize, dwWritten;

	 if (!fForce && !m_fDirty)
		 return TRUE;

	 builder.Init();

	 // for all sections
	 ININODE* pSection = m_pRoot;
	 while (pSection != NULL)
	 {

		 ININODE* pKey = pSection->pChild;

		 if (pKey)	//prune empty sections
		 {

			 // append the section name
			 builder.AppendChar('[');
			 builder.AppendNode(pSection);
			 builder.AppendChar(']');
			 builder.AppendNewLine();

			 // for all keys
			 while (pKey != NULL)
			 {
				 // append the key-value pair
				 builder.AppendNode(pKey);
				 builder.AppendChar('=');

				 ININODE* pValue = pKey->pChild;
				 if (pValue != NULL)
				 {
					 builder.AppendNode(pValue);
					 builder.AppendNewLine();
				 }
				 pKey = pKey->pNext;
			 }
			 
			 if (pSection->pNext != NULL)
				 builder.AppendNewLine();

		}

		pSection = pSection->pNext;

	 }

	 // did we run out of memory?
	 if (builder.IsError())
		 goto Error;

#ifdef _UNICODE
	 // convert to UTF-8 (one Unicode char can be up to 3 UTF-8 bytes)
	 dwSize = 3 * (builder.GetSize() / sizeof(TCHAR));
	 pBuffer = malloc(dwSize);
	 if (pBuffer == NULL)
		 goto Error;

	 dwSize = WideCharToMultiByte(CP_UTF8, 0,
		 (LPCTSTR)builder.GetBuffer(), builder.GetSize() / sizeof(TCHAR),
		 (LPSTR)pBuffer, dwSize, 0, 0);
#else
	pBuffer = builder.GetBuffer();
	dwSize = builder.GetSize();

#endif
	 // write it to the disk
	 hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
	 if (hFile == INVALID_HANDLE_VALUE)
		 goto Error;

	 if (!WriteFile(hFile, pBuffer, dwSize, &dwWritten, NULL))
		 goto Error;

	 if (dwWritten != dwSize)
	 {
		 SetLastError(ERROR_WRITE_FAULT);
		 goto Error;
	 }

	 m_fDirty = FALSE;

	 fRet = TRUE;

Error:
	if (hFile != INVALID_HANDLE_VALUE)
	 CloseHandle(hFile);
#ifdef _UNICODE
	free(pBuffer);
#endif
	builder.Destroy();

	return fRet;
}

DWORD IniFile::ReadString( LPCTSTR pszSection,LPCTSTR pszKey, LPTSTR pszValue, DWORD dwBufLength)
{
	 ININODE* pNode;

	 if (pszSection == NULL)
	 {
		 pNode = m_pRoot;
		 goto SetOfValues;
	 }

	 pNode = GetNode(&m_pRoot, pszSection, FIND_NODE);
	 if (pNode == NULL)
		 goto SetOfValues;

	 if (pszKey == NULL)
	 {
		 pNode = pNode->pChild;
		 goto SetOfValues;
	 }

	 pNode = GetNode(&pNode->pChild, pszKey, FIND_NODE);
	 if (pNode == NULL)
		 goto SetOfValues;

	 pNode = pNode->pChild;

SetOfValues:
	 DWORD dwTotalLength = 0;
	 BOOL fMultiple = (pszSection == NULL) || (pszKey == NULL) || (pNode == NULL);

	 // Save space for the terminating '\0'
	 if (fMultiple)
	 {
		 if (dwBufLength == 0)
			 return 0;
		 dwBufLength--;
	 }

	 while (pNode != NULL)
	 {
		 DWORD dwLength = pNode->dwSize / sizeof(TCHAR);

		 if (dwLength >= dwBufLength)
		 {
			 if (dwBufLength == 0)
				 break;

			 dwLength = dwBufLength - 1;
		 }

		 memcpy(pszValue, &pNode->Value, dwLength * sizeof(TCHAR));
		 *(pszValue + dwLength) = '\0';

		 pszValue += (dwLength + 1);
		 dwBufLength -= (dwLength + 1);

		 dwTotalLength += dwLength;

		 if (!fMultiple)
			 break;

		 // count the '\0' separator
		 dwTotalLength++;

		 pNode = pNode->pNext;
	 }

	 if (fMultiple)
	 {
		 // terminating '\0'
		 *pszValue = '\0';
	 }

	 return dwTotalLength;
}

BOOL IniFile::WriteString( LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszValue)
{
	ININODE* pSection = GetNode(&m_pRoot, pszSection, (pszKey != NULL) ? CREATE_NODE : DELETE_NODE);
	if (pszKey == NULL)
		return TRUE;
	if (pSection == NULL)
		return FALSE;

	ININODE* pKey = GetNode(&pSection->pChild, pszKey, (pszValue != NULL) ? CREATE_NODE : DELETE_NODE);
	if (pszValue == NULL)
		return TRUE;
	if (pKey == NULL)
		return FALSE;

	ININODE* pValue = pKey->pChild;
	DWORD dwSize = sizeof(TCHAR) * _tcsclen(pszValue);
	if (pValue)
	{
		if (Equal(pValue, (LPCVOID)pszValue, dwSize))
			return TRUE;
		DestroyNode(pValue);
	}

	pKey->pChild = CreateNode((LPCVOID)pszValue, dwSize);
	if (pKey->pChild == NULL)
		return FALSE;

	return TRUE;
}

BOOL IniFile::GetString( LPCTSTR pszSection, LPCTSTR pszKey, LPTSTR pszValue, DWORD dwBufLength)
{
	ININODE* pNode;

	pNode = GetNode(&m_pRoot, pszSection, FIND_NODE);
	if (pNode == NULL)
		goto NotFound;

	pNode = GetNode(&pNode->pChild, pszKey, FIND_NODE);
	if (pNode == NULL)
		goto NotFound;

	pNode = pNode->pChild;
	if (pNode == NULL)
	{
NotFound:
		SetLastError(NOERROR);
		return FALSE;
	}

	DWORD dwLength = pNode->dwSize / sizeof(TCHAR);

	if (dwLength >= dwBufLength)
	{
		SetLastError(ERROR_INSUFFICIENT_BUFFER);
		return FALSE;
	}

	memcpy(pszValue, &pNode->Value, dwLength * sizeof(TCHAR));
	*(pszValue + dwLength) = '\0';
	return TRUE;
}

BOOL IniFile::WriteInt( LPCTSTR pszSection, LPCTSTR pszKey, int nValue)
{
	TCHAR buf[20];
	_itot( nValue, buf, 10);
	return WriteString( pszSection, pszKey, buf); 

}

BOOL IniFile::WriteUInt( LPCTSTR pszSection, LPCTSTR pszKey, UINT uValue)
{

	TCHAR buf[20];
	_ultot( uValue, buf, 10);
	return WriteString( pszSection, pszKey, buf);

}



void WINAPI WriteWinvnProfile()
{

	char buf[MAXINTERNALLINE];
	register int j;

	IniFile profile;

	profile.Load( szAppProFile);

	profile.WriteString (ADMIN, "Version", WINVN_VERSION);

	profile.WriteString (PERSONAL, "UserName", UserName);
	profile.WriteString (PERSONAL, "MailAddress", MailAddress);
	profile.WriteString (PERSONAL, "Organization", Organization);
	profile.WriteString (PERSONAL, "ReplyTo", ReplyTo);

	if (!gURLinfo)
	{
		//settings not saved when using command line URL
		profile.WriteString (COMM, "NNTPHost", NNTPHost);
		profile.WriteString (COMM, "NNTPService", NNTPService);
		profile.WriteString (AUTH, "NNTPUserName", NNTPUserName);
		profile.WriteInt    (AUTH, "NNTPSavePassword", NNTPSavePassword);

		if (NNTPSavePassword)
			 profile.WriteString (AUTH, "NNTPPassword", NNTPPasswordEncrypted);
		else
			 profile.WriteString (AUTH, "NNTPPassword", DEF_NNTPPassword);
	}
	profile.WriteString (COMM, "SMTPHost", SMTPHost);
	profile.WriteString (COMM, "SMTPService", SMTPService);
	if (DEF_SMTPUseSSL != SMTPUseSSL)
		profile.WriteInt    (COMM, "SMTPSSL", SMTPUseSSL);
	else
		profile.WriteString (COMM, "SMTPSSL", NULL);
	if (DEF_SMTPIP_Family != SMTPIP_Family)
		profile.WriteInt    (COMM, "SMTP_IPFamily", SMTPIP_Family);
	else
		profile.WriteString (COMM, "SMTP_IPFamily", NULL);



	profile.WriteString (AUTH, "SMTPUserName", SMTPUserName);
	profile.WriteInt    (AUTH, "SMTPSavePassword", SMTPSavePassword);
	if (SMTPSavePassword)
		 profile.WriteString (AUTH, "SMTPPassword", SMTPPasswordEncrypted);
	else
		 profile.WriteString (AUTH, "SMTPPassword", NULL);

	profile.WriteInt (COMM, "ConnectAtStartup", ConnectAtStartup);
	profile.WriteInt (COMM, "ForceXhdr", force_xhdr);
	profile.WriteInt (COMM, "UseCompressedHdr", use_zhdr);
	profile.WriteInt (COMM, "IPFamily", ip_family);
	
	profile.WriteInt (COMM, "MailDemandLogon", MailDemandLogon);
	profile.WriteInt (COMM, "MailForceType", MailForceType);
	//profile.WriteString (COMM, "GenSockDLL", GenSockDLL);
	profile.WriteString (COMM, "CommTimeout", NULL);
	profile.WriteInt (COMM, "UseSSL", UseSSL);
	
	if (StallTimeout == DEF_StallTime)
		profile.WriteString (COMM, "StallTimeout", NULL);
	else
		profile.WriteInt (COMM, "StallTimeout", StallTimeout);

	if (CommTimeout == DEF_CommTimeout)
		profile.WriteString (COMM, "CommTimeout", NULL);
	else
		profile.WriteInt (COMM, "CommTimeout", CommTimeout);
	if (TimeZone[0] != 0)
		profile.WriteString (COMM, "TimeZone", TimeZone);
	profile.WriteString (COMM, "MAPIPrefix", MAPIPrefix);
	profile.WriteInt (LOGGING, "MailLog", MailLog);
	profile.WriteString (LOGGING, "MailLogFile", MailLogFile);
	profile.WriteInt (LOGGING, "PostLog", PostLog ? 1 : 0);
	profile.WriteString (LOGGING, "PostLogFile", PostLogFile);
	profile.WriteInt (LOGGING, "LogMailAttach", MailLogAttach ? 1 : 0);
	profile.WriteInt (LOGGING, "LogPostAttach", PostLogAttach ? 1 : 0);
	profile.WriteInt (CONFIRM, "ConfirmBatchOps", ConfirmBatchOps ? 1 : 0);
	profile.WriteInt (CONFIRM, "ConfirmDisconnect", ConfirmDisconnect ? 1 : 0);
	profile.WriteInt (CONFIRM, "ConfirmSaveOnExit", ConfirmSaveOnExit ? 1 : 0);
	profile.WriteInt (CONFIRM, "ConfirmExitWhenActive", ConfirmExitActive ? 1 : 0);

	profile.WriteInt (CONFIRM, "ConfirmCrossPosting", ConfirmXPosting ? 1 : 0);
	profile.WriteInt (CONFIRM, "ConfirmReplyTo", ConfirmReplyTo ? 1 : 0);

#ifdef OFFLINE  // TANAKA Goh
	profile.WriteInt (CONFIRM, "ConfirmDeleteArticle", ConfirmDeleteArticle);
#endif

	profile.WriteInt (GROUPLIST, "DoList", DoList);
	profile.WriteInt (GROUPLIST, "ShowUnsubscribed", ShowUnsubscribed);
	profile.WriteInt (GROUPLIST, "ShowUnreadCount", ShowUnreadCount);
	profile.WriteInt (GROUPLIST, "SortGroupList", SortGroupList);
	profile.WriteInt (GROUPLIST, "GroupListMultiSelect", GroupListMultiSelect);
	profile.WriteInt (GROUPLIST, "GroupListSeparator", GroupListSeparator);
	profile.WriteInt (GROUPLIST, "ToolbarSize", nToolbarMain);

	// these aren't changeable from within winvn yet
	//  profile.WriteString (GROUPLIST, "GroupFilter", GroupFilter);
	//  profile.WriteInt    (GROUPLIST, "EnableGroupFilter", EnableGroupFilter);
	if (UScrollMode > WHEELSCROLL_SYSTEM ) 
	{
		profile.WriteInt (GROUPLIST, "ScrollByWheel", UScrollByWheel[0]);
		for (j = 1 ; ((j < MAX_WHEEL_ENTRIES)  && (UScrollByWheel[j] != 0)) ; j++) 
		{
      			sprintf(buf, "ScrollByWheel%d", j);
      			profile.WriteInt (GROUPLIST, buf , UScrollByWheel[j]);
		}
	}

	profile.WriteInt (ARTLIST, "ArticleThreshold", article_threshold);
	profile.WriteInt (ARTLIST, "MinArticlesToRetrieve", min_to_retrieve);
	profile.WriteInt (ARTLIST, "FullNameFrom", FullNameFrom);
	profile.WriteInt (ARTLIST, "EnableThreading", threadp);
	
	//deprecated profile.WriteInt (ARTLIST, "ShowUnreadOnly", ShowUnreadOnly);
	//profile.WriteString (ARTLIST, "ShowUnreadOnly", NULL); //remove preference

	profile.WriteInt (ARTLIST, "NewWndGroup", ViewNew);
	profile.WriteInt (ARTLIST, "ThreadFullSubject", ThreadFullSubject);
	profile.WriteString (ARTLIST, "ThreadDepthIndicator", ThreadDepthIndicator);
	profile.WriteInt (ARTLIST, "SaveArtAppend", SaveArtAppend);
	profile.WriteInt (ARTLIST, "SaveNewsrcOnClose", SaveNewsrcOnClose);
	profile.WriteInt (ARTLIST, "ArtListMultiSelect", ArtListMultiSelect);
	profile.WriteInt (ARTLIST, "TrackSubscribedCrossposts", TrackSubscribedCrossposts);
	profile.WriteInt (ARTLIST, "TrackUnsubscribedCrossposts", TrackUnsubscribedCrossposts);
	profile.WriteInt (ARTLIST, "ArtListSeparator1", ArtListSeparator1);
	profile.WriteInt (ARTLIST, "ArtListSeparator2", ArtListSeparator2);
	profile.WriteInt (ARTLIST, "ArtListSeparator3", ArtListSeparator3);
	profile.WriteInt (ARTLIST, "EnableArticleAction", bEnableArticleAction);
	profile.WriteInt (ARTLIST, "SortWithAlphaOnly", SortWithAlphaOnly);
	profile.WriteInt (ARTLIST, "ToolbarSize", nToolbarArt); 

	if (GScrollMode > WHEELSCROLL_SYSTEM) 
	{
		profile.WriteInt (ARTLIST, "ScrollByWheel", GScrollByWheel[0]);
		for (j = 1 ; ((j < MAX_WHEEL_ENTRIES)  && (GScrollByWheel[j] != 0)) ; j++) 
		{
      			sprintf(buf, "ScrollByWheel%d", j);
      			profile.WriteInt (ARTLIST, buf , GScrollByWheel[j]);
		}
	}


	if (AScrollMode > WHEELSCROLL_SYSTEM) 
	{
		profile.WriteInt (ARTICLE, "ScrollByWheel", AScrollByWheel[0]);
		for (j = 1 ; ((j < MAX_WHEEL_ENTRIES)  && (AScrollByWheel[j] != 0)) ; j++) 
		{
      			sprintf(buf, "ScrollByWheel%d", j);
      			profile.WriteInt (ARTICLE, buf , AScrollByWheel[j]);
		}
	}
  
	profile.WriteInt (ARTICLE, "NewWndArticle", NewArticleWindow);
	profile.WriteInt (ARTICLE, "WrapIncomingArticleText", WrapIncomingArticleText);
	profile.WriteInt (ARTICLE, "WrapIncomingArticleTextLength", WrapIncomingArticleTextLength);
	profile.WriteInt (ARTICLE, "TrimHeaders", TrimHeaders);
#ifdef RFC1522
	profile.WriteInt (ARTICLE, "UseRFC1522", UseRFC1522);
#endif
#ifdef VISUAL1521
	profile.WriteInt (ARTICLE, "UseVISUAL1521", UseVISUAL1521);  
#endif
	profile.WriteInt (ARTICLE, "TruncateSignatures", TruncateSignatures);
	profile.WriteInt (ARTICLE, "TruncateSignatureFollowup", TruncateSignatureFollowup);
	profile.WriteInt (ARTICLE, "ScrollPastHeaders", ScrollPastHeaders);
	profile.WriteInt (ARTICLE, "ShowAuthorName", ShowAuthorName);
	profile.WriteInt (ARTICLE, "ShowXFace", ShowXFace);
	profile.WriteInt (ARTICLE, "DeselectOnMarkRead", RMDeselectOnMark);
	profile.WriteInt (ARTICLE, "ItalicizeQuotes", ItalicizeQuotes);
	profile.WriteInt (ARTICLE, "SearchUnreadGroup", ConfigJumpGroup);
	profile.WriteInt (ARTICLE, "ToolbarSize", nToolbarView);
	sprintf (buf, "%d,%d", ptXFaceCoord.x, ptXFaceCoord.y);
	profile.WriteString (ARTICLE, "XFacePosition", buf);
  
#ifdef RFC1522
	profile.WriteInt (COMPOSE, "EncodeRFC1522", EncodeRFC1522);
#endif  
	profile.WriteInt (COMPOSE, "WordWrap", WordWrap);
	profile.WriteInt (COMPOSE, "PrefillCcAddress", PrefillCcAddress);
	profile.WriteInt (COMPOSE, "ShowOrgHdr", ShowOrgHdr);
	profile.WriteInt (COMPOSE, "ShowReplyToHdr", ShowReplyToHdr);
	profile.WriteInt (COMPOSE, "ShowFollowupToHdr", ShowFollowupToHdr);
	profile.WriteInt (COMPOSE, "ShowKeywordsHdr", ShowKeywordsHdr);
	profile.WriteInt (COMPOSE, "ShowSummaryHdr", ShowSummaryHdr);
	profile.WriteInt (COMPOSE, "ShowDistributionHdr", ShowDistributionHdr);
	profile.WriteInt (COMPOSE, "ShowFromHdr", ShowFromHdr);
	profile.WriteString (COMPOSE, "FollowupSaysTemplate", FollowupSaysTemplate);
	profile.WriteString (COMPOSE, "ReplySaysTemplate", ReplySaysTemplate);

	if (*IDToken)
		profile.WriteString (COMPOSE, "MessageIDToken", IDToken);
	else
		profile.WriteString (COMPOSE, "MessageIDToken", NULL);

	if (*CancelKey)
		profile.WriteString (COMPOSE, "CancelKey", CancelKey);
	else
		profile.WriteString (COMPOSE, "CancelKey", NULL);
	profile.WriteInt(COMPOSE, "UseCancelKey", UseCancelKey);
	
	/* //dont save this preference
    if (NoXNewsreader != DEF_NoXNewsreader)
		profile.WriteInt (COMPOSE, "StripXNewsreader", NoXNewsreader);
	else
		profile.WriteInt (COMPOSE, "StripXNewsreader", NULL);
	*/

	profile.WriteString (COMPOSE, "SignatureFile", SigFileName);
	profile.WriteInt (COMPOSE, "EnableSignature", EnableSig);
	profile.WriteInt (COMPOSE, "XNoArchive", XNoArchive);   //MDS 20000401

	if (CScrollMode > WHEELSCROLL_SYSTEM) 
	{
	profile.WriteInt (COMPOSE, "ScrollByWheel", CScrollByWheel[0]);
	for (j = 1 ; ((j < MAX_WHEEL_ENTRIES)  && (CScrollByWheel[j] != 0)) ; j++) 
	{
      		sprintf(buf, "ScrollByWheel%d", j);
      		profile.WriteInt (COMPOSE, buf , CScrollByWheel[j]);
	}
	}

	profile.WriteString (CODING, "DecodePath", DecodePathName);
	profile.WriteString (CODING, "AttachPath", AttachPath);
	profile.WriteInt (CODING, "CodingStatusVerbose", CodingStatusVerbose);
	profile.WriteInt (CODING, "AlsoDecodeOpenArticles", AlsoDecodeOpenArticles);
	profile.WriteInt (CODING, "DumbDecode", DumbDecode);
	profile.WriteInt (CODING, "BlockCodingStatusAlwaysOnTop", BlockCodingStatusAlwaysOnTop);
	profile.WriteInt (CODING, "MinimizeStatusWindows", MinimizeStatusWindows);
	profile.WriteInt (CODING, "ExecuteDecodedFiles", ExecuteDecodedFiles);
	profile.WriteInt (CODING, "NeverEXEWarnFiles", NeverExeWarnFiles);
	profile.WriteInt (CODING, "KeepArticleHeaderVisible", KeepArticleHeaderVisible);
	profile.WriteInt (CODING, "EnableDownloadSnd", EnableDownloadSnd);
	profile.WriteString (CODING, "DownloadSndPath",  szDownloadSnd);

	profile.WriteInt    (ATTACH, "DefaultAttachInNewArt", DefaultAttachInNewArt);
	profile.WriteInt    (ATTACH, "DefaultNewArtForAttach", DefaultNewArtForAttach);
	
	profile.WriteInt    (ATTACH, "ArticleSplitLength", ArticleSplitLength);
	profile.WriteString (ATTACH, "SubjectTemplate", SubjectTemplate);
	profile.WriteInt    (ATTACH, "GenerateMIME", GenerateMIME);
	profile.WriteInt    (ATTACH, "MIMEUsageSuggestions", MIMEUsageSuggestions);
	profile.WriteString (ATTACH, "MIMEUUType", MIMEUUType);
	profile.WriteString (ATTACH, "MIMEXXType", MIMEXXType);
	profile.WriteString (ATTACH, "MIMECustomType", MIMECustomType);
	profile.WriteString (ATTACH, "MIMECharset", MIMECharset);
	profile.WriteString (ATTACH, "DefaultEncodingType", EncodingTypeToStr (DefaultEncodingType));
	profile.WriteString (ATTACH, "DefaultContentType", DefaultContentType);
	profile.WriteString (ATTACH, "EncodingTable", UserCodingTable);

	profile.WriteInt    (WVINTERFACE, "UseClearType", UseClearType);
	profile.WriteString (WVINTERFACE, "WinVnFontFace", WinVnFontFace);
	profile.WriteInt    (WVINTERFACE, "WinVnFontSize", WinVnFontSize);
	profile.WriteString (WVINTERFACE, "WinVnFontStyle", WinVnFontStyle);
	profile.WriteString (WVINTERFACE, "CompositionFontFace", CompositionFontFace);
	profile.WriteInt    (WVINTERFACE, "CompositionFontSize", CompositionFontSize);
	profile.WriteString (WVINTERFACE, "CompositionFontStyle", CompositionFontStyle);
	profile.WriteString (WVINTERFACE, "ListFontFace", ListFontFace);
	profile.WriteInt    (WVINTERFACE, "ListFontSize", ListFontSize);
	profile.WriteString (WVINTERFACE, "ListFontStyle", ListFontStyle);
	profile.WriteString (WVINTERFACE, "ArticleFontFace", ArticleFontFace);
	profile.WriteInt    (WVINTERFACE, "ArticleFontSize", ArticleFontSize);
	profile.WriteString (WVINTERFACE, "ArticleFontStyle", ArticleFontStyle);
	profile.WriteString (WVINTERFACE, "StatusFontFace", StatusFontFace);
	profile.WriteInt    (WVINTERFACE, "StatusFontSize", StatusFontSize);
	profile.WriteString (WVINTERFACE, "StatusFontStyle", StatusFontStyle);
	profile.WriteString (WVINTERFACE, "PrintFontFace", PrintFontFace);
	profile.WriteInt    (WVINTERFACE, "PrintFontSize", PrintFontSize);
	//  profile.WriteInt    (WVINTERFACE, "FontBold", FontBold);
	//  profile.WriteInt    (WVINTERFACE, "ArticleFixedFont", ArticleFixedFont);
	profile.WriteString (WVINTERFACE, "NetUnSubscribedColor", RGBToStr (str, NetUnSubscribedColor));
	profile.WriteString (WVINTERFACE, "NetSubscribedColor", RGBToStr (str, NetSubscribedColor));
	profile.WriteString (WVINTERFACE, "ArticleUnSeenColor", RGBToStr (str, ArticleUnSeenColor));
	profile.WriteString (WVINTERFACE, "ArticleSeenColor", RGBToStr (str, ArticleSeenColor));
	profile.WriteString (WVINTERFACE, "ArticleKilledColor", RGBToStr (str, ArticleKilledColor));
	profile.WriteString (WVINTERFACE, "ArticleTextColor", RGBToStr (str, ArticleTextColor));
	profile.WriteString (WVINTERFACE, "StatusTextColor", RGBToStr (str, StatusTextColor));
	profile.WriteString (WVINTERFACE, "ArticleBackgroundColor", RGBToStr (str, ArticleBackgroundColor));
	profile.WriteString (WVINTERFACE, "ListBackgroundColor", RGBToStr (str, ListBackgroundColor));
	profile.WriteString (WVINTERFACE, "StatusBackgroundColor", RGBToStr (str, StatusBackgroundColor));
	profile.WriteString (WVINTERFACE, "ArticleErrorColor", RGBToStr (str, ArticleErrorColor));
	profile.WriteString (WVINTERFACE, "ArticlePreviewColor", RGBToStr (str, ArticlePreviewColor));
	for (j = 0; j < 16; j++) // save any custom colors
	{
		char szEntry[20];
		wsprintf( szEntry, "CustomColor%02d", j);
		if (CustomColors[j] != RGB(255,255,255))
			profile.WriteString (WVINTERFACE,  szEntry, RGBToStr (str, CustomColors[j]));
		else
			profile.WriteString (WVINTERFACE,  szEntry, NULL);

	}



	profile.WriteInt ( WVINTERFACE, "UseInverseSelections", UseInverseSelections);
	profile.WriteInt ( WVINTERFACE, "ThumbTrack", ThumbTrack);
#ifdef USE_SPLASH
	profile.WriteInt ( WVINTERFACE, "ShowSplashScreen", ShowSplashScreen);
#endif
	profile.WriteString (SEARCHES, "LastArticleTextFind", LastArticleTextFind);
	profile.WriteString (SEARCHES, "LastArticleHeaderFind", LastArticleHeaderFind);
	profile.WriteString (SEARCHES, "LastGroupNameFind", LastGroupNameFind);

	// from Mitsuo Shimomai - Kagoshima University, Japan
	profile.WriteString (EXECUTE, "HttpClient", HttpClient);
	profile.WriteString (EXECUTE, "FtpClient", FtpClient);
	profile.WriteString (EXECUTE, "GopherClient", GopherClient);
	profile.WriteString (EXECUTE, "WaisClient", WaisClient);
	profile.WriteInt (EXECUTE, "UsingDDE", UsingDDE);
	profile.WriteInt (EXECUTE, "DdeServID", DdeServID);
	profile.WriteString (EXECUTE, "DdeServName", DdeServName);

	profile.WriteInt (SMARTFILER, "OnDupeName", OnDupeName);
	profile.WriteInt (SMARTFILER, "OnNameTooLong", OnNameTooLong);
	profile.WriteInt (SMARTFILER, "MaxFileNameLen", MaxFileNameLen);
	profile.WriteInt (SMARTFILER, "MaxFileExtLen", MaxFileExtLen);
	profile.WriteInt (SMARTFILER, "EnableExtensionConversion", EnableExtensionConversion);
	profile.WriteInt (SMARTFILER, "RemoveSpaces", SpaceProcessing);


  if (MailList) 
  {
        for (j = 0; j < max (MailListOrigNum, (int) MailList->numLines); j++) 
		{
          sprintf (str, "Address%d", j + 1);
          profile.WriteString (MAIL_ADDRESSES, str,
               (j >= (int) MailList->numLines) ? "" : TextBlockLine (MailList, j));
        }
  }

  if (ExtMapSourceList && ExtMapDosList) 
  {
        for (j = 0; j < max (ExtListOrigNum, (int) ExtMapSourceList->numLines); j++) 
		{
          sprintf (str, "ExtConvert%d", j + 1);
          if (j >= (int) ExtMapSourceList->numLines)
                *buf = '\0';
          else
                _snprintf (buf, MAXINTERNALLINE - 1, "%s,%s",
                                   TextBlockLine (ExtMapSourceList, j), TextBlockLine (ExtMapDosList, j));

          profile.WriteString (SMARTFILER, str, buf);
        }
  }

  profile.Save(szAppProFile, TRUE);



}


