Skip to content

Commit

Permalink
JAMES-1930 MPT test for delegation
Browse files Browse the repository at this point in the history
  • Loading branch information
chibenwa authored and aduprat committed Feb 14, 2017
1 parent 157be0d commit 3f26ba0
Show file tree
Hide file tree
Showing 16 changed files with 133 additions and 121 deletions.
Expand Up @@ -51,8 +51,6 @@
import org.apache.james.mailbox.cassandra.quota.CassandraPerUserMaxQuotaManager; import org.apache.james.mailbox.cassandra.quota.CassandraPerUserMaxQuotaManager;
import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.quota.QuotaRootResolver; import org.apache.james.mailbox.quota.QuotaRootResolver;
import org.apache.james.mailbox.store.FakeAuthenticator;
import org.apache.james.mailbox.store.FakeAuthorizator;
import org.apache.james.mailbox.store.JVMMailboxPathLocker; import org.apache.james.mailbox.store.JVMMailboxPathLocker;
import org.apache.james.mailbox.store.StoreSubscriptionManager; import org.apache.james.mailbox.store.StoreSubscriptionManager;
import org.apache.james.mailbox.store.mail.model.impl.MessageParser; import org.apache.james.mailbox.store.mail.model.impl.MessageParser;
Expand All @@ -73,8 +71,6 @@ public class CassandraHostSystem extends JamesImapHostSystem {
Feature.ANNOTATION_SUPPORT); Feature.ANNOTATION_SUPPORT);


private final CassandraMailboxManager mailboxManager; private final CassandraMailboxManager mailboxManager;
private final FakeAuthenticator userManager;
private final FakeAuthorizator authorizator;
private final CassandraCluster cassandraClusterSingleton; private final CassandraCluster cassandraClusterSingleton;


public CassandraHostSystem() throws Exception { public CassandraHostSystem() throws Exception {
Expand All @@ -91,8 +87,6 @@ public CassandraHostSystem() throws Exception {
new CassandraAttachmentModule(), new CassandraAttachmentModule(),
new CassandraAnnotationModule()); new CassandraAnnotationModule());
cassandraClusterSingleton = CassandraCluster.create(mailboxModule); cassandraClusterSingleton = CassandraCluster.create(mailboxModule);
userManager = new FakeAuthenticator();
authorizator = FakeAuthorizator.defaultReject();
com.datastax.driver.core.Session session = cassandraClusterSingleton.getConf(); com.datastax.driver.core.Session session = cassandraClusterSingleton.getConf();
CassandraModSeqProvider modSeqProvider = new CassandraModSeqProvider(session); CassandraModSeqProvider modSeqProvider = new CassandraModSeqProvider(session);
CassandraUidProvider uidProvider = new CassandraUidProvider(session); CassandraUidProvider uidProvider = new CassandraUidProvider(session);
Expand All @@ -107,7 +101,7 @@ public CassandraHostSystem() throws Exception {
CassandraMailboxSessionMapperFactory mapperFactory = new CassandraMailboxSessionMapperFactory(uidProvider, modSeqProvider, CassandraMailboxSessionMapperFactory mapperFactory = new CassandraMailboxSessionMapperFactory(uidProvider, modSeqProvider,
session, typesProvider, messageDAO, messageIdDAO, imapUidDAO, mailboxCounterDAO, mailboxRecentsDAO); session, typesProvider, messageDAO, messageIdDAO, imapUidDAO, mailboxCounterDAO, mailboxRecentsDAO);


mailboxManager = new CassandraMailboxManager(mapperFactory, userManager, authorizator, new JVMMailboxPathLocker(), new MessageParser(), messageIdFactory); mailboxManager = new CassandraMailboxManager(mapperFactory, authenticator, authorizator, new JVMMailboxPathLocker(), new MessageParser(), messageIdFactory);
QuotaRootResolver quotaRootResolver = new DefaultQuotaRootResolver(mapperFactory); QuotaRootResolver quotaRootResolver = new DefaultQuotaRootResolver(mapperFactory);


CassandraPerUserMaxQuotaManager perUserMaxQuotaManager = new CassandraPerUserMaxQuotaManager(session); CassandraPerUserMaxQuotaManager perUserMaxQuotaManager = new CassandraPerUserMaxQuotaManager(session);
Expand Down Expand Up @@ -143,11 +137,6 @@ protected void resetData() throws Exception {
cassandraClusterSingleton.clearAllTables(); cassandraClusterSingleton.clearAllTables();
} }


public boolean addUser(String user, String password) {
userManager.addUser(user, password);
return true;
}

@Override @Override
protected void finalize() throws Throwable { protected void finalize() throws Throwable {
super.finalize(); super.finalize();
Expand Down
8 changes: 8 additions & 0 deletions mpt/impl/imap-mailbox/core/pom.xml
Expand Up @@ -57,6 +57,14 @@
<groupId>org.apache.james</groupId> <groupId>org.apache.james</groupId>
<artifactId>apache-james-mpt-onami-test</artifactId> <artifactId>apache-james-mpt-onami-test</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.apache.james</groupId>
<artifactId>james-server-data-memory</artifactId>
</dependency>
<dependency>
<groupId>org.apache.james</groupId>
<artifactId>james-server-mailbox-adapter</artifactId>
</dependency>
<dependency> <dependency>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
Expand Down
Expand Up @@ -19,35 +19,54 @@


package org.apache.james.mpt.host; package org.apache.james.mpt.host;


import java.util.HashSet;
import java.util.Set;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.plist.PropertyListConfiguration;
import org.apache.james.adapter.mailbox.store.UserRepositoryAuthenticator;
import org.apache.james.adapter.mailbox.store.UserRepositoryAuthorizator;
import org.apache.james.imap.api.process.ImapProcessor; import org.apache.james.imap.api.process.ImapProcessor;
import org.apache.james.imap.decode.ImapDecoder; import org.apache.james.imap.decode.ImapDecoder;
import org.apache.james.imap.decode.main.ImapRequestStreamHandler; import org.apache.james.imap.decode.main.ImapRequestStreamHandler;
import org.apache.james.imap.encode.ImapEncoder; import org.apache.james.imap.encode.ImapEncoder;
import org.apache.james.mailbox.MailboxSession.User; import org.apache.james.mailbox.MailboxSession.User;
import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.store.Authenticator;
import org.apache.james.mailbox.store.Authorizator;
import org.apache.james.mpt.api.Continuation; import org.apache.james.mpt.api.Continuation;
import org.apache.james.mpt.api.ImapHostSystem; import org.apache.james.mpt.api.ImapHostSystem;
import org.apache.james.mpt.helper.ByteBufferInputStream; import org.apache.james.mpt.helper.ByteBufferInputStream;
import org.apache.james.mpt.helper.ByteBufferOutputStream; import org.apache.james.mpt.helper.ByteBufferOutputStream;
import org.apache.james.mpt.session.ImapSessionImpl; import org.apache.james.mpt.session.ImapSessionImpl;
import org.apache.james.user.memory.MemoryUsersRepository;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;


import java.util.HashSet; import com.google.common.base.Throwables;
import java.util.Set;


public abstract class JamesImapHostSystem implements ImapHostSystem { public abstract class JamesImapHostSystem implements ImapHostSystem {


private ImapDecoder decoder; private final MemoryUsersRepository memoryUsersRepository;
private final Set<User> users;
protected final Authorizator authorizator;
protected final Authenticator authenticator;


private ImapDecoder decoder;
private ImapEncoder encoder; private ImapEncoder encoder;

private ImapProcessor processor; private ImapProcessor processor;


private final Set<User> users;

public JamesImapHostSystem() { public JamesImapHostSystem() {
super(); super();
users = new HashSet<User>(); users = new HashSet<User>();
memoryUsersRepository = MemoryUsersRepository.withoutVirtualHosting();
try {
memoryUsersRepository.configure(userRepositoryConfiguration());
} catch (ConfigurationException e) {
throw Throwables.propagate(e);
}
authenticator = new UserRepositoryAuthenticator(memoryUsersRepository);
authorizator = new UserRepositoryAuthorizator(memoryUsersRepository);
} }


public void configure(ImapDecoder decoder, ImapEncoder encoder, public void configure(ImapDecoder decoder, ImapEncoder encoder,
Expand All @@ -57,6 +76,12 @@ public void configure(ImapDecoder decoder, ImapEncoder encoder,
this.processor = processor; this.processor = processor;
} }


@Override
public boolean addUser(String user, String password) throws Exception {
memoryUsersRepository.addUser(user, password);
return true;
}

public Session newSession(Continuation continuation) public Session newSession(Continuation continuation)
throws Exception { throws Exception {
return new Session(continuation); return new Session(continuation);
Expand All @@ -67,6 +92,7 @@ public void beforeTest() throws Exception {


public void afterTest() throws Exception { public void afterTest() throws Exception {
users.clear(); users.clear();
memoryUsersRepository.clear();
resetData(); resetData();
} }


Expand Down Expand Up @@ -127,5 +153,11 @@ public void afterTests() throws Exception {
public void beforeTests() throws Exception { public void beforeTests() throws Exception {
// default do nothing // default do nothing
} }

private HierarchicalConfiguration userRepositoryConfiguration() {
PropertyListConfiguration configuration = new PropertyListConfiguration();
configuration.addProperty("administratorId", "imapuser");
return configuration;
}


} }
Expand Up @@ -23,6 +23,8 @@


import javax.inject.Inject; import javax.inject.Inject;


import org.apache.james.mailbox.model.MailboxConstants;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mpt.api.ImapHostSystem; import org.apache.james.mpt.api.ImapHostSystem;
import org.apache.james.mpt.imapmailbox.suite.base.BaseNonAuthenticatedState; import org.apache.james.mpt.imapmailbox.suite.base.BaseNonAuthenticatedState;
import org.junit.Test; import org.junit.Test;
Expand All @@ -32,23 +34,31 @@ public class AuthenticatePlain extends BaseNonAuthenticatedState {
@Inject @Inject
private static ImapHostSystem system; private static ImapHostSystem system;



public AuthenticatePlain() throws Exception { public AuthenticatePlain() throws Exception {
super(system); super(system);
} }


@Test @Test
public void testAuthenticatePlainUS() throws Exception { public void testAuthenticatePlainUS() throws Exception {
system.addUser("delegate", "123456");
system.createMailbox(new MailboxPath(MailboxConstants.USER_NAMESPACE, "delegate", "delegate"));
system.createMailbox(new MailboxPath(MailboxConstants.USER_NAMESPACE, "imapuser", "imapuser"));
scriptTest("AuthenticatePlain", Locale.US); scriptTest("AuthenticatePlain", Locale.US);
} }


@Test @Test
public void testAuthenticatePlainITALY() throws Exception { public void testAuthenticatePlainITALY() throws Exception {
system.addUser("delegate", "123456");
system.createMailbox(new MailboxPath(MailboxConstants.USER_NAMESPACE, "delegate", "delegate"));
system.createMailbox(new MailboxPath(MailboxConstants.USER_NAMESPACE, "imapuser", "imapuser"));
scriptTest("AuthenticatePlain", Locale.ITALY); scriptTest("AuthenticatePlain", Locale.ITALY);
} }


@Test @Test
public void testAuthenticatePlainKOREA() throws Exception { public void testAuthenticatePlainKOREA() throws Exception {
system.addUser("delegate", "123456");
system.createMailbox(new MailboxPath(MailboxConstants.USER_NAMESPACE, "delegate", "delegate"));
system.createMailbox(new MailboxPath(MailboxConstants.USER_NAMESPACE, "imapuser", "imapuser"));
scriptTest("AuthenticatePlain", Locale.KOREA); scriptTest("AuthenticatePlain", Locale.KOREA);
} }
} }
Expand Up @@ -18,39 +18,78 @@
################################################################ ################################################################


# Correct user with correct password can authenticate with delegation part missing # Correct user with correct password can authenticate with delegation part missing
C: abcd AUTHENTICATE "PLAIN" {24+} C: 0001 AUTHENTICATE "PLAIN" {24+}
# imapuser\0password # imapuser\0password
C: aW1hcHVzZXIAcGFzc3dvcmQ= C: aW1hcHVzZXIAcGFzc3dvcmQ=
S: abcd OK AUTHENTICATE completed. S: 0001 OK AUTHENTICATE completed.

# Ensure we are imapuser
C: 0002 SELECT imapuser
SUB {
S: \* FLAGS .*
S: \* .* EXISTS
S: \* .* RECENT
S: \* OK \[UIDVALIDITY .*\] UIDs valid
S: \* OK \[PERMANENTFLAGS .*\] Limited
S: \* OK \[HIGHESTMODSEQ .*\] Highest
S: \* OK \[UIDNEXT .*\] Predicted next UID
}
S: 0002 OK \[READ-WRITE\] SELECT completed\.


REINIT REINIT


# Correct user with correct password can authenticate with empty delegation part # Correct user with correct password can authenticate with empty delegation part
C: abcd AUTHENTICATE "PLAIN" {24+} C: 0003 AUTHENTICATE "PLAIN" {24+}
# \0imapuser\0password # \0imapuser\0password
C: AGltYXB1c2VyAHBhc3N3b3Jk C: AGltYXB1c2VyAHBhc3N3b3Jk
S: abcd OK AUTHENTICATE completed. S: 0003 OK AUTHENTICATE completed.

# Ensure we are imapuser
C: 0004 SELECT imapuser
SUB {
S: \* FLAGS .*
S: \* .* EXISTS
S: \* .* RECENT
S: \* OK \[UIDVALIDITY .*\] UIDs valid
S: \* OK \[PERMANENTFLAGS .*\] Limited
S: \* OK \[HIGHESTMODSEQ .*\] Highest
S: \* OK \[UIDNEXT .*\] Predicted next UID
}
S: 0004 OK \[READ-WRITE\] SELECT completed\.


REINIT REINIT


# Correct user with bad password cannot authenticate # Correct user with bad password cannot authenticate
C: abcd AUTHENTICATE "PLAIN" {28+} C: 0005 AUTHENTICATE "PLAIN" {28+}
# \0imapuser\0badpassword # \0imapuser\0badpassword
C: AGltYXB1c2VyAGJhZHBhc3N3b3Jk C: AGltYXB1c2VyAGJhZHBhc3N3b3Jk
S: abcd NO AUTHENTICATE failed. Authentication failed. S: 0005 NO AUTHENTICATE failed. Authentication failed.


REINIT REINIT


# Bad user cannot authenticate # Bad user cannot authenticate
C: abcd AUTHENTICATE "PLAIN" {24+} C: 0006 AUTHENTICATE "PLAIN" {24+}
# \0baduser\0password # \0baduser\0password
C: AGJhZHVzZXIAcGFzc3dvcmQ= C: AGJhZHVzZXIAcGFzc3dvcmQ=
S: abcd NO AUTHENTICATE failed. Authentication failed. S: 0006 NO AUTHENTICATE failed. Authentication failed.


REINIT REINIT


# Correct user with correct password can authenticate with any delegation part # Correct user with correct password can authenticate with any delegation part
C: abcd AUTHENTICATE "PLAIN" {36+} C: 0007 AUTHENTICATE "PLAIN" {36+}
# delegate\0imapuser\0password # delegate\0imapuser\0password
C: ZGVsZWdhdGUAaW1hcHVzZXIAcGFzc3dvcmQ= C: ZGVsZWdhdGUAaW1hcHVzZXIAcGFzc3dvcmQ=
S: abcd OK AUTHENTICATE completed. S: 0007 OK AUTHENTICATE completed.

# Ensure we are delegate
C: 0008 SELECT delegate
SUB {
S: \* FLAGS .*
S: \* .* EXISTS
S: \* .* RECENT
S: \* OK \[UIDVALIDITY .*\] UIDs valid
S: \* OK \[PERMANENTFLAGS .*\] Limited
S: \* OK \[HIGHESTMODSEQ .*\] Highest
S: \* OK \[UIDNEXT .*\] Predicted next UID
}
S: 0008 OK \[READ-WRITE\] SELECT completed\.
Expand Up @@ -53,8 +53,6 @@
import org.apache.james.mailbox.inmemory.InMemoryMessageId; import org.apache.james.mailbox.inmemory.InMemoryMessageId;
import org.apache.james.mailbox.model.MailboxConstants; import org.apache.james.mailbox.model.MailboxConstants;
import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.store.FakeAuthenticator;
import org.apache.james.mailbox.store.FakeAuthorizator;
import org.apache.james.mailbox.store.StoreMailboxManager; import org.apache.james.mailbox.store.StoreMailboxManager;
import org.apache.james.mailbox.store.StoreSubscriptionManager; import org.apache.james.mailbox.store.StoreSubscriptionManager;
import org.apache.james.mailbox.store.extractor.DefaultTextExtractor; import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
Expand All @@ -77,12 +75,7 @@ public class ElasticSearchHostSystem extends JamesImapHostSystem {
private EmbeddedElasticSearch embeddedElasticSearch; private EmbeddedElasticSearch embeddedElasticSearch;
private Path tempDirectory; private Path tempDirectory;
private StoreMailboxManager mailboxManager; private StoreMailboxManager mailboxManager;
private FakeAuthenticator userManager;


public boolean addUser(String user, String password) throws Exception {
userManager.addUser(user, password);
return true;
}


@Override @Override
public void beforeTest() throws Exception { public void beforeTest() throws Exception {
Expand Down Expand Up @@ -111,7 +104,6 @@ private void initFields() {
MailboxMappingFactory.getMappingContent() MailboxMappingFactory.getMappingContent()
); );


userManager = new FakeAuthenticator();
InMemoryMailboxSessionMapperFactory factory = new InMemoryMailboxSessionMapperFactory(); InMemoryMailboxSessionMapperFactory factory = new InMemoryMailboxSessionMapperFactory();
InMemoryMessageId.Factory messageIdFactory = new InMemoryMessageId.Factory(); InMemoryMessageId.Factory messageIdFactory = new InMemoryMessageId.Factory();


Expand All @@ -125,7 +117,7 @@ private void initFields() {
GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver();
MessageParser messageParser = new MessageParser(); MessageParser messageParser = new MessageParser();


mailboxManager = new StoreMailboxManager(factory, userManager, FakeAuthorizator.defaultReject(), aclResolver, groupMembershipResolver, messageParser, mailboxManager = new StoreMailboxManager(factory, authenticator, authorizator, aclResolver, groupMembershipResolver, messageParser,
messageIdFactory, MailboxConstants.DEFAULT_LIMIT_ANNOTATIONS_ON_MAILBOX, MailboxConstants.DEFAULT_LIMIT_ANNOTATION_SIZE); messageIdFactory, MailboxConstants.DEFAULT_LIMIT_ANNOTATIONS_ON_MAILBOX, MailboxConstants.DEFAULT_LIMIT_ANNOTATION_SIZE);
mailboxManager.setMessageSearchIndex(searchIndex); mailboxManager.setMessageSearchIndex(searchIndex);


Expand Down
Expand Up @@ -40,8 +40,6 @@
import org.apache.james.mailbox.hbase.mail.HBaseModSeqProvider; import org.apache.james.mailbox.hbase.mail.HBaseModSeqProvider;
import org.apache.james.mailbox.hbase.mail.HBaseUidProvider; import org.apache.james.mailbox.hbase.mail.HBaseUidProvider;
import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.store.FakeAuthenticator;
import org.apache.james.mailbox.store.FakeAuthorizator;
import org.apache.james.mailbox.store.StoreSubscriptionManager; import org.apache.james.mailbox.store.StoreSubscriptionManager;
import org.apache.james.mailbox.store.mail.model.DefaultMessageId; import org.apache.james.mailbox.store.mail.model.DefaultMessageId;
import org.apache.james.mailbox.store.mail.model.impl.MessageParser; import org.apache.james.mailbox.store.mail.model.impl.MessageParser;
Expand All @@ -66,7 +64,6 @@ public class HBaseHostSystem extends JamesImapHostSystem {
public static Boolean useMiniCluster = true; public static Boolean useMiniCluster = true;


private final HBaseMailboxManager mailboxManager; private final HBaseMailboxManager mailboxManager;
private final FakeAuthenticator userManager;
private MiniHBaseCluster hbaseCluster; private MiniHBaseCluster hbaseCluster;
private final Configuration conf; private final Configuration conf;


Expand All @@ -91,9 +88,6 @@ public HBaseHostSystem(boolean useMiniCluster) throws Exception {
conf = HBaseConfiguration.create(); conf = HBaseConfiguration.create();
} }


userManager = new FakeAuthenticator();
FakeAuthorizator authorizator = FakeAuthorizator.defaultReject();

final HBaseModSeqProvider modSeqProvider = new HBaseModSeqProvider(conf); final HBaseModSeqProvider modSeqProvider = new HBaseModSeqProvider(conf);
final HBaseUidProvider uidProvider = new HBaseUidProvider(conf); final HBaseUidProvider uidProvider = new HBaseUidProvider(conf);
DefaultMessageId.Factory messageIdFactory = new DefaultMessageId.Factory(); DefaultMessageId.Factory messageIdFactory = new DefaultMessageId.Factory();
Expand All @@ -103,7 +97,7 @@ public HBaseHostSystem(boolean useMiniCluster) throws Exception {
GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver();
MessageParser messageParser = new MessageParser(); MessageParser messageParser = new MessageParser();


mailboxManager = new HBaseMailboxManager(mapperFactory, userManager, authorizator, aclResolver, groupMembershipResolver, mailboxManager = new HBaseMailboxManager(mapperFactory, authenticator, authorizator, aclResolver, groupMembershipResolver,
messageParser, messageIdFactory); messageParser, messageIdFactory);
mailboxManager.init(); mailboxManager.init();


Expand Down Expand Up @@ -133,11 +127,6 @@ protected void resetData() throws Exception {
mailboxManager.logout(session, false); mailboxManager.logout(session, false);
} }


public boolean addUser(String user, String password) {
userManager.addUser(user, password);
return true;
}

public final void resetUserMetaData() throws Exception { public final void resetUserMetaData() throws Exception {
File dir = new File(META_DATA_DIRECTORY); File dir = new File(META_DATA_DIRECTORY);
if (dir.exists()) { if (dir.exists()) {
Expand Down
Expand Up @@ -19,6 +19,7 @@
package org.apache.james.mpt.imapmailbox.inmemory; package org.apache.james.mpt.imapmailbox.inmemory;


import org.apache.james.mpt.imapmailbox.AbstractMailboxTest; import org.apache.james.mpt.imapmailbox.AbstractMailboxTest;
import org.apache.james.mpt.imapmailbox.suite.AuthenticatePlain;
import org.apache.james.mpt.imapmailbox.suite.AuthenticatedState; import org.apache.james.mpt.imapmailbox.suite.AuthenticatedState;
import org.apache.james.mpt.imapmailbox.suite.ConcurrentSessions; import org.apache.james.mpt.imapmailbox.suite.ConcurrentSessions;
import org.apache.james.mpt.imapmailbox.suite.Events; import org.apache.james.mpt.imapmailbox.suite.Events;
Expand Down Expand Up @@ -48,6 +49,7 @@
@GuiceModules({ InMemoryMailboxTestModule.class }) @GuiceModules({ InMemoryMailboxTestModule.class })
@SuiteClasses({ @SuiteClasses({
AuthenticatedState.class, AuthenticatedState.class,
AuthenticatePlain.class,
ConcurrentSessions.class, ConcurrentSessions.class,
Events.class, Events.class,
Expunge.class, Expunge.class,
Expand Down

0 comments on commit 3f26ba0

Please sign in to comment.