in postfix main.cf. Modified PostfixIntegrate to use PostfixMilterConf. Need to implement unit test for PostfixMilterConf. git-svn-id: https://svn.microneil.com/svn/SNFUtility/trunk@49 aa37657e-1934-4a5f-aa6d-2d8eab27ff7cmaster
@top_srcdir@/SNFUtility/SNFMilterConfig/main.cpp \ | @top_srcdir@/SNFUtility/SNFMilterConfig/main.cpp \ | ||||
@top_srcdir@/SNFUtility/SNFMilterConfig/SNFMilterConfig.cpp \ | @top_srcdir@/SNFUtility/SNFMilterConfig/SNFMilterConfig.cpp \ | ||||
@top_srcdir@/SNFUtility/SNFMilterConfig/PostfixIntegrate.cpp \ | @top_srcdir@/SNFUtility/SNFMilterConfig/PostfixIntegrate.cpp \ | ||||
@top_srcdir@/SNFUtility/SNFMilterConfig/PostfixMilterConf.cpp \ | |||||
@top_srcdir@/SNFUtility/SNFMilterConfig/SendmailIntegrate.cpp | @top_srcdir@/SNFUtility/SNFMilterConfig/SendmailIntegrate.cpp | ||||
noinst_HEADERS = \ | noinst_HEADERS = \ | ||||
@top_srcdir@/SNFUtility/SNFMilterConfig/SNFMilterConfig.hpp \ | @top_srcdir@/SNFUtility/SNFMilterConfig/SNFMilterConfig.hpp \ | ||||
@top_srcdir@/SNFUtility/SNFMilterConfig/PostfixIntegrate.hpp \ | @top_srcdir@/SNFUtility/SNFMilterConfig/PostfixIntegrate.hpp \ | ||||
@top_srcdir@/SNFUtility/SNFMilterConfig/PostfixMilterConf.hpp \ | |||||
@top_srcdir@/SNFUtility/SNFMilterConfig/SendmailIntegrate.hpp | @top_srcdir@/SNFUtility/SNFMilterConfig/SendmailIntegrate.hpp | ||||
EXTRA_DIST = \ | EXTRA_DIST = \ |
#include <fstream> | #include <fstream> | ||||
#include "PostfixIntegrate.hpp" | #include "PostfixIntegrate.hpp" | ||||
#include "PostfixMilterConf.hpp" | |||||
////////////////////////////////////////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
// Configuration. //////////////////////////////////////////////////////////////////////////////////////// | // Configuration. //////////////////////////////////////////////////////////////////////////////////////// | ||||
const std::string SnfMilterMainCfSearchString("unix:/var/snf-milter/socket"); | const std::string SnfMilterMainCfSearchString("unix:/var/snf-milter/socket"); | ||||
const std::string SnfMilterMainCfIntegrationString("smtpd_milters = unix:/var/snf-milter/socket"); | |||||
const std::string SnfMilterMainCfMilterSpec("unix:/var/snf-milter/socket"); | |||||
////////////////////////////////////////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
// End of configuration. ///////////////////////////////////////////////////////////////////////////////// | // End of configuration. ///////////////////////////////////////////////////////////////////////////////// | ||||
} | } | ||||
std::ifstream Input; | |||||
if (Verbose()) { | if (Verbose()) { | ||||
std::cout << "Add to postfix file " << PostfixMainCfPath << ": '" | |||||
<< SnfMilterMainCfIntegrationString << "'..."; | |||||
std::cout << "Integrate with postfix...\n"; | |||||
} | } | ||||
SaveFile->CreateBackupFile(PostfixMainCfPath); // Save any existing file. | SaveFile->CreateBackupFile(PostfixMainCfPath); // Save any existing file. | ||||
std::ofstream Output; // Append the configuration. | |||||
Output.open(PostfixMainCfPath.c_str(), std::ios::app); | |||||
if (!Output) { | |||||
Input.open(PostfixMainCfPath.c_str()); // Read the contents. | |||||
if (!Input) { | |||||
std::string Temp; | std::string Temp; | ||||
Temp = "Error opening the postfix configuration file " + PostfixMainCfPath; | Temp = "Error opening the postfix configuration file " + PostfixMainCfPath; | ||||
Temp += " for writing: "; | |||||
Temp += " for reading: "; | |||||
Temp += strerror(errno); | Temp += strerror(errno); | ||||
throw std::runtime_error(Temp); | throw std::runtime_error(Temp); | ||||
} | } | ||||
Output << SnfMilterMainCfIntegrationString << "\n"; | |||||
if (!Output) { | |||||
std::string Content; | |||||
std::string Line; | |||||
bool ModifiedLine = false; | |||||
PostfixMilterConf MilterConf; // Object to update the config line. | |||||
while (getline(Input, Line)) { | |||||
MilterConf.ConfLine(Line); // Load the object with the line. | |||||
if (MilterConf.IsMilterLine() && !ModifiedLine) { // Check for milter integration line. | |||||
// Ignore subsequence integration lines. | |||||
MilterConf.AddIntegration(); // Found milter line. Add integration. | |||||
if (Verbose()) { | |||||
std::cout << " Replace '" << Line << "' with '" | |||||
<< MilterConf.ConfLine() << "'...\n"; | |||||
} | |||||
Line = MilterConf.ConfLine(); // Copy updated line. | |||||
ModifiedLine = true; | |||||
} | |||||
Content += Line + "\n"; // Copy this line. | |||||
} | |||||
if (!ModifiedLine) { | |||||
MilterConf.ConfLine(""); | |||||
MilterConf.AddIntegration(); | |||||
if (Verbose()) { | |||||
std::cout << " Add '" << MilterConf.ConfLine() << "'...\n"; | |||||
} | |||||
Content += MilterConf.ConfLine() + "\n"; | |||||
} | |||||
if (!Input.eof()) { // Should be at end-of-file. | |||||
std::string Temp; | std::string Temp; | ||||
Temp = "Error appending to the postfix configuration file " + PostfixMainCfPath; | |||||
Temp = "Error reading the postfix configuration file " + PostfixMainCfPath; | |||||
Temp += ": "; | Temp += ": "; | ||||
Temp += strerror(errno); | Temp += strerror(errno); | ||||
throw std::runtime_error(Temp); | throw std::runtime_error(Temp); | ||||
} | } | ||||
Output.close(); | |||||
if (!Output) { | |||||
Input.close(); | |||||
if (Input.bad()) { | |||||
std::string Temp; | std::string Temp; | ||||
Temp = "Error closing the postfix configuration file " + PostfixMainCfPath; | Temp = "Error closing the postfix configuration file " + PostfixMainCfPath; | ||||
Temp += " after appending: "; | |||||
Temp += " after reading: "; | |||||
Temp += strerror(errno); | Temp += strerror(errno); | ||||
throw std::runtime_error(Temp); | throw std::runtime_error(Temp); | ||||
} | } | ||||
if (!Explain()) { | |||||
std::ofstream Output; // Write the updated contents. | |||||
Output.open(PostfixMainCfPath.c_str(), std::ios::trunc); | |||||
if (!Output) { | |||||
std::string Temp; | |||||
Temp = "Error opening the postfix configuration file " + PostfixMainCfPath; | |||||
Temp += " for writing: "; | |||||
Temp += strerror(errno); | |||||
throw std::runtime_error(Temp); | |||||
} | |||||
Output << Content; | |||||
if (!Output) { | |||||
std::string Temp; | |||||
Temp = "Error writing the postfix configuration file " + PostfixMainCfPath; | |||||
Temp += ": "; | |||||
Temp += strerror(errno); | |||||
throw std::runtime_error(Temp); | |||||
} | |||||
Output.close(); | |||||
if (!Output) { | |||||
std::string Temp; | |||||
Temp = "Error closing the postfix configuration file " + PostfixMainCfPath; | |||||
Temp += " after writing: "; | |||||
Temp += strerror(errno); | |||||
throw std::runtime_error(Temp); | |||||
} | |||||
} | |||||
} | } | ||||
OutputVerboseEnd(); | OutputVerboseEnd(); | ||||
std::string Content; | std::string Content; | ||||
std::string Line; | std::string Line; | ||||
PostfixMilterConf MilterConf; // Object to update the config line. | |||||
while (getline(Input, Line)) { | while (getline(Input, Line)) { | ||||
if (std::string::npos != Line.find(SnfMilterMainCfSearchString)) { // Check for integration line. | |||||
MilterConf.ConfLine(Line); // Load the object with the line. | |||||
if (MilterConf.IsIntegrated()) { // Check for integration. | |||||
MilterConf.RemoveIntegration(); // Integrated. Remove the milter spec. | |||||
if (Verbose()) { | if (Verbose()) { | ||||
std::cout << " Remove '" << Line << "'...\n"; | |||||
std::cout << " Replace '" << Line << "' with '" | |||||
<< MilterConf.ConfLine() << "'...\n"; | |||||
} | } | ||||
continue; // Do not copy this line. | |||||
Content += MilterConf.ConfLine(); // Copy updated line. | |||||
continue; | |||||
} | } | ||||
if (!Explain()) { | if (!Explain()) { | ||||
std::ofstream Output; // Write the updated contents. | |||||
std::ofstream Output; // Write the updated contents. | |||||
Output.open(PostfixMainCfPath.c_str(), std::ios::trunc); | Output.open(PostfixMainCfPath.c_str(), std::ios::trunc); | ||||
if (!Output) { | if (!Output) { | ||||
throw std::runtime_error(Temp); | throw std::runtime_error(Temp); | ||||
} | } | ||||
PostfixMilterConf MilterConf; // Object to update the config line. | |||||
std::string Line; | std::string Line; | ||||
while (getline(Input, Line)) { | while (getline(Input, Line)) { | ||||
if (std::string::npos != Line.find(SnfMilterMainCfSearchString)) { // Check for integration line. | |||||
MilterConf.ConfLine(Line); | |||||
if (MilterConf.IsIntegrated()) { // Check for integration line. | |||||
Integrated = true; // Found it. | Integrated = true; // Found it. | ||||
// /file PostfixMilterConf.cpp | |||||
// | |||||
// Copyright (C) 2011, ARM Research Labs, LLC. | |||||
// See www.armresearch.com for the copyright terms. | |||||
// | |||||
// This file contains the functions for PostfixMilterConf. | |||||
// | |||||
// $Id$ | |||||
// | |||||
/////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
#include <exception> | |||||
#include <stdexcept> | |||||
#include <sstream> | |||||
#include "Utility.hpp" | |||||
#include "PostfixMilterConf.hpp" | |||||
////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
// Configuration. //////////////////////////////////////////////////////////////////////////////////////// | |||||
////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
/// SNFMilter socket specification. | |||||
const std::string SnfMilterSocketSpec("unix:/var/snf-milter/socket"); | |||||
/// Milter keyword in the postfix main.cf file. | |||||
const std::string SmtpdMilterKeyword("smtpd_milters"); | |||||
////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
// End of configuration. ///////////////////////////////////////////////////////////////////////////////// | |||||
////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
void | |||||
PostfixMilterConf::ConfLine(std::string Line) { | |||||
ConfigurationLine = Utility::Trim(Line); // Remove leading and trailing whitespace. | |||||
} | |||||
bool | |||||
PostfixMilterConf::IsIntegrated() { | |||||
return (IsMilterLine() && ContainsSnfMilterSocketSpec()); | |||||
} | |||||
void | |||||
PostfixMilterConf::AddIntegration() { | |||||
if (IsIntegrated()) { | |||||
return; | |||||
} | |||||
if (IsMilterLine()) { // Add to existing milter line. | |||||
std::string NewConfLine; | |||||
NewConfLine = SmtpdMilterKeyword + " ="; | |||||
// Skip to "=" in configuration line. | |||||
std::string::size_type NextIndex; | |||||
NextIndex = ConfigurationLine.find("="); | |||||
if (std::string::npos == NextIndex) { // Check format. | |||||
std::ostringstream Temp; | |||||
Temp << "Error processing postfix main.cf file smtpd_milters line: '" | |||||
<< ConfigurationLine << "'"; | |||||
throw std::runtime_error(Temp.str()); | |||||
} | |||||
std::string ExistingMilters; | |||||
ExistingMilters = Utility::Trim(ConfigurationLine.substr(NextIndex + 1)); // Should contain existing milters. | |||||
while (ExistingMilters != "") { | |||||
NewConfLine += " "; | |||||
std::string NextMilter; | |||||
NextIndex = ExistingMilters.find_first_of(" ,"); | |||||
NextMilter = Utility::Trim(ExistingMilters.substr(0, NextIndex)); | |||||
ExistingMilters = Utility::Trim(ExistingMilters.substr(NextIndex + 1)); | |||||
if (NextMilter == "") { | |||||
std::ostringstream Temp; | |||||
Temp << "Error processing postfix main.cf file smtpd_milters line: '" | |||||
<< ConfigurationLine << "'"; | |||||
throw std::runtime_error(Temp.str()); | |||||
} | |||||
NewConfLine += NextMilter; | |||||
} | |||||
ConfigurationLine = NewConfLine + " "; | |||||
ConfigurationLine += SnfMilterSocketSpec; | |||||
} else if (ConfigurationLine == "") { // Empty configuration line. | |||||
ConfigurationLine = SmtpdMilterKeyword + " = "; | |||||
ConfigurationLine += SnfMilterSocketSpec; | |||||
} else { // Unexpected non-empty line. | |||||
std::ostringstream Temp; | |||||
Temp << "Internal error: Attempted to modify a line in main.cf that does not begin with '" | |||||
<< SmtpdMilterKeyword << "'"; | |||||
throw std::runtime_error(Temp.str()); | |||||
} | |||||
} | |||||
void | |||||
PostfixMilterConf::RemoveIntegration() { | |||||
if (!IsIntegrated()) { | |||||
return; | |||||
} | |||||
if (IsMilterLine()) { // Remove from an existing milter line. | |||||
std::string NewConfLine; | |||||
NewConfLine = SmtpdMilterKeyword + " ="; | |||||
// Skip to "=" in configuration line. | |||||
std::string::size_type NextIndex; | |||||
NextIndex = ConfigurationLine.find("="); | |||||
if (std::string::npos == NextIndex) { // Check format. | |||||
std::ostringstream Temp; | |||||
Temp << "Error processing postfix main.cf file smtpd_milters line: '" | |||||
<< ConfigurationLine << "'"; | |||||
throw std::runtime_error(Temp.str()); | |||||
} | |||||
std::string ExistingMilters; | |||||
bool AddedMilter = false; | |||||
ExistingMilters = Utility::Trim(ConfigurationLine.substr(NextIndex + 1)); // Should contain existing milters. | |||||
while (ExistingMilters != "") { | |||||
NewConfLine += " "; | |||||
std::string NextMilter; | |||||
NextIndex = ExistingMilters.find_first_of(" ,"); | |||||
if (std::string::npos == NextIndex) { | |||||
NextMilter = ExistingMilters; | |||||
ExistingMilters = ""; | |||||
} else { | |||||
NextMilter = Utility::Trim(ExistingMilters.substr(0, NextIndex)); | |||||
ExistingMilters = Utility::Trim(ExistingMilters.substr(NextIndex + 1)); | |||||
} | |||||
if (NextMilter == "") { | |||||
std::ostringstream Temp; | |||||
Temp << "Error processing postfix main.cf file smtpd_milters line: '" | |||||
<< ConfigurationLine << "'"; | |||||
throw std::runtime_error(Temp.str()); | |||||
} | |||||
if (NextMilter != SnfMilterSocketSpec) { // Copy if not for SNFMilter. | |||||
NewConfLine += NextMilter; | |||||
AddedMilter = true; | |||||
} | |||||
} | |||||
if (AddedMilter) { | |||||
ConfigurationLine = NewConfLine; | |||||
} else { | |||||
ConfigurationLine = ""; | |||||
} | |||||
} else { // Unexpected non-empty line. | |||||
std::ostringstream Temp; | |||||
Temp << "Internal error: Attempted to modify a line in main.cf that does not begin with '" | |||||
<< SmtpdMilterKeyword << "'"; | |||||
throw std::runtime_error(Temp.str()); | |||||
} | |||||
} | |||||
std::string | |||||
PostfixMilterConf::ConfLine() { | |||||
return ConfigurationLine; | |||||
} | |||||
bool | |||||
PostfixMilterConf::IsMilterLine() { | |||||
return (ConfigurationLine.find(SmtpdMilterKeyword) == 0); | |||||
} | |||||
bool | |||||
PostfixMilterConf::ContainsSnfMilterSocketSpec() { | |||||
return (ConfigurationLine.find(SnfMilterSocketSpec) != std::string::npos); | |||||
} |
// \file PostfixMilterConf.hpp | |||||
// | |||||
// Copyright (C) 2011 ARM Research Labs, LLC. | |||||
// See www.armresearch.com for the copyright terms. | |||||
// | |||||
// This file defines the PostfixMilterConf interface. | |||||
// | |||||
// $Id$ | |||||
// | |||||
/////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
#ifndef PostfixMilterConfhpp_included | |||||
#define PostfixMilterConfhpp_included | |||||
#include <string> | |||||
/// Class to update smtpd_milters line in the postfix main.cf file. | |||||
// | |||||
////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
class PostfixMilterConf { | |||||
public: | |||||
/// Load a new configuration line. | |||||
void ConfLine(std::string Line); | |||||
/// Check whether the configuration line indicates that SNFMilter is integrated. | |||||
// | |||||
// \returns true if the line indicates integration, false otherwise. | |||||
// | |||||
bool IsIntegrated(); | |||||
/// Update the configuration line to integrate with SNFMilter. | |||||
void AddIntegration(); | |||||
/// Update the configuration line to remove integration with SNFMilter. | |||||
void RemoveIntegration(); | |||||
/// Get a copy of the configuration line. | |||||
std::string ConfLine(); | |||||
/// Check whether the configuration line is a milter specification. | |||||
// | |||||
// \returns true if the configuration line is a milter | |||||
// specification, false otherwise. | |||||
// | |||||
bool IsMilterLine(); | |||||
private: | |||||
/// Check whether the configuration line contains a SNFMilter soecket specification. | |||||
// | |||||
// \returns true if the configuration line contains a SNFMilter | |||||
// socket specification, false otherwise. | |||||
// | |||||
bool ContainsSnfMilterSocketSpec(); | |||||
/// Configuration line. | |||||
std::string ConfigurationLine; | |||||
}; | |||||
#endif |