Is there an OOTB Way to Extract and Attach Attachments from Uploaded .MSG/.EML Files?

Hi Team,

We have a requirement where, when a user attaches a .msg or .eml file to a case, all attachments contained within that email should also be attached to the case automatically.

For example:

Email A (.msg/.eml)
├─ Document1.pdf
└─ Document2.docx

Expected behavior:

After attaching Email A to the case, the following attachments should be available on the case:

  • Email A (.msg/.eml)
  • Document1.pdf
  • Document2.docx

We understand that Pega appears to perform similar attachment extraction when processing inbound emails through an Email Listener, where attachments within the email are automatically recognized and handled. However, this processing logic seems to be internal to the platform and not exposed for reuse when a user manually uploads a .msg or .eml file.

Has anyone implemented a similar requirement? Is there any OOTB API, utility, or recommended approach to extract and attach the embedded attachments from a .msg or .eml file uploaded to a case, similar to the processing performed by the Email Listener?

Any guidance would be appreciated. Thanks!

@MarcCheong

AFAIK, this requirement need custom solution to extract the attachements from .eml/.msg and attach it to the case.

Like @RameshSangili I’m not aware of anyway to do this using any provided activities or automations.

I would question if this requirement is what the business really wants? I know that is probably what they are asking for but in my experience, doing so results in far too many attachments on your case and those attachments are no longer in context with the emails they were sent. Email parsing commonly view email signatures an an image, so that only adds to the problem. The UX, without say Pega Messaging / Email capability wrapped around it to give it a threaded email context, can get unintuitive.

Would ingesting the email be a better way to go? Rather than getting people to attach the email you either automatically ingest it or have users forward it to an ingestion mailbox?

As @MarcCheong mentioned, without doing any specific customization, it’s better to have the emails forwarded to a mailbox for which you can setup an email listener and get the emails and its respective attachments linked to the case.

Having said that, it’s very important for us to understand the outcome you are delivering by having this functionality to suggest any alternatives.

Regards

JC

@MarcCheong & @JayachandraSiddipeta

Thanks for the suggestion. In general, I agree that email ingestion is the preferred pattern when the email itself is part of the business process and the email thread/context needs to be retained.

However, our requirement is slightly different. Users are not sending these emails through a monitored mailbox, nor are we trying to establish email conversations within the case. Instead, users are manually uploading existing .msg or .eml files as supporting documents to satisfy business/audit requirements.

In this scenario, the expectation is that all documents contained within the email are available as individual case attachments for downstream processing, document review, integrations, and reporting. Simply attaching the parent .msg/.eml file requires users to manually open the email to access the underlying documents.

Additionally, since the upload is initiated directly from the UI, users expect the extracted attachments to be processed immediately and reflected on the case as individual attachments without requiring any asynchronous email ingestion or background processing.

I have not tried this approach personally but give it a try if it’s mandated requirement for you.

Build a flow action, which accepts the .eml or .msg files. On submit of the flow action issues a Queue processor entry which calls an activity which invokes the Email Triage (ET) case functionality which can intake the email content and creates a triage case.

As part of the triage case functionality it can also parse the attachments attach to the triage case. In turn you can link the triage case to your work case. Hence you should be able to view the emails and its attachments from your work case without opening the triage case.

Things you have to assess before you jump into this approach once you are through this.

  1. Check whether triage cases are counted as part of your pega license contract.
  2. Whether Email Triage supports this kind of customisation without making major changes. Of course, it has lot of extension points given.
  3. Size limits of the email and its attachments.
  4. Should the email content be visible on the case always. Role based access, etc.
  5. Storage location and archiving of the case attachments.
  6. Have a word with business about all the complexities involved in achieving this. Think of alternatives as well.

Give us a shout once you have some outcome.

Regards

JC

@JayachandraSiddipeta

We have explored thie option which you are calling out.

Before control reaches the Email Triage activity, the Email Listener handle attachment processing based on its configuration. If the “No Attachments” option is not selected in the Email Listener, the platform automatically extracts all embedded attachments and populates them into a Page List. If the “No Attachments” option is selected, the embedded attachments are ignored. As a result, there is no opportunity to control or alter this behavior within the Email Triage activity itself.

Nevertheless, will share the outcome once we implement any other solution.

Thanks!

Hi Srinivas,

Your diagnosis is correct — the Email Listener’s attachment-extraction works because attachments are part of multi-part message. The logic to extract attachments is internal and is not exposed as utility.

Suggested approach (with activity + Java step, no unsupported internal APIs):

  1. Trigger — after the .msg/.eml is attached to the case, run an activity against that specific attachment
  2. Parse the file:
    • .eml (standard MIME) can be parsed with jakarta.mail, which is already bundled with the platform for the Email connector.
    • .msg (Outlook’s proprietary format) is not MIME and jakarta.mail cannot read it — you’d need a dedicated .msg/MAPI parser such as Apache POI’s HSMF module, added as a Java Library rule if not already available.
  3. Re-attach — for each embedded file found, call existing “attach a single file to this case”.

@VikasRaidhan ,

You are spot on regarding what we are thought to implement.

Instead of using Jakarta Mail, we are planning to use “javax.mail”, as Pega 24.1 environment includes mail-1.5.5.jar but does not include “jakarta.mail”. Our current approach is to implement the email parsing logic within Pega by building a custom Rule-Utility-Function (RUF). When an uploaded file is identified as an email (“.eml” or “.msg”), the RUF will parse the email, extract all embedded attachments. For “.eml” files, we plan to leverage “javax.mail”, while for Outlook “.msg” files, we plan to use Apache POI HSMF available through poi-scratchpad-3.11-20141221.jar.

Thanks for the response!

Thanks all for the inputs. We have implemented a Rule Utility Function (RUF) ExtractAttachmentsFromEmailFile that accepts base64Content and attachmentList as parameters. The function automatically determines whether the uploaded content is an .msg or .eml file based on its signature and extracts all embedded attachments into the supplied Page List.

try {
	byte[] fileBytes = java.util.Base64.getDecoder().decode(base64Content);
	boolean isMSG = fileBytes.length >= 8 && ((fileBytes[0] & 0xFF) == 0xD0) && ((fileBytes[1] & 0xFF) == 0xCF) && ((fileBytes[2] & 0xFF) == 0x11) && ((fileBytes[3] & 0xFF) == 0xE0);
	if(isMSG) {
		java.io.File tempFile = java.io.File.createTempFile("mail", ".msg");
		java.io.FileOutputStream fos = new java.io.FileOutputStream(tempFile);
		fos.write(fileBytes);
		fos.close();
		org.apache.poi.hsmf.MAPIMessage msg = new org.apache.poi.hsmf.MAPIMessage(tempFile.getAbsolutePath());
		org.apache.poi.hsmf.datatypes.AttachmentChunks[] attachments = msg.getAttachmentFiles();
		if(attachments != null) {
			for(int i = 0; i < attachments.length; i++) {
				org.apache.poi.hsmf.datatypes.AttachmentChunks attachment = attachments[i];
				ClipboardPage page = tools.createPage("Data-EmailAttachments", "");
				String fileName = "Attachment_" + (i + 1);
				if(attachment.getAttachLongFileName() != null) {
					fileName = attachment.getAttachLongFileName().toString();
				} else if(attachment.getAttachFileName() != null) {
					fileName = attachment.getAttachFileName().toString();
				}
				byte[] attachmentBytes = (attachment.getAttachData() != null) ? attachment.getAttachData().getValue() : new byte[0];
				page.putString("FileName", fileName);
				page.putString("Base64Content", java.util.Base64.getEncoder().encodeToString(attachmentBytes));
				page.putString("EmailFormat", "MSG");
				page.putString("FileSize", String.valueOf(attachmentBytes.length));
				page.putString("IsEmbeddedEmail", (fileName.toLowerCase().endsWith(".msg") || fileName.toLowerCase().endsWith(".eml")) ? "true" : "false");
				attachmentList.add(page);
			}
		}
		tempFile.delete();
	} else {
		java.util.Properties props = new java.util.Properties();
		javax.mail.Session session = javax.mail.Session.getDefaultInstance(props);
		javax.mail.internet.MimeMessage message = new javax.mail.internet.MimeMessage(session, new java.io.ByteArrayInputStream(fileBytes));
		Object content = message.getContent();
		if(content instanceof javax.mail.Multipart) {
			javax.mail.Multipart multipart = (javax.mail.Multipart) content;
			for(int i = 0; i < multipart.getCount(); i++) {
				javax.mail.BodyPart part = multipart.getBodyPart(i);
				String fileName = part.getFileName();
				if(fileName == null) {
					continue;
				}
				fileName = javax.mail.internet.MimeUtility.decodeText(fileName);
				java.io.InputStream is = part.getInputStream();
				java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
				byte[] buffer = new byte[4096];
				int len = 0;
				while((len = is.read(buffer)) != -1) {
					baos.write(buffer, 0, len);
				}
				byte[] attachmentBytes = baos.toByteArray();
				ClipboardPage page = tools.createPage("Data-EmailAttachments", "");
				page.putString("FileName", fileName);
				page.putString("ContentType", part.getContentType());
				page.putString("Base64Content", java.util.Base64.getEncoder().encodeToString(attachmentBytes));
				page.putString("EmailFormat", "EML");
				page.putString("FileSize", String.valueOf(attachmentBytes.length));
				page.putString("IsEmbeddedEmail", (fileName.toLowerCase().endsWith(".msg") || fileName.toLowerCase().endsWith(".eml")) ? "true" : "false");
				attachmentList.add(page);
			}
		}
	}
} catch (Exception e) {
	oLog.error("ExtractAttachmentsFromEmailFile Error", e);
	throw new RuntimeException(e);
}

Thanks!