From a1ece5258a50db77e7d3bc89e69999e54056d6e8 Mon Sep 17 00:00:00 2001 From: pvincent Date: Fri, 27 Aug 2021 22:03:50 +0400 Subject: [PATCH] sendemail is working --- src/App.hx | 53 ++++++----- src/controller/Cron.hx | 4 +- src/controller/Messages.hx | 11 +-- src/sugoi/db/BufferedMail.hx | 144 ++++++++++++++---------------- src/sugoi/mail/BufferedMailer.hx | 49 +++++----- src/sugoi/mail/SendEmailMailer.hx | 100 +++++++++++++++++++++ 6 files changed, 224 insertions(+), 137 deletions(-) create mode 100644 src/sugoi/mail/SendEmailMailer.hx diff --git a/src/App.hx b/src/App.hx index bb60cf0..255af0e 100755 --- a/src/App.hx +++ b/src/App.hx @@ -77,10 +77,14 @@ class App extends sugoi.BaseApp { } public static function log(t:Dynamic, ?infos:haxe.PosInfos) { - // if (App.config.DEBUG) { - // neko.Web.logMessage(Std.string(t)); // write in Apache error log - // } - neko.Web.logMessage('[${infos.fileName}:${infos.lineNumber} ${infos.className}#${infos.methodName}] ' + Std.string(t)); + var message:String = '[${infos.fileName}:${infos.lineNumber} ${infos.className}#${infos.methodName}] '; + message += Std.string(t); + + // trace(message); + if (neko.Web.isModNeko) + neko.Web.logMessage(message); + else + Sys.println(message); } public function event(e:Event) { @@ -230,24 +234,34 @@ class App extends sugoi.BaseApp { } public static function getMailer():sugoi.mail.IMailer { - var mailer:sugoi.mail.IMailer = new sugoi.mail.BufferedMailer(); + var typeOfMailer = sugoi.db.Variable.get("mailer"); + App.log('type of mailer is <$typeOfMailer>'); + + if (typeOfMailer == null) { + var msg = sugoi.i18n.Locale.texts._("Please configure the email settings in a this section"); + throw sugoi.ControllerAction.ErrorAction("/", msg); + } if (App.config.DEBUG) { - App.log("DEBUGEmailer enabled => emails are written to tmp folder"); - mailer = new sugoi.mail.DebugMailer(); + App.log('mailer in DEBUG mode, no deferred mail from CRON'); + + var conf = { + smtp_host: sugoi.db.Variable.get("smtp_host"), + smtp_port: sugoi.db.Variable.getInt("smtp_port"), + smtp_user: sugoi.db.Variable.get("smtp_user"), + smtp_pass: sugoi.db.Variable.get("smtp_pass") + }; + + return new sugoi.mail.SendEmailMailer().init(conf); + } + + var mailer:sugoi.mail.IMailer = new sugoi.mail.BufferedMailer(); + if (sugoi.db.Variable.get("mailer") == "mandrill") { + // Buffered emails with Mandrill + untyped mailer.defineFinalMailer("mandrill"); } else { - if (sugoi.db.Variable.get("mailer") == null) { - var msg = sugoi.i18n.Locale.texts._("Please configure the email settings in a this section"); - throw sugoi.ControllerAction.ErrorAction("/", msg); - } - - if (sugoi.db.Variable.get("mailer") == "mandrill") { - // Buffered emails with Mandrill - untyped mailer.defineFinalMailer("mandrill"); - } else { - // Buffered emails with SMTP - untyped mailer.defineFinalMailer("smtp"); - } + // Buffered emails with SMTP + untyped mailer.defineFinalMailer("smtp"); } return mailer; } @@ -265,7 +279,6 @@ class App extends sugoi.BaseApp { App.log('about to send email with subject=<${m.getSubject()}> ...'); getMailer().send(m, params, function(o) {}); - App.log('email from=<${m.getSender().email}>, subject=<${m.getSubject()}> successfully sent'); } public static function quickMail(to:String, subject:String, html:String, ?group:db.Amap) { diff --git a/src/controller/Cron.hx b/src/controller/Cron.hx index 4f8070a..9640180 100755 --- a/src/controller/Cron.hx +++ b/src/controller/Cron.hx @@ -444,7 +444,6 @@ class Cron extends Controller { */ function sendEmailsfromBuffer() { App.log("Send Emails from Buffer"); - print("

Send Emails from Buffer

"); // send for (e in sugoi.db.BufferedMail.manager.search($sdate == null, {limit: 50, orderBy: -cdate}, false)) { @@ -452,8 +451,7 @@ class Cron extends Controller { if (e.isSent()) continue; - App.log('Send Email id=${e.id} - title=${e.title}'); - print('#${e.id} - ${e.title}'); + App.log('Send Email id=#${e.id} - title=${e.title}'); e.finallySend(); Sys.sleep(0.1); } diff --git a/src/controller/Messages.hx b/src/controller/Messages.hx index d0bb535..7e1dc0d 100755 --- a/src/controller/Messages.hx +++ b/src/controller/Messages.hx @@ -82,16 +82,7 @@ class Messages extends Controller { var html = app.processTemplate("mail/message.mtt", {text: text, group: app.user.amap, list: getListName(listId)}); e.setHtmlBody(html); - // FIXME: re-apply sendMail - // App.sendMail(e, app.user.getAmap(), listId, app.user); - - // Sys.command("sendemail", [ - // "-f", "no-reply@comptoirduvrac.re", "-s", "mail1.zourit.net:587", "-t", "pvincent@comptoirduvrac.re", "-bcc", - // "pvincent974@gmail.com,pvincent974@laposte.net", "-u", subject, "-m", "alors tout va bien", "-xu", "postmaster@comptoirduvrac.re", "-xp", - // "QqQeAPT6EpoK" - // ]); - - sys.io.File.saveContent('/tmp/my_file.json', "CONTENT"); + App.sendMail(e, app.user.getAmap(), listId, app.user); // store message var lm = new db.Message(); diff --git a/src/sugoi/db/BufferedMail.hx b/src/sugoi/db/BufferedMail.hx index 173c1cd..74e0c74 100644 --- a/src/sugoi/db/BufferedMail.hx +++ b/src/sugoi/db/BufferedMail.hx @@ -1,4 +1,5 @@ package sugoi.db; + import sys.db.Types; import sugoi.mail.IMail; import sugoi.mail.IMailer; @@ -6,127 +7,116 @@ import sugoi.mail.IMailer; /** * DB Buffer for emails */ -@:index(remoteId,sdate,cdate) -class BufferedMail extends sys.db.Object -{ - public var id : SId; - - //email content - public var title : SString<256>; - public var htmlBody : SNull; - public var textBody : SNull; - public var headers : SData>; - public var sender : SData<{name:String,email:String,?userId:Int}>; - public var recipients : SData>; - - //utility fields - public var mailerType : SString<32>; //mailer used when sending for real - public var tries : SInt; //number of times we tried to send the mail - public var cdate : SDateTime; //creation date - public var sdate : SNull; //sent date - public var rawStatus : SNull; //raw return from the smtp server or mandrill API - public var status : SNull>; //map of emails with api/smtp results - - //custom datas - public var data : SNull>;//custom datas - public var remoteId : SNull; //custom remote Id (userId, groupId ...) - - - public function getMailerResultMessage(k:String):{failure:String, success:String}{ +@:index(remoteId, sdate, cdate) +class BufferedMail extends sys.db.Object { + public var id:SId; + + // email content + public var title:SString<256>; + public var htmlBody:SNull; + public var textBody:SNull; + public var headers:SData>; + public var sender:SData<{name:String, email:String, ?userId:Int}>; + public var recipients:SData>; + + // utility fields + public var mailerType:SString<32>; // mailer used when sending for real + public var tries:SInt; // number of times we tried to send the mail + public var cdate:SDateTime; // creation date + public var sdate:SNull; // sent date + public var rawStatus:SNull; // raw return from the smtp server or mandrill API + public var status:SNull>; // map of emails with api/smtp results + + // custom datas + public var data:SNull>; // custom datas + public var remoteId:SNull; // custom remote Id (userId, groupId ...) + + public function getMailerResultMessage(k:String):{failure:String, success:String} { var t = sugoi.i18n.Locale.texts; - var out = {failure:null, success:null}; - switch(status.get(k)){ + var out = {failure: null, success: null}; + switch (status.get(k)) { case tink.core.Outcome.Failure(f): - out.failure = switch(f){ - case GenericError(e): t._("Generic error: ") + e.toString(); - case HardBounce : t._("Mailbox does not exist"); - case SoftBounce : t._("Mailbox full or blocked"); - case Spam: t._("Message considered as spam"); - case Unsub: t._("This user unsubscribed"); - case Unsigned: t._("Sender incorrect (Unsigned)"); - + out.failure = switch (f) { + case GenericError(e): t._("Generic error: ") + e.toString(); + case HardBounce: t._("Mailbox does not exist"); + case SoftBounce: t._("Mailbox full or blocked"); + case Spam: t._("Message considered as spam"); + case Unsub: t._("This user unsubscribed"); + case Unsigned: t._("Sender incorrect (Unsigned)"); }; case tink.core.Outcome.Success(s): - out.success = switch(s){ - case Sent : t._("Sent"); - case Queued : t._("Queued"); + out.success = switch (s) { + case Sent: t._("Sent"); + case Queued: t._("Queued"); }; } return out; - - } + } - - public function new(){ + public function new() { super(); cdate = Date.now(); tries = 0; } - public function isSent(){ - return sdate!=null; + public function isSent() { + return sdate != null; } /** * Finally really send the message */ - public function finallySend():Void{ + public function finallySend():Void { + if (isSent()) + throw "already sent"; - if(isSent()) throw "already sent"; - var conf = { - smtp_host:sugoi.db.Variable.get("smtp_host"), - smtp_port:sugoi.db.Variable.getInt("smtp_port"), - smtp_user:sugoi.db.Variable.get("smtp_user"), - smtp_pass:sugoi.db.Variable.get("smtp_pass") + smtp_host: sugoi.db.Variable.get("smtp_host"), + smtp_port: sugoi.db.Variable.getInt("smtp_port"), + smtp_user: sugoi.db.Variable.get("smtp_user"), + smtp_pass: sugoi.db.Variable.get("smtp_pass") }; - var mailer : sugoi.mail.IMailer = switch(this.mailerType){ + App.log('choose mail from ${this.mailerType}'); + + var mailer:sugoi.mail.IMailer = switch (this.mailerType) { case "mandrill": new sugoi.mail.MandrillMailer().init(conf); case "smtp": - new sugoi.mail.SmtpMailer().init(conf); - case "debug": - new sugoi.mail.DebugMailer(); - default : - throw "Unknown mailer type : "+this.mailerType; + new sugoi.mail.SendEmailMailer().init(conf); + default: + throw "Unknown mailer type : " + this.mailerType; }; - var m = new sugoi.mail.Mail(); - for( k in this.headers.keys() ) m.setHeader( k,headers[k] ); + for (k in this.headers.keys()) + m.setHeader(k, headers[k]); m.setSubject(this.title); - for( r in recipients) m.setRecipient(r.email,r.name,r.userId); - m.setSender(sender.email,sender.name,sender.userId); + for (r in recipients) + m.setRecipient(r.email, r.name, r.userId); + m.setSender(sender.email, sender.name, sender.userId); m.setHtmlBody(this.htmlBody); m.setTextBody(this.textBody); - this.lock(); this.tries++; - try{ - mailer.send(m,null,afterSendCb); + try { + mailer.send(m, null, afterSendCb); this.sdate = Date.now(); this.rawStatus = null; - }catch(e:Dynamic){ + } catch (e:Dynamic) { this.sdate = null; this.rawStatus = Std.string(e); // App.current.logError( Std.string(e) ); } - - this.update(); + this.update(); } - - function afterSendCb(status:MailerResult){ - //App.current.logError(status); + function afterSendCb(status:MailerResult) { + // App.current.logError(status); this.status = status; this.update(); } - - - - -} \ No newline at end of file +} diff --git a/src/sugoi/mail/BufferedMailer.hx b/src/sugoi/mail/BufferedMailer.hx index d722181..800e986 100644 --- a/src/sugoi/mail/BufferedMailer.hx +++ b/src/sugoi/mail/BufferedMailer.hx @@ -1,4 +1,5 @@ package sugoi.mail; + import sugoi.mail.IMail; import sugoi.mail.IMailer; @@ -6,23 +7,21 @@ import sugoi.mail.IMailer; * Manage an email buffer in a table before sending them * @author fbarbut */ -class BufferedMailer implements IMailer -{ - var conf : Dynamic; - var type : String; +class BufferedMailer implements IMailer { + var conf:Dynamic; + var type:String; public function new() {} - - public function init(?c:Dynamic):IMailer{ + + public function init(?c:Dynamic):IMailer { return this; } - public function defineFinalMailer(type:String){ + public function defineFinalMailer(type:String) { this.type = type; } - - public function send(m:sugoi.mail.IMail,?params:Dynamic,?callback:MailerResult->Void):Void{ + public function send(m:sugoi.mail.IMail, ?params:Dynamic, ?callback:MailerResult->Void):Void { var bm = new sugoi.db.BufferedMail(); bm.headers = m.getHeaders(); bm.title = m.getTitle(); @@ -30,32 +29,28 @@ class BufferedMailer implements IMailer bm.textBody = m.getTextBody(); bm.recipients = m.getRecipients(); bm.sender = m.getSender(); - + bm.mailerType = this.type; - - //custom params - if(params!=null){ + + // custom params + if (params != null) { bm.data = params; - if(Reflect.hasField(params,"remoteId")){ - bm.remoteId = Reflect.getProperty(params,"remoteId"); + if (Reflect.hasField(params, "remoteId")) { + bm.remoteId = Reflect.getProperty(params, "remoteId"); } - } - - //set sending status as "queued" + } + + // set sending status as "queued" var map = new MailerResult(); - for( r in m.getRecipients() ){ - map.set( r.email , Success(Queued) ); + for (r in m.getRecipients()) { + map.set(r.email, Success(Queued)); } bm.status = map; bm.insert(); - if(callback!=null) callback(map); - + App.log('email from=<${m.getSender().email}>, subject=<${m.getSubject()}> buffered, waiting for cron to be further processed'); + if (callback != null) + callback(map); } - - - } - - \ No newline at end of file diff --git a/src/sugoi/mail/SendEmailMailer.hx b/src/sugoi/mail/SendEmailMailer.hx new file mode 100644 index 0000000..79227bc --- /dev/null +++ b/src/sugoi/mail/SendEmailMailer.hx @@ -0,0 +1,100 @@ +package sugoi.mail; + +import tink.core.Future; +import tink.core.Noise; +import sugoi.mail.IMailer; +import smtpmailer.Address; + +typedef SmtpInfo = { + host:String, + port:Int, + auth:{ + username:String, password:String + } +} + +/** + * Send emails thru OS command `sendemail` + * requires: `apt install sendemail libio-socket-ssl-perl libnet-ssleay-perl` + */ +class SendEmailMailer implements IMailer { + var m:SmtpInfo; + + public function new() {} + + public function init(?conf:{ + smtp_host:String, + smtp_port:Int, + smtp_user:String, + smtp_pass:String + }):IMailer { + var mailer:SendEmailMailer = new SendEmailMailer(); + mailer.m = { + host: conf.smtp_host, + port: conf.smtp_port, + auth: { + username: conf.smtp_user, + password: conf.smtp_pass + } + }; + return mailer; + } + + public function send(e:sugoi.mail.IMail, ?params:Dynamic, ?callback:MailerResult->Void) { + var mailServer = "mail1.zourit.net"; + var mailPort = 587; + + var fromName = "John Doe"; + var fromEmail = "no-reply@comptoirduvrac.re"; + + var args = [ + "-f", + '${fromName} <${fromEmail}>', + "-s", + "mail1.zourit.net:587", + "-t", + "pvincent@comptoirduvrac.re", + // "-bcc", "pvincent974@gmail.com,pvincent974@laposte.net", + "-u", + e.getSubject(), + "-m", + "alors tout va bien", + "-xu", + "postmaster@comptoirduvrac.re", + "-xp", + "QqQeAPT6EpoK" + ]; + + App.log('args=${args.join(" ")}'); + + var exitCode = Sys.command("sendemail", args); + // var exitCode = 0; + + if (exitCode == 0) + App.log('email from="${e.getSender().name} <${e.getSender().email}"> subject=<${e.getSubject()}> successfully sent'); + else + App.log('ERROR: email from="${e.getSender().name} <${e.getSender().email}"> subject=<${e.getSubject()}> cannot be sent'); + + // sys.io.File.saveContent('/tmp/my_file.json', "CONTENT"); + + // var surprise = m.send({ + // subject: e.getSubject(), + // 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() + // } + // }); + + if (callback != null) { + var map = new MailerResult(); + if (exitCode == 0) + map.set("*", Success(Sent)); + else + map.set("*", Failure(HardBounce)); + callback(map); + } + } +}