package cc.blynk.server.hardware.handlers.hardware.logic; import cc.blynk.server.core.BlockingIOProcessor; import cc.blynk.server.core.model.DashBoard; import cc.blynk.server.core.model.auth.User; import cc.blynk.server.core.model.widgets.notifications.Mail; import cc.blynk.server.core.processors.NotificationBase; import cc.blynk.server.core.protocol.exceptions.IllegalCommandException; import cc.blynk.server.core.protocol.exceptions.NotAllowedException; import cc.blynk.server.core.protocol.model.messages.StringMessage; import cc.blynk.server.core.session.HardwareStateHolder; import cc.blynk.server.notifications.mail.MailWrapper; import cc.blynk.utils.validators.BlynkEmailValidator; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import static cc.blynk.server.core.protocol.enums.Response.NOTIFICATION_ERROR; import static cc.blynk.utils.BlynkByteBufUtil.makeResponse; import static cc.blynk.utils.BlynkByteBufUtil.ok; /** * Sends email from received from hardware. Via google smtp server. * * The Blynk Project. * Created by Dmitriy Dumanskiy. * Created on 2/1/2015. * */ public class MailLogic extends NotificationBase { private static final Logger log = LogManager.getLogger(MailLogic.class); private final BlockingIOProcessor blockingIOProcessor; private final MailWrapper mailWrapper; private static final int EMAIL_DAY_LIMIT = 100; private static final int MILLIS_IN_DAY = 24 * 60 * 60 * 1000; public MailLogic(BlockingIOProcessor blockingIOProcessor, MailWrapper mailWrapper, long notificationQuotaLimit) { super(notificationQuotaLimit); this.blockingIOProcessor = blockingIOProcessor; this.mailWrapper = mailWrapper; } public void messageReceived(ChannelHandlerContext ctx, HardwareStateHolder state, StringMessage message) { DashBoard dash = state.user.profile.getDashByIdOrThrow(state.dashId); Mail mail = dash.getWidgetByType(Mail.class); if (mail == null || !dash.isActive) { throw new NotAllowedException("User has no mail widget or active dashboard."); } if (message.body.isEmpty()) { throw new IllegalCommandException("Invalid mail notification body."); } checkDailyEmailLimit(state.user); String[] bodyParts = message.body.split("\0"); if (bodyParts.length < 2) { throw new IllegalCommandException("Invalid mail notification body."); } String to; String subj; String body; if (bodyParts.length == 3) { to = bodyParts[0]; subj = bodyParts[1]; body = bodyParts[2]; } else { if (mail.to == null || mail.to.isEmpty()) { to = state.user.email; } else { to = mail.to; } subj = bodyParts[0]; body = bodyParts[1]; } checkIfNotificationQuotaLimitIsNotReached(); //minimal validation for receiver. if (BlynkEmailValidator.isNotValidEmail(to)) { throw new IllegalCommandException("Invalid mail receiver."); } log.trace("Sending Mail for user {}, with message : '{}'.", state.user.email, message.body); mail(ctx.channel(), state.user.email, to, subj, body, message.id); state.user.emailMessages++; } //todo add test for it. private void checkDailyEmailLimit(User user) { long now = System.currentTimeMillis(); if (now - user.emailSentTs < MILLIS_IN_DAY) { if (user.emailMessages > EMAIL_DAY_LIMIT) { throw EXCEPTION_CACHE; } } else { user.emailMessages = 0; user.emailSentTs = now; } } private void mail(Channel channel, String email, String to, String subj, String body, int msgId) { blockingIOProcessor.execute(() -> { try { mailWrapper.sendHtml(to, subj, body); channel.writeAndFlush(ok(msgId), channel.voidPromise()); } catch (Exception e) { log.error("Error sending email from hardware. From user {}, to : {}. Reason : {}", email, to, e.getMessage()); channel.writeAndFlush(makeResponse(msgId, NOTIFICATION_ERROR), channel.voidPromise()); } }); } }