JavaLikeCalc.JavaScript
if(f_start)	io = tr = EVAL;

//-----------------------------------------------
//Request form of the UserProtocol ProtIt="SMTP":
//-----------------------------------------------
//<send auth="{user}:{pass}" from="{FromAddr}" to="{ToAddr}" topic="{My Topic}" err="1:Error">{MessageText}</send>
//  topic - topic of the message;
//  from - sender address;
//  to - destination address of the receiver;
//  auth - authenticate user and password;
//  MessageText - text of the message;
//  err - sets for the request result.
function SMTP( ) {
	//Transport's timings check
	if(tr.timings() != "5:1")	tr.timings("5:1");
	io.setAttr("err", "");
	if(!tr.start(true))	{ io.setAttr("err", "10:"+tr("Impossible connect to the SMTP-server.")); return; }

	//Wait for a greeting after the connection
	rez = "";
	for(iTr = 0; iTr < 5 && !rez.length; iTr++) rez += tr.messIO("");
	while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;

	auth = io.attr("auth");

	//Send HELLO
	rez = tr.messIO("EHLO "+io.attr("from").parse(1,"@")+"\x0D\x0A");
	while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
	if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
	for(off = 0; (sit=rez.parse(0,"\x0D\x0A",off)).length; )
		if(sit.toInt() != 250)	{ io.setAttr("err", "101:"+tr("Error HELLO: %1.").replace("%1",sit)); return; }
		else if(sit.indexOf("AUTH LOGIN") >= 0) {
			if(sit.indexOf("PLAIN") < 0)	{ io.setAttr("err", "101:"+tr("Unsupported auth methods: %1.").replace("%1",sit)); return; }
			else if(!auth.length)			{ io.setAttr("err", "101:"+tr("Auth required: %1.").replace("%1",sit)); return; }
		}

	//Send AUTH
	if(auth.length) {
		rez = tr.messIO("AUTH LOGIN\x0D\x0A");
		while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
		if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
		if(rez.toInt() != 334)	{ io.setAttr("err", "102:"+tr("AUTH error: %1.").replace("%1",rez)); return; }

		rez = tr.messIO(SYS.strEncode(io.attr("auth").parse(0,":"),"Base64")+"\x0D\x0A");
		while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
		if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
		if(rez.toInt() != 334)	{ io.setAttr("err", "102:"+tr("AUTH USER error: %1.").replace("%1",rez)); return; }

		rez = tr.messIO(SYS.strEncode(io.attr("auth").parse(1,":"),"Base64")+"\x0D\x0A");
		while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
		if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
		if(rez.toInt() != 235)	{ io.setAttr("err", "102:"+tr("AUTH PASS error: %1.").replace("%1",rez)); return; }
	}

	//Send FROM
	rez = tr.messIO("MAIL FROM:<"+io.attr("from")+">\x0D\x0A");
	while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
	if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
	if(rez.toInt() != 250)	{ io.setAttr("err", "103:"+tr("MAIL FROM error: %1.").replace("%1",rez)); return; }

	//Send TO
	rez = tr.messIO("RCPT TO:<"+io.attr("to")+">\x0D\x0A");
	while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
	if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
	if(rez.toInt() != 250)	{ io.setAttr("err", "104:"+tr("RCPT TO error: %1.").replace("%1",rez)); return; }

	//Prepare and send DATA
	rez = tr.messIO("DATA\x0D\x0A");
	while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
	if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
	if(rez.toInt() != 354)	{ io.setAttr("err", "105:"+tr("DATA error: %1.").replace("%1",rez)); return; }

	// Req version
	reqVer = SYS.XMLNode("get").setAttr("path","/%2fgen%2fver"); SYS.cntrReq(reqVer);

	data = "Message-ID: <"+rand(999999).toString()+"."+rand(999999999).toString()+"-OpenSCADA@"+SYS.system("hostname").parse(0,"\n")+">\x0D\x0A"+
				"From: \""+io.attr("from")+"\" <"+io.attr("from")+">\x0D\x0A"+
				"To: \""+io.attr("to")+"\" <"+io.attr("to")+">\x0D\x0A"+
				"Subject: =?utf8?B?"+SYS.strEncode(io.attr("topic"),"Base64")+"?=\x0D\x0A"+
				"Date: "+SYS.system("LANG=en date \"+%a, %d %b %Y %T %z\"").parse(0,"\n")+"\x0D\x0A"+
				//"Date: "+SYS.strftime(SYS.time(),"%a, %d %b %Y %T %z")+"\x0D\x0A"+
				"X-Mailer: OpenSCADA "+reqVer.text()+"\x0D\x0A"+
				"Content-type: text/plain;charset=utf-8\x0D\x0A\x0D\x0A"+
				io.text()+"\x0D\x0A"+
				"\x0D\x0A.\x0D\x0A";	//End for code 354
	rez = tr.messIO(data);
	while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
	if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
	if(rez.toInt() != 250)	{ io.setAttr("err", "105:"+tr("DATA send error: %1.").replace("%1",rez)); return; }

	//Send QUIT
	rez = tr.messIO("QUIT\x0D\x0A");
	while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;

	//SYS.messInfo("SMTP", "Response: "+rez);

	//Disconnect from the SMTP-server
	tr.start(false);
}

if(!tr.isEVal() && !io.isEVal() && io.attr("ProtIt") == "SMTP") { SMTP(); return; }

//--------------------------------------------
//Request form of the UserProtocol ProtIt="SMS":
//--------------------------------------------
//<cmd pin="1111" tel="+380XXXXXXXXX" text="{0|1}" err="1:Error">{SMSText}</cmd>
//  cmd - command, for now only "send" allowed;
//  pin - PIN code to the SIM card access;
//  tel - telephone number of the receiver (remote cell phone or GSM modem);
//  text{0,1} - SMS in the text mode, only ASCII;
//  SMSText - the message text;
//  err - sets for the request result.
function SMS( ) {
	//Transport's timings check
	if(tr.timings() != "5000:100")	tr.timings("5000:100");
	io.setAttr("err", "");
	if(io.name() == "send") {
		tel = io.attr("tel");
		if(!tel.length || tel[0] != "+") { io.setAttr("err", "100:"+tr("Telephone number error.")); return; }

		//Disable ECHO
		rez = tr.messIO("ATE0\r");
		while(rez.length && (trez=tr.messIO("")).length) rez += trez;
		if(rez.indexOf("OK\r") < 0) {
			io.setAttr("err", "101:"+tr("Error disabling ECHO: %1").replace("%1",rez));
			return;
		}

		//Set PIN
		if(io.attr("pin").length) {
			//Check for PIN
			rez = tr.messIO("AT+CPIN?\r");
			while(rez.length && (trez=tr.messIO("")).length) rez += trez;
			if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "102:"+tr("Error checking for PIN-code: %1").replace("%1",rez)); return; }
			if(rez.indexOf("+CPIN: READY\r") < 0) {	//PIN is needed
				rez = tr.messIO("AT+CPIN="+io.attr("pin")+"\r");
				while(rez.length && (trez=tr.messIO("")).length) rez += trez;
				if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "102:"+tr("Error setting PIN: %1").replace("%1",rez)); return; }
			}
		}

		if(io.attr("text").toInt()) {	//SMS in the text mode
			//Switch to Text SMS mode
			rez = tr.messIO("AT+CMGF=1\r");
			while(rez.length && (trez=tr.messIO("")).length) rez += trez;
			if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "103:"+tr("Error setting the SMS Text mode: %1").replace("%1",rez)); return; }

			//Send the Telephone number
			rez = tr.messIO("AT+CMGS=\""+tel+"\"\r");
			while(rez.length && (trez=tr.messIO("")).length) rez += trez;
			if(rez.indexOf(">") < 0)	{ io.setAttr("err", "104:"+tr("Error sending SMS: %1").replace("%1",rez)); return; }
			rez = tr.messIO(io.text()+"\x1A");
			while(rez.length && (trez=tr.messIO("")).length) rez += trez;
			//for(var iTr = 0; iTr < 100 && rez.indexOf("OK\r") < 0; iTr++) rez += tr.messIO("");	//Up to 10 seconds wait for reply
			if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "104:"+tr("Error sending SMS PDU: %1").replace("%1",rez)); return; }
		}
		else {	//SMS in the PDU mode
			// Switch to PDU SMS mode
			rez = tr.messIO("AT+CMGF=0\r");
			while(rez.length && (trez=tr.messIO("")).length) rez += trez;
			if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "103:"+tr("Error setting the SMS PDU mode: %1").replace("%1",rez)); return; }

			//Prepare PDU
			pdu = "001100";	//SMS center number (default) + SMS-Submit
			//Telephone number encode
			tel = tel.slice(1);
			pdu += tel.length.toString(16,2) + "91";	//Telephone length and type
			while(tel.length < 12) tel += "F";
			for(i = 0; i < 6; i++) pdu += tel[i*2+1]+tel[i*2];
			//Message encode
			text = SYS.strCodeConv(io.text(),"","UCS2");
			if((text.length/2) > 70)	text = text.slice(0,140);
			//{ io.setAttr("err", "104:"+tr("Too long length (%1) of the message.").replace("%1",(text.length/2).toString())); return; }
			pdu += "0018C1"+(text.length).toString(16,2);
			for(i = 0; i < text.length/2; i++) pdu += text.charCodeAt(i*2+1).toString(16,2)+text.charCodeAt(i*2).toString(16,2);
			//SYS.messDebug("TEST SMS","PDU :"+pdu);

			// Send the PDU message
			rez = tr.messIO("AT+CMGS="+(pdu.length/2-1)+"\r");
			while(rez.length && (trez=tr.messIO("")).length) rez += trez;
			if(rez.indexOf(">") < 0)	{ io.setAttr("err", "104:"+tr("Error sending SMS: %1").replace("%1",rez)); return; }
			rez = tr.messIO(pdu+"\x1A");
			while(rez.length && (trez=tr.messIO("")).length) rez += trez;
			//for(var iTr = 0; iTr < 100 && rez.indexOf("OK\r") < 0; iTr++) rez += tr.messIO("");	//Up to 10 seconds wait for reply
			if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "104:"+tr("Error sending SMS PDU: %1").replace("%1",rez)); return; }
			//SYS.messDebug("TEST SMS","PDU REZ :"+rez);
		}
		io.setAttr("err", "0");
	}
}

if(!tr.isEVal() && !io.isEVal() && io.attr("ProtIt") == "SMS")	{ SMS(); return; }

//---------------------------
//The notification dispatcher
//---------------------------
if(f_start) {
	queue = new Object();
	queueSMS = new Object();
	prTm = SYS.time();
	prTmU = 0;
	emailSentN = SMSSentN = 0;
	emailTrAddr_ = emailTrAddr;
	SMSTrAddr_ = SMSTrAddr;
	SMTPTr = emailTrAddr.length ? SYS.Transport.nodeAt(emailTrAddr, ".") : false;
	SMSTr = SMSTrAddr.length ? SYS.Transport.nodeAt(SMSTrAddr, ".") : false;
	return;	//Don't real process at start
}
if(f_stop)	{ emailState = SMSState = ""; return; }
if(!SMTPTr || SMTPTr.isEVal() || emailTrAddr_ != emailTrAddr) {
	SMTPTr = emailTrAddr.length ? SYS.Transport.nodeAt(emailTrAddr, ".") : false;
	emailTrAddr_ = emailTrAddr;
}
if(!SMSTr || SMSTr.isEVal() || SMSTrAddr_ != SMSTrAddr)	{
	SMSTr = SMSTrAddr.length ? SYS.Transport.nodeAt(SMSTrAddr, ".") : false;
	SMSTrAddr_ = SMSTrAddr;
}

//Current alarms reading, and the queue of dispatching update
mess = SYS.Archive.messGet(prTm, SYS.time(), messCat, abs(messLev), "<buffer>");
for(iM = 0; iM < mess.length; iM++) {
	oM = mess[iM];
	if((oM.tm == prTm && oM.utm <= prTmU) || (messLev < 0 && oM.level >= 0) ||
		(messCatExcl.length && (new RegExp(messCatExcl)).test(oM.categ)))	continue;

	if(emailTrAddr.length)	queue[oM.level.toString()+oM.tm.toString()+oM.categ] = oM;
	if(SMSTr)	queueSMS[oM.level.toString()+oM.tm.toString()+oM.categ] = oM;

	if((oM.tm*1e6+oM.utm) > (prTm*1e6+prTmU))	prTm = oM.tm, prTmU = oM.utm;
	//SYS.messInfo("NTF","Queue mess: "+oM.mess);
}

//The notifications mail queue process in a cycle limited by overall time
stTm = SYS.time();
emailQueueN = 0;
emailErr = "";
for(var nM in queue) {
	if(!SMTPTr)	break;
	if((SYS.time()-stTm) > tmOut)	{ emailQueueN++; continue; }
	oM = queue[nM];
	//SYS.messInfo("NTF","Send mess \""+nM+"\": "+oM.mess);

	topic = emailTopic + " (" + ((oM.level < 0)?tr("Alarm"):tr("Norm")) + ")";
	mess = emailMess + SYS.strftime(oM.tm) + ": " + oM.mess;

	tr = SMTPTr, io = SYS.XMLNode("send");
	io.setAttr("ProtIt", "SMTP").setAttr("auth", emailAuth).setAttr("from", emailSender).setAttr("to", emailReceiver).setAttr("topic", topic).setText(mess);
	SMTP();

	//Remove the message from the queue anytime
	delete queue[nM]; emailSentN++;
	// and just notify the SMTP errors
	if((tVl=io.attr("err").toInt())) {
		if(tVl == 10)	SMTPTr = false;	//Reconnect the transport
		SYS.messWarning("/service/NTF", "EMail(SMTP) sending error: "+io.attr("err"));
	}
	io = tr = EVAL;

	//SYS.messInfo("NTF", "Send error: "+io.attr("err"));
	/*if(!(tVl=io.attr("err").toInt())) { delete queue[nM]; emailSentN++; }
	else {
		if(tVl == 10)	SMTPTr = false;	//Reconnect the transport
		emailQueueN++;
		if(!emailErr.length)	emailErr = io.attr("err");
	}
	io = tr = EVAL;

	/*req = "sendemail -q -f " + emailSender + " -t " + emailReceiver +
			" -u \"=?utf8?B?" + SYS.strEncode(topic,"Base64") + "?=\" -m \"" + mess + "\" -s " + emailTrAddr;
	if(emailAuth.length)	req += " -xu " + emailAuth.parse(0,":") + " -xp " + emailAuth.parse(1,":");
	req += " -o tls=no -o message-charset=utf8";
	//SYS.messInfo("NTF","Send mess: "+req);
	rez = SYS.system(req, true);
	if(!rez) { delete queue[nM]; emailSentN++; }
	else {
		emailQueueN++;
		if(!emailErr.length)	emailErr = req.attr("err");
	}*/
}
if(!SMTPTr)	emailState = "Disabled!";
else {
	emailState = tr("Sent %1. In queue %2.").replace("%1",emailSentN.toString()).replace("%2",emailQueueN.toString());
	if(emailErr.length) emailState += " "+tr("Error: %1.").replace("%1",emailErr);
}

//The notifications SMS queue process in a cycle limited by overall time
stTm = SYS.time();
SMSQueueN = 0;
SMSErr = "";
for(var nM in queueSMS) {
	if(!SMSTr)	break;
	if((SYS.time()-stTm) > tmOut)	{ SMSQueueN++; continue; }
	oM = queueSMS[nM];
	//SYS.messInfo("NTF","Send SMS \""+nM+"\": "+oM.mess);

	mess = oM.mess;
	tr = SMSTr, io = SYS.XMLNode("send");
	io.setAttr("ProtIt", "SMS").setAttr("pin", SMSPin).setAttr("text",SMSTextMd).setAttr("tel", SMSTel).setText(mess);
	SMS();
	if(!io.attr("err").toInt()) { delete queueSMS[nM]; SMSSentN++; }
	else {
		SMSQueueN++;
		if(!SMSErr.length)	SMSErr = io.attr("err");
	}
	io = tr = EVAL;
}
if(!SMSTr)	SMSState = "Disabled!";
else {
	SMSState = tr("Sent %1. In queue %2.").replace("%1",SMSSentN.toString()).replace("%2",SMSQueueN.toString());
	if(SMSErr.length) SMSState += " "+tr("Error: %1.").replace("%1",SMSErr);
}