You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PostfixMilterConf.cpp 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. // /file PostfixMilterConf.cpp
  2. //
  3. // Copyright (C) 2011, ARM Research Labs, LLC.
  4. // See www.armresearch.com for the copyright terms.
  5. //
  6. // This file contains the functions for PostfixMilterConf.
  7. //
  8. // $Id$
  9. //
  10. ///////////////////////////////////////////////////////////////////////////////////////////////////
  11. #include <exception>
  12. #include <stdexcept>
  13. #include <sstream>
  14. #include "Utility.hpp"
  15. #include "PostfixMilterConf.hpp"
  16. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  17. // Configuration. ////////////////////////////////////////////////////////////////////////////////////////
  18. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  19. /// SNFMilter socket specification.
  20. const std::string SnfMilterSocketSpec("unix:/var/snf-milter/socket");
  21. /// Milter keyword in the postfix main.cf file.
  22. const std::string SmtpdMilterKeyword("smtpd_milters");
  23. const std::string Whitespace(", \t");
  24. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  25. // End of configuration. /////////////////////////////////////////////////////////////////////////////////
  26. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  27. void
  28. PostfixMilterConf::ConfLine(std::string Line) {
  29. ConfigurationLine = Utility::Trim(Line); // Remove leading and trailing whitespace.
  30. }
  31. bool
  32. PostfixMilterConf::IsIntegrated() {
  33. return (IsMilterLine() && ContainsSnfMilterSocketSpec());
  34. }
  35. void
  36. PostfixMilterConf::AddIntegration() {
  37. if (IsIntegrated()) {
  38. return;
  39. }
  40. if (IsMilterLine()) { // Add to existing milter line.
  41. std::string NewConfLine;
  42. NewConfLine = SmtpdMilterKeyword + " =";
  43. // Skip to "=" in configuration line.
  44. std::string::size_type NextIndex;
  45. NextIndex = ConfigurationLine.find("=");
  46. if (std::string::npos == NextIndex) { // Check format.
  47. std::ostringstream Temp;
  48. Temp << "Error processing postfix main.cf file smtpd_milters line: '"
  49. << ConfigurationLine << "'";
  50. throw std::runtime_error(Temp.str());
  51. }
  52. std::string ExistingMilters;
  53. ExistingMilters = Utility::Trim(ConfigurationLine.substr(NextIndex + 1)); // Should contain existing milters.
  54. while (std::string::npos != NextIndex) {
  55. NewConfLine += " ";
  56. std::string NextMilter;
  57. NextIndex = ExistingMilters.find_first_of(Whitespace);
  58. NextMilter = Utility::Trim(ExistingMilters.substr(0, NextIndex));
  59. ExistingMilters = Utility::Trim(ExistingMilters.substr(NextIndex + 1));
  60. if (NextMilter == "") {
  61. std::ostringstream Temp;
  62. Temp << "Error processing postfix main.cf file smtpd_milters line: '"
  63. << ConfigurationLine << "'";
  64. throw std::runtime_error(Temp.str());
  65. }
  66. NewConfLine += NextMilter;
  67. }
  68. ConfigurationLine = NewConfLine + " ";
  69. ConfigurationLine += SnfMilterSocketSpec;
  70. } else if (ConfigurationLine == "") { // Empty configuration line.
  71. ConfigurationLine = SmtpdMilterKeyword + " = ";
  72. ConfigurationLine += SnfMilterSocketSpec;
  73. } else { // Unexpected non-empty line.
  74. std::ostringstream Temp;
  75. Temp << "Internal error: Attempted to modify a line in main.cf that does not begin with '"
  76. << SmtpdMilterKeyword << "'";
  77. throw std::runtime_error(Temp.str());
  78. }
  79. }
  80. void
  81. PostfixMilterConf::RemoveIntegration() {
  82. if (!IsIntegrated()) {
  83. return;
  84. }
  85. if (IsMilterLine()) { // Remove from an existing milter line.
  86. std::string NewConfLine;
  87. NewConfLine = SmtpdMilterKeyword + " =";
  88. // Skip to "=" in configuration line.
  89. std::string::size_type NextIndex;
  90. NextIndex = ConfigurationLine.find("=");
  91. if (std::string::npos == NextIndex) { // Check format.
  92. std::ostringstream Temp;
  93. Temp << "Error processing postfix main.cf file smtpd_milters line: '"
  94. << ConfigurationLine << "'";
  95. throw std::runtime_error(Temp.str());
  96. }
  97. std::string ExistingMilters;
  98. bool AddedMilter = false;
  99. ExistingMilters = Utility::Trim(ConfigurationLine.substr(NextIndex + 1)); // Should contain existing milters.
  100. while (ExistingMilters != "") {
  101. NewConfLine += " ";
  102. std::string NextMilter;
  103. NextIndex = ExistingMilters.find_first_of(" ,");
  104. if (std::string::npos == NextIndex) {
  105. NextMilter = ExistingMilters;
  106. ExistingMilters = "";
  107. } else {
  108. NextMilter = Utility::Trim(ExistingMilters.substr(0, NextIndex));
  109. ExistingMilters = Utility::Trim(ExistingMilters.substr(NextIndex + 1));
  110. }
  111. if (NextMilter == "") {
  112. std::ostringstream Temp;
  113. Temp << "Error processing postfix main.cf file smtpd_milters line: '"
  114. << ConfigurationLine << "'";
  115. throw std::runtime_error(Temp.str());
  116. }
  117. if (NextMilter != SnfMilterSocketSpec) { // Copy if not for SNFMilter.
  118. NewConfLine += NextMilter;
  119. AddedMilter = true;
  120. }
  121. }
  122. if (AddedMilter) {
  123. ConfigurationLine = NewConfLine;
  124. } else {
  125. ConfigurationLine = "";
  126. }
  127. } else { // Unexpected non-empty line.
  128. std::ostringstream Temp;
  129. Temp << "Internal error: Attempted to modify a line in main.cf that does not begin with '"
  130. << SmtpdMilterKeyword << "'";
  131. throw std::runtime_error(Temp.str());
  132. }
  133. }
  134. std::string
  135. PostfixMilterConf::ConfLine() {
  136. return ConfigurationLine;
  137. }
  138. bool
  139. PostfixMilterConf::IsMilterLine() {
  140. bool StartsWithKeyword;
  141. bool KeywordEndsCorrectly;
  142. char NextChar;
  143. StartsWithKeyword = (ConfigurationLine.find(SmtpdMilterKeyword) == 0);
  144. NextChar = ConfigurationLine[SmtpdMilterKeyword.length()];
  145. KeywordEndsCorrectly =
  146. (' ' == NextChar) ||
  147. ('\t' == NextChar) ||
  148. ('=' == NextChar);
  149. return (StartsWithKeyword && KeywordEndsCorrectly);
  150. }
  151. bool
  152. PostfixMilterConf::ContainsSnfMilterSocketSpec() {
  153. std::string::size_type SpecIndex;
  154. SpecIndex = ConfigurationLine.find("=");
  155. if (std::string::npos == SpecIndex) {
  156. return false;
  157. }
  158. SpecIndex++;
  159. while (SpecIndex < ConfigurationLine.length()) {
  160. SpecIndex = ConfigurationLine.find_first_not_of(Whitespace, SpecIndex); // Find next specification.
  161. if (ConfigurationLine.substr(SpecIndex, SnfMilterSocketSpec.length()) == SnfMilterSocketSpec) {
  162. SpecIndex += SnfMilterSocketSpec.length(); // Skip to after the specification.
  163. if (SpecIndex >= ConfigurationLine.length()) {
  164. return true; // No characters after the specification.
  165. }
  166. if ( (ConfigurationLine[SpecIndex] == ' ') ||
  167. (ConfigurationLine[SpecIndex] == '\t') ||
  168. (ConfigurationLine[SpecIndex] == ',') ) {
  169. return true; // Found specification.
  170. }
  171. }
  172. SpecIndex = ConfigurationLine.find_first_of(Whitespace, SpecIndex); // Find next specification.
  173. }
  174. return false;
  175. }