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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  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 = "/usr/sbin";
  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. std::ostringstream Temp;
  81. Temp << "Integration with sendmail is not supported on this platform. "
  82. << "Please see " << DOC_DIR << "/INSTALL for instructions for manual "
  83. << "integration with sendmail.";
  84. throw std::runtime_error(Temp.str());
  85. }
  86. if (IsIntegrated()) {
  87. return;
  88. }
  89. // Check whether the configuration has procmail as the LDA.
  90. if (!MtaConfigurationHasProcmailForLda()) {
  91. std::string Temp;
  92. Temp = "Error--sendmail must be configured to use procmail as the LDA.";
  93. throw std::runtime_error(Temp);
  94. }
  95. if (Verbose()) {
  96. std::cout << "Create " << SnfSnifferFileName << " if it doesn't exist...";
  97. }
  98. if (!Explain()) {
  99. if (!FileExists(SnfSnifferFileName)) { // Create SnfSnifferFilter script
  100. // if it doesn't exist.
  101. SaveFile->CreateBackupFile(SnfSnifferFileName);
  102. if (!FileExists(SnfSnifferDirName)) {
  103. MkDir(SnfSnifferDirName);
  104. SetMode(SnfSnifferDirName, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
  105. SetOwnerGroup(SnfSnifferDirName);
  106. }
  107. Copy(SnfSnifferSampleFileName, SnfSnifferFileName);
  108. SetMode(SnfSnifferFileName, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
  109. SetOwnerGroup(SnfSnifferFileName);
  110. }
  111. }
  112. OutputVerboseEnd();
  113. if (Verbose()) {
  114. std::cout << "Add\n\n" << ProcmailRcSnifferIntegration << "\n\nto " << ProcmailRcFileName << "...";
  115. }
  116. std::string ProcmailRcFileContent;
  117. if (!Explain()) {
  118. if (FileExists(ProcmailRcFileName)) { // Read any existing procmail configuration.
  119. std::ifstream Input;
  120. Input.open(ProcmailRcFileName.c_str());
  121. if (!Input) {
  122. std::string Temp;
  123. Temp = "Error opening the procmail configuration file " + ProcmailRcFileName;
  124. Temp += " for reading: ";
  125. Temp += strerror(errno);
  126. throw std::runtime_error(Temp);
  127. }
  128. if (!Input.eof()) {
  129. std::ostringstream Buffer;
  130. Buffer << Input.rdbuf();
  131. ProcmailRcFileContent = Buffer.str();
  132. }
  133. Input.close();
  134. if (!Input) {
  135. std::string Temp;
  136. Temp = "Error closing the procmail configuration file " + ProcmailRcFileName;
  137. Temp += " after reading: ";
  138. Temp += strerror(errno);
  139. throw std::runtime_error(Temp);
  140. }
  141. }
  142. ProcmailRcFileContent = ProcmailRcSnifferIntegration + ProcmailRcFileContent;
  143. SaveFile->CreateBackupFile(ProcmailRcFileName);
  144. std::ofstream Output; // Write the updated contents.
  145. Output.open(ProcmailRcFileName.c_str(), std::ios::trunc);
  146. if (!Output) {
  147. std::string Temp;
  148. Temp = "Error opening the procmail configuration file " + ProcmailRcFileName;
  149. Temp += " for writing: ";
  150. Temp += strerror(errno);
  151. throw std::runtime_error(Temp);
  152. }
  153. Output << ProcmailRcFileContent;
  154. if (!Output) {
  155. std::string Temp;
  156. Temp = "Error writing the procmail configuration file " + ProcmailRcFileName;
  157. Temp += ": ";
  158. Temp += strerror(errno);
  159. throw std::runtime_error(Temp);
  160. }
  161. Output.close();
  162. if (!Output) {
  163. std::string Temp;
  164. Temp = "Error closing the procmail configuration file " + ProcmailRcFileName;
  165. Temp += " after writing: ";
  166. Temp += strerror(errno);
  167. throw std::runtime_error(Temp);
  168. }
  169. }
  170. OutputVerboseEnd();
  171. if (!ReloadMta()) {
  172. std::cerr << "Unable to reload the sendmail configuration. Please reload "
  173. << " the sendmail configuration for the integration with SNFServer to take effect.";
  174. }
  175. }
  176. void
  177. SendmailIntegrate::Unintegrate(FileBackup *SaveFile) {
  178. if (!IntegrationIsSupported) {
  179. return;
  180. }
  181. if (!IsIntegrated()) {
  182. return;
  183. }
  184. std::ifstream Input;
  185. if (Verbose()) {
  186. std::cout << "Remove integration in procmail file " << ProcmailRcFileName << "--\n";
  187. }
  188. if (!Explain()) {
  189. Input.open(ProcmailRcFileName.c_str());
  190. if (!Input) {
  191. std::string Temp;
  192. Temp = "Error opening the procmail configuration file " + ProcmailRcFileName;
  193. Temp += " for reading: ";
  194. Temp += strerror(errno);
  195. throw std::runtime_error(Temp);
  196. }
  197. std::ostringstream ContentStream;
  198. ContentStream << Input.rdbuf();
  199. Input.close();
  200. if (!Input) {
  201. std::string Temp;
  202. Temp = "Error closing the procmail configuration file " + ProcmailRcFileName;
  203. Temp += ": ";
  204. Temp += strerror(errno);
  205. throw std::runtime_error(Temp);
  206. }
  207. std::string Content;
  208. Content = ContentStream.str();
  209. if (Verbose()) {
  210. std::cout << " Remove all occurances of\n\n" << ProcmailRcSnifferIntegration << "\n\n"
  211. << " from" << ProcmailRcFileName << "...\n";
  212. }
  213. std::string::size_type IntegrationBegin = std::string::npos;
  214. while ((IntegrationBegin = Content.find(ProcmailRcSnifferIntegration)) != std::string::npos) {
  215. Content.erase(IntegrationBegin, ProcmailRcSnifferIntegration.length());
  216. }
  217. SaveFile->CreateBackupFile(ProcmailRcFileName);
  218. std::ofstream Output; // Write the updated contents.
  219. Output.open(ProcmailRcFileName.c_str(), std::ios::trunc);
  220. if (!Output) {
  221. std::string Temp;
  222. Temp = "Error opening the procmail configuration file " + ProcmailRcFileName;
  223. Temp += " for writing: ";
  224. Temp += strerror(errno);
  225. throw std::runtime_error(Temp);
  226. }
  227. Output << Content;
  228. if (!Output) {
  229. std::string Temp;
  230. Temp = "Error writing the procmail configuration file " + ProcmailRcFileName;
  231. Temp += ": ";
  232. Temp += strerror(errno);
  233. throw std::runtime_error(Temp);
  234. }
  235. Output.close();
  236. if (!Output) {
  237. std::string Temp;
  238. Temp = "Error closing the procmail configuration file " + ProcmailRcFileName;
  239. Temp += " after writing: ";
  240. Temp += strerror(errno);
  241. throw std::runtime_error(Temp);
  242. }
  243. }
  244. OutputVerboseEnd();
  245. if (!ReloadMta()) {
  246. std::cerr << "Unable to reload the sendmail configuration. Please run "
  247. << "'sendmail reload' for the integration with SNFServer to take effect.";
  248. }
  249. }
  250. bool
  251. SendmailIntegrate::MtaIsRunningDetected() {
  252. if (Verbose()) {
  253. std::cout << "Checking whether sendmail is detected to be running...";
  254. }
  255. bool IsRunningDetected;
  256. IsRunningDetected = (std::system(MtaIsRunningCommand.c_str()) == 0);
  257. if (Verbose()) {
  258. std::cout << (IsRunningDetected ? "yes..." : "no...");
  259. }
  260. OutputVerboseEnd();
  261. return IsRunningDetected;
  262. }
  263. bool
  264. SendmailIntegrate::ReloadMta() {
  265. if (!MtaIsRunningDetected()) {
  266. return true;
  267. }
  268. if (Verbose()) {
  269. std::cout << "Reloading sendmail with the command '"
  270. << ReloadMtaCommand << "'...\n";
  271. std::cout.flush();
  272. }
  273. bool Succeeded;
  274. if (!Explain()) {
  275. Succeeded = (std::system(ReloadMtaCommand.c_str()) == 0);
  276. if (Verbose()) {
  277. std::cout << (Succeeded ? "succeeded..." : "failed...");
  278. }
  279. }
  280. OutputVerboseEnd();
  281. return Succeeded;
  282. }
  283. bool
  284. SendmailIntegrate::IsIntegrated() {
  285. if (Verbose()) {
  286. std::cout << "Checking for any SNFServer integration in the procmail file " << ProcmailRcFileName << "...";
  287. }
  288. if (!FileExists(ProcmailRcFileName)) {
  289. if (Verbose()) {
  290. std::cout << "file doesn't exist; sendmail is not integrated...";
  291. }
  292. OutputVerboseEnd();
  293. return false;
  294. }
  295. bool Integrated = false;
  296. std::ifstream Input;
  297. Input.open(ProcmailRcFileName.c_str()); // Read the contents.
  298. if (!Input) {
  299. std::string Temp;
  300. Temp = "Error opening the procmail configuration file " + ProcmailRcFileName;
  301. Temp += " for reading: ";
  302. Temp += strerror(errno);
  303. throw std::runtime_error(Temp);
  304. }
  305. std::string ProcmailRcFileContent;
  306. if (!Input.eof()) {
  307. std::ostringstream Buffer;
  308. Buffer << Input.rdbuf();
  309. ProcmailRcFileContent = Buffer.str();
  310. }
  311. Input.close();
  312. if (!Input) {
  313. std::string Temp;
  314. Temp = "Error closing the procmail configuration file " + ProcmailRcFileName;
  315. Temp += " after reading: ";
  316. Temp += strerror(errno);
  317. throw std::runtime_error(Temp);
  318. }
  319. Integrated = (ProcmailRcFileContent.find(ProcmailRcSnifferIntegration) != std::string::npos);
  320. if (Verbose()) {
  321. if (Integrated) {
  322. std::cout << "found\n\n" << ProcmailRcSnifferIntegration << "\n\n...";
  323. } else {
  324. std::cout << "none found...";
  325. }
  326. }
  327. OutputVerboseEnd();
  328. return Integrated;
  329. }
  330. bool
  331. SendmailIntegrate::MtaConfigurationHasProcmailForLda() {
  332. std::string File;
  333. std::ifstream Input;
  334. File = SendmailSendmailMcPath;
  335. if (Verbose()) {
  336. std::cout << "Checking sendmail configuraton file " + File
  337. << " to verify that procmail is the Local Delivery Agent.\n";
  338. }
  339. Input.open(File.c_str());
  340. if (!Input) {
  341. std::string Temp;
  342. Temp = "Error opening sendmail configuration file " + File;
  343. Temp += " for reading: ";
  344. Temp += strerror(errno);
  345. throw std::runtime_error(Temp);
  346. }
  347. std::string Line;
  348. bool LdaIsProcmail = true;
  349. bool FoundLdaLine = false;
  350. while (getline(Input, Line)) {
  351. if (Line.substr(0, SendmailLdaKey.length()) == SendmailLdaKey) { // Check for LDA line.
  352. FoundLdaLine = true;
  353. if (Line.find(",", SendmailLdaKey.length()) != std::string::npos) { // Additional arguments?
  354. if (Line.find("procmail", SendmailLdaKey.length()) == std::string::npos) { // Yes.
  355. LdaIsProcmail = false; // procmail not specified in the config line.
  356. if (Verbose()) {
  357. std::cout << "The following line indicates that the sendmail LDA is not procmail:\n\n"
  358. << Line << "\n";
  359. }
  360. break;
  361. } else { // procmail is specified in the config line.
  362. if (Verbose()) {
  363. std::cout << "The following line indicates that the sendmail LDA is procmail, "
  364. << "as required to integrate with SNFServer:\n\n"
  365. << Line << "\n\n";
  366. }
  367. break;
  368. }
  369. } else { // LDA line uses default, which is procmail.
  370. if (Verbose()) {
  371. std::cout << "The following line indicates that the sendmail LDA is procmail, "
  372. << "as required to integrate with SNFServer:\n\n"
  373. << Line << "\n\n";
  374. }
  375. break;
  376. }
  377. }
  378. }
  379. Input.close();
  380. if (Input.bad()) {
  381. std::string Temp;
  382. Temp = "Error closing the sendmail configuration file " + File;
  383. Temp += " after reading: ";
  384. Temp += strerror(errno);
  385. throw std::runtime_error(Temp);
  386. }
  387. if (Verbose() && !FoundLdaLine) {
  388. std::cout << "The absence of \"" << SendmailLdaKey << "\" indicates that the LDA is procmail, "
  389. << "as required to integrate with SNFServer.\n";
  390. }
  391. return LdaIsProcmail;
  392. }