diff --git a/library/protobuf/IncomingPushMessageSignal.proto b/library/protobuf/IncomingPushMessageSignal.proto
index 1e82872705..90465ae279 100644
--- a/library/protobuf/IncomingPushMessageSignal.proto
+++ b/library/protobuf/IncomingPushMessageSignal.proto
@@ -7,13 +7,18 @@ message IncomingPushMessageSignal {
optional uint32 type = 1;
optional string source = 2;
repeated string destinations = 3;
- optional bytes message = 4;
+ optional uint64 timestamp = 4;
+ optional bytes message = 5; // Contains an encrypted IncomingPushMessageContent
+}
+
+message PushMessageContent {
+ optional string body = 1;
message AttachmentPointer {
- optional string contentType = 1;
- optional string key = 2;
+ optional fixed64 id = 1;
+ optional string contentType = 2;
+ optional bytes key = 3;
}
- repeated AttachmentPointer attachments = 5;
- optional uint64 timestamp = 6;
+ repeated AttachmentPointer attachments = 2;
}
\ No newline at end of file
diff --git a/library/src/org/whispersystems/textsecure/crypto/AttachmentCipher.java b/library/src/org/whispersystems/textsecure/crypto/AttachmentCipher.java
new file mode 100644
index 0000000000..4496f8653a
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/AttachmentCipher.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright (C) 2013 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.whispersystems.textsecure.crypto;
+
+import android.util.Log;
+
+import org.whispersystems.textsecure.util.Hex;
+import org.whispersystems.textsecure.util.Util;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.Mac;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+/**
+ * Encrypts push attachments.
+ *
+ * @author Moxie Marlinspike
+ */
+public class AttachmentCipher {
+
+ static final int CIPHER_KEY_SIZE = 32;
+ static final int MAC_KEY_SIZE = 20;
+
+ private final SecretKeySpec cipherKey;
+ private final SecretKeySpec macKey;
+ private final Cipher cipher;
+ private final Mac mac;
+
+ public AttachmentCipher() {
+ this.cipherKey = initializeRandomCipherKey();
+ this.macKey = initializeRandomMacKey();
+ this.cipher = initializeCipher();
+ this.mac = initializeMac();
+ }
+
+ public AttachmentCipher(byte[] combinedKeyMaterial) {
+ byte[][] parts = Util.split(combinedKeyMaterial, CIPHER_KEY_SIZE, MAC_KEY_SIZE);
+ this.cipherKey = new SecretKeySpec(parts[0], "AES");
+ this.macKey = new SecretKeySpec(parts[1], "HmacSHA1");
+ this.cipher = initializeCipher();
+ this.mac = initializeMac();
+ }
+
+ public byte[] getCombinedKeyMaterial() {
+ return Util.combine(this.cipherKey.getEncoded(), this.macKey.getEncoded());
+ }
+
+ public byte[] encrypt(byte[] plaintext) {
+ try {
+ this.cipher.init(Cipher.ENCRYPT_MODE, this.cipherKey);
+ this.mac.init(this.macKey);
+
+ byte[] ciphertext = this.cipher.doFinal(plaintext);
+ byte[] iv = this.cipher.getIV();
+ byte[] mac = this.mac.doFinal(Util.combine(iv, ciphertext));
+
+ return Util.combine(iv, ciphertext, mac);
+ } catch (IllegalBlockSizeException e) {
+ throw new AssertionError(e);
+ } catch (BadPaddingException e) {
+ throw new AssertionError(e);
+ } catch (InvalidKeyException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ public byte[] decrypt(byte[] ciphertext)
+ throws InvalidMacException, InvalidMessageException
+ {
+ try {
+ if (ciphertext.length <= cipher.getBlockSize() + mac.getMacLength()) {
+ throw new InvalidMessageException("Message too short!");
+ }
+
+ byte[][] ciphertextParts = Util.split(ciphertext,
+ this.cipher.getBlockSize(),
+ ciphertext.length - this.cipher.getBlockSize() - this.mac.getMacLength(),
+ this.mac.getMacLength());
+
+ this.mac.update(ciphertext, 0, ciphertext.length - mac.getMacLength());
+ byte[] ourMac = this.mac.doFinal();
+
+ if (!Arrays.equals(ourMac, ciphertextParts[2])) {
+ throw new InvalidMacException("Mac doesn't match!");
+ }
+
+ this.cipher.init(Cipher.DECRYPT_MODE, this.cipherKey,
+ new IvParameterSpec(ciphertextParts[0]));
+
+ return cipher.doFinal(ciphertextParts[1]);
+ } catch (InvalidKeyException e) {
+ throw new AssertionError(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new AssertionError(e);
+ } catch (IllegalBlockSizeException e) {
+ throw new AssertionError(e);
+ } catch (BadPaddingException e) {
+ throw new InvalidMessageException(e);
+ }
+ }
+
+ private Mac initializeMac() {
+ try {
+ Mac mac = Mac.getInstance("HmacSHA1");
+ return mac;
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private Cipher initializeCipher() {
+ try {
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ return cipher;
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError(e);
+ } catch (NoSuchPaddingException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private SecretKeySpec initializeRandomCipherKey() {
+ byte[] key = new byte[CIPHER_KEY_SIZE];
+ Util.getSecureRandom().nextBytes(key);
+ return new SecretKeySpec(key, "AES");
+ }
+
+ private SecretKeySpec initializeRandomMacKey() {
+ byte[] key = new byte[MAC_KEY_SIZE];
+ Util.getSecureRandom().nextBytes(key);
+ return new SecretKeySpec(key, "HmacSHA1");
+ }
+
+}
diff --git a/library/src/org/whispersystems/textsecure/crypto/AttachmentCipherInputStream.java b/library/src/org/whispersystems/textsecure/crypto/AttachmentCipherInputStream.java
new file mode 100644
index 0000000000..753e4c80fd
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/AttachmentCipherInputStream.java
@@ -0,0 +1,178 @@
+/**
+ * Copyright (C) 2013 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.whispersystems.textsecure.crypto;
+
+import android.util.Log;
+
+import org.whispersystems.textsecure.util.Hex;
+import org.whispersystems.textsecure.util.Util;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.Mac;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+/**
+ * Class for streaming an encrypted push attachment off disk.
+ *
+ * @author Moxie Marlinspike
+ */
+
+public class AttachmentCipherInputStream extends FileInputStream {
+
+ private static final int BLOCK_SIZE = 16;
+
+ private Cipher cipher;
+ private boolean done;
+ private long totalDataSize;
+ private long totalRead;
+
+ public AttachmentCipherInputStream(File file, byte[] combinedKeyMaterial)
+ throws IOException, InvalidMessageException
+ {
+ super(file);
+
+ try {
+ byte[][] parts = Util.split(combinedKeyMaterial,
+ AttachmentCipher.CIPHER_KEY_SIZE,
+ AttachmentCipher.MAC_KEY_SIZE);
+
+ Mac mac = Mac.getInstance("HmacSHA1");
+ mac.init(new SecretKeySpec(parts[1], "HmacSHA1"));
+
+ if (file.length() <= BLOCK_SIZE + mac.getMacLength()) {
+ throw new InvalidMessageException("Message shorter than crypto overhead!");
+ }
+
+ verifyMac(file, mac);
+
+ byte[] iv = new byte[BLOCK_SIZE];
+ readFully(iv);
+
+ this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ this.cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(parts[0], "AES"), new IvParameterSpec(iv));
+
+ this.done = false;
+ this.totalRead = 0;
+ this.totalDataSize = file.length() - cipher.getBlockSize() - mac.getMacLength();
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError(e);
+ } catch (InvalidKeyException e) {
+ throw new AssertionError(e);
+ } catch (InvalidMacException e) {
+ throw new InvalidMessageException(e);
+ } catch (NoSuchPaddingException e) {
+ throw new AssertionError(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public int read(byte[] buffer) throws IOException {
+ return read(buffer, 0, buffer.length);
+ }
+
+ @Override
+ public int read(byte[] buffer, int offset, int length) throws IOException {
+ if (totalRead != totalDataSize) return readIncremental(buffer, offset, length);
+ else if (!done) return readFinal(buffer, offset, length);
+ else return -1;
+ }
+
+ private int readFinal(byte[] buffer, int offset, int length) throws IOException {
+ try {
+ int flourish = cipher.doFinal(buffer, offset);
+
+ done = true;
+ return flourish;
+ } catch (IllegalBlockSizeException e) {
+ Log.w("EncryptingPartInputStream", e);
+ throw new IOException("Illegal block size exception!");
+ } catch (ShortBufferException e) {
+ Log.w("EncryptingPartInputStream", e);
+ throw new IOException("Short buffer exception!");
+ } catch (BadPaddingException e) {
+ Log.w("EncryptingPartInputStream", e);
+ throw new IOException("Bad padding exception!");
+ }
+ }
+
+ private int readIncremental(byte[] buffer, int offset, int length) throws IOException {
+ if (length + totalRead > totalDataSize)
+ length = (int)(totalDataSize - totalRead);
+
+ byte[] internalBuffer = new byte[length];
+ int read = super.read(internalBuffer, 0, internalBuffer.length <= cipher.getBlockSize() ? internalBuffer.length : internalBuffer.length - cipher.getBlockSize());
+ totalRead += read;
+
+ try {
+ return cipher.update(internalBuffer, 0, read, buffer, offset);
+ } catch (ShortBufferException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private void verifyMac(File file, Mac mac) throws FileNotFoundException, InvalidMacException {
+ try {
+ FileInputStream fin = new FileInputStream(file);
+ int remainingData = (int) file.length() - mac.getMacLength();
+ byte[] buffer = new byte[4096];
+
+ while (remainingData > 0) {
+ int read = fin.read(buffer, 0, Math.min(buffer.length, remainingData));
+ mac.update(buffer, 0, read);
+ remainingData -= read;
+ }
+
+ byte[] ourMac = mac.doFinal();
+ byte[] theirMac = new byte[mac.getMacLength()];
+ Util.readFully(fin, theirMac);
+
+ if (!Arrays.equals(ourMac, theirMac)) {
+ throw new InvalidMacException("MAC doesn't match!");
+ }
+ } catch (IOException e1) {
+ throw new InvalidMacException(e1);
+ }
+ }
+
+ private void readFully(byte[] buffer) throws IOException {
+ int offset = 0;
+
+ for (;;) {
+ int read = super.read(buffer, offset, buffer.length - offset);
+
+ if (read + offset < buffer.length) offset += read;
+ else return;
+ }
+ }
+
+
+}
diff --git a/library/src/org/whispersystems/textsecure/push/IncomingPushMessage.java b/library/src/org/whispersystems/textsecure/push/IncomingPushMessage.java
index 57b2fd1427..8de52cd294 100644
--- a/library/src/org/whispersystems/textsecure/push/IncomingPushMessage.java
+++ b/library/src/org/whispersystems/textsecure/push/IncomingPushMessage.java
@@ -1,12 +1,26 @@
+/**
+ * Copyright (C) 2013 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package org.whispersystems.textsecure.push;
-import org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
-import org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer;
-import org.whispersystems.textsecure.util.Base64;
-
import android.os.Parcel;
import android.os.Parcelable;
+import org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
+
import java.util.LinkedList;
import java.util.List;
@@ -24,12 +38,20 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
}
};
- private int type;
- private String source;
- private List destinations;
- private byte[] message;
- private List attachments;
- private long timestamp;
+ private int type;
+ private String source;
+ private List destinations;
+ private byte[] message;
+ private long timestamp;
+
+ private IncomingPushMessage(IncomingPushMessage message, byte[] body) {
+ this.type = message.type;
+ this.source = message.source;
+ this.destinations = new LinkedList();
+ this.destinations.addAll(message.destinations);
+ this.message = body;
+ this.timestamp = message.timestamp;
+ }
public IncomingPushMessage(IncomingPushMessageSignal signal) {
this.type = signal.getType();
@@ -37,25 +59,15 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
this.destinations = signal.getDestinationsList();
this.message = signal.getMessage().toByteArray();
this.timestamp = signal.getTimestamp();
- this.attachments = new LinkedList();
-
- List attachmentPointers = signal.getAttachmentsList();
-
- for (AttachmentPointer pointer : attachmentPointers) {
- this.attachments.add(new PushAttachmentPointer(pointer.getContentType(), pointer.getKey()));
- }
}
public IncomingPushMessage(Parcel in) {
this.destinations = new LinkedList();
- this.attachments = new LinkedList();
-
this.type = in.readInt();
this.source = in.readString();
in.readStringList(destinations);
this.message = new byte[in.readInt()];
in.readByteArray(this.message);
- in.readList(attachments, PushAttachmentPointer.class.getClassLoader());
this.timestamp = in.readLong();
}
@@ -67,10 +79,6 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
return source;
}
- public List getAttachments() {
- return attachments;
- }
-
public byte[] getBody() {
return message;
}
@@ -79,10 +87,6 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
return destinations;
}
- public boolean hasAttachments() {
- return getAttachments() != null && !getAttachments().isEmpty();
- }
-
@Override
public int describeContents() {
return 0;
@@ -95,11 +99,22 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
dest.writeStringList(destinations);
dest.writeInt(message.length);
dest.writeByteArray(message);
- dest.writeList(attachments);
dest.writeLong(timestamp);
}
+ public IncomingPushMessage withBody(byte[] body) {
+ return new IncomingPushMessage(this, body);
+ }
+
public int getType() {
return type;
}
+
+ public boolean isSecureMessage() {
+ return getType() == PushMessage.TYPE_MESSAGE_CIPHERTEXT;
+ }
+
+ public boolean isPreKeyBundle() {
+ return getType() == PushMessage.TYPE_MESSAGE_PREKEY_BUNDLE;
+ }
}
diff --git a/library/src/org/whispersystems/textsecure/push/OutgoingPushMessage.java b/library/src/org/whispersystems/textsecure/push/OutgoingPushMessage.java
index 487a0da07d..1cfe139895 100644
--- a/library/src/org/whispersystems/textsecure/push/OutgoingPushMessage.java
+++ b/library/src/org/whispersystems/textsecure/push/OutgoingPushMessage.java
@@ -1,34 +1,35 @@
+/**
+ * Copyright (C) 2013 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package org.whispersystems.textsecure.push;
import org.whispersystems.textsecure.util.Base64;
-import java.util.LinkedList;
-import java.util.List;
-
public class OutgoingPushMessage implements PushMessage {
- private int type;
- private String destination;
- private String body;
- private List attachments;
+ private int type;
+ private String destination;
+ private String body;
public OutgoingPushMessage(String destination, byte[] body, int type) {
- this.attachments = new LinkedList();
this.destination = destination;
this.body = Base64.encodeBytes(body);
this.type = type;
}
- public OutgoingPushMessage(String destination, byte[] body,
- List attachments,
- int type)
- {
- this.destination = destination;
- this.body = Base64.encodeBytes(body);
- this.attachments = attachments;
- this.type = type;
- }
-
public String getDestination() {
return destination;
}
@@ -37,10 +38,6 @@ public class OutgoingPushMessage implements PushMessage {
return body;
}
- public List getAttachments() {
- return attachments;
- }
-
public int getType() {
return type;
}
diff --git a/library/src/org/whispersystems/textsecure/push/PushAttachmentPointer.java b/library/src/org/whispersystems/textsecure/push/PushAttachmentPointer.java
index 8a3a81286e..30e893861b 100644
--- a/library/src/org/whispersystems/textsecure/push/PushAttachmentPointer.java
+++ b/library/src/org/whispersystems/textsecure/push/PushAttachmentPointer.java
@@ -18,23 +18,33 @@ public class PushAttachmentPointer implements Parcelable {
};
private final String contentType;
- private final String key;
+ private final long id;
+ private final byte[] key;
- public PushAttachmentPointer(String contentType, String key) {
+ public PushAttachmentPointer(String contentType, long id, byte[] key) {
this.contentType = contentType;
+ this.id = id;
this.key = key;
}
public PushAttachmentPointer(Parcel in) {
this.contentType = in.readString();
- this.key = in.readString();
+ this.id = in.readLong();
+
+ int keyLength = in.readInt();
+ this.key = new byte[keyLength];
+ in.readByteArray(this.key);
}
public String getContentType() {
return contentType;
}
- public String getKey() {
+ public long getId() {
+ return id;
+ }
+
+ public byte[] getKey() {
return key;
}
@@ -46,6 +56,8 @@ public class PushAttachmentPointer implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(contentType);
- dest.writeString(key);
+ dest.writeLong(id);
+ dest.writeInt(this.key.length);
+ dest.writeByteArray(this.key);
}
}
diff --git a/library/src/org/whispersystems/textsecure/push/PushMessageProtos.java b/library/src/org/whispersystems/textsecure/push/PushMessageProtos.java
index ff79ffa74e..ca0179643d 100644
--- a/library/src/org/whispersystems/textsecure/push/PushMessageProtos.java
+++ b/library/src/org/whispersystems/textsecure/push/PushMessageProtos.java
@@ -24,23 +24,13 @@ public final class PushMessageProtos {
int getDestinationsCount();
String getDestinations(int index);
- // optional bytes message = 4;
- boolean hasMessage();
- com.google.protobuf.ByteString getMessage();
-
- // repeated .textsecure.IncomingPushMessageSignal.AttachmentPointer attachments = 5;
- java.util.List
- getAttachmentsList();
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer getAttachments(int index);
- int getAttachmentsCount();
- java.util.List extends org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder>
- getAttachmentsOrBuilderList();
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder getAttachmentsOrBuilder(
- int index);
-
- // optional uint64 timestamp = 6;
+ // optional uint64 timestamp = 4;
boolean hasTimestamp();
long getTimestamp();
+
+ // optional bytes message = 5;
+ boolean hasMessage();
+ com.google.protobuf.ByteString getMessage();
}
public static final class IncomingPushMessageSignal extends
com.google.protobuf.GeneratedMessage
@@ -70,475 +60,6 @@ public final class PushMessageProtos {
return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_fieldAccessorTable;
}
- public interface AttachmentPointerOrBuilder
- extends com.google.protobuf.MessageOrBuilder {
-
- // optional string contentType = 1;
- boolean hasContentType();
- String getContentType();
-
- // optional string key = 2;
- boolean hasKey();
- String getKey();
- }
- public static final class AttachmentPointer extends
- com.google.protobuf.GeneratedMessage
- implements AttachmentPointerOrBuilder {
- // Use AttachmentPointer.newBuilder() to construct.
- private AttachmentPointer(Builder builder) {
- super(builder);
- }
- private AttachmentPointer(boolean noInit) {}
-
- private static final AttachmentPointer defaultInstance;
- public static AttachmentPointer getDefaultInstance() {
- return defaultInstance;
- }
-
- public AttachmentPointer getDefaultInstanceForType() {
- return defaultInstance;
- }
-
- public static final com.google.protobuf.Descriptors.Descriptor
- getDescriptor() {
- return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_descriptor;
- }
-
- protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
- internalGetFieldAccessorTable() {
- return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_fieldAccessorTable;
- }
-
- private int bitField0_;
- // optional string contentType = 1;
- public static final int CONTENTTYPE_FIELD_NUMBER = 1;
- private java.lang.Object contentType_;
- public boolean hasContentType() {
- return ((bitField0_ & 0x00000001) == 0x00000001);
- }
- public String getContentType() {
- java.lang.Object ref = contentType_;
- if (ref instanceof String) {
- return (String) ref;
- } else {
- com.google.protobuf.ByteString bs =
- (com.google.protobuf.ByteString) ref;
- String s = bs.toStringUtf8();
- if (com.google.protobuf.Internal.isValidUtf8(bs)) {
- contentType_ = s;
- }
- return s;
- }
- }
- private com.google.protobuf.ByteString getContentTypeBytes() {
- java.lang.Object ref = contentType_;
- if (ref instanceof String) {
- com.google.protobuf.ByteString b =
- com.google.protobuf.ByteString.copyFromUtf8((String) ref);
- contentType_ = b;
- return b;
- } else {
- return (com.google.protobuf.ByteString) ref;
- }
- }
-
- // optional string key = 2;
- public static final int KEY_FIELD_NUMBER = 2;
- private java.lang.Object key_;
- public boolean hasKey() {
- return ((bitField0_ & 0x00000002) == 0x00000002);
- }
- public String getKey() {
- java.lang.Object ref = key_;
- if (ref instanceof String) {
- return (String) ref;
- } else {
- com.google.protobuf.ByteString bs =
- (com.google.protobuf.ByteString) ref;
- String s = bs.toStringUtf8();
- if (com.google.protobuf.Internal.isValidUtf8(bs)) {
- key_ = s;
- }
- return s;
- }
- }
- private com.google.protobuf.ByteString getKeyBytes() {
- java.lang.Object ref = key_;
- if (ref instanceof String) {
- com.google.protobuf.ByteString b =
- com.google.protobuf.ByteString.copyFromUtf8((String) ref);
- key_ = b;
- return b;
- } else {
- return (com.google.protobuf.ByteString) ref;
- }
- }
-
- private void initFields() {
- contentType_ = "";
- key_ = "";
- }
- 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, getContentTypeBytes());
- }
- if (((bitField0_ & 0x00000002) == 0x00000002)) {
- output.writeBytes(2, getKeyBytes());
- }
- 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, getContentTypeBytes());
- }
- if (((bitField0_ & 0x00000002) == 0x00000002)) {
- size += com.google.protobuf.CodedOutputStream
- .computeBytesSize(2, getKeyBytes());
- }
- 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.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom(
- com.google.protobuf.ByteString data)
- throws com.google.protobuf.InvalidProtocolBufferException {
- return newBuilder().mergeFrom(data).buildParsed();
- }
- public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom(
- com.google.protobuf.ByteString data,
- com.google.protobuf.ExtensionRegistryLite extensionRegistry)
- throws com.google.protobuf.InvalidProtocolBufferException {
- return newBuilder().mergeFrom(data, extensionRegistry)
- .buildParsed();
- }
- public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom(byte[] data)
- throws com.google.protobuf.InvalidProtocolBufferException {
- return newBuilder().mergeFrom(data).buildParsed();
- }
- public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom(
- byte[] data,
- com.google.protobuf.ExtensionRegistryLite extensionRegistry)
- throws com.google.protobuf.InvalidProtocolBufferException {
- return newBuilder().mergeFrom(data, extensionRegistry)
- .buildParsed();
- }
- public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom(java.io.InputStream input)
- throws java.io.IOException {
- return newBuilder().mergeFrom(input).buildParsed();
- }
- public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom(
- java.io.InputStream input,
- com.google.protobuf.ExtensionRegistryLite extensionRegistry)
- throws java.io.IOException {
- return newBuilder().mergeFrom(input, extensionRegistry)
- .buildParsed();
- }
- public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseDelimitedFrom(java.io.InputStream input)
- throws java.io.IOException {
- Builder builder = newBuilder();
- if (builder.mergeDelimitedFrom(input)) {
- return builder.buildParsed();
- } else {
- return null;
- }
- }
- public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseDelimitedFrom(
- java.io.InputStream input,
- com.google.protobuf.ExtensionRegistryLite extensionRegistry)
- throws java.io.IOException {
- Builder builder = newBuilder();
- if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
- return builder.buildParsed();
- } else {
- return null;
- }
- }
- public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom(
- com.google.protobuf.CodedInputStream input)
- throws java.io.IOException {
- return newBuilder().mergeFrom(input).buildParsed();
- }
- public static org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer parseFrom(
- com.google.protobuf.CodedInputStream input,
- com.google.protobuf.ExtensionRegistryLite extensionRegistry)
- throws java.io.IOException {
- return newBuilder().mergeFrom(input, extensionRegistry)
- .buildParsed();
- }
-
- public static Builder newBuilder() { return Builder.create(); }
- public Builder newBuilderForType() { return newBuilder(); }
- public static Builder newBuilder(org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer 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;
- }
- public static final class Builder extends
- com.google.protobuf.GeneratedMessage.Builder
- implements org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder {
- public static final com.google.protobuf.Descriptors.Descriptor
- getDescriptor() {
- return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_descriptor;
- }
-
- protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
- internalGetFieldAccessorTable() {
- return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_fieldAccessorTable;
- }
-
- // Construct using org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.newBuilder()
- private Builder() {
- maybeForceBuilderInitialization();
- }
-
- private Builder(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();
- contentType_ = "";
- bitField0_ = (bitField0_ & ~0x00000001);
- key_ = "";
- bitField0_ = (bitField0_ & ~0x00000002);
- return this;
- }
-
- public Builder clone() {
- return create().mergeFrom(buildPartial());
- }
-
- public com.google.protobuf.Descriptors.Descriptor
- getDescriptorForType() {
- return org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.getDescriptor();
- }
-
- public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer getDefaultInstanceForType() {
- return org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.getDefaultInstance();
- }
-
- public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer build() {
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer result = buildPartial();
- if (!result.isInitialized()) {
- throw newUninitializedMessageException(result);
- }
- return result;
- }
-
- private org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer buildParsed()
- throws com.google.protobuf.InvalidProtocolBufferException {
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer result = buildPartial();
- if (!result.isInitialized()) {
- throw newUninitializedMessageException(
- result).asInvalidProtocolBufferException();
- }
- return result;
- }
-
- public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer buildPartial() {
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer result = new org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer(this);
- int from_bitField0_ = bitField0_;
- int to_bitField0_ = 0;
- if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
- to_bitField0_ |= 0x00000001;
- }
- result.contentType_ = contentType_;
- if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
- to_bitField0_ |= 0x00000002;
- }
- result.key_ = key_;
- result.bitField0_ = to_bitField0_;
- onBuilt();
- return result;
- }
-
- public Builder mergeFrom(com.google.protobuf.Message other) {
- if (other instanceof org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer) {
- return mergeFrom((org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer)other);
- } else {
- super.mergeFrom(other);
- return this;
- }
- }
-
- public Builder mergeFrom(org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer other) {
- if (other == org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.getDefaultInstance()) return this;
- if (other.hasContentType()) {
- setContentType(other.getContentType());
- }
- if (other.hasKey()) {
- setKey(other.getKey());
- }
- 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 {
- com.google.protobuf.UnknownFieldSet.Builder unknownFields =
- com.google.protobuf.UnknownFieldSet.newBuilder(
- this.getUnknownFields());
- while (true) {
- int tag = input.readTag();
- switch (tag) {
- case 0:
- this.setUnknownFields(unknownFields.build());
- onChanged();
- return this;
- default: {
- if (!parseUnknownField(input, unknownFields,
- extensionRegistry, tag)) {
- this.setUnknownFields(unknownFields.build());
- onChanged();
- return this;
- }
- break;
- }
- case 10: {
- bitField0_ |= 0x00000001;
- contentType_ = input.readBytes();
- break;
- }
- case 18: {
- bitField0_ |= 0x00000002;
- key_ = input.readBytes();
- break;
- }
- }
- }
- }
-
- private int bitField0_;
-
- // optional string contentType = 1;
- private java.lang.Object contentType_ = "";
- public boolean hasContentType() {
- return ((bitField0_ & 0x00000001) == 0x00000001);
- }
- public String getContentType() {
- java.lang.Object ref = contentType_;
- if (!(ref instanceof String)) {
- String s = ((com.google.protobuf.ByteString) ref).toStringUtf8();
- contentType_ = s;
- return s;
- } else {
- return (String) ref;
- }
- }
- public Builder setContentType(String value) {
- if (value == null) {
- throw new NullPointerException();
- }
- bitField0_ |= 0x00000001;
- contentType_ = value;
- onChanged();
- return this;
- }
- public Builder clearContentType() {
- bitField0_ = (bitField0_ & ~0x00000001);
- contentType_ = getDefaultInstance().getContentType();
- onChanged();
- return this;
- }
- void setContentType(com.google.protobuf.ByteString value) {
- bitField0_ |= 0x00000001;
- contentType_ = value;
- onChanged();
- }
-
- // optional string key = 2;
- private java.lang.Object key_ = "";
- public boolean hasKey() {
- return ((bitField0_ & 0x00000002) == 0x00000002);
- }
- public String getKey() {
- java.lang.Object ref = key_;
- if (!(ref instanceof String)) {
- String s = ((com.google.protobuf.ByteString) ref).toStringUtf8();
- key_ = s;
- return s;
- } else {
- return (String) ref;
- }
- }
- public Builder setKey(String value) {
- if (value == null) {
- throw new NullPointerException();
- }
- bitField0_ |= 0x00000002;
- key_ = value;
- onChanged();
- return this;
- }
- public Builder clearKey() {
- bitField0_ = (bitField0_ & ~0x00000002);
- key_ = getDefaultInstance().getKey();
- onChanged();
- return this;
- }
- void setKey(com.google.protobuf.ByteString value) {
- bitField0_ |= 0x00000002;
- key_ = value;
- onChanged();
- }
-
- // @@protoc_insertion_point(builder_scope:textsecure.IncomingPushMessageSignal.AttachmentPointer)
- }
-
- static {
- defaultInstance = new AttachmentPointer(true);
- defaultInstance.initFields();
- }
-
- // @@protoc_insertion_point(class_scope:textsecure.IncomingPushMessageSignal.AttachmentPointer)
- }
-
private int bitField0_;
// optional uint32 type = 1;
public static final int TYPE_FIELD_NUMBER = 1;
@@ -596,54 +117,32 @@ public final class PushMessageProtos {
return destinations_.get(index);
}
- // optional bytes message = 4;
- public static final int MESSAGE_FIELD_NUMBER = 4;
- private com.google.protobuf.ByteString message_;
- public boolean hasMessage() {
- return ((bitField0_ & 0x00000004) == 0x00000004);
- }
- public com.google.protobuf.ByteString getMessage() {
- return message_;
- }
-
- // repeated .textsecure.IncomingPushMessageSignal.AttachmentPointer attachments = 5;
- public static final int ATTACHMENTS_FIELD_NUMBER = 5;
- private java.util.List attachments_;
- public java.util.List getAttachmentsList() {
- return attachments_;
- }
- public java.util.List extends org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder>
- getAttachmentsOrBuilderList() {
- return attachments_;
- }
- public int getAttachmentsCount() {
- return attachments_.size();
- }
- public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer getAttachments(int index) {
- return attachments_.get(index);
- }
- public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder getAttachmentsOrBuilder(
- int index) {
- return attachments_.get(index);
- }
-
- // optional uint64 timestamp = 6;
- public static final int TIMESTAMP_FIELD_NUMBER = 6;
+ // optional uint64 timestamp = 4;
+ public static final int TIMESTAMP_FIELD_NUMBER = 4;
private long timestamp_;
public boolean hasTimestamp() {
- return ((bitField0_ & 0x00000008) == 0x00000008);
+ return ((bitField0_ & 0x00000004) == 0x00000004);
}
public long getTimestamp() {
return timestamp_;
}
+ // optional bytes message = 5;
+ public static final int MESSAGE_FIELD_NUMBER = 5;
+ private com.google.protobuf.ByteString message_;
+ public boolean hasMessage() {
+ return ((bitField0_ & 0x00000008) == 0x00000008);
+ }
+ public com.google.protobuf.ByteString getMessage() {
+ return message_;
+ }
+
private void initFields() {
type_ = 0;
source_ = "";
destinations_ = com.google.protobuf.LazyStringArrayList.EMPTY;
- message_ = com.google.protobuf.ByteString.EMPTY;
- attachments_ = java.util.Collections.emptyList();
timestamp_ = 0L;
+ message_ = com.google.protobuf.ByteString.EMPTY;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@@ -667,13 +166,10 @@ public final class PushMessageProtos {
output.writeBytes(3, destinations_.getByteString(i));
}
if (((bitField0_ & 0x00000004) == 0x00000004)) {
- output.writeBytes(4, message_);
- }
- for (int i = 0; i < attachments_.size(); i++) {
- output.writeMessage(5, attachments_.get(i));
+ output.writeUInt64(4, timestamp_);
}
if (((bitField0_ & 0x00000008) == 0x00000008)) {
- output.writeUInt64(6, timestamp_);
+ output.writeBytes(5, message_);
}
getUnknownFields().writeTo(output);
}
@@ -703,15 +199,11 @@ public final class PushMessageProtos {
}
if (((bitField0_ & 0x00000004) == 0x00000004)) {
size += com.google.protobuf.CodedOutputStream
- .computeBytesSize(4, message_);
- }
- for (int i = 0; i < attachments_.size(); i++) {
- size += com.google.protobuf.CodedOutputStream
- .computeMessageSize(5, attachments_.get(i));
+ .computeUInt64Size(4, timestamp_);
}
if (((bitField0_ & 0x00000008) == 0x00000008)) {
size += com.google.protobuf.CodedOutputStream
- .computeUInt64Size(6, timestamp_);
+ .computeBytesSize(5, message_);
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
@@ -829,7 +321,6 @@ public final class PushMessageProtos {
}
private void maybeForceBuilderInitialization() {
if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
- getAttachmentsFieldBuilder();
}
}
private static Builder create() {
@@ -844,16 +335,10 @@ public final class PushMessageProtos {
bitField0_ = (bitField0_ & ~0x00000002);
destinations_ = com.google.protobuf.LazyStringArrayList.EMPTY;
bitField0_ = (bitField0_ & ~0x00000004);
- message_ = com.google.protobuf.ByteString.EMPTY;
- bitField0_ = (bitField0_ & ~0x00000008);
- if (attachmentsBuilder_ == null) {
- attachments_ = java.util.Collections.emptyList();
- bitField0_ = (bitField0_ & ~0x00000010);
- } else {
- attachmentsBuilder_.clear();
- }
timestamp_ = 0L;
- bitField0_ = (bitField0_ & ~0x00000020);
+ bitField0_ = (bitField0_ & ~0x00000008);
+ message_ = com.google.protobuf.ByteString.EMPTY;
+ bitField0_ = (bitField0_ & ~0x00000010);
return this;
}
@@ -909,20 +394,11 @@ public final class PushMessageProtos {
if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
to_bitField0_ |= 0x00000004;
}
- result.message_ = message_;
- if (attachmentsBuilder_ == null) {
- if (((bitField0_ & 0x00000010) == 0x00000010)) {
- attachments_ = java.util.Collections.unmodifiableList(attachments_);
- bitField0_ = (bitField0_ & ~0x00000010);
- }
- result.attachments_ = attachments_;
- } else {
- result.attachments_ = attachmentsBuilder_.build();
- }
- if (((from_bitField0_ & 0x00000020) == 0x00000020)) {
+ result.timestamp_ = timestamp_;
+ if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
to_bitField0_ |= 0x00000008;
}
- result.timestamp_ = timestamp_;
+ result.message_ = message_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@@ -955,38 +431,12 @@ public final class PushMessageProtos {
}
onChanged();
}
- if (other.hasMessage()) {
- setMessage(other.getMessage());
- }
- if (attachmentsBuilder_ == null) {
- if (!other.attachments_.isEmpty()) {
- if (attachments_.isEmpty()) {
- attachments_ = other.attachments_;
- bitField0_ = (bitField0_ & ~0x00000010);
- } else {
- ensureAttachmentsIsMutable();
- attachments_.addAll(other.attachments_);
- }
- onChanged();
- }
- } else {
- if (!other.attachments_.isEmpty()) {
- if (attachmentsBuilder_.isEmpty()) {
- attachmentsBuilder_.dispose();
- attachmentsBuilder_ = null;
- attachments_ = other.attachments_;
- bitField0_ = (bitField0_ & ~0x00000010);
- attachmentsBuilder_ =
- com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
- getAttachmentsFieldBuilder() : null;
- } else {
- attachmentsBuilder_.addAllMessages(other.attachments_);
- }
- }
- }
if (other.hasTimestamp()) {
setTimestamp(other.getTimestamp());
}
+ if (other.hasMessage()) {
+ setMessage(other.getMessage());
+ }
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
@@ -1033,20 +483,14 @@ public final class PushMessageProtos {
destinations_.add(input.readBytes());
break;
}
- case 34: {
+ case 32: {
bitField0_ |= 0x00000008;
- message_ = input.readBytes();
+ timestamp_ = input.readUInt64();
break;
}
case 42: {
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder subBuilder = org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.newBuilder();
- input.readMessage(subBuilder, extensionRegistry);
- addAttachments(subBuilder.buildPartial());
- break;
- }
- case 48: {
- bitField0_ |= 0x00000020;
- timestamp_ = input.readUInt64();
+ bitField0_ |= 0x00000010;
+ message_ = input.readBytes();
break;
}
}
@@ -1168,10 +612,31 @@ public final class PushMessageProtos {
onChanged();
}
- // optional bytes message = 4;
+ // optional uint64 timestamp = 4;
+ private long timestamp_ ;
+ public boolean hasTimestamp() {
+ return ((bitField0_ & 0x00000008) == 0x00000008);
+ }
+ public long getTimestamp() {
+ return timestamp_;
+ }
+ public Builder setTimestamp(long value) {
+ bitField0_ |= 0x00000008;
+ timestamp_ = value;
+ onChanged();
+ return this;
+ }
+ public Builder clearTimestamp() {
+ bitField0_ = (bitField0_ & ~0x00000008);
+ timestamp_ = 0L;
+ onChanged();
+ return this;
+ }
+
+ // optional bytes message = 5;
private com.google.protobuf.ByteString message_ = com.google.protobuf.ByteString.EMPTY;
public boolean hasMessage() {
- return ((bitField0_ & 0x00000008) == 0x00000008);
+ return ((bitField0_ & 0x00000010) == 0x00000010);
}
public com.google.protobuf.ByteString getMessage() {
return message_;
@@ -1180,32 +645,986 @@ public final class PushMessageProtos {
if (value == null) {
throw new NullPointerException();
}
- bitField0_ |= 0x00000008;
+ bitField0_ |= 0x00000010;
message_ = value;
onChanged();
return this;
}
public Builder clearMessage() {
- bitField0_ = (bitField0_ & ~0x00000008);
+ bitField0_ = (bitField0_ & ~0x00000010);
message_ = getDefaultInstance().getMessage();
onChanged();
return this;
}
- // repeated .textsecure.IncomingPushMessageSignal.AttachmentPointer attachments = 5;
- private java.util.List attachments_ =
+ // @@protoc_insertion_point(builder_scope:textsecure.IncomingPushMessageSignal)
+ }
+
+ static {
+ defaultInstance = new IncomingPushMessageSignal(true);
+ defaultInstance.initFields();
+ }
+
+ // @@protoc_insertion_point(class_scope:textsecure.IncomingPushMessageSignal)
+ }
+
+ public interface PushMessageContentOrBuilder
+ extends com.google.protobuf.MessageOrBuilder {
+
+ // optional string body = 1;
+ boolean hasBody();
+ String getBody();
+
+ // repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2;
+ java.util.List
+ getAttachmentsList();
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer getAttachments(int index);
+ int getAttachmentsCount();
+ java.util.List extends org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder>
+ getAttachmentsOrBuilderList();
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder getAttachmentsOrBuilder(
+ int index);
+ }
+ public static final class PushMessageContent extends
+ com.google.protobuf.GeneratedMessage
+ implements PushMessageContentOrBuilder {
+ // Use PushMessageContent.newBuilder() to construct.
+ private PushMessageContent(Builder builder) {
+ super(builder);
+ }
+ private PushMessageContent(boolean noInit) {}
+
+ private static final PushMessageContent defaultInstance;
+ public static PushMessageContent getDefaultInstance() {
+ return defaultInstance;
+ }
+
+ public PushMessageContent getDefaultInstanceForType() {
+ return defaultInstance;
+ }
+
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_fieldAccessorTable;
+ }
+
+ public interface AttachmentPointerOrBuilder
+ extends com.google.protobuf.MessageOrBuilder {
+
+ // optional fixed64 id = 1;
+ boolean hasId();
+ long getId();
+
+ // optional string contentType = 2;
+ boolean hasContentType();
+ String getContentType();
+
+ // optional bytes key = 3;
+ boolean hasKey();
+ com.google.protobuf.ByteString getKey();
+ }
+ public static final class AttachmentPointer extends
+ com.google.protobuf.GeneratedMessage
+ implements AttachmentPointerOrBuilder {
+ // Use AttachmentPointer.newBuilder() to construct.
+ private AttachmentPointer(Builder builder) {
+ super(builder);
+ }
+ private AttachmentPointer(boolean noInit) {}
+
+ private static final AttachmentPointer defaultInstance;
+ public static AttachmentPointer getDefaultInstance() {
+ return defaultInstance;
+ }
+
+ public AttachmentPointer getDefaultInstanceForType() {
+ return defaultInstance;
+ }
+
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_AttachmentPointer_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_AttachmentPointer_fieldAccessorTable;
+ }
+
+ private int bitField0_;
+ // optional fixed64 id = 1;
+ public static final int ID_FIELD_NUMBER = 1;
+ private long id_;
+ public boolean hasId() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ public long getId() {
+ return id_;
+ }
+
+ // optional string contentType = 2;
+ public static final int CONTENTTYPE_FIELD_NUMBER = 2;
+ private java.lang.Object contentType_;
+ public boolean hasContentType() {
+ return ((bitField0_ & 0x00000002) == 0x00000002);
+ }
+ public String getContentType() {
+ java.lang.Object ref = contentType_;
+ if (ref instanceof String) {
+ return (String) ref;
+ } else {
+ com.google.protobuf.ByteString bs =
+ (com.google.protobuf.ByteString) ref;
+ String s = bs.toStringUtf8();
+ if (com.google.protobuf.Internal.isValidUtf8(bs)) {
+ contentType_ = s;
+ }
+ return s;
+ }
+ }
+ private com.google.protobuf.ByteString getContentTypeBytes() {
+ java.lang.Object ref = contentType_;
+ if (ref instanceof String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8((String) ref);
+ contentType_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+
+ // optional bytes key = 3;
+ public static final int KEY_FIELD_NUMBER = 3;
+ private com.google.protobuf.ByteString key_;
+ public boolean hasKey() {
+ return ((bitField0_ & 0x00000004) == 0x00000004);
+ }
+ public com.google.protobuf.ByteString getKey() {
+ return key_;
+ }
+
+ private void initFields() {
+ id_ = 0L;
+ contentType_ = "";
+ key_ = com.google.protobuf.ByteString.EMPTY;
+ }
+ 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.writeFixed64(1, id_);
+ }
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ output.writeBytes(2, getContentTypeBytes());
+ }
+ if (((bitField0_ & 0x00000004) == 0x00000004)) {
+ output.writeBytes(3, key_);
+ }
+ 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
+ .computeFixed64Size(1, id_);
+ }
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeBytesSize(2, getContentTypeBytes());
+ }
+ if (((bitField0_ & 0x00000004) == 0x00000004)) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeBytesSize(3, key_);
+ }
+ 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.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return newBuilder().mergeFrom(data).buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return newBuilder().mergeFrom(data, extensionRegistry)
+ .buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom(byte[] data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return newBuilder().mergeFrom(data).buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom(
+ byte[] data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return newBuilder().mergeFrom(data, extensionRegistry)
+ .buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return newBuilder().mergeFrom(input).buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return newBuilder().mergeFrom(input, extensionRegistry)
+ .buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseDelimitedFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ Builder builder = newBuilder();
+ if (builder.mergeDelimitedFrom(input)) {
+ return builder.buildParsed();
+ } else {
+ return null;
+ }
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseDelimitedFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ Builder builder = newBuilder();
+ if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+ return builder.buildParsed();
+ } else {
+ return null;
+ }
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom(
+ com.google.protobuf.CodedInputStream input)
+ throws java.io.IOException {
+ return newBuilder().mergeFrom(input).buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return newBuilder().mergeFrom(input, extensionRegistry)
+ .buildParsed();
+ }
+
+ public static Builder newBuilder() { return Builder.create(); }
+ public Builder newBuilderForType() { return newBuilder(); }
+ public static Builder newBuilder(org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer 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;
+ }
+ public static final class Builder extends
+ com.google.protobuf.GeneratedMessage.Builder
+ implements org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_AttachmentPointer_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_AttachmentPointer_fieldAccessorTable;
+ }
+
+ // Construct using org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(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();
+ id_ = 0L;
+ bitField0_ = (bitField0_ & ~0x00000001);
+ contentType_ = "";
+ bitField0_ = (bitField0_ & ~0x00000002);
+ key_ = com.google.protobuf.ByteString.EMPTY;
+ bitField0_ = (bitField0_ & ~0x00000004);
+ return this;
+ }
+
+ public Builder clone() {
+ return create().mergeFrom(buildPartial());
+ }
+
+ public com.google.protobuf.Descriptors.Descriptor
+ getDescriptorForType() {
+ return org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDescriptor();
+ }
+
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer getDefaultInstanceForType() {
+ return org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance();
+ }
+
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer build() {
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ private org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer buildParsed()
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(
+ result).asInvalidProtocolBufferException();
+ }
+ return result;
+ }
+
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer buildPartial() {
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer result = new org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer(this);
+ int from_bitField0_ = bitField0_;
+ int to_bitField0_ = 0;
+ if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+ to_bitField0_ |= 0x00000001;
+ }
+ result.id_ = id_;
+ if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+ to_bitField0_ |= 0x00000002;
+ }
+ result.contentType_ = contentType_;
+ if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
+ to_bitField0_ |= 0x00000004;
+ }
+ result.key_ = key_;
+ result.bitField0_ = to_bitField0_;
+ onBuilt();
+ return result;
+ }
+
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer) {
+ return mergeFrom((org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer)other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer other) {
+ if (other == org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance()) return this;
+ if (other.hasId()) {
+ setId(other.getId());
+ }
+ if (other.hasContentType()) {
+ setContentType(other.getContentType());
+ }
+ if (other.hasKey()) {
+ setKey(other.getKey());
+ }
+ 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 {
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder(
+ this.getUnknownFields());
+ while (true) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ this.setUnknownFields(unknownFields.build());
+ onChanged();
+ return this;
+ default: {
+ if (!parseUnknownField(input, unknownFields,
+ extensionRegistry, tag)) {
+ this.setUnknownFields(unknownFields.build());
+ onChanged();
+ return this;
+ }
+ break;
+ }
+ case 9: {
+ bitField0_ |= 0x00000001;
+ id_ = input.readFixed64();
+ break;
+ }
+ case 18: {
+ bitField0_ |= 0x00000002;
+ contentType_ = input.readBytes();
+ break;
+ }
+ case 26: {
+ bitField0_ |= 0x00000004;
+ key_ = input.readBytes();
+ break;
+ }
+ }
+ }
+ }
+
+ private int bitField0_;
+
+ // optional fixed64 id = 1;
+ private long id_ ;
+ public boolean hasId() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ public long getId() {
+ return id_;
+ }
+ public Builder setId(long value) {
+ bitField0_ |= 0x00000001;
+ id_ = value;
+ onChanged();
+ return this;
+ }
+ public Builder clearId() {
+ bitField0_ = (bitField0_ & ~0x00000001);
+ id_ = 0L;
+ onChanged();
+ return this;
+ }
+
+ // optional string contentType = 2;
+ private java.lang.Object contentType_ = "";
+ public boolean hasContentType() {
+ return ((bitField0_ & 0x00000002) == 0x00000002);
+ }
+ public String getContentType() {
+ java.lang.Object ref = contentType_;
+ if (!(ref instanceof String)) {
+ String s = ((com.google.protobuf.ByteString) ref).toStringUtf8();
+ contentType_ = s;
+ return s;
+ } else {
+ return (String) ref;
+ }
+ }
+ public Builder setContentType(String value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000002;
+ contentType_ = value;
+ onChanged();
+ return this;
+ }
+ public Builder clearContentType() {
+ bitField0_ = (bitField0_ & ~0x00000002);
+ contentType_ = getDefaultInstance().getContentType();
+ onChanged();
+ return this;
+ }
+ void setContentType(com.google.protobuf.ByteString value) {
+ bitField0_ |= 0x00000002;
+ contentType_ = value;
+ onChanged();
+ }
+
+ // optional bytes key = 3;
+ private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY;
+ public boolean hasKey() {
+ return ((bitField0_ & 0x00000004) == 0x00000004);
+ }
+ public com.google.protobuf.ByteString getKey() {
+ return key_;
+ }
+ public Builder setKey(com.google.protobuf.ByteString value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000004;
+ key_ = value;
+ onChanged();
+ return this;
+ }
+ public Builder clearKey() {
+ bitField0_ = (bitField0_ & ~0x00000004);
+ key_ = getDefaultInstance().getKey();
+ onChanged();
+ return this;
+ }
+
+ // @@protoc_insertion_point(builder_scope:textsecure.PushMessageContent.AttachmentPointer)
+ }
+
+ static {
+ defaultInstance = new AttachmentPointer(true);
+ defaultInstance.initFields();
+ }
+
+ // @@protoc_insertion_point(class_scope:textsecure.PushMessageContent.AttachmentPointer)
+ }
+
+ private int bitField0_;
+ // optional string body = 1;
+ public static final int BODY_FIELD_NUMBER = 1;
+ private java.lang.Object body_;
+ public boolean hasBody() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ public String getBody() {
+ java.lang.Object ref = body_;
+ if (ref instanceof String) {
+ return (String) ref;
+ } else {
+ com.google.protobuf.ByteString bs =
+ (com.google.protobuf.ByteString) ref;
+ String s = bs.toStringUtf8();
+ if (com.google.protobuf.Internal.isValidUtf8(bs)) {
+ body_ = s;
+ }
+ return s;
+ }
+ }
+ private com.google.protobuf.ByteString getBodyBytes() {
+ java.lang.Object ref = body_;
+ if (ref instanceof String) {
+ com.google.protobuf.ByteString b =
+ com.google.protobuf.ByteString.copyFromUtf8((String) ref);
+ body_ = b;
+ return b;
+ } else {
+ return (com.google.protobuf.ByteString) ref;
+ }
+ }
+
+ // repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2;
+ public static final int ATTACHMENTS_FIELD_NUMBER = 2;
+ private java.util.List attachments_;
+ public java.util.List getAttachmentsList() {
+ return attachments_;
+ }
+ public java.util.List extends org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder>
+ getAttachmentsOrBuilderList() {
+ return attachments_;
+ }
+ public int getAttachmentsCount() {
+ return attachments_.size();
+ }
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer getAttachments(int index) {
+ return attachments_.get(index);
+ }
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder getAttachmentsOrBuilder(
+ int index) {
+ return attachments_.get(index);
+ }
+
+ private void initFields() {
+ body_ = "";
+ attachments_ = java.util.Collections.emptyList();
+ }
+ 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, getBodyBytes());
+ }
+ for (int i = 0; i < attachments_.size(); i++) {
+ output.writeMessage(2, attachments_.get(i));
+ }
+ 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, getBodyBytes());
+ }
+ for (int i = 0; i < attachments_.size(); i++) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeMessageSize(2, attachments_.get(i));
+ }
+ 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.push.PushMessageProtos.PushMessageContent parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return newBuilder().mergeFrom(data).buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return newBuilder().mergeFrom(data, extensionRegistry)
+ .buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent parseFrom(byte[] data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return newBuilder().mergeFrom(data).buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent parseFrom(
+ byte[] data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return newBuilder().mergeFrom(data, extensionRegistry)
+ .buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent parseFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return newBuilder().mergeFrom(input).buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent parseFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return newBuilder().mergeFrom(input, extensionRegistry)
+ .buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent parseDelimitedFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ Builder builder = newBuilder();
+ if (builder.mergeDelimitedFrom(input)) {
+ return builder.buildParsed();
+ } else {
+ return null;
+ }
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent parseDelimitedFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ Builder builder = newBuilder();
+ if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+ return builder.buildParsed();
+ } else {
+ return null;
+ }
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent parseFrom(
+ com.google.protobuf.CodedInputStream input)
+ throws java.io.IOException {
+ return newBuilder().mergeFrom(input).buildParsed();
+ }
+ public static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return newBuilder().mergeFrom(input, extensionRegistry)
+ .buildParsed();
+ }
+
+ public static Builder newBuilder() { return Builder.create(); }
+ public Builder newBuilderForType() { return newBuilder(); }
+ public static Builder newBuilder(org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent 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;
+ }
+ public static final class Builder extends
+ com.google.protobuf.GeneratedMessage.Builder
+ implements org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContentOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_descriptor;
+ }
+
+ protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return org.whispersystems.textsecure.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_fieldAccessorTable;
+ }
+
+ // Construct using org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+ getAttachmentsFieldBuilder();
+ }
+ }
+ private static Builder create() {
+ return new Builder();
+ }
+
+ public Builder clear() {
+ super.clear();
+ body_ = "";
+ bitField0_ = (bitField0_ & ~0x00000001);
+ if (attachmentsBuilder_ == null) {
+ attachments_ = java.util.Collections.emptyList();
+ bitField0_ = (bitField0_ & ~0x00000002);
+ } else {
+ attachmentsBuilder_.clear();
+ }
+ return this;
+ }
+
+ public Builder clone() {
+ return create().mergeFrom(buildPartial());
+ }
+
+ public com.google.protobuf.Descriptors.Descriptor
+ getDescriptorForType() {
+ return org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.getDescriptor();
+ }
+
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent getDefaultInstanceForType() {
+ return org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.getDefaultInstance();
+ }
+
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent build() {
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ private org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent buildParsed()
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(
+ result).asInvalidProtocolBufferException();
+ }
+ return result;
+ }
+
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent buildPartial() {
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent result = new org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent(this);
+ int from_bitField0_ = bitField0_;
+ int to_bitField0_ = 0;
+ if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+ to_bitField0_ |= 0x00000001;
+ }
+ result.body_ = body_;
+ if (attachmentsBuilder_ == null) {
+ if (((bitField0_ & 0x00000002) == 0x00000002)) {
+ attachments_ = java.util.Collections.unmodifiableList(attachments_);
+ bitField0_ = (bitField0_ & ~0x00000002);
+ }
+ result.attachments_ = attachments_;
+ } else {
+ result.attachments_ = attachmentsBuilder_.build();
+ }
+ result.bitField0_ = to_bitField0_;
+ onBuilt();
+ return result;
+ }
+
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent) {
+ return mergeFrom((org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent)other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent other) {
+ if (other == org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.getDefaultInstance()) return this;
+ if (other.hasBody()) {
+ setBody(other.getBody());
+ }
+ if (attachmentsBuilder_ == null) {
+ if (!other.attachments_.isEmpty()) {
+ if (attachments_.isEmpty()) {
+ attachments_ = other.attachments_;
+ bitField0_ = (bitField0_ & ~0x00000002);
+ } else {
+ ensureAttachmentsIsMutable();
+ attachments_.addAll(other.attachments_);
+ }
+ onChanged();
+ }
+ } else {
+ if (!other.attachments_.isEmpty()) {
+ if (attachmentsBuilder_.isEmpty()) {
+ attachmentsBuilder_.dispose();
+ attachmentsBuilder_ = null;
+ attachments_ = other.attachments_;
+ bitField0_ = (bitField0_ & ~0x00000002);
+ attachmentsBuilder_ =
+ com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
+ getAttachmentsFieldBuilder() : null;
+ } else {
+ attachmentsBuilder_.addAllMessages(other.attachments_);
+ }
+ }
+ }
+ 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 {
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder(
+ this.getUnknownFields());
+ while (true) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ this.setUnknownFields(unknownFields.build());
+ onChanged();
+ return this;
+ default: {
+ if (!parseUnknownField(input, unknownFields,
+ extensionRegistry, tag)) {
+ this.setUnknownFields(unknownFields.build());
+ onChanged();
+ return this;
+ }
+ break;
+ }
+ case 10: {
+ bitField0_ |= 0x00000001;
+ body_ = input.readBytes();
+ break;
+ }
+ case 18: {
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder subBuilder = org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.newBuilder();
+ input.readMessage(subBuilder, extensionRegistry);
+ addAttachments(subBuilder.buildPartial());
+ break;
+ }
+ }
+ }
+ }
+
+ private int bitField0_;
+
+ // optional string body = 1;
+ private java.lang.Object body_ = "";
+ public boolean hasBody() {
+ return ((bitField0_ & 0x00000001) == 0x00000001);
+ }
+ public String getBody() {
+ java.lang.Object ref = body_;
+ if (!(ref instanceof String)) {
+ String s = ((com.google.protobuf.ByteString) ref).toStringUtf8();
+ body_ = s;
+ return s;
+ } else {
+ return (String) ref;
+ }
+ }
+ public Builder setBody(String value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000001;
+ body_ = value;
+ onChanged();
+ return this;
+ }
+ public Builder clearBody() {
+ bitField0_ = (bitField0_ & ~0x00000001);
+ body_ = getDefaultInstance().getBody();
+ onChanged();
+ return this;
+ }
+ void setBody(com.google.protobuf.ByteString value) {
+ bitField0_ |= 0x00000001;
+ body_ = value;
+ onChanged();
+ }
+
+ // repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2;
+ private java.util.List attachments_ =
java.util.Collections.emptyList();
private void ensureAttachmentsIsMutable() {
- if (!((bitField0_ & 0x00000010) == 0x00000010)) {
- attachments_ = new java.util.ArrayList(attachments_);
- bitField0_ |= 0x00000010;
+ if (!((bitField0_ & 0x00000002) == 0x00000002)) {
+ attachments_ = new java.util.ArrayList(attachments_);
+ bitField0_ |= 0x00000002;
}
}
private com.google.protobuf.RepeatedFieldBuilder<
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder> attachmentsBuilder_;
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer, org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder, org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder> attachmentsBuilder_;
- public java.util.List getAttachmentsList() {
+ public java.util.List getAttachmentsList() {
if (attachmentsBuilder_ == null) {
return java.util.Collections.unmodifiableList(attachments_);
} else {
@@ -1219,7 +1638,7 @@ public final class PushMessageProtos {
return attachmentsBuilder_.getCount();
}
}
- public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer getAttachments(int index) {
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer getAttachments(int index) {
if (attachmentsBuilder_ == null) {
return attachments_.get(index);
} else {
@@ -1227,7 +1646,7 @@ public final class PushMessageProtos {
}
}
public Builder setAttachments(
- int index, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer value) {
+ int index, org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer value) {
if (attachmentsBuilder_ == null) {
if (value == null) {
throw new NullPointerException();
@@ -1241,7 +1660,7 @@ public final class PushMessageProtos {
return this;
}
public Builder setAttachments(
- int index, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder builderForValue) {
+ int index, org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder builderForValue) {
if (attachmentsBuilder_ == null) {
ensureAttachmentsIsMutable();
attachments_.set(index, builderForValue.build());
@@ -1251,7 +1670,7 @@ public final class PushMessageProtos {
}
return this;
}
- public Builder addAttachments(org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer value) {
+ public Builder addAttachments(org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer value) {
if (attachmentsBuilder_ == null) {
if (value == null) {
throw new NullPointerException();
@@ -1265,7 +1684,7 @@ public final class PushMessageProtos {
return this;
}
public Builder addAttachments(
- int index, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer value) {
+ int index, org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer value) {
if (attachmentsBuilder_ == null) {
if (value == null) {
throw new NullPointerException();
@@ -1279,7 +1698,7 @@ public final class PushMessageProtos {
return this;
}
public Builder addAttachments(
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder builderForValue) {
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder builderForValue) {
if (attachmentsBuilder_ == null) {
ensureAttachmentsIsMutable();
attachments_.add(builderForValue.build());
@@ -1290,7 +1709,7 @@ public final class PushMessageProtos {
return this;
}
public Builder addAttachments(
- int index, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder builderForValue) {
+ int index, org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder builderForValue) {
if (attachmentsBuilder_ == null) {
ensureAttachmentsIsMutable();
attachments_.add(index, builderForValue.build());
@@ -1301,7 +1720,7 @@ public final class PushMessageProtos {
return this;
}
public Builder addAllAttachments(
- java.lang.Iterable extends org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer> values) {
+ java.lang.Iterable extends org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer> values) {
if (attachmentsBuilder_ == null) {
ensureAttachmentsIsMutable();
super.addAll(values, attachments_);
@@ -1314,7 +1733,7 @@ public final class PushMessageProtos {
public Builder clearAttachments() {
if (attachmentsBuilder_ == null) {
attachments_ = java.util.Collections.emptyList();
- bitField0_ = (bitField0_ & ~0x00000010);
+ bitField0_ = (bitField0_ & ~0x00000002);
onChanged();
} else {
attachmentsBuilder_.clear();
@@ -1331,18 +1750,18 @@ public final class PushMessageProtos {
}
return this;
}
- public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder getAttachmentsBuilder(
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder getAttachmentsBuilder(
int index) {
return getAttachmentsFieldBuilder().getBuilder(index);
}
- public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder getAttachmentsOrBuilder(
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder getAttachmentsOrBuilder(
int index) {
if (attachmentsBuilder_ == null) {
return attachments_.get(index); } else {
return attachmentsBuilder_.getMessageOrBuilder(index);
}
}
- public java.util.List extends org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder>
+ public java.util.List extends org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder>
getAttachmentsOrBuilderList() {
if (attachmentsBuilder_ != null) {
return attachmentsBuilder_.getMessageOrBuilderList();
@@ -1350,27 +1769,27 @@ public final class PushMessageProtos {
return java.util.Collections.unmodifiableList(attachments_);
}
}
- public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder addAttachmentsBuilder() {
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder addAttachmentsBuilder() {
return getAttachmentsFieldBuilder().addBuilder(
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.getDefaultInstance());
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance());
}
- public org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder addAttachmentsBuilder(
+ public org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder addAttachmentsBuilder(
int index) {
return getAttachmentsFieldBuilder().addBuilder(
- index, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.getDefaultInstance());
+ index, org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance());
}
- public java.util.List
+ public java.util.List
getAttachmentsBuilderList() {
return getAttachmentsFieldBuilder().getBuilderList();
}
private com.google.protobuf.RepeatedFieldBuilder<
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder>
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer, org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder, org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder>
getAttachmentsFieldBuilder() {
if (attachmentsBuilder_ == null) {
attachmentsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder, org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointerOrBuilder>(
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer, org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder, org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder>(
attachments_,
- ((bitField0_ & 0x00000010) == 0x00000010),
+ ((bitField0_ & 0x00000002) == 0x00000002),
getParentForChildren(),
isClean());
attachments_ = null;
@@ -1378,36 +1797,15 @@ public final class PushMessageProtos {
return attachmentsBuilder_;
}
- // optional uint64 timestamp = 6;
- private long timestamp_ ;
- public boolean hasTimestamp() {
- return ((bitField0_ & 0x00000020) == 0x00000020);
- }
- public long getTimestamp() {
- return timestamp_;
- }
- public Builder setTimestamp(long value) {
- bitField0_ |= 0x00000020;
- timestamp_ = value;
- onChanged();
- return this;
- }
- public Builder clearTimestamp() {
- bitField0_ = (bitField0_ & ~0x00000020);
- timestamp_ = 0L;
- onChanged();
- return this;
- }
-
- // @@protoc_insertion_point(builder_scope:textsecure.IncomingPushMessageSignal)
+ // @@protoc_insertion_point(builder_scope:textsecure.PushMessageContent)
}
static {
- defaultInstance = new IncomingPushMessageSignal(true);
+ defaultInstance = new PushMessageContent(true);
defaultInstance.initFields();
}
- // @@protoc_insertion_point(class_scope:textsecure.IncomingPushMessageSignal)
+ // @@protoc_insertion_point(class_scope:textsecure.PushMessageContent)
}
private static com.google.protobuf.Descriptors.Descriptor
@@ -1416,10 +1814,15 @@ public final class PushMessageProtos {
com.google.protobuf.GeneratedMessage.FieldAccessorTable
internal_static_textsecure_IncomingPushMessageSignal_fieldAccessorTable;
private static com.google.protobuf.Descriptors.Descriptor
- internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_descriptor;
+ internal_static_textsecure_PushMessageContent_descriptor;
private static
com.google.protobuf.GeneratedMessage.FieldAccessorTable
- internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_fieldAccessorTable;
+ internal_static_textsecure_PushMessageContent_fieldAccessorTable;
+ private static com.google.protobuf.Descriptors.Descriptor
+ internal_static_textsecure_PushMessageContent_AttachmentPointer_descriptor;
+ private static
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable
+ internal_static_textsecure_PushMessageContent_AttachmentPointer_fieldAccessorTable;
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
@@ -1430,14 +1833,15 @@ public final class PushMessageProtos {
static {
java.lang.String[] descriptorData = {
"\n\037IncomingPushMessageSignal.proto\022\ntexts" +
- "ecure\"\370\001\n\031IncomingPushMessageSignal\022\014\n\004t" +
- "ype\030\001 \001(\r\022\016\n\006source\030\002 \001(\t\022\024\n\014destination" +
- "s\030\003 \003(\t\022\017\n\007message\030\004 \001(\014\022L\n\013attachments\030" +
- "\005 \003(\01327.textsecure.IncomingPushMessageSi" +
- "gnal.AttachmentPointer\022\021\n\ttimestamp\030\006 \001(" +
- "\004\0325\n\021AttachmentPointer\022\023\n\013contentType\030\001 " +
- "\001(\t\022\013\n\003key\030\002 \001(\tB7\n\"org.whispersystems.t" +
- "extsecure.pushB\021PushMessageProtos"
+ "ecure\"s\n\031IncomingPushMessageSignal\022\014\n\004ty" +
+ "pe\030\001 \001(\r\022\016\n\006source\030\002 \001(\t\022\024\n\014destinations" +
+ "\030\003 \003(\t\022\021\n\ttimestamp\030\004 \001(\004\022\017\n\007message\030\005 \001" +
+ "(\014\"\254\001\n\022PushMessageContent\022\014\n\004body\030\001 \001(\t\022" +
+ "E\n\013attachments\030\002 \003(\01320.textsecure.PushMe" +
+ "ssageContent.AttachmentPointer\032A\n\021Attach" +
+ "mentPointer\022\n\n\002id\030\001 \001(\006\022\023\n\013contentType\030\002" +
+ " \001(\t\022\013\n\003key\030\003 \001(\014B7\n\"org.whispersystems." +
+ "textsecure.pushB\021PushMessageProtos"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@@ -1449,17 +1853,25 @@ public final class PushMessageProtos {
internal_static_textsecure_IncomingPushMessageSignal_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_textsecure_IncomingPushMessageSignal_descriptor,
- new java.lang.String[] { "Type", "Source", "Destinations", "Message", "Attachments", "Timestamp", },
+ new java.lang.String[] { "Type", "Source", "Destinations", "Timestamp", "Message", },
org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.class,
org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.Builder.class);
- internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_descriptor =
- internal_static_textsecure_IncomingPushMessageSignal_descriptor.getNestedTypes().get(0);
- internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_fieldAccessorTable = new
+ internal_static_textsecure_PushMessageContent_descriptor =
+ getDescriptor().getMessageTypes().get(1);
+ internal_static_textsecure_PushMessageContent_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
- internal_static_textsecure_IncomingPushMessageSignal_AttachmentPointer_descriptor,
- new java.lang.String[] { "ContentType", "Key", },
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.class,
- org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal.AttachmentPointer.Builder.class);
+ internal_static_textsecure_PushMessageContent_descriptor,
+ new java.lang.String[] { "Body", "Attachments", },
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.class,
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.Builder.class);
+ internal_static_textsecure_PushMessageContent_AttachmentPointer_descriptor =
+ internal_static_textsecure_PushMessageContent_descriptor.getNestedTypes().get(0);
+ internal_static_textsecure_PushMessageContent_AttachmentPointer_fieldAccessorTable = new
+ com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+ internal_static_textsecure_PushMessageContent_AttachmentPointer_descriptor,
+ new java.lang.String[] { "Id", "ContentType", "Key", },
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.class,
+ org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder.class);
return null;
}
};
diff --git a/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java b/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java
index acde1f67c1..bee4cd9235 100644
--- a/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java
+++ b/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java
@@ -84,31 +84,21 @@ public class PushServiceSocket {
sendMessage(new OutgoingPushMessageList(message));
}
- public void sendMessage(List recipients, List bodies,
- List> attachmentsList, int type)
+ public void sendMessage(List recipients, List bodies, List types)
throws IOException
{
List messages = new LinkedList();
- Iterator recipientsIterator = recipients.iterator();
- Iterator bodiesIterator = bodies.iterator();
- Iterator> attachmentsIterator = attachmentsList.iterator();
+ Iterator recipientsIterator = recipients.iterator();
+ Iterator bodiesIterator = bodies.iterator();
+ Iterator typesIterator = types.iterator();
while (recipientsIterator.hasNext()) {
- String recipient = recipientsIterator.next();
- byte[] body = bodiesIterator.next();
- List attachments = attachmentsIterator.next();
+ String recipient = recipientsIterator.next();
+ byte[] body = bodiesIterator.next();
+ int type = typesIterator.next();
- OutgoingPushMessage message;
-
- if (!attachments.isEmpty()) {
- List attachmentIds = sendAttachments(attachments);
- message = new OutgoingPushMessage(recipient, body, attachmentIds, type);
- } else {
- message = new OutgoingPushMessage(recipient, body, type);
- }
-
- messages.add(message);
+ messages.add(new OutgoingPushMessage(recipient, body, type));
}
sendMessage(new OutgoingPushMessageList(messages));
@@ -149,20 +139,7 @@ public class PushServiceSocket {
return PreKeyEntity.fromJson(responseText);
}
- private List sendAttachments(List attachments)
- throws IOException
- {
- List attachmentIds = new LinkedList();
-
- for (PushAttachmentData attachment : attachments) {
- attachmentIds.add(new PushAttachmentPointer(attachment.getContentType(),
- sendAttachment(attachment)));
- }
-
- return attachmentIds;
- }
-
- private String sendAttachment(PushAttachmentData attachment) throws IOException {
+ public long sendAttachment(PushAttachmentData attachment) throws IOException {
Pair response = makeRequestForResponseHeader(String.format(ATTACHMENT_PATH, ""),
"GET", null, "Content-Location");
@@ -178,25 +155,18 @@ public class PushServiceSocket {
return new Gson().fromJson(response.second, AttachmentKey.class).getId();
}
- public List> retrieveAttachments(List attachmentIds)
- throws IOException
- {
- List> attachments = new LinkedList>();
+ public File retrieveAttachment(long attachmentId) throws IOException {
+ Pair response = makeRequestForResponseHeader(String.format(ATTACHMENT_PATH, String.valueOf(attachmentId)),
+ "GET", null, "Content-Location");
- for (PushAttachmentPointer attachmentId : attachmentIds) {
- Pair response = makeRequestForResponseHeader(String.format(ATTACHMENT_PATH, attachmentId.getKey()),
- "GET", null, "Content-Location");
+ Log.w("PushServiceSocket", "Attachment: " + attachmentId + " is at: " + response.first);
- Log.w("PushServiceSocket", "Attachment: " + attachmentId.getKey() + " is at: " + response.first);
+ File attachment = File.createTempFile("attachment", ".tmp", context.getFilesDir());
+ attachment.deleteOnExit();
- File attachment = File.createTempFile("attachment", ".tmp", context.getFilesDir());
- attachment.deleteOnExit();
+ downloadExternalFile(response.first, attachment);
- downloadExternalFile(response.first, attachment);
- attachments.add(new Pair(attachment, attachmentId.getContentType()));
- }
-
- return attachments;
+ return attachment;
}
public Pair retrieveDirectory() {
@@ -394,13 +364,13 @@ public class PushServiceSocket {
}
private static class AttachmentKey {
- private String id;
+ private long id;
- public AttachmentKey(String id) {
+ public AttachmentKey(long id) {
this.id = id;
}
- public String getId() {
+ public long getId() {
return id;
}
}
diff --git a/library/src/org/whispersystems/textsecure/util/Util.java b/library/src/org/whispersystems/textsecure/util/Util.java
index 379c250000..800050c631 100644
--- a/library/src/org/whispersystems/textsecure/util/Util.java
+++ b/library/src/org/whispersystems/textsecure/util/Util.java
@@ -45,6 +45,33 @@ public class Util {
}
+ public static byte[][] split(byte[] input, int firstLength, int secondLength) {
+ byte[][] parts = new byte[2][];
+
+ parts[0] = new byte[firstLength];
+ System.arraycopy(input, 0, parts[0], 0, firstLength);
+
+ parts[1] = new byte[secondLength];
+ System.arraycopy(input, firstLength, parts[1], 0, secondLength);
+
+ return parts;
+ }
+
+ public static byte[][] split(byte[] input, int firstLength, int secondLength, int thirdLength) {
+ byte[][] parts = new byte[3][];
+
+ parts[0] = new byte[firstLength];
+ System.arraycopy(input, 0, parts[0], 0, firstLength);
+
+ parts[1] = new byte[secondLength];
+ System.arraycopy(input, firstLength, parts[1], 0, secondLength);
+
+ parts[2] = new byte[thirdLength];
+ System.arraycopy(input, firstLength + secondLength, parts[2], 0, thirdLength);
+
+ return parts;
+ }
+
public static boolean isEmpty(String value) {
return value == null || value.trim().length() == 0;
}
@@ -94,6 +121,18 @@ public class Util {
return new String(bout.toByteArray());
}
+ public static void readFully(InputStream in, byte[] buffer) throws IOException {
+ int offset = 0;
+
+ for (;;) {
+ int read = in.read(buffer, offset, buffer.length - offset);
+
+ if (read + offset < buffer.length) offset += read;
+ else return;
+ }
+ }
+
+
public static void copy(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[4096];
int read;
diff --git a/res/drawable-hdpi/stat_sys_download_anim0.png b/res/drawable-hdpi/stat_sys_download_anim0.png
new file mode 100644
index 0000000000..72d5f3fcb2
Binary files /dev/null and b/res/drawable-hdpi/stat_sys_download_anim0.png differ
diff --git a/res/drawable-hdpi/stat_sys_download_anim1.png b/res/drawable-hdpi/stat_sys_download_anim1.png
new file mode 100644
index 0000000000..e7ba0a5d0a
Binary files /dev/null and b/res/drawable-hdpi/stat_sys_download_anim1.png differ
diff --git a/res/drawable-hdpi/stat_sys_download_anim2.png b/res/drawable-hdpi/stat_sys_download_anim2.png
new file mode 100644
index 0000000000..972e50428f
Binary files /dev/null and b/res/drawable-hdpi/stat_sys_download_anim2.png differ
diff --git a/res/drawable-hdpi/stat_sys_download_anim3.png b/res/drawable-hdpi/stat_sys_download_anim3.png
new file mode 100644
index 0000000000..5a8d9d0d01
Binary files /dev/null and b/res/drawable-hdpi/stat_sys_download_anim3.png differ
diff --git a/res/drawable-hdpi/stat_sys_download_anim4.png b/res/drawable-hdpi/stat_sys_download_anim4.png
new file mode 100644
index 0000000000..98796a2606
Binary files /dev/null and b/res/drawable-hdpi/stat_sys_download_anim4.png differ
diff --git a/res/drawable-hdpi/stat_sys_download_anim5.png b/res/drawable-hdpi/stat_sys_download_anim5.png
new file mode 100644
index 0000000000..b82a689e07
Binary files /dev/null and b/res/drawable-hdpi/stat_sys_download_anim5.png differ
diff --git a/res/drawable-mdpi/stat_sys_download_anim0.png b/res/drawable-mdpi/stat_sys_download_anim0.png
new file mode 100644
index 0000000000..b14d0f671d
Binary files /dev/null and b/res/drawable-mdpi/stat_sys_download_anim0.png differ
diff --git a/res/drawable-mdpi/stat_sys_download_anim1.png b/res/drawable-mdpi/stat_sys_download_anim1.png
new file mode 100644
index 0000000000..7e002201a7
Binary files /dev/null and b/res/drawable-mdpi/stat_sys_download_anim1.png differ
diff --git a/res/drawable-mdpi/stat_sys_download_anim2.png b/res/drawable-mdpi/stat_sys_download_anim2.png
new file mode 100644
index 0000000000..941839935a
Binary files /dev/null and b/res/drawable-mdpi/stat_sys_download_anim2.png differ
diff --git a/res/drawable-mdpi/stat_sys_download_anim3.png b/res/drawable-mdpi/stat_sys_download_anim3.png
new file mode 100644
index 0000000000..84a9362a47
Binary files /dev/null and b/res/drawable-mdpi/stat_sys_download_anim3.png differ
diff --git a/res/drawable-mdpi/stat_sys_download_anim4.png b/res/drawable-mdpi/stat_sys_download_anim4.png
new file mode 100644
index 0000000000..8fdc8716d6
Binary files /dev/null and b/res/drawable-mdpi/stat_sys_download_anim4.png differ
diff --git a/res/drawable-mdpi/stat_sys_download_anim5.png b/res/drawable-mdpi/stat_sys_download_anim5.png
new file mode 100644
index 0000000000..6b64abd82b
Binary files /dev/null and b/res/drawable-mdpi/stat_sys_download_anim5.png differ
diff --git a/res/drawable-xhdpi/stat_sys_download_anim0.png b/res/drawable-xhdpi/stat_sys_download_anim0.png
new file mode 100644
index 0000000000..f465442a89
Binary files /dev/null and b/res/drawable-xhdpi/stat_sys_download_anim0.png differ
diff --git a/res/drawable-xhdpi/stat_sys_download_anim1.png b/res/drawable-xhdpi/stat_sys_download_anim1.png
new file mode 100644
index 0000000000..15d1a487be
Binary files /dev/null and b/res/drawable-xhdpi/stat_sys_download_anim1.png differ
diff --git a/res/drawable-xhdpi/stat_sys_download_anim2.png b/res/drawable-xhdpi/stat_sys_download_anim2.png
new file mode 100644
index 0000000000..3b47b47d7e
Binary files /dev/null and b/res/drawable-xhdpi/stat_sys_download_anim2.png differ
diff --git a/res/drawable-xhdpi/stat_sys_download_anim3.png b/res/drawable-xhdpi/stat_sys_download_anim3.png
new file mode 100644
index 0000000000..ecc2eef5d7
Binary files /dev/null and b/res/drawable-xhdpi/stat_sys_download_anim3.png differ
diff --git a/res/drawable-xhdpi/stat_sys_download_anim4.png b/res/drawable-xhdpi/stat_sys_download_anim4.png
new file mode 100644
index 0000000000..1014804a86
Binary files /dev/null and b/res/drawable-xhdpi/stat_sys_download_anim4.png differ
diff --git a/res/drawable-xhdpi/stat_sys_download_anim5.png b/res/drawable-xhdpi/stat_sys_download_anim5.png
new file mode 100644
index 0000000000..e3d4730ee7
Binary files /dev/null and b/res/drawable-xhdpi/stat_sys_download_anim5.png differ
diff --git a/res/drawable/stat_sys_download.xml b/res/drawable/stat_sys_download.xml
new file mode 100644
index 0000000000..77ecf8588b
--- /dev/null
+++ b/res/drawable/stat_sys_download.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java b/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java
index 0be4d8a767..6bbefa0406 100644
--- a/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java
+++ b/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java
@@ -17,6 +17,7 @@
package org.thoughtcrime.securesms.crypto;
import android.content.Context;
+import android.content.Intent;
import android.database.Cursor;
import android.util.Log;
@@ -33,22 +34,26 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
+import org.thoughtcrime.securesms.service.PushDownloader;
+import org.thoughtcrime.securesms.service.PushReceiver;
+import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.sms.SmsTransportDetails;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.IdentityKeyPair;
import org.whispersystems.textsecure.crypto.InvalidKeyException;
import org.whispersystems.textsecure.crypto.InvalidMessageException;
import org.whispersystems.textsecure.crypto.InvalidVersionException;
-import org.whispersystems.textsecure.crypto.MessageCipher;
-import org.whispersystems.textsecure.crypto.SessionCipher;
-import org.whispersystems.textsecure.util.Hex;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.thoughtcrime.securesms.util.WorkerThread;
import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
+import org.whispersystems.textsecure.crypto.MessageCipher;
+import org.whispersystems.textsecure.crypto.SessionCipher;
+import org.whispersystems.textsecure.push.IncomingPushMessage;
+import org.whispersystems.textsecure.push.PushTransportDetails;
+import org.whispersystems.textsecure.util.Hex;
import java.io.IOException;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.MmsException;
@@ -64,21 +69,13 @@ import ws.com.google.android.mms.pdu.RetrieveConf;
public class DecryptingQueue {
- private static final List workQueue = new LinkedList();
-
- static {
- Thread workerThread = new WorkerThread(workQueue, "Async Decryption Thread");
- workerThread.start();
- }
+ private static final Executor executor = Executors.newSingleThreadExecutor();
public static void scheduleDecryption(Context context, MasterSecret masterSecret,
long messageId, long threadId, MultimediaMessagePdu mms)
{
MmsDecryptionItem runnable = new MmsDecryptionItem(context, masterSecret, messageId, threadId, mms);
- synchronized (workQueue) {
- workQueue.add(runnable);
- workQueue.notifyAll();
- }
+ executor.execute(runnable);
}
public static void scheduleDecryption(Context context, MasterSecret masterSecret,
@@ -87,10 +84,15 @@ public class DecryptingQueue {
{
DecryptionWorkItem runnable = new DecryptionWorkItem(context, masterSecret, messageId, threadId,
originator, body, isSecureMessage, isKeyExchange);
- synchronized (workQueue) {
- workQueue.add(runnable);
- workQueue.notifyAll();
- }
+ executor.execute(runnable);
+ }
+
+ public static void scheduleDecryption(Context context, MasterSecret masterSecret,
+ long messageId, IncomingPushMessage message)
+ {
+ PushDecryptionWorkItem runnable = new PushDecryptionWorkItem(context, masterSecret,
+ messageId, message);
+ executor.execute(runnable);
}
public static void schedulePendingDecrypts(Context context, MasterSecret masterSecret) {
@@ -143,6 +145,59 @@ public class DecryptingQueue {
originator, body, isSecureMessage, isKeyExchange);
}
+ private static class PushDecryptionWorkItem implements Runnable {
+
+ private Context context;
+ private MasterSecret masterSecret;
+ private long messageId;
+ private IncomingPushMessage message;
+
+ public PushDecryptionWorkItem(Context context, MasterSecret masterSecret,
+ long messageId, IncomingPushMessage message)
+ {
+ this.context = context;
+ this.masterSecret = masterSecret;
+ this.messageId = messageId;
+ this.message = message;
+ }
+
+ public void run() {
+ synchronized (SessionCipher.CIPHER_LOCK) {
+ try {
+ Recipients recipients = RecipientFactory.getRecipientsFromString(context, message.getSource(), false);
+ Recipient recipient = recipients.getPrimaryRecipient();
+
+ if (!KeyUtil.isSessionFor(context, recipient)) {
+ sendResult(PushReceiver.RESULT_NO_SESSION);
+ return;
+ }
+
+ IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
+ MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKey, new PushTransportDetails());
+
+ byte[] plaintextBody = messageCipher.decrypt(recipient, message.getBody());
+ message = message.withBody(plaintextBody);
+ sendResult(PushReceiver.RESULT_OK);
+ } catch (InvalidMessageException e) {
+ Log.w("DecryptionQueue", e);
+ sendResult(PushReceiver.RESULT_DECRYPT_FAILED);
+ } catch (RecipientFormattingException e) {
+ Log.w("DecryptionQueue", e);
+ sendResult(PushReceiver.RESULT_DECRYPT_FAILED);
+ }
+ }
+ }
+
+ private void sendResult(int result) {
+ Intent intent = new Intent(context, SendReceiveService.class);
+ intent.setAction(SendReceiveService.DECRYPTED_PUSH_ACTION);
+ intent.putExtra("message", message);
+ intent.putExtra("message_id", messageId);
+ intent.putExtra("result", result);
+ context.startService(intent);
+ }
+ }
+
private static class MmsDecryptionItem implements Runnable {
private long messageId;
private long threadId;
@@ -267,13 +322,10 @@ public class DecryptingQueue {
synchronized (SessionCipher.CIPHER_LOCK) {
try {
- Log.w("DecryptingQueue", "Parsing recipient for originator: " + originator);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, originator, false);
Recipient recipient = recipients.getPrimaryRecipient();
- Log.w("DecryptingQueue", "Parsed Recipient: " + recipient.getNumber());
if (!KeyUtil.isSessionFor(context, recipient)) {
- Log.w("DecryptingQueue", "No such recipient session...");
database.markAsNoSession(messageId);
return;
}
diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
index 4ef1cb5be4..3315f7ada0 100644
--- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
+++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
@@ -51,7 +51,8 @@ public class DatabaseFactory {
private static final int INTRODUCED_MMS_BODY_VERSION = 7;
private static final int INTRODUCED_MMS_FROM_VERSION = 8;
private static final int INTRODUCED_TOFU_IDENTITY_VERSION = 9;
- private static final int DATABASE_VERSION = 9;
+ private static final int INTRODUCED_PUSH_DATABASE_VERSION = 10;
+ private static final int DATABASE_VERSION = 10;
private static final String DATABASE_NAME = "messages.db";
private static final Object lock = new Object();
@@ -71,6 +72,7 @@ public class DatabaseFactory {
private final MmsSmsDatabase mmsSmsDatabase;
private final IdentityDatabase identityDatabase;
private final DraftDatabase draftDatabase;
+ private final PushDatabase pushDatabase;
public static DatabaseFactory getInstance(Context context) {
synchronized (lock) {
@@ -132,6 +134,10 @@ public class DatabaseFactory {
return getInstance(context).draftDatabase;
}
+ public static PushDatabase getPushDatabase(Context context) {
+ return getInstance(context).pushDatabase;
+ }
+
private DatabaseFactory(Context context) {
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
this.sms = new SmsDatabase(context, databaseHelper);
@@ -144,6 +150,7 @@ public class DatabaseFactory {
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
this.draftDatabase = new DraftDatabase(context, databaseHelper);
+ this.pushDatabase = new PushDatabase(context, databaseHelper);
}
public void reset(Context context) {
@@ -425,6 +432,7 @@ public class DatabaseFactory {
db.execSQL(MmsAddressDatabase.CREATE_TABLE);
db.execSQL(IdentityDatabase.CREATE_TABLE);
db.execSQL(DraftDatabase.CREATE_TABLE);
+ db.execSQL(PushDatabase.CREATE_TABLE);
executeStatements(db, SmsDatabase.CREATE_INDEXS);
executeStatements(db, MmsDatabase.CREATE_INDEXS);
@@ -617,6 +625,12 @@ public class DatabaseFactory {
db.execSQL("CREATE TABLE identities (_id INTEGER PRIMARY KEY, recipient INTEGER UNIQUE, key TEXT, mac TEXT);");
}
+ if (oldVersion < INTRODUCED_PUSH_DATABASE_VERSION) {
+ db.execSQL("CREATE TABLE push (_id INTEGER PRIMARY KEY, type INTEGER, source TEXT, destinations TEXT, body TEXT, TIMESTAMP INTEGER);");
+ db.execSQL("ALTER TABLE part ADD COLUMN pending_push INTEGER;");
+ db.execSQL("CREATE INDEX IF NOT EXISTS pending_push_index ON parts (pending_push);");
+ }
+
db.setTransactionSuccessful();
db.endTransaction();
}
diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java
index 7d6b484d2c..0df05ae1f0 100644
--- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java
@@ -51,6 +51,7 @@ import java.io.UnsupportedEncodingException;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
@@ -63,6 +64,7 @@ import ws.com.google.android.mms.pdu.EncodedStringValue;
import ws.com.google.android.mms.pdu.NotificationInd;
import ws.com.google.android.mms.pdu.PduBody;
import ws.com.google.android.mms.pdu.PduHeaders;
+import ws.com.google.android.mms.pdu.PduPart;
import ws.com.google.android.mms.pdu.SendReq;
// XXXX Clean up MMS efficiency:
@@ -289,11 +291,11 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
public SendReq[] getOutgoingMessages(MasterSecret masterSecret, long messageId)
throws MmsException
{
- MmsAddressDatabase addr = DatabaseFactory.getMmsAddressDatabase(context);
- PartDatabase parts = getPartDatabase(masterSecret);
- SQLiteDatabase database = databaseHelper.getReadableDatabase();
- MasterCipher masterCipher = masterSecret == null ? null : new MasterCipher(masterSecret);
- Cursor cursor = null;
+ MmsAddressDatabase addr = DatabaseFactory.getMmsAddressDatabase(context);
+ PartDatabase partDatabase = getPartDatabase(masterSecret);
+ SQLiteDatabase database = databaseHelper.getReadableDatabase();
+ MasterCipher masterCipher = masterSecret == null ? null : new MasterCipher(masterSecret);
+ Cursor cursor = null;
String selection;
@@ -322,8 +324,8 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY));
PduHeaders headers = getHeadersFromCursor(cursor);
addr.getAddressesForId(messageId, headers);
- PduBody body = parts.getParts(messageId, true);
+ PduBody body = getPartsAsBody(partDatabase.getParts(messageId, true));
try {
if (!Util.isEmpty(messageText) && Types.isSymmetricEncryption(outboxType)) {
@@ -864,9 +866,12 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
if (masterSecret == null)
return null;
- PduBody body = getPartDatabase(masterSecret).getParts(id, false);
+ PduBody body = getPartsAsBody(getPartDatabase(masterSecret).getParts(id, false));
SlideDeck slideDeck = new SlideDeck(context, masterSecret, body);
- slideCache.put(id, new SoftReference(slideDeck));
+
+ if (!body.containsPushInProgress()) {
+ slideCache.put(id, new SoftReference(slideDeck));
+ }
return slideDeck;
}
@@ -907,4 +912,14 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
}
}
+ private PduBody getPartsAsBody(List> parts) {
+ PduBody body = new PduBody();
+
+ for (Pair part : parts) {
+ body.addPart(part.second);
+ }
+
+ return body;
+ }
+
}
diff --git a/src/org/thoughtcrime/securesms/database/PartDatabase.java b/src/org/thoughtcrime/securesms/database/PartDatabase.java
index 9f981722ec..067a17b14a 100644
--- a/src/org/thoughtcrime/securesms/database/PartDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/PartDatabase.java
@@ -23,10 +23,12 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
+import android.util.Pair;
import org.thoughtcrime.securesms.providers.PartProvider;
import org.thoughtcrime.securesms.util.Util;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -34,6 +36,8 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.LinkedList;
+import java.util.List;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.MmsException;
@@ -42,31 +46,34 @@ import ws.com.google.android.mms.pdu.PduPart;
public class PartDatabase extends Database {
- private static final String TABLE_NAME = "part";
- private static final String ID = "_id";
- private static final String MMS_ID = "mid";
- private static final String SEQUENCE = "seq";
- private static final String CONTENT_TYPE = "ct";
- private static final String NAME = "name";
- private static final String CHARSET = "chset";
- private static final String CONTENT_DISPOSITION = "cd";
- private static final String FILENAME = "fn";
- private static final String CONTENT_ID = "cid";
- private static final String CONTENT_LOCATION = "cl";
- private static final String CONTENT_TYPE_START = "ctt_s";
- private static final String CONTENT_TYPE_TYPE = "ctt_t";
- private static final String ENCRYPTED = "encrypted";
- private static final String DATA = "_data";
+ private static final String TABLE_NAME = "part";
+ private static final String ID = "_id";
+ private static final String MMS_ID = "mid";
+ private static final String SEQUENCE = "seq";
+ private static final String CONTENT_TYPE = "ct";
+ private static final String NAME = "name";
+ private static final String CHARSET = "chset";
+ private static final String CONTENT_DISPOSITION = "cd";
+ private static final String FILENAME = "fn";
+ private static final String CONTENT_ID = "cid";
+ private static final String CONTENT_LOCATION = "cl";
+ private static final String CONTENT_TYPE_START = "ctt_s";
+ private static final String CONTENT_TYPE_TYPE = "ctt_t";
+ private static final String ENCRYPTED = "encrypted";
+ private static final String DATA = "_data";
+ private static final String PENDING_PUSH_ATTACHMENT = "pending_push";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
MMS_ID + " INTEGER, " + SEQUENCE + " INTEGER DEFAULT 0, " +
CONTENT_TYPE + " TEXT, " + NAME + " TEXT, " + CHARSET + " INTEGER, " +
CONTENT_DISPOSITION + " TEXT, " + FILENAME + " TEXT, " + CONTENT_ID + " TEXT, " +
CONTENT_LOCATION + " TEXT, " + CONTENT_TYPE_START + " INTEGER, " +
- CONTENT_TYPE_TYPE + " TEXT, " + ENCRYPTED + " INTEGER, " + DATA + " TEXT);";
+ CONTENT_TYPE_TYPE + " TEXT, " + ENCRYPTED + " INTEGER, " +
+ PENDING_PUSH_ATTACHMENT + " INTEGER, "+ DATA + " TEXT);";
public static final String[] CREATE_INDEXS = {
- "CREATE INDEX IF NOT EXISTS part_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");"
+ "CREATE INDEX IF NOT EXISTS part_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
+ "CREATE INDEX IF NOT EXISTS pending_push_index ON " + TABLE_NAME + " (" + PENDING_PUSH_ATTACHMENT + ");",
};
public PartDatabase(Context context, SQLiteOpenHelper databaseHelper) {
@@ -113,6 +120,11 @@ public class PartDatabase extends Database {
if (!cursor.isNull(encryptedColumn))
part.setEncrypted(cursor.getInt(encryptedColumn) == 1);
+
+ int pendingPushColumn = cursor.getColumnIndexOrThrow(PENDING_PUSH_ATTACHMENT);
+
+ if (!cursor.isNull(pendingPushColumn))
+ part.setPendingPush(cursor.getInt(pendingPushColumn) == 1);
}
@@ -126,8 +138,9 @@ public class PartDatabase extends Database {
if (part.getContentType() != null) {
contentValues.put(CONTENT_TYPE, Util.toIsoString(part.getContentType()));
- if (Util.toIsoString(part.getContentType()).equals(ContentType.APP_SMIL))
+ if (Util.toIsoString(part.getContentType()).equals(ContentType.APP_SMIL)) {
contentValues.put(SEQUENCE, -1);
+ }
} else {
throw new MmsException("There is no content type for this part.");
}
@@ -153,6 +166,7 @@ public class PartDatabase extends Database {
}
contentValues.put(ENCRYPTED, part.getEncrypted() ? 1 : 0);
+ contentValues.put(PENDING_PUSH_ATTACHMENT, part.isPendingPush() ? 1 : 0);
return contentValues;
}
@@ -186,35 +200,42 @@ public class PartDatabase extends Database {
}
}
- private File writePartData(PduPart part) throws MmsException {
+ private File writePartData(PduPart part, InputStream in) throws MmsException {
try {
File partsDirectory = context.getDir("parts", Context.MODE_PRIVATE);
File dataFile = File.createTempFile("part", ".mms", partsDirectory);
FileOutputStream fout = getPartOutputStream(dataFile, part);
+ byte[] buf = new byte[512];
+ int read;
+
+ while ((read = in.read(buf)) != -1) {
+ fout.write(buf, 0, read);
+ }
+
+ fout.close();
+ in.close();
+
+ return dataFile;
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private File writePartData(PduPart part) throws MmsException {
+ try {
if (part.getData() != null) {
Log.w("PartDatabase", "Writing part data from buffer");
- fout.write(part.getData());
- fout.close();
- return dataFile;
+ return writePartData(part, new ByteArrayInputStream(part.getData()));
} else if (part.getDataUri() != null) {
Log.w("PartDatabase", "Writing part dat from URI");
- byte[] buf = new byte[512];
InputStream in = context.getContentResolver().openInputStream(part.getDataUri());
- int read;
- while ((read = in.read(buf)) != -1)
- fout.write(buf, 0, read);
-
- fout.close();
- in.close();
- return dataFile;
+ return writePartData(part, in);
} else {
throw new MmsException("Part is empty!");
}
} catch (FileNotFoundException e) {
throw new AssertionError(e);
- } catch (IOException e) {
- throw new AssertionError(e);
}
}
@@ -224,7 +245,7 @@ public class PartDatabase extends Database {
long partId = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
getPartValues(part, cursor);
- if (includeData)
+ if (includeData && !part.isPendingPush())
readPartData(part, dataLocation);
part.setDataUri(ContentUris.withAppendedId(PartProvider.CONTENT_URI, partId));
@@ -232,14 +253,20 @@ public class PartDatabase extends Database {
}
private long insertPart(PduPart part, long mmsId) throws MmsException {
- SQLiteDatabase database = databaseHelper.getWritableDatabase();
- File dataFile = writePartData(part);
+ SQLiteDatabase database = databaseHelper.getWritableDatabase();
+ File dataFile = null;
+
+ if (!part.isPendingPush()) {
+ dataFile = writePartData(part);
+ Log.w("PartDatabase", "Wrote part to file: " + dataFile.getAbsolutePath());
+ }
- Log.w("PartDatabase", "Wrote part to file: " + dataFile.getAbsolutePath());
ContentValues contentValues = getContentValuesForPart(part);
-
contentValues.put(MMS_ID, mmsId);
- contentValues.put(DATA, dataFile.getAbsolutePath());
+
+ if (dataFile != null) {
+ contentValues.put(DATA, dataFile.getAbsolutePath());
+ }
return database.insert(TABLE_NAME, null, contentValues);
}
@@ -256,6 +283,10 @@ public class PartDatabase extends Database {
PduPart part = new PduPart();
part.setEncrypted(cursor.getInt(1) == 1);
+ if (cursor.isNull(0)) {
+ throw new FileNotFoundException("No part data for id: " + partId);
+ }
+
return getPartInputStream(new File(cursor.getString(0)), part);
} else {
throw new FileNotFoundException("No part for id: " + partId);
@@ -273,6 +304,41 @@ public class PartDatabase extends Database {
}
}
+ public void updateDownloadedPart(long messageId, long partId, PduPart part, InputStream data)
+ throws MmsException
+ {
+ SQLiteDatabase database = databaseHelper.getWritableDatabase();
+ File partData = writePartData(part, data);
+
+ part.setContentDisposition(new byte[0]);
+ part.setPendingPush(false);
+
+ ContentValues values = getContentValuesForPart(part);
+
+ if (partData != null) {
+ values.put(DATA, partData.getAbsolutePath());
+ }
+
+ database.update(TABLE_NAME, values, ID_WHERE, new String[] {partId+""});
+ notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId));
+ }
+
+ public void updateFailedDownloadedPart(long messageId, long partId, PduPart part)
+ throws MmsException
+ {
+ SQLiteDatabase database = databaseHelper.getWritableDatabase();
+
+ part.setContentDisposition(new byte[0]);
+ part.setPendingPush(false);
+
+ ContentValues values = getContentValuesForPart(part);
+
+ values.put(DATA, (String)null);
+
+ database.update(TABLE_NAME, values, ID_WHERE, new String[] {partId+""});
+ notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId));
+ }
+
public PduPart getPart(long partId, boolean includeData) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null;
@@ -290,26 +356,50 @@ public class PartDatabase extends Database {
}
}
- public PduBody getParts(long mmsId, boolean includeData) {
- SQLiteDatabase database = databaseHelper.getReadableDatabase();
- PduBody body = new PduBody();
- Cursor cursor = null;
+ public List> getParts(long mmsId, boolean includeData) {
+ SQLiteDatabase database = databaseHelper.getReadableDatabase();
+ List> results = new LinkedList>();
+ Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, null, MMS_ID + " = ?", new String[] {mmsId+""}, null, null, null);
while (cursor != null && cursor.moveToNext()) {
PduPart part = getPart(cursor, includeData);
- body.addPart(part);
+ results.add(new Pair(cursor.getLong(cursor.getColumnIndexOrThrow(ID)),
+ part));
}
- return body;
+ return results;
} finally {
if (cursor != null)
cursor.close();
}
}
+ public List>> getPushPendingParts() {
+ SQLiteDatabase database = databaseHelper.getReadableDatabase();
+ List>> results = new LinkedList>>();
+ Cursor cursor = null;
+
+ try {
+ cursor = database.query(TABLE_NAME, null, PENDING_PUSH_ATTACHMENT + " = ?", new String[] {"1"}, null, null, null);
+
+ while (cursor != null && cursor.moveToNext()) {
+ PduPart part = getPart(cursor, false);
+ results.add(new Pair>(cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)),
+ new Pair(cursor.getLong(cursor.getColumnIndexOrThrow(ID)),
+ part)));
+ }
+
+ return results;
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+
+ }
+
public void deleteParts(long mmsId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Cursor cursor = null;
diff --git a/src/org/thoughtcrime/securesms/database/PushDatabase.java b/src/org/thoughtcrime/securesms/database/PushDatabase.java
new file mode 100644
index 0000000000..0ca927423e
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/database/PushDatabase.java
@@ -0,0 +1,42 @@
+package org.thoughtcrime.securesms.database;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import org.spongycastle.util.encoders.Base64;
+import org.whispersystems.textsecure.push.IncomingPushMessage;
+import org.whispersystems.textsecure.util.Util;
+
+public class PushDatabase extends Database {
+
+ private static final String TABLE_NAME = "push";
+ public static final String ID = "_id";
+ public static final String TYPE = "type";
+ public static final String SOURCE = "source";
+ public static final String DESTINATIONS = "destinations";
+ public static final String BODY = "body";
+ public static final String TIMESTAMP = "timestamp";
+
+ public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
+ TYPE + " INTEGER, " + SOURCE + " TEXT, " + DESTINATIONS + " TEXT, " + BODY + " TEXT, " + TIMESTAMP + " INTEGER);";
+
+ public PushDatabase(Context context, SQLiteOpenHelper databaseHelper) {
+ super(context, databaseHelper);
+ }
+
+ public long insert(IncomingPushMessage message) {
+ ContentValues values = new ContentValues();
+ values.put(TYPE, message.getType());
+ values.put(SOURCE, message.getSource());
+ values.put(DESTINATIONS, Util.join(message.getDestinations(), ","));
+ values.put(BODY, Base64.encode(message.getBody()));
+ values.put(TIMESTAMP, message.getTimestampMillis());
+
+ return databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values);
+ }
+
+ public void delete(long id) {
+ databaseHelper.getWritableDatabase().delete(TABLE_NAME, ID_WHERE, new String[] {id+""});
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/gcm/GcmIntentService.java b/src/org/thoughtcrime/securesms/gcm/GcmIntentService.java
index dd437e29eb..397e34a529 100644
--- a/src/org/thoughtcrime/securesms/gcm/GcmIntentService.java
+++ b/src/org/thoughtcrime/securesms/gcm/GcmIntentService.java
@@ -7,8 +7,6 @@ import android.util.Log;
import com.google.android.gcm.GCMBaseIntentService;
import org.thoughtcrime.securesms.service.RegistrationService;
import org.thoughtcrime.securesms.service.SendReceiveService;
-import org.thoughtcrime.securesms.sms.IncomingTextMessage;
-import org.thoughtcrime.securesms.sms.SmsTransportDetails;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.InvalidVersionException;
import org.whispersystems.textsecure.push.IncomingEncryptedPushMessage;
@@ -17,7 +15,6 @@ import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.util.Util;
import java.io.IOException;
-import java.util.ArrayList;
public class GcmIntentService extends GCMBaseIntentService {
@@ -61,8 +58,11 @@ public class GcmIntentService extends GCMBaseIntentService {
IncomingEncryptedPushMessage encryptedMessage = new IncomingEncryptedPushMessage(data, sessionKey);
IncomingPushMessage message = encryptedMessage.getIncomingPushMessage();
- if (!message.hasAttachments()) handleIncomingTextMessage(context, message);
- else handleIncomingMediaMessage(context, message);
+ Intent service = new Intent(context, SendReceiveService.class);
+ service.setAction(SendReceiveService.RECEIVE_PUSH_ACTION);
+ service.putExtra("message", message);
+
+ context.startService(service);
} catch (IOException e) {
Log.w("GcmIntentService", e);
} catch (InvalidVersionException e) {
@@ -75,25 +75,6 @@ public class GcmIntentService extends GCMBaseIntentService {
Log.w("GcmIntentService", "GCM Error: " + s);
}
- private void handleIncomingTextMessage(Context context, IncomingPushMessage message) {
- ArrayList messages = new ArrayList();
- String encodedBody = new String(new SmsTransportDetails().getEncodedMessage(message.getBody()));
- messages.add(new IncomingTextMessage(message, encodedBody));
-
- Intent receivedIntent = new Intent(context, SendReceiveService.class);
- receivedIntent.setAction(SendReceiveService.RECEIVE_SMS_ACTION);
- receivedIntent.putParcelableArrayListExtra("text_messages", messages);
- receivedIntent.putExtra("push_type", message.getType());
- context.startService(receivedIntent);
- }
-
- private void handleIncomingMediaMessage(Context context, IncomingPushMessage message) {
- Intent receivedIntent = new Intent(context, SendReceiveService.class);
- receivedIntent.setAction(SendReceiveService.RECEIVE_PUSH_MMS_ACTION);
- receivedIntent.putExtra("media_message", message);
- context.startService(receivedIntent);
- }
-
private PushServiceSocket getGcmSocket(Context context) {
String localNumber = TextSecurePreferences.getLocalNumber(context);
String password = TextSecurePreferences.getPushServerPassword(context);
diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
index 451391b06d..559bb4132f 100644
--- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
+++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
@@ -55,21 +55,21 @@ public class AttachmentManager {
public void setImage(Uri image) throws IOException, BitmapDecodingException {
ImageSlide slide = new ImageSlide(context, image);
slideDeck.addSlide(slide);
- thumbnail.setImageBitmap(slide.getThumbnail(345, 261));
+ thumbnail.setImageDrawable(slide.getThumbnail(345, 261));
attachmentView.setVisibility(View.VISIBLE);
}
public void setVideo(Uri video) throws IOException, MediaTooLargeException {
VideoSlide slide = new VideoSlide(context, video);
slideDeck.addSlide(slide);
- thumbnail.setImageBitmap(slide.getThumbnail(thumbnail.getWidth(), thumbnail.getHeight()));
+ thumbnail.setImageDrawable(slide.getThumbnail(thumbnail.getWidth(), thumbnail.getHeight()));
attachmentView.setVisibility(View.VISIBLE);
}
public void setAudio(Uri audio)throws IOException, MediaTooLargeException {
AudioSlide slide = new AudioSlide(context, audio);
slideDeck.addSlide(slide);
- thumbnail.setImageBitmap(slide.getThumbnail(thumbnail.getWidth(), thumbnail.getHeight()));
+ thumbnail.setImageDrawable(slide.getThumbnail(thumbnail.getWidth(), thumbnail.getHeight()));
attachmentView.setVisibility(View.VISIBLE);
}
diff --git a/src/org/thoughtcrime/securesms/mms/AudioSlide.java b/src/org/thoughtcrime/securesms/mms/AudioSlide.java
index aa62e527db..ee27e9e951 100644
--- a/src/org/thoughtcrime/securesms/mms/AudioSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/AudioSlide.java
@@ -25,6 +25,7 @@ import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.MediaStore.Audio;
import android.widget.ImageView;
@@ -50,8 +51,8 @@ public class AudioSlide extends Slide {
}
@Override
- public Bitmap getThumbnail(int maxWidth, int maxHeight) {
- return BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_menu_add_sound);
+ public Drawable getThumbnail(int maxWidth, int maxHeight) {
+ return context.getResources().getDrawable(R.drawable.ic_menu_add_sound);
}
public static PduPart constructPartFromUri(Context context, Uri uri) throws IOException, MediaTooLargeException {
diff --git a/src/org/thoughtcrime/securesms/mms/ImageSlide.java b/src/org/thoughtcrime/securesms/mms/ImageSlide.java
index 0b4fc29a9b..16054180fc 100644
--- a/src/org/thoughtcrime/securesms/mms/ImageSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/ImageSlide.java
@@ -17,9 +17,8 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Color;
+import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -30,11 +29,11 @@ import android.util.Log;
import android.widget.ImageView;
import org.thoughtcrime.securesms.R;
-import org.whispersystems.textsecure.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.LRUCache;
+import org.whispersystems.textsecure.crypto.MasterSecret;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -50,8 +49,8 @@ import ws.com.google.android.mms.pdu.PduPart;
public class ImageSlide extends Slide {
private static final int MAX_CACHE_SIZE = 10;
- private static final Map> thumbnailCache =
- Collections.synchronizedMap(new LRUCache>(MAX_CACHE_SIZE));
+ private static final Map> thumbnailCache =
+ Collections.synchronizedMap(new LRUCache>(MAX_CACHE_SIZE));
public ImageSlide(Context context, MasterSecret masterSecret, PduPart part) {
super(context, masterSecret, part);
@@ -62,32 +61,37 @@ public class ImageSlide extends Slide {
}
@Override
- public Bitmap getThumbnail(int maxWidth, int maxHeight) {
- Bitmap thumbnail = getCachedThumbnail();
+ public Drawable getThumbnail(int maxWidth, int maxHeight) {
+ Drawable thumbnail = getCachedThumbnail();
- if (thumbnail != null)
+ if (thumbnail != null) {
return thumbnail;
+ }
+
+ if (part.isPendingPush()) {
+ return context.getResources().getDrawable(R.drawable.stat_sys_download);
+ }
try {
InputStream measureStream = getPartDataInputStream();
InputStream dataStream = getPartDataInputStream();
- thumbnail = BitmapUtil.createScaledBitmap(measureStream, dataStream, maxWidth, maxHeight);
- thumbnailCache.put(part.getDataUri(), new SoftReference(thumbnail));
+ thumbnail = new BitmapDrawable(context.getResources(), BitmapUtil.createScaledBitmap(measureStream, dataStream, maxWidth, maxHeight));
+ thumbnailCache.put(part.getDataUri(), new SoftReference(thumbnail));
return thumbnail;
} catch (FileNotFoundException e) {
Log.w("ImageSlide", e);
- return BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_missing_thumbnail_picture);
+ return context.getResources().getDrawable(R.drawable.ic_missing_thumbnail_picture);
} catch (BitmapDecodingException e) {
Log.w("ImageSlide", e);
- return BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_missing_thumbnail_picture);
+ return context.getResources().getDrawable(R.drawable.ic_missing_thumbnail_picture);
}
}
@Override
public void setThumbnailOn(ImageView imageView) {
- Bitmap thumbnail = getCachedThumbnail();
+ Drawable thumbnail = getCachedThumbnail();
if (thumbnail != null) {
Log.w("ImageSlide", "Setting cached thumbnail...");
@@ -109,8 +113,9 @@ public class ImageSlide extends Slide {
MmsDatabase.slideResolver.execute(new Runnable() {
@Override
public void run() {
- final Bitmap bitmap = getThumbnail(maxWidth, maxHeight);
+ final Drawable bitmap = getThumbnail(maxWidth, maxHeight);
final ImageView destination = weakImageView.get();
+
if (destination != null && destination.getDrawable() == temporaryDrawable) {
handler.post(new Runnable() {
@Override
@@ -123,24 +128,26 @@ public class ImageSlide extends Slide {
});
}
- private void setThumbnailOn(ImageView imageView, Bitmap thumbnail, boolean fromMemory) {
+ private void setThumbnailOn(ImageView imageView, Drawable thumbnail, boolean fromMemory) {
if (fromMemory) {
- imageView.setImageBitmap(thumbnail);
+ imageView.setImageDrawable(thumbnail);
+ } else if (thumbnail instanceof AnimationDrawable) {
+ imageView.setImageDrawable(thumbnail);
+ ((AnimationDrawable)imageView.getDrawable()).start();
} else {
- BitmapDrawable result = new BitmapDrawable(context.getResources(), thumbnail);
- TransitionDrawable fadingResult = new TransitionDrawable(new Drawable[]{new ColorDrawable(Color.TRANSPARENT), result});
+ TransitionDrawable fadingResult = new TransitionDrawable(new Drawable[]{new ColorDrawable(Color.TRANSPARENT), thumbnail});
imageView.setImageDrawable(fadingResult);
fadingResult.startTransition(300);
}
}
- private Bitmap getCachedThumbnail() {
+ private Drawable getCachedThumbnail() {
synchronized (thumbnailCache) {
- SoftReference bitmapReference = thumbnailCache.get(part.getDataUri());
+ SoftReference bitmapReference = thumbnailCache.get(part.getDataUri());
Log.w("ImageSlide", "Got soft reference: " + bitmapReference);
if (bitmapReference != null) {
- Bitmap bitmap = bitmapReference.get();
+ Drawable bitmap = bitmapReference.get();
Log.w("ImageSlide", "Got cached bitmap: " + bitmap);
if (bitmap != null) return bitmap;
else thumbnailCache.remove(part.getDataUri());
diff --git a/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java b/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java
index 636b41bef6..29b23bdcec 100644
--- a/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java
+++ b/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java
@@ -1,15 +1,13 @@
package org.thoughtcrime.securesms.mms;
-import android.util.Log;
-import android.util.Pair;
-
import org.thoughtcrime.securesms.util.Util;
+import org.whispersystems.textsecure.crypto.MasterCipher;
+import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.push.IncomingPushMessage;
+import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
+import org.whispersystems.textsecure.util.Base64;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.List;
+import java.io.UnsupportedEncodingException;
import ws.com.google.android.mms.pdu.CharacterSets;
import ws.com.google.android.mms.pdu.EncodedStringValue;
@@ -28,9 +26,9 @@ public class IncomingMediaMessage {
this.body = retreived.getBody();
}
- public IncomingMediaMessage(String localNumber, IncomingPushMessage message,
- List> attachments)
- throws IOException
+ public IncomingMediaMessage(MasterSecret masterSecret, String localNumber,
+ IncomingPushMessage message,
+ PushMessageContent messageContent)
{
this.headers = new PduHeaders();
this.body = new PduBody();
@@ -39,32 +37,29 @@ public class IncomingMediaMessage {
this.headers.appendEncodedStringValue(new EncodedStringValue(localNumber), PduHeaders.TO);
for (String destination : message.getDestinations()) {
- if (!destination.equals(localNumber)) {
- this.headers.appendEncodedStringValue(new EncodedStringValue(destination), PduHeaders.CC);
- }
+ this.headers.appendEncodedStringValue(new EncodedStringValue(destination), PduHeaders.CC);
}
this.headers.setLongInteger(message.getTimestampMillis() / 1000, PduHeaders.DATE);
- if (message.getBody() != null && message.getBody().length > 0) {
+ if (messageContent.getBody() != null && messageContent.getBody().length() > 0) {
PduPart text = new PduPart();
- text.setData(message.getBody());
- text.setContentType("text/plain".getBytes(CharacterSets.MIMENAME_ISO_8859_1));
+ text.setData(Util.toIsoBytes(messageContent.getBody()));
+ text.setContentType(Util.toIsoBytes("text/plain"));
body.addPart(text);
}
- if (attachments != null) {
- for (Pair attachment : attachments) {
- PduPart media = new PduPart();
- FileInputStream fin = new FileInputStream(attachment.first);
- byte[] data = Util.readFully(fin);
+ if (messageContent.getAttachmentsCount() > 0) {
+ for (PushMessageContent.AttachmentPointer attachment : messageContent.getAttachmentsList()) {
+ PduPart media = new PduPart();
+ byte[] encryptedKey = new MasterCipher(masterSecret).encryptBytes(attachment.getKey().toByteArray());
- Log.w("IncomingMediaMessage", "Adding part: " + attachment.second + " with length: " + data.length);
+ media.setContentType(Util.toIsoBytes(attachment.getContentType()));
+ media.setContentLocation(Util.toIsoBytes(String.valueOf(attachment.getId())));
+ media.setContentDisposition(Util.toIsoBytes(Base64.encodeBytes(encryptedKey)));
+ media.setPendingPush(true);
- media.setContentType(attachment.second.getBytes(CharacterSets.MIMENAME_ISO_8859_1));
- media.setData(data);
body.addPart(media);
- attachment.first.delete();
}
}
}
diff --git a/src/org/thoughtcrime/securesms/mms/Slide.java b/src/org/thoughtcrime/securesms/mms/Slide.java
index d3a0af680f..71467565b0 100644
--- a/src/org/thoughtcrime/securesms/mms/Slide.java
+++ b/src/org/thoughtcrime/securesms/mms/Slide.java
@@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.providers.PartProvider;
import android.content.ContentUris;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.Log;
import android.widget.ImageView;
@@ -90,12 +91,12 @@ public abstract class Slide {
return part.getDataUri();
}
- public Bitmap getThumbnail(int maxWidth, int maxHeight) {
+ public Drawable getThumbnail(int maxWidth, int maxHeight) {
throw new AssertionError("getThumbnail() called on non-thumbnail producing slide!");
}
public void setThumbnailOn(ImageView imageView) {
- imageView.setImageBitmap(getThumbnail(imageView.getWidth(), imageView.getHeight()));
+ imageView.setImageDrawable(getThumbnail(imageView.getWidth(), imageView.getHeight()));
}
public boolean hasImage() {
diff --git a/src/org/thoughtcrime/securesms/mms/VideoSlide.java b/src/org/thoughtcrime/securesms/mms/VideoSlide.java
index dfece90c48..5596cd90b5 100644
--- a/src/org/thoughtcrime/securesms/mms/VideoSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/VideoSlide.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Log;
@@ -42,8 +43,8 @@ public class VideoSlide extends Slide {
}
@Override
- public Bitmap getThumbnail(int width, int height) {
- return BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher_video_player);
+ public Drawable getThumbnail(int width, int height) {
+ return context.getResources().getDrawable(R.drawable.ic_launcher_video_player);
}
@Override
diff --git a/src/org/thoughtcrime/securesms/service/MmsReceiver.java b/src/org/thoughtcrime/securesms/service/MmsReceiver.java
index 871fdbae0d..e51cea39da 100644
--- a/src/org/thoughtcrime/securesms/service/MmsReceiver.java
+++ b/src/org/thoughtcrime/securesms/service/MmsReceiver.java
@@ -48,14 +48,8 @@ public class MmsReceiver {
}
public void process(MasterSecret masterSecret, Intent intent) {
- try {
- if (intent.getAction().equals(SendReceiveService.RECEIVE_MMS_ACTION)) {
- handleMmsNotification(intent);
- } else if (intent.getAction().equals(SendReceiveService.RECEIVE_PUSH_MMS_ACTION)) {
- handlePushMedia(masterSecret, intent);
- }
- } catch (MmsException e) {
- Log.w("MmsReceiver", e);
+ if (intent.getAction().equals(SendReceiveService.RECEIVE_MMS_ACTION)) {
+ handleMmsNotification(intent);
}
}
@@ -73,28 +67,6 @@ public class MmsReceiver {
}
}
- private void handlePushMedia(MasterSecret masterSecret, Intent intent) throws MmsException {
- IncomingPushMessage pushMessage = intent.getParcelableExtra("media_message");
- String localNumber = TextSecurePreferences.getLocalNumber(context);
- String password = TextSecurePreferences.getPushServerPassword(context);
- PushServiceSocket socket = new PushServiceSocket(context, localNumber, password);
-
- try {
- List> attachments = socket.retrieveAttachments(pushMessage.getAttachments());
- IncomingMediaMessage message = new IncomingMediaMessage(localNumber, pushMessage, attachments);
-
- DatabaseFactory.getMmsDatabase(context).insertMessageInbox(masterSecret, message, "", -1);
- } catch (IOException e) {
- Log.w("MmsReceiver", e);
- try {
- IncomingMediaMessage message = new IncomingMediaMessage(localNumber, pushMessage, null);
- DatabaseFactory.getMmsDatabase(context).insertMessageInbox(masterSecret, message, "", -1);
- } catch (IOException e1) {
- throw new MmsException(e1);
- }
- }
- }
-
private void scheduleDownload(NotificationInd pdu, long messageId, long threadId) {
Intent intent = new Intent(SendReceiveService.DOWNLOAD_MMS_ACTION, null, context, SendReceiveService.class);
intent.putExtra("content_location", new String(pdu.getContentLocation()));
diff --git a/src/org/thoughtcrime/securesms/service/PushDownloader.java b/src/org/thoughtcrime/securesms/service/PushDownloader.java
new file mode 100644
index 0000000000..506a74c62c
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/service/PushDownloader.java
@@ -0,0 +1,107 @@
+package org.thoughtcrime.securesms.service;
+
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.util.Pair;
+
+import org.thoughtcrime.securesms.database.DatabaseFactory;
+import org.thoughtcrime.securesms.database.EncryptingPartDatabase;
+import org.thoughtcrime.securesms.database.PartDatabase;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
+import org.thoughtcrime.securesms.util.Util;
+import org.whispersystems.textsecure.crypto.AttachmentCipherInputStream;
+import org.whispersystems.textsecure.crypto.InvalidMessageException;
+import org.whispersystems.textsecure.crypto.MasterCipher;
+import org.whispersystems.textsecure.crypto.MasterSecret;
+import org.whispersystems.textsecure.push.PushServiceSocket;
+import org.whispersystems.textsecure.util.Base64;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import ws.com.google.android.mms.MmsException;
+import ws.com.google.android.mms.pdu.PduPart;
+
+public class PushDownloader {
+
+ private final Context context;
+
+ public PushDownloader(Context context) {
+ this.context = context.getApplicationContext();
+ }
+
+ public void process(MasterSecret masterSecret, Intent intent) {
+ if (!intent.getAction().equals(SendReceiveService.DOWNLOAD_PUSH_ACTION))
+ return;
+
+ long messageId = intent.getLongExtra("message_id", -1);
+ PartDatabase database = DatabaseFactory.getEncryptingPartDatabase(context, masterSecret);
+
+ Log.w("PushDownloader", "Downloading push parts for: " + messageId);
+
+ if (messageId != -1) {
+ List> parts = database.getParts(messageId, false);
+
+ for (Pair partPair : parts) {
+ retrievePart(masterSecret, partPair.second, messageId, partPair.first);
+ Log.w("PushDownloader", "Got part: " + partPair.first);
+ }
+ } else {
+ List>> parts = database.getPushPendingParts();
+
+ for (Pair> partPair : parts) {
+ retrievePart(masterSecret, partPair.second.second, partPair.first, partPair.second.first);
+ Log.w("PushDownloader", "Got part: " + partPair.second.first);
+ }
+ }
+ }
+
+ private void retrievePart(MasterSecret masterSecret, PduPart part, long messageId, long partId) {
+ EncryptingPartDatabase database = DatabaseFactory.getEncryptingPartDatabase(context, masterSecret);
+ File attachmentFile = null;
+
+ try {
+ MasterCipher masterCipher = new MasterCipher(masterSecret);
+ long contentLocation = Long.parseLong(Util.toIsoString(part.getContentLocation()));
+ byte[] key = masterCipher.decryptBytes(Base64.decode(Util.toIsoString(part.getContentDisposition())));
+
+ attachmentFile = downloadAttachment(contentLocation);
+ InputStream attachmentInput = new AttachmentCipherInputStream(attachmentFile, key);
+
+ database.updateDownloadedPart(messageId, partId, part, attachmentInput);
+ } catch (InvalidMessageException e) {
+ Log.w("PushDownloader", e);
+ try {
+ database.updateFailedDownloadedPart(messageId, partId, part);
+ } catch (MmsException mme) {
+ Log.w("PushDownloader", mme);
+ }
+ } catch (MmsException e) {
+ Log.w("PushDownloader", e);
+ try {
+ database.updateFailedDownloadedPart(messageId, partId, part);
+ } catch (MmsException mme) {
+ Log.w("PushDownloader", mme);
+ }
+ } catch (IOException e) {
+ Log.w("PushDownloader", e);
+ /// XXX schedule some kind of soft failure retry action
+ } finally {
+ if (attachmentFile != null)
+ attachmentFile.delete();
+ }
+ }
+
+ private File downloadAttachment(long contentLocation) throws IOException {
+ String localNumber = TextSecurePreferences.getLocalNumber(context);
+ String password = TextSecurePreferences.getPushServerPassword(context);
+ PushServiceSocket socket = new PushServiceSocket(context, localNumber, password);
+
+ return socket.retrieveAttachment(contentLocation);
+ }
+
+}
diff --git a/src/org/thoughtcrime/securesms/service/PushReceiver.java b/src/org/thoughtcrime/securesms/service/PushReceiver.java
new file mode 100644
index 0000000000..4750615d20
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/service/PushReceiver.java
@@ -0,0 +1,211 @@
+package org.thoughtcrime.securesms.service;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.util.Pair;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+import org.thoughtcrime.securesms.crypto.DecryptingQueue;
+import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
+import org.thoughtcrime.securesms.database.DatabaseFactory;
+import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
+import org.thoughtcrime.securesms.database.MmsDatabase;
+import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
+import org.thoughtcrime.securesms.recipients.Recipient;
+import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
+import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage;
+import org.thoughtcrime.securesms.sms.IncomingTextMessage;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
+import org.whispersystems.textsecure.crypto.InvalidKeyException;
+import org.whispersystems.textsecure.crypto.InvalidVersionException;
+import org.whispersystems.textsecure.crypto.MasterSecret;
+import org.whispersystems.textsecure.crypto.protocol.PreKeyBundleMessage;
+import org.whispersystems.textsecure.push.IncomingPushMessage;
+import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
+import org.whispersystems.textsecure.storage.InvalidKeyIdException;
+
+import ws.com.google.android.mms.MmsException;
+
+public class PushReceiver {
+
+ public static final int RESULT_OK = 0;
+ public static final int RESULT_NO_SESSION = 1;
+ public static final int RESULT_DECRYPT_FAILED = 2;
+
+ private final Context context;
+
+ public PushReceiver(Context context) {
+ this.context = context.getApplicationContext();
+ }
+
+ public void process(MasterSecret masterSecret, Intent intent) {
+ if (intent.getAction().equals(SendReceiveService.RECEIVE_PUSH_ACTION)) {
+ handleMessage(masterSecret, intent);
+ } else if (intent.getAction().equals(SendReceiveService.DECRYPTED_PUSH_ACTION)) {
+ handleDecrypt(masterSecret, intent);
+ }
+ }
+
+ private void handleDecrypt(MasterSecret masterSecret, Intent intent) {
+ IncomingPushMessage message = intent.getParcelableExtra("message");
+ long messageId = intent.getLongExtra("message_id", -1);
+ int result = intent.getIntExtra("result", 0);
+
+ if (result == RESULT_OK) handleReceivedMessage(masterSecret, message, true);
+ else if (result == RESULT_NO_SESSION) handleReceivedMessageForNoSession(masterSecret, message);
+ else if (result == RESULT_DECRYPT_FAILED) handleReceivedCorruptedMessage(masterSecret, message, true);
+
+ DatabaseFactory.getPushDatabase(context).delete(messageId);
+ }
+
+ private void handleMessage(MasterSecret masterSecret, Intent intent) {
+ IncomingPushMessage message = intent.getExtras().getParcelable("message");
+
+ if (message.isSecureMessage()) handleReceivedSecureMessage(masterSecret, message);
+ else if (message.isPreKeyBundle()) handleReceivedPreKeyBundle(masterSecret, message);
+ else handleReceivedMessage(masterSecret, message, false);
+ }
+
+ private void handleReceivedSecureMessage(MasterSecret masterSecret, IncomingPushMessage message) {
+ long id = DatabaseFactory.getPushDatabase(context).insert(message);
+ DecryptingQueue.scheduleDecryption(context, masterSecret, id, message);
+ }
+
+ private void handleReceivedPreKeyBundle(MasterSecret masterSecret, IncomingPushMessage message) {
+ try {
+ Recipient recipient = new Recipient(null, message.getSource(), null, null);
+ KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipient);
+ PreKeyBundleMessage preKeyExchange = new PreKeyBundleMessage(message.getBody());
+
+ if (processor.isTrusted(preKeyExchange)) {
+ processor.processKeyExchangeMessage(preKeyExchange);
+
+ IncomingPushMessage bundledMessage = message.withBody(preKeyExchange.getBundledMessage());
+ handleReceivedSecureMessage(masterSecret, bundledMessage);
+ } else {
+ /// XXX
+ }
+ } catch (InvalidKeyException e) {
+ Log.w("SmsReceiver", e);
+ handleReceivedCorruptedKey(masterSecret, message, false);
+ } catch (InvalidVersionException e) {
+ Log.w("SmsReceiver", e);
+ handleReceivedCorruptedKey(masterSecret, message, true);
+ } catch (InvalidKeyIdException e) {
+ Log.w("SmsReceiver", e);
+ handleReceivedCorruptedKey(masterSecret, message, false);
+ }
+ }
+
+ private void handleReceivedMessage(MasterSecret masterSecret,
+ IncomingPushMessage message,
+ boolean secure)
+ {
+ try {
+ PushMessageContent messageContent = PushMessageContent.parseFrom(message.getBody());
+
+ if (messageContent.getAttachmentsCount() > 0) {
+ Log.w("PushReceiver", "Received push media message...");
+ handleReceivedMediaMessage(masterSecret, message, messageContent, secure);
+ } else {
+ Log.w("PushReceiver", "Received push text message...");
+ handleReceivedTextMessage(masterSecret, message, messageContent, secure);
+ }
+ } catch (InvalidProtocolBufferException e) {
+ Log.w("PushReceiver", e);
+ handleReceivedCorruptedMessage(masterSecret, message, secure);
+ }
+ }
+
+ private void handleReceivedMediaMessage(MasterSecret masterSecret,
+ IncomingPushMessage message,
+ PushMessageContent messageContent,
+ boolean secure)
+ {
+
+ try {
+ String localNumber = TextSecurePreferences.getLocalNumber(context);
+ MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
+ IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, localNumber,
+ message, messageContent);
+
+ Pair messageAndThreadId;
+
+ if (secure) {
+ messageAndThreadId = database.insertSecureDecryptedMessageInbox(masterSecret, mediaMessage, -1);
+ } else {
+ messageAndThreadId = database.insertMessageInbox(masterSecret, mediaMessage, null, -1);
+ }
+
+ Intent intent = new Intent(context, SendReceiveService.class);
+ intent.setAction(SendReceiveService.DOWNLOAD_PUSH_ACTION);
+ intent.putExtra("message_id", messageAndThreadId.first);
+ context.startService(intent);
+
+ } catch (MmsException e) {
+ Log.w("PushReceiver", e);
+ // XXX
+ }
+ }
+
+ private void handleReceivedTextMessage(MasterSecret masterSecret,
+ IncomingPushMessage message,
+ PushMessageContent messageContent,
+ boolean secure)
+ {
+ EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
+ IncomingTextMessage textMessage = new IncomingTextMessage(message, "");
+
+ if (secure) {
+ textMessage = new IncomingEncryptedMessage(textMessage, "");
+ }
+
+ Pair messageAndThreadId = database.insertMessageInbox(masterSecret, textMessage);
+ database.updateMessageBody(masterSecret, messageAndThreadId.first, messageContent.getBody());
+ }
+
+ private void handleReceivedCorruptedMessage(MasterSecret masterSecret,
+ IncomingPushMessage message,
+ boolean secure)
+ {
+ long messageId = insertMessagePlaceholder(masterSecret, message, secure);
+ DatabaseFactory.getEncryptingSmsDatabase(context).markAsDecryptFailed(messageId);
+ }
+
+ private void handleReceivedCorruptedKey(MasterSecret masterSecret,
+ IncomingPushMessage message,
+ boolean invalidVersion)
+ {
+ IncomingTextMessage corruptedMessage = new IncomingTextMessage(message, "");
+ IncomingKeyExchangeMessage corruptedKeyMessage = new IncomingKeyExchangeMessage(corruptedMessage, "");
+
+ if (!invalidVersion) corruptedKeyMessage.setCorrupted(true);
+ else corruptedKeyMessage.setInvalidVersion(true);
+
+ DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, corruptedKeyMessage);
+ }
+
+ private void handleReceivedMessageForNoSession(MasterSecret masterSecret,
+ IncomingPushMessage message)
+ {
+ long messageId = insertMessagePlaceholder(masterSecret, message, true);
+ DatabaseFactory.getEncryptingSmsDatabase(context).markAsNoSession(messageId);
+ }
+
+ private long insertMessagePlaceholder(MasterSecret masterSecret,
+ IncomingPushMessage message,
+ boolean secure)
+ {
+ IncomingTextMessage placeholder = new IncomingTextMessage(message, "");
+
+ if (secure) {
+ placeholder = new IncomingEncryptedMessage(placeholder, "");
+ }
+
+ Pair messageAndThreadId = DatabaseFactory.getEncryptingSmsDatabase(context)
+ .insertMessageInbox(masterSecret,
+ placeholder);
+ return messageAndThreadId.first;
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/service/SendReceiveService.java b/src/org/thoughtcrime/securesms/service/SendReceiveService.java
index 28573124d8..c1759b3cfc 100644
--- a/src/org/thoughtcrime/securesms/service/SendReceiveService.java
+++ b/src/org/thoughtcrime/securesms/service/SendReceiveService.java
@@ -51,10 +51,12 @@ public class SendReceiveService extends Service {
public static final String RECEIVE_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_SMS_ACTION";
public static final String SEND_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_MMS_ACTION";
public static final String RECEIVE_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_MMS_ACTION";
- public static final String RECEIVE_PUSH_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_PUSH_MMS_ACTION";
public static final String DOWNLOAD_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_MMS_ACTION";
public static final String DOWNLOAD_MMS_CONNECTIVITY_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_MMS_CONNECTIVITY_ACTION";
public static final String DOWNLOAD_MMS_PENDING_APN_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_MMS_PENDING_APN_ACTION";
+ public static final String RECEIVE_PUSH_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_PUSH_ACTION";
+ public static final String DECRYPTED_PUSH_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DECRYPTED_PUSH_ACTION";
+ public static final String DOWNLOAD_PUSH_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_PUSH_ACTION";
private static final int SEND_SMS = 0;
private static final int RECEIVE_SMS = 1;
@@ -62,14 +64,18 @@ public class SendReceiveService extends Service {
private static final int RECEIVE_MMS = 3;
private static final int DOWNLOAD_MMS = 4;
private static final int DOWNLOAD_MMS_PENDING = 5;
+ private static final int RECEIVE_PUSH = 6;
+ private static final int DOWNLOAD_PUSH = 7;
private ToastHandler toastHandler;
- private SmsReceiver smsReceiver;
- private SmsSender smsSender;
- private MmsReceiver mmsReceiver;
- private MmsSender mmsSender;
- private MmsDownloader mmsDownloader;
+ private SmsReceiver smsReceiver;
+ private SmsSender smsSender;
+ private MmsReceiver mmsReceiver;
+ private MmsSender mmsSender;
+ private MmsDownloader mmsDownloader;
+ private PushReceiver pushReceiver;
+ private PushDownloader pushDownloader;
private MasterSecret masterSecret;
private boolean hasSecret;
@@ -78,7 +84,6 @@ public class SendReceiveService extends Service {
private ClearKeyReceiver clearKeyReceiver;
private List workQueue;
private List pendingSecretList;
- private Thread workerThread;
@Override
public void onCreate() {
@@ -105,12 +110,18 @@ public class SendReceiveService extends Service {
scheduleIntent(SEND_SMS, intent);
else if (action.equals(SEND_MMS_ACTION))
scheduleSecretRequiredIntent(SEND_MMS, intent);
- else if (action.equals(RECEIVE_MMS_ACTION) || action.equals(RECEIVE_PUSH_MMS_ACTION))
+ else if (action.equals(RECEIVE_MMS_ACTION))
scheduleIntent(RECEIVE_MMS, intent);
else if (action.equals(DOWNLOAD_MMS_ACTION))
scheduleSecretRequiredIntent(DOWNLOAD_MMS, intent);
else if (intent.getAction().equals(DOWNLOAD_MMS_PENDING_APN_ACTION))
scheduleSecretRequiredIntent(DOWNLOAD_MMS_PENDING, intent);
+ else if (action.equals(RECEIVE_PUSH_ACTION))
+ scheduleIntent(RECEIVE_PUSH, intent);
+ else if (action.equals(DECRYPTED_PUSH_ACTION))
+ scheduleSecretRequiredIntent(RECEIVE_PUSH, intent);
+ else if (action.equals(DOWNLOAD_PUSH_ACTION))
+ scheduleSecretRequiredIntent(DOWNLOAD_PUSH, intent);
else
Log.w("SendReceiveService", "Received intent with unknown action: " + intent.getAction());
}
@@ -142,13 +153,15 @@ public class SendReceiveService extends Service {
mmsReceiver = new MmsReceiver(this);
mmsSender = new MmsSender(this, toastHandler);
mmsDownloader = new MmsDownloader(this, toastHandler);
+ pushReceiver = new PushReceiver(this);
+ pushDownloader = new PushDownloader(this);
}
private void initializeWorkQueue() {
pendingSecretList = new LinkedList();
workQueue = new LinkedList();
- workerThread = new WorkerThread(workQueue, "SendReceveService-WorkerThread");
+ Thread workerThread = new WorkerThread(workQueue, "SendReceveService-WorkerThread");
workerThread.start();
}
@@ -222,12 +235,14 @@ public class SendReceiveService extends Service {
@Override
public void run() {
switch (what) {
- case RECEIVE_SMS: smsReceiver.process(masterSecret, intent); return;
- case SEND_SMS: smsSender.process(masterSecret, intent); return;
- case RECEIVE_MMS: mmsReceiver.process(masterSecret, intent); return;
- case SEND_MMS: mmsSender.process(masterSecret, intent); return;
- case DOWNLOAD_MMS: mmsDownloader.process(masterSecret, intent); return;
+ case RECEIVE_SMS: smsReceiver.process(masterSecret, intent); return;
+ case SEND_SMS: smsSender.process(masterSecret, intent); return;
+ case RECEIVE_MMS: mmsReceiver.process(masterSecret, intent); return;
+ case SEND_MMS: mmsSender.process(masterSecret, intent); return;
+ case DOWNLOAD_MMS: mmsDownloader.process(masterSecret, intent); return;
case DOWNLOAD_MMS_PENDING: mmsDownloader.process(masterSecret, intent); return;
+ case RECEIVE_PUSH: pushReceiver.process(masterSecret, intent); return;
+ case DOWNLOAD_PUSH: pushDownloader.process(masterSecret, intent); return;
}
}
}
diff --git a/src/org/thoughtcrime/securesms/transport/PushTransport.java b/src/org/thoughtcrime/securesms/transport/PushTransport.java
index 4f6623cf98..8fbb04e7d4 100644
--- a/src/org/thoughtcrime/securesms/transport/PushTransport.java
+++ b/src/org/thoughtcrime/securesms/transport/PushTransport.java
@@ -4,11 +4,18 @@ import android.content.Context;
import android.util.Log;
import android.util.Pair;
+import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.mms.PartParser;
import org.thoughtcrime.securesms.recipients.Recipient;
+import org.thoughtcrime.securesms.recipients.RecipientFactory;
+import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
+import org.thoughtcrime.securesms.recipients.Recipients;
+import org.whispersystems.textsecure.crypto.AttachmentCipher;
+import org.whispersystems.textsecure.push.PushAttachmentPointer;
+import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
import org.whispersystems.textsecure.push.RawTransportDetails;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@@ -24,6 +31,7 @@ import org.whispersystems.textsecure.push.PushAttachmentData;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.push.PushTransportDetails;
import org.whispersystems.textsecure.push.RateLimitException;
+import org.whispersystems.textsecure.util.Hex;
import org.whispersystems.textsecure.util.PhoneNumberFormatter;
import java.io.IOException;
@@ -50,10 +58,12 @@ public class PushTransport extends BaseTransport {
String password = TextSecurePreferences.getPushServerPassword(context);
PushServiceSocket socket = new PushServiceSocket(context, localNumber, password);
- Recipient recipient = message.getIndividualRecipient();
- String plaintext = message.getBody().getBody();
- String recipientCanonicalNumber = PhoneNumberFormatter.formatNumber(recipient.getNumber(),
- localNumber);
+ Recipient recipient = message.getIndividualRecipient();
+ String plaintextBody = message.getBody().getBody();
+ PushMessageContent.Builder builder = PushMessageContent.newBuilder();
+ byte[] plaintext = builder.setBody(plaintextBody).build().toByteArray();
+ String recipientCanonicalNumber = PhoneNumberFormatter.formatNumber(recipient.getNumber(),
+ localNumber);
Pair typeAndCiphertext = getEncryptedMessage(socket, recipient, recipientCanonicalNumber, plaintext);
@@ -68,39 +78,70 @@ public class PushTransport extends BaseTransport {
public void deliver(SendReq message, List destinations) throws IOException {
try {
- String localNumber = TextSecurePreferences.getLocalNumber(context);
- String password = TextSecurePreferences.getPushServerPassword(context);
- PushServiceSocket socket = new PushServiceSocket(context, localNumber, password);
- byte[] messageText = PartParser.getMessageText(message.getBody()).getBytes();
- List attachments = getAttachmentsFromBody(message.getBody());
+ String localNumber = TextSecurePreferences.getLocalNumber(context);
+ String password = TextSecurePreferences.getPushServerPassword(context);
+ PushServiceSocket socket = new PushServiceSocket(context, localNumber, password);
+ String messageBody = PartParser.getMessageText(message.getBody());
+ List ciphertext = new LinkedList ();
+ List types = new LinkedList();
- List messagesList = new LinkedList();
- List> attachmentsList = new LinkedList>();
+ for (String destination : destinations) {
+ Recipients recipients = RecipientFactory.getRecipientsFromString(context, destination, false);
+ List attachments = getPushAttachmentPointers(socket, message.getBody());
+ PushMessageContent.Builder builder = PushMessageContent.newBuilder();
- for (String recipient : destinations) {
- messagesList.add(messageText);
- attachmentsList.add(attachments);
+ if (messageBody != null) {
+ builder.setBody(messageBody);
+ }
+
+ for (PushAttachmentPointer attachment : attachments) {
+ PushMessageContent.AttachmentPointer.Builder attachmentBuilder =
+ PushMessageContent.AttachmentPointer.newBuilder();
+
+ attachmentBuilder.setId(attachment.getId());
+ attachmentBuilder.setContentType(attachment.getContentType());
+ attachmentBuilder.setKey(ByteString.copyFrom(attachment.getKey()));
+
+ builder.addAttachments(attachmentBuilder.build());
+ }
+
+ byte[] plaintext = builder.build().toByteArray();
+ Pair typeAndCiphertext = getEncryptedMessage(socket, recipients.getPrimaryRecipient(),
+ destination, plaintext);
+
+ types.add(typeAndCiphertext.first);
+ ciphertext.add(typeAndCiphertext.second);
}
- socket.sendMessage(destinations, messagesList, attachmentsList,
- OutgoingPushMessage.TYPE_MESSAGE_PLAINTEXT);
+ socket.sendMessage(destinations, ciphertext, types);
+
} catch (RateLimitException e) {
Log.w("PushTransport", e);
throw new IOException("Rate limit exceeded.");
+ } catch (RecipientFormattingException e) {
+ Log.w("PushTransport", e);
+ throw new IOException("Bad destination!");
}
}
- private List getAttachmentsFromBody(PduBody body) {
- List attachments = new LinkedList();
+ private List getPushAttachmentPointers(PushServiceSocket socket, PduBody body)
+ throws IOException
+ {
+ List attachments = new LinkedList();
for (int i=0;i getEncryptedMessage(PushServiceSocket socket, Recipient recipient,
- String canonicalRecipientNumber, String plaintext)
+ String canonicalRecipientNumber, byte[] plaintext)
throws IOException
{
if (KeyUtil.isNonPrekeySessionFor(context, masterSecret, recipient)) {
@@ -127,13 +168,13 @@ public class PushTransport extends BaseTransport {
}
private byte[] getEncryptedPrekeyBundleMessageForExistingSession(Recipient recipient,
- String plaintext)
+ byte[] plaintext)
{
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
IdentityKey identityKey = identityKeyPair.getPublicKey();
MessageCipher message = new MessageCipher(context, masterSecret, identityKeyPair, new RawTransportDetails());
- byte[] bundledMessage = message.encrypt(recipient, plaintext.getBytes());
+ byte[] bundledMessage = message.encrypt(recipient, plaintext);
PreKeyBundleMessage preKeyBundleMessage = new PreKeyBundleMessage(identityKey, bundledMessage);
return preKeyBundleMessage.serialize();
@@ -142,7 +183,7 @@ public class PushTransport extends BaseTransport {
private byte[] getEncryptedPrekeyBundleMessageForNewSession(PushServiceSocket socket,
Recipient recipient,
String canonicalRecipientNumber,
- String plaintext)
+ byte[] plaintext)
throws IOException
{
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
@@ -153,20 +194,20 @@ public class PushTransport extends BaseTransport {
processor.processKeyExchangeMessage(preKey);
MessageCipher message = new MessageCipher(context, masterSecret, identityKeyPair, new RawTransportDetails());
- byte[] bundledMessage = message.encrypt(recipient, plaintext.getBytes());
+ byte[] bundledMessage = message.encrypt(recipient, plaintext);
PreKeyBundleMessage preKeyBundleMessage = new PreKeyBundleMessage(identityKey, bundledMessage);
return preKeyBundleMessage.serialize();
}
- private byte[] getEncryptedMessageForExistingSession(Recipient recipient, String plaintext)
+ private byte[] getEncryptedMessageForExistingSession(Recipient recipient, byte[] plaintext)
throws IOException
{
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKeyPair,
new PushTransportDetails());
- return messageCipher.encrypt(recipient, plaintext.getBytes());
+ return messageCipher.encrypt(recipient, plaintext);
}
}
diff --git a/src/ws/com/google/android/mms/pdu/PduBody.java b/src/ws/com/google/android/mms/pdu/PduBody.java
index 1947dbe7d1..9cb5cb17b0 100644
--- a/src/ws/com/google/android/mms/pdu/PduBody.java
+++ b/src/ws/com/google/android/mms/pdu/PduBody.java
@@ -41,6 +41,16 @@ public class PduBody {
mPartMapByFileName = new HashMap();
}
+ public boolean containsPushInProgress() {
+ for (int i=0;i