// /file SNFServerConfig.cpp
//
// Copyright (C) 2011, ARM Research Labs, LLC.
// See www.armresearch.com for the copyright terms.
//
// This file contains the functions for SNFServerConfig.
//
// $Id$
//
///////////////////////////////////////////////////////////////////////////////////////////////////

#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <exception>
#include <stdexcept>
#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>
#include "SNFServerConfig.hpp"

using namespace std;

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Configuration. ////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////

// Initialize default configuration file path.
#ifdef WIN

//  Windows OS.
const std::string SNFServerConfig::DefaultConfigFile("C:\\SNF\\SNFServer.xml");
const std::string SNFServerConfig::SampleConfigFile("C:\\SNF\\SNFServer.xml.sample");
const std::string SNFServerConfig::SampleIdentityFile("C:\\SNF\\identity.xml.sample");
const std::string InstallFile("");

#else

#ifdef DEFAULT_CONFIG_DIR
//  *nix, DEFAULT_CONFIG_DIR is specified on the compile command line.
const std::string SNFServerConfig::DefaultConfigFile(DEFAULT_CONFIG_DIR "/snf-server/SNFServer.xml");
const std::string SNFServerConfig::SampleConfigFile(DEFAULT_CONFIG_DIR "/snf-server/SNFServer.xml.sample");
const std::string SNFServerConfig::SampleIdentityFile(DEFAULT_CONFIG_DIR "/snf-server/identity.xml.sample");
const std::string InstallFile(DOC_DIR "/INSTALL");
#else
// Not Windows, and DEFAULT_CONFIG_DIR is not specified on the compile
// command line.  In this case, we don't know the default path for the
// configuration file.
const std::string SNFServerConfig::DefaultConfigFile("");
const std::string SNFServerConfig::SampleConfigFile("");
const std::string SNFServerConfig::SampleIdentityFile("");
const std::string InstallFile("INSTALL");
#endif

#endif

const string SNFServerConfig::ApplicationName("SNFServer");

const string IntegrateWithNoneKey("-with=none");
const string IntegrateWithPostfixKey("-with=postfix");
const string IntegrateWithSendmailKey("-with=sendmail");

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// End of configuration. /////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////

void
SNFServerConfig::DisplayHelp(std::string Version) {

    std::string ExclusiveCommands;
    std::string ExclusiveCommandsHelp;

    ExclusiveCommands  = IntegrateWithPostfixKey + " | ";
    ExclusiveCommands += IntegrateWithSendmailKey + " | ";
    ExclusiveCommands += IntegrateWithNoneKey;

    ExclusiveCommandsHelp  = "  -with=postfix              Integrate with postfix and start/reload postfix\n";
    ExclusiveCommandsHelp += "  -with=sendmail             Integrate with sendmail and start/reload sendmail\n";
    ExclusiveCommandsHelp += "                             (Not available on OpenBSD or FreeBSD)\n";
    ExclusiveCommandsHelp += "  -with=none                 Remove any integration with all supported MTAs\n";

    cout
      << Version << endl
      << "Copyright (C) 2012, ARM Research Labs, LLC (www.armresearch.com)\n\n"
      << "Usage:\n\n"
      << "SNFServerConfig "
      << UtilityConfig::HelpCommandLine(ExclusiveCommands) << "\n\n"
      << "SNFServerConfig "
      << UtilityConfig::HelpDescription(ExclusiveCommandsHelp) << "\n"
      << "The configuration file name is:\n\n"
      << "  " << DefaultConfigFile << "\n\n"
      << "If the above file doesn't exist, then it is copied from the following file:\n\n"
      << "  " << SampleConfigFile << "\n\n"
      << "If integration with an MTA is specified, the MTA's configuration is reloaded "
      << "if the MTA is running.\n\n";

};

bool
SNFServerConfig::GetCommandLineInput(int argc, char* argv[]) {

    int i;
    int NumCommandsFound = 0;
    string OneInput;

    Command = NoCommand;                                                        // Default is to do nothing.

    for (i = 1; i < argc; i++) {                                                // Check each input.

        OneInput = argv[i];

	if (OneInput == IntegrateWithPostfixKey) {

            Command = IntegrateWithPostfixCommand;
	    NumCommandsFound++;

        } else if (0 == OneInput.find(IntegrateWithSendmailKey)) {
	    std::string OsType;

            OsType = GetOperatingSystemType();                                  // Check whether the platform is supported.

	    if ( ("OpenBSD" == OsType) || ("FreeBSD" == OsType) ) {
		std::string Temp;

		Temp = "Integration with sendmail is not supported on " + OsType;
		Temp += ".\n";
		Temp += "Please see " + InstallFile;
		Temp += " for information on integration with sendmail.\n";

		throw std::runtime_error(Temp);

	    }		

            Command = IntegrateWithSendmailCommand;
	    NumCommandsFound++;

        } else if (OneInput == IntegrateWithNoneKey) {

	    Command = IntegrateWithNoneCommand;
	    NumCommandsFound++;

        } else {

	    // Process command-line input by the base class.
	    if (!ProcessCommandLineItem(OneInput)) {

		Command = UnknownCommand;
                return false;                                                   // Illegal input.

	    }

        }

    }

    if (UpdateCredentialsSpecified()) {

	Command = UpdateCredentialsCommand;
	NumCommandsFound++;

    }

    if (SetupRepairSpecified()) {

	Command = SetupRepairCommand;
	NumCommandsFound++;

    }

    if (StartSnifferSpecified()) {

	Command = StartSnifferCommand;
	NumCommandsFound++;

    }

    if (StopSnifferSpecified()) {

	Command = StopSnifferCommand;
	NumCommandsFound++;

    }

    return ( (NumCommandsFound == 1) && CommandLineIsOkay() );

}

void
SNFServerConfig::ExecuteCommand() {

    Postfix.SetOperatingSystem(GetOperatingSystemType());
    Postfix.SetVerbose(Verbose());
    Postfix.SetExplain(Explain());

    Sendmail.SetOperatingSystem(GetOperatingSystemType());
    Sendmail.SetVerbose(Verbose());
    Sendmail.SetExplain(Explain());

    SetConfigFileName(DefaultConfigFile);

    if (SetupRepairCommand == Command) {

        CreateDefaultConfigFile(SampleConfigFile);                              // Create the file if it doesn't exist,
                                                                                // Set owner and mode in any case.

    }

    LoadConfig();
    LoadInfo();                                                                 // Load the file paths.

    switch (Command) {

    case SetupRepairCommand:

        SetupRepair(SampleIdentityFile);

        break;

    case UpdateCredentialsCommand:

        UpdateRulebaseScriptCredentials();
	DownloadRulebase();
	UpdateIdentityFile();

	break;

    case IntegrateWithPostfixCommand:

	Postfix.Integrate(&SaveFile);

	UnintegrateWithAllExcept("postfix");

	break;

    case IntegrateWithSendmailCommand:

	Sendmail.Integrate(&SaveFile);

	UnintegrateWithAllExcept("sendmail");

	break;

    case IntegrateWithNoneCommand:

	UnintegrateWithAllExcept();

	break;

    case StartSnifferCommand:

	LoadCredentials();

	StartSniffer("snf-server start", ApplicationName);
	break;

    case StopSnifferCommand:

	LoadCredentials();

	StopSniffer("snf-server stop", ApplicationName);
	break;

    default:

	break;

    }

}

void
SNFServerConfig::SaveFileState() {

    if (!Explain()) {

        SaveFile.CreateBackupFile(GetRulebaseScriptName());

	if (UpdateCredentialsSpecified()) {

	    SaveFile.CreateBackupFile(GetRulebaseFileName());

	}
        SaveFile.CreateBackupFile(GetIdentityFileName());
        SaveFile.CreateBackupFile(GetIgnoreListFileName());

    }

}

void
SNFServerConfig::UnintegrateWithAllExcept(std::string Except) {

    if (Except != "postfix") {

	Postfix.Unintegrate(&SaveFile);

    }

    if (Except != "sendmail") {

	Sendmail.Unintegrate(&SaveFile);

    }

}
