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
@@ -25,11 +25,13 @@ SNFMilterConfig_SOURCES = \ | |||
@top_srcdir@/SNFUtility/SNFMilterConfig/main.cpp \ | |||
@top_srcdir@/SNFUtility/SNFMilterConfig/SNFMilterConfig.cpp \ | |||
@top_srcdir@/SNFUtility/SNFMilterConfig/PostfixIntegrate.cpp \ | |||
@top_srcdir@/SNFUtility/SNFMilterConfig/PostfixMilterConf.cpp \ | |||
@top_srcdir@/SNFUtility/SNFMilterConfig/SendmailIntegrate.cpp | |||
noinst_HEADERS = \ | |||
@top_srcdir@/SNFUtility/SNFMilterConfig/SNFMilterConfig.hpp \ | |||
@top_srcdir@/SNFUtility/SNFMilterConfig/PostfixIntegrate.hpp \ | |||
@top_srcdir@/SNFUtility/SNFMilterConfig/PostfixMilterConf.hpp \ | |||
@top_srcdir@/SNFUtility/SNFMilterConfig/SendmailIntegrate.hpp | |||
EXTRA_DIST = \ |
@@ -20,6 +20,7 @@ | |||
#include <fstream> | |||
#include "PostfixIntegrate.hpp" | |||
#include "PostfixMilterConf.hpp" | |||
////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
// Configuration. //////////////////////////////////////////////////////////////////////////////////////// | |||
@@ -27,7 +28,7 @@ | |||
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. ///////////////////////////////////////////////////////////////////////////////// | |||
@@ -90,10 +91,11 @@ PostfixIntegrate::Integrate(FileBackup *SaveFile) { | |||
} | |||
std::ifstream Input; | |||
if (Verbose()) { | |||
std::cout << "Add to postfix file " << PostfixMainCfPath << ": '" | |||
<< SnfMilterMainCfIntegrationString << "'..."; | |||
std::cout << "Integrate with postfix...\n"; | |||
} | |||
@@ -101,38 +103,116 @@ PostfixIntegrate::Integrate(FileBackup *SaveFile) { | |||
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; | |||
Temp = "Error opening the postfix configuration file " + PostfixMainCfPath; | |||
Temp += " for writing: "; | |||
Temp += " for reading: "; | |||
Temp += strerror(errno); | |||
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; | |||
Temp = "Error appending to the postfix configuration file " + PostfixMainCfPath; | |||
Temp = "Error reading the postfix configuration file " + PostfixMainCfPath; | |||
Temp += ": "; | |||
Temp += strerror(errno); | |||
throw std::runtime_error(Temp); | |||
} | |||
Output.close(); | |||
if (!Output) { | |||
Input.close(); | |||
if (Input.bad()) { | |||
std::string Temp; | |||
Temp = "Error closing the postfix configuration file " + PostfixMainCfPath; | |||
Temp += " after appending: "; | |||
Temp += " after reading: "; | |||
Temp += strerror(errno); | |||
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(); | |||
@@ -180,16 +260,25 @@ PostfixIntegrate::Unintegrate(FileBackup *SaveFile) { | |||
std::string Content; | |||
std::string Line; | |||
PostfixMilterConf MilterConf; // Object to update the config 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()) { | |||
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; | |||
} | |||
@@ -218,7 +307,7 @@ PostfixIntegrate::Unintegrate(FileBackup *SaveFile) { | |||
if (!Explain()) { | |||
std::ofstream Output; // Write the updated contents. | |||
std::ofstream Output; // Write the updated contents. | |||
Output.open(PostfixMainCfPath.c_str(), std::ios::trunc); | |||
if (!Output) { | |||
@@ -363,11 +452,14 @@ PostfixIntegrate::IsIntegrated() { | |||
throw std::runtime_error(Temp); | |||
} | |||
PostfixMilterConf MilterConf; // Object to update the config line. | |||
std::string 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. | |||
@@ -0,0 +1,246 @@ | |||
// /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); | |||
} |
@@ -0,0 +1,63 @@ | |||
// \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 |