diff --git a/common/remote/rmtsmtp.cpp b/common/remote/rmtsmtp.cpp index 7a3cf970ea8..c0dad58a889 100644 --- a/common/remote/rmtsmtp.cpp +++ b/common/remote/rmtsmtp.cpp @@ -476,14 +476,15 @@ class CMailInfo char inbuff[200]; unsigned inlen; bool highPriority; + bool termJobOnFail; static char const * toHeader; static char const * ccHeader; static char const * subjectHeader; static char const * senderHeader; public: - CMailInfo(char const * _to, char const * _cc, char const * _bcc, char const * _subject, char const * _mailServer, unsigned _port, char const * _sender, StringArray *_warnings, bool _highPriority) - : subject(_subject), mailServer(_mailServer), port(_port), sender(_sender), lastAction("process initialization"), inlen(0), highPriority(_highPriority) + CMailInfo(char const * _to, char const * _cc, char const * _bcc, char const * _subject, char const * _mailServer, unsigned _port, char const * _sender, StringArray *_warnings, bool _highPriority, bool _termJobOnFail) + : subject(_subject), mailServer(_mailServer), port(_port), sender(_sender), lastAction("process initialization"), inlen(0), highPriority(_highPriority), termJobOnFail(_termJobOnFail) { warnings = _warnings; CSMTPValidator validator; @@ -528,6 +529,7 @@ class CMailInfo lastAction.clear().append(action); else lastAction.clear().append(len, out).clip(); + try { socket->write(out, len); @@ -545,30 +547,34 @@ class CMailInfo } } - void read() + bool read(int numRetriesRemaining) { try { - socket->read(inbuff,1,sizeof(inbuff),inlen); + socket->readtms(inbuff, 1, sizeof(inbuff), inlen, 30000); + //MORE: the following is somewhat primitive and not RFC compliant (see bug 25951) - but it is a lot better than nothing - if((*inbuff == '4') || (*inbuff == '5')) + if ( (*inbuff == '5') || ((*inbuff == '4') && (!numRetriesRemaining)) ) { StringBuffer b; - b.append("Negative reply from mail server at ").append(mailServer.get()).append(":").append(port).append(" after writing ").append(lastAction.str()).append(" in SendEmail*: ").append(inlen, inbuff).clip(); - WARNLOG("%s", b.str()); - if (warnings) - warnings->append(b.str()); + b.append("negative reply after writing ").append(inlen, inbuff).clip(); + // don't continue on after these responses ... + throw makeStringException(MSGAUD_operator, 0, b.str()); } -#ifdef SMTP_TRACE else { +#ifdef SMTP_TRACE StringBuffer b(inlen, inbuff); b.clip(); DBGLOG("SMTP read: [%s]", b.str()); - } #endif + if (*inbuff == '4') + return false; + else + return true; + } } - catch(IException * e) + catch (IException * e) { int code = e->errorCode(); StringBuffer buff; @@ -578,6 +584,34 @@ class CMailInfo } } + void readRemainingData() + { + // read any remaining bytes ... + try + { + socket->readtms(inbuff, 0, sizeof(inbuff), inlen, 0); + } + catch(IException * e) + { + e->Release(); + } + } + + void writeAndAck(char const * out, size32_t len, char const * action = NULL) + { + bool ok; + int retries = 2; + while (retries >= 0) + { + readRemainingData(); + write(out, len, action); + ok = read(retries); + if (ok) + break; + retries--; + } + } + void getHeader(StringBuffer & header) const { header.append(senderHeader).append(sender.get()).append("\r\n"); @@ -615,6 +649,17 @@ class CMailInfo out.append("RCPT TO:<").append(rcpt).append(">\r\n"); } + bool getTermJobOnFail() const + { + return termJobOnFail; + } + + void addToWarnings(const char *warnStr) const + { + if (warnings && (warnStr && *warnStr)) + warnings->append(warnStr); + } + private: void getRecipients(CSMTPValidator & validator, char const * _to, StringBuffer &destBuffer) { @@ -773,77 +818,98 @@ static const char *quit="QUIT\r\n"; static void doSendEmail(CMailInfo & info, CMailPart const & part) { - info.open(); - StringBuffer outbuff; - - info.read(); - info.getHelo(outbuff); - info.write(outbuff.str(), outbuff.length()); - info.read(); - - info.getMailFrom(outbuff.clear()); - info.write(outbuff.str(), outbuff.length()); - info.read(); - - unsigned numRcpt = info.numRecipients(); - for(unsigned i=0; i= 0) + { + info.readRemainingData(); + info.write(outbuff.str(), outbuff.length(), "mail header"); + part.write(info); + info.write(endMail, strlen(endMail), "end of mail body"); + ok = info.read(retries); + if (ok) + break; + retries--; + } + + info.writeAndAck(quit, strlen(quit)); + } + catch(IException * e) + { + if (info.getTermJobOnFail()) + throw e; + + StringBuffer msg; + info.addToWarnings(e->errorMessage(msg).str()); + EXCLOG(MCoperatorError, e, "WARNING"); + e->Release(); + } } -void sendEmail(const char * to, const char * cc, const char * bcc, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, StringArray *warnings, bool highPriority) +void sendEmail(const char * to, const char * cc, const char * bcc, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, StringArray *warnings, bool highPriority, bool termJobOnFail) { - CMailInfo info(to, cc, bcc, subject, mailServer, port, sender, warnings, highPriority); + CMailInfo info(to, cc, bcc, subject, mailServer, port, sender, warnings, highPriority, termJobOnFail); CTextMailPart bodyPart(body, "text/plain; charset=ISO-8859-1", NULL); doSendEmail(info, bodyPart); } -void sendEmail(const char * to, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, StringArray *warnings, bool highPriority) +void sendEmail(const char * to, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, StringArray *warnings, bool highPriority, bool termJobOnFail) { - sendEmail(to, nullptr, nullptr, subject, body, mailServer, port, sender, warnings, highPriority); + sendEmail(to, nullptr, nullptr, subject, body, mailServer, port, sender, warnings, highPriority, termJobOnFail); } -void sendEmailAttachText(const char * to, const char * cc, const char * bcc, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings, bool highPriority) +void sendEmailAttachText(const char * to, const char * cc, const char * bcc, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings, bool highPriority, bool termJobOnFail) { - CMailInfo info(to, cc, bcc, subject, mailServer, port, sender, warnings, highPriority); + CMailInfo info(to, cc, bcc, subject, mailServer, port, sender, warnings, highPriority, termJobOnFail); CTextMailPart inlinedPart(body, "text/plain; charset=ISO-8859-1", NULL); CTextMailPart attachmentPart(attachment, mimeType, attachmentName); CMultiMailPart multiPart(inlinedPart, attachmentPart); doSendEmail(info, multiPart); } -void sendEmailAttachText(const char * to, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings, bool highPriority) +void sendEmailAttachText(const char * to, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings, bool highPriority, bool termJobOnFail) { - sendEmailAttachText(to, nullptr, nullptr, subject, body, attachment, mimeType, attachmentName, mailServer, port, sender, warnings, highPriority); + sendEmailAttachText(to, nullptr, nullptr, subject, body, attachment, mimeType, attachmentName, mailServer, port, sender, warnings, highPriority, termJobOnFail); } -void sendEmailAttachData(const char * to, const char * cc, const char * bcc, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings, bool highPriority) +void sendEmailAttachData(const char * to, const char * cc, const char * bcc, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings, bool highPriority, bool termJobOnFail) { - CMailInfo info(to, cc, bcc, subject, mailServer, port, sender, warnings, highPriority); + CMailInfo info(to, cc, bcc, subject, mailServer, port, sender, warnings, highPriority, termJobOnFail); CTextMailPart inlinedPart(body, "text/plain; charset=ISO-8859-1", NULL); CDataMailPart attachmentPart(lenAttachment, attachment, mimeType, attachmentName); CMultiMailPart multiPart(inlinedPart, attachmentPart); doSendEmail(info, multiPart); } -void sendEmailAttachData(const char * to, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings, bool highPriority) +void sendEmailAttachData(const char * to, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings, bool highPriority, bool termJobOnFail) { - sendEmailAttachData(to, nullptr, nullptr, subject, body, lenAttachment, attachment, mimeType, attachmentName, mailServer, port, sender, warnings, highPriority); + sendEmailAttachData(to, nullptr, nullptr, subject, body, lenAttachment, attachment, mimeType, attachmentName, mailServer, port, sender, warnings, highPriority, termJobOnFail); } diff --git a/common/remote/rmtsmtp.hpp b/common/remote/rmtsmtp.hpp index 22e3f687f2c..d5933b2fd37 100644 --- a/common/remote/rmtsmtp.hpp +++ b/common/remote/rmtsmtp.hpp @@ -27,14 +27,14 @@ #define REMOTE_API DECL_IMPORT #endif -extern REMOTE_API void sendEmail( const char * to, const char * cc, const char * bcc, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, StringArray *warnings=NULL, bool highPriority=false); -extern REMOTE_API void sendEmail( const char * to, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, StringArray *warnings=NULL, bool highPriority=false); +extern REMOTE_API void sendEmail( const char * to, const char * cc, const char * bcc, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, StringArray *warnings=NULL, bool highPriority=false, bool termJobOnFail=true); +extern REMOTE_API void sendEmail( const char * to, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, StringArray *warnings=NULL, bool highPriority=false, bool termJobOnFail=true); -extern REMOTE_API void sendEmailAttachText(const char * to, const char * cc, const char * bcc, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings=NULL, bool highPriority=false); -extern REMOTE_API void sendEmailAttachText(const char * to, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings=NULL, bool highPriority=false); +extern REMOTE_API void sendEmailAttachText(const char * to, const char * cc, const char * bcc, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings=NULL, bool highPriority=false, bool termJobOnFail=true); +extern REMOTE_API void sendEmailAttachText(const char * to, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings=NULL, bool highPriority=false, bool termJobOnFail=true); -extern REMOTE_API void sendEmailAttachData(const char * to, const char * cc, const char * bcc, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings=NULL, bool highPriority=false); -extern REMOTE_API void sendEmailAttachData(const char * to, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings=NULL, bool highPriority=false); +extern REMOTE_API void sendEmailAttachData(const char * to, const char * cc, const char * bcc, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings=NULL, bool highPriority=false, bool termJobOnFail=true); +extern REMOTE_API void sendEmailAttachData(const char * to, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, StringArray *warnings=NULL, bool highPriority=false, bool termJobOnFail=true); #endif diff --git a/ecllibrary/std/system/Email.ecl b/ecllibrary/std/system/Email.ecl index 96ea6db0296..3c4d3a05678 100644 --- a/ecllibrary/std/system/Email.ecl +++ b/ecllibrary/std/system/Email.ecl @@ -18,10 +18,11 @@ RETURN MODULE * @param cc Optional comma-delimited addresses of carbon-copy recipients. Defaults to an empty string (none). * @param bcc Optional comma-delimited addresses of blind-carbon-copy recipients. Defaults to an empty string (none). * @param highPriority Optional; if true, message is sent with high priority. Defaults to false (normal priority). + * @param rptErrAsFail Optional; if true, any error while trying to send email will result in an error/exception. Defaults to true. */ -EXPORT SendEmail(varstring to, varstring subject, varstring body, varstring mailServer=GETENV('SMTPserver'), unsigned4 port=(unsigned4) GETENV('SMTPport', '25'), varstring sender=GETENV('emailSenderAddress'), varstring cc='', varstring bcc='', boolean highPriority=false) := - lib_fileservices.FileServices.SendEmail(to, subject, body, mailServer, port, sender, cc, bcc, highPriority); +EXPORT SendEmail(varstring to, varstring subject, varstring body, varstring mailServer=GETENV('SMTPserver'), unsigned4 port=(unsigned4) GETENV('SMTPport', '25'), varstring sender=GETENV('emailSenderAddress'), varstring cc='', varstring bcc='', boolean highPriority=false, boolean rptErrAsFail=true) := + lib_fileservices.FileServices.SendEmail(to, subject, body, mailServer, port, sender, cc, bcc, highPriority, rptErrAsFail); /* * Sends an email message with a text attachment using a mail server. @@ -36,10 +37,11 @@ EXPORT SendEmail(varstring to, varstring subject, varstring body, varstring mail * @param cc Optional comma-delimited addresses of carbon-copy recipients. Defaults to an empty string (none). * @param bcc Optional comma-delimited addresses of blind-carbon-copy recipients. Defaults to an empty string (none). * @param highPriority Optional; if true, message is sent with high priority. Defaults to false (normal priority). + * @param rptErrAsFail Optional; if true, any error while trying to send email will result in an error/exception. Defaults to true. */ -EXPORT SendEmailAttachText(varstring to, varstring subject, varstring body, varstring attachment, varstring mimeType, varstring attachmentName, varstring mailServer=GETENV('SMTPserver'), unsigned4 port=(unsigned4) GETENV('SMTPport', '25'), varstring sender=GETENV('emailSenderAddress'), varstring cc='', varstring bcc='', boolean highPriority=false) := - lib_fileservices.FileServices.SendEmailAttachText(to, subject, body, attachment, mimeType, attachmentName, mailServer, port, sender, cc, bcc, highPriority); +EXPORT SendEmailAttachText(varstring to, varstring subject, varstring body, varstring attachment, varstring mimeType, varstring attachmentName, varstring mailServer=GETENV('SMTPserver'), unsigned4 port=(unsigned4) GETENV('SMTPport', '25'), varstring sender=GETENV('emailSenderAddress'), varstring cc='', varstring bcc='', boolean highPriority=false, boolean rptErrAsFail=true) := + lib_fileservices.FileServices.SendEmailAttachText(to, subject, body, attachment, mimeType, attachmentName, mailServer, port, sender, cc, bcc, highPriority, rptErrAsFail); /* * Sends an email message with an arbitrary attachment using a mail server. @@ -56,9 +58,10 @@ EXPORT SendEmailAttachText(varstring to, varstring subject, varstring body, vars * @param cc Optional comma-delimited addresses of carbon-copy recipients. Defaults to an empty string (none). * @param bcc Optional comma-delimited addresses of blind-carbon-copy recipients. Defaults to an empty string (none). * @param highPriority Optional; if true, message is sent with high priority. Defaults to false (normal priority). + * @param rptErrAsFail Optional; if true, any error while trying to send email will result in an error/exception. Defaults to true. */ -EXPORT SendEmailAttachData(varstring to, varstring subject, varstring body, data attachment, varstring mimeType, varstring attachmentName, varstring mailServer=GETENV('SMTPserver'), unsigned4 port=(unsigned4) GETENV('SMTPport', '25'), varstring sender=GETENV('emailSenderAddress'), varstring cc='', varstring bcc='', boolean highPriority=false) := - lib_fileservices.FileServices.SendEmailAttachData(to, subject, body, attachment, mimeType, attachmentName, mailServer, port, sender, cc, bcc, highPriority); +EXPORT SendEmailAttachData(varstring to, varstring subject, varstring body, data attachment, varstring mimeType, varstring attachmentName, varstring mailServer=GETENV('SMTPserver'), unsigned4 port=(unsigned4) GETENV('SMTPport', '25'), varstring sender=GETENV('emailSenderAddress'), varstring cc='', varstring bcc='', boolean highPriority=false, boolean rptErrAsFail=true) := + lib_fileservices.FileServices.SendEmailAttachData(to, subject, body, attachment, mimeType, attachmentName, mailServer, port, sender, cc, bcc, highPriority, rptErrAsFail); END; diff --git a/esp/services/ws_workunits/ws_workunitsHelpers.cpp b/esp/services/ws_workunits/ws_workunitsHelpers.cpp index a43d82d562f..4dacd1b8b3e 100644 --- a/esp/services/ws_workunits/ws_workunitsHelpers.cpp +++ b/esp/services/ws_workunits/ws_workunitsHelpers.cpp @@ -4503,7 +4503,7 @@ IFileIOStream* CWsWuFileHelper::createWUZAPFileIOStream(IEspContext& context, IC if (request.sendEmail) { - CWsWuEmailHelper emailHelper(request.emailFrom.str(), request.emailTo.str(), request.emailServer.str(), request.port); + CWsWuEmailHelper emailHelper(request.emailFrom.str(), request.emailTo.str(), request.emailServer.str(), request.port, true); StringBuffer subject(request.emailSubject.str()); if (subject.isEmpty()) @@ -4935,10 +4935,10 @@ void CWsWuFileHelper::readLocalFileToBuffer(const char* file, offset_t sizeLimit void CWsWuEmailHelper::send(const char* body, const void* attachment, size32_t lenAttachment, StringArray& warnings) { if (lenAttachment == 0) - sendEmail(to.get(), subject.get(), body, mailServer.get(), port, sender.get(), &warnings); + sendEmail(to.get(), subject.get(), body, mailServer.get(), port, sender.get(), &warnings, termOnJobFail); else sendEmailAttachData(to.get(), subject.get(), body, lenAttachment, attachment, mimeType.get(), - attachmentName.get(), mailServer.get(), port, sender.get(), &warnings); + attachmentName.get(), mailServer.get(), port, sender.get(), &warnings, termOnJobFail); } } diff --git a/esp/services/ws_workunits/ws_workunitsHelpers.hpp b/esp/services/ws_workunits/ws_workunitsHelpers.hpp index d43f0849601..cd5fc99d1f2 100644 --- a/esp/services/ws_workunits/ws_workunitsHelpers.hpp +++ b/esp/services/ws_workunits/ws_workunitsHelpers.hpp @@ -708,9 +708,10 @@ class CWsWuEmailHelper StringAttr mailServer, sender, to, subject, body; StringAttr attachmentName, mimeType; unsigned port; + bool termOnJobFail; public: - CWsWuEmailHelper(const char *_sender, const char *_to, const char *_mailServer, unsigned _port) - : sender(_sender), to(_to), mailServer(_mailServer), port(_port) {}; + CWsWuEmailHelper(const char *_sender, const char *_to, const char *_mailServer, unsigned _port, bool _termOnJobFail) + : sender(_sender), to(_to), mailServer(_mailServer), port(_port), termOnJobFail(_termOnJobFail) {}; void setSubject(const char *_subject) { subject.set(_subject); }; void setMimeType(const char *_mimeType) { mimeType.set(_mimeType); }; diff --git a/esp/src/eclwatch/stub.js b/esp/src/eclwatch/stub.js index cd91622159f..79fd6b04002 100644 --- a/esp/src/eclwatch/stub.js +++ b/esp/src/eclwatch/stub.js @@ -35,10 +35,10 @@ define([ if (modernMode === String(true) && hpccWidget !== "IFrameWidget") { switch (hpccWidget) { case "WUDetailsWidget": - window.location.replace(`/esp/files/index.html#/workunits/${params.Wuid}`); + window.location.replace(`/#/workunits/${params.Wuid}`); break; case "GraphsWUWidget": - window.location.replace(`/esp/files/index.html#/workunits/${params.Wuid}/metrics`); + window.location.replace(`/#/workunits/${params.Wuid}/metrics`); break; case "TopologyWidget": case "DiskUsageWidget": @@ -49,7 +49,7 @@ define([ loadUI(); break; default: - window.location.replace("/esp/files/index.html"); + window.location.replace("/"); } } else { loadUI(); diff --git a/esp/src/src-react/components/Frame.tsx b/esp/src/src-react/components/Frame.tsx index fda78a9b15e..35c1392b87a 100644 --- a/esp/src/src-react/components/Frame.tsx +++ b/esp/src/src-react/components/Frame.tsx @@ -1,18 +1,21 @@ import * as React from "react"; +import * as topic from "dojo/topic"; import { ThemeProvider } from "@fluentui/react"; import { FluentProvider } from "@fluentui/react-components"; import { select as d3Select } from "@hpcc-js/common"; import { scopedLogger } from "@hpcc-js/util"; -import { useUserTheme } from "../hooks/theme"; -import { useGlobalWorkunitNotes } from "../hooks/workunit"; import { HolyGrail } from "../layouts/HolyGrail"; import { hashHistory } from "../util/history"; import { router } from "../routes"; import { DevTitle } from "./Title"; import { MainNavigation, SubNavigation } from "./Menu"; import { CookieConsent } from "./forms/CookieConsent"; -import { userKeyValStore } from "../../src/KeyValStore"; +import { userKeyValStore } from "src/KeyValStore"; +import { fireIdle, initSession, lock, unlock } from "src/Session"; import { useGlobalStore } from "../hooks/store"; +import { useUserTheme } from "../hooks/theme"; +import { useGlobalWorkunitNotes } from "../hooks/workunit"; +import { useUserSession } from "../hooks/user"; const logger = scopedLogger("../components/Frame.tsx"); const envLogger = scopedLogger("environment"); @@ -23,6 +26,7 @@ interface FrameProps { export const Frame: React.FunctionComponent = () => { const [showCookieConsent, setShowCookieConsent] = React.useState(false); + const { userSession, setUserSession } = useUserSession(); const [locationPathname, setLocationPathname] = React.useState(window.location.hash.split("#").join("")); const [body, setBody] = React.useState(

...loading...

); const { theme, themeV9, isDark } = useUserTheme(); @@ -49,6 +53,24 @@ export const Frame: React.FunctionComponent = () => { }); }, [globalWUNotes]); + React.useEffect(() => { + initSession(); + + topic.subscribe("hpcc/session_management_status", function (publishedMessage) { + if (publishedMessage.status === "Unlocked") { + unlock(); + } else if (publishedMessage.status === "Locked") { + lock(); + } else if (publishedMessage.status === "DoIdle") { + fireIdle(); + } else if (publishedMessage.status === "Idle") { + window.localStorage.setItem("pageOnLock", window.location.hash.substring(1)); + setUserSession({ ...userSession, Status: "Locked" }); + window.location.reload(); + } + }); + }, [setUserSession, userSession]); + React.useEffect(() => { const unlisten = hashHistory.listen(async (location, action) => { @@ -69,7 +91,43 @@ export const Frame: React.FunctionComponent = () => { }, []); React.useEffect(() => { - document.title = `${showEnvironmentTitle && environmentTitle.length ? environmentTitle : "ECL Watch"}${locationPathname.split("/").join(" | ")}`; + initSession(); + + topic.subscribe("hpcc/session_management_status", function (publishedMessage) { + if (publishedMessage.status === "Unlocked") { + unlock(); + } else if (publishedMessage.status === "Locked") { + lock(); + } else if (publishedMessage.status === "DoIdle") { + fireIdle(); + } else if (publishedMessage.status === "Idle") { + window.localStorage.setItem("pageOnLock", window.location.hash.substring(1)); + setUserSession({ ...userSession, Status: "Locked" }); + window.location.reload(); + } + }); + }, [setUserSession, userSession]); + + React.useEffect(() => { + initSession(); + + topic.subscribe("hpcc/session_management_status", function (publishedMessage) { + if (publishedMessage.status === "Unlocked") { + unlock(); + } else if (publishedMessage.status === "Locked") { + lock(); + } else if (publishedMessage.status === "DoIdle") { + fireIdle(); + } else if (publishedMessage.status === "Idle") { + window.localStorage.setItem("pageOnLock", window.location.hash.substring(1)); + setUserSession({ ...userSession, Status: "Locked" }); + window.location.reload(); + } + }); + }, [setUserSession, userSession]); + + React.useEffect(() => { + document.title = `${showEnvironmentTitle && environmentTitle.length ? environmentTitle : "ECL Watch "}${locationPathname.split("/").join(" | ")}`; }, [environmentTitle, locationPathname, showEnvironmentTitle]); React.useEffect(() => { diff --git a/esp/src/src-react/components/forms/Login.tsx b/esp/src/src-react/components/forms/Login.tsx index a5dc021cb51..a6d7d39c769 100644 --- a/esp/src/src-react/components/forms/Login.tsx +++ b/esp/src/src-react/components/forms/Login.tsx @@ -37,6 +37,15 @@ export const Login: React.FunctionComponent = ({ const [showError, setShowError] = React.useState(false); const [errorMessage, setErrorMessage] = React.useState(""); + React.useEffect(() => { + const cookies = Utility.parseCookies(); + if (cookies["ESPSessionState"] === "true") { + const lastUrl = window.localStorage.getItem("pageOnLock") ?? "/"; + window.localStorage.removeItem("pageOnLock"); + replaceUrl(lastUrl); + } + }, []); + const loginStyles = React.useMemo(() => mergeStyleSets({ root: { height: "100%", @@ -107,6 +116,8 @@ export const Login: React.FunctionComponent = ({ setErrorMessage(cookies.ESPAuthenticationMSG); setShowError(true); } else { + cookies["Status"] = "Unlocked"; + cookies["ESPAuthenticated"] = "true"; createUserSession(cookies).then(() => { setErrorMessage(""); replaceUrl("/", null, true); diff --git a/esp/src/src-react/index.tsx b/esp/src/src-react/index.tsx index 3203e3f27ed..7bc9398ebca 100644 --- a/esp/src/src-react/index.tsx +++ b/esp/src/src-react/index.tsx @@ -5,6 +5,7 @@ import { scopedLogger } from "@hpcc-js/util"; import { cookieKeyValStore, userKeyValStore } from "src/KeyValStore"; import { ModernMode } from "src/BuildInfo"; import { ECLWatchLogger } from "./hooks/logging"; +import { replaceUrl } from "./util/history"; import "css!dijit-themes/flat/flat.css"; import "css!hpcc/css/ecl.css"; @@ -40,8 +41,10 @@ store.getEx(ModernMode, { defaultValue: String(true) }).then(async modernMode => const authType = await authTypeResp?.text() ?? "None"; const userStore = cookieKeyValStore(); const userSession = await userStore.getAll(); - if (authType.indexOf("None") < 0 && (!userSession["ESPAuthenticated"] && (!userSession["ECLWatchUser"] || !userSession["Status"] || userSession["Status"] === "Locked"))) { - window.location.replace("#/login"); + if (authType.indexOf("None") < 0 && (userSession["ESPSessionState"] === "false" || userSession["ECLWatchUser"] === "false" || (!userSession["Status"] || userSession["Status"] === "Locked"))) { + if (window.location.hash.indexOf("login") < 0) { + replaceUrl("/login"); + } import("./components/forms/Login").then(_ => { try { ReactDOM.render( diff --git a/esp/src/src/ESPUtil.ts b/esp/src/src/ESPUtil.ts index 43cf058435d..8c8a94e4abc 100644 --- a/esp/src/src/ESPUtil.ts +++ b/esp/src/src/ESPUtil.ts @@ -314,10 +314,10 @@ export function goToPageUserPreference(gridName, key) { export const MonitorLockClick = dojo.declare([Evented], { unlocked() { - this.emit("unlocked", {}); + this.emit("Unlocked", {}); }, locked() { - this.emit("locked", {}); + this.emit("Locked", {}); } }); diff --git a/esp/src/src/KeyValStore.ts b/esp/src/src/KeyValStore.ts index 3066af2b4cd..8dc900bf2d3 100644 --- a/esp/src/src/KeyValStore.ts +++ b/esp/src/src/KeyValStore.ts @@ -212,7 +212,7 @@ class CookieStorage implements IKeyValStore { set(key: string, value: string, broadcast?: boolean): Promise { const cookies = Utility.parseCookies(); const oldValue = cookies[key]; - document.cookie = `${key}=${value}`; + document.cookie = `${key}=${value};path=/`; return Promise.resolve().then(() => { if (broadcast) { this._dispatch.post(new ValueChangedMessage(key, value, oldValue)); diff --git a/esp/src/src/Session.ts b/esp/src/src/Session.ts index d24351a7771..530fe131694 100644 --- a/esp/src/src/Session.ts +++ b/esp/src/src/Session.ts @@ -14,7 +14,6 @@ const espTimeoutSeconds = cookie("ESPSessionTimeoutSeconds") || 600; // 10 minun const IDLE_TIMEOUT = espTimeoutSeconds * 1000; const SESSION_RESET_FREQ = 30 * 1000; const idleWatcher = new ESPUtil.IdleWatcher(IDLE_TIMEOUT); -const monitorLockClick = new ESPUtil.MonitorLockClick(); const sessionIsActive = espTimeoutSeconds; let _prevReset = Date.now(); @@ -73,7 +72,9 @@ export function initSession() { }); idleWatcher.start(); - monitorLockClick.unlocked(); + if (!cookie("Status")) { + document.cookie = "Status=Unlocked;Path=/"; + } } else if (cookie("ECLWatchUser")) { window.location.replace(dojoConfig.urlInfo.basePath + "/Login.html"); } diff --git a/plugins/fileservices/fileservices.cpp b/plugins/fileservices/fileservices.cpp index 058a1638673..e7ae50bd641 100644 --- a/plugins/fileservices/fileservices.cpp +++ b/plugins/fileservices/fileservices.cpp @@ -523,43 +523,64 @@ FILESERVICES_API void FILESERVICES_CALL fsRenameLogicalFile_v2(ICodeContext *ctx } -FILESERVICES_API void FILESERVICES_CALL fsSendEmail_v2(ICodeContext * ctx, const char * to, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, const char *cc, const char *bcc, bool highPriority) +FILESERVICES_API void FILESERVICES_CALL fsSendEmail_v3(ICodeContext * ctx, const char * to, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, const char *cc, const char *bcc, bool highPriority, bool termJobOnFail) { StringArray warnings; - sendEmail( to, cc, bcc, subject, body, mailServer, port, sender, &warnings, highPriority); + sendEmail( to, cc, bcc, subject, body, mailServer, port, sender, &warnings, highPriority, termJobOnFail); ForEachItemIn(i,warnings) WUmessage(ctx, SeverityWarning, "SendEmail", warnings.item(i)); } +FILESERVICES_API void FILESERVICES_CALL fsSendEmail_v2(ICodeContext * ctx, const char * to, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, const char *cc, const char *bcc, bool highPriority) +{ + fsSendEmail_v3(ctx, to, subject, body, mailServer, port, sender, cc, bcc, highPriority, true); +} + FILESERVICES_API void FILESERVICES_CALL fsSendEmail(ICodeContext * ctx, const char * to, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender) { - fsSendEmail_v2(ctx, to, subject, body, mailServer, port, sender, nullptr, nullptr, false); + fsSendEmail_v3(ctx, to, subject, body, mailServer, port, sender, nullptr, nullptr, false, true); +} + +FILESERVICES_API void FILESERVICES_CALL fsSendEmailAttachText_v3(ICodeContext * ctx, const char * to, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, const char *cc, const char *bcc, bool highPriority, bool termJobOnFail) +{ + StringArray warnings; + sendEmailAttachText(to, cc, bcc, subject, body, attachment, mimeType, attachmentName, mailServer, port, sender, &warnings, highPriority, termJobOnFail); + ForEachItemIn(i,warnings) + WUmessage(ctx, SeverityWarning, "SendEmailAttachText", warnings.item(i)); } FILESERVICES_API void FILESERVICES_CALL fsSendEmailAttachText_v2(ICodeContext * ctx, const char * to, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, const char *cc, const char *bcc, bool highPriority) { StringArray warnings; - sendEmailAttachText(to, cc, bcc, subject, body, attachment, mimeType, attachmentName, mailServer, port, sender, &warnings, highPriority); + sendEmailAttachText(to, cc, bcc, subject, body, attachment, mimeType, attachmentName, mailServer, port, sender, &warnings, highPriority, true); ForEachItemIn(i,warnings) WUmessage(ctx, SeverityWarning, "SendEmailAttachText", warnings.item(i)); } FILESERVICES_API void FILESERVICES_CALL fsSendEmailAttachText(ICodeContext * ctx, const char * to, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender) { - fsSendEmailAttachText_v2(ctx, to, subject, body, attachment, mimeType, attachmentName, mailServer, port, sender, nullptr, nullptr, false); + fsSendEmailAttachText_v3(ctx, to, subject, body, attachment, mimeType, attachmentName, mailServer, port, sender, nullptr, nullptr, false, true); +} + +FILESERVICES_API void FILESERVICES_CALL fsSendEmailAttachData_v3(ICodeContext * ctx, const char * to, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, const char *cc, const char *bcc, bool highPriority, bool termJobOnFail) +{ + StringArray warnings; + sendEmailAttachData(to, cc, bcc, subject, body, lenAttachment, attachment, mimeType, attachmentName, mailServer, port, sender, &warnings, highPriority, termJobOnFail); + ForEachItemIn(i,warnings) + WUmessage(ctx, SeverityWarning, "SendEmailAttachData", warnings.item(i)); } FILESERVICES_API void FILESERVICES_CALL fsSendEmailAttachData_v2(ICodeContext * ctx, const char * to, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, const char *cc, const char *bcc, bool highPriority) { StringArray warnings; - sendEmailAttachData(to, cc, bcc, subject, body, lenAttachment, attachment, mimeType, attachmentName, mailServer, port, sender, &warnings, highPriority); + sendEmailAttachData(to, cc, bcc, subject, body, lenAttachment, attachment, mimeType, attachmentName, mailServer, port, sender, &warnings, highPriority, true); ForEachItemIn(i,warnings) WUmessage(ctx, SeverityWarning, "SendEmailAttachData", warnings.item(i)); } FILESERVICES_API void FILESERVICES_CALL fsSendEmailAttachData(ICodeContext * ctx, const char * to, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender) { - fsSendEmailAttachData_v2(ctx, to, subject, body, lenAttachment, attachment, mimeType, attachmentName, mailServer, port, sender, nullptr, nullptr, false); + fsSendEmailAttachData_v3(ctx, to, subject, body, lenAttachment, attachment, mimeType, attachmentName, mailServer, port, sender, nullptr, nullptr, false, true); } diff --git a/plugins/fileservices/fileservices.hpp b/plugins/fileservices/fileservices.hpp index a8d5b762f61..a34fc86ab7f 100644 --- a/plugins/fileservices/fileservices.hpp +++ b/plugins/fileservices/fileservices.hpp @@ -46,10 +46,13 @@ FILESERVICES_API bool FILESERVICES_CALL fsFileValidate(ICodeContext *ctx, const FILESERVICES_API void FILESERVICES_CALL fsSetReadOnly(ICodeContext *ctx, const char *name, bool ro); FILESERVICES_API void FILESERVICES_CALL fsRenameLogicalFile(ICodeContext *ctx, const char *oldname, const char *newname); FILESERVICES_API void FILESERVICES_CALL fsRenameLogicalFile_v2(ICodeContext *ctx, const char *oldname, const char *newname, bool overwrite=false); +FILESERVICES_API void FILESERVICES_CALL fsSendEmail_v3(ICodeContext * ctx, const char *to, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, const char *cc, const char *bcc, bool highPriority, bool termJobOnFail); FILESERVICES_API void FILESERVICES_CALL fsSendEmail_v2(ICodeContext * ctx, const char *to, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender, const char *cc, const char *bcc, bool highPriority); FILESERVICES_API void FILESERVICES_CALL fsSendEmail(ICodeContext * ctx, const char *to, const char * subject, const char * body, const char * mailServer, unsigned port, const char * sender); +FILESERVICES_API void FILESERVICES_CALL fsSendEmailAttachText_v3(ICodeContext * ctx, const char * to, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, const char *cc, const char *bcc, bool highPriority, bool termJobOnFail); FILESERVICES_API void FILESERVICES_CALL fsSendEmailAttachText_v2(ICodeContext * ctx, const char * to, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, const char *cc, const char *bcc, bool highPriority); FILESERVICES_API void FILESERVICES_CALL fsSendEmailAttachText(ICodeContext * ctx, const char * to, const char * subject, const char * body, const char * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender); +FILESERVICES_API void FILESERVICES_CALL fsSendEmailAttachData_v3(ICodeContext * ctx, const char * to, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, const char *cc, const char *bcc, bool highPriority, bool termJobOnFail); FILESERVICES_API void FILESERVICES_CALL fsSendEmailAttachData_v2(ICodeContext * ctx, const char * to, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender, const char *cc, const char *bcc, bool highPriority); FILESERVICES_API void FILESERVICES_CALL fsSendEmailAttachData(ICodeContext * ctx, const char * to, const char * subject, const char * body, size32_t lenAttachment, const void * attachment, const char * mimeType, const char * attachmentName, const char * mailServer, unsigned int port, const char * sender); FILESERVICES_API void FILESERVICES_CALL fsSprayFixed(ICodeContext *ctx, const char * sourceIP, const char * sourcePath, int recordSize, const char * destinationGroup, const char * destinationLogicalName, int timeOut, const char * espServerIpPort, int maxConnections, bool overwrite=false, bool replicate=false, bool compress=false, bool failIfNoSourceFile=false); diff --git a/plugins/proxies/lib_fileservices.ecllib b/plugins/proxies/lib_fileservices.ecllib index d1ea58e7276..a63d05e8431 100644 --- a/plugins/proxies/lib_fileservices.ecllib +++ b/plugins/proxies/lib_fileservices.ecllib @@ -36,9 +36,9 @@ export FileServices := SERVICE : plugin('fileservices'), time SetReadOnly(const varstring lfn, boolean ro) : c,action,context,entrypoint='fsSetReadOnly'; RenameLogicalFile(const varstring oldname, const varstring newname, boolean allowoverwrite=false) : c,action,context,entrypoint='fsRenameLogicalFile_v2'; varstring GetBuildInfo() : c,pure,entrypoint='fsGetBuildInfo'; - SendEmail(const varstring to, const varstring subject, const varstring body, const varstring mailServer=GETENV('SMTPserver'), unsigned4 port=(unsigned4) GETENV('SMTPport', '25'), const varstring sender=GETENV('emailSenderAddress'), const varstring cc='', const varstring bcc='', boolean highPriority=false) : c,action,context,entrypoint='fsSendEmail_v2'; - SendEmailAttachText(const varstring to, const varstring subject, const varstring body, const varstring attachment, const varstring mimeType, const varstring attachmentName, const varstring mailServer=GETENV('SMTPserver'), unsigned4 port=(unsigned4) GETENV('SMTPport', '25'), const varstring sender=GETENV('emailSenderAddress'), const varstring cc='', const varstring bcc='', boolean highPriority=false) : c,action,context,entrypoint='fsSendEmailAttachText_v2'; - SendEmailAttachData(const varstring to, const varstring subject, const varstring body, const data attachment, const varstring mimeType, const varstring attachmentName, const varstring mailServer=GETENV('SMTPserver'), unsigned4 port=(unsigned4) GETENV('SMTPport', '25'), const varstring sender=GETENV('emailSenderAddress'), const varstring cc='', const varstring bcc='', boolean highPriority=false) : c,action,context,entrypoint='fsSendEmailAttachData_v2'; + SendEmail(const varstring to, const varstring subject, const varstring body, const varstring mailServer=GETENV('SMTPserver'), unsigned4 port=(unsigned4) GETENV('SMTPport', '25'), const varstring sender=GETENV('emailSenderAddress'), const varstring cc='', const varstring bcc='', boolean highPriority=false, boolean termJobOnFail=true) : c,action,context,entrypoint='fsSendEmail_v3'; + SendEmailAttachText(const varstring to, const varstring subject, const varstring body, const varstring attachment, const varstring mimeType, const varstring attachmentName, const varstring mailServer=GETENV('SMTPserver'), unsigned4 port=(unsigned4) GETENV('SMTPport', '25'), const varstring sender=GETENV('emailSenderAddress'), const varstring cc='', const varstring bcc='', boolean highPriority=false, boolean termJobOnFail=true) : c,action,context,entrypoint='fsSendEmailAttachText_v3'; + SendEmailAttachData(const varstring to, const varstring subject, const varstring body, const data attachment, const varstring mimeType, const varstring attachmentName, const varstring mailServer=GETENV('SMTPserver'), unsigned4 port=(unsigned4) GETENV('SMTPport', '25'), const varstring sender=GETENV('emailSenderAddress'), const varstring cc='', const varstring bcc='', boolean highPriority=false, boolean termJobOnFail=true) : c,action,context,entrypoint='fsSendEmailAttachData_v3'; SprayFixed(const varstring sourceIP='', const varstring sourcePath, integer4 recordSize, const varstring destinationGroup, const varstring destinationLogicalName, integer4 timeOut=-1, const varstring espServerIpPort=GETENV('ws_fs_server'), integer4 maxConnections=-1, boolean allowoverwrite=false, boolean replicate=false,boolean compress=false, boolean failIfNoSourceFile=false, integer4 expireDays=-1, const varstring dfuServerQueue='', boolean noSplit=false, const varstring sourcePlane='', unsigned4 destinationNumParts=0, boolean noCommon=true) : c,action,context,entrypoint='fsSprayFixed_v6'; SprayVariable(const varstring sourceIP='', const varstring sourcePath, integer4 sourceMaxRecordSize=8192, const varstring sourceCsvSeparate='\\,', const varstring sourceCsvTerminate='\\n,\\r\\n', const varstring sourceCsvQuote='\"', const varstring destinationGroup, const varstring destinationLogicalName, integer4 timeOut=-1, const varstring espServerIpPort=GETENV('ws_fs_server'), integer4 maxConnections=-1, boolean allowoverwrite=false, boolean replicate=false,boolean compress=false,const varstring sourceCsvEscape='', boolean failIfNoSourceFile=false, boolean recordStructurePresent=false, boolean quotedTerminator=true, const varstring encoding='ascii', integer4 expireDays=-1, const varstring dfuServerQueue='', boolean noSplit=false, const varstring sourcePlane='', unsigned4 destinationNumParts=0, boolean noCommon=true) : c,action,context,entrypoint='fsSprayVariable_v10'; SprayXml(const varstring sourceIP='', const varstring sourcePath, integer4 sourceMaxRecordSize=8192, const varstring sourceRowTag, const varstring sourceEncoding='utf8', const varstring destinationGroup, const varstring destinationLogicalName, integer4 timeOut=-1, const varstring espServerIpPort=GETENV('ws_fs_server'), integer4 maxConnections=-1, boolean allowoverwrite=false, boolean replicate=false,boolean compress=false, boolean failIfNoSourceFile=false, integer4 expireDays=-1, const varstring dfuServerQueue='', boolean noSplit=false, const varstring sourcePlane='', unsigned4 destinationNumParts=0, boolean noCommon=true) : c,action,context,entrypoint='fsSprayXml_v6'; diff --git a/tools/swapnode/swapnodelib.cpp b/tools/swapnode/swapnodelib.cpp index f5eb6afc8ec..63e5e4a2af4 100644 --- a/tools/swapnode/swapnodelib.cpp +++ b/tools/swapnode/swapnodelib.cpp @@ -285,7 +285,7 @@ class CSwapNode // add tbd StringBuffer ips; StringArray warnings; - sendEmail(emailtarget.str(),subject,out.str(),ep.getIpText(ips).str(),ep.port,sender.str(),&warnings); + sendEmail(emailtarget.str(),subject,out.str(),ep.getIpText(ips).str(),ep.port,sender.str(),&warnings, false); ForEachItemIn(i,warnings) WARNLOG("SWAPNODE: %s",warnings.item(i)); }