Author: norman
Date: Sat Jul 16 19:28:53 2011
New Revision: 1147479
URL: http://svn.apache.org/viewvc?rev=1147479&view=rev
Log:
Handle the "CONDSTORE enabling commands" as stated in CONDSTORE RFC. This say
we MUST include MODSEQ infos in any untagges FETCH Responses after a "CONDSTORE
enabling command" was issued and the selected mailbox does store the mod
sequences in a permanent way. See IMAP-305
Modified:
james/imap/trunk/api/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/EnableProcessor.java
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/fetch/FetchProcessor.java
Modified:
james/imap/trunk/api/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java
URL:
http://svn.apache.org/viewvc/james/imap/trunk/api/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java?rev=1147479&r1=1147478&r2=1147479&view=diff
==============================================================================
---
james/imap/trunk/api/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java
(original)
+++
james/imap/trunk/api/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java
Sat Jul 16 19:28:53 2011
@@ -197,11 +197,4 @@ public interface SelectedMailbox {
public void resetNewApplicableFlags();
- /**
- * Return true if the selected mailbox was selected with the condstore
option
- *
- * @return condstore
- */
- public boolean getCondstore();
-
}
\ No newline at end of file
Modified:
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
URL:
http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java?rev=1147479&r1=1147478&r2=1147479&view=diff
==============================================================================
---
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
(original)
+++
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
Sat Jul 16 19:28:53 2011
@@ -58,6 +58,7 @@ import org.apache.james.mailbox.MessageR
import org.apache.james.mailbox.MessageResult;
import org.apache.james.mailbox.SearchQuery;
import org.apache.james.mailbox.MessageManager.MetaData;
+import org.apache.james.mailbox.MessageManager.MetaData.FetchGroup;
import org.apache.james.mailbox.MessageRange.Type;
import org.apache.james.mailbox.SearchQuery.NumericRange;
@@ -231,6 +232,8 @@ abstract public class AbstractMailboxPro
throw new MailboxException("No message found with uid " + uid);
boolean qresyncEnabled =
EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_QRESYNC);
+ boolean condstoreEnabled =
EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_CONDSTORE);
+
final Flags flags = mr.getFlags();
final Long uidOut;
if (useUid || qresyncEnabled) {
@@ -245,9 +248,9 @@ abstract public class AbstractMailboxPro
}
final FetchResponse response;
- // Check if we also need to return the MODSEQ in the response.
This is true if the mailbox was selected with the CONSTORE option or
- // if QRESYNC was enabled
- if (selected.getCondstore() || qresyncEnabled) {
+ // Check if we also need to return the MODSEQ in the response.
This is true if CONDSTORE or
+ // if QRESYNC was enabled, and the mailbox supports the permant
storage of mod-sequences
+ if ((condstoreEnabled || qresyncEnabled) &&
mailbox.getMetaData(false, mailboxSession,
FetchGroup.NO_COUNT).isModSeqPermanent()) {
response = new FetchResponse(msn, flags, uidOut,
mr.getModSeq(), null, null, null, null, null, null);
} else {
response = new FetchResponse(msn, flags, uidOut, null, null,
null, null, null, null, null);
@@ -256,6 +259,24 @@ abstract public class AbstractMailboxPro
}
}
+ protected void condstoreEnablingCommand(ImapSession session, Responder
responder, MetaData metaData, boolean sendHighestModSeq) {
+ Set<String> enabled = EnableProcessor.getEnabledCapabilities(session);
+ if (!enabled.contains(ImapConstants.SUPPORTS_CONDSTORE)) {
+ if (sendHighestModSeq) {
+ if (metaData.isModSeqPermanent()) {
+
+ final long highestModSeq = metaData.getHighestModSeq();
+
+ StatusResponse untaggedOk =
getStatusResponseFactory().untaggedOk(HumanReadableText.HIGHEST_MOD_SEQ,
ResponseCode.highestModSeq(highestModSeq));
+ responder.respond(untaggedOk);
+ }
+ }
+ enabled.add(ImapConstants.SUPPORTS_CONDSTORE);
+
+
+ }
+ }
+
private MessageManager getMailbox(final ImapSession session, final
SelectedMailbox selected) throws MailboxException {
final MailboxManager mailboxManager = getMailboxManager();
final MessageManager mailbox =
mailboxManager.getMailbox(selected.getPath(),
ImapSessionUtils.getMailboxSession(session));
Modified:
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
URL:
http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java?rev=1147479&r1=1147478&r2=1147479&view=diff
==============================================================================
---
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
(original)
+++
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
Sat Jul 16 19:28:53 2011
@@ -131,7 +131,9 @@ abstract class AbstractSelectionProcesso
return;
}
- final MessageManager.MetaData metaData =
selectMailbox(fullMailboxPath, session, request.getCondstore());
+
+
+ final MessageManager.MetaData metaData =
selectMailbox(fullMailboxPath, session);
final SelectedMailbox selected = session.getSelected();
flags(responder, selected);
@@ -143,9 +145,13 @@ abstract class AbstractSelectionProcesso
highestModSeq(responder, metaData, selected);
uidNext(responder, metaData);
+ if (request.getCondstore()) {
+ condstoreEnablingCommand(session, responder, metaData, false);
+ }
+
// Now do the QRESYNC processing if necessary
//
- // If the mailbox does nto store the mod-sequence in a permanent way
its needed to not process the QRESYNC paramters
+ // If the mailbox does not store the mod-sequence in a permanent way
its needed to not process the QRESYNC paramters
// The same is true if none are given ;)
if (metaData.isModSeqPermanent() && lastKnownUidValidity != null) {
if (lastKnownUidValidity == metaData.getUidValidity()) {
@@ -320,6 +326,7 @@ abstract class AbstractSelectionProcesso
}
+
private void highestModSeq(Responder responder, MetaData metaData,
SelectedMailbox selected) {
final StatusResponse untaggedOk;
if (metaData.isModSeqPermanent()) {
@@ -382,7 +389,7 @@ abstract class AbstractSelectionProcesso
responder.respond(existsResponse);
}
- private MessageManager.MetaData selectMailbox(MailboxPath mailboxPath,
ImapSession session, boolean condstore) throws MailboxException {
+ private MessageManager.MetaData selectMailbox(MailboxPath mailboxPath,
ImapSession session) throws MailboxException {
final MailboxManager mailboxManager = getMailboxManager();
final MailboxSession mailboxSession =
ImapSessionUtils.getMailboxSession(session);
final MessageManager mailbox = mailboxManager.getMailbox(mailboxPath,
mailboxSession);
@@ -399,10 +406,7 @@ abstract class AbstractSelectionProcesso
if (currentMailbox != null) {
getStatusResponseFactory().untaggedOk(HumanReadableText.QRESYNC_CLOSED,
ResponseCode.closed());
}
- if (condstore == false) {
- condstore =
EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_CONDSTORE);
- }
- sessionMailbox = createNewSelectedMailbox(mailbox, mailboxSession,
session, mailboxPath, condstore);
+ sessionMailbox = createNewSelectedMailbox(mailbox, mailboxSession,
session, mailboxPath);
} else {
// TODO: Check if we need to handle CONDSTORE there too
@@ -413,7 +417,7 @@ abstract class AbstractSelectionProcesso
return metaData;
}
- private SelectedMailbox createNewSelectedMailbox(final MessageManager
mailbox, final MailboxSession mailboxSession, ImapSession session, MailboxPath
path, boolean condstore) throws MailboxException {
+ private SelectedMailbox createNewSelectedMailbox(final MessageManager
mailbox, final MailboxSession mailboxSession, ImapSession session, MailboxPath
path) throws MailboxException {
Iterator<MessageResult> messages =
mailbox.getMessages(MessageRange.all(), FetchGroupImpl.MINIMAL, mailboxSession);
Flags applicableFlags = new Flags(flags);
@@ -428,7 +432,7 @@ abstract class AbstractSelectionProcesso
// \RECENT is not a applicable flag in imap so remove it from the list
applicableFlags.remove(Flags.Flag.RECENT);
- final SelectedMailbox sessionMailbox = new
SelectedMailboxImpl(getMailboxManager(), uids.iterator(),applicableFlags,
session, path, condstore);
+ final SelectedMailbox sessionMailbox = new
SelectedMailboxImpl(getMailboxManager(), uids.iterator(),applicableFlags,
session, path);
session.selected(sessionMailbox);
return sessionMailbox;
}
Modified:
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/EnableProcessor.java
URL:
http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/EnableProcessor.java?rev=1147479&r1=1147478&r2=1147479&view=diff
==============================================================================
---
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/EnableProcessor.java
(original)
+++
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/EnableProcessor.java
Sat Jul 16 19:28:53 2011
@@ -23,11 +23,13 @@ import static org.apache.james.imap.api.
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.james.imap.api.ImapCommand;
import org.apache.james.imap.api.display.HumanReadableText;
+import org.apache.james.imap.api.message.request.ImapRequest;
import org.apache.james.imap.api.message.response.StatusResponseFactory;
import org.apache.james.imap.api.process.ImapProcessor;
import org.apache.james.imap.api.process.ImapSession;
@@ -38,12 +40,12 @@ import org.apache.james.mailbox.MailboxM
public class EnableProcessor extends AbstractMailboxProcessor<EnableRequest>
implements CapabilityImplementingProcessor {
- private final List<PermitEnableCapabilityProcessor> capabilities = new
ArrayList<PermitEnableCapabilityProcessor>();
+ private final static List<PermitEnableCapabilityProcessor> capabilities =
new ArrayList<PermitEnableCapabilityProcessor>();
public final static String ENABLED_CAPABILITIES = "ENABLED_CAPABILITIES";
public EnableProcessor(final ImapProcessor next, final MailboxManager
mailboxManager, final StatusResponseFactory factory, final
List<PermitEnableCapabilityProcessor> capabilities) {
this(next, mailboxManager, factory);
- this.capabilities.addAll(capabilities);
+ EnableProcessor.capabilities.addAll(capabilities);
}
@@ -60,22 +62,7 @@ public class EnableProcessor extends Abs
try {
List<String> caps = request.getCapabilities();
- Set<String> enabledCaps = new HashSet<String>();
- for (int i = 0; i < caps.size(); i++) {
- String cap = caps.get(i);
- // Check if the CAPABILITY is supported at all
- if
(CapabilityProcessor.getSupportedCapabilities(session).contains(cap)) {
- for (int a = 0; a < capabilities.size(); a++) {
- PermitEnableCapabilityProcessor enableProcessor =
capabilities.get(a);
- if
(enableProcessor.getPermitEnableCapabilities(session).contains(cap)) {
- enableProcessor.enable(request, responder,
session, cap);
- enabledCaps.add(cap);
- }
- }
- }
- }
- getEnabledCapabilities(session).addAll(enabledCaps);
-
+ Set<String> enabledCaps = enable(request, responder, session,
caps.iterator());
responder.respond(new EnableResponse(enabledCaps));
unsolicitedResponses(session, responder, false);
@@ -85,6 +72,25 @@ public class EnableProcessor extends Abs
}
}
+ public static Set<String> enable(ImapRequest request, Responder responder,
ImapSession session, Iterator<String> caps) throws EnableException {
+ Set<String> enabledCaps = new HashSet<String>();
+ while(caps.hasNext()) {
+ String cap = caps.next();
+ // Check if the CAPABILITY is supported at all
+ if
(CapabilityProcessor.getSupportedCapabilities(session).contains(cap)) {
+ for (int a = 0; a < capabilities.size(); a++) {
+ PermitEnableCapabilityProcessor enableProcessor =
capabilities.get(a);
+ if
(enableProcessor.getPermitEnableCapabilities(session).contains(cap)) {
+ enableProcessor.enable(request, responder, session,
cap);
+ enabledCaps.add(cap);
+ }
+ }
+ }
+ }
+ getEnabledCapabilities(session).addAll(enabledCaps);
+ return enabledCaps;
+ }
+
/**
* Add a {@link PermitEnableCapabilityProcessor} which can be enabled
*
Modified:
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
URL:
http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/SearchProcessor.java?rev=1147479&r1=1147478&r2=1147479&view=diff
==============================================================================
---
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
(original)
+++
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
Sat Jul 16 19:28:53 2011
@@ -53,6 +53,7 @@ import org.apache.james.mailbox.MailboxM
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.MessageManager;
import org.apache.james.mailbox.MessageManager.MetaData;
+import org.apache.james.mailbox.MessageManager.MetaData.FetchGroup;
import org.apache.james.mailbox.MessageRange;
import org.apache.james.mailbox.MessageRangeException;
import org.apache.james.mailbox.MessageResult;
@@ -118,6 +119,10 @@ public class SearchProcessor extends Abs
if (session.getAttribute(SEARCH_MODSEQ) != null) {
MetaData metaData = mailbox.getMetaData(false, msession ,
MessageManager.MetaData.FetchGroup.NO_COUNT);
highestModSeq = findHighestModSeq(msession, mailbox,
MessageRange.toRanges(uids), metaData.getHighestModSeq());
+
+ // Enable CONDSTORE as this is a CONDSTORE enabling command
+ condstoreEnablingCommand(session, responder, metaData, true);
+
} else {
highestModSeq = null;
}
Modified:
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
URL:
http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StatusProcessor.java?rev=1147479&r1=1147478&r2=1147479&view=diff
==============================================================================
---
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
(original)
+++
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
Sat Jul 16 19:28:53 2011
@@ -20,6 +20,7 @@
package org.apache.james.imap.processor;
import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.imap.api.ImapConstants;
import org.apache.james.imap.api.ImapSessionUtils;
import org.apache.james.imap.api.display.HumanReadableText;
import org.apache.james.imap.api.message.StatusDataItems;
@@ -78,6 +79,11 @@ public class StatusProcessor extends Abs
final Long uidValidity = uidValidity(statusDataItems, metaData);
final Long unseen = unseen(statusDataItems, metaData);
final Long highestModSeq = highestModSeq(statusDataItems,
metaData);
+
+ // Enable CONDSTORE as this is a CONDSTORE enabling command
+ if (highestModSeq != null) {
+
EnableProcessor.getEnabledCapabilities(session).add(ImapConstants.SUPPORTS_CONDSTORE);
+ }
final MailboxStatusResponse response = new
MailboxStatusResponse(messages, recent, uidNext, highestModSeq, uidValidity,
unseen, request.getMailboxName());
responder.respond(response);
unsolicitedResponses(session, responder, false);
Modified:
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
URL:
http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StoreProcessor.java?rev=1147479&r1=1147478&r2=1147479&view=diff
==============================================================================
---
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
(original)
+++
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
Sat Jul 16 19:28:53 2011
@@ -292,25 +292,32 @@ public class StoreProcessor extends Abst
resultFlags.add(Flags.Flag.RECENT);
}
-
+ session.getLog().debug("CONDSTORE="
+enabled.contains(ImapConstants.SUPPORTS_CONDSTORE));
+
final FetchResponse response;
// For more informations related to the FETCH response see
//
// RFC4551 3.2. STORE and UID STORE Commands
if (silent && (unchangedSince != -1 || qresyncEnabled ||
condstoreEnabled)) {
// We need to return an FETCH response which contains the
mod-sequence of the message even if FLAGS.SILENT was used
- response = new FetchResponse(msn, null, resultUid,
modSeqs.get(resultUid), null, null, null, null, null, null);
+ response = new FetchResponse(msn, null, resultUid,
modSeqs.get(uid), null, null, null, null, null, null);
} else if (!silent && (unchangedSince != -1 || qresyncEnabled
|| condstoreEnabled)){
//
// Use a FETCH response which contains the mod-sequence
and the flags
- response = new FetchResponse(msn, resultFlags, resultUid,
modSeqs.get(resultUid), null, null, null, null, null, null);
+ response = new FetchResponse(msn, resultFlags, resultUid,
modSeqs.get(uid), null, null, null, null, null, null);
} else {
// Use a FETCH response which only contains the flags as
no CONDSTORE was used
response = new FetchResponse(msn, resultFlags, resultUid,
null, null, null, null, null, null, null);
}
responder.respond(response);
+ }
+ if (unchangedSince != -1) {
+ // Enable CONDSTORE as this is a CONDSTORE enabling command
+ condstoreEnablingCommand(session, responder,
mailbox.getMetaData(false, mailboxSession, FetchGroup.NO_COUNT), true);
+
}
}
+
}
}
Modified:
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
URL:
http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java?rev=1147479&r1=1147478&r2=1147479&view=diff
==============================================================================
---
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
(original)
+++
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
Sat Jul 16 19:28:53 2011
@@ -47,10 +47,8 @@ public class SelectedMailboxImpl impleme
private final Set<Long> recentUids;
private boolean recentUidRemoved;
-
- private final boolean condstore;
-
- public SelectedMailboxImpl(final MailboxManager mailboxManager, final
Iterator<Long> uids, final Flags applicableFlags, final ImapSession session,
final MailboxPath path, final boolean condstore) throws MailboxException {
+
+ public SelectedMailboxImpl(final MailboxManager mailboxManager, final
Iterator<Long> uids, final Flags applicableFlags, final ImapSession session,
final MailboxPath path) throws MailboxException {
recentUids = new TreeSet<Long>();
recentUidRemoved = false;
MailboxSession mailboxSession =
ImapSessionUtils.getMailboxSession(session);
@@ -60,7 +58,6 @@ public class SelectedMailboxImpl impleme
mailboxManager.addListener(path, events, mailboxSession);
converter = new UidToMsnConverter(session, uids);
mailboxManager.addListener(path, converter, mailboxSession);
- this.condstore = condstore;
}
/**
@@ -287,9 +284,4 @@ public class SelectedMailboxImpl impleme
public void resetNewApplicableFlags() {
events.resetNewApplicableFlags();
}
-
- @Override
- public boolean getCondstore() {
- return condstore;
- }
}
Modified:
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/fetch/FetchProcessor.java
URL:
http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/fetch/FetchProcessor.java?rev=1147479&r1=1147478&r2=1147479&view=diff
==============================================================================
---
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/fetch/FetchProcessor.java
(original)
+++
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/fetch/FetchProcessor.java
Sat Jul 16 19:28:53 2011
@@ -44,6 +44,7 @@ import org.apache.james.mailbox.MailboxM
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.MessageManager;
import org.apache.james.mailbox.MessageManager.MessageCallback;
+import org.apache.james.mailbox.MessageManager.MetaData;
import org.apache.james.mailbox.MessageRange;
import org.apache.james.mailbox.MessageRangeException;
import org.apache.james.mailbox.MessageResult;
@@ -95,6 +96,13 @@ public class FetchProcessor extends Abst
return;
}
final MailboxSession mailboxSession =
ImapSessionUtils.getMailboxSession(session);
+
+ MetaData metaData = mailbox.getMetaData(false, mailboxSession,
org.apache.james.mailbox.MessageManager.MetaData.FetchGroup.NO_COUNT);
+ if (fetch.getChangedSince() != -1 || fetch.isModSeq()) {
+ // Enable CONDSTORE as this is a CONDSTORE enabling command
+ condstoreEnablingCommand(session, responder, metaData, true);
+ }
+
List<MessageRange> ranges = new ArrayList<MessageRange>();
for (int i = 0; i < idSet.length; i++) {
@@ -109,7 +117,7 @@ public class FetchProcessor extends Abst
if (vanished ) {
// TODO: From the QRESYNC RFC it seems ok to send the VANISHED
responses after the FETCH Responses.
// If we do so we could prolly save one mailbox access
which should give use some more speed up
- respondVanished(mailboxSession, mailbox, ranges, changedSince,
mailbox.getMetaData(false, mailboxSession,
org.apache.james.mailbox.MessageManager.MetaData.FetchGroup.NO_COUNT),
responder);
+ respondVanished(mailboxSession, mailbox, ranges, changedSince,
metaData, responder);
}
// if QRESYNC is enable its necessary to also return the UID in
all cases
if
(EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_QRESYNC))
{
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]