/*
 * MultiMail offline mail reader
 * resource class

 Copyright (c) 1996 Toth Istvan <stoty@vma.bme.hu>
 Copyright (c) 1999 William McBrine <wmcbrine@clark.net>

 Distributed under the GNU General Public License.
 For details, see the file COPYING in the parent directory. */

#include "mmail.h"
#include "../interfac/error.h"
#include "../interfac/mysystem.h"

extern "C" {
#include <sys/stat.h>
}

/* Default filenames. Note that EMX now serves for the Win32 port (via
   RSXNT), as well as for OS/2; one of the few differences is the default
   editor defined here:
*/

#ifdef __MSDOS__
# define DEFEDIT "edit"
# define DEFZIP "pkzip -o"
# define DEFUNZIP "pkunzip -o"
# define DEFLHA "lha a /m"
# define DEFUNLHA "lha e"
#else
# ifdef __EMX__
#  ifdef __WIN32__
#   define DEFEDIT "edit"
#  else
#   define DEFEDIT "tedit"
#  endif
# else
#  define DEFEDIT "vi"
# endif
# define DEFZIP "zip -jo"
# define DEFUNZIP "unzip -joL"
# define DEFLHA "lha af"
# define DEFUNLHA "lha efi"
#endif

#define DEFARJ "arj a"
#define DEFUNARJ "arj e"
#define DEFRAR "rar u -ep"
#define DEFUNRAR "rar e -cl -o+"

#define DEFNONE "xxcompress"
#define DEFUNNONE "xxuncompress"

#if defined (__MSDOS__) || defined (__EMX__)
# define RCNAME "mmail.rc"
# define ADDRBOOK "address.bk"
# define SIGNATURE "sig.txt"
#else
# define RCNAME ".mmailrc"
# define ADDRBOOK "addressbook"
# define SIGNATURE ".signature"
#endif

extern ErrorType error;

const int startUpLen = 23;

// ================
// baseconfig class
// ================

bool baseconfig::parseConfig(const char *configFileName)
{
	FILE *configFile;
	char buffer[256], *pos, *resName, *resValue;
	int vermajor = 0, verminor = 0;

	if ((configFile = fopen(configFileName, "rt"))) {
	    while (fgets(buffer, sizeof buffer, configFile)) {
		if ((buffer[0] != '#') && (buffer[0] != '\n')) {
			pos = buffer;

			//leading spaces
			while (*pos == ' ' || *pos == '\t')
				pos++;

			//skip "bw" -- for backwards compatiblity
			if (*pos == 'b' && pos[1] == 'w')
				pos += 2;

			//resName
			resName = pos;
			while (*pos != ':' && *pos != '=' &&
				*pos != ' ' && *pos != '\t' && *pos)
					pos++;
			if (*pos)
				*pos++ = '\0';

			//chars between strings
			while (*pos == ' ' || *pos == '\t' ||
				*pos == ':' || *pos == '=')
					pos++;

			//resValue
			resValue = pos;
			while (*pos != '\n' && *pos)
				pos++;
			*pos = '\0';

			if (!strncasecmp("ver", resName, 3))
				sscanf(resValue, "%d.%d", &vermajor,
					&verminor);
			else
				for (int c = 0; c < configItemNum; c++)
					if (!strcasecmp(names[c], resName)) {
						processOne(c, resValue);
						break;
					}
		}
	    }
	    fclose(configFile);
	}

	// Does the config file need updating?
	return (vermajor < MM_MAJOR) || ((vermajor == MM_MAJOR) &&
		(verminor < MM_MINOR));
}

void baseconfig::newConfig(const char *configname)
{
	FILE *fd;
	const char **p;

	printf("Updating %s...\n", configname);

	if ((fd = fopen(configname, "wt"))) {

		for (p = intro; *p; p++)
			fprintf(fd, "# %s\n", *p);

		fprintf(fd, "\nVersion: %d.%d\n", MM_MAJOR, MM_MINOR);

		for (int x = 0; x < configItemNum; x++) {
			if (comments[x])
				fprintf(fd, "\n# %s\n", comments[x]);
			fprintf(fd, "%s: %s\n", names[x],
				configLineOut(x));
		}
		fclose(fd);
	} else
		fatalError("Error writing config file");
}

// ==============
// resource class
// ==============

const char *resource::rc_names[startUpLen] =
{
	"homeDir", "mmHomeDir", "signature", "editor",
	"PacketDir", "ReplyDir", "SaveDir", "AddressBook", "TaglineFile",
	 "ColorFile", "arjUncompressCommand", "zipUncompressCommand",
	"lhaUncompressCommand", "rarUncompressCommand",
	"unknownUncompressCommand", "arjCompressCommand",
	"zipCompressCommand", "lhaCompressCommand", "rarCompressCommand",
	"unknownCompressCommand", "PacketSort", "LetterSort", "Charset"
};

const char *resource::rc_intro[] = {
 "-----------------------",
 MM_NAME " configuration",
 "-----------------------",
 "",
 "Any of these keywords may be omitted, in which case the default values",
 "(shown here) will be used.",
 "",
 "If you change either of the base directories, all the subsequent paths",
 "will be changed, unless they're overriden in the individual settings.",
 0
};

const char *resource::rc_comments[startUpLen] = {
 "Base directories (derived from $HOME or $MMAIL)", 0,
 "Signature (file) that should be appended to each message. (Not used\n"
 "# unless specified here.)",
 "Editor for replies = $EDITOR; or if not defined, " DEFEDIT,
 MM_NAME " will look for packets here",
 "Reply packets go here",
 "Saved messages go here, by default",
 "Full paths to the address book and tagline file", 0,
 "Full path to color specification file",
 "Decompression commands (must include an option to junk/discard paths!)",
	0, 0, 0, 0,
 "Compression commands (must include an option to junk/discard paths!)",
	0, 0, 0, 0,
 "Default sort for packet list: by Name or Time (most recent first)",
 "Default sort for letter list: by Subject, Number, From or To",
 "Console character set: CP437 (IBM PC) or Latin-1 (ISO-8859-1)"
};

const int resource::startUp[startUpLen] =
{
	homeDir, mmHomeDir, sigFile, editor,
	PacketDir, ReplyDir, SaveDir, AddressFile, TaglineFile,
	ColorFile, arjUncompressCommand, zipUncompressCommand,
	lhaUncompressCommand, rarUncompressCommand,
	unknownUncompressCommand, arjCompressCommand, zipCompressCommand,
	lhaCompressCommand, rarCompressCommand, unknownCompressCommand,
	PacketSort, LetterSort, Charset
};

resource::resource()
{
	char configFileName[256], inp;
	const char *envhome, *greeting =
		"\nWelcome to " MM_NAME " v%d.%d!\n\n"
		"A new or updated " RCNAME " has been written. "
		"If you continue now, " MM_NAME " will\nuse the default "
		"values. If you wish to edit your " RCNAME " first, say "
		"'N' at\nthe prompt.\n\nContinue? (y/n) ";

	names = rc_names;
	intro = rc_intro;
	comments = rc_comments;
	configItemNum = startUpLen;

	for (int c = 0; c < noOfStrings; c++)
		resourceData[c] = 0;

	resourceInt[0] = 1;
	resourceInt[1] = 0;
	resourceInt[2] =
#if defined (__MSDOS__) || defined (__EMX__)
	0;
#else
	1;
#endif

	envhome = getenv("MMAIL");
	if (!envhome)
		envhome = getenv("HOME");
	if (!envhome)
		envhome = error.getOrigDir();

	set(homeDir, fixPath(envhome));
	sprintf(configFileName, "%.243s/" RCNAME, get(homeDir));

	initinit();
	homeInit();
	mmHomeInit();

	if (parseConfig(configFileName)) {
		newConfig(configFileName);
		printf(greeting, MM_MAJOR, MM_MINOR);
		inp = fgetc(stdin);
		if (toupper(inp) == 'N')
			exit(1);
	}

	if (!verifyPaths())
		fatalError("Unable to access data directories");

	mytmpnam(basedir);
	checkPath(basedir, false);
	subPath(WorkDir, "work");
	subPath(UpWorkDir, "upwork");
}

resource::~resource()
{
	clearDirectory(resourceData[WorkDir]);
	clearDirectory(resourceData[UpWorkDir]);
	mychdir("..");
	rmdir(resourceData[WorkDir]);
	rmdir(resourceData[UpWorkDir]);
	mychdir("..");
	rmdir(basedir);
	for (int c = 0; c < noOfStrings; c++)
		delete[] resourceData[c];
}

// For consistency, no path should end in a slash:
const char *resource::fixPath(const char *path)
{
	static char tmp[256];
	char d = path[strlen(path) - 1];

	if ((d == '/') || (d == '\\')) {
		sprintf(tmp, "%.254s.", path); 
		return tmp;
	} else
		return path;
}

bool resource::checkPath(const char *onepath, bool show)
{
	if (mychdir(onepath)) {
		if (show)
			printf("Creating %s...\n", onepath);
		if (mkdir(onepath, S_IRWXU))
			return false;
	}
	return true;
}

bool resource::verifyPaths()
{
	if (checkPath(resourceData[mmHomeDir], true))
	    if (checkPath(resourceData[PacketDir], true))
		if (checkPath(resourceData[ReplyDir], true))
		    if (checkPath(resourceData[SaveDir], true))
			return true;
	return false;
}

void resource::processOne(int c, const char *resValue)
{
	if (*resValue) {
		c = startUp[c];
		if (c < noOfStrings) {
			// Canonized for the benefit of the Win32 version:
			set(c, canonize(fixPath(resValue)));
			switch (c) {
			case homeDir:
				homeInit();
			case mmHomeDir:
				mmHomeInit();
			}
		} else {
			int x = 0;
			char r = toupper(*resValue);

			switch (c) {
			case PacketSort:
				x = (r == 'T');
				break;
			case LetterSort:
				switch (r) {
				case 'N':
					x = 1;
					break;
				case 'F':
					x = 2;
					break;
				case 'T':
					x = 3;
				}
				break;
			case Charset:
				x = (r == 'L');
			}

			set(c, x);
		}
	}
}

const char *resource::configLineOut(int x)
{
	static const char *pktopt[] = {"Name", "Time"},
		*lttopt[] = {"Subject", "Number", "From", "To"},
		*charopt[] = {"CP437", "Latin-1"};

	x = startUp[x];

	return (x < noOfStrings) ? get(x) : ((x == PacketSort) ? pktopt :
		((x == LetterSort) ? lttopt : charopt))[getInt(x)];
}

const char *resource::get(int ID) const
{
	return resourceData[ID];
}

int resource::getInt(int ID) const
{
	ID -= noOfStrings;
	return resourceInt[ID];
}

void resource::set(int ID, const char *newValue)
{
	delete[] resourceData[ID];
	resourceData[ID] = strdupplus(newValue);
}

void resource::set(int ID, int newValue)
{
	ID -= noOfStrings;
	resourceInt[ID] = newValue;
}

//------------------------------------------------------------------------
// The resource initializer functions
//------------------------------------------------------------------------

void resource::homeInit()
{
	char tmp[256];

	sprintf(tmp, "%.243s/mmail", resourceData[homeDir]);
	set(mmHomeDir, tmp);
}

void resource::mmEachInit(int index, const char *dirname)
{
	char tmp[256];

	sprintf(tmp, "%.243s/%s", resourceData[mmHomeDir], dirname);
	set(index, tmp);
}

void resource::subPath(int index, const char *dirname)
{
	char tmp[256];

	sprintf(tmp, "%.243s/%s", basedir, dirname);
	set(index, tmp);
	if (!checkPath(tmp, 0))
		fatalError("tmp Dir could not be created");
}

void resource::initinit()
{
	set(arjUncompressCommand, DEFUNARJ);
	set(zipUncompressCommand, DEFUNZIP);
	set(lhaUncompressCommand, DEFUNLHA);
	set(rarUncompressCommand, DEFUNRAR);
	set(unknownUncompressCommand, DEFUNNONE);
	set(arjCompressCommand, DEFARJ);
	set(zipCompressCommand, DEFZIP);
	set(lhaCompressCommand, DEFLHA);
	set(rarCompressCommand, DEFRAR);
	set(unknownCompressCommand, DEFNONE);

	set(UncompressCommand, DEFUNZIP);
	set(CompressCommand, DEFZIP);

	set(sigFile, "");

	char *p = getenv("EDITOR");
	set(editor, (p ? p : DEFEDIT));
}

void resource::mmHomeInit()
{
	mmEachInit(PacketDir, "down");
	mmEachInit(ReplyDir, "up");
	mmEachInit(SaveDir, "save");
	mmEachInit(AddressFile, ADDRBOOK);
	mmEachInit(TaglineFile, "taglines");
	mmEachInit(ColorFile, "colors");
}
