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.

main.cpp 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. // main.cpp
  2. // SNF MDaemon Plugin
  3. // Copyright (C) 2007-2008, ARM Research Labs, LLC.
  4. #include <wx/app.h>
  5. #include <wx/window.h>
  6. #include <wx/dialog.h>
  7. #include <wx/toplevel.h>
  8. #include "dialogapp.h"
  9. #include "configdialog.h"
  10. #include <winsock2.h>
  11. #include <windows.h>
  12. #include "mdconfiguration.hpp"
  13. #include "SNFMulti/SNFMulti.hpp"
  14. #ifndef _UNICODE
  15. #define _UNICODE
  16. #endif
  17. #ifndef UNICODE
  18. #define UNICODE
  19. #endif
  20. #define GX_STANDARD_CALL __attribute__((stdcall))
  21. #define _stdcall __attribute__((stdcall))
  22. //IMPLEMENT_APP_NO_MAIN(dialogapp);
  23. //IMPLEMENT_WX_THEME_SUPPORT;
  24. ////////////////////////////////////////////////////////////////////////////////
  25. // Constants And Tweaks
  26. ////////////////////////////////////////////////////////////////////////////////
  27. const int MDPLUGIN_MSG = 22000;
  28. const int MDPLUGIN_DISPLAY = 22001;
  29. const char* PLUGIN_VERSION_INFO = "SNF MDaemon Plugin Version 4.0 Build: " __DATE__ " " __TIME__;
  30. ////////////////////////////////////////////////////////////////////////////////
  31. // SNF MDaemon Plugin DLL Interface Setup.
  32. ////////////////////////////////////////////////////////////////////////////////
  33. extern "C" { // All of these "C" functions for export.
  34. BOOL APIENTRY DllMain ( HINSTANCE hInst, DWORD wDataSeg, LPVOID lpvReserved ); // The DllMain starup/shutdown function.
  35. void _stdcall StartupFunc(HWND Parent); // Startup Function.
  36. void _stdcall ConfigFunc(HWND Parent); // Configuration function.
  37. void _stdcall PreMessageFunc(HWND Parent, const char* File); // Message Scan Function.
  38. void _stdcall SMTPMessageFunc(HWND Parent, const char* File); // IP Scan Function.
  39. void _stdcall ShutdownFunc(HWND Parent); // Shutdown Function.
  40. }
  41. BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { // Thread attach/detach functions.
  42. switch (fdwReason) { // Switch on the reason for the call.
  43. case DLL_PROCESS_ATTACH: // Attach to process.
  44. /*** Startup function moved to API ***/
  45. return true; // We will assume it worked.
  46. break;
  47. case DLL_PROCESS_DETACH: // Detach from process.
  48. /*** Shutdown function moved to API ***/
  49. return true; // We will assume that worked.
  50. break;
  51. case DLL_THREAD_ATTACH: // Attach to thread
  52. break; // Just be happy.
  53. case DLL_THREAD_DETACH: // Detach from thread
  54. break; // Just be happy.
  55. }
  56. return true; // Be happy by default.
  57. }
  58. ////////////////////////////////////////////////////////////////////////////////
  59. // SNF MDaemon Plugin Engine Code
  60. ////////////////////////////////////////////////////////////////////////////////
  61. // Handy Debug Functions. This is how we can display critical events on screen
  62. // and emit entries into the MDaemon logs.
  63. HWND LatestParent = 0; // Handle MDaemon log operations.
  64. void sayToScreen(const string StuffToSay) { // Display a modal system message.
  65. MessageBox ( // Use a message box.
  66. GetFocus(), // Take the user's focus.
  67. (LPCWSTR) StuffToSay.c_str(), // This is what to say.
  68. (LPCWSTR) "Message Sniffer Plug-In", // This is how to title the box.
  69. MB_OK|MB_SYSTEMMODAL ); // This makes it modal with a button.
  70. }
  71. void sayToLog(const string Msg) { // Emit message into MDaemon log.
  72. if(0 != LatestParent) { // If we have a handle:
  73. COPYDATASTRUCT Packet; // Create a packet for the log.
  74. Packet.dwData = MDPLUGIN_DISPLAY;
  75. Packet.cbData = Msg.length();
  76. Packet.lpData = reinterpret_cast<void*>(
  77. const_cast<char*>(Msg.c_str()));
  78. SendMessage(LatestParent, MDPLUGIN_MSG, MDPLUGIN_DISPLAY, // Send the packet.
  79. (LPARAM)(PCOPYDATASTRUCT)&Packet);
  80. }
  81. }
  82. // The configuration file path is theoretically in a fixed location based on
  83. // the installation directory of MDaemon. We read the installation directory
  84. // from the registry and derive the path from that. If we get what we want
  85. // then we return the full path to the SNFMDPlugin.xml file - which is derived
  86. // from snf_engine.xml. If we don't get what we want then we return nothing.
  87. string ConfigurationFilePath() { // Get the config file path.
  88. std::wstring install_pathW;
  89. HKEY hKey;
  90. WCHAR szBuffer[512];
  91. DWORD dwBufferSize = sizeof(szBuffer);
  92. std::wstring regValue = L"";
  93. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Alt-N Technologies\\MDaemon", 0, KEY_READ, &hKey)){
  94. if(ERROR_SUCCESS == RegQueryValueEx(hKey, (LPCWSTR) L"AppPath", NULL, NULL, (LPBYTE) &szBuffer, &dwBufferSize)){
  95. RegCloseKey(hKey);
  96. regValue = std::wstring(szBuffer);
  97. std::wstring termCheck;
  98. for(auto val: regValue){
  99. if(val != L'\0'){
  100. termCheck += val;
  101. }
  102. }
  103. if(termCheck.size() == 512){
  104. regValue = L""; // RegValue wasn't null terminated
  105. }
  106. else{
  107. int PathEnd = regValue.length(); // Grab the length.
  108. if(PathEnd > 0) { regValue.erase(PathEnd-1,1); } // Eat the stroke at the end.
  109. PathEnd = regValue.find_last_of(L"\\"); // Find the next stroke in.
  110. int PathLength = regValue.length(); // Grab the path length.
  111. int EraseSpan = PathLength - (PathEnd+1); // Calculate the amount to erase.
  112. regValue.erase(PathEnd+1,EraseSpan); // Erase it to get the root path.
  113. regValue.append(L"SNF\\snfmdplugin.xml");
  114. }
  115. }
  116. }
  117. //convert wstring to utf8
  118. size_t buffer_size = WideCharToMultiByte(CP_UTF8, 0, &regValue[0], (int)regValue.size(), NULL, 0, NULL, NULL);
  119. std::string strFilePath(buffer_size, '\0');
  120. WideCharToMultiByte(CP_UTF8, 0, &regValue[0], (int)regValue.size(), &strFilePath[0], buffer_size, NULL, NULL);
  121. // Return either the empty string or the configuration file path.
  122. return strFilePath; // Return what we have.
  123. }
  124. // Here are out engine components, startup, and shutdown logic. The actual
  125. // engine components are built as the DLL is loaded. They are "lit up" by the
  126. // startup() function - which is called when an application attaches to this
  127. // DLL; and they are closed down when an application detaches from this DLL.
  128. // 20080311 _M Added configuration interpreter for <platform/> and linked it
  129. // to the ConfigFunc() and MessageIPFunc() functions.
  130. volatile bool EngineIsGood = false; // True when things are ready to go.
  131. snf_RulebaseHandler* Rulebase = 0; // Our Rulebase Handler.
  132. snf_EngineHandler* ScanEngine = 0; // Our Scan Engine.
  133. int Generation = -1; // Configuration generation tag.
  134. string Configuration; // Configuration file path.
  135. MDConfiguration* PlatformConfig = 0; // Platform config interpreter.
  136. extern "C" void _stdcall StartupFunc(HWND Parent){ // How to get all this started.
  137. LatestParent = Parent;
  138. EngineIsGood = false; // Start off pessimistically.
  139. try { // Be sure to catch any exceptions.
  140. Rulebase = new snf_RulebaseHandler(); // Create our rulebase handler.
  141. ScanEngine = new snf_EngineHandler(); // Create our engine scanner.
  142. Configuration = ConfigurationFilePath(); // Get the configuration path.
  143. PlatformConfig = new MDConfiguration(*Rulebase, Configuration); // Set up our configuration monitor.
  144. Rulebase->open(Configuration.c_str(), "", ""); // Open a configured rulebase.
  145. Rulebase->PlatformVersion(PLUGIN_VERSION_INFO); // Set the Platform version string.
  146. ScanEngine->open(Rulebase); // Open the scanning engine.
  147. EngineIsGood = true; // If all went well, we're up!
  148. sayToLog(Rulebase->EngineVersion()); // version info to show we're ok.
  149. sayToLog(Rulebase->PlatformVersion()); // Log our platform and engine
  150. string ConfigInfo = "SNF Config: "; // Build a configuration info
  151. ConfigInfo.append(Configuration); // message and display that
  152. sayToLog(ConfigInfo); // too.
  153. }
  154. catch(snf_RulebaseHandler::ConfigurationError) { // Can't work with config file!
  155. string ErrorMessage = "Unable to Configure with: "; // Tell them about it and give
  156. ErrorMessage.append(Configuration); // them a hint where we looked.
  157. sayToScreen(ErrorMessage);
  158. }
  159. catch(snf_RulebaseHandler::FileError) { // Can't load the rulebase file!
  160. string ErrorMessage = "Unable to Load Rulebase in: "; // Tell them about it and give
  161. ErrorMessage.append(Configuration); // them a hint where we looked.
  162. sayToScreen(ErrorMessage);
  163. }
  164. catch(snf_RulebaseHandler::AllocationError) { // Can't allocate memory!
  165. sayToScreen("Unable to Allocate Enough Memory!"); // Tell them about it.
  166. }
  167. catch(snf_RulebaseHandler::IgnoreListError) { // Can't load ignore list!
  168. sayToScreen("Unable to Load Ingore List!"); // Tell them about it.
  169. }
  170. catch(snf_RulebaseHandler::AuthenticationError) { // Can't authenticate!
  171. sayToScreen("Unable to Authenticate Rulebase!"); // Tell them about it.
  172. }
  173. catch(snf_RulebaseHandler::Busy) { // Busy?!! That's weird.
  174. sayToScreen("Busy Exception?!!"); // Tell them about it.
  175. }
  176. catch(snf_RulebaseHandler::Panic) { // Panic?!! That's weird.
  177. sayToScreen("Panic Exception?!!"); // Tell them about it.
  178. }
  179. catch(exception& e) { // Some other runtime error?
  180. sayToScreen(e.what()); // Tell them what it was.
  181. }
  182. catch(...) { // Catch the unknown exception.
  183. sayToScreen("Unexpected Exception!"); // Tell them things didn't work.
  184. }
  185. return; // All done.
  186. }
  187. /****** THE FOLLOWING IS DEFUNCT, BUT USEFUL INFO SO WE KEEP IT! ******/
  188. /**** *Note: Why do we always return true even when we fail???
  189. ***** Because, in a DLL that is being loaded, none of the threads that
  190. ***** get created in the rulebase object will get any cycles until the
  191. ***** DLL load sequence is complete _AND_SUCCESSFUL_!
  192. *****
  193. ***** The same is true when we return false -- because then the OS KNOWS
  194. ***** that it's not safe to run any of the threads we've created.
  195. *****
  196. ***** As a result, if we try to destroy our rulebase manager after an
  197. ***** unsuccessful load then all of the stop() calls to our threads will
  198. ***** block waiting for the initialized threads to see their stop bits and
  199. ***** end. Since none of these threads actually get any love until the
  200. ***** DLL is successfully loaded, we end up waiting happily for ever!
  201. *****
  202. ***** Instead of stepping into this world of hurt, we've decided to leave
  203. ***** the internal flag set to indicate that the engine is Not-Good so that
  204. ***** the scanning functions remain inert, and then we wait patiently for
  205. ***** the DLL to be unloaded.
  206. *****
  207. ***** When that happens, since the OS KNOWS the DLL was loaded happily
  208. ***** before, the objects can go through their normal destruction without
  209. ***** deadlocking on their stop() functions (join()).
  210. ****/
  211. extern "C" void _stdcall ShutdownFunc(HWND Parent){ // How to get all this stopped.
  212. LatestParent = Parent;
  213. if(EngineIsGood) { // If the engine is good:
  214. try {
  215. EngineIsGood = false; // Stop using the engine now.
  216. LatestParent = 0; // No more logging calls.
  217. if(ScanEngine) { // Close the scanner if it exists:
  218. ScanEngine->close(); // Close it.
  219. delete ScanEngine; // Delete it.
  220. ScanEngine = 0; // Forget it.
  221. }
  222. if(Rulebase) { // Close the rulebase if it exists.
  223. Rulebase->close(); // Close it.
  224. delete Rulebase; // Delete it.
  225. Rulebase = 0; // Forget it.
  226. }
  227. if(PlatformConfig) { // Drop the PlatformConfig if it be.
  228. delete PlatformConfig; // Delete it.
  229. PlatformConfig = 0; // Forget it.
  230. }
  231. Generation = -1; // Reset our config gen tag.
  232. sayToLog("SNF: Plugin Shutdown."); // Tell them we're shut down.
  233. }
  234. catch(exception& e) { // If we have a runtime exception
  235. string InShutdown = "SNF, Shutdown: "; // make a human friendly message
  236. InShutdown.append(e.what()); // and package the exception for
  237. sayToScreen(InShutdown); // a screen pop-up.
  238. }
  239. catch(...) { // If we see some other kind of
  240. sayToScreen("SNF, Shutdown: Unknown Exception"); // exception then show that.
  241. }
  242. }
  243. return; // All done.
  244. }
  245. // Now we get to the business end of the plugin. Three functions are exported.
  246. // ConfigFunc() launches an editor for the configuration file.
  247. // 20080311 _M Adjusted ConfigFunc to use Platform Configuration Parameters.
  248. //DLL_EXPORT void __stdcall ConfigFunc(HWND Parent) { // Configuration function.
  249. extern "C" void _stdcall ConfigFunc(HWND Parent) {
  250. // Initialize application.
  251. wxApp::SetInstance(new dialogapp());
  252. wxEntryStart(0, NULL);
  253. wxTheApp->CallOnInit();
  254. wxTheApp->OnRun();
  255. wxTheApp->OnExit();
  256. wxEntryCleanup();
  257. }
  258. // MessageFunc() scans a message file and injects headers.
  259. extern "C" void _stdcall PreMessageFunc(HWND Parent, const char* File){ // Message Scan Function.
  260. LatestParent = Parent; // Capture the parent for logging.
  261. string LogMessage = "SNF MessageScan: "; // Start a plugin log entry.
  262. LogMessage.append(File); // add the file name.
  263. if(false == EngineIsGood) { // If the engine is not up then
  264. LogMessage.append(", Engine Not Ready!"); // report that in the plug-in log.
  265. sayToLog(LogMessage); // Post our entry to the plug-in log.
  266. return; // We're done.
  267. }
  268. // We are ready to begin our scan.
  269. int ResultCode = 0; // Keep track of the scan result.
  270. try { // Be sure to catch any exceptions.
  271. ResultCode = ScanEngine->scanMessageFile(File);
  272. }
  273. catch(snf_EngineHandler::FileError& e) { // Exception when a file won't open.
  274. LogMessage.append(", File Error!");
  275. sayToLog(LogMessage);
  276. string DebugData = "SNF Debug: ";
  277. DebugData.append(e.what());
  278. sayToLog(DebugData);
  279. return;
  280. }
  281. catch(snf_EngineHandler::XHDRError& e) { // Exception when XHDR Inject/File fails.
  282. LogMessage.append(", X-Header Error!");
  283. sayToLog(LogMessage);
  284. string DebugData = "SNF Debug: ";
  285. DebugData.append(e.what());
  286. sayToLog(DebugData);
  287. return;
  288. }
  289. catch(snf_EngineHandler::BadMatrix& e) { // Exception out of bounds of matrix.
  290. LogMessage.append(", Bad Matrix!");
  291. sayToLog(LogMessage);
  292. string DebugData = "SNF Debug: ";
  293. DebugData.append(e.what());
  294. sayToLog(DebugData);
  295. return;
  296. }
  297. catch(snf_EngineHandler::MaxEvals& e) { // Exception too many evaluators.
  298. LogMessage.append(", Too Many Evaluators!");
  299. sayToLog(LogMessage);
  300. string DebugData = "SNF Debug: ";
  301. DebugData.append(e.what());
  302. sayToLog(DebugData);
  303. return;
  304. }
  305. catch(snf_EngineHandler::AllocationError& e) { // Exception when we can't allocate something.
  306. LogMessage.append(", Allocation Error!");
  307. sayToLog(LogMessage);
  308. string DebugData = "SNF Debug: ";
  309. DebugData.append(e.what());
  310. sayToLog(DebugData);
  311. return;
  312. }
  313. catch(snf_EngineHandler::Busy& e) { // Exception when there is a collision.
  314. LogMessage.append(", Engine Busy!");
  315. sayToLog(LogMessage);
  316. string DebugData = "SNF Debug: ";
  317. DebugData.append(e.what());
  318. sayToLog(DebugData);
  319. return;
  320. }
  321. catch(snf_EngineHandler::Panic& e) { // Exception when something else happens.
  322. LogMessage.append(", Engine Panic!");
  323. sayToLog(LogMessage);
  324. string DebugData = "SNF Debug: ";
  325. DebugData.append(e.what());
  326. sayToLog(DebugData);
  327. return;
  328. }
  329. catch(exception& e) { // Some unexpected exception.
  330. LogMessage.append(", Unexpected Exception!");
  331. sayToLog(LogMessage);
  332. string DebugData = "SNF Debug: ";
  333. DebugData.append(e.what());
  334. sayToLog(DebugData);
  335. return;
  336. }
  337. catch(...) { // We should never get one of these, but
  338. LogMessage.append(", Non-Std Exception!"); // if we do we have something to say.
  339. sayToLog(LogMessage);
  340. return;
  341. }
  342. // At this point we know our scan worked ok. So, let's post the result.
  343. ostringstream O; // A stringstream makes it easy to
  344. O << LogMessage << ", Result=" << ResultCode; // format our plug-in log entry.
  345. sayToLog(O.str()); // Then we post it to the plug-in log.
  346. }
  347. // ControlFilePath(MessageFilePath) Converts .msg path to .ctl path.
  348. string ControlFilePath(string MessageFilePath) { // Convert .msg/.tmp to .ctl path.
  349. const string ControlFileExt(".ctl"); // Control file extension.
  350. const string EmptyString(""); // The empty string.
  351. string ControlFilePath(MessageFilePath); // Start with the message path.
  352. string::size_type CFExtPosition = ControlFilePath.find_last_of('.'); // Find the extension.
  353. if(string::npos == CFExtPosition) return EmptyString; // If we didn't find it return ""
  354. ControlFilePath.replace( // Replace the message file
  355. CFExtPosition, ControlFileExt.length(), // extension with the control
  356. ControlFileExt // file extension.
  357. );
  358. return ControlFilePath;
  359. }
  360. // MessageIPFunc() looks at the control file for a message, extracts the
  361. // source IP, and deletes the message if the IP is in the truncate range.
  362. extern "C" void _stdcall SMTPMessageFunc(HWND Parent, const char* File){ // IP Scan Function.
  363. LatestParent = Parent; // Capture the parent for logging.
  364. if(false == PlatformConfig->MessageIPFuncOn()) { // If the IP func is off:
  365. return; // We're done.
  366. }
  367. string LogMessage = "SNF IPScan: "; // Start a plugin log entry.
  368. LogMessage.append(File); // add the file name.
  369. if(false == EngineIsGood) { // If the engine is not up then
  370. LogMessage.append(", Engine Not Ready!"); // report that in the plug-in log.
  371. sayToLog(LogMessage); // Post our entry to the plug-in log.
  372. return; // We're done.
  373. }
  374. // Open the control file and find the RemoteIP
  375. const string CtlRemoteIP("RemoteIP="); // This is what we're looking for.
  376. string RemoteIP = ""; // This is where it will go.
  377. try { // Do this safely.
  378. ifstream ControlFile(ControlFilePath(File).c_str()); // Open the control file.
  379. while(ControlFile) { // While the file is good...
  380. string Line; // Read one line at a time
  381. getline(ControlFile, Line); // into a string.
  382. string::size_type TagPos = Line.find(CtlRemoteIP); // Look for the RemoteIP tag.
  383. if(string::npos != TagPos) { // If we find the RemoteIP tag:
  384. RemoteIP = Line.substr(TagPos + CtlRemoteIP.length()); // Get the string after the tag
  385. break; // We're done with the ctl file!
  386. }
  387. }
  388. ControlFile.close(); // Be nice and close our file.
  389. }
  390. catch(...) {} // Eat any exceptions.
  391. if(0 == RemoteIP.length()) { // If we didn't get the IP then
  392. LogMessage.append(", Didn't Get Remote IP!"); // make that the rest of our log
  393. sayToLog(LogMessage); // entry and post it.
  394. return; // We're done.
  395. }
  396. // At this point we have the remote IP to check.
  397. LogMessage.append(", "); LogMessage.append(RemoteIP); // Add the IP to the log entry.
  398. IPTestRecord IPAnalysis(RemoteIP); // Set up a test record.
  399. try { // Safely,
  400. Rulebase->performIPTest(IPAnalysis); // check the IP w/ GBUdb.
  401. }
  402. catch(...) { // If we encountered a problem
  403. LogMessage.append(", Analysis Failed!"); // then we need to report that
  404. sayToLog(LogMessage); // our analysis failed.
  405. return; // We're done.
  406. }
  407. // At this point we've got a good analysis, so we take action (if needed)
  408. // and produce a helpful log entry.
  409. ostringstream Happy; // A stringstream for easy formatting.
  410. Happy << LogMessage << ", {"; // Start the analysis report.
  411. switch(IPAnalysis.G.Flag()) { // Identify the flag data for this IP.
  412. case Good: Happy << "Good, "; break;
  413. case Bad: Happy << "Bad, "; break;
  414. case Ugly: Happy << "Ugly, "; break;
  415. case Ignore: Happy << "Ignore, "; break;
  416. }
  417. Happy << "p=" << IPAnalysis.G.Probability() << ", " // Probability and Confidence.
  418. << "c=" << IPAnalysis.G.Confidence() << ", ";
  419. switch(IPAnalysis.R) { // The Range analysis.
  420. case snfIPRange::Unknown: { Happy << " Unknown}"; break; } // Unknown - not defined.
  421. case snfIPRange::White: { Happy << " White}"; break; } // This is a good guy.
  422. case snfIPRange::Normal: { Happy << " Normal}"; break; } // Benefit of the doubt.
  423. case snfIPRange::New: { Happy << " New}"; break; } // It is new to us.
  424. case snfIPRange::Caution: { Happy << " Caution}"; break; } // This is suspicious.
  425. case snfIPRange::Black: { Happy << " Black}"; break; } // This is bad.
  426. case snfIPRange::Truncate: { Happy << " Truncate}"; break; } // This is bad unless we ignore it.
  427. }
  428. string WhatWeDid; // So, what do we do?
  429. if( // Are we in Truncate land?
  430. (snfIPRange::Truncate == IPAnalysis.R && Ugly == IPAnalysis.G.Flag()) || // If the IP is Truncate & Ugly, or
  431. Bad == IPAnalysis.G.Flag() // If the IP is just plain bad then
  432. ) { // we are in truncate land so we
  433. Happy << " Rejected!"; WhatWeDid = "Rejected"; // mark the message Rejected! and
  434. remove(File); // remove the file to reject it!
  435. } else { // If we are not rejecting the
  436. Happy << " Allowed."; WhatWeDid = "Allowed"; // message tell them it's allowed.
  437. }
  438. Rulebase->logThisIPTest(IPAnalysis, WhatWeDid); // Log the event.
  439. sayToLog(Happy.str()); // Post to the plug-in log.
  440. }