From ea4da3c2709119e691ed1e5d11ddef82ce82532f Mon Sep 17 00:00:00 2001 From: pvincent Date: Sat, 28 Aug 2021 16:40:37 +0400 Subject: [PATCH] reply-to working --- cagette.hxml | 1 + lang/master/tpl/messages/default.mtt | 2 +- src/controller/Messages.hx | 6 +- src/sugoi/mail/Mail.hx | 133 ++++++++++++++------------- src/sugoi/mail/SendEmailMailer.hx | 25 +++-- src/sugoi/mail/SmtpMailer.hx | 68 -------------- 6 files changed, 90 insertions(+), 145 deletions(-) delete mode 100644 src/sugoi/mail/SmtpMailer.hx diff --git a/cagette.hxml b/cagette.hxml index 7671435..743b8c3 100644 --- a/cagette.hxml +++ b/cagette.hxml @@ -21,6 +21,7 @@ -lib tink_streams:0.2.1 -lib tink_syntaxhub:0.4.3 -lib tink_typecrawler:0.7.0 +-lib thx.text:0.2.1 ## GIT # -lib sugoi:git:https://git.artcode.re/cagetters/sugoi.git diff --git a/lang/master/tpl/messages/default.mtt b/lang/master/tpl/messages/default.mtt index b4e4d09..7ae734d 100644 --- a/lang/master/tpl/messages/default.mtt +++ b/lang/master/tpl/messages/default.mtt @@ -29,7 +29,7 @@

::_("Send an e-mail")::

- + ::raw form::
diff --git a/src/controller/Messages.hx b/src/controller/Messages.hx index 9a6f41c..4cb0cc2 100755 --- a/src/controller/Messages.hx +++ b/src/controller/Messages.hx @@ -51,7 +51,7 @@ class Messages extends Controller { // send mail confirmation link var e = new sugoi.mail.Mail(); - e.setSender(App.config.get("default_email")); + e.setSender(App.config.get("default_email"), t._("Cagette.net")); e.setSubject(subject); var replyTo = form.getValueOf("senderMail"); @@ -62,8 +62,6 @@ class Messages extends Controller { for (x in mails) e.addRecipient(x); - // sender : default email ( explicitly tells that the server send an email on behalf of the user ) - // e.setHeader("Sender", App.config.get("default_email")); var text:String = form.getValueOf("text"); var html = app.processTemplate("mail/message.mtt", {text: text, group: app.user.amap, list: getListName(listId)}); e.setHtmlBody(html); @@ -84,6 +82,8 @@ class Messages extends Controller { lm.insert(); throw Ok("/messages", t._("The message has been sent")); + } else { + App.log('Administrator #${app.user.id} <${senderName}> prepares sending an email from choices list'); } view.form = form; diff --git a/src/sugoi/mail/Mail.hx b/src/sugoi/mail/Mail.hx index aa90aa9..862fbb9 100644 --- a/src/sugoi/mail/Mail.hx +++ b/src/sugoi/mail/Mail.hx @@ -1,45 +1,53 @@ package sugoi.mail; + using Lambda; -class Mail implements IMail -{ - - public var title : String; - public var htmlBody : String; - public var textBody : String; - var headers : Map; - var sender : {name:String,email:String,?userId:Int}; - var recipients : Array<{name:String,email:String,?userId:Int}>; - - - +class Mail implements IMail { + public var title:String; + public var htmlBody:String; + public var textBody:String; + + var headers:Map; + var sender:{name:String, email:String, ?userId:Int}; + var recipients:Array<{name:String, email:String, ?userId:Int}>; + + static public function fromString(name:String, email:String) { + if (name == null) + return email; + else { + var finalName = thx.text.Diactrics.clean(name); + return '${finalName} <${email}>'; + } + } + public function new() { recipients = []; headers = new Map(); } - - public function getRecipients(){ + + public function getRecipients() { return recipients; } - - public function setSender(email, ?name,?userId) { - if(!isValid(email)) throw "invalid sender email : \""+email+"\""; - - sender = {name:name,email:email,userId:userId}; + + public function setSender(email, ?name, ?userId) { + if (!isValid(email)) + throw "invalid sender email : \"" + email + "\""; + + sender = {name: name, email: email, userId: userId}; return this; } - + public function setReplyTo(email, ?name) { - if(!isValid(email)) throw "invalid reply-to email : \""+email+"\""; - - setHeader("Reply-To","<"+email+">"+(name==null?"":name)); + if (!isValid(email)) + throw "invalid reply-to email : \"" + email + "\""; + setHeader("Reply-To", fromString(name, email)); } - + public function setSubject(s:String) { title = s; return this; } - + /** * can add one or more recipient * @param email @@ -47,11 +55,12 @@ class Mail implements IMail * @param ?userId */ public function addRecipient(email:String, ?name:String, ?userId:Int) { - if(!isValid(email)) throw "invalid recipient \""+email+"\""; - recipients.push( {email:email, name:name, userId:userId } ); + if (!isValid(email)) + throw "invalid recipient \"" + email + "\""; + recipients.push({email: email, name: name, userId: userId}); return this; } - + /** * alias to addRecipient() * @param email @@ -62,24 +71,24 @@ class Mail implements IMail addRecipient(email, name, userId); return this; } - - public static function isValid( addr : String ){ + + public static function isValid(addr:String) { var reg = ~/^[^()<>@,;:\\"\[\]\s[:cntrl:]]+@[A-Z0-9][A-Z0-9-]*(\.[A-Z0-9][A-Z0-9-]*)*\.(xn--[A-Z0-9]+|[A-Z]{2,8})$/i; return addr != null && reg.match(addr); } - + public function setHeader(k:String, v:String) { headers.set(k, v); return this; } - + /** * generate a custom key for transactionnal emails, valid during the current day */ public function getKey() { - return haxe.crypto.Md5.encode(App.config.get("key")+recipients[0].email+(Date.now().getDate())).substr(0,12); + return haxe.crypto.Md5.encode(App.config.get("key") + recipients[0].email + (Date.now().getDate())).substr(0, 12); } - + /** * render html from a template + vars * @param tpl A Template path @@ -88,7 +97,8 @@ class Mail implements IMail public function setHtmlBodyWithTemplate(tpl, ctx:Dynamic) { var app = App.current; var tpl = app.loadTemplate(tpl); - if( ctx == null ) ctx = { }; + if (ctx == null) + ctx = {}; ctx.HOST = App.config.HOST; ctx.key = getKey(); ctx.senderName = sender.name; @@ -98,28 +108,27 @@ class Mail implements IMail ctx.recipients = recipients; CSSInlining(ctx); htmlBody = tpl.execute(ctx); - } - + public function setHtmlBody(s) { htmlBody = s; return this; } - - public function setTextBodyWithTemplate(tpl, ctx:Dynamic) { + + public function setTextBodyWithTemplate(tpl, ctx:Dynamic) { var app = App.current; var tpl = app.loadTemplate(tpl); - if( ctx == null ) ctx = { }; + if (ctx == null) + ctx = {}; ctx.HOST = App.config.HOST; ctx.key = getKey(); textBody = tpl.execute(ctx); return this; } - function CSSInlining(ctx) { // CSS inlining - var css : Map> = new Map(); + var css:Map> = new Map(); ctx.addStyle = function(sel:String, style:String) { sel = sel.toLowerCase(); if (css.exists(sel)) @@ -130,11 +139,11 @@ class Mail implements IMail } var applyStyleRec = null; applyStyleRec = function(x:Xml) { - if (x.nodeType==Xml.Element) { + if (x.nodeType == Xml.Element) { var name = x.nodeName.toLowerCase(); - if( css.exists(name) ) - if (x.get("style")!=null) - x.set("style", x.get("style")+";"+css.get(name).join(";")); + if (css.exists(name)) + if (x.get("style") != null) + x.set("style", x.get("style") + ";" + css.get(name).join(";")); else x.set("style", css.get(name).join(";")); for (n in x) @@ -143,40 +152,38 @@ class Mail implements IMail } ctx.applyStyle = function(raw:String) { var x = Xml.parse(raw); - for(n in x) + for (n in x) applyStyleRec(n); return x.toString(); } } - - - public function getSubject(){ + + public function getSubject() { return title; } - public function getTitle(){ + public function getTitle() { return getSubject(); } - - public function getHtmlBody(){ + + public function getHtmlBody() { return htmlBody; } - - public function getTextBody(){ + + public function getTextBody() { return textBody; } - - public function setTextBody(t){ + + public function setTextBody(t) { textBody = t; return this; } - - public function getHeaders(){ + + public function getHeaders() { return headers; } - - public function getSender(){ + + public function getSender() { return sender; } - -} \ No newline at end of file +} diff --git a/src/sugoi/mail/SendEmailMailer.hx b/src/sugoi/mail/SendEmailMailer.hx index 7335824..fd298b8 100644 --- a/src/sugoi/mail/SendEmailMailer.hx +++ b/src/sugoi/mail/SendEmailMailer.hx @@ -40,17 +40,13 @@ class SendEmailMailer implements IMailer { return mailer; } - private function fromString(name:String, email:String) { - return (name == null) ? email : '${name} <${email}>'; - } - public function send(e:sugoi.mail.IMail, ?params:Dynamic, ?callback:MailerResult->Void) { var to:String; var bcc:String; var recipientCounts = e.getRecipients().length; if (recipientCounts > 1) { - to = fromString(e.getSender().name, e.getSender().email); + to = sugoi.mail.Mail.fromString(e.getSender().name, e.getSender().email); bcc = [for (i in e.getRecipients()) i.email].join(","); App.log('recipients has been transformed to ${recipientCounts} bcc'); App.log(bcc); @@ -72,26 +68,35 @@ class SendEmailMailer implements IMailer { "-o", "message-format=html", "-o", - "X-Mailer=CagettePei", + "message-header=X-Mailer: CagettePei", "-f", - fromString(e.getSender().name, e.getSender().email), + sugoi.mail.Mail.fromString(e.getSender().name, e.getSender().email), "-t", to, "-bcc", bcc, "-u", e.getSubject(), - "-m", - '${e.getHtmlBody()}', ]; + // FIME: deal with replyTo + if (e.getHeaders().exists("Reply-To")) { + var replyTo = e.getHeaders()["Reply-To"]; + App.log('replyTo "$replyTo" detected'); + args.push("-o"); + args.push('message-header=Reply-To: ${replyTo}'); + } + + args.push("-m"); + args.push('${e.getHtmlBody()}'); + // if (App.config.DEBUG) // App.log('args=${args.join(" ")}'); var exitCode = Sys.command("sendemail", args); // var exitCode = 0; - var summary = 'email from="${fromString(e.getSender().name, e.getSender().email)}"> subject=<${e.getSubject()}>'; + var summary = 'email from="${sugoi.mail.Mail.fromString(e.getSender().name, e.getSender().email)}" subject=<${e.getSubject()}>'; if (exitCode == 0) App.log('$summary successfully sent'); else diff --git a/src/sugoi/mail/SmtpMailer.hx b/src/sugoi/mail/SmtpMailer.hx deleted file mode 100644 index aa52f5d..0000000 --- a/src/sugoi/mail/SmtpMailer.hx +++ /dev/null @@ -1,68 +0,0 @@ -package sugoi.mail; -import tink.core.Future; -import tink.core.Noise; -import sugoi.mail.IMailer; -import smtpmailer.Address; - -/** - * Send emails thru SMTP by using ben merckx's library - * @ref https://github.com/benmerckx/smtpmailer - */ -class SmtpMailer implements IMailer -{ - var m : smtpmailer.SmtpMailer; - - public function new(){} - - public function init(?conf:{smtp_host:String,smtp_port:Int,smtp_user:String,smtp_pass:String}) :IMailer - { - m = new smtpmailer.SmtpMailer({ - host: conf.smtp_host, - port: conf.smtp_port, - auth: { - username: conf.smtp_user, - password: conf.smtp_pass - } - }); - - return this; - } - - public function send(e:sugoi.mail.IMail,?params:Dynamic,?callback:MailerResult->Void) - { - var surprise = m.send({ - subject: e.getSubject(), - /*from: e.getSender().email, - to: Lambda.array(Lambda.map(e.getRecipients(), function(x) return smtpmailer.Address.ofString(x.email) )), - //headers : e.getHeaders(),*/ - from: new Address({address:e.getSender().email}), - to: Lambda.array(Lambda.map(e.getRecipients(), function(x) return new Address({address:x.email}) )), - headers : e.getHeaders(), - content: { - text: e.getTextBody(), - html: e.getHtmlBody() - }/*, - attachments: []*/ - }); - - - if (callback != null){ - - surprise.handle(function(s){ - - var map = new MailerResult(); - - switch(s){ - case Success(_): - map.set("*",Success(Sent)); - - case Failure(e): - map.set("*",Failure(GenericError(e))); - } - - callback(map); - }); - } - } - -} \ No newline at end of file