Browse code

feature/FEATBL-839 Feat: add attachments as links when max message size has been reached

Thomas Fricker authored on 13/08/2019 12:28:51
Showing 10 changed files
... ...
@@ -66,7 +66,6 @@ public class CalendarMail {
66 66
 		MessageBuilder builder = createBuilder();
67 67
 
68 68
 		MessageImpl m = new MessageImpl();
69
-		Header messageHeader = builder.newHeader();
70 69
 		m.setDate(new Date());
71 70
 		m.setSubject(subject);
72 71
 		m.setSender(sender);
... ...
@@ -116,20 +115,21 @@ public class CalendarMail {
116 115
 
117 116
 		attachments.ifPresent(atts -> {
118 117
 			for (EventAttachment att : atts) {
119
-				BodyPart attBody = new BodyPart();
120
-				attBody.setBody(att.part.getBody());
121
-				attBody.setFilename(att.name);
122
-				Header header = builder.newHeader();
123
-				header.setField(Fields.contentType(att.contentType + "; name=\"" + att.name + "\""));
124
-				header.setField(Fields.contentDisposition("attachment; filename=\"" + att.name + "\""));
125
-				header.setField(Fields.contentTransferEncoding("base64"));
126
-				attBody.setHeader(header);
127
-				mixed.addBodyPart(attBody);
118
+				if (att.isBinaryAttachment()) {
119
+					BodyPart attBody = new BodyPart();
120
+					attBody.setBody(att.part.get().getBody());
121
+					attBody.setFilename(att.name);
122
+					Header header = builder.newHeader();
123
+					header.setField(Fields.contentType(att.contentType + "; name=\"" + att.name + "\""));
124
+					header.setField(Fields.contentDisposition("attachment; filename=\"" + att.name + "\""));
125
+					header.setField(Fields.contentTransferEncoding("base64"));
126
+					attBody.setHeader(header);
127
+					mixed.addBodyPart(attBody);
128
+				}
128 129
 			}
129 130
 		});
130 131
 
131 132
 		m.setMultipart(mixed);
132
-		m.setHeader(messageHeader);
133 133
 
134 134
 		return m;
135 135
 	}
... ...
@@ -18,6 +18,8 @@
18 18
  */
19 19
 package net.bluemind.calendar.helper.mail;
20 20
 
21
+import java.util.Optional;
22
+
21 23
 import org.apache.james.mime4j.message.BodyPart;
22 24
 
23 25
 public class EventAttachment {
... ...
@@ -25,13 +27,32 @@ public class EventAttachment {
25 27
 	public final String uri;
26 28
 	public final String name;
27 29
 	public final String contentType;
28
-	public final BodyPart part;
30
+	public final Optional<BodyPart> part;
29 31
 
30 32
 	public EventAttachment(String uri, String name, String contentType, BodyPart part) {
31 33
 		this.uri = uri;
32 34
 		this.name = name;
33 35
 		this.contentType = contentType;
34
-		this.part = part;
36
+		this.part = Optional.of(part);
37
+	}
38
+
39
+	public EventAttachment(String uri, String name, String contentType) {
40
+		this.uri = uri;
41
+		this.name = name;
42
+		this.contentType = contentType;
43
+		this.part = Optional.empty();
44
+	}
45
+
46
+	public boolean isBinaryAttachment() {
47
+		return part.isPresent();
48
+	}
49
+
50
+	public String getUri() {
51
+		return uri;
52
+	}
53
+
54
+	public String getName() {
55
+		return name;
35 56
 	}
36 57
 
37 58
 }
38 59
new file mode 100644
... ...
@@ -0,0 +1,10 @@
1
+<#if attachments??>
2
+	<#list attachments as attachment>
3
+		<div id="cloudAttachmentListRoot" style="padding: 15px; background-color: #d9edff;">
4
+			<div id="cloudAttachmentList" style="background-color: #ffffff; padding: 15px;">
5
+			<div class="cloudAttachmentItem" style="border: 1px  solid  #cdcdcd; border-radius: 5px; margin-top: 10px; margin-bottom: 10px; padding: 15px;"><a style="color: #0f7edb ;" href="${attachment.uri}" target="_blank">${attachment.name}</a><span style="margin-left: 5px; font-size: small; color: grey;">(146 ko)</span><span style="display: block; font-size: small; color: grey;"></span></div>
6
+			</div>
7
+		</div>
8
+		</br>
9
+	</#list>
10
+</#if>
0 11
\ No newline at end of file
... ...
@@ -3,4 +3,5 @@
3 3
   ${msg("organizerInvitesYou", owner)}
4 4
 </h1>
5 5
 <#include "EventDetail.ftl">
6
+<#include "EventAttachments.ftl">
6 7
 <#include "foot.ftl">
... ...
@@ -3,4 +3,5 @@
3 3
   ${msg("meetingUpdated", owner)}
4 4
 </h1>
5 5
 <#include "EventDetail.ftl">
6
+<#include "EventAttachments.ftl">
6 7
 <#include "foot.ftl">
... ...
@@ -27,6 +27,7 @@ Require-Bundle: net.bluemind.slf4j;bundle-version="1.0.0",
27 27
  net.bluemind.icalendar.parser,
28 28
  net.bluemind.calendar.auditlog,
29 29
  net.bluemind.core.auditlog,
30
- net.bluemind.attachment.api
30
+ net.bluemind.attachment.api,
31
+ net.bluemind.system.api
31 32
 Import-Package: net.bluemind.directory.api
32 33
 
... ...
@@ -89,6 +89,9 @@ import net.bluemind.icalendar.api.ICalendarElement.Organizer;
89 89
 import net.bluemind.icalendar.api.ICalendarElement.ParticipationStatus;
90 90
 import net.bluemind.icalendar.api.ICalendarElement.Role;
91 91
 import net.bluemind.icalendar.parser.Mime;
92
+import net.bluemind.system.api.ISystemConfiguration;
93
+import net.bluemind.system.api.SysConfKeys;
94
+import net.bluemind.system.api.SystemConf;
92 95
 import net.bluemind.user.api.IUser;
93 96
 import net.bluemind.user.api.IUserSettings;
94 97
 import net.bluemind.user.api.User;
... ...
@@ -716,17 +719,9 @@ public class IcsHook implements ICalendarHook {
716 719
 					settings = userSettingsService.get(user.uid);
717 720
 				}
718 721
 
719
-				List<EventAttachment> attachments = new ArrayList<>();
720
-				for (AttachedFile att : event.attachments) {
721
-					BodyPart binaryPart;
722
-					try {
723
-						binaryPart = new CalendarMailHelper().createBinaryPart(loadAttachment(att));
724
-						// FIXME use mime-class
725
-						attachments.add(
726
-								new EventAttachment(att.publicUrl, att.name, Mime.getMimeType(att.name), binaryPart));
727
-					} catch (IOException e) {
728
-						logger.warn("Cannot read event attachment from url {}", att.publicUrl, e);
729
-					}
722
+				List<EventAttachment> attachments = handleAttachments(event);
723
+				if (!hasBinaryAttachments(attachments)) {
724
+					data.put("attachments", attachments);
730 725
 				}
731 726
 
732 727
 				Message mail = buildMailMessage(from, from, attendeeListTo, attendeeListCc, subjectTemplate, template,
... ...
@@ -738,13 +733,51 @@ public class IcsHook implements ICalendarHook {
738 733
 				auditor.actionSend(message.itemUid, recipient.getAddress(), ics);
739 734
 			});
740 735
 
741
-		} catch (ServerFault e) {
736
+		} catch (
737
+
738
+		ServerFault e) {
742 739
 			logger.error(e.getMessage(), e);
743 740
 		}
744 741
 
745 742
 	}
746 743
 
747
-	private byte[] loadAttachment(AttachedFile attachment) throws IOException {
744
+	private boolean hasBinaryAttachments(List<EventAttachment> attachments) {
745
+		return !attachments.isEmpty() && attachments.get(0).isBinaryAttachment();
746
+	}
747
+
748
+	private List<EventAttachment> handleAttachments(VEvent event) {
749
+		long maxBytes = 10000l;
750
+		long bytesRead = 0;
751
+		if (!event.attachments.isEmpty()) {
752
+			SystemConf systemConf = ServerSideServiceProvider.getProvider(SecurityContext.SYSTEM)
753
+					.instance(ISystemConfiguration.class).getValues();
754
+			maxBytes = systemConf.convertedValue(SysConfKeys.message_size_limit.name(), val -> Long.parseLong(val),
755
+					10000l);
756
+		}
757
+		List<EventAttachment> attachments = new ArrayList<>();
758
+		try {
759
+			List<EventAttachment> binaryParts = new ArrayList<>(event.attachments.size());
760
+			for (AttachedFile att : event.attachments) {
761
+				try {
762
+					byte[] attachmentAsBytes = loadAttachment(att, bytesRead, maxBytes);
763
+					bytesRead += attachmentAsBytes.length;
764
+					BodyPart binaryPart = new CalendarMailHelper().createBinaryPart(attachmentAsBytes);
765
+					binaryParts
766
+							.add(new EventAttachment(att.publicUrl, att.name, Mime.getMimeType(att.name), binaryPart));
767
+				} catch (IOException e) {
768
+					logger.warn("Cannot read event attachment from url {}", att.publicUrl, e);
769
+				}
770
+			}
771
+			attachments.addAll(binaryParts);
772
+		} catch (FileSizeExceededException fee) {
773
+			attachments.addAll(event.attachments.stream()
774
+					.map(att -> new EventAttachment(att.publicUrl, att.name, Mime.getMimeType(att.name)))
775
+					.collect(Collectors.toList()));
776
+		}
777
+		return attachments;
778
+	}
779
+
780
+	private byte[] loadAttachment(AttachedFile attachment, long read, long maxBytes) throws IOException {
748 781
 		try {
749 782
 			SSLContext sc = SSLContext.getInstance("SSL");
750 783
 			sc.init(null, new TrustManager[] { Trust.createTrustManager() }, new SecureRandom());
... ...
@@ -753,9 +786,13 @@ public class IcsHook implements ICalendarHook {
753 786
 		}
754 787
 		ByteArrayOutputStream baos = new ByteArrayOutputStream();
755 788
 		try (BufferedInputStream in = new BufferedInputStream(new URL(attachment.publicUrl).openStream())) {
756
-			byte dataBuffer[] = new byte[1024];
789
+			byte dataBuffer[] = new byte[8192];
757 790
 			int bytesRead = 0;
758
-			while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
791
+			while ((bytesRead = in.read(dataBuffer, 0, 8192)) != -1) {
792
+				read += bytesRead;
793
+				if (read > maxBytes) {
794
+					throw new FileSizeExceededException();
795
+				}
759 796
 				baos.write(dataBuffer, 0, bytesRead);
760 797
 			}
761 798
 		}
... ...
@@ -1223,4 +1260,8 @@ public class IcsHook implements ICalendarHook {
1223 1260
 
1224 1261
 	}
1225 1262
 
1226
-}
1227 1263
\ No newline at end of file
1264
+	@SuppressWarnings("serial")
1265
+	public static class FileSizeExceededException extends RuntimeException {
1266
+	}
1267
+
1268
+}
... ...
@@ -7,12 +7,12 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8
7 7
 Require-Bundle: net.bluemind.attachment.api;bundle-version="4.1.0",
8 8
  net.bluemind.restbus.api.gwt;bundle-version="4.1.0",
9 9
  com.google.gwt.user;resolution:=optional,
10
- net.bluemind.core.commons.gwt;bundle-version="4.1.0"
10
+ net.bluemind.core.commons.gwt;bundle-version="4.1.0",
11 11
  net.bluemind.user.api.gwt;bundle-version="4.1.0";visibility:=reexport,
12 12
  net.bluemind.restbus.api.gwt;bundle-version="4.1.0";visibility:=reexport
13 13
 Export-Package: net.bluemind.attachment.api.gwt.endpoint,
14 14
  net.bluemind.attachment.api.gwt.js,
15
- net.bluemind.attachment.api.gwt.serder
15
+ net.bluemind.attachment.api.gwt.serder,
16 16
  net.bluemind.attachment.api.gwt.js
17 17
 Automatic-Module-Name: net.bluemind.attachment.api.gwt
18 18
 
... ...
@@ -11,7 +11,7 @@ Require-Bundle: net.bluemind.core.commons.gwt;bundle-version="4.1.0",
11 11
  net.bluemind.core.task.api.gwt;bundle-version="4.1.0";visibility:=reexport,
12 12
  com.google.gwt.user;resolution:=optional,
13 13
  net.bluemind.user.api.gwt;bundle-version="4.1.0";visibility:=reexport,
14
- net.bluemind.attachment.api.gwt;bundle-version="4.1.0"
14
+ net.bluemind.attachment.api.gwt;bundle-version="4.1.0",
15 15
  net.bluemind.attachment.api;bundle-version="4.1.0"
16 16
 Export-Package: net.bluemind.icalendar.api.gwt.js,
17 17
  net.bluemind.icalendar.api.gwt.serder
... ...
@@ -49,8 +49,7 @@
49 49
 		<module>net.bluemind.eas.api.gwt</module>
50 50
 		<module>net.bluemind.backend.mail.api.gwt</module>
51 51
 		<module>net.bluemind.monitoring.api.gwt</module>
52
-        <module>net.bluemind.externaluser.api.gwt</module>
53
-        <module>net.bluemind.attachment.api.gwt</module>
52
+        	<module>net.bluemind.externaluser.api.gwt</module>
54 53
 	</modules>
55 54
 
56 55
 	<build>