// /file SendmailIntegrate.cpp // // Copyright (C) 2013, ARM Research Labs, LLC. // See www.armresearch.com for the copyright terms. // // This file contains the functions for SendmailIntegrate. // // $Id$ // /////////////////////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include "SendmailIntegrate.hpp" ////////////////////////////////////////////////////////////////////////////////////////////////////////// // Configuration. //////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////// const std::string SendmailLdaKey("FEATURE(`local_procmail'"); ///< Line in sendmail.cf that specifies the LDA. const std::string MtaIsRunningCommand("ps axl | grep -v grep | grep -q ' sendmail: '"); ////////////////////////////////////////////////////////////////////////////////////////////////////////// // End of configuration. ///////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////// void SendmailIntegrate::SetOperatingSystem(std::string OperatingSystemType) { ProcmailRcFileName = "/etc/procmailrc"; if ("OpenBSD" == OperatingSystemType) { IntegrationIsSupported = false; } else if ("FreeBSD" == OperatingSystemType) { IntegrationIsSupported = false; } else if ("Ubuntu" == OperatingSystemType) { IntegrationIsSupported = true; SendmailSendmailMcPath = "/etc/mail/sendmail.mc"; SendmailSendmailCfPath = "/etc/mail/sendmail.cf"; SnfSnifferDirName = "/var/spool/postfix/snf-server"; SnfSnifferFileName = SnfSnifferDirName + "/snfSnifferFilter"; SnfSnifferSampleFileName = "/usr/sbin/snfSnifferFilter.sample"; BuildInstallSendmailCfFile = "(cd /etc/mail && make)"; ReloadMtaCommand = "/etc/init.d/sendmail reload"; FileToBackup.push_back(SendmailSendmailMcPath); FileToBackup.push_back(SendmailSendmailCfPath); } else if ("RedHat" == OperatingSystemType) { IntegrationIsSupported = true; SendmailSendmailMcPath = "/etc/mail/sendmail.mc"; SendmailSendmailCfPath = "/etc/mail/sendmail.cf"; SnfSnifferDirName = "/usr/sbin"; SnfSnifferFileName = SnfSnifferDirName + "/snfSnifferFilter"; SnfSnifferSampleFileName = SnfSnifferDirName + "/snfSnifferFilter.sample"; BuildInstallSendmailCfFile = "(cd /etc/mail && make)"; ReloadMtaCommand = "/etc/init.d/sendmail reload"; FileToBackup.push_back(SendmailSendmailMcPath); FileToBackup.push_back(SendmailSendmailCfPath); } else if ("Suse" == OperatingSystemType) { IntegrationIsSupported = true; SendmailSendmailMcPath = "/etc/mail/linux.mc"; SendmailSendmailCfPath = "/etc/mail/sendmail.cf"; SnfSnifferDirName = "/usr/sbin"; SnfSnifferFileName = SnfSnifferDirName + "/snfSnifferFilter"; SnfSnifferSampleFileName = SnfSnifferDirName + "/snfSnifferFilter.sample"; BuildInstallSendmailCfFile = "(cd /etc/mail && rm -f sendmail.cf && m4 /etc/mail/linux.mc > sendmail.cf)"; ReloadMtaCommand = "/etc/init.d/sendmail reload"; FileToBackup.push_back(SendmailSendmailMcPath); FileToBackup.push_back(SendmailSendmailCfPath); } else { std::ostringstream Temp; Temp << "***Error from SendmailIntegrate::SetOperatingSystem: Invalid value of OperatingSystemType: " << OperatingSystemType; throw std::runtime_error(Temp.str()); } ProcmailRcSnifferIntegration = ":0 fw\n| " + SnfSnifferFileName; ProcmailRcSnifferIntegration += "\n"; } void SendmailIntegrate::Integrate(FileBackup *SaveFile) { if (!IntegrationIsSupported) { std::ostringstream Temp; Temp << "Integration with sendmail is not supported on this platform. " << "Please see " << DOC_DIR << "/INSTALL for instructions for manual " << "integration with sendmail."; throw std::runtime_error(Temp.str()); } if (IsIntegrated()) { return; } // Check whether the configuration has procmail as the LDA. if (!MtaConfigurationHasProcmailForLda()) { std::string Temp; Temp = "Error--sendmail must be configured to use procmail as the LDA."; throw std::runtime_error(Temp); } if (Verbose()) { std::cout << "Create " << SnfSnifferFileName << " if it doesn't exist..."; } if (!Explain()) { if (!FileExists(SnfSnifferFileName)) { // Create SnfSnifferFilter script // if it doesn't exist. SaveFile->CreateBackupFile(SnfSnifferFileName); if (!FileExists(SnfSnifferDirName)) { MkDir(SnfSnifferDirName); } SetMode(SnfSnifferDirName, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); SetOwnerGroup(SnfSnifferDirName); Copy(SnfSnifferSampleFileName, SnfSnifferFileName); SetMode(SnfSnifferFileName, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); SetOwnerGroup(SnfSnifferFileName); } } OutputVerboseEnd(); if (Verbose()) { std::cout << "Add\n\n" << ProcmailRcSnifferIntegration << "\n\nto " << ProcmailRcFileName << "..."; } std::string ProcmailRcFileContent; if (!Explain()) { if (FileExists(ProcmailRcFileName)) { // Read any existing procmail configuration. std::ifstream Input; Input.open(ProcmailRcFileName.c_str()); if (!Input) { std::string Temp; Temp = "Error opening the procmail configuration file " + ProcmailRcFileName; Temp += " for reading: "; Temp += strerror(errno); throw std::runtime_error(Temp); } if (!Input.eof()) { std::ostringstream Buffer; Buffer << Input.rdbuf(); ProcmailRcFileContent = Buffer.str(); } Input.close(); if (!Input) { std::string Temp; Temp = "Error closing the procmail configuration file " + ProcmailRcFileName; Temp += " after reading: "; Temp += strerror(errno); throw std::runtime_error(Temp); } } ProcmailRcFileContent = ProcmailRcSnifferIntegration + ProcmailRcFileContent; SaveFile->CreateBackupFile(ProcmailRcFileName); std::ofstream Output; // Write the updated contents. Output.open(ProcmailRcFileName.c_str(), std::ios::trunc); if (!Output) { std::string Temp; Temp = "Error opening the procmail configuration file " + ProcmailRcFileName; Temp += " for writing: "; Temp += strerror(errno); throw std::runtime_error(Temp); } Output << ProcmailRcFileContent; if (!Output) { std::string Temp; Temp = "Error writing the procmail configuration file " + ProcmailRcFileName; Temp += ": "; Temp += strerror(errno); throw std::runtime_error(Temp); } Output.close(); if (!Output) { std::string Temp; Temp = "Error closing the procmail configuration file " + ProcmailRcFileName; Temp += " after writing: "; Temp += strerror(errno); throw std::runtime_error(Temp); } } OutputVerboseEnd(); if (!ReloadMta()) { std::cerr << "Unable to reload the sendmail configuration. Please reload " << " the sendmail configuration for the integration with SNFServer to take effect."; } } void SendmailIntegrate::Unintegrate(FileBackup *SaveFile) { if (!IntegrationIsSupported) { return; } if (!IsIntegrated()) { return; } std::ifstream Input; if (Verbose()) { std::cout << "Remove integration in procmail file " << ProcmailRcFileName << "--\n"; } if (!Explain()) { Input.open(ProcmailRcFileName.c_str()); if (!Input) { std::string Temp; Temp = "Error opening the procmail configuration file " + ProcmailRcFileName; Temp += " for reading: "; Temp += strerror(errno); throw std::runtime_error(Temp); } std::ostringstream ContentStream; ContentStream << Input.rdbuf(); Input.close(); if (!Input) { std::string Temp; Temp = "Error closing the procmail configuration file " + ProcmailRcFileName; Temp += ": "; Temp += strerror(errno); throw std::runtime_error(Temp); } std::string Content; Content = ContentStream.str(); if (Verbose()) { std::cout << " Remove all occurances of\n\n" << ProcmailRcSnifferIntegration << "\n\n" << " from" << ProcmailRcFileName << "...\n"; } std::string::size_type IntegrationBegin = std::string::npos; while ((IntegrationBegin = Content.find(ProcmailRcSnifferIntegration)) != std::string::npos) { Content.erase(IntegrationBegin, ProcmailRcSnifferIntegration.length()); } SaveFile->CreateBackupFile(ProcmailRcFileName); std::ofstream Output; // Write the updated contents. Output.open(ProcmailRcFileName.c_str(), std::ios::trunc); if (!Output) { std::string Temp; Temp = "Error opening the procmail configuration file " + ProcmailRcFileName; Temp += " for writing: "; Temp += strerror(errno); throw std::runtime_error(Temp); } Output << Content; if (!Output) { std::string Temp; Temp = "Error writing the procmail configuration file " + ProcmailRcFileName; Temp += ": "; Temp += strerror(errno); throw std::runtime_error(Temp); } Output.close(); if (!Output) { std::string Temp; Temp = "Error closing the procmail configuration file " + ProcmailRcFileName; Temp += " after writing: "; Temp += strerror(errno); throw std::runtime_error(Temp); } } OutputVerboseEnd(); if (!ReloadMta()) { std::cerr << "Unable to reload the sendmail configuration. Please run " << "'sendmail reload' for the integration with SNFServer to take effect."; } } bool SendmailIntegrate::MtaIsRunningDetected() { if (Verbose()) { std::cout << "Checking whether sendmail is detected to be running..."; } bool IsRunningDetected; IsRunningDetected = (std::system(MtaIsRunningCommand.c_str()) == 0); if (Verbose()) { std::cout << (IsRunningDetected ? "yes..." : "no..."); } OutputVerboseEnd(); return IsRunningDetected; } bool SendmailIntegrate::ReloadMta() { if (!MtaIsRunningDetected()) { return true; } if (Verbose()) { std::cout << "Reloading sendmail with the command '" << ReloadMtaCommand << "'...\n"; std::cout.flush(); } bool Succeeded; if (!Explain()) { Succeeded = (std::system(ReloadMtaCommand.c_str()) == 0); if (Verbose()) { std::cout << (Succeeded ? "succeeded..." : "failed..."); } } OutputVerboseEnd(); return Succeeded; } bool SendmailIntegrate::IsIntegrated() { if (Verbose()) { std::cout << "Checking for any SNFServer integration in the procmail file " << ProcmailRcFileName << "..."; } if (!FileExists(ProcmailRcFileName)) { if (Verbose()) { std::cout << "file doesn't exist; sendmail is not integrated..."; } OutputVerboseEnd(); return false; } bool Integrated = false; std::ifstream Input; Input.open(ProcmailRcFileName.c_str()); // Read the contents. if (!Input) { std::string Temp; Temp = "Error opening the procmail configuration file " + ProcmailRcFileName; Temp += " for reading: "; Temp += strerror(errno); throw std::runtime_error(Temp); } std::string ProcmailRcFileContent; if (!Input.eof()) { std::ostringstream Buffer; Buffer << Input.rdbuf(); ProcmailRcFileContent = Buffer.str(); } Input.close(); if (!Input) { std::string Temp; Temp = "Error closing the procmail configuration file " + ProcmailRcFileName; Temp += " after reading: "; Temp += strerror(errno); throw std::runtime_error(Temp); } Integrated = (ProcmailRcFileContent.find(ProcmailRcSnifferIntegration) != std::string::npos); if (Verbose()) { if (Integrated) { std::cout << "found\n\n" << ProcmailRcSnifferIntegration << "\n\n..."; } else { std::cout << "none found..."; } } OutputVerboseEnd(); return Integrated; } bool SendmailIntegrate::MtaConfigurationHasProcmailForLda() { std::string File; std::ifstream Input; File = SendmailSendmailMcPath; if (Verbose()) { std::cout << "Checking sendmail configuraton file " + File << " to verify that procmail is the Local Delivery Agent.\n"; } Input.open(File.c_str()); if (!Input) { std::string Temp; Temp = "Error opening sendmail configuration file " + File; Temp += " for reading: "; Temp += strerror(errno); throw std::runtime_error(Temp); } std::string Line; bool LdaIsProcmail = true; bool FoundLdaLine = false; while (getline(Input, Line)) { if (Line.substr(0, SendmailLdaKey.length()) == SendmailLdaKey) { // Check for LDA line. FoundLdaLine = true; if (Line.find(",", SendmailLdaKey.length()) != std::string::npos) { // Additional arguments? if (Line.find("procmail", SendmailLdaKey.length()) == std::string::npos) { // Yes. LdaIsProcmail = false; // procmail not specified in the config line. if (Verbose()) { std::cout << "The following line indicates that the sendmail LDA is not procmail:\n\n" << Line << "\n"; } break; } else { // procmail is specified in the config line. if (Verbose()) { std::cout << "The following line indicates that the sendmail LDA is procmail, " << "as required to integrate with SNFServer:\n\n" << Line << "\n\n"; } break; } } else { // LDA line uses default, which is procmail. if (Verbose()) { std::cout << "The following line indicates that the sendmail LDA is procmail, " << "as required to integrate with SNFServer:\n\n" << Line << "\n\n"; } break; } } } Input.close(); if (Input.bad()) { std::string Temp; Temp = "Error closing the sendmail configuration file " + File; Temp += " after reading: "; Temp += strerror(errno); throw std::runtime_error(Temp); } if (Verbose() && !FoundLdaLine) { std::cout << "The absence of \"" << SendmailLdaKey << "\" indicates that the LDA is procmail, " << "as required to integrate with SNFServer.\n"; } return LdaIsProcmail; }