session-android/src/org/thoughtcrime/securesms/service/GroupReceiver.java
Moxie Marlinspike a6e1d56cde Refactor group messaging protocol.
// FREEBIE
2014-02-21 17:51:52 -08:00

160 lines
6.1 KiB
Java

package org.thoughtcrime.securesms.service;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.util.Pair;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.util.Base64;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
public class GroupReceiver {
private final Context context;
public GroupReceiver(Context context) {
this.context = context.getApplicationContext();
}
public void process(MasterSecret masterSecret,
IncomingPushMessage message,
PushMessageContent messageContent,
boolean secure)
{
if (!messageContent.getGroup().hasId()) {
Log.w("GroupReceiver", "Received group message with no id! Ignoring...");
return;
}
if (!secure) {
Log.w("GroupReceiver", "Received insecure group push action! Ignoring...");
return;
}
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
GroupContext group = messageContent.getGroup();
byte[] id = group.getId().toByteArray();
int type = group.getType().getNumber();
GroupRecord record = database.getGroup(id);
if (record != null && type == GroupContext.Type.UPDATE_VALUE) {
handleGroupUpdate(masterSecret, message, group, record);
} else if (record == null && type == GroupContext.Type.UPDATE_VALUE) {
handleGroupCreate(masterSecret, message, group);
} else if (type == GroupContext.Type.QUIT_VALUE) {
handleGroupLeave(masterSecret, message, group, record);
} else if (type == GroupContext.Type.UNKNOWN_VALUE) {
Log.w("GroupReceiver", "Received unknown type, ignoring...");
}
}
private void handleGroupCreate(MasterSecret masterSecret,
IncomingPushMessage message,
GroupContext group)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getId().toByteArray();
database.create(id, message.getSource(), group.getName(), group.getMembersList(),
group.getAvatar(), message.getRelay());
storeMessage(masterSecret, message, group);
}
private void handleGroupUpdate(MasterSecret masterSecret,
IncomingPushMessage message,
GroupContext group,
GroupRecord groupRecord)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getId().toByteArray();
Set<String> recordMembers = new HashSet<String>(groupRecord.getMembers());
Set<String> messageMembers = new HashSet<String>(group.getMembersList());
Set<String> addedMembers = new HashSet<String>(messageMembers);
addedMembers.removeAll(recordMembers);
Set<String> missingMembers = new HashSet<String>(recordMembers);
missingMembers.removeAll(messageMembers);
if (addedMembers.size() > 0) {
Set<String> unionMembers = new HashSet<String>(recordMembers);
unionMembers.addAll(messageMembers);
database.add(id, message.getSource(), new LinkedList<String>(unionMembers));
group = group.toBuilder().clearMembers().addAllMembers(addedMembers).build();
} else {
group = group.toBuilder().clearMembers().build();
}
if (missingMembers.size() > 0) {
// TODO We should tell added and missing about each-other.
}
if (group.hasName() || group.hasAvatar()) {
database.update(id, message.getSource(), group.getName(), group.getAvatar());
}
if (group.hasName() && group.getName() != null && group.getName().equals(groupRecord.getTitle())) {
group = group.toBuilder().clearName().build();
}
storeMessage(masterSecret, message, group);
}
private void handleGroupLeave(MasterSecret masterSecret,
IncomingPushMessage message,
GroupContext group,
GroupRecord record)
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
byte[] id = group.getId().toByteArray();
List<String> members = record.getMembers();
if (members.contains(message.getSource())) {
database.remove(id, message.getSource());
storeMessage(masterSecret, message, group);
}
}
private void storeMessage(MasterSecret masterSecret, IncomingPushMessage message, GroupContext group) {
if (group.hasAvatar()) {
Intent intent = new Intent(context, SendReceiveService.class);
intent.setAction(SendReceiveService.DOWNLOAD_AVATAR_ACTION);
intent.putExtra("group_id", group.getId().toByteArray());
context.startService(intent);
}
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
String body = Base64.encodeBytes(group.toByteArray());
IncomingTextMessage incoming = new IncomingTextMessage(message, body, group);
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, group, body);
Pair<Long, Long> messageAndThreadId = smsDatabase.insertMessageInbox(masterSecret, groupMessage);
smsDatabase.updateMessageBody(masterSecret, messageAndThreadId.first, body);
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
}
}