// /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 #include #include #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"); const std::string Whitespace(", \t"); ////////////////////////////////////////////////////////////////////////////////////////////////////////// // 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 (std::string::npos != NextIndex) { NewConfLine += " "; std::string NextMilter; NextIndex = ExistingMilters.find_first_of(Whitespace); 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() { bool StartsWithKeyword; bool KeywordEndsCorrectly; char NextChar; StartsWithKeyword = (ConfigurationLine.find(SmtpdMilterKeyword) == 0); NextChar = ConfigurationLine[SmtpdMilterKeyword.length()]; KeywordEndsCorrectly = (' ' == NextChar) || ('\t' == NextChar) || ('=' == NextChar); return (StartsWithKeyword && KeywordEndsCorrectly); } bool PostfixMilterConf::ContainsSnfMilterSocketSpec() { std::string::size_type SpecIndex; SpecIndex = ConfigurationLine.find("="); if (std::string::npos == SpecIndex) { return false; } SpecIndex++; while (SpecIndex < ConfigurationLine.length()) { SpecIndex = ConfigurationLine.find_first_not_of(Whitespace, SpecIndex); // Find next specification. if (ConfigurationLine.substr(SpecIndex, SnfMilterSocketSpec.length()) == SnfMilterSocketSpec) { SpecIndex += SnfMilterSocketSpec.length(); // Skip to after the specification. if (SpecIndex >= ConfigurationLine.length()) { return true; // No characters after the specification. } if ( (ConfigurationLine[SpecIndex] == ' ') || (ConfigurationLine[SpecIndex] == '\t') || (ConfigurationLine[SpecIndex] == ',') ) { return true; // Found specification. } } SpecIndex = ConfigurationLine.find_first_of(Whitespace, SpecIndex); // Find next specification. } return false; }