Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb523985ed | ||
|
|
7ffe6f053c | ||
|
|
ea77191b70 | ||
|
|
e0b882d4d2 | ||
|
|
10a74d6d08 | ||
|
|
f092e85b62 | ||
|
|
8aa0f15740 | ||
|
|
18d4d4de24 | ||
|
|
b3c42dee7e | ||
|
|
5836f35291 | ||
|
|
dbd8a4083c | ||
|
|
0fd52ad1fe | ||
|
|
8031c788d9 | ||
|
|
10a29db93d | ||
|
|
8f3f2e6921 | ||
|
|
65c262acef | ||
|
|
d586893402 | ||
|
|
ba62e018db | ||
|
|
57c17e705f | ||
|
|
2e253fb6a8 | ||
|
|
0c32001fe4 | ||
|
|
48f6c2c526 | ||
|
|
39fd1e8f46 | ||
|
|
810abe0275 | ||
|
|
62816ee51a | ||
|
|
a0599c1639 | ||
|
|
8a4a1b385d |
@@ -2,8 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.thoughtcrime.securesms"
|
||||
android:versionCode="90"
|
||||
android:versionName="2.4.2">
|
||||
android:versionCode="92"
|
||||
android:versionName="2.5.1">
|
||||
|
||||
<permission android:name="org.thoughtcrime.securesms.ACCESS_SECRETS"
|
||||
android:label="Access to TextSecure Secrets"
|
||||
@@ -94,6 +94,17 @@
|
||||
android:theme="@style/TextSecure.Light.Dialog"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".DeviceProvisioningActivity"
|
||||
android:theme="@style/TextSecure.DialogActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="tsdevice"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".MmsPreferencesActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
@@ -212,6 +223,8 @@
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name="com.soundcloud.android.crop.CropImageActivity" />
|
||||
|
||||
<service android:enabled="true" android:name=".service.ApplicationMigrationService"/>
|
||||
<service android:enabled="true" android:name=".service.KeyCachingService"/>
|
||||
<service android:enabled="true" android:name=".service.RegistrationService"/>
|
||||
|
||||
31036
artwork/icon.ai
|
Before Width: | Height: | Size: 496 KiB |
|
Before Width: | Height: | Size: 93 KiB |
BIN
artwork/logo-512.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
47
build.gradle
@@ -5,13 +5,12 @@ buildscript {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.0.0'
|
||||
classpath 'com.android.tools.build:gradle:1.0.1'
|
||||
classpath files('libs/gradle-witness.jar')
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply from: 'strip_play_services.gradle'
|
||||
apply plugin: 'witness'
|
||||
|
||||
repositories {
|
||||
@@ -30,30 +29,34 @@ repositories {
|
||||
maven {
|
||||
url "https://raw.github.com/whispersystems/maven/master/shortcutbadger/releases/"
|
||||
}
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'me.leolin:ShortcutBadger:1.0.2-WS2'
|
||||
compile 'se.emilsjolander:stickylistheaders:2.2.0'
|
||||
compile 'com.google.android.gms:play-services:6.1.71'
|
||||
compile 'com.google.android.gms:play-services-base:6.5.87'
|
||||
compile 'com.astuetz:pagerslidingtabstrip:1.0.1'
|
||||
compile 'org.w3c:smil:1.0.0'
|
||||
compile 'org.apache.httpcomponents:httpclient-android:4.3.5'
|
||||
compile 'com.github.chrisbanes.photoview:library:1.2.3'
|
||||
compile 'com.android.support:appcompat-v7:20.0.0'
|
||||
compile 'com.madgag.spongycastle:prov:1.51.0.0'
|
||||
compile 'com.makeramen:roundedimageview:1.5.0'
|
||||
compile 'com.afollestad:material-dialogs:0.6.1.5'
|
||||
compile 'com.soundcloud.android:android-crop:0.9.10@aar'
|
||||
compile 'com.android.support:appcompat-v7:21.0.3'
|
||||
compile 'com.melnykov:floatingactionbutton:1.1.0'
|
||||
compile 'com.google.zxing:android-integration:3.1.0'
|
||||
compile ('com.android.support:support-v4-preferencefragment:1.0.0@aar'){
|
||||
exclude module: 'support-v4'
|
||||
}
|
||||
compile 'com.squareup.dagger:dagger:1.2.2'
|
||||
compile ("com.doomonafireball.betterpickers:library:1.5.2") {
|
||||
compile ("com.doomonafireball.betterpickers:library:1.5.3") {
|
||||
exclude group: 'com.android.support', module: 'support-v4'
|
||||
}
|
||||
provided 'com.squareup.dagger:dagger-compiler:1.2.2'
|
||||
|
||||
compile 'org.whispersystems:jobmanager:0.10.0'
|
||||
compile 'org.whispersystems:libpastelog:1.0.2'
|
||||
compile 'org.whispersystems:libpastelog:1.0.4'
|
||||
|
||||
androidTestCompile 'com.squareup:fest-android:1.0.8'
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker:1.1'
|
||||
@@ -66,28 +69,31 @@ dependencyVerification {
|
||||
verify = [
|
||||
'me.leolin:ShortcutBadger:027977c718035e5997035e04e05152d6c72d94df645e8b7099a274ada722bd14',
|
||||
'se.emilsjolander:stickylistheaders:89146b46c96fea0e40200474a2625cda10fe94891e4128f53cdb42375091b9b6',
|
||||
'com.google.android.gms:play-services:32e7d1834a1cf8fa4b17e8d359db580c286e26c1eefbf84fdb9996eac8d74919',
|
||||
'com.google.android.gms:play-services-base:832cb6b3130e871db6a412c4ab585656dbcc5e7948101f190186757785703f75',
|
||||
'com.astuetz:pagerslidingtabstrip:f1641396732c7132a7abb837e482e5ee2b0ebb8d10813fc52bbaec2c15c184c2',
|
||||
'org.w3c:smil:085dc40f2bb249651578bfa07499fd08b16ad0886dbe2c4078586a408da62f9b',
|
||||
'org.apache.httpcomponents:httpclient-android:6f56466a9bd0d42934b90bfbfe9977a8b654c058bf44a12bdc2877c4e1f033f1',
|
||||
'com.github.chrisbanes.photoview:library:8b5344e206f125e7ba9d684008f36c4992d03853c57e5814125f88496126e3cc',
|
||||
'com.android.support:appcompat-v7:736f576ab0b68d27bdf18b1e7931566e6d8254b73965175313e87f8866b91547',
|
||||
'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a',
|
||||
'com.makeramen:roundedimageview:7dda2e78c406760e5c356ccce59b0df46b5b171cf18abb891998594405021548',
|
||||
'com.afollestad:material-dialogs:ccb013e6572c86cfcca433855cf0dbfbff9b5e7bb9d1f504b761a6bc6f467b60',
|
||||
'com.soundcloud.android:android-crop:ffd4b973cf6e97f7d64118a0dc088df50e9066fd5634fe6911dd0c0c5d346177',
|
||||
'com.android.support:appcompat-v7:5dbeb5316d0a6027d646ae552804c3baa5e3bd53f7f33db50904d51505c8a0e5',
|
||||
'com.melnykov:floatingactionbutton:0679ad9f7d61eb7aeab91e8dc56358cdedd5b1c1b9c48464499ffa05c40d3985',
|
||||
'com.google.zxing:android-integration:89e56aadf1164bd71e57949163c53abf90af368b51669c0d4a47a163335f95c4',
|
||||
'com.android.support:support-v4-preferencefragment:5470f5872514a6226fa1fc6f4e000991f38805691c534cf0bd2778911fc773ad',
|
||||
'com.squareup.dagger:dagger:789aca24537022e49f91fc6444078d9de8f1dd99e1bfb090f18491b186967883',
|
||||
'com.doomonafireball.betterpickers:library:1c2dd66b0f4555e9e68427b28eed8826b7cd2e7074b8c038c88836a6aa9abe64',
|
||||
'org.whispersystems:libpastelog:9798b3c93a91082c2c68542ce5b5c182e18556aebdcb7c8cebbd89eb48ac4047',
|
||||
'com.doomonafireball.betterpickers:library:132ecd685c95a99e7377c4e27bfadbb2d7ed0bea995944060cd62d4369fdaf3d',
|
||||
'org.whispersystems:jobmanager:01f35586c43aa3806f1c18d3d6a5a972def98103ba1a5a9ca3eec08d15f974b7',
|
||||
'com.android.support:support-v4:81f2b1c2c94efd5a4ec7fcd97b6cdcd00e87a933905c5c86103c7319eb024572',
|
||||
'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f',
|
||||
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'org.whispersystems:libpastelog:3ccf00fe1597eb8ca1e5de99b17fc225387a1b80b5bbc00ec1bc4d4f3ea9cdde',
|
||||
'com.android.support:recyclerview-v7:ab2390d688601b65e2f3a0718b3d25487e61546c4e20f81eb0b033f30ca15b31',
|
||||
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
|
||||
'org.whispersystems:gson:08f4f7498455d1539c9233e5aac18e9b1805815ef29221572996508eb512fe51',
|
||||
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',
|
||||
'com.googlecode.libphonenumber:libphonenumber:eba17eae81dd622ea89a00a3a8c025b2f25d342e0d9644c5b62e16f15687c3ab',
|
||||
'org.whispersystems:gson:08f4f7498455d1539c9233e5aac18e9b1805815ef29221572996508eb512fe51',
|
||||
'org.whispersystems:axolotl-android:7617256d05aaecd7b5475cd55e42773d7079167a22ca48512bcb0f84f8473cc9',
|
||||
'com.android.support:support-annotations:1aa96ef0cc4a445bfc2f93ccf762305bc57fa107b12afe9d11f3863ae8a11036',
|
||||
'com.android.support:support-v4:703572d3015a088cc5604b7e38885af3d307c829d0c5ceaf8654ff41c71cd160',
|
||||
'com.android.support:support-annotations:fdee2354787ef66b268e75958de3f7f6c4f8f325510a6dac9f49c929f83a63de',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -113,10 +119,12 @@ android {
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
minifyEnabled false
|
||||
minifyEnabled true
|
||||
proguardFiles 'proguard.cfg'
|
||||
}
|
||||
release {
|
||||
minifyEnabled false
|
||||
minifyEnabled true
|
||||
proguardFiles 'proguard.cfg'
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
@@ -139,7 +147,6 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
@@ -42,12 +42,18 @@ message PushMessageContent {
|
||||
optional AttachmentPointer avatar = 5;
|
||||
}
|
||||
|
||||
message SyncMessageContext {
|
||||
optional string destination = 1;
|
||||
optional uint64 timestamp = 2;
|
||||
}
|
||||
|
||||
enum Flags {
|
||||
END_SESSION = 1;
|
||||
}
|
||||
|
||||
optional string body = 1;
|
||||
repeated AttachmentPointer attachments = 2;
|
||||
optional GroupContext group = 3;
|
||||
optional uint32 flags = 4;
|
||||
}
|
||||
optional string body = 1;
|
||||
repeated AttachmentPointer attachments = 2;
|
||||
optional GroupContext group = 3;
|
||||
optional uint32 flags = 4;
|
||||
optional SyncMessageContext sync = 5;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
|
||||
all:
|
||||
protoc --java_out=../src/main/java/ IncomingPushMessageSignal.proto
|
||||
protoc --java_out=../src/main/java/ IncomingPushMessageSignal.proto Provisioning.proto
|
||||
|
||||
16
libtextsecure/protobuf/Provisioning.proto
Normal file
@@ -0,0 +1,16 @@
|
||||
package textsecure;
|
||||
|
||||
option java_package = "org.whispersystems.textsecure.internal.push";
|
||||
option java_outer_classname = "ProvisioningProtos";
|
||||
|
||||
message ProvisionEnvelope {
|
||||
optional bytes publicKey = 1;
|
||||
optional bytes body = 2; // Encrypted ProvisionMessage
|
||||
}
|
||||
|
||||
message ProvisionMessage {
|
||||
optional bytes identityKeyPublic = 1;
|
||||
optional bytes identityKeyPrivate = 2;
|
||||
optional string number = 3;
|
||||
optional string provisioningCode = 4;
|
||||
}
|
||||
@@ -16,29 +16,37 @@
|
||||
*/
|
||||
package org.whispersystems.textsecure.api;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.whispersystems.libaxolotl.IdentityKey;
|
||||
import org.whispersystems.libaxolotl.IdentityKeyPair;
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
|
||||
import org.whispersystems.libaxolotl.state.PreKeyRecord;
|
||||
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
import org.whispersystems.textsecure.api.push.ContactTokenDetails;
|
||||
import org.whispersystems.textsecure.api.push.TrustStore;
|
||||
import org.whispersystems.textsecure.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||
import org.whispersystems.textsecure.api.push.exceptions.PushNetworkException;
|
||||
import org.whispersystems.textsecure.internal.push.PushServiceSocket;
|
||||
import org.whispersystems.textsecure.api.push.SignedPreKeyEntity;
|
||||
import org.whispersystems.textsecure.api.push.TrustStore;
|
||||
import org.whispersystems.textsecure.internal.crypto.ProvisioningCipher;
|
||||
import org.whispersystems.textsecure.internal.push.PushServiceSocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage;
|
||||
|
||||
public class TextSecureAccountManager {
|
||||
|
||||
private final PushServiceSocket pushServiceSocket;
|
||||
private final String user;
|
||||
|
||||
public TextSecureAccountManager(String url, TrustStore trustStore,
|
||||
String user, String password)
|
||||
{
|
||||
this.pushServiceSocket = new PushServiceSocket(url, trustStore, user, password);
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public void setGcmId(Optional<String> gcmRegistrationId) throws IOException {
|
||||
@@ -94,4 +102,26 @@ public class TextSecureAccountManager {
|
||||
return this.pushServiceSocket.retrieveDirectory(contactTokens);
|
||||
}
|
||||
|
||||
public String getNewDeviceVerificationCode() throws IOException {
|
||||
return this.pushServiceSocket.getNewDeviceVerificationCode();
|
||||
}
|
||||
|
||||
public void addDevice(String deviceIdentifier,
|
||||
ECPublicKey deviceKey,
|
||||
IdentityKeyPair identityKeyPair,
|
||||
String code)
|
||||
throws InvalidKeyException, IOException
|
||||
{
|
||||
ProvisioningCipher cipher = new ProvisioningCipher(deviceKey);
|
||||
ProvisionMessage message = ProvisionMessage.newBuilder()
|
||||
.setIdentityKeyPublic(ByteString.copyFrom(identityKeyPair.getPublicKey().serialize()))
|
||||
.setIdentityKeyPrivate(ByteString.copyFrom(identityKeyPair.getPrivateKey().serialize()))
|
||||
.setNumber(user)
|
||||
.setProvisioningCode(code)
|
||||
.build();
|
||||
|
||||
byte[] ciphertext = cipher.encrypt(message);
|
||||
this.pushServiceSocket.sendProvisioningMessage(deviceIdentifier, ciphertext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.whispersystems.textsecure.api;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.libaxolotl.SessionBuilder;
|
||||
@@ -40,6 +41,7 @@ import org.whispersystems.textsecure.internal.push.OutgoingPushMessageList;
|
||||
import org.whispersystems.textsecure.internal.push.PushAttachmentData;
|
||||
import org.whispersystems.textsecure.internal.push.PushBody;
|
||||
import org.whispersystems.textsecure.internal.push.PushServiceSocket;
|
||||
import org.whispersystems.textsecure.internal.push.SendMessageResponse;
|
||||
import org.whispersystems.textsecure.internal.push.StaleDevices;
|
||||
import org.whispersystems.textsecure.api.push.exceptions.UnregisteredUserException;
|
||||
import org.whispersystems.textsecure.api.push.exceptions.EncapsulatedExceptions;
|
||||
@@ -62,14 +64,17 @@ public class TextSecureMessageSender {
|
||||
|
||||
private final PushServiceSocket socket;
|
||||
private final AxolotlStore store;
|
||||
private final PushAddress syncAddress;
|
||||
private final Optional<EventListener> eventListener;
|
||||
|
||||
public TextSecureMessageSender(String url, TrustStore trustStore,
|
||||
String user, String password, AxolotlStore store,
|
||||
String user, String password,
|
||||
long userId, AxolotlStore store,
|
||||
Optional<EventListener> eventListener)
|
||||
{
|
||||
this.socket = new PushServiceSocket(url, trustStore, user, password);
|
||||
this.store = store;
|
||||
this.syncAddress = new PushAddress(userId, user, null);
|
||||
this.eventListener = eventListener;
|
||||
}
|
||||
|
||||
@@ -80,8 +85,14 @@ public class TextSecureMessageSender {
|
||||
public void sendMessage(PushAddress recipient, TextSecureMessage message)
|
||||
throws UntrustedIdentityException, IOException
|
||||
{
|
||||
byte[] content = createMessageContent(message);
|
||||
sendMessage(recipient, message.getTimestamp(), content);
|
||||
byte[] content = createMessageContent(message);
|
||||
long timestamp = message.getTimestamp();
|
||||
SendMessageResponse response = sendMessage(recipient, timestamp, content);
|
||||
|
||||
if (response != null && response.getNeedsSync()) {
|
||||
byte[] syncMessage = createSyncMessageContent(content, recipient, timestamp);
|
||||
sendMessage(syncAddress, timestamp, syncMessage);
|
||||
}
|
||||
|
||||
if (message.isEndSession()) {
|
||||
store.deleteAllSessions(recipient.getRecipientId());
|
||||
@@ -122,6 +133,20 @@ public class TextSecureMessageSender {
|
||||
return builder.build().toByteArray();
|
||||
}
|
||||
|
||||
private byte[] createSyncMessageContent(byte[] content, PushAddress recipient, long timestamp) {
|
||||
try {
|
||||
PushMessageContent.Builder builder = PushMessageContent.parseFrom(content).toBuilder();
|
||||
builder.setSync(PushMessageContent.SyncMessageContext.newBuilder()
|
||||
.setDestination(recipient.getNumber())
|
||||
.setTimestamp(timestamp)
|
||||
.build());
|
||||
|
||||
return builder.build().toByteArray();
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private GroupContext createGroupContent(TextSecureGroup group) throws IOException {
|
||||
GroupContext.Builder builder = GroupContext.newBuilder();
|
||||
builder.setId(ByteString.copyFrom(group.getGroupId()));
|
||||
@@ -168,15 +193,13 @@ public class TextSecureMessageSender {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(PushAddress recipient, long timestamp, byte[] content)
|
||||
private SendMessageResponse sendMessage(PushAddress recipient, long timestamp, byte[] content)
|
||||
throws UntrustedIdentityException, IOException
|
||||
{
|
||||
for (int i=0;i<3;i++) {
|
||||
try {
|
||||
OutgoingPushMessageList messages = getEncryptedMessages(socket, recipient, timestamp, content);
|
||||
socket.sendMessage(messages);
|
||||
|
||||
return;
|
||||
return socket.sendMessage(messages);
|
||||
} catch (MismatchedDevicesException mde) {
|
||||
Log.w(TAG, mde);
|
||||
handleMismatchedDevices(socket, recipient, mde.getMismatchedDevices());
|
||||
@@ -185,6 +208,8 @@ public class TextSecureMessageSender {
|
||||
handleStaleDevices(recipient, ste.getStaleDevices());
|
||||
}
|
||||
}
|
||||
|
||||
throw new IOException("Failed to resolve conflicts after 3 attempts!");
|
||||
}
|
||||
|
||||
private List<AttachmentPointer> createAttachmentPointers(Optional<List<TextSecureAttachment>> attachments) throws IOException {
|
||||
@@ -230,31 +255,31 @@ public class TextSecureMessageSender {
|
||||
byte[] plaintext)
|
||||
throws IOException, UntrustedIdentityException
|
||||
{
|
||||
PushBody masterBody = getEncryptedMessage(socket, recipient, plaintext);
|
||||
|
||||
List<OutgoingPushMessage> messages = new LinkedList<>();
|
||||
messages.add(new OutgoingPushMessage(recipient, masterBody));
|
||||
|
||||
if (!recipient.equals(syncAddress)) {
|
||||
PushBody masterBody = getEncryptedMessage(socket, recipient, PushAddress.DEFAULT_DEVICE_ID, plaintext);
|
||||
messages.add(new OutgoingPushMessage(recipient, PushAddress.DEFAULT_DEVICE_ID, masterBody));
|
||||
}
|
||||
|
||||
for (int deviceId : store.getSubDeviceSessions(recipient.getRecipientId())) {
|
||||
PushAddress device = new PushAddress(recipient.getRecipientId(), recipient.getNumber(), deviceId, recipient.getRelay());
|
||||
PushBody body = getEncryptedMessage(socket, device, plaintext);
|
||||
|
||||
messages.add(new OutgoingPushMessage(device, body));
|
||||
PushBody body = getEncryptedMessage(socket, recipient, deviceId, plaintext);
|
||||
messages.add(new OutgoingPushMessage(recipient, deviceId, body));
|
||||
}
|
||||
|
||||
return new OutgoingPushMessageList(recipient.getNumber(), timestamp, recipient.getRelay(), messages);
|
||||
}
|
||||
|
||||
private PushBody getEncryptedMessage(PushServiceSocket socket, PushAddress recipient, byte[] plaintext)
|
||||
private PushBody getEncryptedMessage(PushServiceSocket socket, PushAddress recipient, int deviceId, byte[] plaintext)
|
||||
throws IOException, UntrustedIdentityException
|
||||
{
|
||||
if (!store.containsSession(recipient.getRecipientId(), recipient.getDeviceId())) {
|
||||
if (!store.containsSession(recipient.getRecipientId(), deviceId)) {
|
||||
try {
|
||||
List<PreKeyBundle> preKeys = socket.getPreKeys(recipient);
|
||||
List<PreKeyBundle> preKeys = socket.getPreKeys(recipient, deviceId);
|
||||
|
||||
for (PreKeyBundle preKey : preKeys) {
|
||||
try {
|
||||
SessionBuilder sessionBuilder = new SessionBuilder(store, recipient.getRecipientId(), recipient.getDeviceId());
|
||||
SessionBuilder sessionBuilder = new SessionBuilder(store, recipient.getRecipientId(), deviceId);
|
||||
sessionBuilder.process(preKey);
|
||||
} catch (org.whispersystems.libaxolotl.UntrustedIdentityException e) {
|
||||
throw new UntrustedIdentityException("Untrusted identity key!", recipient.getNumber(), preKey.getIdentityKey());
|
||||
@@ -269,7 +294,7 @@ public class TextSecureMessageSender {
|
||||
}
|
||||
}
|
||||
|
||||
TextSecureCipher cipher = new TextSecureCipher(store, recipient.getRecipientId(), recipient.getDeviceId());
|
||||
TextSecureCipher cipher = new TextSecureCipher(store, recipient.getRecipientId(), deviceId);
|
||||
CiphertextMessage message = cipher.encrypt(plaintext);
|
||||
int remoteRegistrationId = cipher.getRemoteRegistrationId();
|
||||
|
||||
@@ -292,12 +317,10 @@ public class TextSecureMessageSender {
|
||||
}
|
||||
|
||||
for (int missingDeviceId : mismatchedDevices.getMissingDevices()) {
|
||||
PushAddress device = new PushAddress(recipient.getRecipientId(), recipient.getNumber(),
|
||||
missingDeviceId, recipient.getRelay());
|
||||
PreKeyBundle preKey = socket.getPreKey(device);
|
||||
PreKeyBundle preKey = socket.getPreKey(recipient, missingDeviceId);
|
||||
|
||||
try {
|
||||
SessionBuilder sessionBuilder = new SessionBuilder(store, device.getRecipientId(), device.getDeviceId());
|
||||
SessionBuilder sessionBuilder = new SessionBuilder(store, recipient.getRecipientId(), missingDeviceId);
|
||||
sessionBuilder.process(preKey);
|
||||
} catch (org.whispersystems.libaxolotl.UntrustedIdentityException e) {
|
||||
throw new UntrustedIdentityException("Untrusted identity key!", recipient.getNumber(), preKey.getIdentityKey());
|
||||
|
||||
@@ -22,13 +22,11 @@ public class PushAddress {
|
||||
|
||||
private final long recipientId;
|
||||
private final String e164number;
|
||||
private final int deviceId;
|
||||
private final String relay;
|
||||
|
||||
public PushAddress(long recipientId, String e164number, int deviceId, String relay) {
|
||||
public PushAddress(long recipientId, String e164number, String relay) {
|
||||
this.recipientId = recipientId;
|
||||
this.e164number = e164number;
|
||||
this.deviceId = deviceId;
|
||||
this.relay = relay;
|
||||
}
|
||||
|
||||
@@ -44,7 +42,29 @@ public class PushAddress {
|
||||
return recipientId;
|
||||
}
|
||||
|
||||
public int getDeviceId() {
|
||||
return deviceId;
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == null || !(other instanceof PushAddress)) return false;
|
||||
|
||||
PushAddress that = (PushAddress)other;
|
||||
|
||||
return this.recipientId == that.recipientId &&
|
||||
equals(this.e164number, that.e164number) &&
|
||||
equals(this.relay, that.relay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = (int)this.recipientId;
|
||||
|
||||
if (this.e164number != null) hashCode ^= this.e164number.hashCode();
|
||||
if (this.relay != null) hashCode ^= this.relay.hashCode();
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
private boolean equals(String one, String two) {
|
||||
if (one == null) return two == null;
|
||||
return one.equals(two);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.whispersystems.textsecure.internal.crypto;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.libaxolotl.ecc.Curve;
|
||||
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
|
||||
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
|
||||
import org.whispersystems.libaxolotl.kdf.HKDFv3;
|
||||
import org.whispersystems.textsecure.internal.util.Util;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope;
|
||||
import static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage;
|
||||
|
||||
|
||||
public class ProvisioningCipher {
|
||||
|
||||
private static final String TAG = ProvisioningCipher.class.getSimpleName();
|
||||
|
||||
private final ECPublicKey theirPublicKey;
|
||||
|
||||
public ProvisioningCipher(ECPublicKey theirPublicKey) {
|
||||
this.theirPublicKey = theirPublicKey;
|
||||
}
|
||||
|
||||
public byte[] encrypt(ProvisionMessage message) throws InvalidKeyException {
|
||||
ECKeyPair ourKeyPair = Curve.generateKeyPair();
|
||||
byte[] sharedSecret = Curve.calculateAgreement(theirPublicKey, ourKeyPair.getPrivateKey());
|
||||
byte[] derivedSecret = new HKDFv3().deriveSecrets(sharedSecret, "TextSecure Provisioning Message".getBytes(), 64);
|
||||
byte[][] parts = Util.split(derivedSecret, 32, 32);
|
||||
|
||||
byte[] version = {0x01};
|
||||
byte[] ciphertext = getCiphertext(parts[0], message.toByteArray());
|
||||
byte[] mac = getMac(parts[1], Util.join(version, ciphertext));
|
||||
byte[] body = Util.join(version, ciphertext, mac);
|
||||
|
||||
return ProvisionEnvelope.newBuilder()
|
||||
.setPublicKey(ByteString.copyFrom(ourKeyPair.getPublicKey().serialize()))
|
||||
.setBody(ByteString.copyFrom(body))
|
||||
.build()
|
||||
.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] getCiphertext(byte[] key, byte[] message) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
|
||||
|
||||
return Util.join(cipher.getIV(), cipher.doFinal(message));
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | java.security.InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getMac(byte[] key, byte[] message) {
|
||||
try {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(new SecretKeySpec(key, "HmacSHA256"));
|
||||
|
||||
return mac.doFinal(message);
|
||||
} catch (NoSuchAlgorithmException | java.security.InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
public class DeviceCode {
|
||||
|
||||
private String verificationCode;
|
||||
|
||||
public String getVerificationCode() {
|
||||
return verificationCode;
|
||||
}
|
||||
}
|
||||
@@ -27,9 +27,9 @@ public class OutgoingPushMessage {
|
||||
private int destinationRegistrationId;
|
||||
private String body;
|
||||
|
||||
public OutgoingPushMessage(PushAddress address, PushBody body) {
|
||||
public OutgoingPushMessage(PushAddress address, int deviceId, PushBody body) {
|
||||
this.type = body.getType();
|
||||
this.destinationDeviceId = address.getDeviceId();
|
||||
this.destinationDeviceId = deviceId;
|
||||
this.destinationRegistrationId = body.getRemoteRegistrationId();
|
||||
this.body = Base64.encodeBytes(body.getBody());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
public class ProvisioningMessage {
|
||||
|
||||
private String body;
|
||||
|
||||
public ProvisioningMessage(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -244,6 +244,10 @@ public final class PushMessageProtos {
|
||||
* <code>RECEIPT = 5;</code>
|
||||
*/
|
||||
RECEIPT(5, 5),
|
||||
/**
|
||||
* <code>COPY = 6;</code>
|
||||
*/
|
||||
COPY(6, 6),
|
||||
;
|
||||
|
||||
/**
|
||||
@@ -270,6 +274,10 @@ public final class PushMessageProtos {
|
||||
* <code>RECEIPT = 5;</code>
|
||||
*/
|
||||
public static final int RECEIPT_VALUE = 5;
|
||||
/**
|
||||
* <code>COPY = 6;</code>
|
||||
*/
|
||||
public static final int COPY_VALUE = 6;
|
||||
|
||||
|
||||
public final int getNumber() { return value; }
|
||||
@@ -282,6 +290,7 @@ public final class PushMessageProtos {
|
||||
case 3: return PREKEY_BUNDLE;
|
||||
case 4: return PLAINTEXT;
|
||||
case 5: return RECEIPT;
|
||||
case 6: return COPY;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
@@ -1187,6 +1196,20 @@ public final class PushMessageProtos {
|
||||
* <code>optional uint32 flags = 4;</code>
|
||||
*/
|
||||
int getFlags();
|
||||
|
||||
// optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
boolean hasSync();
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext getSync();
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder getSyncOrBuilder();
|
||||
}
|
||||
/**
|
||||
* Protobuf type {@code textsecure.PushMessageContent}
|
||||
@@ -1270,6 +1293,19 @@ public final class PushMessageProtos {
|
||||
flags_ = input.readUInt32();
|
||||
break;
|
||||
}
|
||||
case 42: {
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder subBuilder = null;
|
||||
if (((bitField0_ & 0x00000008) == 0x00000008)) {
|
||||
subBuilder = sync_.toBuilder();
|
||||
}
|
||||
sync_ = input.readMessage(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.PARSER, extensionRegistry);
|
||||
if (subBuilder != null) {
|
||||
subBuilder.mergeFrom(sync_);
|
||||
sync_ = subBuilder.buildPartial();
|
||||
}
|
||||
bitField0_ |= 0x00000008;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
@@ -3143,6 +3179,558 @@ public final class PushMessageProtos {
|
||||
// @@protoc_insertion_point(class_scope:textsecure.PushMessageContent.GroupContext)
|
||||
}
|
||||
|
||||
public interface SyncMessageContextOrBuilder
|
||||
extends com.google.protobuf.MessageOrBuilder {
|
||||
|
||||
// optional string destination = 1;
|
||||
/**
|
||||
* <code>optional string destination = 1;</code>
|
||||
*/
|
||||
boolean hasDestination();
|
||||
/**
|
||||
* <code>optional string destination = 1;</code>
|
||||
*/
|
||||
java.lang.String getDestination();
|
||||
/**
|
||||
* <code>optional string destination = 1;</code>
|
||||
*/
|
||||
com.google.protobuf.ByteString
|
||||
getDestinationBytes();
|
||||
|
||||
// optional uint64 timestamp = 2;
|
||||
/**
|
||||
* <code>optional uint64 timestamp = 2;</code>
|
||||
*/
|
||||
boolean hasTimestamp();
|
||||
/**
|
||||
* <code>optional uint64 timestamp = 2;</code>
|
||||
*/
|
||||
long getTimestamp();
|
||||
}
|
||||
/**
|
||||
* Protobuf type {@code textsecure.PushMessageContent.SyncMessageContext}
|
||||
*/
|
||||
public static final class SyncMessageContext extends
|
||||
com.google.protobuf.GeneratedMessage
|
||||
implements SyncMessageContextOrBuilder {
|
||||
// Use SyncMessageContext.newBuilder() to construct.
|
||||
private SyncMessageContext(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
|
||||
super(builder);
|
||||
this.unknownFields = builder.getUnknownFields();
|
||||
}
|
||||
private SyncMessageContext(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
|
||||
|
||||
private static final SyncMessageContext defaultInstance;
|
||||
public static SyncMessageContext getDefaultInstance() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
public SyncMessageContext getDefaultInstanceForType() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
private final com.google.protobuf.UnknownFieldSet unknownFields;
|
||||
@java.lang.Override
|
||||
public final com.google.protobuf.UnknownFieldSet
|
||||
getUnknownFields() {
|
||||
return this.unknownFields;
|
||||
}
|
||||
private SyncMessageContext(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
initFields();
|
||||
int mutable_bitField0_ = 0;
|
||||
com.google.protobuf.UnknownFieldSet.Builder unknownFields =
|
||||
com.google.protobuf.UnknownFieldSet.newBuilder();
|
||||
try {
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int tag = input.readTag();
|
||||
switch (tag) {
|
||||
case 0:
|
||||
done = true;
|
||||
break;
|
||||
default: {
|
||||
if (!parseUnknownField(input, unknownFields,
|
||||
extensionRegistry, tag)) {
|
||||
done = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 10: {
|
||||
bitField0_ |= 0x00000001;
|
||||
destination_ = input.readBytes();
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
bitField0_ |= 0x00000002;
|
||||
timestamp_ = input.readUInt64();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(this);
|
||||
} catch (java.io.IOException e) {
|
||||
throw new com.google.protobuf.InvalidProtocolBufferException(
|
||||
e.getMessage()).setUnfinishedMessage(this);
|
||||
} finally {
|
||||
this.unknownFields = unknownFields.build();
|
||||
makeExtensionsImmutable();
|
||||
}
|
||||
}
|
||||
public static final com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptor() {
|
||||
return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_SyncMessageContext_descriptor;
|
||||
}
|
||||
|
||||
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internalGetFieldAccessorTable() {
|
||||
return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_SyncMessageContext_fieldAccessorTable
|
||||
.ensureFieldAccessorsInitialized(
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.class, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder.class);
|
||||
}
|
||||
|
||||
public static com.google.protobuf.Parser<SyncMessageContext> PARSER =
|
||||
new com.google.protobuf.AbstractParser<SyncMessageContext>() {
|
||||
public SyncMessageContext parsePartialFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return new SyncMessageContext(input, extensionRegistry);
|
||||
}
|
||||
};
|
||||
|
||||
@java.lang.Override
|
||||
public com.google.protobuf.Parser<SyncMessageContext> getParserForType() {
|
||||
return PARSER;
|
||||
}
|
||||
|
||||
private int bitField0_;
|
||||
// optional string destination = 1;
|
||||
public static final int DESTINATION_FIELD_NUMBER = 1;
|
||||
private java.lang.Object destination_;
|
||||
/**
|
||||
* <code>optional string destination = 1;</code>
|
||||
*/
|
||||
public boolean hasDestination() {
|
||||
return ((bitField0_ & 0x00000001) == 0x00000001);
|
||||
}
|
||||
/**
|
||||
* <code>optional string destination = 1;</code>
|
||||
*/
|
||||
public java.lang.String getDestination() {
|
||||
java.lang.Object ref = destination_;
|
||||
if (ref instanceof java.lang.String) {
|
||||
return (java.lang.String) ref;
|
||||
} else {
|
||||
com.google.protobuf.ByteString bs =
|
||||
(com.google.protobuf.ByteString) ref;
|
||||
java.lang.String s = bs.toStringUtf8();
|
||||
if (bs.isValidUtf8()) {
|
||||
destination_ = s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>optional string destination = 1;</code>
|
||||
*/
|
||||
public com.google.protobuf.ByteString
|
||||
getDestinationBytes() {
|
||||
java.lang.Object ref = destination_;
|
||||
if (ref instanceof java.lang.String) {
|
||||
com.google.protobuf.ByteString b =
|
||||
com.google.protobuf.ByteString.copyFromUtf8(
|
||||
(java.lang.String) ref);
|
||||
destination_ = b;
|
||||
return b;
|
||||
} else {
|
||||
return (com.google.protobuf.ByteString) ref;
|
||||
}
|
||||
}
|
||||
|
||||
// optional uint64 timestamp = 2;
|
||||
public static final int TIMESTAMP_FIELD_NUMBER = 2;
|
||||
private long timestamp_;
|
||||
/**
|
||||
* <code>optional uint64 timestamp = 2;</code>
|
||||
*/
|
||||
public boolean hasTimestamp() {
|
||||
return ((bitField0_ & 0x00000002) == 0x00000002);
|
||||
}
|
||||
/**
|
||||
* <code>optional uint64 timestamp = 2;</code>
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp_;
|
||||
}
|
||||
|
||||
private void initFields() {
|
||||
destination_ = "";
|
||||
timestamp_ = 0L;
|
||||
}
|
||||
private byte memoizedIsInitialized = -1;
|
||||
public final boolean isInitialized() {
|
||||
byte isInitialized = memoizedIsInitialized;
|
||||
if (isInitialized != -1) return isInitialized == 1;
|
||||
|
||||
memoizedIsInitialized = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void writeTo(com.google.protobuf.CodedOutputStream output)
|
||||
throws java.io.IOException {
|
||||
getSerializedSize();
|
||||
if (((bitField0_ & 0x00000001) == 0x00000001)) {
|
||||
output.writeBytes(1, getDestinationBytes());
|
||||
}
|
||||
if (((bitField0_ & 0x00000002) == 0x00000002)) {
|
||||
output.writeUInt64(2, timestamp_);
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
}
|
||||
|
||||
private int memoizedSerializedSize = -1;
|
||||
public int getSerializedSize() {
|
||||
int size = memoizedSerializedSize;
|
||||
if (size != -1) return size;
|
||||
|
||||
size = 0;
|
||||
if (((bitField0_ & 0x00000001) == 0x00000001)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeBytesSize(1, getDestinationBytes());
|
||||
}
|
||||
if (((bitField0_ & 0x00000002) == 0x00000002)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeUInt64Size(2, timestamp_);
|
||||
}
|
||||
size += getUnknownFields().getSerializedSize();
|
||||
memoizedSerializedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
@java.lang.Override
|
||||
protected java.lang.Object writeReplace()
|
||||
throws java.io.ObjectStreamException {
|
||||
return super.writeReplace();
|
||||
}
|
||||
|
||||
public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom(
|
||||
com.google.protobuf.ByteString data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data);
|
||||
}
|
||||
public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom(
|
||||
com.google.protobuf.ByteString data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data, extensionRegistry);
|
||||
}
|
||||
public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom(byte[] data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data);
|
||||
}
|
||||
public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom(
|
||||
byte[] data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data, extensionRegistry);
|
||||
}
|
||||
public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
return PARSER.parseFrom(input);
|
||||
}
|
||||
public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return PARSER.parseFrom(input, extensionRegistry);
|
||||
}
|
||||
public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseDelimitedFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
return PARSER.parseDelimitedFrom(input);
|
||||
}
|
||||
public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseDelimitedFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return PARSER.parseDelimitedFrom(input, extensionRegistry);
|
||||
}
|
||||
public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom(
|
||||
com.google.protobuf.CodedInputStream input)
|
||||
throws java.io.IOException {
|
||||
return PARSER.parseFrom(input);
|
||||
}
|
||||
public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return PARSER.parseFrom(input, extensionRegistry);
|
||||
}
|
||||
|
||||
public static Builder newBuilder() { return Builder.create(); }
|
||||
public Builder newBuilderForType() { return newBuilder(); }
|
||||
public static Builder newBuilder(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext prototype) {
|
||||
return newBuilder().mergeFrom(prototype);
|
||||
}
|
||||
public Builder toBuilder() { return newBuilder(this); }
|
||||
|
||||
@java.lang.Override
|
||||
protected Builder newBuilderForType(
|
||||
com.google.protobuf.GeneratedMessage.BuilderParent parent) {
|
||||
Builder builder = new Builder(parent);
|
||||
return builder;
|
||||
}
|
||||
/**
|
||||
* Protobuf type {@code textsecure.PushMessageContent.SyncMessageContext}
|
||||
*/
|
||||
public static final class Builder extends
|
||||
com.google.protobuf.GeneratedMessage.Builder<Builder>
|
||||
implements org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder {
|
||||
public static final com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptor() {
|
||||
return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_SyncMessageContext_descriptor;
|
||||
}
|
||||
|
||||
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internalGetFieldAccessorTable() {
|
||||
return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_SyncMessageContext_fieldAccessorTable
|
||||
.ensureFieldAccessorsInitialized(
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.class, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder.class);
|
||||
}
|
||||
|
||||
// Construct using org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.newBuilder()
|
||||
private Builder() {
|
||||
maybeForceBuilderInitialization();
|
||||
}
|
||||
|
||||
private Builder(
|
||||
com.google.protobuf.GeneratedMessage.BuilderParent parent) {
|
||||
super(parent);
|
||||
maybeForceBuilderInitialization();
|
||||
}
|
||||
private void maybeForceBuilderInitialization() {
|
||||
if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
|
||||
}
|
||||
}
|
||||
private static Builder create() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder clear() {
|
||||
super.clear();
|
||||
destination_ = "";
|
||||
bitField0_ = (bitField0_ & ~0x00000001);
|
||||
timestamp_ = 0L;
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder clone() {
|
||||
return create().mergeFrom(buildPartial());
|
||||
}
|
||||
|
||||
public com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptorForType() {
|
||||
return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_SyncMessageContext_descriptor;
|
||||
}
|
||||
|
||||
public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext getDefaultInstanceForType() {
|
||||
return org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance();
|
||||
}
|
||||
|
||||
public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext build() {
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext result = buildPartial();
|
||||
if (!result.isInitialized()) {
|
||||
throw newUninitializedMessageException(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext buildPartial() {
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext result = new org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext(this);
|
||||
int from_bitField0_ = bitField0_;
|
||||
int to_bitField0_ = 0;
|
||||
if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
|
||||
to_bitField0_ |= 0x00000001;
|
||||
}
|
||||
result.destination_ = destination_;
|
||||
if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
|
||||
to_bitField0_ |= 0x00000002;
|
||||
}
|
||||
result.timestamp_ = timestamp_;
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
return result;
|
||||
}
|
||||
|
||||
public Builder mergeFrom(com.google.protobuf.Message other) {
|
||||
if (other instanceof org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext) {
|
||||
return mergeFrom((org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext)other);
|
||||
} else {
|
||||
super.mergeFrom(other);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public Builder mergeFrom(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext other) {
|
||||
if (other == org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance()) return this;
|
||||
if (other.hasDestination()) {
|
||||
bitField0_ |= 0x00000001;
|
||||
destination_ = other.destination_;
|
||||
onChanged();
|
||||
}
|
||||
if (other.hasTimestamp()) {
|
||||
setTimestamp(other.getTimestamp());
|
||||
}
|
||||
this.mergeUnknownFields(other.getUnknownFields());
|
||||
return this;
|
||||
}
|
||||
|
||||
public final boolean isInitialized() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parsedMessage = null;
|
||||
try {
|
||||
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
parsedMessage = (org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext) e.getUnfinishedMessage();
|
||||
throw e;
|
||||
} finally {
|
||||
if (parsedMessage != null) {
|
||||
mergeFrom(parsedMessage);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
private int bitField0_;
|
||||
|
||||
// optional string destination = 1;
|
||||
private java.lang.Object destination_ = "";
|
||||
/**
|
||||
* <code>optional string destination = 1;</code>
|
||||
*/
|
||||
public boolean hasDestination() {
|
||||
return ((bitField0_ & 0x00000001) == 0x00000001);
|
||||
}
|
||||
/**
|
||||
* <code>optional string destination = 1;</code>
|
||||
*/
|
||||
public java.lang.String getDestination() {
|
||||
java.lang.Object ref = destination_;
|
||||
if (!(ref instanceof java.lang.String)) {
|
||||
java.lang.String s = ((com.google.protobuf.ByteString) ref)
|
||||
.toStringUtf8();
|
||||
destination_ = s;
|
||||
return s;
|
||||
} else {
|
||||
return (java.lang.String) ref;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>optional string destination = 1;</code>
|
||||
*/
|
||||
public com.google.protobuf.ByteString
|
||||
getDestinationBytes() {
|
||||
java.lang.Object ref = destination_;
|
||||
if (ref instanceof String) {
|
||||
com.google.protobuf.ByteString b =
|
||||
com.google.protobuf.ByteString.copyFromUtf8(
|
||||
(java.lang.String) ref);
|
||||
destination_ = b;
|
||||
return b;
|
||||
} else {
|
||||
return (com.google.protobuf.ByteString) ref;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>optional string destination = 1;</code>
|
||||
*/
|
||||
public Builder setDestination(
|
||||
java.lang.String value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
bitField0_ |= 0x00000001;
|
||||
destination_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional string destination = 1;</code>
|
||||
*/
|
||||
public Builder clearDestination() {
|
||||
bitField0_ = (bitField0_ & ~0x00000001);
|
||||
destination_ = getDefaultInstance().getDestination();
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional string destination = 1;</code>
|
||||
*/
|
||||
public Builder setDestinationBytes(
|
||||
com.google.protobuf.ByteString value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
bitField0_ |= 0x00000001;
|
||||
destination_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
// optional uint64 timestamp = 2;
|
||||
private long timestamp_ ;
|
||||
/**
|
||||
* <code>optional uint64 timestamp = 2;</code>
|
||||
*/
|
||||
public boolean hasTimestamp() {
|
||||
return ((bitField0_ & 0x00000002) == 0x00000002);
|
||||
}
|
||||
/**
|
||||
* <code>optional uint64 timestamp = 2;</code>
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp_;
|
||||
}
|
||||
/**
|
||||
* <code>optional uint64 timestamp = 2;</code>
|
||||
*/
|
||||
public Builder setTimestamp(long value) {
|
||||
bitField0_ |= 0x00000002;
|
||||
timestamp_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional uint64 timestamp = 2;</code>
|
||||
*/
|
||||
public Builder clearTimestamp() {
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
timestamp_ = 0L;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:textsecure.PushMessageContent.SyncMessageContext)
|
||||
}
|
||||
|
||||
static {
|
||||
defaultInstance = new SyncMessageContext(true);
|
||||
defaultInstance.initFields();
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(class_scope:textsecure.PushMessageContent.SyncMessageContext)
|
||||
}
|
||||
|
||||
private int bitField0_;
|
||||
// optional string body = 1;
|
||||
public static final int BODY_FIELD_NUMBER = 1;
|
||||
@@ -3261,11 +3849,34 @@ public final class PushMessageProtos {
|
||||
return flags_;
|
||||
}
|
||||
|
||||
// optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;
|
||||
public static final int SYNC_FIELD_NUMBER = 5;
|
||||
private org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext sync_;
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
public boolean hasSync() {
|
||||
return ((bitField0_ & 0x00000008) == 0x00000008);
|
||||
}
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext getSync() {
|
||||
return sync_;
|
||||
}
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder getSyncOrBuilder() {
|
||||
return sync_;
|
||||
}
|
||||
|
||||
private void initFields() {
|
||||
body_ = "";
|
||||
attachments_ = java.util.Collections.emptyList();
|
||||
group_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.getDefaultInstance();
|
||||
flags_ = 0;
|
||||
sync_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance();
|
||||
}
|
||||
private byte memoizedIsInitialized = -1;
|
||||
public final boolean isInitialized() {
|
||||
@@ -3291,6 +3902,9 @@ public final class PushMessageProtos {
|
||||
if (((bitField0_ & 0x00000004) == 0x00000004)) {
|
||||
output.writeUInt32(4, flags_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000008) == 0x00000008)) {
|
||||
output.writeMessage(5, sync_);
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
}
|
||||
|
||||
@@ -3316,6 +3930,10 @@ public final class PushMessageProtos {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeUInt32Size(4, flags_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000008) == 0x00000008)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeMessageSize(5, sync_);
|
||||
}
|
||||
size += getUnknownFields().getSerializedSize();
|
||||
memoizedSerializedSize = size;
|
||||
return size;
|
||||
@@ -3426,6 +4044,7 @@ public final class PushMessageProtos {
|
||||
if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
|
||||
getAttachmentsFieldBuilder();
|
||||
getGroupFieldBuilder();
|
||||
getSyncFieldBuilder();
|
||||
}
|
||||
}
|
||||
private static Builder create() {
|
||||
@@ -3450,6 +4069,12 @@ public final class PushMessageProtos {
|
||||
bitField0_ = (bitField0_ & ~0x00000004);
|
||||
flags_ = 0;
|
||||
bitField0_ = (bitField0_ & ~0x00000008);
|
||||
if (syncBuilder_ == null) {
|
||||
sync_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance();
|
||||
} else {
|
||||
syncBuilder_.clear();
|
||||
}
|
||||
bitField0_ = (bitField0_ & ~0x00000010);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -3503,6 +4128,14 @@ public final class PushMessageProtos {
|
||||
to_bitField0_ |= 0x00000004;
|
||||
}
|
||||
result.flags_ = flags_;
|
||||
if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
|
||||
to_bitField0_ |= 0x00000008;
|
||||
}
|
||||
if (syncBuilder_ == null) {
|
||||
result.sync_ = sync_;
|
||||
} else {
|
||||
result.sync_ = syncBuilder_.build();
|
||||
}
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
return result;
|
||||
@@ -3556,6 +4189,9 @@ public final class PushMessageProtos {
|
||||
if (other.hasFlags()) {
|
||||
setFlags(other.getFlags());
|
||||
}
|
||||
if (other.hasSync()) {
|
||||
mergeSync(other.getSync());
|
||||
}
|
||||
this.mergeUnknownFields(other.getUnknownFields());
|
||||
return this;
|
||||
}
|
||||
@@ -4047,6 +4683,123 @@ public final class PushMessageProtos {
|
||||
return this;
|
||||
}
|
||||
|
||||
// optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;
|
||||
private org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext sync_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance();
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder> syncBuilder_;
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
public boolean hasSync() {
|
||||
return ((bitField0_ & 0x00000010) == 0x00000010);
|
||||
}
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext getSync() {
|
||||
if (syncBuilder_ == null) {
|
||||
return sync_;
|
||||
} else {
|
||||
return syncBuilder_.getMessage();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
public Builder setSync(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext value) {
|
||||
if (syncBuilder_ == null) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
sync_ = value;
|
||||
onChanged();
|
||||
} else {
|
||||
syncBuilder_.setMessage(value);
|
||||
}
|
||||
bitField0_ |= 0x00000010;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
public Builder setSync(
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder builderForValue) {
|
||||
if (syncBuilder_ == null) {
|
||||
sync_ = builderForValue.build();
|
||||
onChanged();
|
||||
} else {
|
||||
syncBuilder_.setMessage(builderForValue.build());
|
||||
}
|
||||
bitField0_ |= 0x00000010;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
public Builder mergeSync(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext value) {
|
||||
if (syncBuilder_ == null) {
|
||||
if (((bitField0_ & 0x00000010) == 0x00000010) &&
|
||||
sync_ != org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance()) {
|
||||
sync_ =
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.newBuilder(sync_).mergeFrom(value).buildPartial();
|
||||
} else {
|
||||
sync_ = value;
|
||||
}
|
||||
onChanged();
|
||||
} else {
|
||||
syncBuilder_.mergeFrom(value);
|
||||
}
|
||||
bitField0_ |= 0x00000010;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
public Builder clearSync() {
|
||||
if (syncBuilder_ == null) {
|
||||
sync_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance();
|
||||
onChanged();
|
||||
} else {
|
||||
syncBuilder_.clear();
|
||||
}
|
||||
bitField0_ = (bitField0_ & ~0x00000010);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder getSyncBuilder() {
|
||||
bitField0_ |= 0x00000010;
|
||||
onChanged();
|
||||
return getSyncFieldBuilder().getBuilder();
|
||||
}
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder getSyncOrBuilder() {
|
||||
if (syncBuilder_ != null) {
|
||||
return syncBuilder_.getMessageOrBuilder();
|
||||
} else {
|
||||
return sync_;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>optional .textsecure.PushMessageContent.SyncMessageContext sync = 5;</code>
|
||||
*/
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder>
|
||||
getSyncFieldBuilder() {
|
||||
if (syncBuilder_ == null) {
|
||||
syncBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
||||
org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder>(
|
||||
sync_,
|
||||
getParentForChildren(),
|
||||
isClean());
|
||||
sync_ = null;
|
||||
}
|
||||
return syncBuilder_;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:textsecure.PushMessageContent)
|
||||
}
|
||||
|
||||
@@ -4078,6 +4831,11 @@ public final class PushMessageProtos {
|
||||
private static
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internal_static_textsecure_PushMessageContent_GroupContext_fieldAccessorTable;
|
||||
private static com.google.protobuf.Descriptors.Descriptor
|
||||
internal_static_textsecure_PushMessageContent_SyncMessageContext_descriptor;
|
||||
private static
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internal_static_textsecure_PushMessageContent_SyncMessageContext_fieldAccessorTable;
|
||||
|
||||
public static com.google.protobuf.Descriptors.FileDescriptor
|
||||
getDescriptor() {
|
||||
@@ -4088,28 +4846,32 @@ public final class PushMessageProtos {
|
||||
static {
|
||||
java.lang.String[] descriptorData = {
|
||||
"\n\037IncomingPushMessageSignal.proto\022\ntexts" +
|
||||
"ecure\"\224\002\n\031IncomingPushMessageSignal\0228\n\004t" +
|
||||
"ecure\"\236\002\n\031IncomingPushMessageSignal\0228\n\004t" +
|
||||
"ype\030\001 \001(\0162*.textsecure.IncomingPushMessa" +
|
||||
"geSignal.Type\022\016\n\006source\030\002 \001(\t\022\024\n\014sourceD" +
|
||||
"evice\030\007 \001(\r\022\r\n\005relay\030\003 \001(\t\022\021\n\ttimestamp\030" +
|
||||
"\005 \001(\004\022\017\n\007message\030\006 \001(\014\"d\n\004Type\022\013\n\007UNKNOW" +
|
||||
"\005 \001(\004\022\017\n\007message\030\006 \001(\014\"n\n\004Type\022\013\n\007UNKNOW" +
|
||||
"N\020\000\022\016\n\nCIPHERTEXT\020\001\022\020\n\014KEY_EXCHANGE\020\002\022\021\n" +
|
||||
"\rPREKEY_BUNDLE\020\003\022\r\n\tPLAINTEXT\020\004\022\013\n\007RECEI" +
|
||||
"PT\020\005\"\207\004\n\022PushMessageContent\022\014\n\004body\030\001 \001(" +
|
||||
"\t\022E\n\013attachments\030\002 \003(\01320.textsecure.Push",
|
||||
"MessageContent.AttachmentPointer\022:\n\005grou" +
|
||||
"p\030\003 \001(\0132+.textsecure.PushMessageContent." +
|
||||
"GroupContext\022\r\n\005flags\030\004 \001(\r\032A\n\021Attachmen" +
|
||||
"tPointer\022\n\n\002id\030\001 \001(\006\022\023\n\013contentType\030\002 \001(" +
|
||||
"\t\022\013\n\003key\030\003 \001(\014\032\363\001\n\014GroupContext\022\n\n\002id\030\001 " +
|
||||
"\001(\014\022>\n\004type\030\002 \001(\01620.textsecure.PushMessa" +
|
||||
"geContent.GroupContext.Type\022\014\n\004name\030\003 \001(" +
|
||||
"\t\022\017\n\007members\030\004 \003(\t\022@\n\006avatar\030\005 \001(\01320.tex" +
|
||||
"tsecure.PushMessageContent.AttachmentPoi" +
|
||||
"nter\"6\n\004Type\022\013\n\007UNKNOWN\020\000\022\n\n\006UPDATE\020\001\022\013\n",
|
||||
"\007DELIVER\020\002\022\010\n\004QUIT\020\003\"\030\n\005Flags\022\017\n\013END_SES" +
|
||||
"SION\020\001B@\n+org.whispersystems.textsecure." +
|
||||
"internal.pushB\021PushMessageProtos"
|
||||
"PT\020\005\022\010\n\004COPY\020\006\"\206\005\n\022PushMessageContent\022\014\n" +
|
||||
"\004body\030\001 \001(\t\022E\n\013attachments\030\002 \003(\01320.texts",
|
||||
"ecure.PushMessageContent.AttachmentPoint" +
|
||||
"er\022:\n\005group\030\003 \001(\0132+.textsecure.PushMessa" +
|
||||
"geContent.GroupContext\022\r\n\005flags\030\004 \001(\r\022?\n" +
|
||||
"\004sync\030\005 \001(\01321.textsecure.PushMessageCont" +
|
||||
"ent.SyncMessageContext\032A\n\021AttachmentPoin" +
|
||||
"ter\022\n\n\002id\030\001 \001(\006\022\023\n\013contentType\030\002 \001(\t\022\013\n\003" +
|
||||
"key\030\003 \001(\014\032\363\001\n\014GroupContext\022\n\n\002id\030\001 \001(\014\022>" +
|
||||
"\n\004type\030\002 \001(\01620.textsecure.PushMessageCon" +
|
||||
"tent.GroupContext.Type\022\014\n\004name\030\003 \001(\t\022\017\n\007" +
|
||||
"members\030\004 \003(\t\022@\n\006avatar\030\005 \001(\01320.textsecu",
|
||||
"re.PushMessageContent.AttachmentPointer\"" +
|
||||
"6\n\004Type\022\013\n\007UNKNOWN\020\000\022\n\n\006UPDATE\020\001\022\013\n\007DELI" +
|
||||
"VER\020\002\022\010\n\004QUIT\020\003\032<\n\022SyncMessageContext\022\023\n" +
|
||||
"\013destination\030\001 \001(\t\022\021\n\ttimestamp\030\002 \001(\004\"\030\n" +
|
||||
"\005Flags\022\017\n\013END_SESSION\020\001B@\n+org.whispersy" +
|
||||
"stems.textsecure.internal.pushB\021PushMess" +
|
||||
"ageProtos"
|
||||
};
|
||||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
||||
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||
@@ -4127,7 +4889,7 @@ public final class PushMessageProtos {
|
||||
internal_static_textsecure_PushMessageContent_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_textsecure_PushMessageContent_descriptor,
|
||||
new java.lang.String[] { "Body", "Attachments", "Group", "Flags", });
|
||||
new java.lang.String[] { "Body", "Attachments", "Group", "Flags", "Sync", });
|
||||
internal_static_textsecure_PushMessageContent_AttachmentPointer_descriptor =
|
||||
internal_static_textsecure_PushMessageContent_descriptor.getNestedTypes().get(0);
|
||||
internal_static_textsecure_PushMessageContent_AttachmentPointer_fieldAccessorTable = new
|
||||
@@ -4140,6 +4902,12 @@ public final class PushMessageProtos {
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_textsecure_PushMessageContent_GroupContext_descriptor,
|
||||
new java.lang.String[] { "Id", "Type", "Name", "Members", "Avatar", });
|
||||
internal_static_textsecure_PushMessageContent_SyncMessageContext_descriptor =
|
||||
internal_static_textsecure_PushMessageContent_descriptor.getNestedTypes().get(2);
|
||||
internal_static_textsecure_PushMessageContent_SyncMessageContext_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_textsecure_PushMessageContent_SyncMessageContext_descriptor,
|
||||
new java.lang.String[] { "Destination", "Timestamp", });
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -79,11 +79,15 @@ public class PushServiceSocket {
|
||||
private static final String CREATE_ACCOUNT_VOICE_PATH = "/v1/accounts/voice/code/%s";
|
||||
private static final String VERIFY_ACCOUNT_PATH = "/v1/accounts/code/%s";
|
||||
private static final String REGISTER_GCM_PATH = "/v1/accounts/gcm/";
|
||||
|
||||
private static final String PREKEY_METADATA_PATH = "/v2/keys/";
|
||||
private static final String PREKEY_PATH = "/v2/keys/%s";
|
||||
private static final String PREKEY_DEVICE_PATH = "/v2/keys/%s/%s";
|
||||
private static final String SIGNED_PREKEY_PATH = "/v2/keys/signed";
|
||||
|
||||
private static final String PROVISIONING_CODE_PATH = "/v1/devices/provisioning/code";
|
||||
private static final String PROVISIONING_MESSAGE_PATH = "/v1/provisioning/%s";
|
||||
|
||||
private static final String DIRECTORY_TOKENS_PATH = "/v1/directory/tokens";
|
||||
private static final String DIRECTORY_VERIFY_PATH = "/v1/directory/%s";
|
||||
private static final String MESSAGE_PATH = "/v1/messages/%s";
|
||||
@@ -120,6 +124,16 @@ public class PushServiceSocket {
|
||||
"PUT", new Gson().toJson(signalingKeyEntity));
|
||||
}
|
||||
|
||||
public String getNewDeviceVerificationCode() throws IOException {
|
||||
String responseText = makeRequest(PROVISIONING_CODE_PATH, "GET", null);
|
||||
return new Gson().fromJson(responseText, DeviceCode.class).getVerificationCode();
|
||||
}
|
||||
|
||||
public void sendProvisioningMessage(String destination, byte[] body) throws IOException {
|
||||
makeRequest(String.format(PROVISIONING_MESSAGE_PATH, destination), "PUT",
|
||||
new Gson().toJson(new ProvisioningMessage(Base64.encodeBytes(body))));
|
||||
}
|
||||
|
||||
public void sendReceipt(String destination, long messageId, String relay) throws IOException {
|
||||
String path = String.format(RECEIPT_PATH, destination, messageId);
|
||||
|
||||
@@ -139,11 +153,15 @@ public class PushServiceSocket {
|
||||
makeRequest(REGISTER_GCM_PATH, "DELETE", null);
|
||||
}
|
||||
|
||||
public void sendMessage(OutgoingPushMessageList bundle)
|
||||
public SendMessageResponse sendMessage(OutgoingPushMessageList bundle)
|
||||
throws IOException
|
||||
{
|
||||
try {
|
||||
makeRequest(String.format(MESSAGE_PATH, bundle.getDestination()), "PUT", new Gson().toJson(bundle));
|
||||
String responseText = makeRequest(String.format(MESSAGE_PATH, bundle.getDestination()), "PUT", new Gson().toJson(bundle));
|
||||
|
||||
if (responseText == null) return new SendMessageResponse(false);
|
||||
else return new Gson().fromJson(responseText, SendMessageResponse.class);
|
||||
|
||||
} catch (NotFoundException nfe) {
|
||||
throw new UnregisteredUserException(bundle.getDestination(), nfe);
|
||||
}
|
||||
@@ -183,9 +201,9 @@ public class PushServiceSocket {
|
||||
return preKeyStatus.getCount();
|
||||
}
|
||||
|
||||
public List<PreKeyBundle> getPreKeys(PushAddress destination) throws IOException {
|
||||
public List<PreKeyBundle> getPreKeys(PushAddress destination, int deviceIdInteger) throws IOException {
|
||||
try {
|
||||
String deviceId = String.valueOf(destination.getDeviceId());
|
||||
String deviceId = String.valueOf(deviceIdInteger);
|
||||
|
||||
if (deviceId.equals("1"))
|
||||
deviceId = "*";
|
||||
@@ -231,10 +249,10 @@ public class PushServiceSocket {
|
||||
}
|
||||
}
|
||||
|
||||
public PreKeyBundle getPreKey(PushAddress destination) throws IOException {
|
||||
public PreKeyBundle getPreKey(PushAddress destination, int deviceId) throws IOException {
|
||||
try {
|
||||
String path = String.format(PREKEY_DEVICE_PATH, destination.getNumber(),
|
||||
String.valueOf(destination.getDeviceId()));
|
||||
String.valueOf(deviceId));
|
||||
|
||||
if (!Util.isEmpty(destination.getRelay())) {
|
||||
path = path + "?relay=" + destination.getRelay();
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
public class SendMessageResponse {
|
||||
|
||||
private boolean needsSync;
|
||||
|
||||
public SendMessageResponse() {}
|
||||
|
||||
public SendMessageResponse(boolean needsSync) {
|
||||
this.needsSync = needsSync;
|
||||
}
|
||||
|
||||
public boolean getNeedsSync() {
|
||||
return needsSync;
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,19 @@ import java.security.SecureRandom;
|
||||
|
||||
public class Util {
|
||||
|
||||
public static byte[] join(byte[]... input) {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
for (byte[] part : input) {
|
||||
baos.write(part);
|
||||
}
|
||||
|
||||
return baos.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[][] split(byte[] input, int firstLength, int secondLength) {
|
||||
byte[][] parts = new byte[2][];
|
||||
|
||||
|
||||
7
proguard.cfg
Normal file
@@ -0,0 +1,7 @@
|
||||
-keepattributes **
|
||||
-keep class !android.support.v7.internal.view.menu.**,** {*;}
|
||||
-dontpreverify
|
||||
-dontoptimize
|
||||
-dontshrink
|
||||
-dontwarn **
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:state_enabled="false" android:color="@color/gray27"/>
|
||||
<item android:color="@color/gray95"/>
|
||||
</selector>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:state_enabled="false" android:color="@color/gray27"/>
|
||||
<item android:color="@color/gray50"/>
|
||||
</selector>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 223 B |
|
Before Width: | Height: | Size: 371 B |
BIN
res/drawable-hdpi/encrypted_backup_dark.png
Executable file
|
After Width: | Height: | Size: 309 B |
BIN
res/drawable-hdpi/encrypted_backup_light.png
Executable file
|
After Width: | Height: | Size: 308 B |
|
Before Width: | Height: | Size: 495 B |
|
Before Width: | Height: | Size: 496 B |
|
Before Width: | Height: | Size: 203 B |
|
Before Width: | Height: | Size: 202 B |
|
Before Width: | Height: | Size: 258 B |
|
Before Width: | Height: | Size: 254 B |
|
Before Width: | Height: | Size: 146 B |
|
Before Width: | Height: | Size: 147 B |
|
Before Width: | Height: | Size: 218 B |
|
Before Width: | Height: | Size: 218 B |
BIN
res/drawable-hdpi/ic_arrow_forward_white_24dp.png
Normal file
|
After Width: | Height: | Size: 321 B |
BIN
res/drawable-hdpi/ic_call_split_white_24dp.png
Normal file
|
After Width: | Height: | Size: 381 B |
BIN
res/drawable-hdpi/ic_check_white_24dp.png
Normal file
|
After Width: | Height: | Size: 331 B |
BIN
res/drawable-hdpi/ic_delete_white_24dp.png
Normal file
|
After Width: | Height: | Size: 345 B |
|
Before Width: | Height: | Size: 491 B |
|
Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 528 B |
BIN
res/drawable-hdpi/ic_forward_white_24dp.png
Normal file
|
After Width: | Height: | Size: 305 B |
BIN
res/drawable-hdpi/ic_group_white_24dp.png
Normal file
|
After Width: | Height: | Size: 475 B |
BIN
res/drawable-hdpi/ic_info_outline_white_24dp.png
Normal file
|
After Width: | Height: | Size: 721 B |
|
Before Width: | Height: | Size: 178 B After Width: | Height: | Size: 432 B |
BIN
res/drawable-hdpi/ic_lock_white_24dp.png
Normal file
|
After Width: | Height: | Size: 535 B |
|
Before Width: | Height: | Size: 199 B |
|
Before Width: | Height: | Size: 197 B |
|
Before Width: | Height: | Size: 794 B |
|
Before Width: | Height: | Size: 302 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 437 B |
|
Before Width: | Height: | Size: 229 B |
|
Before Width: | Height: | Size: 405 B |
|
Before Width: | Height: | Size: 397 B |
|
Before Width: | Height: | Size: 145 B |
|
Before Width: | Height: | Size: 245 B |
|
Before Width: | Height: | Size: 490 B |
|
Before Width: | Height: | Size: 291 B |
|
Before Width: | Height: | Size: 759 B |
|
Before Width: | Height: | Size: 278 B |
|
Before Width: | Height: | Size: 437 B |
|
Before Width: | Height: | Size: 448 B |
BIN
res/drawable-hdpi/ic_refresh_white_24dp.png
Normal file
|
After Width: | Height: | Size: 571 B |
BIN
res/drawable-hdpi/ic_save_white_24dp.png
Normal file
|
After Width: | Height: | Size: 482 B |
BIN
res/drawable-hdpi/ic_select_all_white_24dp.png
Normal file
|
After Width: | Height: | Size: 449 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
res/drawable-hdpi/ic_sms.png
Normal file
|
After Width: | Height: | Size: 719 B |
BIN
res/drawable-hdpi/ic_sms_black.png
Normal file
|
After Width: | Height: | Size: 789 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable-hdpi/ic_unlocked_black_18dp.png
Normal file
|
After Width: | Height: | Size: 511 B |
BIN
res/drawable-hdpi/ic_unlocked_white_18dp.png
Normal file
|
After Width: | Height: | Size: 515 B |
BIN
res/drawable-hdpi/ic_unlocked_white_24dp.png
Normal file
|
After Width: | Height: | Size: 638 B |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 3.9 KiB |
BIN
res/drawable-hdpi/icon_dialog.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 344 B |
BIN
res/drawable-hdpi/plaintext_backup_dark.png
Executable file
|
After Width: | Height: | Size: 203 B |
BIN
res/drawable-hdpi/plaintext_backup_light.png
Executable file
|
After Width: | Height: | Size: 195 B |
BIN
res/drawable-hdpi/splash_logo.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 305 B |
BIN
res/drawable-hdpi/stock_sms_dark.png
Executable file
|
After Width: | Height: | Size: 168 B |
BIN
res/drawable-hdpi/stock_sms_light.png
Executable file
|
After Width: | Height: | Size: 158 B |