Author: rdonkin
Date: Wed May 7 14:14:35 2008
New Revision: 654279
URL: http://svn.apache.org/viewvc?rev=654279&view=rev
Log:
Fixed test build
Added:
james/jsieve/trunk/src/test/java/org/apache/jsieve/util/check/ScriptCheckMailAdapter.java
james/jsieve/trunk/src/test/java/org/apache/jsieve/util/check/ScriptChecker.java
Modified:
james/jsieve/trunk/build.xml
james/jsieve/trunk/include.properties
james/jsieve/trunk/pom.xml
james/jsieve/trunk/src/test/java/org/apache/jsieve/junit/utils/SieveMailAdapter.java
Modified: james/jsieve/trunk/build.xml
URL:
http://svn.apache.org/viewvc/james/jsieve/trunk/build.xml?rev=654279&r1=654278&r2=654279&view=diff
==============================================================================
--- james/jsieve/trunk/build.xml (original)
+++ james/jsieve/trunk/build.xml Wed May 7 14:14:35 2008
@@ -52,6 +52,16 @@
<pathelement path="${java.class.path}" />
<pathelement path="${build.classes}" />
</path>
+
+ <path id="mail.class.path">
+ <pathelement location='${mail.jar}'/>
+ <pathelement location='${activation.jar}'/>
+ </path>
+
+ <path id="project.test.class.path">
+ <path refid="project.class.path"/>
+ <path refid="mail.class.path"/>
+ </path>
<!--
===================================================================
@@ -102,7 +112,22 @@
Prepare target
===================================================================
-->
- <target name="prepare" depends="prepare-common, preprocess"/>
+ <target name="prepare" depends="prepare-common, preprocess">
+ <available
+ classname="javax.mail.Message"
+ property="is.available.javamail"
+ classpathref="project.test.class.path"/>
+ <available
+ classname="javax.activation.MimeType"
+ property="is.available.activation"
+ classpathref="project.test.class.path"/>
+ <condition property="is.available.libs.test">
+ <and>
+ <isset property='is.available.javamail'/>
+ <isset property='is.available.activation'/>
+ </and>
+ </condition>
+ </target>
<!--
===================================================================
@@ -187,8 +212,11 @@
<copy file='LICENSE.apache'
tofile="${build.classes}/META-INF/LICENSE.txt"/>
<copy tofile="${build.classes}/META-INF/NOTICE.txt" file='NOTICE.base'/>
</target>
-
- <target name="compile-tests" depends="prepare, compile-main">
+
+ <target
+ name="compile-tests"
+ depends="prepare, compile-main">
+ <fail unless="is.available.libs.test">JavaMail is required for
testing</fail>
<echo message="Compiling jSieve test sources"/>
<mkdir dir="${build.classes}"/>
<javac
@@ -199,7 +227,7 @@
source="${jdk.source}"
target="${jdk.target}"
>
- <classpath refid="project.class.path"/>
+ <classpath refid="project.test.class.path"/>
<src path="${test.dir}"/>
<include name="org/apache/jsieve/**"/>
</javac>
@@ -413,6 +441,7 @@
<pathelement location="${dist.dir}/conf/"/>
<pathelement path="${java.class.path}"/>
<pathelement location="${junit.jar}"/>
+ <path refid='mail.class.path'/>
</classpath>
<formatter type="plain"/>
<batchtest fork="yes" todir="${dist.dir}/reports">
Modified: james/jsieve/trunk/include.properties
URL:
http://svn.apache.org/viewvc/james/jsieve/trunk/include.properties?rev=654279&r1=654278&r2=654279&view=diff
==============================================================================
--- james/jsieve/trunk/include.properties (original)
+++ james/jsieve/trunk/include.properties Wed May 7 14:14:35 2008
@@ -57,3 +57,10 @@
# ----- JavaCC ----
javacc.jar=javacc-4.0.jar
+
+
+# --------------------------------------------------
+# LIBRARIES REQUIRED FOR TESTING
+# --------------------------------------------------
+mail.jar=mail-1.4.1.jar
+activation.jar=activation-1.1.1.jar
Modified: james/jsieve/trunk/pom.xml
URL:
http://svn.apache.org/viewvc/james/jsieve/trunk/pom.xml?rev=654279&r1=654278&r2=654279&view=diff
==============================================================================
--- james/jsieve/trunk/pom.xml (original)
+++ james/jsieve/trunk/pom.xml Wed May 7 14:14:35 2008
@@ -112,6 +112,20 @@
<scope>runtime</scope>
</dependency>
+ <dependency>
+ <groupId>javax.mail</groupId>
+ <artifactId>mail</artifactId>
+ <version>1.4.1</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.activation</groupId>
+ <artifactId>activation</artifactId>
+ <version>1.1.1</version>
+ <scope>test</scope>
+ </dependency>
+
</dependencies>
<build>
Modified:
james/jsieve/trunk/src/test/java/org/apache/jsieve/junit/utils/SieveMailAdapter.java
URL:
http://svn.apache.org/viewvc/james/jsieve/trunk/src/test/java/org/apache/jsieve/junit/utils/SieveMailAdapter.java?rev=654279&r1=654278&r2=654279&view=diff
==============================================================================
---
james/jsieve/trunk/src/test/java/org/apache/jsieve/junit/utils/SieveMailAdapter.java
(original)
+++
james/jsieve/trunk/src/test/java/org/apache/jsieve/junit/utils/SieveMailAdapter.java
Wed May 7 14:14:35 2008
@@ -31,6 +31,7 @@
import java.util.Set;
import javax.mail.Header;
+import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
@@ -43,6 +44,7 @@
import org.apache.jsieve.mail.MailUtils;
import org.apache.jsieve.mail.SieveMailException;
import org.apache.jsieve.parser.address.SieveAddressBuilder;
+import org.apache.jsieve.parser.generated.ParseException;
/**
* <p>Class SieveMailAdapter implements a mock MailAdapter for testing
purposes.</p>
@@ -281,9 +283,41 @@
}
public Address[] parseAddresses(final String headerName) throws
SieveMailException {
- return SieveAddressBuilder.parseAddresses(headerName, getMessage());
+ return parseAddresses(headerName, getMessage());
}
+ /**
+ * Parses the value from the given message into addresses.
+ *
+ * @param headerName
+ * header name, to be matched case insensitively
+ * @param message
+ * <code>Message</code>, not null
+ * @return <code>Address</code> array, not null possibly empty
+ * @throws SieveMailException
+ */
+ public Address[] parseAddresses(final String headerName,
+ final Message message) throws SieveMailException {
+ try {
+ final SieveAddressBuilder builder = new SieveAddressBuilder();
+
+ for (Enumeration en = message.getAllHeaders();
en.hasMoreElements();) {
+ final Header header = (Header) en.nextElement();
+ final String name = header.getName();
+ if (name.trim().equalsIgnoreCase(headerName)) {
+ builder.addAddresses(header.getValue());
+ }
+ }
+
+ final Address[] results = builder.getAddresses();
+ return results;
+
+ } catch (MessagingException ex) {
+ throw new SieveMailException(ex);
+ } catch (org.apache.jsieve.parser.generated.address.ParseException ex)
{
+ throw new SieveMailException(ex);
+ }
+ }
}
Added:
james/jsieve/trunk/src/test/java/org/apache/jsieve/util/check/ScriptCheckMailAdapter.java
URL:
http://svn.apache.org/viewvc/james/jsieve/trunk/src/test/java/org/apache/jsieve/util/check/ScriptCheckMailAdapter.java?rev=654279&view=auto
==============================================================================
---
james/jsieve/trunk/src/test/java/org/apache/jsieve/util/check/ScriptCheckMailAdapter.java
(added)
+++
james/jsieve/trunk/src/test/java/org/apache/jsieve/util/check/ScriptCheckMailAdapter.java
Wed May 7 14:14:35 2008
@@ -0,0 +1,319 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.jsieve.util.check;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.ListIterator;
+
+import javax.mail.Header;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+
+import org.apache.jsieve.parser.generated.ParseException;
+import org.apache.jsieve.exception.SieveException;
+import org.apache.jsieve.mail.Action;
+import org.apache.jsieve.mail.MailAdapter;
+import org.apache.jsieve.mail.MailUtils;
+import org.apache.jsieve.mail.SieveMailException;
+import org.apache.jsieve.parser.address.SieveAddressBuilder;
+
+/**
+ * Checks script execution for an email. The wrapped email is set by called
+ * [EMAIL PROTECTED] #setMail}. Actions are recorded on [EMAIL PROTECTED]
#executedActions} and can
+ * be retrieved by [EMAIL PROTECTED] #getExecutedActions()}.
+ */
+public class ScriptCheckMailAdapter implements MailAdapter {
+
+ private final List actions;
+
+ private final List executedActions;
+
+ private Message mail = null;
+
+ public ScriptCheckMailAdapter() {
+ actions = new ArrayList();
+ executedActions = new ArrayList();
+ }
+
+ /**
+ * Gets the wrapped email.
+ *
+ * @return <code>Message</code>, possibly null
+ */
+ public Message getMail() {
+ return mail;
+ }
+
+ /**
+ * Sets the wrapped email and [EMAIL PROTECTED] #reset}s the adapter ready
for another
+ * execution.
+ *
+ * @param mail
+ * <code>Message</code>, possibly null
+ */
+ public void setMail(Message mail) {
+ this.mail = mail;
+ reset();
+ }
+
+ /**
+ * Method addAction adds an Action to the List of Actions to be performed
by
+ * the receiver.
+ *
+ * @param action
+ */
+ public void addAction(final Action action) {
+ actions.add(action);
+ }
+
+ /**
+ * Method executeActions. Applies the Actions accumulated by the receiver.
+ */
+ public void executeActions() throws SieveException {
+ executedActions.clear();
+ executedActions.addAll(actions);
+ }
+
+ /**
+ * Gets the actions accumulated when [EMAIL PROTECTED] #executedActions}
was last
+ * called.
+ *
+ * @return <code>List</code> of [EMAIL PROTECTED] Action}s, not null. This
list is a
+ * modifiable copy
+ */
+ public List getExecutedActions() {
+ final ArrayList result = new ArrayList(executedActions);
+ return result;
+ }
+
+ /**
+ * Method getActions answers the List of Actions accumulated by the
+ * receiver. Implementations may elect to supply an unmodifiable
collection.
+ *
+ * @return <code>List</code> of [EMAIL PROTECTED] Action}'s, not null,
possibly
+ * unmodifiable
+ */
+ public List getActions() {
+ final List result = Collections.unmodifiableList(actions);
+ return result;
+ }
+
+ /**
+ * Method getActionIteraror answers an Iterator over the List of Actions
+ * accumulated by the receiver. Implementations may elect to supply an
+ * unmodifiable iterator.
+ *
+ * @return <code>ListIterator</code>, not null, possibly unmodifiable
+ */
+ public ListIterator getActionsIterator() {
+ final List unmodifiableActions = getActions();
+ final ListIterator result = unmodifiableActions.listIterator();
+ return result;
+ }
+
+ /**
+ * Resets executed and accumlated actions. An instance may be safely reused
+ * to check a script once this method has been called.
+ */
+ public void reset() {
+ executedActions.clear();
+ actions.clear();
+ }
+
+ /**
+ * Method getHeader answers a List of all of the headers in the receiver
+ * whose name is equal to the passed name. If no headers are found an empty
+ * List is returned.
+ *
+ * @param name
+ * @return <code>List</code> not null, possibly empty
+ * @throws SieveMailException
+ */
+ public List getHeader(String name) throws SieveMailException {
+ List result = Collections.EMPTY_LIST;
+ if (mail != null) {
+ try {
+ String[] values = mail.getHeader(name);
+ if (values != null) {
+ result = Arrays.asList(values);
+ }
+ } catch (MessagingException e) {
+ throw new SieveMailException(e);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Method getHeaderNames answers a List of all of the headers in the
+ * receiver. No duplicates are allowed.
+ *
+ * @return <code>List</code>, not null possible empty, possible
+ * unmodifiable
+ * @throws SieveMailException
+ */
+ public List getHeaderNames() throws SieveMailException {
+ List results = Collections.EMPTY_LIST;
+ if (mail != null) {
+ try {
+ results = new ArrayList();
+ for (final Enumeration en = mail.getAllHeaders(); en
+ .hasMoreElements();) {
+ final Header header = (Header) en.nextElement();
+ final String name = header.getName();
+ if (!results.contains(name)) {
+ results.add(name);
+ }
+ }
+ } catch (MessagingException e) {
+ throw new SieveMailException(e);
+ }
+ }
+ return results;
+ }
+
+ /**
+ * <p>
+ * Method getMatchingHeader answers a List of all of the headers in the
+ * receiver with the passed name. If no headers are found an empty List is
+ * returned.
+ * </p>
+ *
+ * <p>
+ * This method differs from getHeader(String) in that it ignores case and
+ * the whitespace prefixes and suffixes of a header name when performing
the
+ * match, as required by RFC 3028. Thus "From", "from ", " From" and "
from "
+ * are considered equal.
+ * </p>
+ *
+ * @param name
+ * @return <code>List</code>, not null possibly empty
+ * @throws SieveMailException
+ */
+ public List getMatchingHeader(String name) throws SieveMailException {
+ List result = Collections.EMPTY_LIST;
+ if (mail != null) {
+ result = MailUtils.getMatchingHeader(this, name);
+ }
+ return result;
+ }
+
+ /**
+ * Method getSize answers the receiver's message size in octets.
+ *
+ * @return int
+ * @throws SieveMailException
+ */
+ public int getSize() throws SieveMailException {
+ int result = 0;
+ if (mail != null) {
+ try {
+ result = mail.getSize();
+ } catch (MessagingException e) {
+ throw new SieveMailException(e);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Method getContentType returns string/mime representation of the message
+ * type.
+ *
+ * @return String
+ * @throws SieveMailException
+ */
+ public String getContentType() throws SieveMailException {
+ String result = null;
+ if (mail != null) {
+ try {
+ result = mail.getContentType();
+ } catch (MessagingException e) {
+ throw new SieveMailException(e);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Method getContent returns object containing the message content.
+ *
+ * @return Object
+ * @throws SieveMailException
+ */
+ public Object getContent() throws SieveMailException {
+ Object result = null;
+ if (mail != null) {
+ try {
+ result = mail.getContent();
+ } catch (MessagingException e) {
+ throw new SieveMailException(e);
+ } catch (IOException e) {
+ throw new SieveMailException(e);
+ }
+ }
+ return result;
+ }
+
+ public Address[] parseAddresses(String headerName)
+ throws SieveMailException {
+ return parseAddresses(headerName, mail);
+ }
+
+ /**
+ * Parses the value from the given message into addresses.
+ *
+ * @param headerName
+ * header name, to be matched case insensitively
+ * @param message
+ * <code>Message</code>, not null
+ * @return <code>Address</code> array, not null possibly empty
+ * @throws SieveMailException
+ */
+ public Address[] parseAddresses(final String headerName,
+ final Message message) throws SieveMailException {
+ try {
+ final SieveAddressBuilder builder = new SieveAddressBuilder();
+
+ for (Enumeration en = message.getAllHeaders();
en.hasMoreElements();) {
+ final Header header = (Header) en.nextElement();
+ final String name = header.getName();
+ if (name.trim().equalsIgnoreCase(headerName)) {
+ builder.addAddresses(header.getValue());
+ }
+ }
+
+ final Address[] results = builder.getAddresses();
+ return results;
+
+ } catch (MessagingException ex) {
+ throw new SieveMailException(ex);
+ } catch (org.apache.jsieve.parser.generated.address.ParseException ex)
{
+ throw new SieveMailException(ex);
+ }
+ }
+
+}
Added:
james/jsieve/trunk/src/test/java/org/apache/jsieve/util/check/ScriptChecker.java
URL:
http://svn.apache.org/viewvc/james/jsieve/trunk/src/test/java/org/apache/jsieve/util/check/ScriptChecker.java?rev=654279&view=auto
==============================================================================
---
james/jsieve/trunk/src/test/java/org/apache/jsieve/util/check/ScriptChecker.java
(added)
+++
james/jsieve/trunk/src/test/java/org/apache/jsieve/util/check/ScriptChecker.java
Wed May 7 14:14:35 2008
@@ -0,0 +1,241 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.jsieve.util.check;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.jsieve.SieveFactory;
+import org.apache.jsieve.exception.SieveException;
+import org.apache.jsieve.mail.ActionFileInto;
+import org.apache.jsieve.mail.ActionKeep;
+import org.apache.jsieve.mail.ActionRedirect;
+import org.apache.jsieve.mail.ActionReject;
+import org.apache.jsieve.parser.generated.ParseException;
+
+/**
+ * Checks a <code>sieve</code> script by executing it against a given mail and
+ * reporting the results.
+ */
+public class ScriptChecker {
+
+ private final ScriptCheckMailAdapter adapter;
+
+ public ScriptChecker() {
+ adapter = new ScriptCheckMailAdapter();
+ }
+
+ /**
+ * Checks the <code>sieve</code> script contained in the given file by
+ * executing it against the given message.
+ *
+ * @param message
+ * <code>File</code> containing the mail message to be fed
+ * to the script, not null
+ * @param script
+ * <code>File</code> containing the script to be checked
+ * @return <code>Results</code> of that execution
+ * @throws IOException
+ * @throws MessageException
+ */
+ public Results check(final File message, final File script)
+ throws IOException, MessagingException {
+ final FileInputStream messageStream = new FileInputStream(message);
+ final FileInputStream scriptStream = new FileInputStream(script);
+ final Results results = check(messageStream, scriptStream);
+ return results;
+ }
+
+ /**
+ * Checks the <code>sieve</code> script contained in the given file by
+ * executing it against the given message.
+ *
+ * @param script
+ * <code>InputStream</code>, not null
+ * @return <code>Results</code> of the check, not null
+ * @throws IOException
+ * @throws MessagingException
+ */
+ public Results check(final InputStream message, final InputStream script)
+ throws IOException, MessagingException {
+ MimeMessage mimeMessage = new MimeMessage(null, message);
+ adapter.setMail(mimeMessage);
+ Results results;
+ try {
+ SieveFactory.getInstance().interpret(adapter, script);
+ final List executedActions = adapter.getExecutedActions();
+ results = new Results(executedActions);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ results = new Results(e);
+ } catch (SieveException e) {
+ e.printStackTrace();
+ results = new Results(e);
+ }
+ return results;
+ }
+
+ /**
+ * Contains results of script execution.
+ */
+ public final static class Results {
+ private final boolean pass;
+
+ private final Exception exception;
+
+ private final List actionsExecuted;
+
+ public Results(final Exception ex) {
+ this.exception = ex;
+ pass = false;
+ actionsExecuted = null;
+ }
+
+ public Results(final List actions) {
+ this.pass = true;
+ exception = null;
+ this.actionsExecuted = actions;
+ }
+
+ /**
+ * Is this a pass?
+ *
+ * @return true if the script executed without error, false if errors
+ * were encountered
+ */
+ public boolean isPass() {
+ return pass;
+ }
+
+ /**
+ * Gets the exception which was thrown during execution.
+ *
+ * @return <code>Exception</code> or null if no exception was thrown
+ */
+ public Exception getException() {
+ return exception;
+ }
+
+ /**
+ * Gets the actions executed by the script.
+ *
+ * @return <code>List</code> of actions or null if the script failed
+ */
+ public List getActionsExecuted() {
+ return actionsExecuted;
+ }
+
+ /**
+ * Is the <code>n<code>'th action a <code>FileInto</code>
+ * with given destination?
+ * @param destination <code>String</code> destination for the file into
+ * @param n index to check
+ * @return true if the <code>n<code>'th action is a
<code>FileInto</code>
+ * with given destination
+ */
+ public boolean isActionFileInto(String destination, int n) {
+ boolean result = false;
+ Object action = actionsExecuted.get(n);
+ if (action != null && action instanceof ActionFileInto) {
+ ActionFileInto actionFileInto = (ActionFileInto) action;
+ result = destination.equals(actionFileInto.getDestination());
+ }
+ return result;
+ }
+
+ /**
+ * Is the <code>n<code>'th action a <code>Redirect</code>
+ * with given address?
+ * @param address <code>String</code> redirect address
+ * @param n index to check
+ * @return true if the <code>n<code>'th action is a
<code>Redirect</code>
+ * with given redirect address
+ */
+ public boolean isActionRedirect(String address, int n) {
+ boolean result = false;
+ Object action = actionsExecuted.get(n);
+ if (action != null && action instanceof ActionRedirect) {
+ ActionRedirect actionRedirect = (ActionRedirect) action;
+ result = address.equals(actionRedirect.getAddress());
+ }
+ return result;
+ }
+
+ /**
+ * Is the <code>n<code>'th action a <code>Reject</code>
+ * with given message?
+ * @param message <code>String</code> rejection message
+ * @param n index to check
+ * @return true if the <code>n<code>'th action is a
<code>Reject</code>
+ * with given reject message
+ */
+ public boolean isActionReject(String message, int n) {
+ boolean result = false;
+ Object action = actionsExecuted.get(n);
+ if (action != null && action instanceof ActionReject) {
+ ActionReject actionReject = (ActionReject) action;
+ result = message.equals(actionReject.getMessage());
+ }
+ return result;
+ }
+
+ /**
+ * Is the <code>n<code>'th action a <code>Keep</code>?
+ * @param n index to check
+ * @return true if the <code>n<code>'th action is a <code>Keep</code>
+ */
+ public boolean isActionKeep(int n) {
+ boolean result = false;
+ Object action = actionsExecuted.get(n);
+ if (action != null && action instanceof ActionKeep) {
+ result = true;
+ }
+ return result;
+ }
+
+ /**
+ * Prints out details of results.
+ */
+ public String toString() {
+ StringBuffer buffer = new StringBuffer("Results: ");
+ if (pass) {
+ buffer.append("PASS");
+ } else {
+ buffer.append("FAIL: ");
+ if (exception != null) {
+ if (exception instanceof ParseException) {
+ buffer.append("Cannot parse script");
+ } else {
+ buffer.append("Cannot excute script");
+ }
+ buffer.append(exception.getMessage());
+ }
+ }
+ return buffer.toString();
+ }
+
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]