Make things compile

This commit is contained in:
nielsandriesse 2020-07-15 12:24:43 +10:00
parent 5120565a03
commit 21554441f3
43 changed files with 290 additions and 333 deletions

View File

@ -102,12 +102,11 @@ import org.whispersystems.signalservice.loki.api.Poller;
import org.whispersystems.signalservice.loki.api.PushNotificationAcknowledgement; import org.whispersystems.signalservice.loki.api.PushNotificationAcknowledgement;
import org.whispersystems.signalservice.loki.api.SnodeAPI; import org.whispersystems.signalservice.loki.api.SnodeAPI;
import org.whispersystems.signalservice.loki.api.SwarmAPI; import org.whispersystems.signalservice.loki.api.SwarmAPI;
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI;
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI; import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI;
import org.whispersystems.signalservice.loki.api.shelved.p2p.LokiP2PAPI; import org.whispersystems.signalservice.loki.api.shelved.p2p.LokiP2PAPI;
import org.whispersystems.signalservice.loki.api.shelved.p2p.LokiP2PAPIDelegate; import org.whispersystems.signalservice.loki.api.shelved.p2p.LokiP2PAPIDelegate;
import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol; import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol;
import org.whispersystems.signalservice.loki.protocol.friendrequests.FriendRequestProtocol;
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager; import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol; import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink; import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink;
@ -156,7 +155,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
public MessageNotifier messageNotifier = null; public MessageNotifier messageNotifier = null;
public Poller lokiPoller = null; public Poller lokiPoller = null;
public LokiPublicChatManager lokiPublicChatManager = null; public LokiPublicChatManager lokiPublicChatManager = null;
private LokiPublicChatAPI lokiPublicChatAPI = null; private PublicChatAPI publicChatAPI = null;
public Broadcaster broadcaster = null; public Broadcaster broadcaster = null;
public SignalCommunicationModule communicationModule; public SignalCommunicationModule communicationModule;
@ -189,7 +188,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
if (userPublicKey != null) { if (userPublicKey != null) {
SwarmAPI.Companion.configureIfNeeded(apiDB); SwarmAPI.Companion.configureIfNeeded(apiDB);
SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster); SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster);
FriendRequestProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB); MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB);
SessionMetaProtocol.Companion.configureIfNeeded(apiDB, userPublicKey); SessionMetaProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
SyncMessagesProtocol.Companion.configureIfNeeded(apiDB, userPublicKey); SyncMessagesProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
@ -201,7 +199,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
if (setUpStorageAPIIfNeeded()) { if (setUpStorageAPIIfNeeded()) {
if (userPublicKey != null) { if (userPublicKey != null) {
Set<DeviceLink> deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey); Set<DeviceLink> deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey);
LokiFileServerAPI.shared.setDeviceLinks(deviceLinks); FileServerAPI.shared.setDeviceLinks(deviceLinks);
} }
} }
resubmitProfilePictureIfNeeded(); resubmitProfilePictureIfNeeded();
@ -284,15 +282,15 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
} }
// Loki // Loki
public @Nullable LokiPublicChatAPI getLokiPublicChatAPI() { public @Nullable PublicChatAPI getPublicChatAPI() {
if (lokiPublicChatAPI != null || !IdentityKeyUtil.hasIdentityKey(this)) { return lokiPublicChatAPI; } if (publicChatAPI != null || !IdentityKeyUtil.hasIdentityKey(this)) { return publicChatAPI; }
String userPublicKey = TextSecurePreferences.getLocalNumber(this); String userPublicKey = TextSecurePreferences.getLocalNumber(this);
if (userPublicKey== null) { return lokiPublicChatAPI; } if (userPublicKey== null) { return publicChatAPI; }
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize(); byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this); LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
lokiPublicChatAPI = new LokiPublicChatAPI(userPublicKey, userPrivateKey, apiDB, userDB); publicChatAPI = new PublicChatAPI(userPublicKey, userPrivateKey, apiDB, userDB);
return lokiPublicChatAPI; return publicChatAPI;
} }
private void initializeSecurityProvider() { private void initializeSecurityProvider() {
@ -446,8 +444,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(base, TextSecurePreferences.getLanguage(base))); super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(base, TextSecurePreferences.getLanguage(base)));
} }
private static class ProviderInitializationException extends RuntimeException { private static class ProviderInitializationException extends RuntimeException { }
}
// region Loki // region Loki
public boolean setUpStorageAPIIfNeeded() { public boolean setUpStorageAPIIfNeeded() {
@ -456,7 +453,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
boolean isDebugMode = BuildConfig.DEBUG; boolean isDebugMode = BuildConfig.DEBUG;
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize(); byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
LokiAPIDatabaseProtocol apiDB = DatabaseFactory.getLokiAPIDatabase(this); LokiAPIDatabaseProtocol apiDB = DatabaseFactory.getLokiAPIDatabase(this);
LokiFileServerAPI.Companion.configure(isDebugMode, userPublicKey, userPrivateKey, apiDB); FileServerAPI.Companion.configure(userPublicKey, userPrivateKey, apiDB);
return true; return true;
} }
@ -534,7 +531,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
try { try {
File profilePicture = AvatarHelper.getAvatarFile(this, Address.fromSerialized(userPublicKey)); File profilePicture = AvatarHelper.getAvatarFile(this, Address.fromSerialized(userPublicKey));
StreamDetails stream = new StreamDetails(new FileInputStream(profilePicture), "image/jpeg", profilePicture.length()); StreamDetails stream = new StreamDetails(new FileInputStream(profilePicture), "image/jpeg", profilePicture.length());
LokiFileServerAPI.shared.uploadProfilePicture(LokiFileServerAPI.shared.getServer(), profileKey, stream, () -> { FileServerAPI.shared.uploadProfilePicture(FileServerAPI.shared.getServer(), profileKey, stream, () -> {
TextSecurePreferences.setLastProfilePictureUpload(this, new Date().getTime()); TextSecurePreferences.setLastProfilePictureUpload(this, new Date().getTime());
TextSecurePreferences.setProfileAvatarId(this, new SecureRandom().nextInt()); TextSecurePreferences.setProfileAvatarId(this, new SecureRandom().nextInt());
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey); ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey);
@ -548,9 +545,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
public void updateOpenGroupProfilePicturesIfNeeded() { public void updateOpenGroupProfilePicturesIfNeeded() {
AsyncTask.execute(() -> { AsyncTask.execute(() -> {
LokiPublicChatAPI publicChatAPI = null; PublicChatAPI publicChatAPI = null;
try { try {
publicChatAPI = getLokiPublicChatAPI(); publicChatAPI = getPublicChatAPI();
} catch (Exception e) { } catch (Exception e) {
// Do nothing // Do nothing
} }
@ -590,7 +587,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
} }
@Override @Override
public void sendSessionRequest(@NotNull String publicKey) { public void sendSessionRequestIfNeeded(@NotNull String publicKey) {
// It's never necessary to establish a session with self // It's never necessary to establish a session with self
String userPublicKey = TextSecurePreferences.getLocalNumber(this); String userPublicKey = TextSecurePreferences.getLocalNumber(this);
if (publicKey.equals(userPublicKey)) { return; } if (publicKey.equals(userPublicKey)) { return; }

View File

@ -58,8 +58,8 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.crypto.ProfileCipher; import org.whispersystems.signalservice.api.crypto.ProfileCipher;
import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.api.util.StreamDetails;
import org.whispersystems.signalservice.loki.api.LokiDotNetAPI; import org.whispersystems.signalservice.loki.api.LokiDotNetAPI;
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI;
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI; import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
@ -386,7 +386,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
Context context = CreateProfileActivity.this; Context context = CreateProfileActivity.this;
TextSecurePreferences.setProfileName(context, name); TextSecurePreferences.setProfileName(context, name);
LokiPublicChatAPI publicChatAPI = ApplicationContext.getInstance(context).getLokiPublicChatAPI(); PublicChatAPI publicChatAPI = ApplicationContext.getInstance(context).getPublicChatAPI();
if (publicChatAPI != null) { if (publicChatAPI != null) {
Set<String> servers = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChatServers(); Set<String> servers = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChatServers();
for (String server : servers) { for (String server : servers) {
@ -409,7 +409,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
// Loki - Upload the profile photo here // Loki - Upload the profile photo here
if (avatar != null) { if (avatar != null) {
Log.d("Loki", "Start uploading profile photo"); Log.d("Loki", "Start uploading profile photo");
LokiFileServerAPI storageAPI = LokiFileServerAPI.shared; FileServerAPI storageAPI = FileServerAPI.shared;
LokiDotNetAPI.UploadResult result = storageAPI.uploadProfilePicture(storageAPI.getServer(), profileKey, avatar, () -> { LokiDotNetAPI.UploadResult result = storageAPI.uploadProfilePicture(storageAPI.getServer(), profileKey, avatar, () -> {
TextSecurePreferences.setLastProfilePictureUpload(CreateProfileActivity.this, new Date().getTime()); TextSecurePreferences.setLastProfilePictureUpload(CreateProfileActivity.this, new Date().getTime());
return Unit.INSTANCE; return Unit.INSTANCE;

View File

@ -31,7 +31,7 @@ import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat; import org.whispersystems.signalservice.loki.api.opengroups.PublicChat;
import java.util.List; import java.util.List;
@ -197,7 +197,7 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
long threadID = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(conversationRecipient); long threadID = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(conversationRecipient);
String senderHexEncodedPublicKey = author.getAddress().serialize(); String senderHexEncodedPublicKey = author.getAddress().serialize();
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadID); PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadID);
if (senderHexEncodedPublicKey.equalsIgnoreCase(TextSecurePreferences.getLocalNumber(getContext()))) { if (senderHexEncodedPublicKey.equalsIgnoreCase(TextSecurePreferences.getLocalNumber(getContext()))) {
quoteeDisplayName = TextSecurePreferences.getProfileName(getContext()); quoteeDisplayName = TextSecurePreferences.getProfileName(getContext());
} else if (publicChat != null) { } else if (publicChat != null) {

View File

@ -187,7 +187,6 @@ import org.thoughtcrime.securesms.mms.StickerSlide;
import org.thoughtcrime.securesms.mms.TextSlide; import org.thoughtcrime.securesms.mms.TextSlide;
import org.thoughtcrime.securesms.mms.VideoSlide; import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver; import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.profiles.GroupShareProfileView; import org.thoughtcrime.securesms.profiles.GroupShareProfileView;
@ -227,7 +226,7 @@ import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.thoughtcrime.securesms.util.views.Stub; import org.thoughtcrime.securesms.util.views.Stub;
import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat; import org.whispersystems.signalservice.loki.api.opengroups.PublicChat;
import org.whispersystems.signalservice.loki.protocol.mentions.Mention; import org.whispersystems.signalservice.loki.protocol.mentions.Mention;
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager; import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol; import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
@ -465,9 +464,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(threadId, this); MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(threadId, this);
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
if (publicChat != null) { if (publicChat != null) {
ApplicationContext.getInstance(this).getLokiPublicChatAPI().getChannelInfo(publicChat.getChannel(), publicChat.getServer()).success( displayName -> { ApplicationContext.getInstance(this).getPublicChatAPI().getChannelInfo(publicChat.getChannel(), publicChat.getServer()).success(displayName -> {
updateSubtitleTextView(); updateSubtitleTextView();
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
@ -2172,7 +2171,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
try { try {
int startIndex = result.indexOf("@" + mention.getDisplayName()); int startIndex = result.indexOf("@" + mention.getDisplayName());
int endIndex = startIndex + mention.getDisplayName().length() + 1; // + 1 to include the @ int endIndex = startIndex + mention.getDisplayName().length() + 1; // + 1 to include the @
result = result.substring(0, startIndex) + "@" + mention.getHexEncodedPublicKey() + result.substring(endIndex); result = result.substring(0, startIndex) + "@" + mention.getPublicKey() + result.substring(endIndex);
} catch (Exception exception) { } catch (Exception exception) {
Log.d("Loki", "Couldn't process mention due to error: " + exception.toString() + "."); Log.d("Loki", "Couldn't process mention due to error: " + exception.toString() + ".");
} }
@ -3107,7 +3106,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
muteIndicatorImageView.setVisibility(View.VISIBLE); muteIndicatorImageView.setVisibility(View.VISIBLE);
subtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault())); subtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()));
} else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) { } else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) {
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
if (publicChat != null) { if (publicChat != null) {
Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer()); Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer());
if (userCount == null) { userCount = 0; } if (userCount == null) { userCount = 0; }

View File

@ -101,8 +101,8 @@ import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask; import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat; import org.whispersystems.signalservice.loki.api.opengroups.PublicChat;
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI; import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -399,7 +399,7 @@ public class ConversationFragment extends Fragment
boolean isGroupChat = recipient.isGroupRecipient(); boolean isGroupChat = recipient.isGroupRecipient();
if (isGroupChat) { if (isGroupChat) {
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId); PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
boolean isPublicChat = (publicChat != null); boolean isPublicChat = (publicChat != null);
int selectedMessageCount = messageRecords.size(); int selectedMessageCount = messageRecords.size();
boolean areAllSentByUser = true; boolean areAllSentByUser = true;
@ -409,7 +409,7 @@ public class ConversationFragment extends Fragment
menu.findItem(R.id.menu_context_copy_public_key).setVisible(isPublicChat && selectedMessageCount == 1 && !areAllSentByUser); menu.findItem(R.id.menu_context_copy_public_key).setVisible(isPublicChat && selectedMessageCount == 1 && !areAllSentByUser);
menu.findItem(R.id.menu_context_reply).setVisible(selectedMessageCount == 1); menu.findItem(R.id.menu_context_reply).setVisible(selectedMessageCount == 1);
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext()); String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext());
boolean userCanModerate = isPublicChat && LokiPublicChatAPI.Companion.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer()); boolean userCanModerate = isPublicChat && PublicChatAPI.Companion.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer());
boolean isDeleteOptionVisible = !isPublicChat || (areAllSentByUser || userCanModerate); boolean isDeleteOptionVisible = !isPublicChat || (areAllSentByUser || userCanModerate);
menu.findItem(R.id.menu_context_delete_message).setVisible(isDeleteOptionVisible); menu.findItem(R.id.menu_context_delete_message).setVisible(isDeleteOptionVisible);
} else { } else {
@ -502,7 +502,7 @@ public class ConversationFragment extends Fragment
builder.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messagesCount, messagesCount)); builder.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messagesCount, messagesCount));
builder.setCancelable(true); builder.setCancelable(true);
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId); PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() { builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
@Override @Override
@ -518,7 +518,7 @@ public class ConversationFragment extends Fragment
ArrayList<Long> ignoredMessages = new ArrayList<>(); ArrayList<Long> ignoredMessages = new ArrayList<>();
ArrayList<Long> failedMessages = new ArrayList<>(); ArrayList<Long> failedMessages = new ArrayList<>();
boolean isSentByUser = true; boolean isSentByUser = true;
LokiPublicChatAPI publicChatAPI = ApplicationContext.getInstance(getContext()).getLokiPublicChatAPI(); PublicChatAPI publicChatAPI = ApplicationContext.getInstance(getContext()).getPublicChatAPI();
for (MessageRecord messageRecord : messageRecords) { for (MessageRecord messageRecord : messageRecords) {
isSentByUser = isSentByUser && messageRecord.isOutgoing(); isSentByUser = isSentByUser && messageRecord.isOutgoing();
Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id); Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id);

View File

@ -112,8 +112,8 @@ import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.views.Stub; import org.thoughtcrime.securesms.util.views.Stub;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat; import org.whispersystems.signalservice.loki.api.opengroups.PublicChat;
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI; import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -1001,9 +1001,9 @@ public class ConversationItem extends LinearLayout
profilePictureView.setVisibility(VISIBLE); profilePictureView.setVisibility(VISIBLE);
int visibility = View.GONE; int visibility = View.GONE;
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(messageRecord.getThreadId()); PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(messageRecord.getThreadId());
if (publicChat != null) { if (publicChat != null) {
boolean isModerator = LokiPublicChatAPI.Companion.isUserModerator(current.getRecipient().getAddress().toString(), publicChat.getChannel(), publicChat.getServer()); boolean isModerator = PublicChatAPI.Companion.isUserModerator(current.getRecipient().getAddress().toString(), publicChat.getChannel(), publicChat.getServer());
visibility = isModerator ? View.VISIBLE : View.GONE; visibility = isModerator ? View.VISIBLE : View.GONE;
} }

View File

@ -45,7 +45,7 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat; import org.whispersystems.signalservice.loki.api.opengroups.PublicChat;
import java.io.File; import java.io.File;
@ -545,7 +545,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
try (Cursor lokiPublicChatCursor = db.rawQuery("SELECT public_chat FROM loki_public_chat_database", null)) { try (Cursor lokiPublicChatCursor = db.rawQuery("SELECT public_chat FROM loki_public_chat_database", null)) {
while (lokiPublicChatCursor != null && lokiPublicChatCursor.moveToNext()) { while (lokiPublicChatCursor != null && lokiPublicChatCursor.moveToNext()) {
String chatString = lokiPublicChatCursor.getString(0); String chatString = lokiPublicChatCursor.getString(0);
LokiPublicChat publicChat = LokiPublicChat.fromJSON(chatString); PublicChat publicChat = PublicChat.fromJSON(chatString);
if (publicChat != null) { if (publicChat != null) {
byte[] groupId = publicChat.getId().getBytes(); byte[] groupId = publicChat.getId().getBytes();
String oldId = GroupUtil.getEncodedId(groupId, false); String oldId = GroupUtil.getEncodedId(groupId, false);

View File

@ -152,7 +152,6 @@ public class SignalCommunicationModule {
Optional.fromNullable(IncomingMessageObserver.getUnidentifiedPipe()), Optional.fromNullable(IncomingMessageObserver.getUnidentifiedPipe()),
Optional.of(new MessageSenderEventListener(context)), Optional.of(new MessageSenderEventListener(context)),
TextSecurePreferences.getLocalNumber(context), TextSecurePreferences.getLocalNumber(context),
TextSecurePreferences.getMasterHexEncodedPublicKey(context),
DatabaseFactory.getLokiAPIDatabase(context), DatabaseFactory.getLokiAPIDatabase(context),
DatabaseFactory.getLokiThreadDatabase(context), DatabaseFactory.getLokiThreadDatabase(context),
DatabaseFactory.getLokiMessageDatabase(context), DatabaseFactory.getLokiMessageDatabase(context),

View File

@ -27,7 +27,6 @@ import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;

View File

@ -73,7 +73,6 @@ import org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol;
import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation; import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation;
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol; import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol;
import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob; import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
import org.thoughtcrime.securesms.loki.protocol.PushNullMessageSendJob;
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
import org.thoughtcrime.securesms.loki.protocol.SyncMessagesProtocol; import org.thoughtcrime.securesms.loki.protocol.SyncMessagesProtocol;
@ -103,7 +102,7 @@ import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.loki.LokiSessionResetProtocol; import org.whispersystems.libsignal.loki.SessionResetProtocol;
import org.whispersystems.libsignal.state.SignalProtocolStore; import org.whispersystems.libsignal.state.SignalProtocolStore;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@ -128,7 +127,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOper
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI;
import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher; import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher;
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager; import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus; import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus;
@ -263,11 +262,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
private void handleMessage(@NonNull SignalServiceEnvelope envelope, @NonNull Optional<Long> smsMessageId, boolean isPushNotification) { private void handleMessage(@NonNull SignalServiceEnvelope envelope, @NonNull Optional<Long> smsMessageId, boolean isPushNotification) {
try { try {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context); SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
LokiSessionResetProtocol lokiSessionResetProtocol = new LokiSessionResetImplementation(context); SessionResetProtocol sessionResetProtocol = new LokiSessionResetImplementation(context);
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context)); SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, lokiSessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator()); LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, sessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator());
SignalServiceContent content = cipher.decrypt(envelope); SignalServiceContent content = cipher.decrypt(envelope);
@ -298,12 +297,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent(); boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
// Loki - Handle unlinking request if needed // Loki - Handle unlinking request if needed
if (message.isUnlinkingRequest()) { if (message.isDeviceUnlinkingRequest()) {
MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content); MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content);
} else { } else {
// Loki - Don't process session restoration requests any further
if (message.isSessionRestorationRequest()) { return; }
// Loki - Handle friend request acceptance if needed // Loki - Handle friend request acceptance if needed
FriendRequestProtocol.handleFriendRequestAcceptanceIfNeeded(context, content.getSender(), content); FriendRequestProtocol.handleFriendRequestAcceptanceIfNeeded(context, content.getSender(), content);
@ -371,11 +367,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
// Loki - This is needed for compatibility with refactored desktop clients // Loki - This is needed for compatibility with refactored desktop clients
// ======== // ========
if (content.isFriendRequest()) { // if (content.isFriendRequest()) {
ApplicationContext.getInstance(context).getJobManager().add(new PushNullMessageSendJob(content.getSender())); // ApplicationContext.getInstance(context).getJobManager().add(new PushNullMessageSendJob(content.getSender()));
} else { // } else {
Log.w(TAG, "Got unrecognized message..."); // Log.w(TAG, "Got unrecognized message...");
} // }
Recipient recipient = recipient(context, content.getSender()); Recipient recipient = recipient(context, content.getSender());
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient); long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(context); LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(context);
@ -1432,7 +1428,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} else { } else {
try { try {
// TODO: Burn this with fire when we can // TODO: Burn this with fire when we can
PromiseUtilities.timeout(LokiFileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get(); PromiseUtilities.timeout(FileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get();
String masterPublicKey = org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey); String masterPublicKey = org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey);
if (masterPublicKey == null) { if (masterPublicKey == null) {
masterPublicKey = publicKey; masterPublicKey = publicKey;
@ -1464,7 +1460,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} else { } else {
try { try {
// TODO: Burn this with fire when we can // TODO: Burn this with fire when we can
PromiseUtilities.timeout(LokiFileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get(); PromiseUtilities.timeout(FileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get();
String masterPublicKey = org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey); String masterPublicKey = org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey);
if (masterPublicKey == null) { if (masterPublicKey == null) {
masterPublicKey = publicKey; masterPublicKey = publicKey;

View File

@ -291,7 +291,6 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
.withPreviews(previews) .withPreviews(previews)
.asExpirationUpdate(message.isExpirationUpdate()) .asExpirationUpdate(message.isExpirationUpdate())
.withPreKeyBundle(preKeyBundle) .withPreKeyBundle(preKeyBundle)
.asFriendRequest(isLokiPreKeyBundleMessage)
.build(); .build();
if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) { if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) {

View File

@ -36,7 +36,7 @@ public abstract class PushReceivedJob extends BaseJob {
if (envelope.isReceipt()) { if (envelope.isReceipt()) {
handleReceipt(envelope); handleReceipt(envelope);
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage() || envelope.isUnidentifiedSender() || envelope.isFriendRequest()) { } else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage() || envelope.isUnidentifiedSender()) {
handleMessage(envelope, isPushNotification); handleMessage(envelope, isPushNotification);
} else { } else {
Log.w(TAG, "Received envelope of unknown type: " + envelope.getType()); Log.w(TAG, "Received envelope of unknown type: " + envelope.getType());

View File

@ -16,7 +16,6 @@ import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.ExpiringMessageManager; import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
@ -229,7 +228,6 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
.withExpiration((int)(message.getExpiresIn() / 1000)) .withExpiration((int)(message.getExpiresIn() / 1000))
.withProfileKey(profileKey.orNull()) .withProfileKey(profileKey.orNull())
.asEndSessionMessage(message.isEndSession()) .asEndSessionMessage(message.isEndSession())
.asFriendRequest(isLokiPreKeyBundleMessage)
.withPreKeyBundle(preKeyBundle) .withPreKeyBundle(preKeyBundle)
.build(); .build();

View File

@ -40,10 +40,8 @@ import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetViewDelegat
import org.thoughtcrime.securesms.loki.views.SeedReminderViewDelegate import org.thoughtcrime.securesms.loki.views.SeedReminderViewDelegate
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.notifications.MessageNotifier
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
import org.whispersystems.signalservice.loki.protocol.friendrequests.FriendRequestProtocol
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
@ -160,7 +158,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
val userPublicKey = TextSecurePreferences.getLocalNumber(this) val userPublicKey = TextSecurePreferences.getLocalNumber(this)
val sessionResetImpl = LokiSessionResetImplementation(this) val sessionResetImpl = LokiSessionResetImplementation(this)
if (userPublicKey != null) { if (userPublicKey != null) {
FriendRequestProtocol.configureIfNeeded(apiDB, userPublicKey)
MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB) MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB)
SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey) SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey)
SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey) SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey)
@ -175,7 +172,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
}.map { }.map {
it.recipient.address.toPhoneString() it.recipient.address.toPhoneString()
}.toSet() }.toSet()
LokiFileServerAPI.shared.getDeviceLinks(publicKeys) FileServerAPI.shared.getDeviceLinks(publicKeys)
// TODO: Temporary hack to unbork existing clients // TODO: Temporary hack to unbork existing clients
val allContacts = DatabaseFactory.getRecipientDatabase(this).allAddresses.map { val allContacts = DatabaseFactory.getRecipientDatabase(this).allAddresses.map {
MultiDeviceProtocol.shared.getMasterDevice(it.serialize()) ?: it.serialize() MultiDeviceProtocol.shared.getMasterDevice(it.serialize()) ?: it.serialize()
@ -311,7 +308,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
val apiDatabase = DatabaseFactory.getLokiAPIDatabase(activity) val apiDatabase = DatabaseFactory.getLokiAPIDatabase(activity)
apiDatabase.removeLastMessageServerID(publicChat.channel, publicChat.server) apiDatabase.removeLastMessageServerID(publicChat.channel, publicChat.server)
apiDatabase.removeLastDeletionServerID(publicChat.channel, publicChat.server) apiDatabase.removeLastDeletionServerID(publicChat.channel, publicChat.server)
ApplicationContext.getInstance(activity).lokiPublicChatAPI!!.leave(publicChat.channel, publicChat.server) ApplicationContext.getInstance(activity).publicChatAPI!!.leave(publicChat.channel, publicChat.server)
} }
threadDatabase.deleteConversation(threadID) threadDatabase.deleteConversation(threadID)
ApplicationContext.getInstance(activity).messageNotifier.updateNotification(activity) ApplicationContext.getInstance(activity).messageNotifier.updateNotification(activity)

View File

@ -27,7 +27,6 @@ import org.whispersystems.curve25519.Curve25519
import org.whispersystems.libsignal.ecc.Curve import org.whispersystems.libsignal.ecc.Curve
import org.whispersystems.libsignal.ecc.ECKeyPair import org.whispersystems.libsignal.ecc.ECKeyPair
import org.whispersystems.libsignal.util.KeyHelper import org.whispersystems.libsignal.util.KeyHelper
import org.whispersystems.signalservice.loki.protocol.friendrequests.FriendRequestProtocol
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink
@ -111,7 +110,6 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
val userDB = DatabaseFactory.getLokiUserDatabase(this) val userDB = DatabaseFactory.getLokiUserDatabase(this)
val userPublicKey = TextSecurePreferences.getLocalNumber(this) val userPublicKey = TextSecurePreferences.getLocalNumber(this)
val sessionResetImpl = LokiSessionResetImplementation(this) val sessionResetImpl = LokiSessionResetImplementation(this)
FriendRequestProtocol.configureIfNeeded(apiDB, userPublicKey)
MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB) MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB)
SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey) SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey)
org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.configureIfNeeded(apiDB) org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.configureIfNeeded(apiDB)
@ -124,13 +122,13 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog") linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog")
AsyncTask.execute { AsyncTask.execute {
retryIfNeeded(8) { retryIfNeeded(8) {
MultiDeviceProtocol.sendDeviceLinkMessage(this@LandingActivity, deviceLink.masterHexEncodedPublicKey, deviceLink) MultiDeviceProtocol.sendDeviceLinkMessage(this@LandingActivity, deviceLink.masterPublicKey, deviceLink)
} }
} }
} }
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
TextSecurePreferences.setMasterHexEncodedPublicKey(this, deviceLink.masterHexEncodedPublicKey) TextSecurePreferences.setMasterHexEncodedPublicKey(this, deviceLink.masterPublicKey)
val intent = Intent(this, HomeActivity::class.java) val intent = Intent(this, HomeActivity::class.java)
show(intent) show(intent)
finish() finish()

View File

@ -26,7 +26,7 @@ import org.thoughtcrime.securesms.loki.utilities.recipient
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
import java.util.* import java.util.*
import kotlin.concurrent.schedule import kotlin.concurrent.schedule
@ -126,30 +126,30 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager
val userPublicKey = TextSecurePreferences.getLocalNumber(this) val userPublicKey = TextSecurePreferences.getLocalNumber(this)
val apiDB = DatabaseFactory.getLokiAPIDatabase(this) val apiDB = DatabaseFactory.getLokiAPIDatabase(this)
val deviceLinks = apiDB.getDeviceLinks(userPublicKey) val deviceLinks = apiDB.getDeviceLinks(userPublicKey)
val deviceLink = deviceLinks.find { it.masterHexEncodedPublicKey == userPublicKey && it.slaveHexEncodedPublicKey == slaveDevicePublicKey } val deviceLink = deviceLinks.find { it.masterPublicKey == userPublicKey && it.slavePublicKey == slaveDevicePublicKey }
if (deviceLink == null) { if (deviceLink == null) {
return Toast.makeText(this, R.string.activity_linked_devices_unlinking_failed_message, Toast.LENGTH_LONG).show() return Toast.makeText(this, R.string.activity_linked_devices_unlinking_failed_message, Toast.LENGTH_LONG).show()
} }
LokiFileServerAPI.shared.setDeviceLinks(setOf()).successUi { FileServerAPI.shared.setDeviceLinks(setOf()).successUi {
DatabaseFactory.getLokiAPIDatabase(this).clearDeviceLinks(userPublicKey) DatabaseFactory.getLokiAPIDatabase(this).clearDeviceLinks(userPublicKey)
deviceLinks.forEach { deviceLink -> deviceLinks.forEach { deviceLink ->
// We don't use PushEphemeralMessageJob because want these messages to send before the pre key and // We don't use PushEphemeralMessageJob because want these messages to send before the pre key and
// session associated with the slave device have been deleted // session associated with the slave device have been deleted
val unlinkingRequest = SignalServiceDataMessage.newBuilder() val unlinkingRequest = SignalServiceDataMessage.newBuilder()
.withTimestamp(System.currentTimeMillis()) .withTimestamp(System.currentTimeMillis())
.asUnlinkingRequest(true) .asDeviceUnlinkingRequest(true)
val messageSender = ApplicationContext.getInstance(this@LinkedDevicesActivity).communicationModule.provideSignalMessageSender() val messageSender = ApplicationContext.getInstance(this@LinkedDevicesActivity).communicationModule.provideSignalMessageSender()
val address = SignalServiceAddress(deviceLink.slaveHexEncodedPublicKey) val address = SignalServiceAddress(deviceLink.slavePublicKey)
try { try {
val udAccess = UnidentifiedAccessUtil.getAccessFor(this@LinkedDevicesActivity, recipient(this@LinkedDevicesActivity, deviceLink.slaveHexEncodedPublicKey)) val udAccess = UnidentifiedAccessUtil.getAccessFor(this@LinkedDevicesActivity, recipient(this@LinkedDevicesActivity, deviceLink.slavePublicKey))
messageSender.sendMessage(0, address, udAccess, unlinkingRequest.build()) // The message ID doesn't matter messageSender.sendMessage(0, address, udAccess, unlinkingRequest.build()) // The message ID doesn't matter
} catch (e: Exception) { } catch (e: Exception) {
Log.d("Loki", "Failed to send unlinking request due to error: $e.") Log.d("Loki", "Failed to send unlinking request due to error: $e.")
throw e throw e
} }
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey) DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slavePublicKey)
val sessionStore = TextSecureSessionStore(this@LinkedDevicesActivity) val sessionStore = TextSecureSessionStore(this@LinkedDevicesActivity)
sessionStore.deleteAllSessions(deviceLink.slaveHexEncodedPublicKey) sessionStore.deleteAllSessions(deviceLink.slavePublicKey)
} }
LoaderManager.getInstance(this).restartLoader(0, null, this) LoaderManager.getInstance(this).restartLoader(0, null, this)
Toast.makeText(this, R.string.activity_linked_devices_unlinking_successful_message, Toast.LENGTH_LONG).show() Toast.makeText(this, R.string.activity_linked_devices_unlinking_successful_message, Toast.LENGTH_LONG).show()

View File

@ -42,7 +42,7 @@ import org.thoughtcrime.securesms.util.BitmapUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.crypto.ProfileCipher import org.whispersystems.signalservice.api.crypto.ProfileCipher
import org.whispersystems.signalservice.api.util.StreamDetails import org.whispersystems.signalservice.api.util.StreamDetails
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.File import java.io.File
import java.security.SecureRandom import java.security.SecureRandom
@ -151,7 +151,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
val promises = mutableListOf<Promise<*, Exception>>() val promises = mutableListOf<Promise<*, Exception>>()
val displayName = displayNameToBeUploaded val displayName = displayNameToBeUploaded
if (displayName != null) { if (displayName != null) {
val publicChatAPI = ApplicationContext.getInstance(this).lokiPublicChatAPI val publicChatAPI = ApplicationContext.getInstance(this).publicChatAPI
if (publicChatAPI != null) { if (publicChatAPI != null) {
val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers() val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers()
promises.addAll(servers.map { publicChatAPI.setDisplayName(displayName, it) }) promises.addAll(servers.map { publicChatAPI.setDisplayName(displayName, it) })
@ -162,7 +162,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this) val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this)
val profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey) val profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey)
if (isUpdatingProfilePicture && profilePicture != null) { if (isUpdatingProfilePicture && profilePicture != null) {
val storageAPI = LokiFileServerAPI.shared val storageAPI = FileServerAPI.shared
val deferred = deferred<Unit, Exception>() val deferred = deferred<Unit, Exception>()
AsyncTask.execute { AsyncTask.execute {
val stream = StreamDetails(ByteArrayInputStream(profilePicture), "image/jpeg", profilePicture.size.toLong()) val stream = StreamDetails(ByteArrayInputStream(profilePicture), "image/jpeg", profilePicture.size.toLong())

View File

@ -15,7 +15,7 @@ import java.util.concurrent.TimeUnit
class BackgroundPollWorker : PersistentAlarmManagerListener() { class BackgroundPollWorker : PersistentAlarmManagerListener() {
companion object { companion object {
private val pollInterval = TimeUnit.MINUTES.toMillis(15) private val pollInterval = TimeUnit.MINUTES.toMillis(30)
@JvmStatic @JvmStatic
fun schedule(context: Context) { fun schedule(context: Context) {

View File

@ -12,10 +12,10 @@ import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.groups.GroupManager import org.thoughtcrime.securesms.groups.GroupManager
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
class LokiPublicChatManager(private val context: Context) { class LokiPublicChatManager(private val context: Context) {
private var chats = mutableMapOf<Long, LokiPublicChat>() private var chats = mutableMapOf<Long, PublicChat>()
private val pollers = mutableMapOf<Long, LokiPublicChatPoller>() private val pollers = mutableMapOf<Long, LokiPublicChatPoller>()
private val observers = mutableMapOf<Long, ContentObserver>() private val observers = mutableMapOf<Long, ContentObserver>()
private var isPolling = false private var isPolling = false
@ -55,8 +55,8 @@ class LokiPublicChatManager(private val context: Context) {
isPolling = false isPolling = false
} }
public fun addChat(server: String, channel: Long): Promise<LokiPublicChat, Exception> { public fun addChat(server: String, channel: Long): Promise<PublicChat, Exception> {
val groupChatAPI = ApplicationContext.getInstance(context).lokiPublicChatAPI ?: return Promise.ofFail(IllegalStateException("LokiPublicChatAPI is not set!")) val groupChatAPI = ApplicationContext.getInstance(context).publicChatAPI ?: return Promise.ofFail(IllegalStateException("LokiPublicChatAPI is not set!"))
return groupChatAPI.getAuthToken(server).bind { return groupChatAPI.getAuthToken(server).bind {
groupChatAPI.getChannelInfo(channel, server) groupChatAPI.getChannelInfo(channel, server)
}.map { }.map {
@ -64,8 +64,8 @@ class LokiPublicChatManager(private val context: Context) {
} }
} }
public fun addChat(server: String, channel: Long, name: String): LokiPublicChat { public fun addChat(server: String, channel: Long, name: String): PublicChat {
val chat = LokiPublicChat(channel, server, name, true) val chat = PublicChat(channel, server, name, true)
var threadID = GroupManager.getOpenGroupThreadID(chat.id, context) var threadID = GroupManager.getOpenGroupThreadID(chat.id, context)
// Create the group if we don't have one // Create the group if we don't have one
if (threadID < 0) { if (threadID < 0) {
@ -76,7 +76,7 @@ class LokiPublicChatManager(private val context: Context) {
// Set our name on the server // Set our name on the server
val displayName = TextSecurePreferences.getProfileName(context) val displayName = TextSecurePreferences.getProfileName(context)
if (!TextUtils.isEmpty(displayName)) { if (!TextUtils.isEmpty(displayName)) {
ApplicationContext.getInstance(context).lokiPublicChatAPI?.setDisplayName(displayName, server) ApplicationContext.getInstance(context).publicChatAPI?.setDisplayName(displayName, server)
} }
// Start polling // Start polling
Util.runOnMain{ startPollersIfNeeded() } Util.runOnMain{ startPollersIfNeeded() }

View File

@ -22,16 +22,16 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
import org.whispersystems.signalservice.api.messages.SignalServiceGroup import org.whispersystems.signalservice.api.messages.SignalServiceGroup
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage
import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatMessage import org.whispersystems.signalservice.loki.api.opengroups.PublicChatMessage
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
import java.security.MessageDigest import java.security.MessageDigest
import java.util.* import java.util.*
class LokiPublicChatPoller(private val context: Context, private val group: LokiPublicChat) { class LokiPublicChatPoller(private val context: Context, private val group: PublicChat) {
private val handler = Handler() private val handler = Handler()
private var hasStarted = false private var hasStarted = false
public var isCaughtUp = false public var isCaughtUp = false
@ -40,12 +40,12 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
private val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) private val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
private var displayNameUpdatees = setOf<String>() private var displayNameUpdatees = setOf<String>()
private val api: LokiPublicChatAPI private val api: PublicChatAPI
get() = { get() = {
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize() val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context) val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context)
val lokiUserDatabase = DatabaseFactory.getLokiUserDatabase(context) val lokiUserDatabase = DatabaseFactory.getLokiUserDatabase(context)
LokiPublicChatAPI(userHexEncodedPublicKey, userPrivateKey, lokiAPIDatabase, lokiUserDatabase) PublicChatAPI(userHexEncodedPublicKey, userPrivateKey, lokiAPIDatabase, lokiUserDatabase)
}() }()
// endregion // endregion
@ -112,16 +112,16 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
// endregion // endregion
// region Polling // region Polling
private fun getDataMessage(message: LokiPublicChatMessage): SignalServiceDataMessage { private fun getDataMessage(message: PublicChatMessage): SignalServiceDataMessage {
val id = group.id.toByteArray() val id = group.id.toByteArray()
val serviceGroup = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, SignalServiceGroup.GroupType.PUBLIC_CHAT, null, null, null, null) val serviceGroup = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, SignalServiceGroup.GroupType.PUBLIC_CHAT, null, null, null, null)
val quote = if (message.quote != null) { val quote = if (message.quote != null) {
SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteeHexEncodedPublicKey), message.quote!!.quotedMessageBody, listOf()) SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteePublicKey), message.quote!!.quotedMessageBody, listOf())
} else { } else {
null null
} }
val attachments = message.attachments.mapNotNull { attachment -> val attachments = message.attachments.mapNotNull { attachment ->
if (attachment.kind != LokiPublicChatMessage.Attachment.Kind.Attachment) { return@mapNotNull null } if (attachment.kind != PublicChatMessage.Attachment.Kind.Attachment) { return@mapNotNull null }
SignalServiceAttachmentPointer( SignalServiceAttachmentPointer(
attachment.serverID, attachment.serverID,
attachment.contentType, attachment.contentType,
@ -135,7 +135,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
Optional.fromNullable(attachment.caption), Optional.fromNullable(attachment.caption),
attachment.url) attachment.url)
} }
val linkPreview = message.attachments.firstOrNull { it.kind == LokiPublicChatMessage.Attachment.Kind.LinkPreview } val linkPreview = message.attachments.firstOrNull { it.kind == PublicChatMessage.Attachment.Kind.LinkPreview }
val signalLinkPreviews = mutableListOf<SignalServiceDataMessage.Preview>() val signalLinkPreviews = mutableListOf<SignalServiceDataMessage.Preview>()
if (linkPreview != null) { if (linkPreview != null) {
val attachment = SignalServiceAttachmentPointer( val attachment = SignalServiceAttachmentPointer(
@ -157,16 +157,16 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
} }
fun pollForNewMessages() { fun pollForNewMessages() {
fun processIncomingMessage(message: LokiPublicChatMessage) { fun processIncomingMessage(message: PublicChatMessage) {
// If the sender of the current message is not a slave device, set the display name in the database // If the sender of the current message is not a slave device, set the display name in the database
val masterHexEncodedPublicKey = MultiDeviceProtocol.shared.getMasterDevice(message.hexEncodedPublicKey) val masterHexEncodedPublicKey = MultiDeviceProtocol.shared.getMasterDevice(message.publicKey)
if (masterHexEncodedPublicKey == null) { if (masterHexEncodedPublicKey == null) {
val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})" val senderDisplayName = "${message.displayName} (...${message.publicKey.takeLast(8)})"
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName) DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.publicKey, senderDisplayName)
} }
val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.hexEncodedPublicKey val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.publicKey
val serviceDataMessage = getDataMessage(message) val serviceDataMessage = getDataMessage(message)
val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false, false, false, false) val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false, false, false)
if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) { if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) {
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID)) PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
} else { } else {
@ -191,7 +191,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
} }
} }
} }
fun processOutgoingMessage(message: LokiPublicChatMessage) { fun processOutgoingMessage(message: PublicChatMessage) {
val messageServerID = message.serverID ?: return val messageServerID = message.serverID ?: return
val isDuplicate = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) != null val isDuplicate = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) != null
if (isDuplicate) { return } if (isDuplicate) { return }
@ -206,7 +206,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
PushDecryptJob(context).handleSynchronizeSentTextMessage(transcript) PushDecryptJob(context).handleSynchronizeSentTextMessage(transcript)
} }
// If we got a message from our master device then make sure our mapping stays in sync // If we got a message from our master device then make sure our mapping stays in sync
val recipient = Recipient.from(context, Address.fromSerialized(message.hexEncodedPublicKey), false) val recipient = Recipient.from(context, Address.fromSerialized(message.publicKey), false)
if (recipient.isUserMasterDevice && message.profilePicture != null) { if (recipient.isUserMasterDevice && message.profilePicture != null) {
val profileKey = message.profilePicture!!.profileKey val profileKey = message.profilePicture!!.profileKey
val url = message.profilePicture!!.url val url = message.profilePicture!!.url
@ -222,15 +222,15 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
var uniqueDevices = setOf<String>() var uniqueDevices = setOf<String>()
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize() val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
val apiDB = DatabaseFactory.getLokiAPIDatabase(context) val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
LokiFileServerAPI.configure(false, userHexEncodedPublicKey, userPrivateKey, apiDB) FileServerAPI.configure(userHexEncodedPublicKey, userPrivateKey, apiDB)
// Kovenant propagates a context to chained promises, so LokiPublicChatAPI.sharedContext should be used for all of the below // Kovenant propagates a context to chained promises, so LokiPublicChatAPI.sharedContext should be used for all of the below
api.getMessages(group.channel, group.server).bind(LokiPublicChatAPI.sharedContext) { messages -> api.getMessages(group.channel, group.server).bind(PublicChatAPI.sharedContext) { messages ->
if (messages.isNotEmpty()) { if (messages.isNotEmpty()) {
// We need to fetch the device mapping for any devices we don't have // We need to fetch the device mapping for any devices we don't have
uniqueDevices = messages.map { it.hexEncodedPublicKey }.toSet() uniqueDevices = messages.map { it.publicKey }.toSet()
val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && LokiFileServerAPI.shared.hasDeviceLinkCacheExpired(hexEncodedPublicKey = it) } val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && FileServerAPI.shared.hasDeviceLinkCacheExpired(publicKey = it) }
if (devicesToUpdate.isNotEmpty()) { if (devicesToUpdate.isNotEmpty()) {
return@bind LokiFileServerAPI.shared.getDeviceLinks(devicesToUpdate.toSet()).then { messages } return@bind FileServerAPI.shared.getDeviceLinks(devicesToUpdate.toSet()).then { messages }
} }
} }
Promise.of(messages) Promise.of(messages)
@ -244,7 +244,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
}.successBackground { messages -> }.successBackground { messages ->
// Process messages in the background // Process messages in the background
messages.forEach { message -> messages.forEach { message ->
if (userDevices.contains(message.hexEncodedPublicKey)) { if (userDevices.contains(message.publicKey)) {
processOutgoingMessage(message) processOutgoingMessage(message)
} else { } else {
processIncomingMessage(message) processIncomingMessage(message)

View File

@ -29,9 +29,9 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
@JvmStatic val createOnionRequestPathCacheCommand = "CREATE TABLE $onionRequestPathCache ($indexPath TEXT PRIMARY KEY, $snode TEXT);" @JvmStatic val createOnionRequestPathCacheCommand = "CREATE TABLE $onionRequestPathCache ($indexPath TEXT PRIMARY KEY, $snode TEXT);"
// Swarm cache // Swarm cache
private val swarmCache = "loki_api_swarm_cache" private val swarmCache = "loki_api_swarm_cache"
private val hexEncodedPublicKey = "hex_encoded_public_key" private val swarmPublicKey = "hex_encoded_public_key"
private val swarm = "swarm" private val swarm = "swarm"
@JvmStatic val createSwarmCacheCommand = "CREATE TABLE $swarmCache ($hexEncodedPublicKey TEXT PRIMARY KEY, $swarm TEXT);" @JvmStatic val createSwarmCacheCommand = "CREATE TABLE $swarmCache ($swarmPublicKey TEXT PRIMARY KEY, $swarm TEXT);"
// Last message hash value cache // Last message hash value cache
private val lastMessageHashValueCache = "loki_api_last_message_hash_value_cache" private val lastMessageHashValueCache = "loki_api_last_message_hash_value_cache"
private val target = "target" private val target = "target"
@ -59,12 +59,12 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
@JvmStatic val createLastDeletionServerIDCacheCommand = "CREATE TABLE $lastDeletionServerIDCache ($lastDeletionServerIDCacheIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);" @JvmStatic val createLastDeletionServerIDCacheCommand = "CREATE TABLE $lastDeletionServerIDCache ($lastDeletionServerIDCacheIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);"
// Device link cache // Device link cache
private val deviceLinkCache = "loki_pairing_authorisation_cache" private val deviceLinkCache = "loki_pairing_authorisation_cache"
private val masterHexEncodedPublicKey = "primary_device" private val masterPublicKey = "primary_device"
private val slaveHexEncodedPublicKey = "secondary_device" private val slavePublicKey = "secondary_device"
private val requestSignature = "request_signature" private val requestSignature = "request_signature"
private val authorizationSignature = "grant_signature" private val authorizationSignature = "grant_signature"
@JvmStatic val createDeviceLinkCacheCommand = "CREATE TABLE $deviceLinkCache ($masterHexEncodedPublicKey TEXT, $slaveHexEncodedPublicKey TEXT, " + @JvmStatic val createDeviceLinkCacheCommand = "CREATE TABLE $deviceLinkCache ($masterPublicKey TEXT, $slavePublicKey TEXT, " +
"$requestSignature TEXT NULLABLE DEFAULT NULL, $authorizationSignature TEXT NULLABLE DEFAULT NULL, PRIMARY KEY ($masterHexEncodedPublicKey, $slaveHexEncodedPublicKey));" "$requestSignature TEXT NULLABLE DEFAULT NULL, $authorizationSignature TEXT NULLABLE DEFAULT NULL, PRIMARY KEY ($masterPublicKey, $slavePublicKey));"
// User count cache // User count cache
private val userCountCache = "loki_user_count_cache" private val userCountCache = "loki_user_count_cache"
private val publicChatID = "public_chat_id" private val publicChatID = "public_chat_id"
@ -72,9 +72,9 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
@JvmStatic val createUserCountCacheCommand = "CREATE TABLE $userCountCache ($publicChatID STRING PRIMARY KEY, $userCount INTEGER DEFAULT 0);" @JvmStatic val createUserCountCacheCommand = "CREATE TABLE $userCountCache ($publicChatID STRING PRIMARY KEY, $userCount INTEGER DEFAULT 0);"
// Session request timestamp cache // Session request timestamp cache
private val sessionRequestTimestampCache = "session_request_timestamp_cache" private val sessionRequestTimestampCache = "session_request_timestamp_cache"
private val publicKey = "public_key" private val sessionRequestPublicKey = "public_key"
private val timestamp = "timestamp" private val timestamp = "timestamp"
@JvmStatic val createSessionRequestTimestampCacheCommand = "CREATE TABLE $sessionRequestTimestampCache ($publicKey STRING PRIMARY KEY, $timestamp INTEGER DEFAULT 0);" @JvmStatic val createSessionRequestTimestampCacheCommand = "CREATE TABLE $sessionRequestTimestampCache ($sessionRequestPublicKey STRING PRIMARY KEY, $timestamp INTEGER DEFAULT 0);"
} }
override fun getSnodePool(): Set<Snode> { override fun getSnodePool(): Set<Snode> {
@ -161,9 +161,9 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
set("1-1", path1[1]); set("1-2", path1[2]) set("1-1", path1[1]); set("1-2", path1[2])
} }
override fun getSwarm(hexEncodedPublicKey: String): Set<Snode>? { override fun getSwarm(publicKey: String): Set<Snode>? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(swarmCache, "${Companion.hexEncodedPublicKey} = ?", wrap(hexEncodedPublicKey)) { cursor -> return database.get(swarmCache, "${Companion.swarmPublicKey} = ?", wrap(publicKey)) { cursor ->
val swarmAsString = cursor.getString(cursor.getColumnIndexOrThrow(swarm)) val swarmAsString = cursor.getString(cursor.getColumnIndexOrThrow(swarm))
swarmAsString.split(", ").mapNotNull { targetAsString -> swarmAsString.split(", ").mapNotNull { targetAsString ->
val components = targetAsString.split("-") val components = targetAsString.split("-")
@ -176,7 +176,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
}?.toSet() }?.toSet()
} }
override fun setSwarm(hexEncodedPublicKey: String, newValue: Set<Snode>) { override fun setSwarm(publicKey: String, newValue: Set<Snode>) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val swarmAsString = newValue.joinToString(", ") { target -> val swarmAsString = newValue.joinToString(", ") { target ->
var string = "${target.address}-${target.port}" var string = "${target.address}-${target.port}"
@ -186,21 +186,21 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
} }
string string
} }
val row = wrap(mapOf(Companion.hexEncodedPublicKey to hexEncodedPublicKey, swarm to swarmAsString)) val row = wrap(mapOf(Companion.swarmPublicKey to publicKey, swarm to swarmAsString))
database.insertOrUpdate(swarmCache, row, "${Companion.hexEncodedPublicKey} = ?", wrap(hexEncodedPublicKey)) database.insertOrUpdate(swarmCache, row, "${Companion.swarmPublicKey} = ?", wrap(publicKey))
} }
override fun getLastMessageHashValue(target: Snode): String? { override fun getLastMessageHashValue(snode: Snode): String? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(lastMessageHashValueCache, "${Companion.target} = ?", wrap(target.address)) { cursor -> return database.get(lastMessageHashValueCache, "${Companion.target} = ?", wrap(snode.address)) { cursor ->
cursor.getString(cursor.getColumnIndexOrThrow(lastMessageHashValue)) cursor.getString(cursor.getColumnIndexOrThrow(lastMessageHashValue))
} }
} }
override fun setLastMessageHashValue(target: Snode, newValue: String) { override fun setLastMessageHashValue(snode: Snode, newValue: String) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val row = wrap(mapOf(Companion.target to target.address, lastMessageHashValue to newValue)) val row = wrap(mapOf(Companion.target to snode.address, lastMessageHashValue to newValue))
database.insertOrUpdate(lastMessageHashValueCache, row, "${Companion.target} = ?", wrap(target.address)) database.insertOrUpdate(lastMessageHashValueCache, row, "${Companion.target} = ?", wrap(snode.address))
} }
override fun getReceivedMessageHashValues(): Set<String>? { override fun getReceivedMessageHashValues(): Set<String>? {
@ -277,35 +277,35 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
database.delete(lastDeletionServerIDCache,"$lastDeletionServerIDCacheIndex = ?", wrap(index)) database.delete(lastDeletionServerIDCache,"$lastDeletionServerIDCacheIndex = ?", wrap(index))
} }
override fun getDeviceLinks(hexEncodedPublicKey: String): Set<DeviceLink> { override fun getDeviceLinks(publicKey: String): Set<DeviceLink> {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.getAll(deviceLinkCache, "$masterHexEncodedPublicKey = ? OR $slaveHexEncodedPublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor -> return database.getAll(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( publicKey, publicKey )) { cursor ->
val masterHexEncodedPublicKey = cursor.getString(masterHexEncodedPublicKey) val masterHexEncodedPublicKey = cursor.getString(masterPublicKey)
val slaveHexEncodedPublicKey = cursor.getString(slaveHexEncodedPublicKey) val slaveHexEncodedPublicKey = cursor.getString(slavePublicKey)
val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature) val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature)
val authorizationSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(authorizationSignature))) null else cursor.getBase64EncodedData(authorizationSignature) val authorizationSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(authorizationSignature))) null else cursor.getBase64EncodedData(authorizationSignature)
DeviceLink(masterHexEncodedPublicKey, slaveHexEncodedPublicKey, requestSignature, authorizationSignature) DeviceLink(masterHexEncodedPublicKey, slaveHexEncodedPublicKey, requestSignature, authorizationSignature)
}.toSet() }.toSet()
} }
override fun clearDeviceLinks(hexEncodedPublicKey: String) { override fun clearDeviceLinks(publicKey: String) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
database.delete(deviceLinkCache, "$masterHexEncodedPublicKey = ? OR $slaveHexEncodedPublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) database.delete(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( publicKey, publicKey ))
} }
override fun addDeviceLink(deviceLink: DeviceLink) { override fun addDeviceLink(deviceLink: DeviceLink) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val values = ContentValues() val values = ContentValues()
values.put(masterHexEncodedPublicKey, deviceLink.masterHexEncodedPublicKey) values.put(masterPublicKey, deviceLink.masterPublicKey)
values.put(slaveHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey) values.put(slavePublicKey, deviceLink.slavePublicKey)
if (deviceLink.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(deviceLink.requestSignature)) } if (deviceLink.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(deviceLink.requestSignature)) }
if (deviceLink.authorizationSignature != null) { values.put(authorizationSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) } if (deviceLink.authorizationSignature != null) { values.put(authorizationSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) }
database.insertOrUpdate(deviceLinkCache, values, "$masterHexEncodedPublicKey = ? AND $slaveHexEncodedPublicKey = ?", arrayOf( deviceLink.masterHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey )) database.insertOrUpdate(deviceLinkCache, values, "$masterPublicKey = ? AND $slavePublicKey = ?", arrayOf( deviceLink.masterPublicKey, deviceLink.slavePublicKey ))
} }
override fun removeDeviceLink(deviceLink: DeviceLink) { override fun removeDeviceLink(deviceLink: DeviceLink) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
database.delete(deviceLinkCache, "$masterHexEncodedPublicKey = ? OR $slaveHexEncodedPublicKey = ?", arrayOf( deviceLink.masterHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey )) database.delete(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( deviceLink.masterPublicKey, deviceLink.slavePublicKey ))
} }
fun getUserCount(group: Long, server: String): Int? { fun getUserCount(group: Long, server: String): Int? {
@ -332,8 +332,8 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
override fun setSessionRequestTimestamp(publicKey: String, timestamp: Long) { override fun setSessionRequestTimestamp(publicKey: String, timestamp: Long) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val row = wrap(mapOf(LokiAPIDatabase.publicKey to publicKey, LokiAPIDatabase.timestamp to timestamp.toString())) val row = wrap(mapOf(LokiAPIDatabase.sessionRequestPublicKey to publicKey, LokiAPIDatabase.timestamp to timestamp.toString()))
database.insertOrUpdate(sessionRequestTimestampCache, row, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) database.insertOrUpdate(sessionRequestTimestampCache, row, "${LokiAPIDatabase.sessionRequestPublicKey} = ?", wrap(publicKey))
} }
} }

View File

@ -16,34 +16,34 @@ import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequ
class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol { class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol {
companion object { companion object {
private val messageFriendRequestTableName = "loki_message_friend_request_database" private val messageFriendRequestTable = "loki_message_friend_request_database"
private val messageThreadMappingTableName = "loki_message_thread_mapping_database" private val messageThreadMappingTable = "loki_message_thread_mapping_database"
private val errorMessageTableName = "loki_error_message_database" private val errorMessageTable = "loki_error_message_database"
private val messageID = "message_id" private val messageID = "message_id"
private val serverID = "server_id" private val serverID = "server_id"
private val friendRequestStatus = "friend_request_status" private val friendRequestStatus = "friend_request_status"
private val threadID = "thread_id" private val threadID = "thread_id"
private val errorMessage = "error_message" private val errorMessage = "error_message"
@JvmStatic val createMessageFriendRequestTableCommand = "CREATE TABLE $messageFriendRequestTableName ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);" @JvmStatic val createMessageFriendRequestTableCommand = "CREATE TABLE $messageFriendRequestTable ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);"
@JvmStatic val createMessageToThreadMappingTableCommand = "CREATE TABLE IF NOT EXISTS $messageThreadMappingTableName ($messageID INTEGER PRIMARY KEY, $threadID INTEGER);" @JvmStatic val createMessageToThreadMappingTableCommand = "CREATE TABLE IF NOT EXISTS $messageThreadMappingTable ($messageID INTEGER PRIMARY KEY, $threadID INTEGER);"
@JvmStatic val createErrorMessageTableCommand = "CREATE TABLE IF NOT EXISTS $errorMessageTableName ($messageID INTEGER PRIMARY KEY, $errorMessage STRING);" @JvmStatic val createErrorMessageTableCommand = "CREATE TABLE IF NOT EXISTS $errorMessageTable ($messageID INTEGER PRIMARY KEY, $errorMessage STRING);"
} }
override fun getQuoteServerID(quoteID: Long, quoteeHexEncodedPublicKey: String): Long? { override fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? {
val message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteID, Address.fromSerialized(quoteeHexEncodedPublicKey)) val message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteID, Address.fromSerialized(quoteePublicKey))
return if (message != null) getServerID(message.getId()) else null return if (message != null) getServerID(message.getId()) else null
} }
fun getServerID(messageID: Long): Long? { fun getServerID(messageID: Long): Long? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(messageFriendRequestTableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor -> return database.get(messageFriendRequestTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
cursor.getInt(serverID) cursor.getInt(serverID)
}?.toLong() }?.toLong()
} }
fun getMessageID(serverID: Long): Long? { fun getMessageID(serverID: Long): Long? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(messageFriendRequestTableName, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor -> return database.get(messageFriendRequestTable, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor ->
cursor.getInt(messageID) cursor.getInt(messageID)
}?.toLong() }?.toLong()
} }
@ -53,12 +53,12 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
val contentValues = ContentValues(2) val contentValues = ContentValues(2)
contentValues.put(Companion.messageID, messageID) contentValues.put(Companion.messageID, messageID)
contentValues.put(Companion.serverID, serverID) contentValues.put(Companion.serverID, serverID)
database.insertOrUpdate(messageFriendRequestTableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) database.insertOrUpdate(messageFriendRequestTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
} }
fun getOriginalThreadID(messageID: Long): Long { fun getOriginalThreadID(messageID: Long): Long {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(messageThreadMappingTableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor -> return database.get(messageThreadMappingTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
cursor.getInt(threadID) cursor.getInt(threadID)
}?.toLong() ?: -1L }?.toLong() ?: -1L
} }
@ -68,12 +68,12 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
val contentValues = ContentValues(2) val contentValues = ContentValues(2)
contentValues.put(Companion.messageID, messageID) contentValues.put(Companion.messageID, messageID)
contentValues.put(Companion.threadID, threadID) contentValues.put(Companion.threadID, threadID)
database.insertOrUpdate(messageThreadMappingTableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) database.insertOrUpdate(messageThreadMappingTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
} }
fun getFriendRequestStatus(messageID: Long): LokiMessageFriendRequestStatus { fun getFriendRequestStatus(messageID: Long): LokiMessageFriendRequestStatus {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
val result = database.get(messageFriendRequestTableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor -> val result = database.get(messageFriendRequestTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
cursor.getInt(friendRequestStatus) cursor.getInt(friendRequestStatus)
} }
return if (result != null) { return if (result != null) {
@ -83,12 +83,12 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
} }
} }
override fun setFriendRequestStatus(messageID: Long, friendRequestStatus: LokiMessageFriendRequestStatus) { fun setFriendRequestStatus(messageID: Long, friendRequestStatus: LokiMessageFriendRequestStatus) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val contentValues = ContentValues(2) val contentValues = ContentValues(2)
contentValues.put(Companion.messageID, messageID) contentValues.put(Companion.messageID, messageID)
contentValues.put(Companion.friendRequestStatus, friendRequestStatus.rawValue) contentValues.put(Companion.friendRequestStatus, friendRequestStatus.rawValue)
database.insertOrUpdate(messageFriendRequestTableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) database.insertOrUpdate(messageFriendRequestTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
val threadID = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID) val threadID = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID)
notifyConversationListeners(threadID) notifyConversationListeners(threadID)
} }
@ -99,7 +99,7 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
fun getErrorMessage(messageID: Long): String? { fun getErrorMessage(messageID: Long): String? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(errorMessageTableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor -> return database.get(errorMessageTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
cursor.getString(errorMessage) cursor.getString(errorMessage)
} }
} }
@ -109,6 +109,6 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
val contentValues = ContentValues(2) val contentValues = ContentValues(2)
contentValues.put(Companion.messageID, messageID) contentValues.put(Companion.messageID, messageID)
contentValues.put(Companion.errorMessage, errorMessage) contentValues.put(Companion.errorMessage, errorMessage)
database.insertOrUpdate(errorMessageTableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) database.insertOrUpdate(errorMessageTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
} }
} }

View File

@ -26,8 +26,8 @@ import org.whispersystems.signalservice.loki.database.LokiPreKeyBundleDatabasePr
class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiPreKeyBundleDatabaseProtocol { class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiPreKeyBundleDatabaseProtocol {
companion object { companion object {
private val tableName = "loki_pre_key_bundle_database" private val table = "loki_pre_key_bundle_database"
private val hexEncodedPublicKey = "public_key" private val publicKey = "public_key"
private val preKeyID = "pre_key_id" private val preKeyID = "pre_key_id"
private val preKeyPublic = "pre_key_public" private val preKeyPublic = "pre_key_public"
private val signedPreKeyID = "signed_pre_key_id" private val signedPreKeyID = "signed_pre_key_id"
@ -36,16 +36,16 @@ class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) :
private val identityKey = "identity_key" private val identityKey = "identity_key"
private val deviceID = "device_id" private val deviceID = "device_id"
private val registrationID = "registration_id" private val registrationID = "registration_id"
@JvmStatic val createTableCommand = "CREATE TABLE $tableName (" + "$hexEncodedPublicKey TEXT PRIMARY KEY," + "$preKeyID INTEGER," + @JvmStatic val createTableCommand = "CREATE TABLE $table (" + "$publicKey TEXT PRIMARY KEY," + "$preKeyID INTEGER," +
"$preKeyPublic TEXT NOT NULL," + "$signedPreKeyID INTEGER," + "$signedPreKeyPublic TEXT NOT NULL," + "$preKeyPublic TEXT NOT NULL," + "$signedPreKeyID INTEGER," + "$signedPreKeyPublic TEXT NOT NULL," +
"$signedPreKeySignature TEXT," + "$identityKey TEXT NOT NULL," + "$deviceID INTEGER," + "$registrationID INTEGER" + ");" "$signedPreKeySignature TEXT," + "$identityKey TEXT NOT NULL," + "$deviceID INTEGER," + "$registrationID INTEGER" + ");"
} }
fun generatePreKeyBundle(hexEncodedPublicKey: String): PreKeyBundle? { fun generatePreKeyBundle(publicKey: String): PreKeyBundle? {
var failureCount = 0 var failureCount = 0
while (failureCount < 3) { while (failureCount < 3) {
try { try {
val preKey = generatePreKeyBundle(hexEncodedPublicKey, failureCount > 0) ?: return null val preKey = generatePreKeyBundle(publicKey, failureCount > 0) ?: return null
// Verify the bundle is correct // Verify the bundle is correct
if (!Curve.verifySignature(preKey.identityKey.publicKey, preKey.signedPreKey.serialize(), preKey.signedPreKeySignature)) { if (!Curve.verifySignature(preKey.identityKey.publicKey, preKey.signedPreKey.serialize(), preKey.signedPreKeySignature)) {
throw InvalidKeyException() throw InvalidKeyException()
@ -55,19 +55,19 @@ class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) :
failureCount += 1 failureCount += 1
} }
} }
Log.w("Loki", "Failed to generate a valid pre key bundle for: $hexEncodedPublicKey.") Log.w("Loki", "Failed to generate a valid pre key bundle for: $publicKey.")
return null return null
} }
private fun generatePreKeyBundle(hexEncodedPublicKey: String, forceClean: Boolean): PreKeyBundle? { private fun generatePreKeyBundle(publicKey: String, forceClean: Boolean): PreKeyBundle? {
if (hexEncodedPublicKey.isEmpty()) return null if (publicKey.isEmpty()) return null
var registrationID = TextSecurePreferences.getLocalRegistrationId(context) var registrationID = TextSecurePreferences.getLocalRegistrationId(context)
if (registrationID == 0) { if (registrationID == 0) {
registrationID = KeyHelper.generateRegistrationId(false) registrationID = KeyHelper.generateRegistrationId(false)
TextSecurePreferences.setLocalRegistrationId(context, registrationID) TextSecurePreferences.setLocalRegistrationId(context, registrationID)
} }
val deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID val deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID
val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getOrCreatePreKeyRecord(hexEncodedPublicKey) val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getOrCreatePreKeyRecord(publicKey)
val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context) val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context)
if (!forceClean && TextSecurePreferences.isSignedPreKeyRegistered(context)) { if (!forceClean && TextSecurePreferences.isSignedPreKeyRegistered(context)) {
Log.d("Loki", "A signed pre key has already been registered.") Log.d("Loki", "A signed pre key has already been registered.")
@ -80,9 +80,9 @@ class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) :
return PreKeyBundle(registrationID, deviceID, preKeyRecord.id, preKeyRecord.keyPair.publicKey, activeSignedPreKey.id, activeSignedPreKey.keyPair.publicKey, activeSignedPreKey.signature, identityKeyPair.publicKey) return PreKeyBundle(registrationID, deviceID, preKeyRecord.id, preKeyRecord.keyPair.publicKey, activeSignedPreKey.id, activeSignedPreKey.keyPair.publicKey, activeSignedPreKey.signature, identityKeyPair.publicKey)
} }
override fun getPreKeyBundle(hexEncodedPublicKey: String): PreKeyBundle? { override fun getPreKeyBundle(publicKey: String): PreKeyBundle? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(tableName, "${Companion.hexEncodedPublicKey} = ?", arrayOf( hexEncodedPublicKey )) { cursor -> return database.get(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) { cursor ->
val registrationID = cursor.getInt(registrationID) val registrationID = cursor.getInt(registrationID)
val deviceID = cursor.getInt(deviceID) val deviceID = cursor.getInt(deviceID)
val preKeyID = cursor.getInt(preKeyID) val preKeyID = cursor.getInt(preKeyID)
@ -95,7 +95,7 @@ class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) :
} }
} }
fun setPreKeyBundle(hexEncodedPublicKey: String, preKeyBundle: PreKeyBundle) { fun setPreKeyBundle(publicKey: String, preKeyBundle: PreKeyBundle) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val values = ContentValues(9) val values = ContentValues(9)
values.put(registrationID, preKeyBundle.registrationId) values.put(registrationID, preKeyBundle.registrationId)
@ -106,20 +106,20 @@ class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) :
values.put(signedPreKeyPublic, Base64.encodeBytes(preKeyBundle.signedPreKey.serialize())) values.put(signedPreKeyPublic, Base64.encodeBytes(preKeyBundle.signedPreKey.serialize()))
values.put(signedPreKeySignature, Base64.encodeBytes(preKeyBundle.signedPreKeySignature)) values.put(signedPreKeySignature, Base64.encodeBytes(preKeyBundle.signedPreKeySignature))
values.put(identityKey, Base64.encodeBytes(preKeyBundle.identityKey.serialize())) values.put(identityKey, Base64.encodeBytes(preKeyBundle.identityKey.serialize()))
values.put(Companion.hexEncodedPublicKey, hexEncodedPublicKey) values.put(Companion.publicKey, publicKey)
database.insertOrUpdate(tableName, values, "${Companion.hexEncodedPublicKey} = ?", arrayOf( hexEncodedPublicKey )) database.insertOrUpdate(table, values, "${Companion.publicKey} = ?", arrayOf( publicKey ))
} }
override fun removePreKeyBundle(hexEncodedPublicKey: String) { override fun removePreKeyBundle(publicKey: String) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
database.delete(tableName, "${Companion.hexEncodedPublicKey} = ?", arrayOf( hexEncodedPublicKey )) database.delete(table, "${Companion.publicKey} = ?", arrayOf( publicKey ))
} }
fun hasPreKeyBundle(hexEncodedPublicKey: String): Boolean { fun hasPreKeyBundle(publicKey: String): Boolean {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
var cursor: Cursor? = null var cursor: Cursor? = null
return try { return try {
cursor = database.query(tableName, null, "${Companion.hexEncodedPublicKey} = ?", arrayOf( hexEncodedPublicKey ), null, null, null) cursor = database.query(table, null, "${Companion.publicKey} = ?", arrayOf( publicKey ), null, null, null)
cursor != null && cursor.count > 0 cursor != null && cursor.count > 0
} catch (e: Exception) { } catch (e: Exception) {
false false

View File

@ -14,38 +14,38 @@ import org.whispersystems.signalservice.loki.database.LokiPreKeyRecordDatabasePr
class LokiPreKeyRecordDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiPreKeyRecordDatabaseProtocol { class LokiPreKeyRecordDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiPreKeyRecordDatabaseProtocol {
companion object { companion object {
private val tableName = "loki_pre_key_record_database" private val table = "loki_pre_key_record_database"
private val hexEncodedPublicKey = "public_key" private val publicKey = "public_key"
private val preKeyID = "pre_key_id" private val preKeyID = "pre_key_id"
@JvmStatic val createTableCommand = "CREATE TABLE $tableName ($hexEncodedPublicKey TEXT PRIMARY KEY, $preKeyID INTEGER);" @JvmStatic val createTableCommand = "CREATE TABLE $table ($publicKey TEXT PRIMARY KEY, $preKeyID INTEGER);"
} }
fun hasPreKey(hexEncodedPublicKey: String): Boolean { fun hasPreKey(publicKey: String): Boolean {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(tableName, "${Companion.hexEncodedPublicKey} = ?", arrayOf( hexEncodedPublicKey )) { it.count > 0 } ?: false return database.get(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) { it.count > 0 } ?: false
} }
override fun getPreKeyRecord(hexEncodedPublicKey: String): PreKeyRecord? { override fun getPreKeyRecord(publicKey: String): PreKeyRecord? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(tableName, "${Companion.hexEncodedPublicKey} = ?", arrayOf( hexEncodedPublicKey )) { cursor -> return database.get(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) { cursor ->
val preKeyID = cursor.getInt(preKeyID) val preKeyID = cursor.getInt(preKeyID)
PreKeyUtil.loadPreKey(context, preKeyID) PreKeyUtil.loadPreKey(context, preKeyID)
} }
} }
fun getOrCreatePreKeyRecord(hexEncodedPublicKey: String): PreKeyRecord { fun getOrCreatePreKeyRecord(publicKey: String): PreKeyRecord {
return getPreKeyRecord(hexEncodedPublicKey) ?: generateAndStorePreKeyRecord(hexEncodedPublicKey) return getPreKeyRecord(publicKey) ?: generateAndStorePreKeyRecord(publicKey)
} }
private fun generateAndStorePreKeyRecord(hexEncodedPublicKey: String): PreKeyRecord { private fun generateAndStorePreKeyRecord(publicKey: String): PreKeyRecord {
val records = PreKeyUtil.generatePreKeyRecords(context, 1) val records = PreKeyUtil.generatePreKeyRecords(context, 1)
PreKeyUtil.storePreKeyRecords(context, records) PreKeyUtil.storePreKeyRecords(context, records)
val record = records.first() val record = records.first()
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val values = ContentValues(2) val values = ContentValues(2)
values.put(Companion.hexEncodedPublicKey, hexEncodedPublicKey) values.put(Companion.publicKey, publicKey)
values.put(preKeyID, record.id) values.put(preKeyID, record.id)
database.insertOrUpdate(tableName, values, "${Companion.hexEncodedPublicKey} = ?", arrayOf( hexEncodedPublicKey )) database.insertOrUpdate(table, values, "${Companion.publicKey} = ?", arrayOf( publicKey ))
return record return record
} }
} }

View File

@ -11,9 +11,9 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.loki.utilities.* import org.thoughtcrime.securesms.loki.utilities.*
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.libsignal.loki.LokiSessionResetStatus import org.whispersystems.libsignal.loki.SessionResetStatus
import org.whispersystems.signalservice.internal.util.JsonUtil import org.whispersystems.signalservice.internal.util.JsonUtil
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
import org.whispersystems.signalservice.loki.database.LokiThreadDatabaseProtocol import org.whispersystems.signalservice.loki.database.LokiThreadDatabaseProtocol
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation
@ -22,16 +22,16 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
var delegate: LokiThreadDatabaseDelegate? = null var delegate: LokiThreadDatabaseDelegate? = null
companion object { companion object {
private val friendRequestTableName = "loki_thread_friend_request_database" private val friendRequestTable = "loki_thread_friend_request_database"
private val sessionResetTableName = "loki_thread_session_reset_database" private val sessionResetTable = "loki_thread_session_reset_database"
val publicChatTableName = "loki_public_chat_database" val publicChatTable = "loki_public_chat_database"
val threadID = "thread_id" val threadID = "thread_id"
private val friendRequestStatus = "friend_request_status" private val friendRequestStatus = "friend_request_status"
private val sessionResetStatus = "session_reset_status" private val sessionResetStatus = "session_reset_status"
val publicChat = "public_chat" val publicChat = "public_chat"
@JvmStatic val createFriendRequestTableCommand = "CREATE TABLE $friendRequestTableName ($threadID INTEGER PRIMARY KEY, $friendRequestStatus INTEGER DEFAULT 0);" @JvmStatic val createFriendRequestTableCommand = "CREATE TABLE $friendRequestTable ($threadID INTEGER PRIMARY KEY, $friendRequestStatus INTEGER DEFAULT 0);"
@JvmStatic val createSessionResetTableCommand = "CREATE TABLE $sessionResetTableName ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);" @JvmStatic val createSessionResetTableCommand = "CREATE TABLE $sessionResetTable ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);"
@JvmStatic val createPublicChatTableCommand = "CREATE TABLE $publicChatTableName ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);" @JvmStatic val createPublicChatTableCommand = "CREATE TABLE $publicChatTable ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);"
} }
override fun getThreadID(hexEncodedPublicKey: String): Long { override fun getThreadID(hexEncodedPublicKey: String): Long {
@ -49,7 +49,7 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
val recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadID) val recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadID)
if (recipient != null && recipient.isGroupRecipient) { return LokiThreadFriendRequestStatus.FRIENDS; } if (recipient != null && recipient.isGroupRecipient) { return LokiThreadFriendRequestStatus.FRIENDS; }
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
val result = database.get(friendRequestTableName, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor -> val result = database.get(friendRequestTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor ->
cursor.getInt(friendRequestStatus) cursor.getInt(friendRequestStatus)
} }
return if (result != null) { return if (result != null) {
@ -59,59 +59,53 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
} }
} }
override fun setFriendRequestStatus(threadID: Long, friendRequestStatus: LokiThreadFriendRequestStatus) { fun setFriendRequestStatus(threadID: Long, friendRequestStatus: LokiThreadFriendRequestStatus) {
if (threadID < 0) { return } if (threadID < 0) { return }
Log.d("Loki", "Setting FR status for thread with ID $threadID to $friendRequestStatus.") Log.d("Loki", "Setting FR status for thread with ID $threadID to $friendRequestStatus.")
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val contentValues = ContentValues(2) val contentValues = ContentValues(2)
contentValues.put(Companion.threadID, threadID) contentValues.put(Companion.threadID, threadID)
contentValues.put(Companion.friendRequestStatus, friendRequestStatus.rawValue) contentValues.put(Companion.friendRequestStatus, friendRequestStatus.rawValue)
database.insertOrUpdate(friendRequestTableName, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) database.insertOrUpdate(friendRequestTable, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
notifyConversationListListeners() notifyConversationListListeners()
notifyConversationListeners(threadID) notifyConversationListeners(threadID)
delegate?.handleThreadFriendRequestStatusChanged(threadID) delegate?.handleThreadFriendRequestStatusChanged(threadID)
} }
fun hasPendingFriendRequest(threadID: Long): Boolean { fun getSessionResetStatus(hexEncodedPublicKey: String): SessionResetStatus {
val friendRequestStatus = getFriendRequestStatus(threadID)
return friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENDING || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT
|| friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED
}
fun getSessionResetStatus(hexEncodedPublicKey: String): LokiSessionResetStatus {
val threadID = getThreadID(hexEncodedPublicKey) val threadID = getThreadID(hexEncodedPublicKey)
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
val result = database.get(sessionResetTableName, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor -> val result = database.get(sessionResetTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor ->
cursor.getInt(sessionResetStatus) cursor.getInt(sessionResetStatus)
} }
return if (result != null) { return if (result != null) {
LokiSessionResetStatus.values().first { it.rawValue == result } SessionResetStatus.values().first { it.rawValue == result }
} else { } else {
LokiSessionResetStatus.NONE SessionResetStatus.NONE
} }
} }
fun setSessionResetStatus(hexEncodedPublicKey: String, sessionResetStatus: LokiSessionResetStatus) { fun setSessionResetStatus(hexEncodedPublicKey: String, sessionResetStatus: SessionResetStatus) {
val threadID = getThreadID(hexEncodedPublicKey) val threadID = getThreadID(hexEncodedPublicKey)
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val contentValues = ContentValues(2) val contentValues = ContentValues(2)
contentValues.put(Companion.threadID, threadID) contentValues.put(Companion.threadID, threadID)
contentValues.put(Companion.sessionResetStatus, sessionResetStatus.rawValue) contentValues.put(Companion.sessionResetStatus, sessionResetStatus.rawValue)
database.insertOrUpdate(sessionResetTableName, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) database.insertOrUpdate(sessionResetTable, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
notifyConversationListListeners() notifyConversationListListeners()
notifyConversationListeners(threadID) notifyConversationListeners(threadID)
} }
fun getAllPublicChats(): Map<Long, LokiPublicChat> { fun getAllPublicChats(): Map<Long, PublicChat> {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
var cursor: Cursor? = null var cursor: Cursor? = null
val result = mutableMapOf<Long, LokiPublicChat>() val result = mutableMapOf<Long, PublicChat>()
try { try {
cursor = database.rawQuery("select * from $publicChatTableName", null) cursor = database.rawQuery("select * from $publicChatTable", null)
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
val threadID = cursor.getLong(threadID) val threadID = cursor.getLong(threadID)
val string = cursor.getString(publicChat) val string = cursor.getString(publicChat)
val publicChat = LokiPublicChat.fromJSON(string) val publicChat = PublicChat.fromJSON(string)
if (publicChat != null) { result[threadID] = publicChat } if (publicChat != null) { result[threadID] = publicChat }
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -126,31 +120,31 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
return getAllPublicChats().values.fold(setOf()) { set, chat -> set.plus(chat.server) } return getAllPublicChats().values.fold(setOf()) { set, chat -> set.plus(chat.server) }
} }
override fun getPublicChat(threadID: Long): LokiPublicChat? { override fun getPublicChat(threadID: Long): PublicChat? {
if (threadID < 0) { return null } if (threadID < 0) { return null }
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(publicChatTableName, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor -> return database.get(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor ->
val publicChatAsJSON = cursor.getString(publicChat) val publicChatAsJSON = cursor.getString(publicChat)
LokiPublicChat.fromJSON(publicChatAsJSON) PublicChat.fromJSON(publicChatAsJSON)
} }
} }
override fun setPublicChat(publicChat: LokiPublicChat, threadID: Long) { override fun setPublicChat(publicChat: PublicChat, threadID: Long) {
if (threadID < 0) { return } if (threadID < 0) { return }
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val contentValues = ContentValues(2) val contentValues = ContentValues(2)
contentValues.put(Companion.threadID, threadID) contentValues.put(Companion.threadID, threadID)
contentValues.put(Companion.publicChat, JsonUtil.toJson(publicChat.toJSON())) contentValues.put(Companion.publicChat, JsonUtil.toJson(publicChat.toJSON()))
database.insertOrUpdate(publicChatTableName, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) database.insertOrUpdate(publicChatTable, contentValues, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
} }
override fun removePublicChat(threadID: Long) { override fun removePublicChat(threadID: Long) {
databaseHelper.writableDatabase.delete(publicChatTableName, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) databaseHelper.writableDatabase.delete(publicChatTable, "${Companion.threadID} = ?", arrayOf( threadID.toString() ))
} }
fun addSessionRestoreDevice(threadID: Long, hexEncodedPublicKey: String) { fun addSessionRestoreDevice(threadID: Long, publicKey: String) {
val devices = getSessionRestoreDevices(threadID).toMutableSet() val devices = getSessionRestoreDevices(threadID).toMutableSet()
if (devices.add(hexEncodedPublicKey)) { if (devices.add(publicKey)) {
TextSecurePreferences.setStringPreference(context, "session_restore_devices_$threadID", devices.joinToString(",")) TextSecurePreferences.setStringPreference(context, "session_restore_devices_$threadID", devices.joinToString(","))
delegate?.handleSessionRestoreDevicesChanged(threadID) delegate?.handleSessionRestoreDevicesChanged(threadID)
} }

View File

@ -20,60 +20,60 @@ class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database
private val displayName = "display_name" private val displayName = "display_name"
// Display name cache // Display name cache
private val displayNameTable = "loki_user_display_name_database" private val displayNameTable = "loki_user_display_name_database"
private val hexEncodedPublicKey = "hex_encoded_public_key" private val publicKey = "hex_encoded_public_key"
@JvmStatic val createDisplayNameTableCommand = "CREATE TABLE $displayNameTable ($hexEncodedPublicKey TEXT PRIMARY KEY, $displayName TEXT);" @JvmStatic val createDisplayNameTableCommand = "CREATE TABLE $displayNameTable ($publicKey TEXT PRIMARY KEY, $displayName TEXT);"
// Server display name cache // Server display name cache
private val serverDisplayNameTable = "loki_user_server_display_name_database" private val serverDisplayNameTable = "loki_user_server_display_name_database"
private val serverID = "server_id" private val serverID = "server_id"
@JvmStatic val createServerDisplayNameTableCommand = "CREATE TABLE $serverDisplayNameTable ($hexEncodedPublicKey TEXT, $serverID TEXT, $displayName TEXT, PRIMARY KEY ($hexEncodedPublicKey, $serverID));" @JvmStatic val createServerDisplayNameTableCommand = "CREATE TABLE $serverDisplayNameTable ($publicKey TEXT, $serverID TEXT, $displayName TEXT, PRIMARY KEY ($publicKey, $serverID));"
} }
override fun getDisplayName(hexEncodedPublicKey: String): String? { override fun getDisplayName(publicKey: String): String? {
if (hexEncodedPublicKey == TextSecurePreferences.getLocalNumber(context)) { if (publicKey == TextSecurePreferences.getLocalNumber(context)) {
return TextSecurePreferences.getProfileName(context) return TextSecurePreferences.getProfileName(context)
} else { } else {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(displayNameTable, "${Companion.hexEncodedPublicKey} = ?", arrayOf( hexEncodedPublicKey )) { cursor -> return database.get(displayNameTable, "${Companion.publicKey} = ?", arrayOf( publicKey )) { cursor ->
cursor.getString(cursor.getColumnIndexOrThrow(displayName)) cursor.getString(cursor.getColumnIndexOrThrow(displayName))
} }
} }
} }
fun setDisplayName(hexEncodedPublicKey: String, displayName: String) { fun setDisplayName(publicKey: String, displayName: String) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val row = ContentValues(2) val row = ContentValues(2)
row.put(Companion.hexEncodedPublicKey, hexEncodedPublicKey) row.put(Companion.publicKey, publicKey)
row.put(Companion.displayName, displayName) row.put(Companion.displayName, displayName)
database.insertOrUpdate(displayNameTable, row, "${Companion.hexEncodedPublicKey} = ?", arrayOf( hexEncodedPublicKey )) database.insertOrUpdate(displayNameTable, row, "${Companion.publicKey} = ?", arrayOf( publicKey ))
Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false).notifyListeners() Recipient.from(context, Address.fromSerialized(publicKey), false).notifyListeners()
} }
override fun getServerDisplayName(serverID: String, hexEncodedPublicKey: String): String? { override fun getServerDisplayName(serverID: String, publicKey: String): String? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(serverDisplayNameTable, "${Companion.hexEncodedPublicKey} = ? AND ${Companion.serverID} = ?", arrayOf( hexEncodedPublicKey, serverID )) { cursor -> return database.get(serverDisplayNameTable, "${Companion.publicKey} = ? AND ${Companion.serverID} = ?", arrayOf( publicKey, serverID )) { cursor ->
cursor.getString(cursor.getColumnIndexOrThrow(displayName)) cursor.getString(cursor.getColumnIndexOrThrow(displayName))
} }
} }
fun setServerDisplayName(serverID: String, hexEncodedPublicKey: String, displayName: String) { fun setServerDisplayName(serverID: String, publicKey: String, displayName: String) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val values = ContentValues(3) val values = ContentValues(3)
values.put(Companion.serverID, serverID) values.put(Companion.serverID, serverID)
values.put(Companion.hexEncodedPublicKey, hexEncodedPublicKey) values.put(Companion.publicKey, publicKey)
values.put(Companion.displayName, displayName) values.put(Companion.displayName, displayName)
try { try {
database.insertWithOnConflict(serverDisplayNameTable, null, values, SQLiteDatabase.CONFLICT_REPLACE) database.insertWithOnConflict(serverDisplayNameTable, null, values, SQLiteDatabase.CONFLICT_REPLACE)
Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false).notifyListeners() Recipient.from(context, Address.fromSerialized(publicKey), false).notifyListeners()
} catch (e: Exception) { } catch (e: Exception) {
Log.d("Loki", "Couldn't save server display name due to exception: $e.") Log.d("Loki", "Couldn't save server display name due to exception: $e.")
} }
} }
override fun getProfilePictureURL(hexEncodedPublicKey: String): String? { override fun getProfilePictureURL(publicKey: String): String? {
return if (hexEncodedPublicKey == TextSecurePreferences.getLocalNumber(context)) { return if (publicKey == TextSecurePreferences.getLocalNumber(context)) {
TextSecurePreferences.getProfilePictureURL(context) TextSecurePreferences.getProfilePictureURL(context)
} else { } else {
Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false).resolve().profileAvatar Recipient.from(context, Address.fromSerialized(publicKey), false).resolve().profileAvatar
} }
} }
} }

View File

@ -22,7 +22,7 @@ import org.thoughtcrime.securesms.loki.utilities.toPx
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.loki.api.SnodeAPI import org.whispersystems.signalservice.loki.api.SnodeAPI
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLinkingSession import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLinkingSession
@ -52,7 +52,7 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
} }
override fun requestUserAuthorization(deviceLink: DeviceLink) { override fun requestUserAuthorization(deviceLink: DeviceLink) {
if (deviceLink.type != DeviceLink.Type.REQUEST || deviceLink.masterHexEncodedPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return } if (deviceLink.type != DeviceLink.Type.REQUEST || deviceLink.masterPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return }
Util.runOnMain { Util.runOnMain {
this.deviceLink = deviceLink this.deviceLink = deviceLink
contentView.qrCodeImageView.visibility = View.GONE contentView.qrCodeImageView.visibility = View.GONE
@ -62,7 +62,7 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_master_mode_title_2) contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_master_mode_title_2)
contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_master_mode_explanation_2) contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_master_mode_explanation_2)
contentView.mnemonicTextView.visibility = View.VISIBLE contentView.mnemonicTextView.visibility = View.VISIBLE
contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), deviceLink.slaveHexEncodedPublicKey) contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), deviceLink.slavePublicKey)
contentView.authorizeButton.visibility = View.VISIBLE contentView.authorizeButton.visibility = View.VISIBLE
} }
} }
@ -84,7 +84,7 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
contentView.cancelButton.visibility = View.GONE contentView.cancelButton.visibility = View.GONE
contentView.authorizeButton.visibility = View.GONE contentView.authorizeButton.visibility = View.GONE
} }
LokiFileServerAPI.shared.addDeviceLink(deviceLink).bind(SnodeAPI.sharedContext) { FileServerAPI.shared.addDeviceLink(deviceLink).bind(SnodeAPI.sharedContext) {
MultiDeviceProtocol.signAndSendDeviceLinkMessage(context!!, deviceLink) MultiDeviceProtocol.signAndSendDeviceLinkMessage(context!!, deviceLink)
}.success { }.success {
TextSecurePreferences.setMultiDevice(context!!, true) TextSecurePreferences.setMultiDevice(context!!, true)
@ -92,8 +92,8 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
delegate?.onDeviceLinkRequestAuthorized() delegate?.onDeviceLinkRequestAuthorized()
dismiss() dismiss()
}.fail { }.fail {
LokiFileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem FileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem
DatabaseFactory.getLokiPreKeyBundleDatabase(context!!).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey) DatabaseFactory.getLokiPreKeyBundleDatabase(context!!).removePreKeyBundle(deviceLink.slavePublicKey)
}.failUi { }.failUi {
delegate?.onDeviceLinkAuthorizationFailed() delegate?.onDeviceLinkAuthorizationFailed()
dismiss() dismiss()
@ -104,7 +104,7 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
DeviceLinkingSession.shared.stopListeningForLinkingRequests() DeviceLinkingSession.shared.stopListeningForLinkingRequests()
DeviceLinkingSession.shared.removeListener(this) DeviceLinkingSession.shared.removeListener(this)
if (deviceLink != null) { if (deviceLink != null) {
DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(deviceLink!!.slaveHexEncodedPublicKey) DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(deviceLink!!.slavePublicKey)
} }
dismiss() dismiss()
delegate?.onDeviceLinkCanceled() delegate?.onDeviceLinkCanceled()

View File

@ -41,7 +41,7 @@ class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener
} }
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
if (deviceLink.type != DeviceLink.Type.AUTHORIZATION || deviceLink.slaveHexEncodedPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return } if (deviceLink.type != DeviceLink.Type.AUTHORIZATION || deviceLink.slavePublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return }
Util.runOnMain { Util.runOnMain {
this.deviceLink = deviceLink this.deviceLink = deviceLink
DeviceLinkingSession.shared.stopListeningForLinkingRequests() DeviceLinkingSession.shared.stopListeningForLinkingRequests()

View File

@ -18,7 +18,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceContent
import org.whispersystems.signalservice.api.messages.SignalServiceGroup import org.whispersystems.signalservice.api.messages.SignalServiceGroup
import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.loki.api.SnodeAPI import org.whispersystems.signalservice.loki.api.SnodeAPI
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
import java.util.* import java.util.*
@ -32,7 +32,7 @@ object ClosedGroupsProtocol {
if (!conversation.address.isClosedGroup || groupID == null) { return false } if (!conversation.address.isClosedGroup || groupID == null) { return false }
// A closed group's members should never include slave devices // A closed group's members should never include slave devices
val senderPublicKey = content.sender val senderPublicKey = content.sender
LokiFileServerAPI.shared.getDeviceLinks(senderPublicKey).timeout(6000).get() FileServerAPI.shared.getDeviceLinks(senderPublicKey).timeout(6000).get()
val senderMasterPublicKey = MultiDeviceProtocol.shared.getMasterDevice(senderPublicKey) val senderMasterPublicKey = MultiDeviceProtocol.shared.getMasterDevice(senderPublicKey)
val publicKeyToCheckFor = senderMasterPublicKey ?: senderPublicKey val publicKeyToCheckFor = senderMasterPublicKey ?: senderPublicKey
val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, true) val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, true)
@ -57,7 +57,7 @@ object ClosedGroupsProtocol {
} else { } else {
// A closed group's members should never include slave devices // A closed group's members should never include slave devices
val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, false) val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, false)
return LokiFileServerAPI.shared.getDeviceLinks(members.map { it.address.serialize() }.toSet()).map { return FileServerAPI.shared.getDeviceLinks(members.map { it.address.serialize() }.toSet()).map {
val result = members.flatMap { member -> val result = members.flatMap { member ->
MultiDeviceProtocol.shared.getAllLinkedDevices(member.address.serialize()).map { Address.fromSerialized(it) } MultiDeviceProtocol.shared.getAllLinkedDevices(member.address.serialize()).map { Address.fromSerialized(it) }
}.toMutableSet() }.toMutableSet()
@ -106,7 +106,7 @@ object ClosedGroupsProtocol {
allDevices.remove(userPublicKey) allDevices.remove(userPublicKey)
} }
for (device in allDevices) { for (device in allDevices) {
ApplicationContext.getInstance(context).sendSessionRequest(device) ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(device)
} }
} }
} }

View File

@ -140,7 +140,6 @@ object FriendRequestProtocol {
fun handleFriendRequestAcceptanceIfNeeded(context: Context, publicKey: String, content: SignalServiceContent) { fun handleFriendRequestAcceptanceIfNeeded(context: Context, publicKey: String, content: SignalServiceContent) {
// If we get an envelope that isn't a friend request, then we can infer that we had to use // If we get an envelope that isn't a friend request, then we can infer that we had to use
// Signal cipher decryption and thus that we have a session with the other person. // Signal cipher decryption and thus that we have a session with the other person.
if (content.isFriendRequest) { return }
val recipient = recipient(context, publicKey) val recipient = recipient(context, publicKey)
// Friend requests don't apply to groups // Friend requests don't apply to groups
if (recipient.isGroupRecipient) { return } if (recipient.isGroupRecipient) { return }
@ -200,7 +199,6 @@ object FriendRequestProtocol {
@JvmStatic @JvmStatic
fun handleFriendRequestMessageIfNeeded(context: Context, publicKey: String, content: SignalServiceContent) { fun handleFriendRequestMessageIfNeeded(context: Context, publicKey: String, content: SignalServiceContent) {
if (!content.isFriendRequest) { return }
val recipient = recipient(context, publicKey) val recipient = recipient(context, publicKey)
// Friend requests don't apply to groups // Friend requests don't apply to groups
if (recipient.isGroupRecipient) { return } if (recipient.isGroupRecipient) { return }
@ -241,7 +239,7 @@ object FriendRequestProtocol {
@JvmStatic @JvmStatic
fun isFriendRequestFromBeforeRestoration(context: Context, content: SignalServiceContent): Boolean { fun isFriendRequestFromBeforeRestoration(context: Context, content: SignalServiceContent): Boolean {
return content.isFriendRequest && content.timestamp < TextSecurePreferences.getRestorationTime(context) return content.timestamp < TextSecurePreferences.getRestorationTime(context)
} }
@JvmStatic @JvmStatic

View File

@ -3,30 +3,30 @@ package org.thoughtcrime.securesms.loki.protocol
import android.content.Context import android.content.Context
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.whispersystems.libsignal.loki.LokiSessionResetProtocol import org.whispersystems.libsignal.loki.SessionResetProtocol
import org.whispersystems.libsignal.loki.LokiSessionResetStatus import org.whispersystems.libsignal.loki.SessionResetStatus
import org.whispersystems.libsignal.protocol.PreKeySignalMessage import org.whispersystems.libsignal.protocol.PreKeySignalMessage
class LokiSessionResetImplementation(private val context: Context) : LokiSessionResetProtocol { class LokiSessionResetImplementation(private val context: Context) : SessionResetProtocol {
override fun getSessionResetStatus(hexEncodedPublicKey: String): LokiSessionResetStatus { override fun getSessionResetStatus(publicKey: String): SessionResetStatus {
return DatabaseFactory.getLokiThreadDatabase(context).getSessionResetStatus(hexEncodedPublicKey) return DatabaseFactory.getLokiThreadDatabase(context).getSessionResetStatus(publicKey)
} }
override fun setSessionResetStatus(hexEncodedPublicKey: String, sessionResetStatus: LokiSessionResetStatus) { override fun setSessionResetStatus(publicKey: String, sessionResetStatus: SessionResetStatus) {
return DatabaseFactory.getLokiThreadDatabase(context).setSessionResetStatus(hexEncodedPublicKey, sessionResetStatus) return DatabaseFactory.getLokiThreadDatabase(context).setSessionResetStatus(publicKey, sessionResetStatus)
} }
override fun onNewSessionAdopted(hexEncodedPublicKey: String, oldSessionResetStatus: LokiSessionResetStatus) { override fun onNewSessionAdopted(publicKey: String, oldSessionResetStatus: SessionResetStatus) {
if (oldSessionResetStatus == LokiSessionResetStatus.IN_PROGRESS) { if (oldSessionResetStatus == SessionResetStatus.IN_PROGRESS) {
val ephemeralMessage = EphemeralMessage.create(hexEncodedPublicKey) val ephemeralMessage = EphemeralMessage.create(publicKey)
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage)) ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))
} }
// TODO: Show session reset succeed message // TODO: Show session reset succeed message
} }
override fun validatePreKeySignalMessage(sender: String, message: PreKeySignalMessage) { override fun validatePreKeySignalMessage(publicKey: String, message: PreKeySignalMessage) {
val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getPreKeyRecord(sender) ?: return val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getPreKeyRecord(publicKey) ?: return
// TODO: Checking that the pre key record isn't null is causing issues when it shouldn't // TODO: Checking that the pre key record isn't null is causing issues when it shouldn't
check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." } check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." }
} }

View File

@ -12,7 +12,7 @@ import org.thoughtcrime.securesms.logging.Log
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.SignalServiceMessageSender import org.whispersystems.signalservice.api.SignalServiceMessageSender
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@ -43,7 +43,7 @@ class MultiDeviceOpenGroupUpdateJob private constructor(parameters: Parameters)
return return
} }
// Gather open groups // Gather open groups
val openGroups = mutableListOf<LokiPublicChat>() val openGroups = mutableListOf<PublicChat>()
DatabaseFactory.getGroupDatabase(context).groups.use { reader -> DatabaseFactory.getGroupDatabase(context).groups.use { reader ->
while (true) { while (true) {
val record = reader.next ?: return@use val record = reader.next ?: return@use

View File

@ -18,7 +18,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.api.messages.SignalServiceContent
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLinkingSession import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLinkingSession
@ -80,7 +80,7 @@ object MultiDeviceProtocol {
} }
} }
val publicKey = recipient.address.serialize() val publicKey = recipient.address.serialize()
LokiFileServerAPI.shared.getDeviceLinks(publicKey).success { FileServerAPI.shared.getDeviceLinks(publicKey).success {
val devices = MultiDeviceProtocol.shared.getAllLinkedDevices(publicKey) val devices = MultiDeviceProtocol.shared.getAllLinkedDevices(publicKey)
val jobs = devices.map { sendMessagePushToDevice(context, recipient(context, it), messageID, messageType, isEndSession) } val jobs = devices.map { sendMessagePushToDevice(context, recipient(context, it), messageID, messageType, isEndSession) }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@ -105,7 +105,7 @@ object MultiDeviceProtocol {
// A request should include a pre key bundle. An authorization should be a normal message. // A request should include a pre key bundle. An authorization should be a normal message.
if (deviceLink.type == DeviceLink.Type.REQUEST) { if (deviceLink.type == DeviceLink.Type.REQUEST) {
val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.number) val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.number)
message.asFriendRequest(true).withPreKeyBundle(preKeyBundle) message.withPreKeyBundle(preKeyBundle)
} else { } else {
// Include the user's profile key so that the slave device can get the user's profile picture // Include the user's profile key so that the slave device can get the user's profile picture
message.withProfileKey(ProfileKeyUtil.getProfileKey(context)) message.withProfileKey(ProfileKeyUtil.getProfileKey(context))
@ -135,7 +135,7 @@ object MultiDeviceProtocol {
return Promise.ofFail(Exception("Failed to sign device link.")) return Promise.ofFail(Exception("Failed to sign device link."))
} }
return retryIfNeeded(8) { return retryIfNeeded(8) {
sendDeviceLinkMessage(context, deviceLink.slaveHexEncodedPublicKey, signedDeviceLink) sendDeviceLinkMessage(context, deviceLink.slavePublicKey, signedDeviceLink)
} }
} }
@ -144,7 +144,7 @@ object MultiDeviceProtocol {
val userPublicKey = TextSecurePreferences.getLocalNumber(context) val userPublicKey = TextSecurePreferences.getLocalNumber(context)
if (deviceLink.type == DeviceLink.Type.REQUEST) { if (deviceLink.type == DeviceLink.Type.REQUEST) {
handleDeviceLinkRequestMessage(context, deviceLink, content) handleDeviceLinkRequestMessage(context, deviceLink, content)
} else if (deviceLink.slaveHexEncodedPublicKey == userPublicKey) { } else if (deviceLink.slavePublicKey == userPublicKey) {
handleDeviceLinkAuthorizedMessage(context, deviceLink, content) handleDeviceLinkAuthorizedMessage(context, deviceLink, content)
} }
} }
@ -158,10 +158,10 @@ object MultiDeviceProtocol {
} else if (isRequest && TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null) { } else if (isRequest && TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null) {
Log.d("Loki", "Ignoring unexpected device link message (the device is a slave device).") Log.d("Loki", "Ignoring unexpected device link message (the device is a slave device).")
return false return false
} else if (isRequest && deviceLink.masterHexEncodedPublicKey != userPublicKey) { } else if (isRequest && deviceLink.masterPublicKey != userPublicKey) {
Log.d("Loki", "Ignoring device linking message addressed to another user.") Log.d("Loki", "Ignoring device linking message addressed to another user.")
return false return false
} else if (isRequest && deviceLink.slaveHexEncodedPublicKey == userPublicKey) { } else if (isRequest && deviceLink.slavePublicKey == userPublicKey) {
Log.d("Loki", "Ignoring device linking request message from self.") Log.d("Loki", "Ignoring device linking request message from self.")
return false return false
} }
@ -193,9 +193,9 @@ object MultiDeviceProtocol {
val userPublicKey = TextSecurePreferences.getLocalNumber(context) val userPublicKey = TextSecurePreferences.getLocalNumber(context)
DatabaseFactory.getLokiAPIDatabase(context).clearDeviceLinks(userPublicKey) DatabaseFactory.getLokiAPIDatabase(context).clearDeviceLinks(userPublicKey)
DatabaseFactory.getLokiAPIDatabase(context).addDeviceLink(deviceLink) DatabaseFactory.getLokiAPIDatabase(context).addDeviceLink(deviceLink)
TextSecurePreferences.setMasterHexEncodedPublicKey(context, deviceLink.masterHexEncodedPublicKey) TextSecurePreferences.setMasterHexEncodedPublicKey(context, deviceLink.masterPublicKey)
TextSecurePreferences.setMultiDevice(context, true) TextSecurePreferences.setMultiDevice(context, true)
LokiFileServerAPI.shared.addDeviceLink(deviceLink) FileServerAPI.shared.addDeviceLink(deviceLink)
org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content) org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content)
org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol.duplicate_handleProfileKey(context, content) org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol.duplicate_handleProfileKey(context, content)
} }
@ -210,19 +210,19 @@ object MultiDeviceProtocol {
// Ignore the request if we don't know about the device link in question // Ignore the request if we don't know about the device link in question
val masterDeviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(masterDevicePublicKey) val masterDeviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(masterDevicePublicKey)
if (masterDeviceLinks.none { if (masterDeviceLinks.none {
it.masterHexEncodedPublicKey == masterDevicePublicKey && it.slaveHexEncodedPublicKey == userPublicKey it.masterPublicKey == masterDevicePublicKey && it.slavePublicKey == userPublicKey
}) { }) {
return return
} }
LokiFileServerAPI.shared.getDeviceLinks(userPublicKey, true).success { slaveDeviceLinks -> FileServerAPI.shared.getDeviceLinks(userPublicKey, true).success { slaveDeviceLinks ->
// Check that the device link IS present on the file server. // Check that the device link IS present on the file server.
// Note that the device link as seen from the master device's perspective has been deleted at this point, but the // Note that the device link as seen from the master device's perspective has been deleted at this point, but the
// device link as seen from the slave perspective hasn't. // device link as seen from the slave perspective hasn't.
if (slaveDeviceLinks.any { if (slaveDeviceLinks.any {
it.masterHexEncodedPublicKey == masterDevicePublicKey && it.slaveHexEncodedPublicKey == userPublicKey it.masterPublicKey == masterDevicePublicKey && it.slavePublicKey == userPublicKey
}) { }) {
for (slaveDeviceLink in slaveDeviceLinks) { // In theory there should only be one for (slaveDeviceLink in slaveDeviceLinks) { // In theory there should only be one
LokiFileServerAPI.shared.removeDeviceLink(slaveDeviceLink) // Attempt to clean up on the file server FileServerAPI.shared.removeDeviceLink(slaveDeviceLink) // Attempt to clean up on the file server
} }
TextSecurePreferences.setWasUnlinked(context, true) TextSecurePreferences.setWasUnlinked(context, true)
ApplicationContext.getInstance(context).clearData() ApplicationContext.getInstance(context).clearData()

View File

@ -44,12 +44,11 @@ class PushEphemeralMessageSendJob private constructor(parameters: Parameters, pr
// Attach a pre key bundle if needed // Attach a pre key bundle if needed
if (message.get("friendRequest", false)) { if (message.get("friendRequest", false)) {
val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(recipient) val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(recipient)
dataMessage.withPreKeyBundle(bundle).asFriendRequest(true) dataMessage.withPreKeyBundle(bundle)
} }
// Set flags if needed (these are mutually exclusive) // Set flags if needed (these are mutually exclusive)
when { when {
message.get("unpairingRequest", false) -> dataMessage.asUnlinkingRequest(true) message.get("unpairingRequest", false) -> dataMessage.asDeviceUnlinkingRequest(true)
message.get("sessionRestore", false) -> dataMessage.asSessionRestorationRequest(true)
message.get("sessionRequest", false) -> dataMessage.asSessionRequest(true) message.get("sessionRequest", false) -> dataMessage.asSessionRequest(true)
} }
// Send the message // Send the message

View File

@ -56,7 +56,7 @@ class PushNullMessageSendJob private constructor(parameters: Parameters, private
try { try {
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
Date().time, serializedContentMessage, false, ttl, false, Date().time, serializedContentMessage, false, ttl, false,
false, false, false, false) false, false, false)
} catch (e: Exception) { } catch (e: Exception) {
Log.d("Loki", "Failed to send null message to: $publicKey due to error: $e.") Log.d("Loki", "Failed to send null message to: $publicKey due to error: $e.")
throw e throw e

View File

@ -13,7 +13,7 @@ import org.thoughtcrime.securesms.loki.utilities.recipient
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.sms.OutgoingTextMessage import org.thoughtcrime.securesms.sms.OutgoingTextMessage
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.libsignal.loki.LokiSessionResetStatus import org.whispersystems.libsignal.loki.SessionResetStatus
import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.api.messages.SignalServiceContent
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
import java.util.* import java.util.*
@ -93,7 +93,7 @@ object SessionManagementProtocol {
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context) val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
Log.d("Loki", "Received a session reset request from: ${content.sender}; archiving the session.") Log.d("Loki", "Received a session reset request from: ${content.sender}; archiving the session.")
sessionStore.archiveAllSessions(content.sender) sessionStore.archiveAllSessions(content.sender)
lokiThreadDB.setSessionResetStatus(content.sender, LokiSessionResetStatus.REQUEST_RECEIVED) lokiThreadDB.setSessionResetStatus(content.sender, SessionResetStatus.REQUEST_RECEIVED)
Log.d("Loki", "Sending an ephemeral message back to: ${content.sender}.") Log.d("Loki", "Sending an ephemeral message back to: ${content.sender}.")
val ephemeralMessage = EphemeralMessage.create(content.sender) val ephemeralMessage = EphemeralMessage.create(content.sender)
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage)) ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))

View File

@ -22,7 +22,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup
import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsInputStream import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsInputStream
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
@ -151,7 +151,7 @@ object SyncMessagesProtocol {
} }
@JvmStatic @JvmStatic
fun handleOpenGroupSyncMessage(context: Context, content: SignalServiceContent, openGroups: List<LokiPublicChat>) { fun handleOpenGroupSyncMessage(context: Context, content: SignalServiceContent, openGroups: List<PublicChat>) {
val userPublicKey = TextSecurePreferences.getLocalNumber(context) val userPublicKey = TextSecurePreferences.getLocalNumber(context)
val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey)
if (!allUserDevices.contains(content.sender)) { return } if (!allUserDevices.contains(content.sender)) { return }

View File

@ -66,7 +66,7 @@ class LokiRSSFeedPoller(private val context: Context, private val feed: LokiRSSF
val id = feed.id.toByteArray() val id = feed.id.toByteArray()
val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, SignalServiceGroup.GroupType.RSS_FEED, null, null, null, null) val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, SignalServiceGroup.GroupType.RSS_FEED, null, null, null, null)
val x2 = SignalServiceDataMessage(timestamp, x1, null, body) val x2 = SignalServiceDataMessage(timestamp, x1, null, body)
val x3 = SignalServiceContent(x2, "Loki", SignalServiceAddress.DEFAULT_DEVICE_ID, timestamp, false, false, false, false) val x3 = SignalServiceContent(x2, "Loki", SignalServiceAddress.DEFAULT_DEVICE_ID, timestamp, false, false, false)
PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.absent()) PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.absent())
} }
}.fail { exception -> }.fail { exception ->

View File

@ -47,7 +47,7 @@ object ContactUtilities {
var isSlave = false var isSlave = false
if (!recipient.isGroupRecipient) { if (!recipient.isGroupRecipient) {
val deviceLinks = lokiAPIDatabase.getDeviceLinks(publicKey) val deviceLinks = lokiAPIDatabase.getDeviceLinks(publicKey)
isSlave = deviceLinks.find { it.slaveHexEncodedPublicKey == publicKey } != null isSlave = deviceLinks.find { it.slavePublicKey == publicKey } != null
} }
result.add(Contact(recipient, isFriend, isSlave, isUserDevice)) result.add(Contact(recipient, isFriend, isSlave, isUserDevice))
} }

View File

@ -8,20 +8,20 @@ import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.groups.GroupManager import org.thoughtcrime.securesms.groups.GroupManager
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
object OpenGroupUtilities { object OpenGroupUtilities {
@JvmStatic fun addGroup(context: Context, url: String, channel: Long): Promise<LokiPublicChat, Exception> { @JvmStatic fun addGroup(context: Context, url: String, channel: Long): Promise<PublicChat, Exception> {
// Check for an existing group // Check for an existing group
val groupID = LokiPublicChat.getId(channel, url) val groupID = PublicChat.getId(channel, url)
val threadID = GroupManager.getOpenGroupThreadID(groupID, context) val threadID = GroupManager.getOpenGroupThreadID(groupID, context)
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
if (openGroup != null) { return Promise.of(openGroup) } if (openGroup != null) { return Promise.of(openGroup) }
// Add the new group // Add the new group
val application = ApplicationContext.getInstance(context) val application = ApplicationContext.getInstance(context)
val displayName = TextSecurePreferences.getProfileName(context) val displayName = TextSecurePreferences.getProfileName(context)
val lokiPublicChatAPI = application.lokiPublicChatAPI ?: throw Error("LokiPublicChatAPI is not initialized.") val lokiPublicChatAPI = application.publicChatAPI ?: throw Error("LokiPublicChatAPI is not initialized.")
return application.lokiPublicChatManager.addChat(url, channel).then { group -> return application.lokiPublicChatManager.addChat(url, channel).then { group ->
DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url) DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url)
DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url) DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url)

View File

@ -9,7 +9,7 @@ import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_mention_candidate.view.* import kotlinx.android.synthetic.main.view_mention_candidate.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI
import org.whispersystems.signalservice.loki.protocol.mentions.Mention import org.whispersystems.signalservice.loki.protocol.mentions.Mention
class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) { class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) {
@ -31,13 +31,13 @@ class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr:
private fun update() { private fun update() {
displayNameTextView.text = mentionCandidate.displayName displayNameTextView.text = mentionCandidate.displayName
profilePictureView.hexEncodedPublicKey = mentionCandidate.hexEncodedPublicKey profilePictureView.hexEncodedPublicKey = mentionCandidate.publicKey
profilePictureView.additionalHexEncodedPublicKey = null profilePictureView.additionalHexEncodedPublicKey = null
profilePictureView.isRSSFeed = false profilePictureView.isRSSFeed = false
profilePictureView.glide = glide!! profilePictureView.glide = glide!!
profilePictureView.update() profilePictureView.update()
if (publicChatServer != null && publicChatChannel != null) { if (publicChatServer != null && publicChatChannel != null) {
val isUserModerator = LokiPublicChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, publicChatChannel!!, publicChatServer!!) val isUserModerator = PublicChatAPI.isUserModerator(mentionCandidate.publicKey, publicChatChannel!!, publicChatServer!!)
moderatorIconImageView.visibility = if (isUserModerator) View.VISIBLE else View.GONE moderatorIconImageView.visibility = if (isUserModerator) View.VISIBLE else View.GONE
} else { } else {
moderatorIconImageView.visibility = View.GONE moderatorIconImageView.visibility = View.GONE

View File

@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.mms;
import android.content.Context; import android.content.Context;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI;
public class PushMediaConstraints extends MediaConstraints { public class PushMediaConstraints extends MediaConstraints {
@ -22,26 +22,26 @@ public class PushMediaConstraints extends MediaConstraints {
@Override @Override
public int getImageMaxSize(Context context) { public int getImageMaxSize(Context context) {
return LokiFileServerAPI.Companion.getMaxFileSize(); return FileServerAPI.Companion.getMaxFileSize();
} }
@Override @Override
public int getGifMaxSize(Context context) { public int getGifMaxSize(Context context) {
return LokiFileServerAPI.Companion.getMaxFileSize(); return FileServerAPI.Companion.getMaxFileSize();
} }
@Override @Override
public int getVideoMaxSize(Context context) { public int getVideoMaxSize(Context context) {
return LokiFileServerAPI.Companion.getMaxFileSize(); return FileServerAPI.Companion.getMaxFileSize();
} }
@Override @Override
public int getAudioMaxSize(Context context) { public int getAudioMaxSize(Context context) {
return LokiFileServerAPI.Companion.getMaxFileSize(); return FileServerAPI.Companion.getMaxFileSize();
} }
@Override @Override
public int getDocumentMaxSize(Context context) { public int getDocumentMaxSize(Context context) {
return LokiFileServerAPI.Companion.getMaxFileSize(); return FileServerAPI.Companion.getMaxFileSize();
} }
} }

View File

@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.push;
import android.content.Context; import android.content.Context;
import org.thoughtcrime.securesms.crypto.SecurityEvent; import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
@ -18,19 +17,4 @@ public class MessageSenderEventListener implements SignalServiceMessageSender.Ev
public void onSecurityEvent(SignalServiceAddress textSecureAddress) { public void onSecurityEvent(SignalServiceAddress textSecureAddress) {
SecurityEvent.broadcastSecurityUpdateEvent(context); SecurityEvent.broadcastSecurityUpdateEvent(context);
} }
@Override
public void onFriendRequestSending(long messageID, long threadID) {
FriendRequestProtocol.setFriendRequestStatusToSendingIfNeeded(context, messageID, threadID);
}
@Override
public void onFriendRequestSent(long messageID, long threadID) {
FriendRequestProtocol.setFriendRequestStatusToSentIfNeeded(context, messageID, threadID);
}
@Override
public void onFriendRequestSendingFailed(long messageID, long threadID) {
FriendRequestProtocol.setFriendRequestStatusToFailedIfNeeded(context, messageID, threadID);
}
} }