From 695c808727a5267343d37ea9d980161d483d18fc Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Wed, 5 Mar 2025 14:00:22 -0800 Subject: [PATCH 1/2] Fix EmailRecordTable row indexing Clean up dumbster classes and emailWebPart --- .../com/dumbster/smtp/SimpleSmtpServer.java | 44 ++++++++-------- .../src/com/dumbster/smtp/SmtpActionType.java | 2 +- .../src/com/dumbster/smtp/SmtpMessage.java | 50 +++++++++++-------- .../src/com/dumbster/smtp/SmtpRequest.java | 19 +++---- .../src/com/dumbster/smtp/SmtpResponse.java | 6 +-- .../src/com/dumbster/smtp/SmtpState.java | 2 +- .../org/labkey/dumbster/view/mailWebPart.jsp | 47 ++++++++--------- .../components/dumbster/EmailRecordTable.java | 15 +++--- 8 files changed, 93 insertions(+), 92 deletions(-) diff --git a/modules/dumbster/src/com/dumbster/smtp/SimpleSmtpServer.java b/modules/dumbster/src/com/dumbster/smtp/SimpleSmtpServer.java index c10791cc05..54dc3f920c 100644 --- a/modules/dumbster/src/com/dumbster/smtp/SimpleSmtpServer.java +++ b/modules/dumbster/src/com/dumbster/smtp/SimpleSmtpServer.java @@ -16,26 +16,30 @@ */ package com.dumbster.smtp; +import org.apache.logging.log4j.Logger; +import org.labkey.api.util.logging.LogHelper; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; -import java.util.List; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Iterator; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.io.IOException; +import java.util.List; /** * Dummy SMTP server for testing purposes. - * - * @todo constructor allowing user to pass preinitialized ServerSocket */ public class SimpleSmtpServer implements Runnable { + private static final Logger LOG = LogHelper.getLogger(SimpleSmtpServer.class, "Dumbster module SMTP Server"); + /** * Stores all of the email received since this instance started up. */ - private List receivedMail; + private final List receivedMail; /** * Default SMTP port is 25. @@ -91,19 +95,16 @@ public void run() { // Server: loop until stopped while (!isStopped()) { // Start server socket and listen for client connections - Socket socket = null; + Socket socket; try { socket = serverSocket.accept(); } catch (Exception e) { - if (socket != null) { - socket.close(); - } continue; // Non-blocking socket timeout occurred: try accept() again } // Get the input and output streams - BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream())); - PrintWriter out = new PrintWriter(socket.getOutputStream()); + BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)); + PrintWriter out = new PrintWriter(socket.getOutputStream(), false, StandardCharsets.UTF_8); synchronized (this) { /* @@ -112,20 +113,22 @@ public void run() { * For higher concurrency, we could just change handle to return void and update the list inside the method * to limit the duration that we hold the lock. */ - List msgs = handleTransaction(out, input); + List msgs = handleTransaction(out, input); receivedMail.addAll(msgs); } socket.close(); } } catch (Exception e) { - /** @todo Should throw an appropriate exception here. */ - e.printStackTrace(); + if (!isStopped()) + { + LOG.warn(e); + } } finally { if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { - e.printStackTrace(); + LOG.warn("Error shutting down Dumbster SMTP server", e); } } } @@ -165,7 +168,7 @@ public synchronized void stop() { * @return List of SmtpMessage * @throws IOException */ - private List handleTransaction(PrintWriter out, BufferedReader input) throws IOException { + private List handleTransaction(PrintWriter out, BufferedReader input) throws IOException { // Initialize the state machine SmtpState smtpState = SmtpState.CONNECT; SmtpRequest smtpRequest = new SmtpRequest(SmtpActionType.CONNECT, "", smtpState); @@ -207,6 +210,7 @@ private List handleTransaction(PrintWriter out, BufferedReader input) throws IOE } } + LOG.debug("Dumbster received {} message{}", msgList::size, () -> msgList.size() != 1 ? "s" : ""); return msgList; } @@ -228,7 +232,7 @@ private static void sendResponse(PrintWriter out, SmtpResponse smtpResponse) { * Get email received by this instance since start up. * @return List of String */ - public synchronized Iterator getReceivedEmail() { + public synchronized Iterator getReceivedEmail() { return receivedMail.iterator(); } diff --git a/modules/dumbster/src/com/dumbster/smtp/SmtpActionType.java b/modules/dumbster/src/com/dumbster/smtp/SmtpActionType.java index 34f56d99ac..13b6eb6cce 100644 --- a/modules/dumbster/src/com/dumbster/smtp/SmtpActionType.java +++ b/modules/dumbster/src/com/dumbster/smtp/SmtpActionType.java @@ -21,7 +21,7 @@ */ public class SmtpActionType { /** Internal value for the action type. */ - private byte value; + private final byte value; /** Internal representation of the CONNECT action. */ private static final byte CONNECT_BYTE = (byte) 1; diff --git a/modules/dumbster/src/com/dumbster/smtp/SmtpMessage.java b/modules/dumbster/src/com/dumbster/smtp/SmtpMessage.java index c98d29d762..3977892c70 100644 --- a/modules/dumbster/src/com/dumbster/smtp/SmtpMessage.java +++ b/modules/dumbster/src/com/dumbster/smtp/SmtpMessage.java @@ -16,26 +16,32 @@ */ package com.dumbster.smtp; -import java.util.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Container for a complete SMTP message - headers and message body. */ public class SmtpMessage { /** Headers: Map of List of String hashed on header name. */ - private Map headers; + private final Map> headers; private String headerLast; /** Message body. */ - private StringBuffer body; + private final StringBuffer body; private boolean inBody; - private Date createdTimestamp = new Date(); + private final Date createdTimestamp = new Date(); /** * Constructor. Initializes headers Map and body buffer. */ public SmtpMessage() { - headers = new HashMap(10); + headers = new HashMap<>(10); body = new StringBuffer(); } @@ -69,8 +75,8 @@ public void store(SmtpResponse response, String params) { * Get an Iterator over the header names. * @return an Iterator over the set of header names (String) */ - public Iterator getHeaderNames() { - Set nameSet = headers.keySet(); + public Iterator getHeaderNames() { + Set nameSet = headers.keySet(); return nameSet.iterator(); } @@ -80,11 +86,11 @@ public Iterator getHeaderNames() { * @return value(s) associated with the header name */ public String[] getHeaderValues(String name) { - List values = (List)headers.get(name); + List values = headers.get(name); if (values == null) { return new String[0]; } else { - return (String[])values.toArray(new String[0]); + return values.toArray(new String[0]); } } @@ -94,12 +100,12 @@ public String[] getHeaderValues(String name) { * @return first value associated with the header name */ public String getHeaderValue(String name) { - List values = (List)headers.get(name); + List values = headers.get(name); if (values == null) { return null; } else { - Iterator iterator = values.iterator(); - return (String)iterator.next(); + Iterator iterator = values.iterator(); + return iterator.next(); } } @@ -117,9 +123,9 @@ public String getBody() { * @param value header value */ private void addHeader(String name, String value) { - List valueList = (List)headers.get(name); + List valueList = headers.get(name); if (valueList == null) { - valueList = new ArrayList(1); + valueList = new ArrayList<>(1); headers.put(name, valueList); } valueList.add(value); @@ -131,9 +137,9 @@ private void addHeader(String name, String value) { * @param value header value */ private void appendHeader(String name, String value) { - List valueList = (List)headers.get(name); + List valueList = headers.get(name); if (valueList == null) { - valueList = new ArrayList(1); + valueList = new ArrayList<>(1); headers.put(name, valueList); } valueList.set(0, valueList.get(0) + value); @@ -144,12 +150,12 @@ private void appendHeader(String name, String value) { * @return a String */ public String toString() { - StringBuffer msg = new StringBuffer(); - for(Iterator i = headers.keySet().iterator(); i.hasNext();) { - String name = (String)i.next(); - List values = (List)headers.get(name); - for(Iterator j = values.iterator(); j.hasNext();) { - String value = (String)j.next(); + StringBuilder msg = new StringBuilder(); + for(Iterator i = headers.keySet().iterator(); i.hasNext();) { + String name = i.next(); + List values = headers.get(name); + for(Iterator j = values.iterator(); j.hasNext();) { + String value = j.next(); msg.append(name); msg.append(": "); msg.append(value); diff --git a/modules/dumbster/src/com/dumbster/smtp/SmtpRequest.java b/modules/dumbster/src/com/dumbster/smtp/SmtpRequest.java index 6af2916e20..e0e8dfb9ba 100644 --- a/modules/dumbster/src/com/dumbster/smtp/SmtpRequest.java +++ b/modules/dumbster/src/com/dumbster/smtp/SmtpRequest.java @@ -43,11 +43,11 @@ */ public class SmtpRequest { /** SMTP action received from client. */ - private SmtpActionType action; + private final SmtpActionType action; /** Current state of the SMTP state table. */ - private SmtpState state; + private final SmtpState state; /** Additional information passed from the client with the SMTP action. */ - private String params; + private final String params; /** * Create a new SMTP client request. @@ -66,7 +66,7 @@ public SmtpRequest(SmtpActionType actionType, String params, SmtpState state) { * @return response to the request */ public SmtpResponse execute() { - SmtpResponse response = null; + SmtpResponse response; if (action.isStateless()) { if (SmtpActionType.EXPN == action || SmtpActionType.VRFY == action) { response = new SmtpResponse(252, "Not supported", this.state); @@ -74,8 +74,6 @@ public SmtpResponse execute() { response = new SmtpResponse(211, "No help available", this.state); } else if (SmtpActionType.NOOP == action) { response = new SmtpResponse(250, "OK", this.state); - } else if (SmtpActionType.VRFY == action) { - response = new SmtpResponse(252, "Not supported", this.state); } else if (SmtpActionType.RSET == action) { response = new SmtpResponse(250, "OK", SmtpState.GREET); } else { @@ -152,13 +150,13 @@ public SmtpResponse execute() { * @return a populated SmtpRequest object */ public static SmtpRequest createRequest(String s, SmtpState state) { - SmtpActionType action = null; + SmtpActionType action; String params = null; if (state == SmtpState.DATA_HDR) { if (s.equals(".")) { action = SmtpActionType.DATA_END; - } else if (s.length() < 1) { + } else if (s.isEmpty()) { action = SmtpActionType.BLANK_LINE; } else { action = SmtpActionType.UNRECOG; @@ -201,12 +199,11 @@ public static SmtpRequest createRequest(String s, SmtpState state) { } } - SmtpRequest req = new SmtpRequest(action, params, state); - return req; + return new SmtpRequest(action, params, state); } /** - * Get the parameters of this request (remainder of command line once the command is removed. + * Get the parameters of this request (remainder of command line once the command is removed). * @return parameters */ public String getParams() { diff --git a/modules/dumbster/src/com/dumbster/smtp/SmtpResponse.java b/modules/dumbster/src/com/dumbster/smtp/SmtpResponse.java index fdd98c55c4..c7c7c2f201 100644 --- a/modules/dumbster/src/com/dumbster/smtp/SmtpResponse.java +++ b/modules/dumbster/src/com/dumbster/smtp/SmtpResponse.java @@ -21,11 +21,11 @@ */ public class SmtpResponse { /** Response code - see RFC-2821. */ - private int code; + private final int code; /** Response message. */ - private String message; + private final String message; /** New state of the SMTP server once the request has been executed. */ - private SmtpState nextState; + private final SmtpState nextState; /** * Constructor. diff --git a/modules/dumbster/src/com/dumbster/smtp/SmtpState.java b/modules/dumbster/src/com/dumbster/smtp/SmtpState.java index 5cca4c25c7..a4ff9ab509 100644 --- a/modules/dumbster/src/com/dumbster/smtp/SmtpState.java +++ b/modules/dumbster/src/com/dumbster/smtp/SmtpState.java @@ -21,7 +21,7 @@ */ public class SmtpState { /** Internal representation of the state. */ - private byte value; + private final byte value; /** Internal representation of the CONNECT state. */ private static final byte CONNECT_BYTE = (byte) 1; diff --git a/modules/dumbster/src/org/labkey/dumbster/view/mailWebPart.jsp b/modules/dumbster/src/org/labkey/dumbster/view/mailWebPart.jsp index 6578361db6..c9ad2e8381 100644 --- a/modules/dumbster/src/org/labkey/dumbster/view/mailWebPart.jsp +++ b/modules/dumbster/src/org/labkey/dumbster/view/mailWebPart.jsp @@ -16,9 +16,10 @@ */ %> <%@ page import="com.dumbster.smtp.SmtpMessage" %> +<%@ page import="jakarta.mail.MessagingException" %> +<%@ page import="jakarta.mail.internet.MimeMessage" %> <%@ page import="org.apache.commons.lang3.ArrayUtils" %> <%@ page import="org.labkey.api.data.Container" %> -<%@ page import="org.labkey.api.data.DataRegion" %> <%@ page import="org.labkey.api.util.HtmlString" %> <%@ page import="org.labkey.api.util.MailHelper" %> <%@ page import="org.labkey.api.view.HttpView" %> @@ -27,8 +28,6 @@ <%@ page import="org.labkey.dumbster.DumbsterController" %> <%@ page import="org.labkey.dumbster.model.DumbsterManager" %> <%@ page import="org.labkey.dumbster.view.MailPage" %> -<%@ page import="jakarta.mail.MessagingException" %> -<%@ page import="jakarta.mail.internet.MimeMessage" %> <%@ page import="java.util.Iterator" %> <%@ page import="java.util.Map" %> <%@ page import="static org.labkey.api.util.DOM.Attribute.*" %> @@ -43,17 +42,15 @@ } %> <% - JspView me = (JspView) HttpView.currentView(); + JspView me = HttpView.currentView(); MailPage pageInfo = me.getModelBean(); Container c = getContainer(); SmtpMessage[] messages = pageInfo.getMessages(); if ("true".equals(request.getParameter("reverse"))) ArrayUtils.reverse(messages); boolean recorder = pageInfo.isEnableRecorder(); - DataRegion emailRegion = new DataRegion(); - emailRegion.setName("EmailRecord"); - - String renderId = "emailRecordEmpty-" + getRequestScopedUID(); + String tableId = makeId("EmailRecord"); + String renderId = makeId("emailRecordEmpty-"); %><% @@ -105,9 +102,9 @@ function toggleRecorder(checkbox) if (checked) { - var t = document.getElementById(<%=q(emailRegion.getDomId())%>); + var t = document.getElementById(<%=q(tableId)%>); var len = t.rows.length; - for (var i = len - 2; i > 0; i--) + for (var i = len - 1; i > 1; i--) t.deleteRow(i); Ext4.get(<%=q(renderId)%>).setDisplayed(""); } @@ -123,18 +120,19 @@ function toggleRecorder(checkbox) } - - - - +
 
+ + - - - - - - + + + + + + + ;"> + <% if (messages.length > 0) { @@ -148,11 +146,11 @@ function toggleRecorder(checkbox) StringBuilder headers = new StringBuilder(); - Iterator i = m.getHeaderNames(); + Iterator i = m.getHeaderNames(); while (i.hasNext()) { - String header = (String)i.next(); + String header = i.next(); headers.append(h(header)); headers.append(": "); headers.append(h(m.getHeaderValue(header))); @@ -201,15 +199,14 @@ function toggleRecorder(checkbox) - <%=hasHtml ? createHtml(TD(A(at(href, DumbsterController.getViewMessageURL(c, rowIndex - 1, "html")).at(target, "_messageHtml"), "HTML"))) : createHtml(TD())%> - <%=hasText ? createHtml(TD(A(at(href, DumbsterController.getViewMessageURL(c, rowIndex - 1, "text")).at(target, "_messageText"), "Text"))) : createHtml(TD())%> + <%=hasHtml ? createHtml(TD(A(at(href, DumbsterController.getViewMessageURL(c, rowIndex - 1, "html")).at(target, "_messageHtml"), "HTML"))) : createHtml(TD(SPAN(cl("text-muted"), "HTML")))%> + <%=hasText ? createHtml(TD(A(at(href, DumbsterController.getViewMessageURL(c, rowIndex - 1, "text")).at(target, "_messageText"), "Text"))) : createHtml(TD(SPAN(cl("text-muted"), "Text")))%> <%=createHtml(TD(A(at(href, DumbsterController.getViewMessageURL(c, rowIndex - 1, "raw")).at(target, "_messageText"), "Raw")))%> <% } } %> - ;">
To
From
Date/Time
Message
Headers
View
To
From
Date/Time
Message
Headers
View
No email recorded.
View headers
No email recorded.
<% if (getUser().hasRootAdminPermission()) diff --git a/src/org/labkey/test/components/dumbster/EmailRecordTable.java b/src/org/labkey/test/components/dumbster/EmailRecordTable.java index 60854c6de7..30c61cee9d 100644 --- a/src/org/labkey/test/components/dumbster/EmailRecordTable.java +++ b/src/org/labkey/test/components/dumbster/EmailRecordTable.java @@ -36,13 +36,11 @@ public class EmailRecordTable extends Table { private static final String RECORDER_CHECKBOX_NAME = "emailRecordOn"; private static final String _regionName = "EmailRecord"; - private static final Locator gridLocator = Locator.xpath("//table[@lk-region-name='"+ _regionName +"']"); - private static final int _headerRows = 2; - private static final int _footerRows = 1; + private static final Locator gridLocator = Locator.tagWithAttributeContaining("table", "id", _regionName); public EmailRecordTable(WebDriver driver) { - super(driver, new RefindingWebElement(gridLocator, driver).withTimeout(WAIT_FOR_JAVASCRIPT), _headerRows); + super(driver, new RefindingWebElement(gridLocator, driver).withTimeout(WAIT_FOR_JAVASCRIPT), 0); ((RefindingWebElement) getComponentElement()).withRefindListener(el -> clearElementCache()); } @@ -73,8 +71,7 @@ public int getColumnIndex(String headerLabel) public int getEmailCount() { - //3 rows in the table do not contain email messages - return getRowCount() - (_headerRows + _footerRows); + return getRowCount(); } public void startRecording() @@ -138,12 +135,12 @@ public EmailMessage getMessageRegEx(String regExp) private List getMessages(Predicate subjectFilter) { List messages = new ArrayList<>(); - int rows = getRowCount() - _footerRows; + int rows = getRowCount(); if (rows > 0) { int colMessage = getColumnIndex("Message"); - for (int i = _headerRows + 1; i <= rows; i++) + for (int i = 1; i <= rows; i++) { String message = getDataAsText(i, colMessage); String[] lines = trimAll(StringUtils.split(message, "\n")); @@ -176,7 +173,7 @@ public List getTableIndexesWhereTextAppears(String header, String text) for(int i = 1; i <= columnText.size(); i++) { int arrayIndex = i-1; - if(columnText.get(arrayIndex).equals(text)){colsWithString.add(i + _headerRows);} + if(columnText.get(arrayIndex).equals(text)){colsWithString.add(i);} } return colsWithString; } From f9eaca82386ffe5c0cdb9b093b32f321028ce08b Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Thu, 6 Mar 2025 09:27:20 -0800 Subject: [PATCH 2/2] Fix getViews --- .../components/dumbster/EmailRecordTable.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/components/dumbster/EmailRecordTable.java b/src/org/labkey/test/components/dumbster/EmailRecordTable.java index 30c61cee9d..6b7625dc54 100644 --- a/src/org/labkey/test/components/dumbster/EmailRecordTable.java +++ b/src/org/labkey/test/components/dumbster/EmailRecordTable.java @@ -20,6 +20,7 @@ import org.labkey.test.Locator; import org.labkey.test.components.html.Table; import org.labkey.test.selenium.RefindingWebElement; +import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; @@ -212,17 +213,28 @@ private void parseMessageCell(EmailMessage emailMessage) private void parseViewCell(EmailMessage emailMessage) { List views = new ArrayList<>(); - String view; + boolean done = false; int rowIndex = emailMessage.getRowIndex(); int colIndex = EmailColumn.View.getIndex(); do { - view = getDataAsText(rowIndex, colIndex++); - if (view != null && !view.isBlank()) + try { - views.add(view.trim()); + WebElement viewEl = getDataAsElement(rowIndex, colIndex++); + if (Locator.tag("a").existsIn(viewEl)) + { + String view = viewEl.getText(); + if (!view.isBlank()) + { + views.add(view.trim()); + } + } + } + catch (NoSuchElementException nse) + { + done = true; } - } while (view != null); + } while (!done); emailMessage.setViews(views); }