Email Ingestion
Dear Community,
We've been endeavouring to monitor unread emails using the Email Ingest Eventsource. However, I encountered a Java error ("Unable to resolve the class") during implementation. Upon investigating, I learned that JAVAX isn't supported, as mentioned by a member of the LM Team on the Community page.
It appears we might be utilizing an older version of the email ingest that relies on "jakarta". We made minimal adjustments, primarily switching from email.user to apc.email.user and updating the password. Besides these modifications, no other changes were made. We did this changes because we are testing this on APC UPS Devices.
Upon executing the script, it successfully identified the emails, and we applied the event source to all devices. However, we've encountered issues with triggering alerts and displaying data under the event source. Interestingly, upon application, the script reads the emails and marks them as read in the email inbox.
Below is the code we're currently using, and while it does generate output, we're seeking assistance in implementing alert triggers. Any guidance on this matter would be greatly appreciated.
Thank you
/*******************************************************************************
© 2007-2020 - LogicMonitor, Inc. All rights reserved.
*******************************************************************************/
import jakarta.mail.*
import jakarta.mail.internet.*
import jakarta.mail.search.*
import groovy.json.JsonBuilder
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
def debug = false
def deleteProcessedEmails = false
def imapHost = hostProps.get("apc.imap.host") // Required
def imapType = hostProps.get("apc.imap.type") // Optional
def emailUser = hostProps.get("apc.email.user") // Required
def emailPass = hostProps.get("apc.email.pass") // Required
def msgSubject = hostProps.get("apc.email.subject") // Optional, can include a propertyname (example: "##system.hostname##") for dynamic replacement
def deleteProcessedEmailProp = hostProps.get("email.deleteProcessed") // Optional
def emailInbox = hostProps.get("apc.email.folder") ?: "Inbox" // Example: "Inbox/Errors"
def hostName = hostProps.get("system.displayname")
// Default our email subject if one wasn't specified via property...
if (!msgSubject) {
msgSubject = "Incident Notification"
}
// See if a property was specified in our subject line...
def dynamicProp = msgSubject =~ ".*#{2}(.+)#{2}.*";
if (dynamicProp.size() > 0) {
def dynamicPropName = dynamicProp[0][1];
def dynamicPropValue = ""
// Allow simply specifying ##hostname## to replace with system.hostname...
if (dynamicPropName.toLowerCase() == "hostname") {
dynamicPropValue = hostName
// Otherwise, grab the property named (example: "Alert Test ##aws.instanceid##")...
} else {
dynamicPropValue = hostProps.get(dynamicPropName)
}
if (dynamicPropValue != "") {
msgSubject = msgSubject.replaceAll("##" + dynamicPropName + "##", dynamicPropValue)
}
}
// Default is to NOT delete processed emails (will just mark them as read)...
if (deleteProcessedEmailProp && deleteProcessedEmailProp.toLowerCase() == "true") {
deleteProcessedEmails = true
}
def protocolDebug = true
if (debug) {
println "imapHost=${imapHost}"
println "imapType=${imapType}"
println "emailUser=${emailUser}"
println "emailFolder=${emailInbox}"
println "msgSubject=${msgSubject}"
println "deleteProcessedEmails=${deleteProcessedEmails}"
// println "emailAddr=${emailAddr}"
}
def eventList = [];
// Do we have all required params?...
if (!imapHost || !emailUser || !emailPass)
{
debugOutput(debug, "errorCode=-1")
return 0
}
protoDebugFile = new PrintStream('../logs/' + hostName + '-APCEmailAlert-protocol.log')
scriptDebugFile = new File('../logs/' + hostName + '-APCEmailAlert-debug.log')
scriptDebugFile.delete()
/*
* IMAP Setup
*/
imapProps = System.getProperties()
if (imapType == "SSL")
{
store_type = "imaps"
imapProps.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory")
imapProps.setProperty("mail.imap.socketFactory.fallback", "false")
imapProps.setProperty("mail.imap.socketFactory.port", "993")
imapProps.setProperty("mail.imaps.ssl.trust", "*")
imapProps.setProperty("mail.imaps.ssl.checkserveridentity", "false")
}
else if (imapType == "TLS")
{
store_type = "imap"
imapProps.setProperty("mail.imap.starttls.enable", "true")
imapProps.setProperty("mail.imap.socketFactory.port", "143")
}
else
{
store_type = "imap"
imapProps.setProperty("mail.imap.socketFactory.port", "143")
imapProps.setProperty("mail.imap.socketFactory.class", "javax.net.SocketFactory")
}
imapProps.setProperty("mail.store.protocol", store_type)
imapSession = Session.getInstance(imapProps, null)
imapSession.setDebug(protocolDebug)
imapSession.setDebugOut(protoDebugFile)
imap = imapSession.getStore(store_type)
/*
* Connect to IMAP
*/
def tries = 5
while (tries > 0)
{
tries--
try
{
// scriptDebugFile.append("Attempting IMAP Connection\n")
debugOutput(debug, "Attempting IMAP Connection")
imap.connect(imapHost, emailUser, emailPass)
tries = -1
}
catch (Exception e)
{
error_string = "IMAP Connection Error: " + e.message + "\n"
// scriptDebugFile.append(error_string)
debugOutput(debug, error_string)
println(error_string)
sleep(1000)
}
}
// Were we able to connect to imap server?...
if (tries == -1)
{
// Yes. log some debugging...
// scriptDebugFile.append("IMAP Connection Successful\n")
debugOutput(debug, "IMAP Connection Successful")
}
else
{
// No. Exit...
debugOutput(debug, "errorCode=3")
protoDebugFile.close()
return 0
}
/*
* Search inbox for the message we created earlier
*/
// inbox = imap.getFolder("Inbox")
inbox = imap.getFolder(emailInbox)
// Search for unread messages containing our subject (Note: our search term is converted to lowercase above)...
subjectTerm = new SubjectTerm(msgSubject)
unreadTerm = new FlagTerm(new Flags(Flags.Flag.SEEN), false)
search_term = new AndTerm(subjectTerm, unreadTerm)
foundMessage = false
try {
// Open the inbox do the search...
inbox.open(Folder.READ_WRITE)
// scriptDebugFile.append("Initiating new inbox search on subject \"${search_term}\"\n")
debugOutput(debug, "Initiating new inbox search on subject \"${search_term}\"")
searchResults = inbox.search(search_term)
// Message messages[] = inbox.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
foundCount = searchResults.size()
debugOutput(debug, "foundCount: ${foundCount}")
// Did we find any messages?...
if (foundCount == 0)
{
// No -- sleep for a bit...
// scriptDebugFile.append(" - search found no messages -- sleeping\n")
debugOutput(debug, " - search found no messages -- sleeping")
sleep(1000)
}
else
{
// Yes. Iterate through the search results array...
// scriptDebugFile.append(" - search found ${foundCount} messages\n")
debugOutput(debug, " - search found ${foundCount} messages")
// Iterate through results...
searchResults.each { imap_message ->
foundSubject = imap_message.getSubject()
foundSubject = foundSubject.replaceAll(",","")
foundSubject = foundSubject.replaceAll("\\(","")
foundSubject = foundSubject.replaceAll("\\)","")
// Does the subject of this message match the subject of the message we sent earlier?...
if (foundSubject =~ msgSubject) {
// Yes -- exit the loop...
tries = -1
foundMessage = true
// scriptDebugFile.append(" - located target message \"${foundSubject}\"\n")
debugOutput(debug, " - located target message \"${foundSubject}\"")
emailBody = getText(imap_message).trim()
emailBody = emailBody.replaceAll("\\r|\\n", "")
emailBody = emailBody.replaceAll("\\<.*?\\>", "")
emailBody = emailBody.replaceAll("font-family:[^;']*(;)", "")
emailBody = emailBody.replaceAll("font-size:[^;']*(;)", "")
emailBody = emailBody.trim().replaceAll("\\s+", " ")
// From epoch to human readable...
String date = new java.text.SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz ").format(new Date());
eventList.push([
"happenedOn": date.toString(),
"severity" : "Critical",
"message" : foundSubject,
"source" : hostName
])
}
// Flag any messages that matched this search for deletion...
if (deleteProcessedEmails) {
imap_message.setFlag(Flags.Flag.DELETED, true)
// scriptDebugFile.append(" - delete flag set on \"${foundSubject}\"\n")
debugOutput(debug, " - delete flag set on \"${foundSubject}\"")
}
imap_message.setFlag(Flags.Flag.SEEN, true)
}
// Expunge the deleted messages...
if (deleteProcessedEmails) {
// scriptDebugFile.append(" - expunging deleted message(s)\n")
debugOutput(debug, " - expunging deleted message(s)")
inbox.expunge()
}
}
// Close the mailbox...
inbox.close(true)
// scriptDebugFile.append(" - inbox closed\n")
debugOutput(debug, " - inbox closed")
}
catch (Exception e)
{
// Something blew up...
error_string = "IMAP Inbox Search Error: " + e.message
// scriptDebugFile.append(error_string + "\n")
debugOutput(debug, error_string)
println(error_string)
// Because something blew up, lets make sure our inbox is closed...
inbox.close(true)
sleep(1000)
}
// Did we find the message?...
if (!foundMessage) {
// No. Close and exit...
debugOutput(debug, "errorCode=4")
protoDebugFile.close()
return 0
} else {
def events = [events: eventList];
println(new JsonBuilder(events).toPrettyString());
// def jsonOut = JsonOutput.toJson(eventsMap)
// println JsonOutput.prettyPrint(jsonOut)
}
// Close the IMAP connection...
// scriptDebugFile.append("Closing IMAP Connection\n")
debugOutput(debug, "Closing IMAP Connection")
imap.close()
// Everything went well...
debugOutput(debug, "errorCode=0")
return 0
// Capture/log debug output if flagged to do so...
def debugOutput(Boolean debug, String message) {
if (debug) {
println "IMAP Connection Successful"
scriptDebugFile.append("${message}\n")
}
}
// Credit to Mike Suding for the following function...
def getText(Part p) {
if (p.isMimeType("text/*")) {
String s = (String)p.getContent();
textIsHtml = p.isMimeType("text/html");
return s;
}
if (p.isMimeType("multipart/alternative")) {
// Prefer html over plain text...
Multipart mp = (Multipart)p.getContent();
String text = null;
for (int i = 0; i < mp.getCount(); i++) {
Part bp = mp.getBodyPart(i);
if (bp.isMimeType("text/plain")) {
if (text == null)
text = getText(bp);
continue;
} else if (bp.isMimeType("text/html")) {
String s = getText(bp);
if (s != null)
return s;
} else {
return getText(bp);
}
}
return text;
} else if (p.isMimeType("multipart/*")) {
Multipart mp = (Multipart)p.getContent();
for (int i = 0; i < mp.getCount(); i++) {
String s = getText(mp.getBodyPart(i));
if (s != null)
return s;
}
}
return EMPTY;
}