Messages in restricted groups should propagate without moderation.

If the group's owner is spamming, unsubscribe.
This commit is contained in:
akwizgran
2013-04-15 12:22:12 +01:00
parent 85875a4e6c
commit 42fd02d0b9
7 changed files with 786 additions and 773 deletions

View File

@@ -234,7 +234,6 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
i.putExtra("net.sf.briar.MESSAGE_ID", item.getId().getBytes());
Author author = item.getAuthor();
if(author != null) {
i.putExtra("net.sf.briar.AUTHOR_ID", author.getId().getBytes());
i.putExtra("net.sf.briar.AUTHOR_NAME", author.getName());
i.putExtra("net.sf.briar.RATING", item.getRating().toString());
}

View File

@@ -24,7 +24,6 @@ import net.sf.briar.android.BriarService;
import net.sf.briar.android.BriarService.BriarServiceConnection;
import net.sf.briar.android.widgets.HorizontalBorder;
import net.sf.briar.android.widgets.HorizontalSpace;
import net.sf.briar.api.AuthorId;
import net.sf.briar.api.android.BundleEncrypter;
import net.sf.briar.api.android.DatabaseUiExecutor;
import net.sf.briar.api.db.DatabaseComponent;
@@ -66,8 +65,7 @@ implements OnClickListener {
private Rating rating = UNRATED;
private boolean read;
private ImageView thumb = null;
private ImageButton goodButton = null, badButton = null, readButton = null;
private ImageButton prevButton = null, nextButton = null;
private ImageButton readButton = null, prevButton = null, nextButton = null;
private ImageButton replyButton = null;
private TextView content = null;
@@ -75,7 +73,6 @@ implements OnClickListener {
@Inject private volatile DatabaseComponent db;
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
private volatile MessageId messageId = null;
private volatile AuthorId authorId = null;
@Override
public void onCreate(Bundle state) {
@@ -92,15 +89,9 @@ implements OnClickListener {
b = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID");
if(b == null) throw new IllegalStateException();
messageId = new MessageId(b);
String authorName = null;
b = i.getByteArrayExtra("net.sf.briar.AUTHOR_ID");
if(b != null) {
authorId = new AuthorId(b);
authorName = i.getStringExtra("net.sf.briar.AUTHOR_NAME");
if(authorName == null) throw new IllegalStateException();
String r = i.getStringExtra("net.sf.briar.RATING");
if(r != null) rating = Rating.valueOf(r);
}
String authorName = i.getStringExtra("net.sf.briar.AUTHOR_NAME");
String r = i.getStringExtra("net.sf.briar.RATING");
if(r != null) rating = Rating.valueOf(r);
String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE");
if(contentType == null) throw new IllegalStateException();
long timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
@@ -177,23 +168,6 @@ implements OnClickListener {
footer.setOrientation(HORIZONTAL);
footer.setGravity(CENTER);
goodButton = new ImageButton(this);
goodButton.setBackgroundResource(0);
goodButton.setImageResource(R.drawable.rating_good);
if(authorName == null) goodButton.setEnabled(false);
else goodButton.setOnClickListener(this);
footer.addView(goodButton);
footer.addView(new HorizontalSpace(this));
badButton = new ImageButton(this);
badButton.setBackgroundResource(0);
badButton.setImageResource(R.drawable.rating_bad);
badButton.setOnClickListener(this);
if(authorName == null) badButton.setEnabled(false);
else badButton.setOnClickListener(this);
footer.addView(badButton);
footer.addView(new HorizontalSpace(this));
readButton = new ImageButton(this);
readButton.setBackgroundResource(0);
if(read) readButton.setImageResource(R.drawable.content_unread);
@@ -309,13 +283,7 @@ implements OnClickListener {
}
public void onClick(View view) {
if(view == goodButton) {
if(rating == BAD) setRatingInDatabase(UNRATED);
else if(rating == UNRATED) setRatingInDatabase(GOOD);
} else if(view == badButton) {
if(rating == GOOD) setRatingInDatabase(UNRATED);
else if(rating == UNRATED) setRatingInDatabase(BAD);
} else if(view == readButton) {
if(view == readButton) {
setReadInDatabase(!read);
} else if(view == prevButton) {
setResult(RESULT_PREV);
@@ -337,38 +305,4 @@ implements OnClickListener {
}
}
}
private void setRatingInDatabase(final Rating r) {
dbUiExecutor.execute(new Runnable() {
public void run() {
try {
serviceConnection.waitForStartup();
long now = System.currentTimeMillis();
db.setRating(authorId, r);
long duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO))
LOG.info("Setting rating took " + duration + " ms");
setRatingInUi(r);
} catch(DbException e) {
if(LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
} catch(InterruptedException e) {
if(LOG.isLoggable(INFO))
LOG.info("Interrupted while waiting for service");
Thread.currentThread().interrupt();
}
}
});
}
private void setRatingInUi(final Rating r) {
runOnUiThread(new Runnable() {
public void run() {
rating = r;
if(r == GOOD) thumb.setImageResource(R.drawable.rating_good);
else if(r == BAD) thumb.setImageResource(R.drawable.rating_bad);
else thumb.setImageResource(R.drawable.rating_unrated);
}
});
}
}

View File

@@ -352,14 +352,6 @@ interface Database<T> {
Collection<PrivateMessageHeader> getPrivateMessageHeaders(T txn,
ContactId c) throws DbException;
/**
* Returns the IDs of all messages signed by the given author.
* <p>
* Locking: message read.
*/
Collection<MessageId> getMessagesByAuthor(T txn, AuthorId a)
throws DbException;
/**
* Returns the IDs of some messages received from the given contact that
* need to be acknowledged, up to the given number of messages.
@@ -554,6 +546,15 @@ interface Database<T> {
*/
Map<GroupId, Integer> getUnreadMessageCounts(T txn) throws DbException;
/**
* Returns the IDs of all messages posted by the given author to
* unrestricted groups.
* <p>
* Locking: message read.
*/
Collection<MessageId> getUnrestrictedGroupMessages(T txn, AuthorId a)
throws DbException;
/**
* Returns the contacts to which the given group is visible.
* <p>

File diff suppressed because it is too large Load Diff

View File

@@ -1601,27 +1601,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Collection<MessageId> getMessagesByAuthor(Connection txn, AuthorId a)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT messageId FROM messages WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<MessageId>();
while(rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close();
ps.close();
return Collections.unmodifiableList(ids);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<MessageId> getMessagesToAck(Connection txn, ContactId c,
int maxMessages) throws DbException {
PreparedStatement ps = null;
@@ -2568,6 +2547,32 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Collection<MessageId> getUnrestrictedGroupMessages(Connection txn,
AuthorId a) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT messageId"
+ " FROM messages AS m"
+ " JOIN groups AS g"
+ " ON m.groupId = g.groupId"
+ " WHERE authorId = ?"
+ " AND publicKey IS NULL";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<MessageId>();
while(rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close();
ps.close();
return Collections.unmodifiableList(ids);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<ContactId> getVisibility(Connection txn, GroupId g)
throws DbException {
PreparedStatement ps = null;

View File

@@ -59,13 +59,13 @@ import org.junit.Test;
public abstract class DatabaseComponentTest extends BriarTestCase {
protected final Object txn = new Object();
protected final GroupId groupId;
protected final Group group;
protected final GroupId groupId, restrictedGroupId;
protected final Group group, restrictedGroup;
protected final AuthorId authorId;
protected final Author author;
protected final AuthorId localAuthorId;
protected final LocalAuthor localAuthor;
protected final MessageId messageId, messageId1;
protected final MessageId messageId, messageId1, privateMessageId;
protected final String contentType, subject;
protected final long timestamp;
protected final int size;
@@ -81,7 +81,10 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
public DatabaseComponentTest() {
super();
groupId = new GroupId(TestUtils.getRandomId());
restrictedGroupId = new GroupId(TestUtils.getRandomId());
group = new Group(groupId, "Group name", null);
restrictedGroup = new Group(restrictedGroupId, "Restricted group name",
new byte[60]);
authorId = new AuthorId(TestUtils.getRandomId());
author = new Author(authorId, "Alice", new byte[60]);
localAuthorId = new AuthorId(TestUtils.getRandomId());
@@ -89,6 +92,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
new byte[60]);
messageId = new MessageId(TestUtils.getRandomId());
messageId1 = new MessageId(TestUtils.getRandomId());
privateMessageId = new MessageId(TestUtils.getRandomId());
contentType = "text/plain";
subject = "Foo";
timestamp = System.currentTimeMillis();
@@ -96,7 +100,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
raw = new byte[size];
message = new TestMessage(messageId, null, group, author, contentType,
subject, timestamp, raw);
privateMessage = new TestMessage(messageId, null, null, null,
privateMessage = new TestMessage(privateMessageId, null, null, null,
contentType, subject, timestamp, raw);
transportId = new TransportId(TestUtils.getRandomId());
transportProperties = new TransportProperties(Collections.singletonMap(
@@ -139,7 +143,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
// setRating(authorId, GOOD)
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(UNRATED));
oneOf(database).getMessagesByAuthor(txn, authorId);
oneOf(database).getUnrestrictedGroupMessages(txn, authorId);
will(returnValue(Collections.emptyList()));
oneOf(listener).eventOccurred(with(any(RatingChangedEvent.class)));
// setRating(authorId, GOOD) again
@@ -223,6 +227,59 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
context.assertIsSatisfied();
}
@Test
public void testRestrictedGroupMessagesAreAlwaysSendable()
throws Exception {
final Message groupMessage = new TestMessage(messageId, null,
restrictedGroup, author, contentType, subject, timestamp, raw);
final Message groupMessage1 = new TestMessage(messageId1, null,
restrictedGroup, null, contentType, subject, timestamp, raw);
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{
// addLocalGroupMessage(groupMessage)
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsSubscription(txn, restrictedGroupId);
will(returnValue(true));
oneOf(database).addGroupMessage(txn, groupMessage, false);
will(returnValue(true));
oneOf(database).setReadFlag(txn, messageId, true);
oneOf(database).getContactIds(txn);
will(returnValue(Arrays.asList(contactId)));
oneOf(database).addStatus(txn, contactId, messageId, false);
oneOf(database).setSendability(txn, messageId, 1);
oneOf(database).commitTransaction(txn);
// receiveMessage(groupMessage1)
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).containsVisibleSubscription(txn, contactId,
restrictedGroupId);
will(returnValue(true));
oneOf(database).addGroupMessage(txn, groupMessage1, true);
will(returnValue(true));
oneOf(database).addStatus(txn, contactId, messageId1, true);
oneOf(database).getContactIds(txn);
will(returnValue(Arrays.asList(contactId)));
oneOf(database).setSendability(txn, messageId1, 1);
oneOf(database).addMessageToAck(txn, contactId, messageId1);
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addLocalGroupMessage(groupMessage);
db.receiveMessage(contactId, groupMessage1);
context.assertIsSatisfied();
}
@Test
public void testNullParentStopsBackwardInclusion() throws Exception {
Mockery context = new Mockery();
@@ -237,7 +294,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(UNRATED));
// The sendability of the author's messages should be incremented
oneOf(database).getMessagesByAuthor(txn, authorId);
oneOf(database).getUnrestrictedGroupMessages(txn, authorId);
will(returnValue(Arrays.asList(messageId)));
oneOf(database).getSendability(txn, messageId);
will(returnValue(0));
@@ -269,7 +326,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(UNRATED));
// The sendability of the author's messages should be incremented
oneOf(database).getMessagesByAuthor(txn, authorId);
oneOf(database).getUnrestrictedGroupMessages(txn, authorId);
will(returnValue(Arrays.asList(messageId)));
oneOf(database).getSendability(txn, messageId);
will(returnValue(0));
@@ -306,7 +363,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(UNRATED));
// The sendability of the author's messages should be incremented
oneOf(database).getMessagesByAuthor(txn, authorId);
oneOf(database).getUnrestrictedGroupMessages(txn, authorId);
will(returnValue(Arrays.asList(messageId)));
oneOf(database).getSendability(txn, messageId);
will(returnValue(0));
@@ -497,8 +554,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).addPrivateMessage(txn, privateMessage, contactId,
false);
will(returnValue(true));
oneOf(database).setReadFlag(txn, messageId, true);
oneOf(database).addStatus(txn, contactId, messageId, false);
oneOf(database).setReadFlag(txn, privateMessageId, true);
oneOf(database).addStatus(txn, contactId, privateMessageId, false);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
@@ -1154,9 +1211,9 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).addPrivateMessage(txn, privateMessage, contactId,
true);
will(returnValue(true));
oneOf(database).addStatus(txn, contactId, messageId, true);
oneOf(database).addStatus(txn, contactId, privateMessageId, true);
// The message must be acked
oneOf(database).addMessageToAck(txn, contactId, messageId);
oneOf(database).addMessageToAck(txn, contactId, privateMessageId);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
@@ -1184,7 +1241,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
true);
will(returnValue(false));
// The message must still be acked
oneOf(database).addMessageToAck(txn, contactId, messageId);
oneOf(database).addMessageToAck(txn, contactId, privateMessageId);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
@@ -1561,8 +1618,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).addPrivateMessage(txn, privateMessage, contactId,
false);
will(returnValue(true));
oneOf(database).setReadFlag(txn, messageId, true);
oneOf(database).addStatus(txn, contactId, messageId, false);
oneOf(database).setReadFlag(txn, privateMessageId, true);
oneOf(database).addStatus(txn, contactId, privateMessageId, false);
// The message was added, so the listener should be called
oneOf(listener).eventOccurred(with(any(
PrivateMessageAddedEvent.class)));

View File

@@ -534,27 +534,39 @@ public class H2DatabaseTest extends BriarTestCase {
}
@Test
public void testGetMessagesByAuthor() throws Exception {
public void testGetUnrestrictedGroupMessages() throws Exception {
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
Author author1 = new Author(authorId1, "Bob", new byte[60]);
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
Message message1 = new TestMessage(messageId1, null, group, author1,
contentType, subject, timestamp, raw);
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
Group group1 = new Group(groupId1, "Restricted group name",
new byte[60]);
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
Message message2 = new TestMessage(messageId2, null, group1, author,
contentType, subject, timestamp, raw);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Subscribe to a group and store two messages
// Subscribe to an unrestricted group and store two messages
db.addSubscription(txn, group);
db.addGroupMessage(txn, message, false);
db.addGroupMessage(txn, message1, false);
// Check that each message is retrievable via its author
Iterator<MessageId> it =
db.getMessagesByAuthor(txn, authorId).iterator();
// Subscribe to a restricted group and store a message
db.addSubscription(txn, group1);
db.addGroupMessage(txn, message2, false);
// Check that only the messages in the unrestricted group are retrieved
Collection<MessageId> ids = db.getUnrestrictedGroupMessages(txn,
authorId);
Iterator<MessageId> it = ids.iterator();
assertTrue(it.hasNext());
assertEquals(messageId, it.next());
assertFalse(it.hasNext());
it = db.getMessagesByAuthor(txn, authorId1).iterator();
ids = db.getUnrestrictedGroupMessages(txn, authorId1);
it = ids.iterator();
assertTrue(it.hasNext());
assertEquals(messageId1, it.next());
assertFalse(it.hasNext());