Skip to content

Commit 834f7de

Browse files
luis100claude
andcommitted
feat: add client-side email viewer for .msg and .eml files
Renders Outlook MSG and RFC 822 EML files entirely in the browser without server-side conversion. - MSG files decoded with @kenjiuno/msgreader (bundled to browser IIFE via frontend-maven-plugin + esbuild); patches PT_STRING8 into TYPE_MAPPING to fix v1.11.0 field-decoding bug - EML files parsed with eml-parse-js (UMD bundle from WebJar); wraps readEml to suppress harmless InvalidCharacterError console noise from the library's base64 auto-detection heuristic - HTML bodies sanitised with DOMPurify (XSS protection); data: URIs allowed on <img> so CID-resolved inline images survive sanitisation - Inline CID images resolved to data URIs before sanitisation - Attachments downloadable via Blob object-URLs (MSG uses reader.getAttachment(); EML uses att.data64 base64 content) - Uses $wnd.XMLHttpRequest throughout to keep ArrayBuffer in host-page realm, avoiding cross-frame instanceof failures in the MSGReader bundle - Viewer registered for PRONOM fmt/950 (EML), fmt/952 (MSG), message/rfc822, application/vnd.ms-outlook, and .eml/.msg extensions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 1cd1330 commit 834f7de

9 files changed

Lines changed: 685 additions & 0 deletions

File tree

roda-ui/roda-wui/pom.xml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,33 @@
610610
<artifactId>filesaver.js</artifactId>
611611
<version>2.0.4</version>
612612
</dependency>
613+
<!-- Email viewer -->
614+
<!-- Note: jars below may take a few hours to propagate to Maven Central after deployment -->
615+
<dependency>
616+
<!-- Outlook .msg reader — CommonJS only; bundled to browser IIFE by frontend-maven-plugin -->
617+
<groupId>org.webjars.npm</groupId>
618+
<artifactId>github-com-HiraokaHyperTools-msgreader</artifactId>
619+
<version>1.11.0</version>
620+
</dependency>
621+
<dependency>
622+
<!-- RFC 822 .eml parser — browser UMD bundle served directly from webjars path -->
623+
<groupId>org.webjars.npm</groupId>
624+
<artifactId>github-com-MQpeng-eml-parse-js</artifactId>
625+
<version>1.1.15</version>
626+
</dependency>
627+
<dependency>
628+
<!-- Base64 codec — required by eml-parse-js UMD bundle as window.Base64 -->
629+
<groupId>org.webjars.npm</groupId>
630+
<artifactId>js-base64</artifactId>
631+
<version>3.7.7</version>
632+
</dependency>
633+
<dependency>
634+
<!-- HTML sanitizer — prevents XSS from email body content -->
635+
<groupId>org.webjars.npm</groupId>
636+
<artifactId>dompurify</artifactId>
637+
<version>3.2.6</version>
638+
</dependency>
639+
<!-- / Email viewer -->
613640
<!-- Spring boot -->
614641
<dependency>
615642
<groupId>org.springframework.boot</groupId>
@@ -675,6 +702,71 @@
675702
<encoding>UTF-8</encoding>
676703
</configuration>
677704
</plugin>
705+
<!--
706+
Bundles @kenjiuno/msgreader (CommonJS, no browser build) into a
707+
browser-compatible IIFE using esbuild. All transitive npm deps
708+
(iconv-lite, @kenjiuno/decompressrtf) are inlined by esbuild's
709+
bundler. The output lands in target/classes/static/js/msgreader/
710+
and is served by Spring Boot at /js/msgreader/MsgReader.js.
711+
-->
712+
<plugin>
713+
<groupId>com.github.eirslett</groupId>
714+
<artifactId>frontend-maven-plugin</artifactId>
715+
<version>1.15.1</version>
716+
<configuration>
717+
<workingDirectory>${project.basedir}/src/main/js</workingDirectory>
718+
<!-- Keep Node.js binary out of src/ -->
719+
<installDirectory>${project.build.directory}/frontend</installDirectory>
720+
<nodeVersion>v22.14.0</nodeVersion>
721+
</configuration>
722+
<executions>
723+
<execution>
724+
<id>install-node-and-npm</id>
725+
<goals>
726+
<goal>install-node-and-npm</goal>
727+
</goals>
728+
<phase>generate-resources</phase>
729+
</execution>
730+
<execution>
731+
<id>npm-install</id>
732+
<goals>
733+
<goal>npm</goal>
734+
</goals>
735+
<phase>generate-resources</phase>
736+
<!-- default goal is 'install' -->
737+
</execution>
738+
<execution>
739+
<id>bundle-msgreader-for-browser</id>
740+
<goals>
741+
<goal>npx</goal>
742+
</goals>
743+
<phase>generate-resources</phase>
744+
<configuration>
745+
<arguments>esbuild entry.js
746+
--bundle
747+
--global-name=MSGReader
748+
--format=iife
749+
--platform=browser
750+
--outfile=${project.build.outputDirectory}/static/js/msgreader/MsgReader.js</arguments>
751+
</configuration>
752+
</execution>
753+
<execution>
754+
<id>bundle-emlparser-for-browser</id>
755+
<goals>
756+
<goal>npx</goal>
757+
</goals>
758+
<phase>generate-resources</phase>
759+
<configuration>
760+
<arguments>esbuild entry-eml.js
761+
--bundle
762+
--global-name=EmlParseJs
763+
--format=iife
764+
--platform=browser
765+
--outfile=${project.build.outputDirectory}/static/js/emlparser/EmlParseJs.js</arguments>
766+
</configuration>
767+
</execution>
768+
</executions>
769+
</plugin>
678770
</plugins>
679771
</build>
680772
</project>

roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BitstreamPreview.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ public class BitstreamPreview<T extends IsIndexed> extends Composite {
5858
private static final String VIEWER_TYPE_HTML = "html";
5959
private static final String VIEWER_TYPE_PDF = "pdf";
6060
private static final String VIEWER_TYPE_IMAGE = "image";
61+
private static final String VIEWER_TYPE_TIFF = "tiff";
62+
private static final String VIEWER_TYPE_EMAIL = "email";
6163

6264
private static final ClientMessages messages = GWT.create(ClientMessages.class);
6365

@@ -189,6 +191,8 @@ private void preview() {
189191
audioPreview();
190192
} else if (type.equals(VIEWER_TYPE_VIDEO)) {
191193
videoPreview();
194+
} else if (type.equals(VIEWER_TYPE_EMAIL)) {
195+
emailPreview();
192196
} else {
193197
notSupportedPreview();
194198
}
@@ -251,6 +255,17 @@ public void onAttachOrDetach(AttachEvent event) {
251255

252256
}
253257

258+
private void tiffPreview() {
259+
TiffViewer tiffViewer = new TiffViewer(bitstreamDownloadUri);
260+
panel.add(tiffViewer);
261+
}
262+
263+
private void emailPreview() {
264+
EmailViewer emailViewer = new EmailViewer(bitstreamDownloadUri, format, filename, onPreviewFailure);
265+
panel.add(emailViewer);
266+
}
267+
268+
254269
private void pdfPreview() {
255270

256271
String viewerPdf = GWT.getHostPageBaseURL() + "webjars/pdf-js/web/viewer.html" + "?file="

0 commit comments

Comments
 (0)