diff --git a/application.example.yaml b/application.example.yaml index a4caab4..0db19e2 100644 --- a/application.example.yaml +++ b/application.example.yaml @@ -234,9 +234,25 @@ invite: # The display name used in the e-mail name: "Matrix Identity" - # The MIME content to send, UTF-8 expected + # The E-mail template to use. + # + # The template is expected to be a full e-mail body, including client headers, using MIME and UTF-8 encoding. + # The following headers will be set by mxisd directly and should not be present in the template: + # - From + # - To + # - Date + # - Message-Id + # - X-Mailer # # The following placeholders are possible: - # - %SENDER_DISPLAY_NAME% - # - %ROOM_NAME% - contentPath: "/absolute/path/to/file" + # - %DOMAIN% Domain name as per server.name config item + # - %DOMAIN_PRETTY% Word capitalize version of the domain. e.g. example.org -> Example.org + # - %FROM_EMAIL% Value of this section's email config item + # - %FROM_NAME% Value of this section's name config item + # - %SENDER_ID% Matrix ID of the invitation sender + # - %SENDER_NAME% Display name of the invitation sender, empty if not available + # - %SENDER_NAME_OR_ID% Value of %SENDER_NAME% or, if empty, value of %SENDER_ID% + # - %ROOM_ID% ID of the room where the invitation took place + # - %ROOM_NAME% Name of the room, empty if not available + # - %ROOM_NAME_OR_ID% Value of %ROOM_NAME% or, if empty, value of %ROOM_ID% + template: "/absolute/path/to/file" diff --git a/src/main/groovy/io/kamax/mxisd/config/invite/sender/EmailSenderConfig.java b/src/main/groovy/io/kamax/mxisd/config/invite/sender/EmailSenderConfig.java index 80f91ad..d6e12a8 100644 --- a/src/main/groovy/io/kamax/mxisd/config/invite/sender/EmailSenderConfig.java +++ b/src/main/groovy/io/kamax/mxisd/config/invite/sender/EmailSenderConfig.java @@ -42,7 +42,7 @@ public class EmailSenderConfig { private String password; private String email; private String name; - private String contentPath; + private String template; public String getHost() { return host; @@ -100,12 +100,12 @@ public class EmailSenderConfig { this.name = name; } - public String getContentPath() { - return contentPath; + public String getTemplate() { + return template; } - public void setContentPath(String contentPath) { - this.contentPath = contentPath; + public void setTemplate(String template) { + this.template = template; } @PostConstruct @@ -117,14 +117,18 @@ public class EmailSenderConfig { log.info("Login: {}", getLogin()); log.info("Has password: {}", StringUtils.isBlank(getPassword())); log.info("E-mail: {}", getEmail()); - if (StringUtils.isBlank(getContentPath())) { - log.warn("invite.sender.contentPath is empty! Will not send invites"); - } else { - File cp = new File(getContentPath()).getAbsoluteFile(); - log.info("Content path: {}", cp.getAbsolutePath()); - if (!cp.exists() || !cp.isFile() || !cp.canRead()) { - log.warn(getContentPath() + " does not exist, is not a file or cannot be read"); + if (!StringUtils.startsWith(getTemplate(), "classpath:")) { + if (StringUtils.isBlank(getTemplate())) { + log.warn("invite.sender.template is empty! Will not send invites"); + } else { + File cp = new File(getTemplate()).getAbsoluteFile(); + log.info("Template: {}", cp.getAbsolutePath()); + if (!cp.exists() || !cp.isFile() || !cp.canRead()) { + log.warn(getTemplate() + " does not exist, is not a file or cannot be read"); + } } + } else { + log.info("Template: Built-in"); } } diff --git a/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java b/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java index 7f654c5..7287030 100644 --- a/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java +++ b/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java @@ -137,7 +137,7 @@ public class InvitationManager { ThreePid pid = new ThreePid(invitation.getMedium(), invitation.getAddress()); - log.info("Storing invite for {}:{} from {} in room {}", pid.getMedium(), pid.getAddress(), invitation.getSender(), invitation.getRoomId()); + log.info("Handling invite for {}:{} from {} in room {}", pid.getMedium(), pid.getAddress(), invitation.getSender(), invitation.getRoomId()); if (invitations.containsKey(pid)) { log.info("Invite is already pending for {}:{}, returning data", pid.getMedium(), pid.getAddress()); return invitations.get(pid); diff --git a/src/main/groovy/io/kamax/mxisd/invitation/sender/EmailInviteSender.java b/src/main/groovy/io/kamax/mxisd/invitation/sender/EmailInviteSender.java index 49f7b9a..f22a1b6 100644 --- a/src/main/groovy/io/kamax/mxisd/invitation/sender/EmailInviteSender.java +++ b/src/main/groovy/io/kamax/mxisd/invitation/sender/EmailInviteSender.java @@ -22,13 +22,17 @@ package io.kamax.mxisd.invitation.sender; import com.sun.mail.smtp.SMTPTransport; import io.kamax.matrix.ThreePidMedium; +import io.kamax.mxisd.config.ServerConfig; import io.kamax.mxisd.config.invite.sender.EmailSenderConfig; import io.kamax.mxisd.exception.ConfigurationException; import io.kamax.mxisd.invitation.IThreePidInviteReply; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.WordUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @@ -37,7 +41,6 @@ import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; -import java.io.FileInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; @@ -51,6 +54,12 @@ public class EmailInviteSender implements IInviteSender { @Autowired private EmailSenderConfig cfg; + @Autowired + private ServerConfig srvCfg; + + @Autowired + private ApplicationContext app; + private Session session; private InternetAddress sender; @@ -77,16 +86,30 @@ public class EmailInviteSender implements IInviteSender { } try { - String templateBody = IOUtils.toString(new FileInputStream(cfg.getContentPath()), StandardCharsets.UTF_8); - templateBody = - templateBody.replace("%SENDER_DISPLAY_NAME%", invite.getInvite().getProperties().get("sender_display_name")) - .replace("%ROOM_NAME%", invite.getInvite().getProperties().get("room_name")); + String domainPretty = WordUtils.capitalizeFully(srvCfg.getName()); + String senderName = invite.getInvite().getProperties().getOrDefault("sender_display_name", ""); + String senderNameOrId = StringUtils.defaultIfBlank(senderName, invite.getInvite().getSender().getId()); + String roomName = invite.getInvite().getProperties().getOrDefault("room_name", ""); + String roomNameOrId = StringUtils.defaultIfBlank(roomName, invite.getInvite().getRoomId()); + + String templateBody = IOUtils.toString(app.getResource(cfg.getTemplate()).getInputStream(), StandardCharsets.UTF_8); + templateBody = templateBody.replace("%DOMAIN%", srvCfg.getName()); + templateBody = templateBody.replace("%DOMAIN_PRETTY%", domainPretty); + templateBody = templateBody.replace("%FROM_EMAIL%", cfg.getEmail()); + templateBody = templateBody.replace("%FROM_NAME%", cfg.getName()); + templateBody = templateBody.replace("%SENDER_ID%", invite.getInvite().getSender().getId()); + templateBody = templateBody.replace("%SENDER_NAME%", senderName); + templateBody = templateBody.replace("%SENDER_NAME_OR_ID%", senderNameOrId); + templateBody = templateBody.replace("%ROOM_ID%", invite.getInvite().getRoomId()); + templateBody = templateBody.replace("%ROOM_NAME%", roomName); + templateBody = templateBody.replace("%ROOM_NAME_OR_ID%", roomNameOrId); MimeMessage msg = new MimeMessage(session, IOUtils.toInputStream(templateBody, StandardCharsets.UTF_8)); msg.setHeader("X-Mailer", "mxisd"); // TODO set version msg.setSentDate(new Date()); msg.setFrom(sender); msg.setRecipients(Message.RecipientType.TO, invite.getInvite().getAddress()); + msg.saveChanges(); log.info("Sending invite to {} via SMTP using {}:{}", invite.getInvite().getAddress(), cfg.getHost(), cfg.getPort()); SMTPTransport transport = (SMTPTransport) session.getTransport("smtp"); diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 8c78f35..74984af 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,3 +1,11 @@ +logging: + level: + org: + springframework: "WARN" + apache: + catalina: "WARN" + directory: "WARN" + server: port: 8090 @@ -18,16 +26,17 @@ lookup: ldap: enabled: false +firebase: + enabled: false + forward: servers: - "https://matrix.org" - "https://vector.im" -firebase: - enabled: false - invite: sender: email: tls: 1 name: "mxisd Identity Server" + template: "classpath:email/invite-template.eml" diff --git a/src/main/resources/email/invite-template.eml b/src/main/resources/email/invite-template.eml new file mode 100644 index 0000000..0d85320 --- /dev/null +++ b/src/main/resources/email/invite-template.eml @@ -0,0 +1,91 @@ +Subject: You have been invited to %DOMAIN% +MIME-Version: 1.0 +Content-Type: multipart/alternative; + boundary="7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ" + +--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ +Content-Type: text/plain; charset=UTF-8 +Content-Disposition: inline + +Hi, + +%SENDER_NAME_OR_ID% has invited you into a room [%ROOM_NAME_OR_ID%] on +Matrix. To join the conversation, register an account on http://%DOMAIN% + + +About Matrix: + +Matrix is an open standard for interoperable, decentralised, real-time communication +over IP, supporting group chat, file transfer, voice and video calling, integrations to +other apps, bridges to other communication systems and much more. It can be used to power +Instant Messaging, VoIP/WebRTC signalling, Internet of Things communication. + +Thanks, + +%DOMAIN_PRETTY% Admins + +--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ +Content-Type: multipart/related; + boundary="M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR"; + type="text/html" + +--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR +Content-Type: text/html; charset=UTF-8 +Content-Disposition: inline + + + +
+ + + +| + |
+ Hi, + +%SENDER_NAME_OR_ID% has invited you into a room [%ROOM_NAME_OR_ID%] on +Matrix. To join the conversation, register an account on %DOMAIN%. + ++ About Matrix: + +Matrix is an open standard for interoperable, decentralised, real-time communication + over IP, supporting group chat, file transfer, voice and video calling, integrations to + other apps, bridges to other communication systems and much more. It can be used to power + Instant Messaging, VoIP/WebRTC signalling, Internet of Things communication. + +Thanks, + +%DOMAIN_PRETTY% Admins + |
+ + |