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.

SendmailIntegrate.cpp 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. // /file SendmailIntegrate.cpp
  2. //
  3. // Copyright (C) 2013, ARM Research Labs, LLC.
  4. // See www.armresearch.com for the copyright terms.
  5. //
  6. // This file contains the functions for SendmailIntegrate.
  7. //
  8. // $Id$
  9. //
  10. ///////////////////////////////////////////////////////////////////////////////////////////////////
  11. #include <cstdlib>
  12. #include <cerrno>
  13. #include <cstring>
  14. #include <iostream>
  15. #include <exception>
  16. #include <stdexcept>
  17. #include <sstream>
  18. #include <fstream>
  19. #include "SendmailIntegrate.hpp"
  20. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  21. // Configuration. ////////////////////////////////////////////////////////////////////////////////////////
  22. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  23. const std::string SendmailLdaKey("FEATURE(`local_procmail'"); ///< Line in sendmail.cf that specifies the LDA.
  24. const std::string MtaIsRunningCommand("ps axl | grep -v grep | grep -q ' sendmail: '");
  25. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  26. // End of configuration. /////////////////////////////////////////////////////////////////////////////////
  27. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  28. void
  29. SendmailIntegrate::SetOperatingSystem(std::string OperatingSystemType) {
  30. ProcmailRcFileName = "/etc/procmailrc";
  31. if ("OpenBSD" == OperatingSystemType) {
  32. IntegrationIsSupported = false;
  33. } else if ("FreeBSD" == OperatingSystemType) {
  34. IntegrationIsSupported = false;
  35. } else if ("Ubuntu" == OperatingSystemType) {
  36. IntegrationIsSupported = true;
  37. SendmailSendmailMcPath = "/etc/mail/sendmail.mc";
  38. SendmailSendmailCfPath = "/etc/mail/sendmail.cf";
  39. SnfSnifferDirName = "/var/spool/postfix/snf-server";
  40. SnfSnifferFileName = SnfSnifferDirName + "/snfSnifferFilter";
  41. SnfSnifferSampleFileName = "/usr/sbin/snfSnifferFilter.sample";
  42. BuildInstallSendmailCfFile = "(cd /etc/mail && make)";
  43. ReloadMtaCommand = "/etc/init.d/sendmail reload";
  44. FileToBackup.push_back(SendmailSendmailMcPath);
  45. FileToBackup.push_back(SendmailSendmailCfPath);
  46. } else if ("RedHat" == OperatingSystemType) {
  47. IntegrationIsSupported = true;
  48. SendmailSendmailMcPath = "/etc/mail/sendmail.mc";
  49. SendmailSendmailCfPath = "/etc/mail/sendmail.cf";
  50. SnfSnifferDirName = "/usr/sbin";
  51. SnfSnifferFileName = SnfSnifferDirName + "/snfSnifferFilter";
  52. SnfSnifferSampleFileName = SnfSnifferDirName + "/snfSnifferFilter.sample";
  53. BuildInstallSendmailCfFile = "(cd /etc/mail && make)";
  54. ReloadMtaCommand = "/etc/init.d/sendmail reload";
  55. FileToBackup.push_back(SendmailSendmailMcPath);
  56. FileToBackup.push_back(SendmailSendmailCfPath);
  57. } else if ("Suse" == OperatingSystemType) {
  58. IntegrationIsSupported = true;
  59. SendmailSendmailMcPath = "/etc/mail/linux.mc";
  60. SendmailSendmailCfPath = "/etc/mail/sendmail.cf";
  61. SnfSnifferDirName = "/usr/sbin";
  62. SnfSnifferFileName = SnfSnifferDirName + "/snfSnifferFilter";
  63. SnfSnifferSampleFileName = SnfSnifferDirName + "/snfSnifferFilter.sample";
  64. BuildInstallSendmailCfFile = "(cd /etc/mail && rm -f sendmail.cf && m4 /etc/mail/linux.mc > sendmail.cf)";
  65. ReloadMtaCommand = "/etc/init.d/sendmail reload";
  66. FileToBackup.push_back(SendmailSendmailMcPath);
  67. FileToBackup.push_back(SendmailSendmailCfPath);
  68. } else {
  69. std::ostringstream Temp;
  70. Temp << "***Error from SendmailIntegrate::SetOperatingSystem: Invalid value of OperatingSystemType: "
  71. << OperatingSystemType;
  72. throw std::runtime_error(Temp.str());
  73. }
  74. ProcmailRcSnifferIntegration = ":0 fw\n| " + SnfSnifferFileName;
  75. ProcmailRcSnifferIntegration += "\n";
  76. }
  77. void
  78. SendmailIntegrate::Integrate(FileBackup *SaveFile) {
  79. if (!IntegrationIsSupported) {
  80. return;
  81. }
  82. if (IsIntegrated()) {
  83. return;
  84. }
  85. // Check whether the configuration has procmail as the LDA.
  86. if (!MtaConfigurationHasProcmailForLda()) {
  87. std::string Temp;
  88. Temp = "Error--sendmail must be configured to use procmail as the LDA.";
  89. throw std::runtime_error(Temp);
  90. }
  91. if (Verbose()) {
  92. std::cout << "Create " << SnfSnifferFileName << " if it doesn't exist...";
  93. }
  94. if (!Explain()) {
  95. if (!FileExists(SnfSnifferFileName)) { // Create SnfSnifferFilter script
  96. // if it doesn't exist.
  97. SaveFile->CreateBackupFile(SnfSnifferFileName);
  98. if (!FileExists(SnfSnifferDirName)) {
  99. MkDir(SnfSnifferDirName);
  100. }
  101. SetMode(SnfSnifferDirName, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
  102. SetOwnerGroup(SnfSnifferDirName);
  103. Copy(SnfSnifferSampleFileName, SnfSnifferFileName);
  104. SetMode(SnfSnifferFileName, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
  105. SetOwnerGroup(SnfSnifferFileName);
  106. }
  107. }
  108. OutputVerboseEnd();
  109. if (Verbose()) {
  110. std::cout << "Add\n\n" << ProcmailRcSnifferIntegration << "\n\nto " << ProcmailRcFileName << "...";
  111. }
  112. std::string ProcmailRcFileContent;
  113. if (!Explain()) {
  114. if (FileExists(ProcmailRcFileName)) { // Read any existing procmail configuration.
  115. std::ifstream Input;
  116. Input.open(ProcmailRcFileName.c_str());
  117. if (!Input) {
  118. std::string Temp;
  119. Temp = "Error opening the procmail configuration file " + ProcmailRcFileName;
  120. Temp += " for reading: ";
  121. Temp += strerror(errno);
  122. throw std::runtime_error(Temp);
  123. }
  124. if (!Input.eof()) {
  125. std::ostringstream Buffer;
  126. Buffer << Input.rdbuf();
  127. ProcmailRcFileContent = Buffer.str();
  128. }
  129. Input.close();
  130. if (!Input) {
  131. std::string Temp;
  132. Temp = "Error closing the procmail configuration file " + ProcmailRcFileName;
  133. Temp += " after reading: ";
  134. Temp += strerror(errno);
  135. throw std::runtime_error(Temp);
  136. }
  137. }
  138. ProcmailRcFileContent = ProcmailRcSnifferIntegration + ProcmailRcFileContent;
  139. SaveFile->CreateBackupFile(ProcmailRcFileName);
  140. std::ofstream Output; // Write the updated contents.
  141. Output.open(ProcmailRcFileName.c_str(), std::ios::trunc);
  142. if (!Output) {
  143. std::string Temp;
  144. Temp = "Error opening the procmail configuration file " + ProcmailRcFileName;
  145. Temp += " for writing: ";
  146. Temp += strerror(errno);
  147. throw std::runtime_error(Temp);
  148. }
  149. Output << ProcmailRcFileContent;
  150. if (!Output) {
  151. std::string Temp;
  152. Temp = "Error writing the procmail configuration file " + ProcmailRcFileName;
  153. Temp += ": ";
  154. Temp += strerror(errno);
  155. throw std::runtime_error(Temp);
  156. }
  157. Output.close();
  158. if (!Output) {
  159. std::string Temp;
  160. Temp = "Error closing the procmail configuration file " + ProcmailRcFileName;
  161. Temp += " after writing: ";
  162. Temp += strerror(errno);
  163. throw std::runtime_error(Temp);
  164. }
  165. }
  166. OutputVerboseEnd();
  167. if (!ReloadMta()) {
  168. std::cerr << "Unable to reload the sendmail configuration. Please reload "
  169. << " the sendmail configuration for the integration with SNFServer to take effect.";
  170. }
  171. }
  172. void
  173. SendmailIntegrate::Unintegrate(FileBackup *SaveFile) {
  174. if (!IntegrationIsSupported) {
  175. return;
  176. }
  177. if (!IsIntegrated()) {
  178. return;
  179. }
  180. std::ifstream Input;
  181. if (Verbose()) {
  182. std::cout << "Remove integration in procmail file " << ProcmailRcFileName << "--\n";
  183. }
  184. if (!Explain()) {
  185. Input.open(ProcmailRcFileName.c_str());
  186. if (!Input) {
  187. std::string Temp;
  188. Temp = "Error opening the procmail configuration file " + ProcmailRcFileName;
  189. Temp += " for reading: ";
  190. Temp += strerror(errno);
  191. throw std::runtime_error(Temp);
  192. }
  193. std::ostringstream ContentStream;
  194. ContentStream << Input.rdbuf();
  195. Input.close();
  196. if (!Input) {
  197. std::string Temp;
  198. Temp = "Error closing the procmail configuration file " + ProcmailRcFileName;
  199. Temp += ": ";
  200. Temp += strerror(errno);
  201. throw std::runtime_error(Temp);
  202. }
  203. std::string Content;
  204. Content = ContentStream.str();
  205. if (Verbose()) {
  206. std::cout << " Remove all occurances of\n\n" << ProcmailRcSnifferIntegration << "\n\n"
  207. << " from" << ProcmailRcFileName << "...\n";
  208. }
  209. std::string::size_type IntegrationBegin = std::string::npos;
  210. while ((IntegrationBegin = Content.find(ProcmailRcSnifferIntegration)) != std::string::npos) {
  211. Content.erase(IntegrationBegin, ProcmailRcSnifferIntegration.length());
  212. }
  213. SaveFile->CreateBackupFile(ProcmailRcFileName);
  214. std::ofstream Output; // Write the updated contents.
  215. Output.open(ProcmailRcFileName.c_str(), std::ios::trunc);
  216. if (!Output) {
  217. std::string Temp;
  218. Temp = "Error opening the procmail configuration file " + ProcmailRcFileName;
  219. Temp += " for writing: ";
  220. Temp += strerror(errno);
  221. throw std::runtime_error(Temp);
  222. }
  223. Output << Content;
  224. if (!Output) {
  225. std::string Temp;
  226. Temp = "Error writing the procmail configuration file " + ProcmailRcFileName;
  227. Temp += ": ";
  228. Temp += strerror(errno);
  229. throw std::runtime_error(Temp);
  230. }
  231. Output.close();
  232. if (!Output) {
  233. std::string Temp;
  234. Temp = "Error closing the procmail configuration file " + ProcmailRcFileName;
  235. Temp += " after writing: ";
  236. Temp += strerror(errno);
  237. throw std::runtime_error(Temp);
  238. }
  239. }
  240. OutputVerboseEnd();
  241. if (!ReloadMta()) {
  242. std::cerr << "Unable to reload the sendmail configuration. Please run "
  243. << "'sendmail reload' for the integration with SNFServer to take effect.";
  244. }
  245. }
  246. bool
  247. SendmailIntegrate::MtaIsRunningDetected() {
  248. if (Verbose()) {
  249. std::cout << "Checking whether sendmail is detected to be running...";
  250. }
  251. bool IsRunningDetected;
  252. IsRunningDetected = (std::system(MtaIsRunningCommand.c_str()) == 0);
  253. if (Verbose()) {
  254. std::cout << (IsRunningDetected ? "yes..." : "no...");
  255. }
  256. OutputVerboseEnd();
  257. return IsRunningDetected;
  258. }
  259. bool
  260. SendmailIntegrate::ReloadMta() {
  261. if (!MtaIsRunningDetected()) {
  262. return true;
  263. }
  264. if (Verbose()) {
  265. std::cout << "Reloading sendmail with the command '"
  266. << ReloadMtaCommand << "'...\n";
  267. std::cout.flush();
  268. }
  269. bool Succeeded;
  270. if (!Explain()) {
  271. Succeeded = (std::system(ReloadMtaCommand.c_str()) == 0);
  272. if (Verbose()) {
  273. std::cout << (Succeeded ? "succeeded..." : "failed...");
  274. }
  275. }
  276. OutputVerboseEnd();
  277. return Succeeded;
  278. }
  279. bool
  280. SendmailIntegrate::IsIntegrated() {
  281. if (Verbose()) {
  282. std::cout << "Checking for any SNFServer integration in the procmail file " << ProcmailRcFileName << "...";
  283. }
  284. if (!FileExists(ProcmailRcFileName)) {
  285. if (Verbose()) {
  286. std::cout << "file doesn't exist; sendmail is not integrated...";
  287. }
  288. OutputVerboseEnd();
  289. return false;
  290. }
  291. bool Integrated = false;
  292. std::ifstream Input;
  293. Input.open(ProcmailRcFileName.c_str()); // Read the contents.
  294. if (!Input) {
  295. std::string Temp;
  296. Temp = "Error opening the procmail configuration file " + ProcmailRcFileName;
  297. Temp += " for reading: ";
  298. Temp += strerror(errno);
  299. throw std::runtime_error(Temp);
  300. }
  301. std::string ProcmailRcFileContent;
  302. if (!Input.eof()) {
  303. std::ostringstream Buffer;
  304. Buffer << Input.rdbuf();
  305. ProcmailRcFileContent = Buffer.str();
  306. }
  307. Input.close();
  308. if (!Input) {
  309. std::string Temp;
  310. Temp = "Error closing the procmail configuration file " + ProcmailRcFileName;
  311. Temp += " after reading: ";
  312. Temp += strerror(errno);
  313. throw std::runtime_error(Temp);
  314. }
  315. Integrated = (ProcmailRcFileContent.find(ProcmailRcSnifferIntegration) != std::string::npos);
  316. if (Verbose()) {
  317. if (Integrated) {
  318. std::cout << "found\n\n" << ProcmailRcSnifferIntegration << "\n\n...";
  319. } else {
  320. std::cout << "none found...";
  321. }
  322. }
  323. OutputVerboseEnd();
  324. return Integrated;
  325. }
  326. bool
  327. SendmailIntegrate::MtaConfigurationHasProcmailForLda() {
  328. std::string File;
  329. std::ifstream Input;
  330. File = SendmailSendmailMcPath;
  331. if (Verbose()) {
  332. std::cout << "Checking sendmail configuraton file " + File
  333. << " to verify that procmail is the Local Delivery Agent.\n";
  334. }
  335. Input.open(File.c_str());
  336. if (!Input) {
  337. std::string Temp;
  338. Temp = "Error opening sendmail configuration file " + File;
  339. Temp += " for reading: ";
  340. Temp += strerror(errno);
  341. throw std::runtime_error(Temp);
  342. }
  343. std::string Line;
  344. bool LdaIsProcmail = true;
  345. bool FoundLdaLine = false;
  346. while (getline(Input, Line)) {
  347. if (Line.substr(0, SendmailLdaKey.length()) == SendmailLdaKey) { // Check for LDA line.
  348. FoundLdaLine = true;
  349. if (Line.find(",", SendmailLdaKey.length()) != std::string::npos) { // Additional arguments?
  350. if (Line.find("procmail", SendmailLdaKey.length()) == std::string::npos) { // Yes.
  351. LdaIsProcmail = false; // procmail not specified in the config line.
  352. if (Verbose()) {
  353. std::cout << "The following line indicates that the sendmail LDA is not procmail:\n\n"
  354. << Line << "\n";
  355. }
  356. break;
  357. } else { // procmail is specified in the config line.
  358. if (Verbose()) {
  359. std::cout << "The following line indicates that the sendmail LDA is procmail, "
  360. << "as required to integrate with SNFServer:\n\n"
  361. << Line << "\n\n";
  362. }
  363. break;
  364. }
  365. } else { // LDA line uses default, which is procmail.
  366. if (Verbose()) {
  367. std::cout << "The following line indicates that the sendmail LDA is procmail, "
  368. << "as required to integrate with SNFServer:\n\n"
  369. << Line << "\n\n";
  370. }
  371. break;
  372. }
  373. }
  374. }
  375. Input.close();
  376. if (Input.bad()) {
  377. std::string Temp;
  378. Temp = "Error closing the sendmail configuration file " + File;
  379. Temp += " after reading: ";
  380. Temp += strerror(errno);
  381. throw std::runtime_error(Temp);
  382. }
  383. if (Verbose() && !FoundLdaLine) {
  384. std::cout << "The absence of \"" << SendmailLdaKey << "\" indicates that the LDA is procmail, "
  385. << "as required to integrate with SNFServer.\n";
  386. }
  387. return LdaIsProcmail;
  388. }