commit 30623fb20027342da517c5fd0b3c85b2b0811fbd Author: Moxie Marlinspike Date: Fri Feb 27 17:11:42 2015 -0800 Move libtextsecure into independent repository. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..5098a0d0bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.gradle +.idea +*.iml +local.properties +build/ diff --git a/README.md b/README.md new file mode 100644 index 0000000000..0c6a7cbf68 --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ +# libtextsecure-java + +A Java library for communicating via TextSecure. + +## Implementing the Axolotl interfaces + +The axolotl encryption protocol is a stateful protocol, so libtextsecure users +need to implement the storage interface `AxolotlStore`, which handles load/store +of your key and session information to durable media. + +## Creating keys + +````` +IdentityKeyPair identityKey = KeyHelper.generateIdentityKeyPair(); +List oneTimePreKeys = KeyHelper.generatePreKeys(100); +PreKeyRecord lastResortKey = KeyHelper.generateLastResortKey(); +SignedPreKeyRecord signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKey, signedPreKeyId); +````` + +The above are then stored locally so that they're available for load via the `AxolotlStore`. + +## Registering + +At install time, clients need to register with the TextSecure server. + +````` +private final String URL = "https://my.textsecure.server.com"; +private final TrustStore TRUST_STORE = new MyTrustStoreImpl(); +private final String USERNAME = "+14151231234"; +private final String PASSWORD = generateRandomPassword(); + +TextSecureAccountManager accountManager = new TextSecureAccountManager(URL, TRUST_STORE, + USERNAME, PASSWORD); + +accountManager.requestSmsVerificationCode(); +accountManager.verifyAccount(receivedSmsVerificationCode, generateRandomSignalingKey(), + false, generateRandomInstallId()); +accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID))); +accountManager.setPreKeys(identityKey.getPublic(), lastResortKey, signedPreKey, oneTimePreKeys); +````` + +## Sending text messages + +````` +TextSecureMessageSender messageSender = new TextSecureMessageSender(URL, TRUST_STORE, USERNAME, PASSWORD, + localRecipientId, new MyAxolotlStore(), + Optional.absent()); + +long recipientId = getRecipientIdFor("+14159998888"); +TextSecureAddress destination = new TextSecureAddress(recipientId, "+14159998888", null); +TextSecureMessage message = new TextSecureMessage(System.currentTimeMillis(), "Hello, world!"); + +messageSender.sendMessage(destination, message); +````` + +## Sending media messages + +````` +TextSecureMessageSender messageSender = new TextSecureMessageSender(URL, TRUST_STORE, USERNAME, PASSWORD, + localRecipientId, new MyAxolotlStore(), + Optional.absent()); + +long recipientId = getRecipientIdFor("+14159998888"); +TextSecureAddress destination = new TextSecureAddress(recipientId, "+14159998888", null); + +File myAttachment = new File("/path/to/my.attachment"); +FileInputStream attachmentStream = new FileInputStream(myAttachment); +TextSecureAttachment attachment = new TextSecureAttachmentStream(attachmentStream, "image/png", myAttachment.size()); +TextSecureMessage message = new TextSecureMessage(System.currentTimeMillis(), attachment, "Hello, world!"); + +messageSender.sendMessage(destination, message); + +````` + +## Receiving messages + +````` +TextSecureMessageReceiver messageReceiver = new TextSecureMessageReceiver(URL, TRUST_STORE, USERNAME, PASSWORD, mySignalingKey); +TextSecureMessagePipe messagePipe; + +try { + messagePipe = messageReciever.createMessagePipe(); + + while (listeningForMessages) { + TextSecureEnvelope envelope = messagePipe.read(timeout, timeoutTimeUnit); + TextSecureCipher cipher = new TextSecureCipher(new MyAxolotlStore(), + getRecipientIdFor(envelope.getSource()), + envelope.getSourceDevice()); + TextSecureMessage message = cipher.decrypt(envelope); + + System.out.println("Received message: " + message.getBody().get()); + } + +} finally { + if (messagePipe != null) + messagePipe.close(); +} +````` \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..4c6212f32c --- /dev/null +++ b/build.gradle @@ -0,0 +1,53 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:1.0.0' + } +} + +apply plugin: 'com.android.library' +apply plugin: 'maven' + +repositories { + mavenCentral() +} + +dependencies { + compile 'com.google.protobuf:protobuf-java:2.5.0' + compile 'com.googlecode.libphonenumber:libphonenumber:6.1' + compile 'com.fasterxml.jackson.core:jackson-databind:2.5.0' + + compile 'org.whispersystems:axolotl-android:1.0.0' + compile 'com.squareup.okhttp:okhttp:2.2.0' +} + +android { + compileSdkVersion 21 + buildToolsVersion '21.1.2' + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } +} + +tasks.whenTaskAdded { task -> + if (task.name.equals("lint")) { + task.enabled = false + } +} + +version '0.1' +group 'org.whispersystems.textsecure' +archivesBaseName = 'libtextsecure' + +uploadArchives { + repositories { + mavenDeployer { + repository(url: mavenLocal().getUrl()) + } + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..2322723c7e Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..5df071caf3 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Feb 27 17:12:50 PST 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-bin.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..91a7e269e1 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..aec99730b4 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/protobuf/IncomingPushMessageSignal.proto b/protobuf/IncomingPushMessageSignal.proto new file mode 100644 index 0000000000..4dd54b1200 --- /dev/null +++ b/protobuf/IncomingPushMessageSignal.proto @@ -0,0 +1,59 @@ +package textsecure; + +option java_package = "org.whispersystems.textsecure.internal.push"; +option java_outer_classname = "PushMessageProtos"; + +message IncomingPushMessageSignal { + enum Type { + UNKNOWN = 0; + CIPHERTEXT = 1; + KEY_EXCHANGE = 2; + PREKEY_BUNDLE = 3; + PLAINTEXT = 4; + RECEIPT = 5; + } + optional Type type = 1; + optional string source = 2; + optional uint32 sourceDevice = 7; + optional string relay = 3; + optional uint64 timestamp = 5; + optional bytes message = 6; // Contains an encrypted PushMessageContent +// repeated string destinations = 4; // No longer supported +} + +message PushMessageContent { + message AttachmentPointer { + optional fixed64 id = 1; + optional string contentType = 2; + optional bytes key = 3; + } + + message GroupContext { + enum Type { + UNKNOWN = 0; + UPDATE = 1; + DELIVER = 2; + QUIT = 3; + } + optional bytes id = 1; + optional Type type = 2; + optional string name = 3; + repeated string members = 4; + optional AttachmentPointer avatar = 5; + } + + message SyncMessageContext { + optional string destination = 1; + optional uint64 timestamp = 2; + } + + enum Flags { + END_SESSION = 1; + } + + optional string body = 1; + repeated AttachmentPointer attachments = 2; + optional GroupContext group = 3; + optional uint32 flags = 4; + optional SyncMessageContext sync = 5; +} diff --git a/protobuf/Makefile b/protobuf/Makefile new file mode 100644 index 0000000000..70c811fb96 --- /dev/null +++ b/protobuf/Makefile @@ -0,0 +1,3 @@ + +all: + protoc --java_out=../src/main/java/ IncomingPushMessageSignal.proto Provisioning.proto WebSocketResources.proto diff --git a/protobuf/Provisioning.proto b/protobuf/Provisioning.proto new file mode 100644 index 0000000000..776877c344 --- /dev/null +++ b/protobuf/Provisioning.proto @@ -0,0 +1,16 @@ +package textsecure; + +option java_package = "org.whispersystems.textsecure.internal.push"; +option java_outer_classname = "ProvisioningProtos"; + +message ProvisionEnvelope { + optional bytes publicKey = 1; + optional bytes body = 2; // Encrypted ProvisionMessage +} + +message ProvisionMessage { + optional bytes identityKeyPublic = 1; + optional bytes identityKeyPrivate = 2; + optional string number = 3; + optional string provisioningCode = 4; +} diff --git a/protobuf/WebSocketResources.proto b/protobuf/WebSocketResources.proto new file mode 100644 index 0000000000..35943a7826 --- /dev/null +++ b/protobuf/WebSocketResources.proto @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2014-2015 Open WhisperSystems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package textsecure; + +option java_package = "org.whispersystems.textsecure.internal.websocket"; +option java_outer_classname = "WebSocketProtos"; + +message WebSocketRequestMessage { + optional string verb = 1; + optional string path = 2; + optional bytes body = 3; + optional uint64 id = 4; +} + +message WebSocketResponseMessage { + optional uint64 id = 1; + optional uint32 status = 2; + optional string message = 3; + optional bytes body = 4; +} + +message WebSocketMessage { + enum Type { + UNKNOWN = 0; + REQUEST = 1; + RESPONSE = 2; + } + + optional Type type = 1; + optional WebSocketRequestMessage request = 2; + optional WebSocketResponseMessage response = 3; +} \ No newline at end of file diff --git a/src/androidTest/java/org/whispersystems/textsecure/push/PushTransportDetailsTest.java b/src/androidTest/java/org/whispersystems/textsecure/push/PushTransportDetailsTest.java new file mode 100644 index 0000000000..4d6c5ab32d --- /dev/null +++ b/src/androidTest/java/org/whispersystems/textsecure/push/PushTransportDetailsTest.java @@ -0,0 +1,35 @@ +package org.whispersystems.textsecure.push; + +import android.test.AndroidTestCase; + +import org.whispersystems.textsecure.internal.push.PushTransportDetails; + +public class PushTransportDetailsTest extends AndroidTestCase { + + private final PushTransportDetails transportV2 = new PushTransportDetails(2); + private final PushTransportDetails transportV3 = new PushTransportDetails(3); + + public void testV3Padding() { + for (int i=0;i<159;i++) { + byte[] message = new byte[i]; + assertEquals(transportV3.getPaddedMessageBody(message).length, 159); + } + + for (int i=159;i<319;i++) { + byte[] message = new byte[i]; + assertEquals(transportV3.getPaddedMessageBody(message).length, 319); + } + + for (int i=319;i<479;i++) { + byte[] message = new byte[i]; + assertEquals(transportV3.getPaddedMessageBody(message).length, 479); + } + } + + public void testV2Padding() { + for (int i=0;i<480;i++) { + byte[] message = new byte[i]; + assertTrue(transportV2.getPaddedMessageBody(message).length == message.length); + } + } +} diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..fdd26b94d8 --- /dev/null +++ b/src/main/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/src/main/java/org/whispersystems/textsecure/api/TextSecureAccountManager.java b/src/main/java/org/whispersystems/textsecure/api/TextSecureAccountManager.java new file mode 100644 index 0000000000..3326520222 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/TextSecureAccountManager.java @@ -0,0 +1,256 @@ +/** + * Copyright (C) 2014 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.api; + +import com.google.protobuf.ByteString; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.state.PreKeyRecord; +import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; +import org.whispersystems.libaxolotl.util.guava.Optional; +import org.whispersystems.textsecure.api.push.ContactTokenDetails; +import org.whispersystems.textsecure.api.push.SignedPreKeyEntity; +import org.whispersystems.textsecure.api.push.TrustStore; +import org.whispersystems.textsecure.internal.crypto.ProvisioningCipher; +import org.whispersystems.textsecure.internal.push.PushServiceSocket; +import org.whispersystems.textsecure.internal.util.Base64; +import org.whispersystems.textsecure.internal.util.StaticCredentialsProvider; +import org.whispersystems.textsecure.internal.util.Util; + +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage; + +/** + * The main interface for creating, registering, and + * managing a TextSecure account. + * + * @author Moxie Marlinspike + */ +public class TextSecureAccountManager { + + private final PushServiceSocket pushServiceSocket; + private final String user; + + /** + * Construct a TextSecureAccountManager. + * + * @param url The URL for the TextSecure server. + * @param trustStore The {@link org.whispersystems.textsecure.api.push.TrustStore} for the TextSecure server's TLS certificate. + * @param user A TextSecure phone number. + * @param password A TextSecure password. + */ + public TextSecureAccountManager(String url, TrustStore trustStore, + String user, String password) + { + this.pushServiceSocket = new PushServiceSocket(url, trustStore, new StaticCredentialsProvider(user, password, null)); + this.user = user; + } + + /** + * Register/Unregister a Google Cloud Messaging registration ID. + * + * @param gcmRegistrationId The GCM id to register. A call with an absent value will unregister. + * @throws IOException + */ + public void setGcmId(Optional gcmRegistrationId) throws IOException { + if (gcmRegistrationId.isPresent()) { + this.pushServiceSocket.registerGcmId(gcmRegistrationId.get()); + } else { + this.pushServiceSocket.unregisterGcmId(); + } + } + + /** + * Request an SMS verification code. On success, the server will send + * an SMS verification code to this TextSecure user. + * + * @throws IOException + */ + public void requestSmsVerificationCode() throws IOException { + this.pushServiceSocket.createAccount(false); + } + + /** + * Request a Voice verification code. On success, the server will + * make a voice call to this TextSecure user. + * + * @throws IOException + */ + public void requestVoiceVerificationCode() throws IOException { + this.pushServiceSocket.createAccount(true); + } + + /** + * Verify a TextSecure account. + * + * @param verificationCode The verification code received via SMS or Voice + * (see {@link #requestSmsVerificationCode} and + * {@link #requestVoiceVerificationCode}). + * @param signalingKey 52 random bytes. A 32 byte AES key and a 20 byte Hmac256 key, + * concatenated. + * @param supportsSms Indicate whether this client is capable of supporting encrypted SMS. + * @param axolotlRegistrationId A random 14-bit number that identifies this TextSecure install. + * This value should remain consistent across registrations for the + * same install, but probabilistically differ across registrations + * for separate installs. + * + * @throws IOException + */ + public void verifyAccount(String verificationCode, String signalingKey, + boolean supportsSms, int axolotlRegistrationId) + throws IOException + { + this.pushServiceSocket.verifyAccount(verificationCode, signalingKey, + supportsSms, axolotlRegistrationId); + } + + /** + * Register an identity key, last resort key, signed prekey, and list of one time prekeys + * with the server. + * + * @param identityKey The client's long-term identity keypair. + * @param lastResortKey The client's "last resort" prekey. + * @param signedPreKey The client's signed prekey. + * @param oneTimePreKeys The client's list of one-time prekeys. + * + * @throws IOException + */ + public void setPreKeys(IdentityKey identityKey, PreKeyRecord lastResortKey, + SignedPreKeyRecord signedPreKey, List oneTimePreKeys) + throws IOException + { + this.pushServiceSocket.registerPreKeys(identityKey, lastResortKey, signedPreKey, oneTimePreKeys); + } + + /** + * @return The server's count of currently available (eg. unused) prekeys for this user. + * @throws IOException + */ + public int getPreKeysCount() throws IOException { + return this.pushServiceSocket.getAvailablePreKeys(); + } + + /** + * Set the client's signed prekey. + * + * @param signedPreKey The client's new signed prekey. + * @throws IOException + */ + public void setSignedPreKey(SignedPreKeyRecord signedPreKey) throws IOException { + this.pushServiceSocket.setCurrentSignedPreKey(signedPreKey); + } + + /** + * @return The server's view of the client's current signed prekey. + * @throws IOException + */ + public SignedPreKeyEntity getSignedPreKey() throws IOException { + return this.pushServiceSocket.getCurrentSignedPreKey(); + } + + /** + * Checks whether a contact is currently registered with the server. + * + * @param e164number The contact to check. + * @return An optional ContactTokenDetails, present if registered, absent if not. + * @throws IOException + */ + public Optional getContact(String e164number) throws IOException { + String contactToken = createDirectoryServerToken(e164number); + ContactTokenDetails contactTokenDetails = this.pushServiceSocket.getContactTokenDetails(contactToken); + + if (contactTokenDetails != null) { + contactTokenDetails.setNumber(e164number); + } + + return Optional.fromNullable(contactTokenDetails); + } + + /** + * Checks which contacts in a set are registered with the server. + * + * @param e164numbers The contacts to check. + * @return A list of ContactTokenDetails for the registered users. + * @throws IOException + */ + public List getContacts(Set e164numbers) + throws IOException + { + Map contactTokensMap = createDirectoryServerTokenMap(e164numbers); + List activeTokens = this.pushServiceSocket.retrieveDirectory(contactTokensMap.keySet()); + + for (ContactTokenDetails activeToken : activeTokens) { + activeToken.setNumber(contactTokensMap.get(activeToken.getToken())); + } + + return activeTokens; + } + + public String getNewDeviceVerificationCode() throws IOException { + return this.pushServiceSocket.getNewDeviceVerificationCode(); + } + + public void addDevice(String deviceIdentifier, + ECPublicKey deviceKey, + IdentityKeyPair identityKeyPair, + String code) + throws InvalidKeyException, IOException + { + ProvisioningCipher cipher = new ProvisioningCipher(deviceKey); + ProvisionMessage message = ProvisionMessage.newBuilder() + .setIdentityKeyPublic(ByteString.copyFrom(identityKeyPair.getPublicKey().serialize())) + .setIdentityKeyPrivate(ByteString.copyFrom(identityKeyPair.getPrivateKey().serialize())) + .setNumber(user) + .setProvisioningCode(code) + .build(); + + byte[] ciphertext = cipher.encrypt(message); + this.pushServiceSocket.sendProvisioningMessage(deviceIdentifier, ciphertext); + } + + private String createDirectoryServerToken(String e164number) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA1"); + byte[] token = Util.trim(digest.digest(e164number.getBytes()), 10); + return Base64.encodeBytesWithoutPadding(token); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private Map createDirectoryServerTokenMap(Collection e164numbers) { + Map tokenMap = new HashMap<>(e164numbers.size()); + + for (String number : e164numbers) { + tokenMap.put(createDirectoryServerToken(number), number); + } + + return tokenMap; + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/api/TextSecureMessagePipe.java b/src/main/java/org/whispersystems/textsecure/api/TextSecureMessagePipe.java new file mode 100644 index 0000000000..1550c7dbe6 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/TextSecureMessagePipe.java @@ -0,0 +1,129 @@ +package org.whispersystems.textsecure.api; + +import org.whispersystems.libaxolotl.InvalidVersionException; +import org.whispersystems.textsecure.api.messages.TextSecureEnvelope; +import org.whispersystems.textsecure.api.util.CredentialsProvider; +import org.whispersystems.textsecure.internal.websocket.WebSocketConnection; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage; +import static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage; + +/** + * A TextSecureMessagePipe represents a dedicated connection + * to the TextSecure server, which the server can push messages + * down. + */ +public class TextSecureMessagePipe { + + private final WebSocketConnection websocket; + private final CredentialsProvider credentialsProvider; + + TextSecureMessagePipe(WebSocketConnection websocket, CredentialsProvider credentialsProvider) { + this.websocket = websocket; + this.credentialsProvider = credentialsProvider; + + this.websocket.connect(); + } + + /** + * A blocking call that reads a message off the pipe. When this + * call returns, the message has been acknowledged and will not + * be retransmitted. + * + * @param timeout The timeout to wait for. + * @param unit The timeout time unit. + * @return A new message. + * + * @throws InvalidVersionException + * @throws IOException + * @throws TimeoutException + */ + public TextSecureEnvelope read(long timeout, TimeUnit unit) + throws InvalidVersionException, IOException, TimeoutException + { + return read(timeout, unit, new NullMessagePipeCallback()); + } + + /** + * A blocking call that reads a message off the pipe (see {@link #read(long, java.util.concurrent.TimeUnit)} + * + * Unlike {@link #read(long, java.util.concurrent.TimeUnit)}, this method allows you + * to specify a callback that will be called before the received message is acknowledged. + * This allows you to write the received message to durable storage before acknowledging + * receipt of it to the server. + * + * @param timeout The timeout to wait for. + * @param unit The timeout time unit. + * @param callback A callback that will be called before the message receipt is + * acknowledged to the server. + * @return The message read (same as the message sent through the callback). + * @throws TimeoutException + * @throws IOException + * @throws InvalidVersionException + */ + public TextSecureEnvelope read(long timeout, TimeUnit unit, MessagePipeCallback callback) + throws TimeoutException, IOException, InvalidVersionException + { + while (true) { + WebSocketRequestMessage request = websocket.readRequest(unit.toMillis(timeout)); + WebSocketResponseMessage response = createWebSocketResponse(request); + + try { + if (isTextSecureEnvelope(request)) { + TextSecureEnvelope envelope = new TextSecureEnvelope(request.getBody().toByteArray(), + credentialsProvider.getSignalingKey()); + + callback.onMessage(envelope); + return envelope; + } + } finally { + websocket.sendResponse(response); + } + } + } + + /** + * Close this connection to the server. + */ + public void shutdown() { + websocket.disconnect(); + } + + private boolean isTextSecureEnvelope(WebSocketRequestMessage message) { + return "PUT".equals(message.getVerb()) && "/api/v1/message".equals(message.getPath()); + } + + private WebSocketResponseMessage createWebSocketResponse(WebSocketRequestMessage request) { + if (isTextSecureEnvelope(request)) { + return WebSocketResponseMessage.newBuilder() + .setId(request.getId()) + .setStatus(200) + .setMessage("OK") + .build(); + } else { + return WebSocketResponseMessage.newBuilder() + .setId(request.getId()) + .setStatus(400) + .setMessage("Unknown") + .build(); + } + } + + /** + * For receiving a callback when a new message has been + * received. + */ + public static interface MessagePipeCallback { + public void onMessage(TextSecureEnvelope envelope); + } + + private static class NullMessagePipeCallback implements MessagePipeCallback { + @Override + public void onMessage(TextSecureEnvelope envelope) {} + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/api/TextSecureMessageReceiver.java b/src/main/java/org/whispersystems/textsecure/api/TextSecureMessageReceiver.java new file mode 100644 index 0000000000..ecbd8eba92 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/TextSecureMessageReceiver.java @@ -0,0 +1,105 @@ +/** + * Copyright (C) 2014 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.api; + +import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.textsecure.api.crypto.AttachmentCipherInputStream; +import org.whispersystems.textsecure.api.messages.TextSecureAttachmentPointer; +import org.whispersystems.textsecure.api.push.TrustStore; +import org.whispersystems.textsecure.api.util.CredentialsProvider; +import org.whispersystems.textsecure.internal.push.PushServiceSocket; +import org.whispersystems.textsecure.internal.util.StaticCredentialsProvider; +import org.whispersystems.textsecure.internal.websocket.WebSocketConnection; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + * The primary interface for receiving TextSecure messages. + * + * @author Moxie Marlinspike + */ +public class TextSecureMessageReceiver { + + private final PushServiceSocket socket; + private final TrustStore trustStore; + private final String url; + private final CredentialsProvider credentialsProvider; + + /** + * Construct a TextSecureMessageReceiver. + * + * @param url The URL of the TextSecure server. + * @param trustStore The {@link org.whispersystems.textsecure.api.push.TrustStore} containing + * the server's TLS signing certificate. + * @param user The TextSecure user's username (eg. phone number). + * @param password The TextSecure user's password. + * @param signalingKey The 52 byte signaling key assigned to this user at registration. + */ + public TextSecureMessageReceiver(String url, TrustStore trustStore, + String user, String password, String signalingKey) + { + this(url, trustStore, new StaticCredentialsProvider(user, password, signalingKey)); + } + + /** + * Construct a TextSecureMessageReceiver. + * + * @param url The URL of the TextSecure server. + * @param trustStore The {@link org.whispersystems.textsecure.api.push.TrustStore} containing + * the server's TLS signing certificate. + * @param credentials The TextSecure user's credentials. + */ + public TextSecureMessageReceiver(String url, TrustStore trustStore, CredentialsProvider credentials) { + this.url = url; + this.trustStore = trustStore; + this.credentialsProvider = credentials; + this.socket = new PushServiceSocket(url, trustStore, credentials); + } + + /** + * Retrieves a TextSecure attachment. + * + * @param pointer The {@link org.whispersystems.textsecure.api.messages.TextSecureAttachmentPointer} + * received in a {@link org.whispersystems.textsecure.api.messages.TextSecureMessage}. + * @param destination The download destination for this attachment. + * + * @return An InputStream that streams the plaintext attachment contents. + * @throws IOException + * @throws InvalidMessageException + */ + public InputStream retrieveAttachment(TextSecureAttachmentPointer pointer, File destination) + throws IOException, InvalidMessageException + { + socket.retrieveAttachment(pointer.getRelay().orNull(), pointer.getId(), destination); + return new AttachmentCipherInputStream(destination, pointer.getKey()); + } + + /** + * Creates a pipe for receiving TextSecure messages. + * + * Callers must call {@link TextSecureMessagePipe#shutdown()} when finished with the pipe. + * + * @return A TextSecureMessagePipe for receiving TextSecure messages. + */ + public TextSecureMessagePipe createMessagePipe() { + WebSocketConnection webSocket = new WebSocketConnection(url, trustStore, credentialsProvider); + return new TextSecureMessagePipe(webSocket, credentialsProvider); + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/api/TextSecureMessageSender.java b/src/main/java/org/whispersystems/textsecure/api/TextSecureMessageSender.java new file mode 100644 index 0000000000..3ae6095bc7 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/TextSecureMessageSender.java @@ -0,0 +1,393 @@ +/** + * Copyright (C) 2014 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.api; + +import android.util.Log; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.SessionBuilder; +import org.whispersystems.libaxolotl.protocol.CiphertextMessage; +import org.whispersystems.libaxolotl.state.AxolotlStore; +import org.whispersystems.libaxolotl.state.PreKeyBundle; +import org.whispersystems.libaxolotl.util.guava.Optional; +import org.whispersystems.textsecure.api.crypto.TextSecureCipher; +import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException; +import org.whispersystems.textsecure.api.messages.TextSecureAttachment; +import org.whispersystems.textsecure.api.messages.TextSecureAttachmentStream; +import org.whispersystems.textsecure.api.messages.TextSecureGroup; +import org.whispersystems.textsecure.api.messages.TextSecureMessage; +import org.whispersystems.textsecure.api.push.TextSecureAddress; +import org.whispersystems.textsecure.api.push.TrustStore; +import org.whispersystems.textsecure.api.push.exceptions.NetworkFailureException; +import org.whispersystems.textsecure.api.push.exceptions.PushNetworkException; +import org.whispersystems.textsecure.internal.push.MismatchedDevices; +import org.whispersystems.textsecure.internal.push.OutgoingPushMessage; +import org.whispersystems.textsecure.internal.push.OutgoingPushMessageList; +import org.whispersystems.textsecure.internal.push.PushAttachmentData; +import org.whispersystems.textsecure.internal.push.PushBody; +import org.whispersystems.textsecure.internal.push.PushServiceSocket; +import org.whispersystems.textsecure.internal.push.SendMessageResponse; +import org.whispersystems.textsecure.internal.push.StaleDevices; +import org.whispersystems.textsecure.api.push.exceptions.UnregisteredUserException; +import org.whispersystems.textsecure.api.push.exceptions.EncapsulatedExceptions; +import org.whispersystems.textsecure.internal.push.exceptions.MismatchedDevicesException; +import org.whispersystems.textsecure.internal.push.exceptions.StaleDevicesException; +import org.whispersystems.textsecure.internal.util.StaticCredentialsProvider; +import org.whispersystems.textsecure.internal.util.Util; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import static org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type; +import static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent; +import static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer; +import static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext; + +/** + * The main interface for sending TextSecure messages. + * + * @author Moxie Marlinspike + */ +public class TextSecureMessageSender { + + private static final String TAG = TextSecureMessageSender.class.getSimpleName(); + + private final PushServiceSocket socket; + private final AxolotlStore store; + private final TextSecureAddress syncAddress; + private final Optional eventListener; + + /** + * Construct a TextSecureMessageSender. + * + * @param url The URL of the TextSecure server. + * @param trustStore The trust store containing the TextSecure server's signing TLS certificate. + * @param user The TextSecure username (eg phone number). + * @param password The TextSecure user's password. + * @param userId The axolotl recipient id for the local TextSecure user. + * @param store The AxolotlStore. + * @param eventListener An optional event listener, which fires whenever sessions are + * setup or torn down for a recipient. + */ + public TextSecureMessageSender(String url, TrustStore trustStore, + String user, String password, + long userId, AxolotlStore store, + Optional eventListener) + { + this.socket = new PushServiceSocket(url, trustStore, new StaticCredentialsProvider(user, password, null)); + this.store = store; + this.syncAddress = new TextSecureAddress(userId, user, null); + this.eventListener = eventListener; + } + + /** + * Send a delivery receipt for a received message. It is not necessary to call this + * when receiving messages through {@link org.whispersystems.textsecure.api.TextSecureMessagePipe}. + * @param recipient The sender of the received message you're acknowledging. + * @param messageId The message id of the received message you're acknowledging. + * @throws IOException + */ + public void sendDeliveryReceipt(TextSecureAddress recipient, long messageId) throws IOException { + this.socket.sendReceipt(recipient.getNumber(), messageId, recipient.getRelay()); + } + + /** + * Send a message to a single recipient. + * + * @param recipient The message's destination. + * @param message The message. + * @throws UntrustedIdentityException + * @throws IOException + */ + public void sendMessage(TextSecureAddress recipient, TextSecureMessage message) + throws UntrustedIdentityException, IOException + { + byte[] content = createMessageContent(message); + long timestamp = message.getTimestamp(); + SendMessageResponse response = sendMessage(recipient, timestamp, content); + + if (response != null && response.getNeedsSync()) { + byte[] syncMessage = createSyncMessageContent(content, recipient, timestamp); + sendMessage(syncAddress, timestamp, syncMessage); + } + + if (message.isEndSession()) { + store.deleteAllSessions(recipient.getRecipientId()); + + if (eventListener.isPresent()) { + eventListener.get().onSecurityEvent(recipient.getRecipientId()); + } + } + } + + /** + * Send a message to a group. + * + * @param recipients The group members. + * @param message The group message. + * @throws IOException + * @throws EncapsulatedExceptions + */ + public void sendMessage(List recipients, TextSecureMessage message) + throws IOException, EncapsulatedExceptions + { + byte[] content = createMessageContent(message); + sendMessage(recipients, message.getTimestamp(), content); + } + + private byte[] createMessageContent(TextSecureMessage message) throws IOException { + PushMessageContent.Builder builder = PushMessageContent.newBuilder(); + List pointers = createAttachmentPointers(message.getAttachments()); + + if (!pointers.isEmpty()) { + builder.addAllAttachments(pointers); + } + + if (message.getBody().isPresent()) { + builder.setBody(message.getBody().get()); + } + + if (message.getGroupInfo().isPresent()) { + builder.setGroup(createGroupContent(message.getGroupInfo().get())); + } + + if (message.isEndSession()) { + builder.setFlags(PushMessageContent.Flags.END_SESSION_VALUE); + } + + return builder.build().toByteArray(); + } + + private byte[] createSyncMessageContent(byte[] content, TextSecureAddress recipient, long timestamp) { + try { + PushMessageContent.Builder builder = PushMessageContent.parseFrom(content).toBuilder(); + builder.setSync(PushMessageContent.SyncMessageContext.newBuilder() + .setDestination(recipient.getNumber()) + .setTimestamp(timestamp) + .build()); + + return builder.build().toByteArray(); + } catch (InvalidProtocolBufferException e) { + throw new AssertionError(e); + } + } + + private GroupContext createGroupContent(TextSecureGroup group) throws IOException { + GroupContext.Builder builder = GroupContext.newBuilder(); + builder.setId(ByteString.copyFrom(group.getGroupId())); + + if (group.getType() != TextSecureGroup.Type.DELIVER) { + if (group.getType() == TextSecureGroup.Type.UPDATE) builder.setType(GroupContext.Type.UPDATE); + else if (group.getType() == TextSecureGroup.Type.QUIT) builder.setType(GroupContext.Type.QUIT); + else throw new AssertionError("Unknown type: " + group.getType()); + + if (group.getName().isPresent()) builder.setName(group.getName().get()); + if (group.getMembers().isPresent()) builder.addAllMembers(group.getMembers().get()); + + if (group.getAvatar().isPresent() && group.getAvatar().get().isStream()) { + AttachmentPointer pointer = createAttachmentPointer(group.getAvatar().get().asStream()); + builder.setAvatar(pointer); + } + } else { + builder.setType(GroupContext.Type.DELIVER); + } + + return builder.build(); + } + + private void sendMessage(List recipients, long timestamp, byte[] content) + throws IOException, EncapsulatedExceptions + { + List untrustedIdentities = new LinkedList<>(); + List unregisteredUsers = new LinkedList<>(); + List networkExceptions = new LinkedList<>(); + + for (TextSecureAddress recipient : recipients) { + try { + sendMessage(recipient, timestamp, content); + } catch (UntrustedIdentityException e) { + Log.w(TAG, e); + untrustedIdentities.add(e); + } catch (UnregisteredUserException e) { + Log.w(TAG, e); + unregisteredUsers.add(e); + } catch (PushNetworkException e) { + Log.w(TAG, e); + networkExceptions.add(new NetworkFailureException(recipient.getNumber(), e)); + } + } + + if (!untrustedIdentities.isEmpty() || !unregisteredUsers.isEmpty() || !networkExceptions.isEmpty()) { + throw new EncapsulatedExceptions(untrustedIdentities, unregisteredUsers, networkExceptions); + } + } + + private SendMessageResponse sendMessage(TextSecureAddress recipient, long timestamp, byte[] content) + throws UntrustedIdentityException, IOException + { + for (int i=0;i<3;i++) { + try { + OutgoingPushMessageList messages = getEncryptedMessages(socket, recipient, timestamp, content); + return socket.sendMessage(messages); + } catch (MismatchedDevicesException mde) { + Log.w(TAG, mde); + handleMismatchedDevices(socket, recipient, mde.getMismatchedDevices()); + } catch (StaleDevicesException ste) { + Log.w(TAG, ste); + handleStaleDevices(recipient, ste.getStaleDevices()); + } + } + + throw new IOException("Failed to resolve conflicts after 3 attempts!"); + } + + private List createAttachmentPointers(Optional> attachments) throws IOException { + List pointers = new LinkedList<>(); + + if (!attachments.isPresent() || attachments.get().isEmpty()) { + Log.w(TAG, "No attachments present..."); + return pointers; + } + + for (TextSecureAttachment attachment : attachments.get()) { + if (attachment.isStream()) { + Log.w(TAG, "Found attachment, creating pointer..."); + pointers.add(createAttachmentPointer(attachment.asStream())); + } + } + + return pointers; + } + + private AttachmentPointer createAttachmentPointer(TextSecureAttachmentStream attachment) + throws IOException + { + byte[] attachmentKey = Util.getSecretBytes(64); + PushAttachmentData attachmentData = new PushAttachmentData(attachment.getContentType(), + attachment.getInputStream(), + attachment.getLength(), + attachmentKey); + + long attachmentId = socket.sendAttachment(attachmentData); + + return AttachmentPointer.newBuilder() + .setContentType(attachment.getContentType()) + .setId(attachmentId) + .setKey(ByteString.copyFrom(attachmentKey)) + .build(); + } + + + private OutgoingPushMessageList getEncryptedMessages(PushServiceSocket socket, + TextSecureAddress recipient, + long timestamp, + byte[] plaintext) + throws IOException, UntrustedIdentityException + { + List messages = new LinkedList<>(); + + if (!recipient.equals(syncAddress)) { + PushBody masterBody = getEncryptedMessage(socket, recipient, TextSecureAddress.DEFAULT_DEVICE_ID, plaintext); + messages.add(new OutgoingPushMessage(recipient, TextSecureAddress.DEFAULT_DEVICE_ID, masterBody)); + } + + for (int deviceId : store.getSubDeviceSessions(recipient.getRecipientId())) { + PushBody body = getEncryptedMessage(socket, recipient, deviceId, plaintext); + messages.add(new OutgoingPushMessage(recipient, deviceId, body)); + } + + return new OutgoingPushMessageList(recipient.getNumber(), timestamp, recipient.getRelay(), messages); + } + + private PushBody getEncryptedMessage(PushServiceSocket socket, TextSecureAddress recipient, int deviceId, byte[] plaintext) + throws IOException, UntrustedIdentityException + { + if (!store.containsSession(recipient.getRecipientId(), deviceId)) { + try { + List preKeys = socket.getPreKeys(recipient, deviceId); + + for (PreKeyBundle preKey : preKeys) { + try { + SessionBuilder sessionBuilder = new SessionBuilder(store, recipient.getRecipientId(), deviceId); + sessionBuilder.process(preKey); + } catch (org.whispersystems.libaxolotl.UntrustedIdentityException e) { + throw new UntrustedIdentityException("Untrusted identity key!", recipient.getNumber(), preKey.getIdentityKey()); + } + } + + if (eventListener.isPresent()) { + eventListener.get().onSecurityEvent(recipient.getRecipientId()); + } + } catch (InvalidKeyException e) { + throw new IOException(e); + } + } + + TextSecureCipher cipher = new TextSecureCipher(store, recipient.getRecipientId(), deviceId); + CiphertextMessage message = cipher.encrypt(plaintext); + int remoteRegistrationId = cipher.getRemoteRegistrationId(); + + if (message.getType() == CiphertextMessage.PREKEY_TYPE) { + return new PushBody(Type.PREKEY_BUNDLE_VALUE, remoteRegistrationId, message.serialize()); + } else if (message.getType() == CiphertextMessage.WHISPER_TYPE) { + return new PushBody(Type.CIPHERTEXT_VALUE, remoteRegistrationId, message.serialize()); + } else { + throw new AssertionError("Unknown ciphertext type: " + message.getType()); + } + } + + private void handleMismatchedDevices(PushServiceSocket socket, TextSecureAddress recipient, + MismatchedDevices mismatchedDevices) + throws IOException, UntrustedIdentityException + { + try { + for (int extraDeviceId : mismatchedDevices.getExtraDevices()) { + store.deleteSession(recipient.getRecipientId(), extraDeviceId); + } + + for (int missingDeviceId : mismatchedDevices.getMissingDevices()) { + PreKeyBundle preKey = socket.getPreKey(recipient, missingDeviceId); + + try { + SessionBuilder sessionBuilder = new SessionBuilder(store, recipient.getRecipientId(), missingDeviceId); + sessionBuilder.process(preKey); + } catch (org.whispersystems.libaxolotl.UntrustedIdentityException e) { + throw new UntrustedIdentityException("Untrusted identity key!", recipient.getNumber(), preKey.getIdentityKey()); + } + } + } catch (InvalidKeyException e) { + throw new IOException(e); + } + } + + private void handleStaleDevices(TextSecureAddress recipient, StaleDevices staleDevices) { + long recipientId = recipient.getRecipientId(); + + for (int staleDeviceId : staleDevices.getStaleDevices()) { + store.deleteSession(recipientId, staleDeviceId); + } + } + + public static interface EventListener { + public void onSecurityEvent(long recipientId); + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/api/crypto/AttachmentCipherInputStream.java b/src/main/java/org/whispersystems/textsecure/api/crypto/AttachmentCipherInputStream.java new file mode 100644 index 0000000000..d0f23ad23b --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/crypto/AttachmentCipherInputStream.java @@ -0,0 +1,220 @@ +/** + * Copyright (C) 2013-2014 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.api.crypto; + +import org.whispersystems.libaxolotl.InvalidMacException; +import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.textsecure.internal.util.Util; + +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; + +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; + +/** + * 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 static final int CIPHER_KEY_SIZE = 32; + private static final int MAC_KEY_SIZE = 32; + + private Cipher cipher; + private boolean done; + private long totalDataSize; + private long totalRead; + private byte[] overflowBuffer; + + public AttachmentCipherInputStream(File file, byte[] combinedKeyMaterial) + throws IOException, InvalidMessageException + { + super(file); + + try { + byte[][] parts = Util.split(combinedKeyMaterial, CIPHER_KEY_SIZE, MAC_KEY_SIZE); + Mac mac = Mac.getInstance("HmacSHA256"); + + mac.init(new SecretKeySpec(parts[1], "HmacSHA256")); + + 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 | InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } catch (InvalidMacException e) { + throw new InvalidMessageException(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; + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public long skip(long byteCount) throws IOException { + long skipped = 0L; + while (skipped < byteCount) { + byte[] buf = new byte[Math.min(4096, (int)(byteCount-skipped))]; + int read = read(buf); + + skipped += read; + } + + return skipped; + } + + private int readFinal(byte[] buffer, int offset, int length) throws IOException { + try { + int flourish = cipher.doFinal(buffer, offset); + + done = true; + return flourish; + } catch (IllegalBlockSizeException | BadPaddingException | ShortBufferException e) { + throw new IOException(e); + } + } + + private int readIncremental(byte[] buffer, int offset, int length) throws IOException { + int readLength = 0; + if (null != overflowBuffer) { + if (overflowBuffer.length > length) { + System.arraycopy(overflowBuffer, 0, buffer, offset, length); + overflowBuffer = Arrays.copyOfRange(overflowBuffer, length, overflowBuffer.length); + return length; + } else if (overflowBuffer.length == length) { + System.arraycopy(overflowBuffer, 0, buffer, offset, length); + overflowBuffer = null; + return length; + } else { + System.arraycopy(overflowBuffer, 0, buffer, offset, overflowBuffer.length); + readLength += overflowBuffer.length; + offset += readLength; + length -= readLength; + overflowBuffer = null; + } + } + + 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 { + int outputLen = cipher.getOutputSize(read); + + if (outputLen <= length) { + readLength += cipher.update(internalBuffer, 0, read, buffer, offset); + return readLength; + } + + byte[] transientBuffer = new byte[outputLen]; + outputLen = cipher.update(internalBuffer, 0, read, transientBuffer, 0); + if (outputLen <= length) { + System.arraycopy(transientBuffer, 0, buffer, offset, outputLen); + readLength += outputLen; + } else { + System.arraycopy(transientBuffer, 0, buffer, offset, length); + overflowBuffer = Arrays.copyOfRange(transientBuffer, length, outputLen); + readLength += length; + } + return readLength; + } 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/src/main/java/org/whispersystems/textsecure/api/crypto/AttachmentCipherOutputStream.java b/src/main/java/org/whispersystems/textsecure/api/crypto/AttachmentCipherOutputStream.java new file mode 100644 index 0000000000..09d06433b0 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/crypto/AttachmentCipherOutputStream.java @@ -0,0 +1,121 @@ +/** + * Copyright (C) 2014 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.api.crypto; + +import org.whispersystems.textsecure.internal.util.Util; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; + +public class AttachmentCipherOutputStream extends OutputStream { + + private final Cipher cipher; + private final Mac mac; + private final OutputStream outputStream; + + private long ciphertextLength = 0; + + public AttachmentCipherOutputStream(byte[] combinedKeyMaterial, + OutputStream outputStream) + throws IOException + { + try { + this.outputStream = outputStream; + this.cipher = initializeCipher(); + this.mac = initializeMac(); + + byte[][] keyParts = Util.split(combinedKeyMaterial, 32, 32); + + this.cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyParts[0], "AES")); + this.mac.init(new SecretKeySpec(keyParts[1], "HmacSHA256")); + + mac.update(cipher.getIV()); + outputStream.write(cipher.getIV()); + ciphertextLength += cipher.getIV().length; + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + @Override + public void write(byte[] buffer) throws IOException { + write(buffer, 0, buffer.length); + } + + @Override + public void write(byte[] buffer, int offset, int length) throws IOException { + byte[] ciphertext = cipher.update(buffer, offset, length); + + if (ciphertext != null) { + mac.update(ciphertext); + outputStream.write(ciphertext); + ciphertextLength += ciphertext.length; + } + } + + @Override + public void write(int b) { + throw new AssertionError("NYI"); + } + + @Override + public void flush() throws IOException { + try { + byte[] ciphertext = cipher.doFinal(); + byte[] auth = mac.doFinal(ciphertext); + + outputStream.write(ciphertext); + outputStream.write(auth); + + ciphertextLength += ciphertext.length; + ciphertextLength += auth.length; + + outputStream.flush(); + } catch (IllegalBlockSizeException | BadPaddingException e) { + throw new AssertionError(e); + } + } + + public static long getCiphertextLength(long plaintextLength) { + return 16 + (((plaintextLength / 16) +1) * 16) + 32; + } + + private Mac initializeMac() { + try { + return Mac.getInstance("HmacSHA256"); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private Cipher initializeCipher() { + try { + return Cipher.getInstance("AES/CBC/PKCS5Padding"); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new AssertionError(e); + } + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/crypto/TextSecureCipher.java b/src/main/java/org/whispersystems/textsecure/api/crypto/TextSecureCipher.java new file mode 100644 index 0000000000..46376a4f2f --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/crypto/TextSecureCipher.java @@ -0,0 +1,167 @@ +/** + * Copyright (C) 2014 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.api.crypto; + +import com.google.protobuf.InvalidProtocolBufferException; + +import org.whispersystems.libaxolotl.DuplicateMessageException; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.InvalidKeyIdException; +import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.libaxolotl.InvalidVersionException; +import org.whispersystems.libaxolotl.LegacyMessageException; +import org.whispersystems.libaxolotl.NoSessionException; +import org.whispersystems.libaxolotl.SessionCipher; +import org.whispersystems.libaxolotl.UntrustedIdentityException; +import org.whispersystems.libaxolotl.protocol.CiphertextMessage; +import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; +import org.whispersystems.libaxolotl.protocol.WhisperMessage; +import org.whispersystems.libaxolotl.state.AxolotlStore; +import org.whispersystems.textsecure.api.messages.TextSecureAttachment; +import org.whispersystems.textsecure.api.messages.TextSecureAttachmentPointer; +import org.whispersystems.textsecure.api.messages.TextSecureEnvelope; +import org.whispersystems.textsecure.api.messages.TextSecureGroup; +import org.whispersystems.textsecure.api.messages.TextSecureMessage; +import org.whispersystems.textsecure.internal.push.PushTransportDetails; + +import java.util.LinkedList; +import java.util.List; + +import static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent; +import static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type.DELIVER; + +/** + * This is used to decrypt received {@link org.whispersystems.textsecure.api.messages.TextSecureEnvelope}s. + * + * @author Moxie Marlinspike + */ +public class TextSecureCipher { + + private final SessionCipher sessionCipher; + + public TextSecureCipher(AxolotlStore axolotlStore, long recipientId, int deviceId) { + this.sessionCipher = new SessionCipher(axolotlStore, recipientId, deviceId); + } + + public CiphertextMessage encrypt(byte[] unpaddedMessage) { + PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); + return sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage)); + } + + /** + * Decrypt a received {@link org.whispersystems.textsecure.api.messages.TextSecureEnvelope} + * + * @param envelope The received TextSecureEnvelope + * @return a decrypted TextSecureMessage + * @throws InvalidVersionException + * @throws InvalidMessageException + * @throws InvalidKeyException + * @throws DuplicateMessageException + * @throws InvalidKeyIdException + * @throws UntrustedIdentityException + * @throws LegacyMessageException + * @throws NoSessionException + */ + public TextSecureMessage decrypt(TextSecureEnvelope envelope) + throws InvalidVersionException, InvalidMessageException, InvalidKeyException, + DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException, + LegacyMessageException, NoSessionException + { + try { + byte[] paddedMessage; + + if (envelope.isPreKeyWhisperMessage()) { + paddedMessage = sessionCipher.decrypt(new PreKeyWhisperMessage(envelope.getMessage())); + } else if (envelope.isWhisperMessage()) { + paddedMessage = sessionCipher.decrypt(new WhisperMessage(envelope.getMessage())); + } else if (envelope.isPlaintext()) { + paddedMessage = envelope.getMessage(); + } else { + throw new InvalidMessageException("Unknown type: " + envelope.getType()); + } + + PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); + PushMessageContent content = PushMessageContent.parseFrom(transportDetails.getStrippedPaddingMessageBody(paddedMessage)); + + return createTextSecureMessage(envelope, content); + } catch (InvalidProtocolBufferException e) { + throw new InvalidMessageException(e); + } + } + + public int getRemoteRegistrationId() { + return sessionCipher.getRemoteRegistrationId(); + } + + private TextSecureMessage createTextSecureMessage(TextSecureEnvelope envelope, PushMessageContent content) { + TextSecureGroup groupInfo = createGroupInfo(envelope, content); + List attachments = new LinkedList<>(); + boolean endSession = ((content.getFlags() & PushMessageContent.Flags.END_SESSION_VALUE) != 0); + boolean secure = envelope.isWhisperMessage() || envelope.isPreKeyWhisperMessage(); + + for (PushMessageContent.AttachmentPointer pointer : content.getAttachmentsList()) { + attachments.add(new TextSecureAttachmentPointer(pointer.getId(), + pointer.getContentType(), + pointer.getKey().toByteArray(), + envelope.getRelay())); + } + + return new TextSecureMessage(envelope.getTimestamp(), groupInfo, attachments, + content.getBody(), secure, endSession); + } + + private TextSecureGroup createGroupInfo(TextSecureEnvelope envelope, PushMessageContent content) { + if (!content.hasGroup()) return null; + + TextSecureGroup.Type type; + + switch (content.getGroup().getType()) { + case DELIVER: type = TextSecureGroup.Type.DELIVER; break; + case UPDATE: type = TextSecureGroup.Type.UPDATE; break; + case QUIT: type = TextSecureGroup.Type.QUIT; break; + default: type = TextSecureGroup.Type.UNKNOWN; break; + } + + if (content.getGroup().getType() != DELIVER) { + String name = null; + List members = null; + TextSecureAttachmentPointer avatar = null; + + if (content.getGroup().hasName()) { + name = content.getGroup().getName(); + } + + if (content.getGroup().getMembersCount() > 0) { + members = content.getGroup().getMembersList(); + } + + if (content.getGroup().hasAvatar()) { + avatar = new TextSecureAttachmentPointer(content.getGroup().getAvatar().getId(), + content.getGroup().getAvatar().getContentType(), + content.getGroup().getAvatar().getKey().toByteArray(), + envelope.getRelay()); + } + + return new TextSecureGroup(type, content.getGroup().getId().toByteArray(), name, members, avatar); + } + + return new TextSecureGroup(content.getGroup().getId().toByteArray()); + } + + +} + diff --git a/src/main/java/org/whispersystems/textsecure/api/crypto/UntrustedIdentityException.java b/src/main/java/org/whispersystems/textsecure/api/crypto/UntrustedIdentityException.java new file mode 100644 index 0000000000..acdace2d61 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/crypto/UntrustedIdentityException.java @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2014 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.api.crypto; + +import org.whispersystems.libaxolotl.IdentityKey; + +public class UntrustedIdentityException extends Exception { + + private final IdentityKey identityKey; + private final String e164number; + + public UntrustedIdentityException(String s, String e164number, IdentityKey identityKey) { + super(s); + this.e164number = e164number; + this.identityKey = identityKey; + } + + public UntrustedIdentityException(UntrustedIdentityException e) { + this(e.getMessage(), e.getE164Number(), e.getIdentityKey()); + } + + public IdentityKey getIdentityKey() { + return identityKey; + } + + public String getE164Number() { + return e164number; + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureAttachment.java b/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureAttachment.java new file mode 100644 index 0000000000..c58b709f62 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureAttachment.java @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2014 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.api.messages; + +public abstract class TextSecureAttachment { + + private final String contentType; + + protected TextSecureAttachment(String contentType) { + this.contentType = contentType; + } + + public String getContentType() { + return contentType; + } + + public abstract boolean isStream(); + public abstract boolean isPointer(); + + public TextSecureAttachmentStream asStream() { + return (TextSecureAttachmentStream)this; + } + + public TextSecureAttachmentPointer asPointer() { + return (TextSecureAttachmentPointer)this; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureAttachmentPointer.java b/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureAttachmentPointer.java new file mode 100644 index 0000000000..a86e6199d2 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureAttachmentPointer.java @@ -0,0 +1,62 @@ +/** + * Copyright (C) 2014 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.api.messages; + +import org.whispersystems.libaxolotl.util.guava.Optional; + +/** + * Represents a received TextSecureMessage attachment "handle." This + * is a pointer to the actual attachment content, which needs to be + * retrieved using {@link org.whispersystems.textsecure.api.TextSecureMessageReceiver#retrieveAttachment(TextSecureAttachmentPointer, java.io.File)} + * + * @author Moxie Marlinspike + */ +public class TextSecureAttachmentPointer extends TextSecureAttachment { + + private final long id; + private final byte[] key; + private final Optional relay; + + public TextSecureAttachmentPointer(long id, String contentType, byte[] key, String relay) { + super(contentType); + this.id = id; + this.key = key; + this.relay = Optional.fromNullable(relay); + } + + public long getId() { + return id; + } + + public byte[] getKey() { + return key; + } + + @Override + public boolean isStream() { + return false; + } + + @Override + public boolean isPointer() { + return true; + } + + public Optional getRelay() { + return relay; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureAttachmentStream.java b/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureAttachmentStream.java new file mode 100644 index 0000000000..2f57a1bcc8 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureAttachmentStream.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2014 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.api.messages; + +import java.io.InputStream; + +/** + * Represents a local TextSecureAttachment to be sent. + */ +public class TextSecureAttachmentStream extends TextSecureAttachment { + + private final InputStream inputStream; + private final long length; + + public TextSecureAttachmentStream(InputStream inputStream, String contentType, long length) { + super(contentType); + this.inputStream = inputStream; + this.length = length; + } + + @Override + public boolean isStream() { + return true; + } + + @Override + public boolean isPointer() { + return false; + } + + public InputStream getInputStream() { + return inputStream; + } + + public long getLength() { + return length; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureEnvelope.java b/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureEnvelope.java new file mode 100644 index 0000000000..630b7807d9 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureEnvelope.java @@ -0,0 +1,252 @@ +/** + * Copyright (C) 2014 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.api.messages; + +import android.util.Log; + +import com.google.protobuf.ByteString; + +import org.whispersystems.libaxolotl.InvalidVersionException; +import org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal; +import org.whispersystems.textsecure.internal.util.Base64; +import org.whispersystems.textsecure.internal.util.Hex; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +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; + +/** + * This class represents an encrypted TextSecure envelope. + * + * The envelope contains the wrapping information, such as the sender, the + * message timestamp, the encrypted message type, etc. + * + * @author Moxie Marlinspike + */ +public class TextSecureEnvelope { + + private static final String TAG = TextSecureEnvelope.class.getSimpleName(); + + private static final int SUPPORTED_VERSION = 1; + private static final int CIPHER_KEY_SIZE = 32; + private static final int MAC_KEY_SIZE = 20; + private static final int MAC_SIZE = 10; + + private static final int VERSION_OFFSET = 0; + private static final int VERSION_LENGTH = 1; + private static final int IV_OFFSET = VERSION_OFFSET + VERSION_LENGTH; + private static final int IV_LENGTH = 16; + private static final int CIPHERTEXT_OFFSET = IV_OFFSET + IV_LENGTH; + + private final IncomingPushMessageSignal signal; + + /** + * Construct an envelope from a serialized, Base64 encoded TextSecureEnvelope, encrypted + * with a signaling key. + * + * @param message The serialized TextSecureEnvelope, base64 encoded and encrypted. + * @param signalingKey The signaling key. + * @throws IOException + * @throws InvalidVersionException + */ + public TextSecureEnvelope(String message, String signalingKey) + throws IOException, InvalidVersionException + { + this(Base64.decode(message), signalingKey); + } + + /** + * Construct an envelope from a serialized TextSecureEnvelope, encrypted with a signaling key. + * + * @param ciphertext The serialized and encrypted TextSecureEnvelope. + * @param signalingKey The signaling key. + * @throws InvalidVersionException + * @throws IOException + */ + public TextSecureEnvelope(byte[] ciphertext, String signalingKey) + throws InvalidVersionException, IOException + { + if (ciphertext.length < VERSION_LENGTH || ciphertext[VERSION_OFFSET] != SUPPORTED_VERSION) + throw new InvalidVersionException("Unsupported version!"); + + SecretKeySpec cipherKey = getCipherKey(signalingKey); + SecretKeySpec macKey = getMacKey(signalingKey); + + verifyMac(ciphertext, macKey); + + this.signal = IncomingPushMessageSignal.parseFrom(getPlaintext(ciphertext, cipherKey)); + } + + public TextSecureEnvelope(int type, String source, int sourceDevice, + String relay, long timestamp, byte[] message) + { + this.signal = IncomingPushMessageSignal.newBuilder() + .setType(IncomingPushMessageSignal.Type.valueOf(type)) + .setSource(source) + .setSourceDevice(sourceDevice) + .setRelay(relay) + .setTimestamp(timestamp) + .setMessage(ByteString.copyFrom(message)) + .build(); + } + + /** + * @return The envelope's sender. + */ + public String getSource() { + return signal.getSource(); + } + + /** + * @return The envelope's sender device ID. + */ + public int getSourceDevice() { + return signal.getSourceDevice(); + } + + /** + * @return The envelope content type. + */ + public int getType() { + return signal.getType().getNumber(); + } + + /** + * @return The federated server this envelope came from. + */ + public String getRelay() { + return signal.getRelay(); + } + + /** + * @return The timestamp this envelope was sent. + */ + public long getTimestamp() { + return signal.getTimestamp(); + } + + /** + * @return The envelope's containing message. + */ + public byte[] getMessage() { + return signal.getMessage().toByteArray(); + } + + /** + * @return true if the containing message is a {@link org.whispersystems.libaxolotl.protocol.WhisperMessage} + */ + public boolean isWhisperMessage() { + return signal.getType().getNumber() == IncomingPushMessageSignal.Type.CIPHERTEXT_VALUE; + } + + /** + * @return true if the containing message is a {@link org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage} + */ + public boolean isPreKeyWhisperMessage() { + return signal.getType().getNumber() == IncomingPushMessageSignal.Type.PREKEY_BUNDLE_VALUE; + } + + /** + * @return true if the containing message is plaintext. + */ + public boolean isPlaintext() { + return signal.getType().getNumber() == IncomingPushMessageSignal.Type.PLAINTEXT_VALUE; + } + + /** + * @return true if the containing message is a delivery receipt. + */ + public boolean isReceipt() { + return signal.getType().getNumber() == IncomingPushMessageSignal.Type.RECEIPT_VALUE; + } + + private byte[] getPlaintext(byte[] ciphertext, SecretKeySpec cipherKey) throws IOException { + try { + byte[] ivBytes = new byte[IV_LENGTH]; + System.arraycopy(ciphertext, IV_OFFSET, ivBytes, 0, ivBytes.length); + IvParameterSpec iv = new IvParameterSpec(ivBytes); + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, cipherKey, iv); + + return cipher.doFinal(ciphertext, CIPHERTEXT_OFFSET, + ciphertext.length - VERSION_LENGTH - IV_LENGTH - MAC_SIZE); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + Log.w(TAG, e); + throw new IOException("Bad padding?"); + } + } + + private void verifyMac(byte[] ciphertext, SecretKeySpec macKey) throws IOException { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(macKey); + + if (ciphertext.length < MAC_SIZE + 1) + throw new IOException("Invalid MAC!"); + + mac.update(ciphertext, 0, ciphertext.length - MAC_SIZE); + + byte[] ourMacFull = mac.doFinal(); + byte[] ourMacBytes = new byte[MAC_SIZE]; + System.arraycopy(ourMacFull, 0, ourMacBytes, 0, ourMacBytes.length); + + byte[] theirMacBytes = new byte[MAC_SIZE]; + System.arraycopy(ciphertext, ciphertext.length-MAC_SIZE, theirMacBytes, 0, theirMacBytes.length); + + Log.w(TAG, "Our MAC: " + Hex.toString(ourMacBytes)); + Log.w(TAG, "Thr MAC: " + Hex.toString(theirMacBytes)); + + if (!Arrays.equals(ourMacBytes, theirMacBytes)) { + throw new IOException("Invalid MAC compare!"); + } + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new AssertionError(e); + } + } + + + private SecretKeySpec getCipherKey(String signalingKey) throws IOException { + byte[] signalingKeyBytes = Base64.decode(signalingKey); + byte[] cipherKey = new byte[CIPHER_KEY_SIZE]; + System.arraycopy(signalingKeyBytes, 0, cipherKey, 0, cipherKey.length); + + return new SecretKeySpec(cipherKey, "AES"); + } + + + private SecretKeySpec getMacKey(String signalingKey) throws IOException { + byte[] signalingKeyBytes = Base64.decode(signalingKey); + byte[] macKey = new byte[MAC_KEY_SIZE]; + System.arraycopy(signalingKeyBytes, CIPHER_KEY_SIZE, macKey, 0, macKey.length); + + return new SecretKeySpec(macKey, "HmacSHA256"); + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureGroup.java b/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureGroup.java new file mode 100644 index 0000000000..83d6a755d2 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureGroup.java @@ -0,0 +1,99 @@ +/** + * Copyright (C) 2014 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.api.messages; + +import org.whispersystems.libaxolotl.util.guava.Optional; + +import java.util.List; + +/** + * Group information to include in TextSecureMessages destined to groups. + * + * This class represents a "context" that is included with textsecure messages + * to make them group messages. There are three types of context: + * + * 1) Update -- Sent when either creating a group, or updating the properties + * of a group (such as the avatar icon, membership list, or title). + * 2) Deliver -- Sent when a message is to be delivered to an existing group. + * 3) Quit -- Sent when the sender wishes to leave an existing group. + * + * @author Moxie Marlinspike + */ +public class TextSecureGroup { + + public enum Type { + UNKNOWN, + UPDATE, + DELIVER, + QUIT + } + + private final byte[] groupId; + private final Type type; + private final Optional name; + private final Optional> members; + private final Optional avatar; + + + /** + * Construct a DELIVER group context. + * @param groupId + */ + public TextSecureGroup(byte[] groupId) { + this(Type.DELIVER, groupId, null, null, null); + } + + /** + * Construct a group context. + * @param type The group message type (update, deliver, quit). + * @param groupId The group ID. + * @param name The group title. + * @param members The group membership list. + * @param avatar The group avatar icon. + */ + public TextSecureGroup(Type type, byte[] groupId, String name, + List members, + TextSecureAttachment avatar) + { + this.type = type; + this.groupId = groupId; + this.name = Optional.fromNullable(name); + this.members = Optional.fromNullable(members); + this.avatar = Optional.fromNullable(avatar); + } + + public byte[] getGroupId() { + return groupId; + } + + public Type getType() { + return type; + } + + public Optional getName() { + return name; + } + + public Optional> getMembers() { + return members; + } + + public Optional getAvatar() { + return avatar; + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureMessage.java b/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureMessage.java new file mode 100644 index 0000000000..ef31e1e391 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/messages/TextSecureMessage.java @@ -0,0 +1,136 @@ +/** + * Copyright (C) 2014 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.api.messages; + +import org.whispersystems.libaxolotl.util.guava.Optional; + +import java.util.LinkedList; +import java.util.List; + +/** + * Represents a decrypted text secure message. + */ +public class TextSecureMessage { + + private final long timestamp; + private final Optional> attachments; + private final Optional body; + private final Optional group; + private final boolean secure; + private final boolean endSession; + + /** + * Construct a TextSecureMessage with a body and no attachments. + * + * @param timestamp The sent timestamp. + * @param body The message contents. + */ + public TextSecureMessage(long timestamp, String body) { + this(timestamp, (List)null, body); + } + + public TextSecureMessage(final long timestamp, final TextSecureAttachment attachment, final String body) { + this(timestamp, new LinkedList() {{add(attachment);}}, body); + } + + /** + * Construct a TextSecureMessage with a body and list of attachments. + * + * @param timestamp The sent timestamp. + * @param attachments The attachments. + * @param body The message contents. + */ + public TextSecureMessage(long timestamp, List attachments, String body) { + this(timestamp, null, attachments, body); + } + + /** + * Construct a TextSecure group message with attachments and body. + * + * @param timestamp The sent timestamp. + * @param group The group information. + * @param attachments The attachments. + * @param body The message contents. + */ + public TextSecureMessage(long timestamp, TextSecureGroup group, List attachments, String body) { + this(timestamp, group, attachments, body, true, false); + } + + /** + * Construct a TextSecureMessage. + * + * @param timestamp The sent timestamp. + * @param group The group information (or null if none). + * @param attachments The attachments (or null if none). + * @param body The message contents. + * @param secure Flag indicating whether this message is to be encrypted. + * @param endSession Flag indicating whether this message should close a session. + */ + public TextSecureMessage(long timestamp, TextSecureGroup group, List attachments, String body, boolean secure, boolean endSession) { + this.timestamp = timestamp; + this.body = Optional.fromNullable(body); + this.group = Optional.fromNullable(group); + this.secure = secure; + this.endSession = endSession; + + if (attachments != null && !attachments.isEmpty()) { + this.attachments = Optional.of(attachments); + } else { + this.attachments = Optional.absent(); + } + } + + /** + * @return The message timestamp. + */ + public long getTimestamp() { + return timestamp; + } + + /** + * @return The message attachments (if any). + */ + public Optional> getAttachments() { + return attachments; + } + + /** + * @return The message body (if any). + */ + public Optional getBody() { + return body; + } + + /** + * @return The message group info (if any). + */ + public Optional getGroupInfo() { + return group; + } + + public boolean isSecure() { + return secure; + } + + public boolean isEndSession() { + return endSession; + } + + public boolean isGroupUpdate() { + return group.isPresent() && group.get().getType() != TextSecureGroup.Type.DELIVER; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/push/ContactTokenDetails.java b/src/main/java/org/whispersystems/textsecure/api/push/ContactTokenDetails.java new file mode 100644 index 0000000000..3012492446 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/ContactTokenDetails.java @@ -0,0 +1,62 @@ +/** + * Copyright (C) 2014 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.api.push; + +/** + * A class that represents a contact's registration state. + */ +public class ContactTokenDetails { + + private String token; + private String relay; + private String number; + private boolean supportsSms; + + public ContactTokenDetails() {} + + /** + * @return The "anonymized" token (truncated hash) that's transmitted to the server. + */ + public String getToken() { + return token; + } + + /** + * @return The federated server this contact is registered with, or null if on your server. + */ + public String getRelay() { + return relay; + } + + /** + * @return Whether this contact supports receiving encrypted SMS. + */ + public boolean isSupportsSms() { + return supportsSms; + } + + public void setNumber(String number) { + this.number = number; + } + + /** + * @return This contact's username (e164 formatted number). + */ + public String getNumber() { + return number; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/push/SignedPreKeyEntity.java b/src/main/java/org/whispersystems/textsecure/api/push/SignedPreKeyEntity.java new file mode 100644 index 0000000000..663568a849 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/SignedPreKeyEntity.java @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2014 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.api.push; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.textsecure.internal.push.PreKeyEntity; +import org.whispersystems.textsecure.internal.util.Base64; + +import java.io.IOException; + +public class SignedPreKeyEntity extends PreKeyEntity { + + @JsonProperty + @JsonSerialize(using = ByteArraySerializer.class) + @JsonDeserialize(using = ByteArrayDeserializer.class) + private byte[] signature; + + public SignedPreKeyEntity() {} + + public SignedPreKeyEntity(int keyId, ECPublicKey publicKey, byte[] signature) { + super(keyId, publicKey); + this.signature = signature; + } + + public byte[] getSignature() { + return signature; + } + + private static class ByteArraySerializer extends JsonSerializer { + @Override + public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(Base64.encodeBytesWithoutPadding(value)); + } + } + + private static class ByteArrayDeserializer extends JsonDeserializer { + + @Override + public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return Base64.decodeWithoutPadding(p.getValueAsString()); + } + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/push/TextSecureAddress.java b/src/main/java/org/whispersystems/textsecure/api/push/TextSecureAddress.java new file mode 100644 index 0000000000..ed44fdde73 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/TextSecureAddress.java @@ -0,0 +1,80 @@ +/** + * Copyright (C) 2014 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.api.push; + +/** + * A class representing a message destination or origin. + */ +public class TextSecureAddress { + + public static final int DEFAULT_DEVICE_ID = 1; + + private final long recipientId; + private final String e164number; + private final String relay; + + /** + * Construct a PushAddress. + * + * @param recipientId The axolotl recipient ID of this destination. + * @param e164number The TextSecure username of this destination (eg e164 representation of a phone number). + * @param relay The TextSecure federated server this user is registered with (if not your own server). + */ + public TextSecureAddress(long recipientId, String e164number, String relay) { + this.recipientId = recipientId; + this.e164number = e164number; + this.relay = relay; + } + + public String getNumber() { + return e164number; + } + + public String getRelay() { + return relay; + } + + public long getRecipientId() { + return recipientId; + } + + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof TextSecureAddress)) return false; + + TextSecureAddress that = (TextSecureAddress)other; + + return this.recipientId == that.recipientId && + equals(this.e164number, that.e164number) && + equals(this.relay, that.relay); + } + + @Override + public int hashCode() { + int hashCode = (int)this.recipientId; + + if (this.e164number != null) hashCode ^= this.e164number.hashCode(); + if (this.relay != null) hashCode ^= this.relay.hashCode(); + + return hashCode; + } + + private boolean equals(String one, String two) { + if (one == null) return two == null; + return one.equals(two); + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/push/TrustStore.java b/src/main/java/org/whispersystems/textsecure/api/push/TrustStore.java new file mode 100644 index 0000000000..e62e4f1a8a --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/TrustStore.java @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2014 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.api.push; + +import java.io.InputStream; + +/** + * A class that represents a Java {@link java.security.KeyStore} and + * its associated password. + */ +public interface TrustStore { + public InputStream getKeyStoreInputStream(); + public String getKeyStorePassword(); +} + diff --git a/src/main/java/org/whispersystems/textsecure/api/push/exceptions/AuthorizationFailedException.java b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/AuthorizationFailedException.java new file mode 100644 index 0000000000..32ac769c58 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/AuthorizationFailedException.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2014 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.api.push.exceptions; + +public class AuthorizationFailedException extends NonSuccessfulResponseCodeException { + public AuthorizationFailedException(String s) { + super(s); + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/push/exceptions/EncapsulatedExceptions.java b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/EncapsulatedExceptions.java new file mode 100644 index 0000000000..dc0f15030b --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/EncapsulatedExceptions.java @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2014 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.api.push.exceptions; + +import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException; + +import java.util.List; + +public class EncapsulatedExceptions extends Throwable { + + private final List untrustedIdentityExceptions; + private final List unregisteredUserExceptions; + private final List networkExceptions; + + public EncapsulatedExceptions(List untrustedIdentities, + List unregisteredUsers, + List networkExceptions) + { + this.untrustedIdentityExceptions = untrustedIdentities; + this.unregisteredUserExceptions = unregisteredUsers; + this.networkExceptions = networkExceptions; + } + + public List getUntrustedIdentityExceptions() { + return untrustedIdentityExceptions; + } + + public List getUnregisteredUserExceptions() { + return unregisteredUserExceptions; + } + + public List getNetworkExceptions() { + return networkExceptions; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/push/exceptions/ExpectationFailedException.java b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/ExpectationFailedException.java new file mode 100644 index 0000000000..91e3a4c1e0 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/ExpectationFailedException.java @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2014 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.api.push.exceptions; + +public class ExpectationFailedException extends NonSuccessfulResponseCodeException { +} diff --git a/src/main/java/org/whispersystems/textsecure/api/push/exceptions/NetworkFailureException.java b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/NetworkFailureException.java new file mode 100644 index 0000000000..bc9b42c5b3 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/NetworkFailureException.java @@ -0,0 +1,15 @@ +package org.whispersystems.textsecure.api.push.exceptions; + +public class NetworkFailureException extends Exception { + + private final String e164number; + + public NetworkFailureException(String e164number, Exception nested) { + super(nested); + this.e164number = e164number; + } + + public String getE164number() { + return e164number; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/push/exceptions/NonSuccessfulResponseCodeException.java b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/NonSuccessfulResponseCodeException.java new file mode 100644 index 0000000000..c366dcf23a --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/NonSuccessfulResponseCodeException.java @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2014 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.api.push.exceptions; + +import java.io.IOException; + +public class NonSuccessfulResponseCodeException extends IOException { + + public NonSuccessfulResponseCodeException() { + super(); + } + + public NonSuccessfulResponseCodeException(String s) { + super(s); + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/push/exceptions/NotFoundException.java b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/NotFoundException.java new file mode 100644 index 0000000000..7717248455 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/NotFoundException.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2014 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.api.push.exceptions; + +public class NotFoundException extends NonSuccessfulResponseCodeException { + public NotFoundException(String s) { + super(s); + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/push/exceptions/PushNetworkException.java b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/PushNetworkException.java new file mode 100644 index 0000000000..f875ba900d --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/PushNetworkException.java @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2014 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.api.push.exceptions; + +import java.io.IOException; + +public class PushNetworkException extends IOException { + + public PushNetworkException(Exception exception) { + super(exception); + } + + public PushNetworkException(String s) { + super(s); + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/api/push/exceptions/RateLimitException.java b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/RateLimitException.java new file mode 100644 index 0000000000..bf15b3cf7d --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/RateLimitException.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2014 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.api.push.exceptions; + +public class RateLimitException extends NonSuccessfulResponseCodeException { + public RateLimitException(String s) { + super(s); + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/push/exceptions/UnregisteredUserException.java b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/UnregisteredUserException.java new file mode 100644 index 0000000000..eee53b09d5 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/push/exceptions/UnregisteredUserException.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2014 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.api.push.exceptions; + +import java.io.IOException; + +public class UnregisteredUserException extends IOException { + + private final String e164number; + + public UnregisteredUserException(String e164number, Exception exception) { + super(exception); + this.e164number = e164number; + } + + public String getE164Number() { + return e164number; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/util/CredentialsProvider.java b/src/main/java/org/whispersystems/textsecure/api/util/CredentialsProvider.java new file mode 100644 index 0000000000..baa745adee --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/util/CredentialsProvider.java @@ -0,0 +1,7 @@ +package org.whispersystems.textsecure.api.util; + +public interface CredentialsProvider { + public String getUser(); + public String getPassword(); + public String getSignalingKey(); +} diff --git a/src/main/java/org/whispersystems/textsecure/api/util/InvalidNumberException.java b/src/main/java/org/whispersystems/textsecure/api/util/InvalidNumberException.java new file mode 100644 index 0000000000..6bafebace4 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/util/InvalidNumberException.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2014 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.api.util; + +public class InvalidNumberException extends Throwable { + public InvalidNumberException(String s) { + super(s); + } +} diff --git a/src/main/java/org/whispersystems/textsecure/api/util/PhoneNumberFormatter.java b/src/main/java/org/whispersystems/textsecure/api/util/PhoneNumberFormatter.java new file mode 100644 index 0000000000..1b228eccec --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/api/util/PhoneNumberFormatter.java @@ -0,0 +1,137 @@ +/** + * Copyright (C) 2014 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.api.util; + +import android.util.Log; + +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; + +import java.util.Locale; + +/** + * Phone number formats are a pain. + * + * @author Moxie Marlinspike + * + */ +public class PhoneNumberFormatter { + + private static final String TAG = PhoneNumberFormatter.class.getSimpleName(); + + public static boolean isValidNumber(String number) { + return number.matches("^\\+[0-9]{10,}"); + } + + private static String impreciseFormatNumber(String number, String localNumber) + throws InvalidNumberException + { + number = number.replaceAll("[^0-9+]", ""); + + if (number.charAt(0) == '+') + return number; + + if (localNumber.charAt(0) == '+') + localNumber = localNumber.substring(1); + + if (localNumber.length() == number.length() || number.length() > localNumber.length()) + return "+" + number; + + int difference = localNumber.length() - number.length(); + + return "+" + localNumber.substring(0, difference) + number; + } + + public static String formatNumberInternational(String number) { + try { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + PhoneNumber parsedNumber = util.parse(number, null); + return util.format(parsedNumber, PhoneNumberFormat.INTERNATIONAL); + } catch (NumberParseException e) { + Log.w(TAG, e); + return number; + } + } + + public static String formatNumber(String number, String localNumber) + throws InvalidNumberException + { + if (number.contains("@")) { + throw new InvalidNumberException("Possible attempt to use email address."); + } + + number = number.replaceAll("[^0-9+]", ""); + + if (number.length() == 0) { + throw new InvalidNumberException("No valid characters found."); + } + + if (number.charAt(0) == '+') + return number; + + try { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + PhoneNumber localNumberObject = util.parse(localNumber, null); + + String localCountryCode = util.getRegionCodeForNumber(localNumberObject); + Log.w(TAG, "Got local CC: " + localCountryCode); + + PhoneNumber numberObject = util.parse(number, localCountryCode); + return util.format(numberObject, PhoneNumberFormat.E164); + } catch (NumberParseException e) { + Log.w(TAG, e); + return impreciseFormatNumber(number, localNumber); + } + } + + public static String getRegionDisplayName(String regionCode) { + return (regionCode == null || regionCode.equals("ZZ") || regionCode.equals(PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY)) + ? "Unknown country" : new Locale("", regionCode).getDisplayCountry(Locale.getDefault()); + } + + public static String formatE164(String countryCode, String number) { + try { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + int parsedCountryCode = Integer.parseInt(countryCode); + PhoneNumber parsedNumber = util.parse(number, + util.getRegionCodeForCountryCode(parsedCountryCode)); + + return util.format(parsedNumber, PhoneNumberUtil.PhoneNumberFormat.E164); + } catch (NumberParseException | NumberFormatException npe) { + Log.w(TAG, npe); + } + + return "+" + + countryCode.replaceAll("[^0-9]", "").replaceAll("^0*", "") + + number.replaceAll("[^0-9]", ""); + } + + public static String getInternationalFormatFromE164(String e164number) { + try { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + PhoneNumber parsedNumber = util.parse(e164number, null); + return util.format(parsedNumber, PhoneNumberFormat.INTERNATIONAL); + } catch (NumberParseException e) { + Log.w(TAG, e); + return e164number; + } + } + + +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/crypto/ProvisioningCipher.java b/src/main/java/org/whispersystems/textsecure/internal/crypto/ProvisioningCipher.java new file mode 100644 index 0000000000..f9586c5d5f --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/crypto/ProvisioningCipher.java @@ -0,0 +1,75 @@ +package org.whispersystems.textsecure.internal.crypto; + +import com.google.protobuf.ByteString; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.kdf.HKDFv3; +import org.whispersystems.textsecure.internal.util.Util; + +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; + +import static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope; +import static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage; + + +public class ProvisioningCipher { + + private static final String TAG = ProvisioningCipher.class.getSimpleName(); + + private final ECPublicKey theirPublicKey; + + public ProvisioningCipher(ECPublicKey theirPublicKey) { + this.theirPublicKey = theirPublicKey; + } + + public byte[] encrypt(ProvisionMessage message) throws InvalidKeyException { + ECKeyPair ourKeyPair = Curve.generateKeyPair(); + byte[] sharedSecret = Curve.calculateAgreement(theirPublicKey, ourKeyPair.getPrivateKey()); + byte[] derivedSecret = new HKDFv3().deriveSecrets(sharedSecret, "TextSecure Provisioning Message".getBytes(), 64); + byte[][] parts = Util.split(derivedSecret, 32, 32); + + byte[] version = {0x01}; + byte[] ciphertext = getCiphertext(parts[0], message.toByteArray()); + byte[] mac = getMac(parts[1], Util.join(version, ciphertext)); + byte[] body = Util.join(version, ciphertext, mac); + + return ProvisionEnvelope.newBuilder() + .setPublicKey(ByteString.copyFrom(ourKeyPair.getPublicKey().serialize())) + .setBody(ByteString.copyFrom(body)) + .build() + .toByteArray(); + } + + private byte[] getCiphertext(byte[] key, byte[] message) { + try { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES")); + + return Util.join(cipher.getIV(), cipher.doFinal(message)); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | java.security.InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { + throw new AssertionError(e); + } + } + + private byte[] getMac(byte[] key, byte[] message) { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(key, "HmacSHA256")); + + return mac.doFinal(message); + } catch (NoSuchAlgorithmException | java.security.InvalidKeyException e) { + throw new AssertionError(e); + } + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/AccountAttributes.java b/src/main/java/org/whispersystems/textsecure/internal/push/AccountAttributes.java new file mode 100644 index 0000000000..5d32b48378 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/AccountAttributes.java @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2014 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.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class AccountAttributes { + + @JsonProperty + private String signalingKey; + + @JsonProperty + private boolean supportsSms; + + @JsonProperty + private int registrationId; + + public AccountAttributes(String signalingKey, boolean supportsSms, int registrationId) { + this.signalingKey = signalingKey; + this.supportsSms = supportsSms; + this.registrationId = registrationId; + } + + public AccountAttributes() {} + + public String getSignalingKey() { + return signalingKey; + } + + public boolean isSupportsSms() { + return supportsSms; + } + + public int getRegistrationId() { + return registrationId; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/ContactTokenDetailsList.java b/src/main/java/org/whispersystems/textsecure/internal/push/ContactTokenDetailsList.java new file mode 100644 index 0000000000..79fd91a4ed --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/ContactTokenDetailsList.java @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2014 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.internal.push; + +import org.whispersystems.textsecure.api.push.ContactTokenDetails; + +import java.util.List; + +public class ContactTokenDetailsList { + + private List contacts; + + public ContactTokenDetailsList() {} + + public List getContacts() { + return contacts; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/ContactTokenList.java b/src/main/java/org/whispersystems/textsecure/internal/push/ContactTokenList.java new file mode 100644 index 0000000000..6644ab75c1 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/ContactTokenList.java @@ -0,0 +1,34 @@ +/** + * Copyright (C) 2014 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.internal.push; + +import java.util.List; + +public class ContactTokenList { + + private List contacts; + + public ContactTokenList(List contacts) { + this.contacts = contacts; + } + + public ContactTokenList() {} + + public List getContacts() { + return contacts; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/DeviceCode.java b/src/main/java/org/whispersystems/textsecure/internal/push/DeviceCode.java new file mode 100644 index 0000000000..fcc14df84e --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/DeviceCode.java @@ -0,0 +1,13 @@ +package org.whispersystems.textsecure.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class DeviceCode { + + @JsonProperty + private String verificationCode; + + public String getVerificationCode() { + return verificationCode; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/MismatchedDevices.java b/src/main/java/org/whispersystems/textsecure/internal/push/MismatchedDevices.java new file mode 100644 index 0000000000..5a459a378b --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/MismatchedDevices.java @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2014 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.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class MismatchedDevices { + @JsonProperty + private List missingDevices; + + @JsonProperty + private List extraDevices; + + public List getMissingDevices() { + return missingDevices; + } + + public List getExtraDevices() { + return extraDevices; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessage.java b/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessage.java new file mode 100644 index 0000000000..66cd6c0217 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessage.java @@ -0,0 +1,58 @@ +/** + * 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.internal.push; + + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.whispersystems.textsecure.api.push.TextSecureAddress; +import org.whispersystems.textsecure.internal.util.Base64; + +public class OutgoingPushMessage { + + @JsonProperty + private int type; + @JsonProperty + private int destinationDeviceId; + @JsonProperty + private int destinationRegistrationId; + @JsonProperty + private String body; + + public OutgoingPushMessage(TextSecureAddress address, int deviceId, PushBody body) { + this.type = body.getType(); + this.destinationDeviceId = deviceId; + this.destinationRegistrationId = body.getRemoteRegistrationId(); + this.body = Base64.encodeBytes(body.getBody()); + } + + public int getDestinationDeviceId() { + return destinationDeviceId; + } + + public String getBody() { + return body; + } + + public int getType() { + return type; + } + + public int getDestinationRegistrationId() { + return destinationRegistrationId; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessageList.java b/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessageList.java new file mode 100644 index 0000000000..6fa26eab01 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/OutgoingPushMessageList.java @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2014 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.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class OutgoingPushMessageList { + + @JsonProperty + private String destination; + + @JsonProperty + private String relay; + + @JsonProperty + private long timestamp; + + @JsonProperty + private List messages; + + public OutgoingPushMessageList(String destination, long timestamp, String relay, + List messages) + { + this.timestamp = timestamp; + this.destination = destination; + this.relay = relay; + this.messages = messages; + } + + public String getDestination() { + return destination; + } + + public List getMessages() { + return messages; + } + + public String getRelay() { + return relay; + } + + public long getTimestamp() { + return timestamp; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyEntity.java b/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyEntity.java new file mode 100644 index 0000000000..7215a1eb5b --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyEntity.java @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2014 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.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.textsecure.internal.util.Base64; + +import java.io.IOException; + +public class PreKeyEntity { + + @JsonProperty + private int keyId; + + @JsonProperty + @JsonSerialize(using = ECPublicKeySerializer.class) + @JsonDeserialize(using = ECPublicKeyDeserializer.class) + private ECPublicKey publicKey; + + public PreKeyEntity() {} + + public PreKeyEntity(int keyId, ECPublicKey publicKey) { + this.keyId = keyId; + this.publicKey = publicKey; + } + + public int getKeyId() { + return keyId; + } + + public ECPublicKey getPublicKey() { + return publicKey; + } + + private static class ECPublicKeySerializer extends JsonSerializer { + @Override + public void serialize(ECPublicKey value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(Base64.encodeBytesWithoutPadding(value.serialize())); + } + } + + private static class ECPublicKeyDeserializer extends JsonDeserializer { + @Override + public ECPublicKey deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + try { + return Curve.decodePoint(Base64.decodeWithoutPadding(p.getValueAsString()), 0); + } catch (InvalidKeyException e) { + throw new IOException(e); + } + } + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponse.java b/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponse.java new file mode 100644 index 0000000000..1db05f80a1 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponse.java @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2014 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.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.textsecure.internal.util.Base64; +import org.whispersystems.textsecure.internal.util.JsonUtil; + +import java.io.IOException; +import java.util.List; + +public class PreKeyResponse { + + @JsonProperty + @JsonSerialize(using = JsonUtil.IdentityKeySerializer.class) + @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class) + private IdentityKey identityKey; + + @JsonProperty + private List devices; + + public IdentityKey getIdentityKey() { + return identityKey; + } + + public List getDevices() { + return devices; + } + + +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponseItem.java b/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponseItem.java new file mode 100644 index 0000000000..6a5fe32ae0 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyResponseItem.java @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2014 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.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.whispersystems.textsecure.api.push.SignedPreKeyEntity; + +public class PreKeyResponseItem { + + @JsonProperty + private int deviceId; + + @JsonProperty + private int registrationId; + + @JsonProperty + private SignedPreKeyEntity signedPreKey; + + @JsonProperty + private PreKeyEntity preKey; + + public int getDeviceId() { + return deviceId; + } + + public int getRegistrationId() { + return registrationId; + } + + public SignedPreKeyEntity getSignedPreKey() { + return signedPreKey; + } + + public PreKeyEntity getPreKey() { + return preKey; + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyState.java b/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyState.java new file mode 100644 index 0000000000..d3d88a9aed --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyState.java @@ -0,0 +1,39 @@ +package org.whispersystems.textsecure.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.textsecure.api.push.SignedPreKeyEntity; +import org.whispersystems.textsecure.internal.util.JsonUtil; + +import java.util.List; + +public class PreKeyState { + + @JsonProperty + @JsonSerialize(using = JsonUtil.IdentityKeySerializer.class) + @JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class) + private IdentityKey identityKey; + + @JsonProperty + private List preKeys; + + @JsonProperty + private PreKeyEntity lastResortKey; + + @JsonProperty + private SignedPreKeyEntity signedPreKey; + + + public PreKeyState(List preKeys, PreKeyEntity lastResortKey, + SignedPreKeyEntity signedPreKey, IdentityKey identityKey) + { + this.preKeys = preKeys; + this.lastResortKey = lastResortKey; + this.signedPreKey = signedPreKey; + this.identityKey = identityKey; + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyStatus.java b/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyStatus.java new file mode 100644 index 0000000000..cd53f71813 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/PreKeyStatus.java @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2014 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.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class PreKeyStatus { + + @JsonProperty + private int count; + + public PreKeyStatus() {} + + public int getCount() { + return count; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/ProvisioningMessage.java b/src/main/java/org/whispersystems/textsecure/internal/push/ProvisioningMessage.java new file mode 100644 index 0000000000..3034f522f2 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/ProvisioningMessage.java @@ -0,0 +1,11 @@ +package org.whispersystems.textsecure.internal.push; + +public class ProvisioningMessage { + + private String body; + + public ProvisioningMessage(String body) { + this.body = body; + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/ProvisioningProtos.java b/src/main/java/org/whispersystems/textsecure/internal/push/ProvisioningProtos.java new file mode 100644 index 0000000000..3271914320 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/ProvisioningProtos.java @@ -0,0 +1,1375 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Provisioning.proto + +package org.whispersystems.textsecure.internal.push; + +public final class ProvisioningProtos { + private ProvisioningProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface ProvisionEnvelopeOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes publicKey = 1; + /** + * optional bytes publicKey = 1; + */ + boolean hasPublicKey(); + /** + * optional bytes publicKey = 1; + */ + com.google.protobuf.ByteString getPublicKey(); + + // optional bytes body = 2; + /** + * optional bytes body = 2; + * + *
+     * Encrypted ProvisionMessage
+     * 
+ */ + boolean hasBody(); + /** + * optional bytes body = 2; + * + *
+     * Encrypted ProvisionMessage
+     * 
+ */ + com.google.protobuf.ByteString getBody(); + } + /** + * Protobuf type {@code textsecure.ProvisionEnvelope} + */ + public static final class ProvisionEnvelope extends + com.google.protobuf.GeneratedMessage + implements ProvisionEnvelopeOrBuilder { + // Use ProvisionEnvelope.newBuilder() to construct. + private ProvisionEnvelope(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ProvisionEnvelope(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ProvisionEnvelope defaultInstance; + public static ProvisionEnvelope getDefaultInstance() { + return defaultInstance; + } + + public ProvisionEnvelope getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ProvisionEnvelope( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + publicKey_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + body_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.ProvisioningProtos.internal_static_textsecure_ProvisionEnvelope_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.ProvisioningProtos.internal_static_textsecure_ProvisionEnvelope_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope.class, org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ProvisionEnvelope parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ProvisionEnvelope(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes publicKey = 1; + public static final int PUBLICKEY_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString publicKey_; + /** + * optional bytes publicKey = 1; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes publicKey = 1; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + + // optional bytes body = 2; + public static final int BODY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString body_; + /** + * optional bytes body = 2; + * + *
+     * Encrypted ProvisionMessage
+     * 
+ */ + public boolean hasBody() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes body = 2; + * + *
+     * Encrypted ProvisionMessage
+     * 
+ */ + public com.google.protobuf.ByteString getBody() { + return body_; + } + + private void initFields() { + publicKey_ = com.google.protobuf.ByteString.EMPTY; + body_ = 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.writeBytes(1, publicKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, body_); + } + 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, publicKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, body_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.ProvisionEnvelope} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelopeOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.ProvisioningProtos.internal_static_textsecure_ProvisionEnvelope_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.ProvisioningProtos.internal_static_textsecure_ProvisionEnvelope_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope.class, org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope.Builder.class); + } + + // Construct using org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + publicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + body_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.textsecure.internal.push.ProvisioningProtos.internal_static_textsecure_ProvisionEnvelope_descriptor; + } + + public org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope getDefaultInstanceForType() { + return org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope.getDefaultInstance(); + } + + public org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope build() { + org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope buildPartial() { + org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope result = new org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.publicKey_ = publicKey_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.body_ = body_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope) { + return mergeFrom((org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope other) { + if (other == org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope.getDefaultInstance()) return this; + if (other.hasPublicKey()) { + setPublicKey(other.getPublicKey()); + } + if (other.hasBody()) { + setBody(other.getBody()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionEnvelope) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes publicKey = 1; + private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes publicKey = 1; + */ + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes publicKey = 1; + */ + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + /** + * optional bytes publicKey = 1; + */ + public Builder setPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + publicKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes publicKey = 1; + */ + public Builder clearPublicKey() { + bitField0_ = (bitField0_ & ~0x00000001); + publicKey_ = getDefaultInstance().getPublicKey(); + onChanged(); + return this; + } + + // optional bytes body = 2; + private com.google.protobuf.ByteString body_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes body = 2; + * + *
+       * Encrypted ProvisionMessage
+       * 
+ */ + public boolean hasBody() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes body = 2; + * + *
+       * Encrypted ProvisionMessage
+       * 
+ */ + public com.google.protobuf.ByteString getBody() { + return body_; + } + /** + * optional bytes body = 2; + * + *
+       * Encrypted ProvisionMessage
+       * 
+ */ + public Builder setBody(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + body_ = value; + onChanged(); + return this; + } + /** + * optional bytes body = 2; + * + *
+       * Encrypted ProvisionMessage
+       * 
+ */ + public Builder clearBody() { + bitField0_ = (bitField0_ & ~0x00000002); + body_ = getDefaultInstance().getBody(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.ProvisionEnvelope) + } + + static { + defaultInstance = new ProvisionEnvelope(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.ProvisionEnvelope) + } + + public interface ProvisionMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes identityKeyPublic = 1; + /** + * optional bytes identityKeyPublic = 1; + */ + boolean hasIdentityKeyPublic(); + /** + * optional bytes identityKeyPublic = 1; + */ + com.google.protobuf.ByteString getIdentityKeyPublic(); + + // optional bytes identityKeyPrivate = 2; + /** + * optional bytes identityKeyPrivate = 2; + */ + boolean hasIdentityKeyPrivate(); + /** + * optional bytes identityKeyPrivate = 2; + */ + com.google.protobuf.ByteString getIdentityKeyPrivate(); + + // optional string number = 3; + /** + * optional string number = 3; + */ + boolean hasNumber(); + /** + * optional string number = 3; + */ + java.lang.String getNumber(); + /** + * optional string number = 3; + */ + com.google.protobuf.ByteString + getNumberBytes(); + + // optional string provisioningCode = 4; + /** + * optional string provisioningCode = 4; + */ + boolean hasProvisioningCode(); + /** + * optional string provisioningCode = 4; + */ + java.lang.String getProvisioningCode(); + /** + * optional string provisioningCode = 4; + */ + com.google.protobuf.ByteString + getProvisioningCodeBytes(); + } + /** + * Protobuf type {@code textsecure.ProvisionMessage} + */ + public static final class ProvisionMessage extends + com.google.protobuf.GeneratedMessage + implements ProvisionMessageOrBuilder { + // Use ProvisionMessage.newBuilder() to construct. + private ProvisionMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ProvisionMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ProvisionMessage defaultInstance; + public static ProvisionMessage getDefaultInstance() { + return defaultInstance; + } + + public ProvisionMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ProvisionMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + identityKeyPublic_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + identityKeyPrivate_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + number_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + provisioningCode_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.ProvisioningProtos.internal_static_textsecure_ProvisionMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.ProvisioningProtos.internal_static_textsecure_ProvisionMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage.class, org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ProvisionMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ProvisionMessage(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional bytes identityKeyPublic = 1; + public static final int IDENTITYKEYPUBLIC_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString identityKeyPublic_; + /** + * optional bytes identityKeyPublic = 1; + */ + public boolean hasIdentityKeyPublic() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes identityKeyPublic = 1; + */ + public com.google.protobuf.ByteString getIdentityKeyPublic() { + return identityKeyPublic_; + } + + // optional bytes identityKeyPrivate = 2; + public static final int IDENTITYKEYPRIVATE_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString identityKeyPrivate_; + /** + * optional bytes identityKeyPrivate = 2; + */ + public boolean hasIdentityKeyPrivate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes identityKeyPrivate = 2; + */ + public com.google.protobuf.ByteString getIdentityKeyPrivate() { + return identityKeyPrivate_; + } + + // optional string number = 3; + public static final int NUMBER_FIELD_NUMBER = 3; + private java.lang.Object number_; + /** + * optional string number = 3; + */ + public boolean hasNumber() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string number = 3; + */ + public java.lang.String getNumber() { + java.lang.Object ref = number_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + number_ = s; + } + return s; + } + } + /** + * optional string number = 3; + */ + public com.google.protobuf.ByteString + getNumberBytes() { + java.lang.Object ref = number_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + number_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string provisioningCode = 4; + public static final int PROVISIONINGCODE_FIELD_NUMBER = 4; + private java.lang.Object provisioningCode_; + /** + * optional string provisioningCode = 4; + */ + public boolean hasProvisioningCode() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string provisioningCode = 4; + */ + public java.lang.String getProvisioningCode() { + java.lang.Object ref = provisioningCode_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + provisioningCode_ = s; + } + return s; + } + } + /** + * optional string provisioningCode = 4; + */ + public com.google.protobuf.ByteString + getProvisioningCodeBytes() { + java.lang.Object ref = provisioningCode_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + provisioningCode_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private void initFields() { + identityKeyPublic_ = com.google.protobuf.ByteString.EMPTY; + identityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + number_ = ""; + provisioningCode_ = ""; + } + 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, identityKeyPublic_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, identityKeyPrivate_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, getNumberBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, getProvisioningCodeBytes()); + } + 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, identityKeyPublic_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, identityKeyPrivate_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getNumberBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, getProvisioningCodeBytes()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.ProvisionMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.ProvisioningProtos.internal_static_textsecure_ProvisionMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.ProvisioningProtos.internal_static_textsecure_ProvisionMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage.class, org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage.Builder.class); + } + + // Construct using org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + identityKeyPublic_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + identityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + number_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + provisioningCode_ = ""; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.textsecure.internal.push.ProvisioningProtos.internal_static_textsecure_ProvisionMessage_descriptor; + } + + public org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage getDefaultInstanceForType() { + return org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage.getDefaultInstance(); + } + + public org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage build() { + org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage buildPartial() { + org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage result = new org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.identityKeyPublic_ = identityKeyPublic_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.identityKeyPrivate_ = identityKeyPrivate_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.number_ = number_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.provisioningCode_ = provisioningCode_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage) { + return mergeFrom((org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage other) { + if (other == org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage.getDefaultInstance()) return this; + if (other.hasIdentityKeyPublic()) { + setIdentityKeyPublic(other.getIdentityKeyPublic()); + } + if (other.hasIdentityKeyPrivate()) { + setIdentityKeyPrivate(other.getIdentityKeyPrivate()); + } + if (other.hasNumber()) { + bitField0_ |= 0x00000004; + number_ = other.number_; + onChanged(); + } + if (other.hasProvisioningCode()) { + bitField0_ |= 0x00000008; + provisioningCode_ = other.provisioningCode_; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.textsecure.internal.push.ProvisioningProtos.ProvisionMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes identityKeyPublic = 1; + private com.google.protobuf.ByteString identityKeyPublic_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes identityKeyPublic = 1; + */ + public boolean hasIdentityKeyPublic() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes identityKeyPublic = 1; + */ + public com.google.protobuf.ByteString getIdentityKeyPublic() { + return identityKeyPublic_; + } + /** + * optional bytes identityKeyPublic = 1; + */ + public Builder setIdentityKeyPublic(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + identityKeyPublic_ = value; + onChanged(); + return this; + } + /** + * optional bytes identityKeyPublic = 1; + */ + public Builder clearIdentityKeyPublic() { + bitField0_ = (bitField0_ & ~0x00000001); + identityKeyPublic_ = getDefaultInstance().getIdentityKeyPublic(); + onChanged(); + return this; + } + + // optional bytes identityKeyPrivate = 2; + private com.google.protobuf.ByteString identityKeyPrivate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes identityKeyPrivate = 2; + */ + public boolean hasIdentityKeyPrivate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes identityKeyPrivate = 2; + */ + public com.google.protobuf.ByteString getIdentityKeyPrivate() { + return identityKeyPrivate_; + } + /** + * optional bytes identityKeyPrivate = 2; + */ + public Builder setIdentityKeyPrivate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + identityKeyPrivate_ = value; + onChanged(); + return this; + } + /** + * optional bytes identityKeyPrivate = 2; + */ + public Builder clearIdentityKeyPrivate() { + bitField0_ = (bitField0_ & ~0x00000002); + identityKeyPrivate_ = getDefaultInstance().getIdentityKeyPrivate(); + onChanged(); + return this; + } + + // optional string number = 3; + private java.lang.Object number_ = ""; + /** + * optional string number = 3; + */ + public boolean hasNumber() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string number = 3; + */ + public java.lang.String getNumber() { + java.lang.Object ref = number_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + number_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string number = 3; + */ + public com.google.protobuf.ByteString + getNumberBytes() { + java.lang.Object ref = number_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + number_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string number = 3; + */ + public Builder setNumber( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + number_ = value; + onChanged(); + return this; + } + /** + * optional string number = 3; + */ + public Builder clearNumber() { + bitField0_ = (bitField0_ & ~0x00000004); + number_ = getDefaultInstance().getNumber(); + onChanged(); + return this; + } + /** + * optional string number = 3; + */ + public Builder setNumberBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + number_ = value; + onChanged(); + return this; + } + + // optional string provisioningCode = 4; + private java.lang.Object provisioningCode_ = ""; + /** + * optional string provisioningCode = 4; + */ + public boolean hasProvisioningCode() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string provisioningCode = 4; + */ + public java.lang.String getProvisioningCode() { + java.lang.Object ref = provisioningCode_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + provisioningCode_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string provisioningCode = 4; + */ + public com.google.protobuf.ByteString + getProvisioningCodeBytes() { + java.lang.Object ref = provisioningCode_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + provisioningCode_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string provisioningCode = 4; + */ + public Builder setProvisioningCode( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + provisioningCode_ = value; + onChanged(); + return this; + } + /** + * optional string provisioningCode = 4; + */ + public Builder clearProvisioningCode() { + bitField0_ = (bitField0_ & ~0x00000008); + provisioningCode_ = getDefaultInstance().getProvisioningCode(); + onChanged(); + return this; + } + /** + * optional string provisioningCode = 4; + */ + public Builder setProvisioningCodeBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + provisioningCode_ = value; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.ProvisionMessage) + } + + static { + defaultInstance = new ProvisionMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.ProvisionMessage) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_ProvisionEnvelope_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_ProvisionEnvelope_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_ProvisionMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_ProvisionMessage_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\022Provisioning.proto\022\ntextsecure\"4\n\021Prov" + + "isionEnvelope\022\021\n\tpublicKey\030\001 \001(\014\022\014\n\004body" + + "\030\002 \001(\014\"s\n\020ProvisionMessage\022\031\n\021identityKe" + + "yPublic\030\001 \001(\014\022\032\n\022identityKeyPrivate\030\002 \001(" + + "\014\022\016\n\006number\030\003 \001(\t\022\030\n\020provisioningCode\030\004 " + + "\001(\tBA\n+org.whispersystems.textsecure.int" + + "ernal.pushB\022ProvisioningProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_textsecure_ProvisionEnvelope_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_textsecure_ProvisionEnvelope_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_ProvisionEnvelope_descriptor, + new java.lang.String[] { "PublicKey", "Body", }); + internal_static_textsecure_ProvisionMessage_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_textsecure_ProvisionMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_ProvisionMessage_descriptor, + new java.lang.String[] { "IdentityKeyPublic", "IdentityKeyPrivate", "Number", "ProvisioningCode", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/PushAttachmentData.java b/src/main/java/org/whispersystems/textsecure/internal/push/PushAttachmentData.java new file mode 100644 index 0000000000..11b0bcf15c --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/PushAttachmentData.java @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2014 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.internal.push; + +import java.io.InputStream; + +public class PushAttachmentData { + + private final String contentType; + private final InputStream data; + private final long dataSize; + private final byte[] key; + + public PushAttachmentData(String contentType, InputStream data, long dataSize, byte[] key) { + this.contentType = contentType; + this.data = data; + this.dataSize = dataSize; + this.key = key; + } + + public String getContentType() { + return contentType; + } + + public InputStream getData() { + return data; + } + + public long getDataSize() { + return dataSize; + } + + public byte[] getKey() { + return key; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/PushBody.java b/src/main/java/org/whispersystems/textsecure/internal/push/PushBody.java new file mode 100644 index 0000000000..3805aa455c --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/PushBody.java @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2014 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.internal.push; + +public class PushBody { + + private final int type; + private final int remoteRegistrationId; + private final byte[] body; + + public PushBody(int type, int remoteRegistrationId, byte[] body) { + this.type = type; + this.remoteRegistrationId = remoteRegistrationId; + this.body = body; + } + + public int getType() { + return type; + } + + public byte[] getBody() { + return body; + } + + public int getRemoteRegistrationId() { + return remoteRegistrationId; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/PushMessageProtos.java b/src/main/java/org/whispersystems/textsecure/internal/push/PushMessageProtos.java new file mode 100644 index 0000000000..e720e132f7 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/PushMessageProtos.java @@ -0,0 +1,4921 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: IncomingPushMessageSignal.proto + +package org.whispersystems.textsecure.internal.push; + +public final class PushMessageProtos { + private PushMessageProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface IncomingPushMessageSignalOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .textsecure.IncomingPushMessageSignal.Type type = 1; + /** + * optional .textsecure.IncomingPushMessageSignal.Type type = 1; + */ + boolean hasType(); + /** + * optional .textsecure.IncomingPushMessageSignal.Type type = 1; + */ + org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type getType(); + + // optional string source = 2; + /** + * optional string source = 2; + */ + boolean hasSource(); + /** + * optional string source = 2; + */ + java.lang.String getSource(); + /** + * optional string source = 2; + */ + com.google.protobuf.ByteString + getSourceBytes(); + + // optional uint32 sourceDevice = 7; + /** + * optional uint32 sourceDevice = 7; + */ + boolean hasSourceDevice(); + /** + * optional uint32 sourceDevice = 7; + */ + int getSourceDevice(); + + // optional string relay = 3; + /** + * optional string relay = 3; + */ + boolean hasRelay(); + /** + * optional string relay = 3; + */ + java.lang.String getRelay(); + /** + * optional string relay = 3; + */ + com.google.protobuf.ByteString + getRelayBytes(); + + // optional uint64 timestamp = 5; + /** + * optional uint64 timestamp = 5; + */ + boolean hasTimestamp(); + /** + * optional uint64 timestamp = 5; + */ + long getTimestamp(); + + // optional bytes message = 6; + /** + * optional bytes message = 6; + * + *
+     * Contains an encrypted PushMessageContent
+     * 
+ */ + boolean hasMessage(); + /** + * optional bytes message = 6; + * + *
+     * Contains an encrypted PushMessageContent
+     * 
+ */ + com.google.protobuf.ByteString getMessage(); + } + /** + * Protobuf type {@code textsecure.IncomingPushMessageSignal} + */ + public static final class IncomingPushMessageSignal extends + com.google.protobuf.GeneratedMessage + implements IncomingPushMessageSignalOrBuilder { + // Use IncomingPushMessageSignal.newBuilder() to construct. + private IncomingPushMessageSignal(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private IncomingPushMessageSignal(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final IncomingPushMessageSignal defaultInstance; + public static IncomingPushMessageSignal getDefaultInstance() { + return defaultInstance; + } + + public IncomingPushMessageSignal getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private IncomingPushMessageSignal( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + int rawValue = input.readEnum(); + org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type value = org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = value; + } + break; + } + case 18: { + bitField0_ |= 0x00000002; + source_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000008; + relay_ = input.readBytes(); + break; + } + case 40: { + bitField0_ |= 0x00000010; + timestamp_ = input.readUInt64(); + break; + } + case 50: { + bitField0_ |= 0x00000020; + message_ = input.readBytes(); + break; + } + case 56: { + bitField0_ |= 0x00000004; + sourceDevice_ = input.readUInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.class, org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public IncomingPushMessageSignal parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new IncomingPushMessageSignal(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code textsecure.IncomingPushMessageSignal.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * UNKNOWN = 0; + */ + UNKNOWN(0, 0), + /** + * CIPHERTEXT = 1; + */ + CIPHERTEXT(1, 1), + /** + * KEY_EXCHANGE = 2; + */ + KEY_EXCHANGE(2, 2), + /** + * PREKEY_BUNDLE = 3; + */ + PREKEY_BUNDLE(3, 3), + /** + * PLAINTEXT = 4; + */ + PLAINTEXT(4, 4), + /** + * RECEIPT = 5; + */ + RECEIPT(5, 5), + /** + * COPY = 6; + */ + COPY(6, 6), + ; + + /** + * UNKNOWN = 0; + */ + public static final int UNKNOWN_VALUE = 0; + /** + * CIPHERTEXT = 1; + */ + public static final int CIPHERTEXT_VALUE = 1; + /** + * KEY_EXCHANGE = 2; + */ + public static final int KEY_EXCHANGE_VALUE = 2; + /** + * PREKEY_BUNDLE = 3; + */ + public static final int PREKEY_BUNDLE_VALUE = 3; + /** + * PLAINTEXT = 4; + */ + public static final int PLAINTEXT_VALUE = 4; + /** + * RECEIPT = 5; + */ + public static final int RECEIPT_VALUE = 5; + /** + * COPY = 6; + */ + public static final int COPY_VALUE = 6; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 0: return UNKNOWN; + case 1: return CIPHERTEXT; + case 2: return KEY_EXCHANGE; + case 3: return PREKEY_BUNDLE; + case 4: return PLAINTEXT; + case 5: return RECEIPT; + case 6: return COPY; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:textsecure.IncomingPushMessageSignal.Type) + } + + private int bitField0_; + // optional .textsecure.IncomingPushMessageSignal.Type type = 1; + public static final int TYPE_FIELD_NUMBER = 1; + private org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type type_; + /** + * optional .textsecure.IncomingPushMessageSignal.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .textsecure.IncomingPushMessageSignal.Type type = 1; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type getType() { + return type_; + } + + // optional string source = 2; + public static final int SOURCE_FIELD_NUMBER = 2; + private java.lang.Object source_; + /** + * optional string source = 2; + */ + public boolean hasSource() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string source = 2; + */ + public java.lang.String getSource() { + java.lang.Object ref = source_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + source_ = s; + } + return s; + } + } + /** + * optional string source = 2; + */ + public com.google.protobuf.ByteString + getSourceBytes() { + java.lang.Object ref = source_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + source_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint32 sourceDevice = 7; + public static final int SOURCEDEVICE_FIELD_NUMBER = 7; + private int sourceDevice_; + /** + * optional uint32 sourceDevice = 7; + */ + public boolean hasSourceDevice() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 sourceDevice = 7; + */ + public int getSourceDevice() { + return sourceDevice_; + } + + // optional string relay = 3; + public static final int RELAY_FIELD_NUMBER = 3; + private java.lang.Object relay_; + /** + * optional string relay = 3; + */ + public boolean hasRelay() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string relay = 3; + */ + public java.lang.String getRelay() { + java.lang.Object ref = relay_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + relay_ = s; + } + return s; + } + } + /** + * optional string relay = 3; + */ + public com.google.protobuf.ByteString + getRelayBytes() { + java.lang.Object ref = relay_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + relay_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint64 timestamp = 5; + public static final int TIMESTAMP_FIELD_NUMBER = 5; + private long timestamp_; + /** + * optional uint64 timestamp = 5; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint64 timestamp = 5; + */ + public long getTimestamp() { + return timestamp_; + } + + // optional bytes message = 6; + public static final int MESSAGE_FIELD_NUMBER = 6; + private com.google.protobuf.ByteString message_; + /** + * optional bytes message = 6; + * + *
+     * Contains an encrypted PushMessageContent
+     * 
+ */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes message = 6; + * + *
+     * Contains an encrypted PushMessageContent
+     * 
+ */ + public com.google.protobuf.ByteString getMessage() { + return message_; + } + + private void initFields() { + type_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type.UNKNOWN; + source_ = ""; + sourceDevice_ = 0; + relay_ = ""; + timestamp_ = 0L; + message_ = 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.writeEnum(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getSourceBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(3, getRelayBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeUInt64(5, timestamp_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(6, message_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(7, sourceDevice_); + } + 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 + .computeEnumSize(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getSourceBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getRelayBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(5, timestamp_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(6, message_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(7, sourceDevice_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.IncomingPushMessageSignal} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignalOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.class, org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Builder.class); + } + + // Construct using org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + type_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type.UNKNOWN; + bitField0_ = (bitField0_ & ~0x00000001); + source_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + sourceDevice_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + relay_ = ""; + bitField0_ = (bitField0_ & ~0x00000008); + timestamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000010); + message_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_IncomingPushMessageSignal_descriptor; + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal getDefaultInstanceForType() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.getDefaultInstance(); + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal build() { + org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal buildPartial() { + org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal result = new org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.source_ = source_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.sourceDevice_ = sourceDevice_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.relay_ = relay_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.timestamp_ = timestamp_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.message_ = message_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal) { + return mergeFrom((org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal other) { + if (other == org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasSource()) { + bitField0_ |= 0x00000002; + source_ = other.source_; + onChanged(); + } + if (other.hasSourceDevice()) { + setSourceDevice(other.getSourceDevice()); + } + if (other.hasRelay()) { + bitField0_ |= 0x00000008; + relay_ = other.relay_; + onChanged(); + } + if (other.hasTimestamp()) { + setTimestamp(other.getTimestamp()); + } + if (other.hasMessage()) { + setMessage(other.getMessage()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .textsecure.IncomingPushMessageSignal.Type type = 1; + private org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type type_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type.UNKNOWN; + /** + * optional .textsecure.IncomingPushMessageSignal.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .textsecure.IncomingPushMessageSignal.Type type = 1; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type getType() { + return type_; + } + /** + * optional .textsecure.IncomingPushMessageSignal.Type type = 1; + */ + public Builder setType(org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value; + onChanged(); + return this; + } + /** + * optional .textsecure.IncomingPushMessageSignal.Type type = 1; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.IncomingPushMessageSignal.Type.UNKNOWN; + onChanged(); + return this; + } + + // optional string source = 2; + private java.lang.Object source_ = ""; + /** + * optional string source = 2; + */ + public boolean hasSource() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string source = 2; + */ + public java.lang.String getSource() { + java.lang.Object ref = source_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + source_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string source = 2; + */ + public com.google.protobuf.ByteString + getSourceBytes() { + java.lang.Object ref = source_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + source_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string source = 2; + */ + public Builder setSource( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + source_ = value; + onChanged(); + return this; + } + /** + * optional string source = 2; + */ + public Builder clearSource() { + bitField0_ = (bitField0_ & ~0x00000002); + source_ = getDefaultInstance().getSource(); + onChanged(); + return this; + } + /** + * optional string source = 2; + */ + public Builder setSourceBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + source_ = value; + onChanged(); + return this; + } + + // optional uint32 sourceDevice = 7; + private int sourceDevice_ ; + /** + * optional uint32 sourceDevice = 7; + */ + public boolean hasSourceDevice() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 sourceDevice = 7; + */ + public int getSourceDevice() { + return sourceDevice_; + } + /** + * optional uint32 sourceDevice = 7; + */ + public Builder setSourceDevice(int value) { + bitField0_ |= 0x00000004; + sourceDevice_ = value; + onChanged(); + return this; + } + /** + * optional uint32 sourceDevice = 7; + */ + public Builder clearSourceDevice() { + bitField0_ = (bitField0_ & ~0x00000004); + sourceDevice_ = 0; + onChanged(); + return this; + } + + // optional string relay = 3; + private java.lang.Object relay_ = ""; + /** + * optional string relay = 3; + */ + public boolean hasRelay() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string relay = 3; + */ + public java.lang.String getRelay() { + java.lang.Object ref = relay_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + relay_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string relay = 3; + */ + public com.google.protobuf.ByteString + getRelayBytes() { + java.lang.Object ref = relay_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + relay_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string relay = 3; + */ + public Builder setRelay( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + relay_ = value; + onChanged(); + return this; + } + /** + * optional string relay = 3; + */ + public Builder clearRelay() { + bitField0_ = (bitField0_ & ~0x00000008); + relay_ = getDefaultInstance().getRelay(); + onChanged(); + return this; + } + /** + * optional string relay = 3; + */ + public Builder setRelayBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + relay_ = value; + onChanged(); + return this; + } + + // optional uint64 timestamp = 5; + private long timestamp_ ; + /** + * optional uint64 timestamp = 5; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint64 timestamp = 5; + */ + public long getTimestamp() { + return timestamp_; + } + /** + * optional uint64 timestamp = 5; + */ + public Builder setTimestamp(long value) { + bitField0_ |= 0x00000010; + timestamp_ = value; + onChanged(); + return this; + } + /** + * optional uint64 timestamp = 5; + */ + public Builder clearTimestamp() { + bitField0_ = (bitField0_ & ~0x00000010); + timestamp_ = 0L; + onChanged(); + return this; + } + + // optional bytes message = 6; + private com.google.protobuf.ByteString message_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes message = 6; + * + *
+       * Contains an encrypted PushMessageContent
+       * 
+ */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes message = 6; + * + *
+       * Contains an encrypted PushMessageContent
+       * 
+ */ + public com.google.protobuf.ByteString getMessage() { + return message_; + } + /** + * optional bytes message = 6; + * + *
+       * Contains an encrypted PushMessageContent
+       * 
+ */ + public Builder setMessage(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + message_ = value; + onChanged(); + return this; + } + /** + * optional bytes message = 6; + * + *
+       * Contains an encrypted PushMessageContent
+       * 
+ */ + public Builder clearMessage() { + bitField0_ = (bitField0_ & ~0x00000020); + message_ = getDefaultInstance().getMessage(); + onChanged(); + return this; + } + + // @@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; + /** + * optional string body = 1; + */ + boolean hasBody(); + /** + * optional string body = 1; + */ + java.lang.String getBody(); + /** + * optional string body = 1; + */ + com.google.protobuf.ByteString + getBodyBytes(); + + // repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + java.util.List + getAttachmentsList(); + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer getAttachments(int index); + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + int getAttachmentsCount(); + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + java.util.List + getAttachmentsOrBuilderList(); + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder getAttachmentsOrBuilder( + int index); + + // optional .textsecure.PushMessageContent.GroupContext group = 3; + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + boolean hasGroup(); + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext getGroup(); + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContextOrBuilder getGroupOrBuilder(); + + // optional uint32 flags = 4; + /** + * optional uint32 flags = 4; + */ + boolean hasFlags(); + /** + * optional uint32 flags = 4; + */ + int getFlags(); + + // optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + boolean hasSync(); + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext getSync(); + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder getSyncOrBuilder(); + } + /** + * Protobuf type {@code textsecure.PushMessageContent} + */ + public static final class PushMessageContent extends + com.google.protobuf.GeneratedMessage + implements PushMessageContentOrBuilder { + // Use PushMessageContent.newBuilder() to construct. + private PushMessageContent(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private PushMessageContent(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final PushMessageContent defaultInstance; + public static PushMessageContent getDefaultInstance() { + return defaultInstance; + } + + public PushMessageContent getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PushMessageContent( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + body_ = input.readBytes(); + break; + } + case 18: { + if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + attachments_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000002; + } + attachments_.add(input.readMessage(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.PARSER, extensionRegistry)); + break; + } + case 26: { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = group_.toBuilder(); + } + group_ = input.readMessage(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(group_); + group_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 32: { + bitField0_ |= 0x00000004; + flags_ = input.readUInt32(); + break; + } + case 42: { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder subBuilder = null; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + subBuilder = sync_.toBuilder(); + } + sync_ = input.readMessage(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(sync_); + sync_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000008; + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + attachments_ = java.util.Collections.unmodifiableList(attachments_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.class, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public PushMessageContent parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PushMessageContent(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code textsecure.PushMessageContent.Flags} + */ + public enum Flags + implements com.google.protobuf.ProtocolMessageEnum { + /** + * END_SESSION = 1; + */ + END_SESSION(0, 1), + ; + + /** + * END_SESSION = 1; + */ + public static final int END_SESSION_VALUE = 1; + + + public final int getNumber() { return value; } + + public static Flags valueOf(int value) { + switch (value) { + case 1: return END_SESSION; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Flags findValueByNumber(int number) { + return Flags.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.getDescriptor().getEnumTypes().get(0); + } + + private static final Flags[] VALUES = values(); + + public static Flags valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Flags(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:textsecure.PushMessageContent.Flags) + } + + public interface AttachmentPointerOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional fixed64 id = 1; + /** + * optional fixed64 id = 1; + */ + boolean hasId(); + /** + * optional fixed64 id = 1; + */ + long getId(); + + // optional string contentType = 2; + /** + * optional string contentType = 2; + */ + boolean hasContentType(); + /** + * optional string contentType = 2; + */ + java.lang.String getContentType(); + /** + * optional string contentType = 2; + */ + com.google.protobuf.ByteString + getContentTypeBytes(); + + // optional bytes key = 3; + /** + * optional bytes key = 3; + */ + boolean hasKey(); + /** + * optional bytes key = 3; + */ + com.google.protobuf.ByteString getKey(); + } + /** + * Protobuf type {@code textsecure.PushMessageContent.AttachmentPointer} + */ + public static final class AttachmentPointer extends + com.google.protobuf.GeneratedMessage + implements AttachmentPointerOrBuilder { + // Use AttachmentPointer.newBuilder() to construct. + private AttachmentPointer(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private AttachmentPointer(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final AttachmentPointer defaultInstance; + public static AttachmentPointer getDefaultInstance() { + return defaultInstance; + } + + public AttachmentPointer getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private AttachmentPointer( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 9: { + bitField0_ |= 0x00000001; + id_ = input.readFixed64(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + contentType_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + key_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_AttachmentPointer_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_AttachmentPointer_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.class, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public AttachmentPointer parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new AttachmentPointer(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional fixed64 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private long id_; + /** + * optional fixed64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional fixed64 id = 1; + */ + public long getId() { + return id_; + } + + // optional string contentType = 2; + public static final int CONTENTTYPE_FIELD_NUMBER = 2; + private java.lang.Object contentType_; + /** + * optional string contentType = 2; + */ + public boolean hasContentType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string contentType = 2; + */ + public java.lang.String getContentType() { + java.lang.Object ref = contentType_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + contentType_ = s; + } + return s; + } + } + /** + * optional string contentType = 2; + */ + public com.google.protobuf.ByteString + getContentTypeBytes() { + java.lang.Object ref = contentType_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.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_; + /** + * optional bytes key = 3; + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes key = 3; + */ + 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.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.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; + } + /** + * Protobuf type {@code textsecure.PushMessageContent.AttachmentPointer} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_AttachmentPointer_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_AttachmentPointer_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.class, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder.class); + } + + // Construct using org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + 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.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_AttachmentPointer_descriptor; + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer getDefaultInstanceForType() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance(); + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer build() { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer buildPartial() { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer result = new org.whispersystems.textsecure.internal.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.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer) { + return mergeFrom((org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer other) { + if (other == org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasContentType()) { + bitField0_ |= 0x00000002; + contentType_ = other.contentType_; + onChanged(); + } + 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 { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional fixed64 id = 1; + private long id_ ; + /** + * optional fixed64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional fixed64 id = 1; + */ + public long getId() { + return id_; + } + /** + * optional fixed64 id = 1; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional fixed64 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0L; + onChanged(); + return this; + } + + // optional string contentType = 2; + private java.lang.Object contentType_ = ""; + /** + * optional string contentType = 2; + */ + public boolean hasContentType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string contentType = 2; + */ + public java.lang.String getContentType() { + java.lang.Object ref = contentType_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + contentType_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string contentType = 2; + */ + public com.google.protobuf.ByteString + getContentTypeBytes() { + java.lang.Object ref = contentType_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + contentType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string contentType = 2; + */ + public Builder setContentType( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + contentType_ = value; + onChanged(); + return this; + } + /** + * optional string contentType = 2; + */ + public Builder clearContentType() { + bitField0_ = (bitField0_ & ~0x00000002); + contentType_ = getDefaultInstance().getContentType(); + onChanged(); + return this; + } + /** + * optional string contentType = 2; + */ + public Builder setContentTypeBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + contentType_ = value; + onChanged(); + return this; + } + + // optional bytes key = 3; + private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes key = 3; + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes key = 3; + */ + public com.google.protobuf.ByteString getKey() { + return key_; + } + /** + * optional bytes key = 3; + */ + public Builder setKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + key_ = value; + onChanged(); + return this; + } + /** + * optional bytes key = 3; + */ + 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) + } + + public interface GroupContextOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes id = 1; + /** + * optional bytes id = 1; + */ + boolean hasId(); + /** + * optional bytes id = 1; + */ + com.google.protobuf.ByteString getId(); + + // optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + /** + * optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + */ + boolean hasType(); + /** + * optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + */ + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type getType(); + + // optional string name = 3; + /** + * optional string name = 3; + */ + boolean hasName(); + /** + * optional string name = 3; + */ + java.lang.String getName(); + /** + * optional string name = 3; + */ + com.google.protobuf.ByteString + getNameBytes(); + + // repeated string members = 4; + /** + * repeated string members = 4; + */ + java.util.List + getMembersList(); + /** + * repeated string members = 4; + */ + int getMembersCount(); + /** + * repeated string members = 4; + */ + java.lang.String getMembers(int index); + /** + * repeated string members = 4; + */ + com.google.protobuf.ByteString + getMembersBytes(int index); + + // optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + boolean hasAvatar(); + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer getAvatar(); + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder getAvatarOrBuilder(); + } + /** + * Protobuf type {@code textsecure.PushMessageContent.GroupContext} + */ + public static final class GroupContext extends + com.google.protobuf.GeneratedMessage + implements GroupContextOrBuilder { + // Use GroupContext.newBuilder() to construct. + private GroupContext(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private GroupContext(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final GroupContext defaultInstance; + public static GroupContext getDefaultInstance() { + return defaultInstance; + } + + public GroupContext getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private GroupContext( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + id_ = input.readBytes(); + break; + } + case 16: { + int rawValue = input.readEnum(); + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type value = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(2, rawValue); + } else { + bitField0_ |= 0x00000002; + type_ = value; + } + break; + } + case 26: { + bitField0_ |= 0x00000004; + name_ = input.readBytes(); + break; + } + case 34: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + members_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000008; + } + members_.add(input.readBytes()); + break; + } + case 42: { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder subBuilder = null; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + subBuilder = avatar_.toBuilder(); + } + avatar_ = input.readMessage(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(avatar_); + avatar_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000008; + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + members_ = new com.google.protobuf.UnmodifiableLazyStringList(members_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_GroupContext_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_GroupContext_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.class, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public GroupContext parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new GroupContext(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code textsecure.PushMessageContent.GroupContext.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * UNKNOWN = 0; + */ + UNKNOWN(0, 0), + /** + * UPDATE = 1; + */ + UPDATE(1, 1), + /** + * DELIVER = 2; + */ + DELIVER(2, 2), + /** + * QUIT = 3; + */ + QUIT(3, 3), + ; + + /** + * UNKNOWN = 0; + */ + public static final int UNKNOWN_VALUE = 0; + /** + * UPDATE = 1; + */ + public static final int UPDATE_VALUE = 1; + /** + * DELIVER = 2; + */ + public static final int DELIVER_VALUE = 2; + /** + * QUIT = 3; + */ + public static final int QUIT_VALUE = 3; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 0: return UNKNOWN; + case 1: return UPDATE; + case 2: return DELIVER; + case 3: return QUIT; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:textsecure.PushMessageContent.GroupContext.Type) + } + + private int bitField0_; + // optional bytes id = 1; + public static final int ID_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString id_; + /** + * optional bytes id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes id = 1; + */ + public com.google.protobuf.ByteString getId() { + return id_; + } + + // optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + public static final int TYPE_FIELD_NUMBER = 2; + private org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type type_; + /** + * optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type getType() { + return type_; + } + + // optional string name = 3; + public static final int NAME_FIELD_NUMBER = 3; + private java.lang.Object name_; + /** + * optional string name = 3; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string name = 3; + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + name_ = s; + } + return s; + } + } + /** + * optional string name = 3; + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // repeated string members = 4; + public static final int MEMBERS_FIELD_NUMBER = 4; + private com.google.protobuf.LazyStringList members_; + /** + * repeated string members = 4; + */ + public java.util.List + getMembersList() { + return members_; + } + /** + * repeated string members = 4; + */ + public int getMembersCount() { + return members_.size(); + } + /** + * repeated string members = 4; + */ + public java.lang.String getMembers(int index) { + return members_.get(index); + } + /** + * repeated string members = 4; + */ + public com.google.protobuf.ByteString + getMembersBytes(int index) { + return members_.getByteString(index); + } + + // optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + public static final int AVATAR_FIELD_NUMBER = 5; + private org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer avatar_; + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + public boolean hasAvatar() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer getAvatar() { + return avatar_; + } + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder getAvatarOrBuilder() { + return avatar_; + } + + private void initFields() { + id_ = com.google.protobuf.ByteString.EMPTY; + type_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type.UNKNOWN; + name_ = ""; + members_ = com.google.protobuf.LazyStringArrayList.EMPTY; + avatar_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance(); + } + 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, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeEnum(2, type_.getNumber()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, getNameBytes()); + } + for (int i = 0; i < members_.size(); i++) { + output.writeBytes(4, members_.getByteString(i)); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeMessage(5, avatar_); + } + 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, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(2, type_.getNumber()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getNameBytes()); + } + { + int dataSize = 0; + for (int i = 0; i < members_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(members_.getByteString(i)); + } + size += dataSize; + size += 1 * getMembersList().size(); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, avatar_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.PushMessageContent.GroupContext} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContextOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_GroupContext_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_GroupContext_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.class, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Builder.class); + } + + // Construct using org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getAvatarFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type.UNKNOWN; + bitField0_ = (bitField0_ & ~0x00000002); + name_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + members_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + if (avatarBuilder_ == null) { + avatar_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance(); + } else { + avatarBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_GroupContext_descriptor; + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext getDefaultInstanceForType() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.getDefaultInstance(); + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext build() { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext buildPartial() { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext result = new org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext(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.type_ = type_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.name_ = name_; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + members_ = new com.google.protobuf.UnmodifiableLazyStringList( + members_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.members_ = members_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000008; + } + if (avatarBuilder_ == null) { + result.avatar_ = avatar_; + } else { + result.avatar_ = avatarBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext) { + return mergeFrom((org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext other) { + if (other == org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasName()) { + bitField0_ |= 0x00000004; + name_ = other.name_; + onChanged(); + } + if (!other.members_.isEmpty()) { + if (members_.isEmpty()) { + members_ = other.members_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureMembersIsMutable(); + members_.addAll(other.members_); + } + onChanged(); + } + if (other.hasAvatar()) { + mergeAvatar(other.getAvatar()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes id = 1; + private com.google.protobuf.ByteString id_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes id = 1; + */ + public com.google.protobuf.ByteString getId() { + return id_; + } + /** + * optional bytes id = 1; + */ + public Builder setId(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional bytes id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = getDefaultInstance().getId(); + onChanged(); + return this; + } + + // optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + private org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type type_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type.UNKNOWN; + /** + * optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type getType() { + return type_; + } + /** + * optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + */ + public Builder setType(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + type_ = value; + onChanged(); + return this; + } + /** + * optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000002); + type_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type.UNKNOWN; + onChanged(); + return this; + } + + // optional string name = 3; + private java.lang.Object name_ = ""; + /** + * optional string name = 3; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string name = 3; + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string name = 3; + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string name = 3; + */ + public Builder setName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + name_ = value; + onChanged(); + return this; + } + /** + * optional string name = 3; + */ + public Builder clearName() { + bitField0_ = (bitField0_ & ~0x00000004); + name_ = getDefaultInstance().getName(); + onChanged(); + return this; + } + /** + * optional string name = 3; + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + name_ = value; + onChanged(); + return this; + } + + // repeated string members = 4; + private com.google.protobuf.LazyStringList members_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureMembersIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + members_ = new com.google.protobuf.LazyStringArrayList(members_); + bitField0_ |= 0x00000008; + } + } + /** + * repeated string members = 4; + */ + public java.util.List + getMembersList() { + return java.util.Collections.unmodifiableList(members_); + } + /** + * repeated string members = 4; + */ + public int getMembersCount() { + return members_.size(); + } + /** + * repeated string members = 4; + */ + public java.lang.String getMembers(int index) { + return members_.get(index); + } + /** + * repeated string members = 4; + */ + public com.google.protobuf.ByteString + getMembersBytes(int index) { + return members_.getByteString(index); + } + /** + * repeated string members = 4; + */ + public Builder setMembers( + int index, java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.set(index, value); + onChanged(); + return this; + } + /** + * repeated string members = 4; + */ + public Builder addMembers( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.add(value); + onChanged(); + return this; + } + /** + * repeated string members = 4; + */ + public Builder addAllMembers( + java.lang.Iterable values) { + ensureMembersIsMutable(); + super.addAll(values, members_); + onChanged(); + return this; + } + /** + * repeated string members = 4; + */ + public Builder clearMembers() { + members_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + return this; + } + /** + * repeated string members = 4; + */ + public Builder addMembersBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureMembersIsMutable(); + members_.add(value); + onChanged(); + return this; + } + + // optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + private org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer avatar_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder> avatarBuilder_; + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + public boolean hasAvatar() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer getAvatar() { + if (avatarBuilder_ == null) { + return avatar_; + } else { + return avatarBuilder_.getMessage(); + } + } + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + public Builder setAvatar(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer value) { + if (avatarBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + avatar_ = value; + onChanged(); + } else { + avatarBuilder_.setMessage(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + public Builder setAvatar( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder builderForValue) { + if (avatarBuilder_ == null) { + avatar_ = builderForValue.build(); + onChanged(); + } else { + avatarBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + public Builder mergeAvatar(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer value) { + if (avatarBuilder_ == null) { + if (((bitField0_ & 0x00000010) == 0x00000010) && + avatar_ != org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance()) { + avatar_ = + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.newBuilder(avatar_).mergeFrom(value).buildPartial(); + } else { + avatar_ = value; + } + onChanged(); + } else { + avatarBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + public Builder clearAvatar() { + if (avatarBuilder_ == null) { + avatar_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance(); + onChanged(); + } else { + avatarBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder getAvatarBuilder() { + bitField0_ |= 0x00000010; + onChanged(); + return getAvatarFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder getAvatarOrBuilder() { + if (avatarBuilder_ != null) { + return avatarBuilder_.getMessageOrBuilder(); + } else { + return avatar_; + } + } + /** + * optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + */ + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder> + getAvatarFieldBuilder() { + if (avatarBuilder_ == null) { + avatarBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder>( + avatar_, + getParentForChildren(), + isClean()); + avatar_ = null; + } + return avatarBuilder_; + } + + // @@protoc_insertion_point(builder_scope:textsecure.PushMessageContent.GroupContext) + } + + static { + defaultInstance = new GroupContext(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.PushMessageContent.GroupContext) + } + + public interface SyncMessageContextOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string destination = 1; + /** + * optional string destination = 1; + */ + boolean hasDestination(); + /** + * optional string destination = 1; + */ + java.lang.String getDestination(); + /** + * optional string destination = 1; + */ + com.google.protobuf.ByteString + getDestinationBytes(); + + // optional uint64 timestamp = 2; + /** + * optional uint64 timestamp = 2; + */ + boolean hasTimestamp(); + /** + * optional uint64 timestamp = 2; + */ + long getTimestamp(); + } + /** + * Protobuf type {@code textsecure.PushMessageContent.SyncMessageContext} + */ + public static final class SyncMessageContext extends + com.google.protobuf.GeneratedMessage + implements SyncMessageContextOrBuilder { + // Use SyncMessageContext.newBuilder() to construct. + private SyncMessageContext(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SyncMessageContext(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SyncMessageContext defaultInstance; + public static SyncMessageContext getDefaultInstance() { + return defaultInstance; + } + + public SyncMessageContext getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SyncMessageContext( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + destination_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + timestamp_ = input.readUInt64(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_SyncMessageContext_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_SyncMessageContext_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.class, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SyncMessageContext parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SyncMessageContext(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string destination = 1; + public static final int DESTINATION_FIELD_NUMBER = 1; + private java.lang.Object destination_; + /** + * optional string destination = 1; + */ + public boolean hasDestination() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string destination = 1; + */ + public java.lang.String getDestination() { + java.lang.Object ref = destination_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + destination_ = s; + } + return s; + } + } + /** + * optional string destination = 1; + */ + public com.google.protobuf.ByteString + getDestinationBytes() { + java.lang.Object ref = destination_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + destination_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint64 timestamp = 2; + public static final int TIMESTAMP_FIELD_NUMBER = 2; + private long timestamp_; + /** + * optional uint64 timestamp = 2; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint64 timestamp = 2; + */ + public long getTimestamp() { + return timestamp_; + } + + private void initFields() { + destination_ = ""; + timestamp_ = 0L; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getDestinationBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt64(2, timestamp_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getDestinationBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(2, timestamp_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.PushMessageContent.SyncMessageContext} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_SyncMessageContext_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_SyncMessageContext_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.class, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder.class); + } + + // Construct using org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + destination_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + timestamp_ = 0L; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_SyncMessageContext_descriptor; + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext getDefaultInstanceForType() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance(); + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext build() { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext buildPartial() { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext result = new org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.destination_ = destination_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.timestamp_ = timestamp_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext) { + return mergeFrom((org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext other) { + if (other == org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance()) return this; + if (other.hasDestination()) { + bitField0_ |= 0x00000001; + destination_ = other.destination_; + onChanged(); + } + if (other.hasTimestamp()) { + setTimestamp(other.getTimestamp()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string destination = 1; + private java.lang.Object destination_ = ""; + /** + * optional string destination = 1; + */ + public boolean hasDestination() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string destination = 1; + */ + public java.lang.String getDestination() { + java.lang.Object ref = destination_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + destination_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string destination = 1; + */ + public com.google.protobuf.ByteString + getDestinationBytes() { + java.lang.Object ref = destination_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + destination_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string destination = 1; + */ + public Builder setDestination( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + destination_ = value; + onChanged(); + return this; + } + /** + * optional string destination = 1; + */ + public Builder clearDestination() { + bitField0_ = (bitField0_ & ~0x00000001); + destination_ = getDefaultInstance().getDestination(); + onChanged(); + return this; + } + /** + * optional string destination = 1; + */ + public Builder setDestinationBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + destination_ = value; + onChanged(); + return this; + } + + // optional uint64 timestamp = 2; + private long timestamp_ ; + /** + * optional uint64 timestamp = 2; + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint64 timestamp = 2; + */ + public long getTimestamp() { + return timestamp_; + } + /** + * optional uint64 timestamp = 2; + */ + public Builder setTimestamp(long value) { + bitField0_ |= 0x00000002; + timestamp_ = value; + onChanged(); + return this; + } + /** + * optional uint64 timestamp = 2; + */ + public Builder clearTimestamp() { + bitField0_ = (bitField0_ & ~0x00000002); + timestamp_ = 0L; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.PushMessageContent.SyncMessageContext) + } + + static { + defaultInstance = new SyncMessageContext(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.PushMessageContent.SyncMessageContext) + } + + private int bitField0_; + // optional string body = 1; + public static final int BODY_FIELD_NUMBER = 1; + private java.lang.Object body_; + /** + * optional string body = 1; + */ + public boolean hasBody() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string body = 1; + */ + public java.lang.String getBody() { + java.lang.Object ref = body_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + body_ = s; + } + return s; + } + } + /** + * optional string body = 1; + */ + public com.google.protobuf.ByteString + getBodyBytes() { + java.lang.Object ref = body_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.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_; + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public java.util.List getAttachmentsList() { + return attachments_; + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public java.util.List + getAttachmentsOrBuilderList() { + return attachments_; + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public int getAttachmentsCount() { + return attachments_.size(); + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer getAttachments(int index) { + return attachments_.get(index); + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder getAttachmentsOrBuilder( + int index) { + return attachments_.get(index); + } + + // optional .textsecure.PushMessageContent.GroupContext group = 3; + public static final int GROUP_FIELD_NUMBER = 3; + private org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext group_; + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + public boolean hasGroup() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext getGroup() { + return group_; + } + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContextOrBuilder getGroupOrBuilder() { + return group_; + } + + // optional uint32 flags = 4; + public static final int FLAGS_FIELD_NUMBER = 4; + private int flags_; + /** + * optional uint32 flags = 4; + */ + public boolean hasFlags() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional uint32 flags = 4; + */ + public int getFlags() { + return flags_; + } + + // optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + public static final int SYNC_FIELD_NUMBER = 5; + private org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext sync_; + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + public boolean hasSync() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext getSync() { + return sync_; + } + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder getSyncOrBuilder() { + return sync_; + } + + private void initFields() { + body_ = ""; + attachments_ = java.util.Collections.emptyList(); + group_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.getDefaultInstance(); + flags_ = 0; + sync_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + 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)); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(3, group_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(4, flags_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeMessage(5, sync_); + } + 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)); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, group_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(4, flags_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, sync_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.PushMessageContent} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContentOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.class, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.Builder.class); + } + + // Construct using org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getAttachmentsFieldBuilder(); + getGroupFieldBuilder(); + getSyncFieldBuilder(); + } + } + 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(); + } + if (groupBuilder_ == null) { + group_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.getDefaultInstance(); + } else { + groupBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + flags_ = 0; + bitField0_ = (bitField0_ & ~0x00000008); + if (syncBuilder_ == null) { + sync_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance(); + } else { + syncBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.internal_static_textsecure_PushMessageContent_descriptor; + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent getDefaultInstanceForType() { + return org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.getDefaultInstance(); + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent build() { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent buildPartial() { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent result = new org.whispersystems.textsecure.internal.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(); + } + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000002; + } + if (groupBuilder_ == null) { + result.group_ = group_; + } else { + result.group_ = groupBuilder_.build(); + } + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000004; + } + result.flags_ = flags_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000008; + } + if (syncBuilder_ == null) { + result.sync_ = sync_; + } else { + result.sync_ = syncBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent) { + return mergeFrom((org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent other) { + if (other == org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.getDefaultInstance()) return this; + if (other.hasBody()) { + bitField0_ |= 0x00000001; + body_ = other.body_; + onChanged(); + } + 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_); + } + } + } + if (other.hasGroup()) { + mergeGroup(other.getGroup()); + } + if (other.hasFlags()) { + setFlags(other.getFlags()); + } + if (other.hasSync()) { + mergeSync(other.getSync()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string body = 1; + private java.lang.Object body_ = ""; + /** + * optional string body = 1; + */ + public boolean hasBody() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string body = 1; + */ + public java.lang.String getBody() { + java.lang.Object ref = body_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + body_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string body = 1; + */ + public com.google.protobuf.ByteString + getBodyBytes() { + java.lang.Object ref = body_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + body_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string body = 1; + */ + public Builder setBody( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + body_ = value; + onChanged(); + return this; + } + /** + * optional string body = 1; + */ + public Builder clearBody() { + bitField0_ = (bitField0_ & ~0x00000001); + body_ = getDefaultInstance().getBody(); + onChanged(); + return this; + } + /** + * optional string body = 1; + */ + public Builder setBodyBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + body_ = value; + onChanged(); + return this; + } + + // repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + private java.util.List attachments_ = + java.util.Collections.emptyList(); + private void ensureAttachmentsIsMutable() { + if (!((bitField0_ & 0x00000002) == 0x00000002)) { + attachments_ = new java.util.ArrayList(attachments_); + bitField0_ |= 0x00000002; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder> attachmentsBuilder_; + + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public java.util.List getAttachmentsList() { + if (attachmentsBuilder_ == null) { + return java.util.Collections.unmodifiableList(attachments_); + } else { + return attachmentsBuilder_.getMessageList(); + } + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public int getAttachmentsCount() { + if (attachmentsBuilder_ == null) { + return attachments_.size(); + } else { + return attachmentsBuilder_.getCount(); + } + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer getAttachments(int index) { + if (attachmentsBuilder_ == null) { + return attachments_.get(index); + } else { + return attachmentsBuilder_.getMessage(index); + } + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public Builder setAttachments( + int index, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer value) { + if (attachmentsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAttachmentsIsMutable(); + attachments_.set(index, value); + onChanged(); + } else { + attachmentsBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public Builder setAttachments( + int index, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder builderForValue) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.set(index, builderForValue.build()); + onChanged(); + } else { + attachmentsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public Builder addAttachments(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer value) { + if (attachmentsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAttachmentsIsMutable(); + attachments_.add(value); + onChanged(); + } else { + attachmentsBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public Builder addAttachments( + int index, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer value) { + if (attachmentsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAttachmentsIsMutable(); + attachments_.add(index, value); + onChanged(); + } else { + attachmentsBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public Builder addAttachments( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder builderForValue) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.add(builderForValue.build()); + onChanged(); + } else { + attachmentsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public Builder addAttachments( + int index, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder builderForValue) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.add(index, builderForValue.build()); + onChanged(); + } else { + attachmentsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public Builder addAllAttachments( + java.lang.Iterable values) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + super.addAll(values, attachments_); + onChanged(); + } else { + attachmentsBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public Builder clearAttachments() { + if (attachmentsBuilder_ == null) { + attachments_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + } else { + attachmentsBuilder_.clear(); + } + return this; + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public Builder removeAttachments(int index) { + if (attachmentsBuilder_ == null) { + ensureAttachmentsIsMutable(); + attachments_.remove(index); + onChanged(); + } else { + attachmentsBuilder_.remove(index); + } + return this; + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder getAttachmentsBuilder( + int index) { + return getAttachmentsFieldBuilder().getBuilder(index); + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder getAttachmentsOrBuilder( + int index) { + if (attachmentsBuilder_ == null) { + return attachments_.get(index); } else { + return attachmentsBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public java.util.List + getAttachmentsOrBuilderList() { + if (attachmentsBuilder_ != null) { + return attachmentsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(attachments_); + } + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder addAttachmentsBuilder() { + return getAttachmentsFieldBuilder().addBuilder( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance()); + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder addAttachmentsBuilder( + int index) { + return getAttachmentsFieldBuilder().addBuilder( + index, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.getDefaultInstance()); + } + /** + * repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + */ + public java.util.List + getAttachmentsBuilderList() { + return getAttachmentsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder> + getAttachmentsFieldBuilder() { + if (attachmentsBuilder_ == null) { + attachmentsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointer.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.AttachmentPointerOrBuilder>( + attachments_, + ((bitField0_ & 0x00000002) == 0x00000002), + getParentForChildren(), + isClean()); + attachments_ = null; + } + return attachmentsBuilder_; + } + + // optional .textsecure.PushMessageContent.GroupContext group = 3; + private org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext group_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContextOrBuilder> groupBuilder_; + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + public boolean hasGroup() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext getGroup() { + if (groupBuilder_ == null) { + return group_; + } else { + return groupBuilder_.getMessage(); + } + } + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + public Builder setGroup(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext value) { + if (groupBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + group_ = value; + onChanged(); + } else { + groupBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + public Builder setGroup( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Builder builderForValue) { + if (groupBuilder_ == null) { + group_ = builderForValue.build(); + onChanged(); + } else { + groupBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + public Builder mergeGroup(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext value) { + if (groupBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + group_ != org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.getDefaultInstance()) { + group_ = + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.newBuilder(group_).mergeFrom(value).buildPartial(); + } else { + group_ = value; + } + onChanged(); + } else { + groupBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + public Builder clearGroup() { + if (groupBuilder_ == null) { + group_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.getDefaultInstance(); + onChanged(); + } else { + groupBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Builder getGroupBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getGroupFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContextOrBuilder getGroupOrBuilder() { + if (groupBuilder_ != null) { + return groupBuilder_.getMessageOrBuilder(); + } else { + return group_; + } + } + /** + * optional .textsecure.PushMessageContent.GroupContext group = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContextOrBuilder> + getGroupFieldBuilder() { + if (groupBuilder_ == null) { + groupBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContextOrBuilder>( + group_, + getParentForChildren(), + isClean()); + group_ = null; + } + return groupBuilder_; + } + + // optional uint32 flags = 4; + private int flags_ ; + /** + * optional uint32 flags = 4; + */ + public boolean hasFlags() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional uint32 flags = 4; + */ + public int getFlags() { + return flags_; + } + /** + * optional uint32 flags = 4; + */ + public Builder setFlags(int value) { + bitField0_ |= 0x00000008; + flags_ = value; + onChanged(); + return this; + } + /** + * optional uint32 flags = 4; + */ + public Builder clearFlags() { + bitField0_ = (bitField0_ & ~0x00000008); + flags_ = 0; + onChanged(); + return this; + } + + // optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + private org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext sync_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder> syncBuilder_; + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + public boolean hasSync() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext getSync() { + if (syncBuilder_ == null) { + return sync_; + } else { + return syncBuilder_.getMessage(); + } + } + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + public Builder setSync(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext value) { + if (syncBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + sync_ = value; + onChanged(); + } else { + syncBuilder_.setMessage(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + public Builder setSync( + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder builderForValue) { + if (syncBuilder_ == null) { + sync_ = builderForValue.build(); + onChanged(); + } else { + syncBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + public Builder mergeSync(org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext value) { + if (syncBuilder_ == null) { + if (((bitField0_ & 0x00000010) == 0x00000010) && + sync_ != org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance()) { + sync_ = + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.newBuilder(sync_).mergeFrom(value).buildPartial(); + } else { + sync_ = value; + } + onChanged(); + } else { + syncBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + public Builder clearSync() { + if (syncBuilder_ == null) { + sync_ = org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.getDefaultInstance(); + onChanged(); + } else { + syncBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder getSyncBuilder() { + bitField0_ |= 0x00000010; + onChanged(); + return getSyncFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + public org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder getSyncOrBuilder() { + if (syncBuilder_ != null) { + return syncBuilder_.getMessageOrBuilder(); + } else { + return sync_; + } + } + /** + * optional .textsecure.PushMessageContent.SyncMessageContext sync = 5; + */ + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder> + getSyncFieldBuilder() { + if (syncBuilder_ == null) { + syncBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContext.Builder, org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.SyncMessageContextOrBuilder>( + sync_, + getParentForChildren(), + isClean()); + sync_ = null; + } + return syncBuilder_; + } + + // @@protoc_insertion_point(builder_scope:textsecure.PushMessageContent) + } + + static { + defaultInstance = new PushMessageContent(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.PushMessageContent) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_IncomingPushMessageSignal_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_IncomingPushMessageSignal_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_PushMessageContent_descriptor; + private static + com.google.protobuf.GeneratedMessage.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; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_PushMessageContent_GroupContext_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_PushMessageContent_GroupContext_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_PushMessageContent_SyncMessageContext_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_PushMessageContent_SyncMessageContext_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\037IncomingPushMessageSignal.proto\022\ntexts" + + "ecure\"\236\002\n\031IncomingPushMessageSignal\0228\n\004t" + + "ype\030\001 \001(\0162*.textsecure.IncomingPushMessa" + + "geSignal.Type\022\016\n\006source\030\002 \001(\t\022\024\n\014sourceD" + + "evice\030\007 \001(\r\022\r\n\005relay\030\003 \001(\t\022\021\n\ttimestamp\030" + + "\005 \001(\004\022\017\n\007message\030\006 \001(\014\"n\n\004Type\022\013\n\007UNKNOW" + + "N\020\000\022\016\n\nCIPHERTEXT\020\001\022\020\n\014KEY_EXCHANGE\020\002\022\021\n" + + "\rPREKEY_BUNDLE\020\003\022\r\n\tPLAINTEXT\020\004\022\013\n\007RECEI" + + "PT\020\005\022\010\n\004COPY\020\006\"\206\005\n\022PushMessageContent\022\014\n" + + "\004body\030\001 \001(\t\022E\n\013attachments\030\002 \003(\01320.texts", + "ecure.PushMessageContent.AttachmentPoint" + + "er\022:\n\005group\030\003 \001(\0132+.textsecure.PushMessa" + + "geContent.GroupContext\022\r\n\005flags\030\004 \001(\r\022?\n" + + "\004sync\030\005 \001(\01321.textsecure.PushMessageCont" + + "ent.SyncMessageContext\032A\n\021AttachmentPoin" + + "ter\022\n\n\002id\030\001 \001(\006\022\023\n\013contentType\030\002 \001(\t\022\013\n\003" + + "key\030\003 \001(\014\032\363\001\n\014GroupContext\022\n\n\002id\030\001 \001(\014\022>" + + "\n\004type\030\002 \001(\01620.textsecure.PushMessageCon" + + "tent.GroupContext.Type\022\014\n\004name\030\003 \001(\t\022\017\n\007" + + "members\030\004 \003(\t\022@\n\006avatar\030\005 \001(\01320.textsecu", + "re.PushMessageContent.AttachmentPointer\"" + + "6\n\004Type\022\013\n\007UNKNOWN\020\000\022\n\n\006UPDATE\020\001\022\013\n\007DELI" + + "VER\020\002\022\010\n\004QUIT\020\003\032<\n\022SyncMessageContext\022\023\n" + + "\013destination\030\001 \001(\t\022\021\n\ttimestamp\030\002 \001(\004\"\030\n" + + "\005Flags\022\017\n\013END_SESSION\020\001B@\n+org.whispersy" + + "stems.textsecure.internal.pushB\021PushMess" + + "ageProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_textsecure_IncomingPushMessageSignal_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_textsecure_IncomingPushMessageSignal_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_IncomingPushMessageSignal_descriptor, + new java.lang.String[] { "Type", "Source", "SourceDevice", "Relay", "Timestamp", "Message", }); + internal_static_textsecure_PushMessageContent_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_textsecure_PushMessageContent_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_PushMessageContent_descriptor, + new java.lang.String[] { "Body", "Attachments", "Group", "Flags", "Sync", }); + internal_static_textsecure_PushMessageContent_AttachmentPointer_descriptor = + internal_static_textsecure_PushMessageContent_descriptor.getNestedTypes().get(0); + internal_static_textsecure_PushMessageContent_AttachmentPointer_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_PushMessageContent_AttachmentPointer_descriptor, + new java.lang.String[] { "Id", "ContentType", "Key", }); + internal_static_textsecure_PushMessageContent_GroupContext_descriptor = + internal_static_textsecure_PushMessageContent_descriptor.getNestedTypes().get(1); + internal_static_textsecure_PushMessageContent_GroupContext_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_PushMessageContent_GroupContext_descriptor, + new java.lang.String[] { "Id", "Type", "Name", "Members", "Avatar", }); + internal_static_textsecure_PushMessageContent_SyncMessageContext_descriptor = + internal_static_textsecure_PushMessageContent_descriptor.getNestedTypes().get(2); + internal_static_textsecure_PushMessageContent_SyncMessageContext_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_PushMessageContent_SyncMessageContext_descriptor, + new java.lang.String[] { "Destination", "Timestamp", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/PushServiceSocket.java b/src/main/java/org/whispersystems/textsecure/internal/push/PushServiceSocket.java new file mode 100644 index 0000000000..a995906bb6 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/PushServiceSocket.java @@ -0,0 +1,570 @@ +/** + * Copyright (C) 2014 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.internal.push; + +import android.util.Log; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.apache.http.conn.ssl.StrictHostnameVerifier; +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.state.PreKeyBundle; +import org.whispersystems.libaxolotl.state.PreKeyRecord; +import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; +import org.whispersystems.textsecure.api.crypto.AttachmentCipherOutputStream; +import org.whispersystems.textsecure.api.push.ContactTokenDetails; +import org.whispersystems.textsecure.api.push.TextSecureAddress; +import org.whispersystems.textsecure.api.push.SignedPreKeyEntity; +import org.whispersystems.textsecure.api.push.TrustStore; +import org.whispersystems.textsecure.api.push.exceptions.AuthorizationFailedException; +import org.whispersystems.textsecure.api.push.exceptions.ExpectationFailedException; +import org.whispersystems.textsecure.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.whispersystems.textsecure.api.push.exceptions.NotFoundException; +import org.whispersystems.textsecure.api.push.exceptions.PushNetworkException; +import org.whispersystems.textsecure.api.push.exceptions.RateLimitException; +import org.whispersystems.textsecure.api.push.exceptions.UnregisteredUserException; +import org.whispersystems.textsecure.api.util.CredentialsProvider; +import org.whispersystems.textsecure.internal.push.exceptions.MismatchedDevicesException; +import org.whispersystems.textsecure.internal.push.exceptions.StaleDevicesException; +import org.whispersystems.textsecure.internal.util.Base64; +import org.whispersystems.textsecure.internal.util.BlacklistingTrustManager; +import org.whispersystems.textsecure.internal.util.JsonUtil; +import org.whispersystems.textsecure.internal.util.Util; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +/** + * + * Network interface to the TextSecure server API. + * + * @author Moxie Marlinspike + */ +public class PushServiceSocket { + + private static final String CREATE_ACCOUNT_SMS_PATH = "/v1/accounts/sms/code/%s"; + private static final String CREATE_ACCOUNT_VOICE_PATH = "/v1/accounts/voice/code/%s"; + private static final String VERIFY_ACCOUNT_PATH = "/v1/accounts/code/%s"; + private static final String REGISTER_GCM_PATH = "/v1/accounts/gcm/"; + + private static final String PREKEY_METADATA_PATH = "/v2/keys/"; + private static final String PREKEY_PATH = "/v2/keys/%s"; + private static final String PREKEY_DEVICE_PATH = "/v2/keys/%s/%s"; + private static final String SIGNED_PREKEY_PATH = "/v2/keys/signed"; + + private static final String PROVISIONING_CODE_PATH = "/v1/devices/provisioning/code"; + private static final String PROVISIONING_MESSAGE_PATH = "/v1/provisioning/%s"; + + private static final String DIRECTORY_TOKENS_PATH = "/v1/directory/tokens"; + private static final String DIRECTORY_VERIFY_PATH = "/v1/directory/%s"; + private static final String MESSAGE_PATH = "/v1/messages/%s"; + private static final String RECEIPT_PATH = "/v1/receipt/%s/%d"; + private static final String ATTACHMENT_PATH = "/v1/attachments/%s"; + + private static final boolean ENFORCE_SSL = true; + + private final String serviceUrl; + private final TrustManager[] trustManagers; + private final CredentialsProvider credentialsProvider; + + public PushServiceSocket(String serviceUrl, TrustStore trustStore, CredentialsProvider credentialsProvider) + { + this.serviceUrl = serviceUrl; + this.credentialsProvider = credentialsProvider; + this.trustManagers = BlacklistingTrustManager.createFor(trustStore); + } + + public void createAccount(boolean voice) throws IOException { + String path = voice ? CREATE_ACCOUNT_VOICE_PATH : CREATE_ACCOUNT_SMS_PATH; + makeRequest(String.format(path, credentialsProvider.getUser()), "GET", null); + } + + public void verifyAccount(String verificationCode, String signalingKey, + boolean supportsSms, int registrationId) + throws IOException + { + AccountAttributes signalingKeyEntity = new AccountAttributes(signalingKey, supportsSms, registrationId); + makeRequest(String.format(VERIFY_ACCOUNT_PATH, verificationCode), + "PUT", JsonUtil.toJson(signalingKeyEntity)); + } + + public String getNewDeviceVerificationCode() throws IOException { + String responseText = makeRequest(PROVISIONING_CODE_PATH, "GET", null); + return JsonUtil.fromJson(responseText, DeviceCode.class).getVerificationCode(); + } + + public void sendProvisioningMessage(String destination, byte[] body) throws IOException { + makeRequest(String.format(PROVISIONING_MESSAGE_PATH, destination), "PUT", + JsonUtil.toJson(new ProvisioningMessage(Base64.encodeBytes(body)))); + } + + public void sendReceipt(String destination, long messageId, String relay) throws IOException { + String path = String.format(RECEIPT_PATH, destination, messageId); + + if (!Util.isEmpty(relay)) { + path += "?relay=" + relay; + } + + makeRequest(path, "PUT", null); + } + + public void registerGcmId(String gcmRegistrationId) throws IOException { + GcmRegistrationId registration = new GcmRegistrationId(gcmRegistrationId, true); + makeRequest(REGISTER_GCM_PATH, "PUT", JsonUtil.toJson(registration)); + } + + public void unregisterGcmId() throws IOException { + makeRequest(REGISTER_GCM_PATH, "DELETE", null); + } + + public SendMessageResponse sendMessage(OutgoingPushMessageList bundle) + throws IOException + { + try { + String responseText = makeRequest(String.format(MESSAGE_PATH, bundle.getDestination()), "PUT", JsonUtil.toJson(bundle)); + + if (responseText == null) return new SendMessageResponse(false); + else return JsonUtil.fromJson(responseText, SendMessageResponse.class); + } catch (NotFoundException nfe) { + throw new UnregisteredUserException(bundle.getDestination(), nfe); + } + } + + public void registerPreKeys(IdentityKey identityKey, + PreKeyRecord lastResortKey, + SignedPreKeyRecord signedPreKey, + List records) + throws IOException + { + List entities = new LinkedList<>(); + + for (PreKeyRecord record : records) { + PreKeyEntity entity = new PreKeyEntity(record.getId(), + record.getKeyPair().getPublicKey()); + + entities.add(entity); + } + + PreKeyEntity lastResortEntity = new PreKeyEntity(lastResortKey.getId(), + lastResortKey.getKeyPair().getPublicKey()); + + SignedPreKeyEntity signedPreKeyEntity = new SignedPreKeyEntity(signedPreKey.getId(), + signedPreKey.getKeyPair().getPublicKey(), + signedPreKey.getSignature()); + + makeRequest(String.format(PREKEY_PATH, ""), "PUT", + JsonUtil.toJson(new PreKeyState(entities, lastResortEntity, + signedPreKeyEntity, identityKey))); + } + + public int getAvailablePreKeys() throws IOException { + String responseText = makeRequest(PREKEY_METADATA_PATH, "GET", null); + PreKeyStatus preKeyStatus = JsonUtil.fromJson(responseText, PreKeyStatus.class); + + return preKeyStatus.getCount(); + } + + public List getPreKeys(TextSecureAddress destination, int deviceIdInteger) throws IOException { + try { + String deviceId = String.valueOf(deviceIdInteger); + + if (deviceId.equals("1")) + deviceId = "*"; + + String path = String.format(PREKEY_DEVICE_PATH, destination.getNumber(), deviceId); + + if (!Util.isEmpty(destination.getRelay())) { + path = path + "?relay=" + destination.getRelay(); + } + + String responseText = makeRequest(path, "GET", null); + PreKeyResponse response = JsonUtil.fromJson(responseText, PreKeyResponse.class); + List bundles = new LinkedList<>(); + + for (PreKeyResponseItem device : response.getDevices()) { + ECPublicKey preKey = null; + ECPublicKey signedPreKey = null; + byte[] signedPreKeySignature = null; + int preKeyId = -1; + int signedPreKeyId = -1; + + if (device.getSignedPreKey() != null) { + signedPreKey = device.getSignedPreKey().getPublicKey(); + signedPreKeyId = device.getSignedPreKey().getKeyId(); + signedPreKeySignature = device.getSignedPreKey().getSignature(); + } + + if (device.getPreKey() != null) { + preKeyId = device.getPreKey().getKeyId(); + preKey = device.getPreKey().getPublicKey(); + } + + bundles.add(new PreKeyBundle(device.getRegistrationId(), device.getDeviceId(), preKeyId, + preKey, signedPreKeyId, signedPreKey, signedPreKeySignature, + response.getIdentityKey())); + } + + return bundles; + } catch (JsonUtil.JsonParseException e) { + throw new IOException(e); + } catch (NotFoundException nfe) { + throw new UnregisteredUserException(destination.getNumber(), nfe); + } + } + + public PreKeyBundle getPreKey(TextSecureAddress destination, int deviceId) throws IOException { + try { + String path = String.format(PREKEY_DEVICE_PATH, destination.getNumber(), + String.valueOf(deviceId)); + + if (!Util.isEmpty(destination.getRelay())) { + path = path + "?relay=" + destination.getRelay(); + } + + String responseText = makeRequest(path, "GET", null); + PreKeyResponse response = JsonUtil.fromJson(responseText, PreKeyResponse.class); + + if (response.getDevices() == null || response.getDevices().size() < 1) + throw new IOException("Empty prekey list"); + + PreKeyResponseItem device = response.getDevices().get(0); + ECPublicKey preKey = null; + ECPublicKey signedPreKey = null; + byte[] signedPreKeySignature = null; + int preKeyId = -1; + int signedPreKeyId = -1; + + if (device.getPreKey() != null) { + preKeyId = device.getPreKey().getKeyId(); + preKey = device.getPreKey().getPublicKey(); + } + + if (device.getSignedPreKey() != null) { + signedPreKeyId = device.getSignedPreKey().getKeyId(); + signedPreKey = device.getSignedPreKey().getPublicKey(); + signedPreKeySignature = device.getSignedPreKey().getSignature(); + } + + return new PreKeyBundle(device.getRegistrationId(), device.getDeviceId(), preKeyId, preKey, + signedPreKeyId, signedPreKey, signedPreKeySignature, response.getIdentityKey()); + } catch (JsonUtil.JsonParseException e) { + throw new IOException(e); + } catch (NotFoundException nfe) { + throw new UnregisteredUserException(destination.getNumber(), nfe); + } + } + + public SignedPreKeyEntity getCurrentSignedPreKey() throws IOException { + try { + String responseText = makeRequest(SIGNED_PREKEY_PATH, "GET", null); + return JsonUtil.fromJson(responseText, SignedPreKeyEntity.class); + } catch (NotFoundException e) { + Log.w("PushServiceSocket", e); + return null; + } + } + + public void setCurrentSignedPreKey(SignedPreKeyRecord signedPreKey) throws IOException { + SignedPreKeyEntity signedPreKeyEntity = new SignedPreKeyEntity(signedPreKey.getId(), + signedPreKey.getKeyPair().getPublicKey(), + signedPreKey.getSignature()); + makeRequest(SIGNED_PREKEY_PATH, "PUT", JsonUtil.toJson(signedPreKeyEntity)); + } + + public long sendAttachment(PushAttachmentData attachment) throws IOException { + String response = makeRequest(String.format(ATTACHMENT_PATH, ""), "GET", null); + AttachmentDescriptor attachmentKey = JsonUtil.fromJson(response, AttachmentDescriptor.class); + + if (attachmentKey == null || attachmentKey.getLocation() == null) { + throw new IOException("Server failed to allocate an attachment key!"); + } + + Log.w("PushServiceSocket", "Got attachment content location: " + attachmentKey.getLocation()); + + uploadAttachment("PUT", attachmentKey.getLocation(), attachment.getData(), + attachment.getDataSize(), attachment.getKey()); + + return attachmentKey.getId(); + } + + public void retrieveAttachment(String relay, long attachmentId, File destination) throws IOException { + String path = String.format(ATTACHMENT_PATH, String.valueOf(attachmentId)); + + if (!Util.isEmpty(relay)) { + path = path + "?relay=" + relay; + } + + String response = makeRequest(path, "GET", null); + AttachmentDescriptor descriptor = JsonUtil.fromJson(response, AttachmentDescriptor.class); + + Log.w("PushServiceSocket", "Attachment: " + attachmentId + " is at: " + descriptor.getLocation()); + + downloadExternalFile(descriptor.getLocation(), destination); + } + + public List retrieveDirectory(Set contactTokens) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + ContactTokenList contactTokenList = new ContactTokenList(new LinkedList<>(contactTokens)); + String response = makeRequest(DIRECTORY_TOKENS_PATH, "PUT", JsonUtil.toJson(contactTokenList)); + ContactTokenDetailsList activeTokens = JsonUtil.fromJson(response, ContactTokenDetailsList.class); + + return activeTokens.getContacts(); + } + + public ContactTokenDetails getContactTokenDetails(String contactToken) throws IOException { + try { + String response = makeRequest(String.format(DIRECTORY_VERIFY_PATH, contactToken), "GET", null); + return JsonUtil.fromJson(response, ContactTokenDetails.class); + } catch (NotFoundException nfe) { + return null; + } + } + + private void downloadExternalFile(String url, File localDestination) + throws IOException + { + URL downloadUrl = new URL(url); + HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection(); + connection.setRequestProperty("Content-Type", "application/octet-stream"); + connection.setRequestMethod("GET"); + connection.setDoInput(true); + + try { + if (connection.getResponseCode() != 200) { + throw new NonSuccessfulResponseCodeException("Bad response: " + connection.getResponseCode()); + } + + OutputStream output = new FileOutputStream(localDestination); + InputStream input = connection.getInputStream(); + byte[] buffer = new byte[4096]; + int read; + + while ((read = input.read(buffer)) != -1) { + output.write(buffer, 0, read); + } + + output.close(); + Log.w("PushServiceSocket", "Downloaded: " + url + " to: " + localDestination.getAbsolutePath()); + } catch (IOException ioe) { + throw new PushNetworkException(ioe); + } finally { + connection.disconnect(); + } + } + + private void uploadAttachment(String method, String url, InputStream data, long dataSize, byte[] key) + throws IOException + { + URL uploadUrl = new URL(url); + HttpsURLConnection connection = (HttpsURLConnection) uploadUrl.openConnection(); + connection.setDoOutput(true); + + if (dataSize > 0) { + connection.setFixedLengthStreamingMode((int) AttachmentCipherOutputStream.getCiphertextLength(dataSize)); + } else { + connection.setChunkedStreamingMode(0); + } + + connection.setRequestMethod(method); + connection.setRequestProperty("Content-Type", "application/octet-stream"); + connection.connect(); + + try { + OutputStream stream = connection.getOutputStream(); + AttachmentCipherOutputStream out = new AttachmentCipherOutputStream(key, stream); + + Util.copy(data, out); + out.flush(); + + if (connection.getResponseCode() != 200) { + throw new IOException("Bad response: " + connection.getResponseCode() + " " + connection.getResponseMessage()); + } + } finally { + connection.disconnect(); + } + } + + private String makeRequest(String urlFragment, String method, String body) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + HttpURLConnection connection = makeBaseRequest(urlFragment, method, body); + + try { + String response = Util.readFully(connection.getInputStream()); + connection.disconnect(); + + return response; + } catch (IOException ioe) { + throw new PushNetworkException(ioe); + } + } + + private HttpURLConnection makeBaseRequest(String urlFragment, String method, String body) + throws NonSuccessfulResponseCodeException, PushNetworkException + { + HttpURLConnection connection = getConnection(urlFragment, method, body); + int responseCode; + String responseMessage; + String response; + + try { + responseCode = connection.getResponseCode(); + responseMessage = connection.getResponseMessage(); + } catch (IOException ioe) { + throw new PushNetworkException(ioe); + } + + switch (responseCode) { + case 413: + connection.disconnect(); + throw new RateLimitException("Rate limit exceeded: " + responseCode); + case 401: + case 403: + connection.disconnect(); + throw new AuthorizationFailedException("Authorization failed!"); + case 404: + connection.disconnect(); + throw new NotFoundException("Not found"); + case 409: + try { + response = Util.readFully(connection.getErrorStream()); + } catch (IOException e) { + throw new PushNetworkException(e); + } + throw new MismatchedDevicesException(JsonUtil.fromJson(response, MismatchedDevices.class)); + case 410: + try { + response = Util.readFully(connection.getErrorStream()); + } catch (IOException e) { + throw new PushNetworkException(e); + } + throw new StaleDevicesException(JsonUtil.fromJson(response, StaleDevices.class)); + case 417: + throw new ExpectationFailedException(); + } + + if (responseCode != 200 && responseCode != 204) { + throw new NonSuccessfulResponseCodeException("Bad response: " + responseCode + " " + + responseMessage); + } + + return connection; + } + + private HttpURLConnection getConnection(String urlFragment, String method, String body) + throws PushNetworkException + { + try { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, trustManagers, null); + + URL url = new URL(String.format("%s%s", serviceUrl, urlFragment)); + Log.w("PushServiceSocket", "Push service URL: " + serviceUrl); + Log.w("PushServiceSocket", "Opening URL: " + url); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + + if (ENFORCE_SSL) { + ((HttpsURLConnection) connection).setSSLSocketFactory(context.getSocketFactory()); + ((HttpsURLConnection) connection).setHostnameVerifier(new StrictHostnameVerifier()); + } + + connection.setRequestMethod(method); + connection.setRequestProperty("Content-Type", "application/json"); + + if (credentialsProvider.getPassword() != null) { + connection.setRequestProperty("Authorization", getAuthorizationHeader()); + } + + if (body != null) { + connection.setDoOutput(true); + } + + connection.connect(); + + if (body != null) { + Log.w("PushServiceSocket", method + " -- " + body); + OutputStream out = connection.getOutputStream(); + out.write(body.getBytes()); + out.close(); + } + + return connection; + } catch (IOException e) { + throw new PushNetworkException(e); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new AssertionError(e); + } + } + + private String getAuthorizationHeader() { + try { + return "Basic " + Base64.encodeBytes((credentialsProvider.getUser() + ":" + credentialsProvider.getPassword()).getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } + } + + private static class GcmRegistrationId { + + @JsonProperty + private String gcmRegistrationId; + + @JsonProperty + private boolean webSocketChannel; + + public GcmRegistrationId() {} + + public GcmRegistrationId(String gcmRegistrationId, boolean webSocketChannel) { + this.gcmRegistrationId = gcmRegistrationId; + this.webSocketChannel = webSocketChannel; + } + } + + private static class AttachmentDescriptor { + @JsonProperty + private long id; + + @JsonProperty + private String location; + + public long getId() { + return id; + } + + public String getLocation() { + return location; + } + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/PushTransportDetails.java b/src/main/java/org/whispersystems/textsecure/internal/push/PushTransportDetails.java new file mode 100644 index 0000000000..23270ede19 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/PushTransportDetails.java @@ -0,0 +1,75 @@ +/** + * 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.internal.push; + +import android.util.Log; + +public class PushTransportDetails { + + private final int messageVersion; + + public PushTransportDetails(int messageVersion) { + this.messageVersion = messageVersion; + } + + public byte[] getStrippedPaddingMessageBody(byte[] messageWithPadding) { + if (messageVersion < 2) throw new AssertionError("Unknown version: " + messageVersion); + else if (messageVersion == 2) return messageWithPadding; + + int paddingStart = 0; + + for (int i=messageWithPadding.length-1;i>=0;i--) { + if (messageWithPadding[i] == (byte)0x80) { + paddingStart = i; + break; + } else if (messageWithPadding[i] != (byte)0x00) { + Log.w("PushTransportDetails", "Padding byte is malformed, returning unstripped padding."); + return messageWithPadding; + } + } + + byte[] strippedMessage = new byte[paddingStart]; + System.arraycopy(messageWithPadding, 0, strippedMessage, 0, strippedMessage.length); + + return strippedMessage; + } + + public byte[] getPaddedMessageBody(byte[] messageBody) { + if (messageVersion < 2) throw new AssertionError("Unknown version: " + messageVersion); + else if (messageVersion == 2) return messageBody; + + // NOTE: This is dumb. We have our own padding scheme, but so does the cipher. + // The +1 -1 here is to make sure the Cipher has room to add one padding byte, + // otherwise it'll add a full 16 extra bytes. + byte[] paddedMessage = new byte[getPaddedMessageLength(messageBody.length + 1) - 1]; + System.arraycopy(messageBody, 0, paddedMessage, 0, messageBody.length); + paddedMessage[messageBody.length] = (byte)0x80; + + return paddedMessage; + } + + private int getPaddedMessageLength(int messageLength) { + int messageLengthWithTerminator = messageLength + 1; + int messagePartCount = messageLengthWithTerminator / 160; + + if (messageLengthWithTerminator % 160 != 0) { + messagePartCount++; + } + + return messagePartCount * 160; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/SendMessageResponse.java b/src/main/java/org/whispersystems/textsecure/internal/push/SendMessageResponse.java new file mode 100644 index 0000000000..9b8c1e5cf2 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/SendMessageResponse.java @@ -0,0 +1,16 @@ +package org.whispersystems.textsecure.internal.push; + +public class SendMessageResponse { + + private boolean needsSync; + + public SendMessageResponse() {} + + public SendMessageResponse(boolean needsSync) { + this.needsSync = needsSync; + } + + public boolean getNeedsSync() { + return needsSync; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/StaleDevices.java b/src/main/java/org/whispersystems/textsecure/internal/push/StaleDevices.java new file mode 100644 index 0000000000..6ea74ce2ee --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/StaleDevices.java @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2014 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.internal.push; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class StaleDevices { + + @JsonProperty + private List staleDevices; + + public List getStaleDevices() { + return staleDevices; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/exceptions/MismatchedDevicesException.java b/src/main/java/org/whispersystems/textsecure/internal/push/exceptions/MismatchedDevicesException.java new file mode 100644 index 0000000000..58d89f6652 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/exceptions/MismatchedDevicesException.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2014 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.internal.push.exceptions; + +import org.whispersystems.textsecure.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.whispersystems.textsecure.internal.push.MismatchedDevices; + +public class MismatchedDevicesException extends NonSuccessfulResponseCodeException { + + private final MismatchedDevices mismatchedDevices; + + public MismatchedDevicesException(MismatchedDevices mismatchedDevices) { + this.mismatchedDevices = mismatchedDevices; + } + + public MismatchedDevices getMismatchedDevices() { + return mismatchedDevices; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/push/exceptions/StaleDevicesException.java b/src/main/java/org/whispersystems/textsecure/internal/push/exceptions/StaleDevicesException.java new file mode 100644 index 0000000000..083622b310 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/push/exceptions/StaleDevicesException.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2014 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.internal.push.exceptions; + +import org.whispersystems.textsecure.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.whispersystems.textsecure.internal.push.StaleDevices; + +public class StaleDevicesException extends NonSuccessfulResponseCodeException { + + private final StaleDevices staleDevices; + + public StaleDevicesException(StaleDevices staleDevices) { + this.staleDevices = staleDevices; + } + + public StaleDevices getStaleDevices() { + return staleDevices; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/util/Base64.java b/src/main/java/org/whispersystems/textsecure/internal/util/Base64.java new file mode 100644 index 0000000000..fccac7cd4e --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/util/Base64.java @@ -0,0 +1,2096 @@ +package org.whispersystems.textsecure.internal.util; + +/** + *

Encodes and decodes to and from Base64 notation.

+ *

Homepage: http://iharder.net/base64.

+ * + *

Example:

+ * + * String encoded = Base64.encode( myByteArray ); + *
+ * byte[] myByteArray = Base64.decode( encoded ); + * + *

The options parameter, which appears in a few places, is used to pass + * several pieces of information to the encoder. In the "higher level" methods such as + * encodeBytes( bytes, options ) the options parameter can be used to indicate such + * things as first gzipping the bytes before encoding them, not inserting linefeeds, + * and encoding using the URL-safe and Ordered dialects.

+ * + *

Note, according to RFC3548, + * Section 2.1, implementations should not add line feeds unless explicitly told + * to do so. I've got Base64 set to this behavior now, although earlier versions + * broke lines by default.

+ * + *

The constants defined in Base64 can be OR-ed together to combine options, so you + * might make a call like this:

+ * + * String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES ); + *

to compress the data before encoding it and then making the output have newline characters.

+ *

Also...

+ * String encoded = Base64.encodeBytes( crazyString.getBytes() ); + * + * + * + *

+ * Change Log: + *

+ *
    + *
  • v2.3.4 - Fixed bug when working with gzipped streams whereby flushing + * the Base64.OutputStream closed the Base64 encoding (by padding with equals + * signs) too soon. Also added an option to suppress the automatic decoding + * of gzipped streams. Also added experimental support for specifying a + * class loader when using the + * {@link #decodeToObject(java.lang.String, int, java.lang.ClassLoader)} + * method.
  • + *
  • v2.3.3 - Changed default char encoding to US-ASCII which reduces the internal Java + * footprint with its CharEncoders and so forth. Fixed some javadocs that were + * inconsistent. Removed imports and specified things like java.io.IOException + * explicitly inline.
  • + *
  • v2.3.2 - Reduced memory footprint! Finally refined the "guessing" of how big the + * final encoded data will be so that the code doesn't have to create two output + * arrays: an oversized initial one and then a final, exact-sized one. Big win + * when using the {@link #encodeBytesToBytes(byte[])} family of methods (and not + * using the gzip options which uses a different mechanism with streams and stuff).
  • + *
  • v2.3.1 - Added {@link #encodeBytesToBytes(byte[], int, int, int)} and some + * similar helper methods to be more efficient with memory by not returning a + * String but just a byte array.
  • + *
  • v2.3 - This is not a drop-in replacement! This is two years of comments + * and bug fixes queued up and finally executed. Thanks to everyone who sent + * me stuff, and I'm sorry I wasn't able to distribute your fixes to everyone else. + * Much bad coding was cleaned up including throwing exceptions where necessary + * instead of returning null values or something similar. Here are some changes + * that may affect you: + *
      + *
    • Does not break lines, by default. This is to keep in compliance with + * RFC3548.
    • + *
    • Throws exceptions instead of returning null values. Because some operations + * (especially those that may permit the GZIP option) use IO streams, there + * is a possiblity of an java.io.IOException being thrown. After some discussion and + * thought, I've changed the behavior of the methods to throw java.io.IOExceptions + * rather than return null if ever there's an error. I think this is more + * appropriate, though it will require some changes to your code. Sorry, + * it should have been done this way to begin with.
    • + *
    • Removed all references to System.out, System.err, and the like. + * Shame on me. All I can say is sorry they were ever there.
    • + *
    • Throws NullPointerExceptions and IllegalArgumentExceptions as needed + * such as when passed arrays are null or offsets are invalid.
    • + *
    • Cleaned up as much javadoc as I could to avoid any javadoc warnings. + * This was especially annoying before for people who were thorough in their + * own projects and then had gobs of javadoc warnings on this file.
    • + *
    + *
  • v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug + * when using very small files (~< 40 bytes).
  • + *
  • v2.2 - Added some helper methods for encoding/decoding directly from + * one file to the next. Also added a main() method to support command line + * encoding/decoding from one file to the next. Also added these Base64 dialects: + *
      + *
    1. The default is RFC3548 format.
    2. + *
    3. Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates + * URL and file name friendly format as described in Section 4 of RFC3548. + * http://www.faqs.org/rfcs/rfc3548.html
    4. + *
    5. Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates + * URL and file name friendly format that preserves lexical ordering as described + * in http://www.faqs.org/qa/rfcc-1940.html
    6. + *
    + * Special thanks to Jim Kellerman at http://www.powerset.com/ + * for contributing the new Base64 dialects. + *
  • + * + *
  • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added + * some convenience methods for reading and writing to and from files.
  • + *
  • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems + * with other encodings (like EBCDIC).
  • + *
  • v2.0.1 - Fixed an error when decoding a single byte, that is, when the + * encoded data was a single byte.
  • + *
  • v2.0 - I got rid of methods that used booleans to set options. + * Now everything is more consolidated and cleaner. The code now detects + * when data that's being decoded is gzip-compressed and will decompress it + * automatically. Generally things are cleaner. You'll probably have to + * change some method calls that you were making to support the new + * options format (ints that you "OR" together).
  • + *
  • v1.5.1 - Fixed bug when decompressing and decoding to a + * byte[] using decode( String s, boolean gzipCompressed ). + * Added the ability to "suspend" encoding in the Output Stream so + * you can turn on and off the encoding if you need to embed base64 + * data in an otherwise "normal" stream (like an XML file).
  • + *
  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. + * This helps when using GZIP streams. + * Added the ability to GZip-compress objects before encoding them.
  • + *
  • v1.4 - Added helper methods to read/write files.
  • + *
  • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
  • + *
  • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream + * where last buffer being read, if not completely full, was not returned.
  • + *
  • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
  • + *
  • v1.3.3 - Fixed I/O streams which were totally messed up.
  • + *
+ * + *

+ * I am placing this code in the Public Domain. Do with it as you will. + * This software comes with no guarantees or warranties but with + * plenty of well-wishing instead! + * Please visit http://iharder.net/base64 + * periodically to check for updates or to contribute improvements. + *

+ * + * @author Robert Harder + * @author rob@iharder.net + * @version 2.3.3 + */ +public class Base64 +{ + +/* ******** P U B L I C F I E L D S ******** */ + + + /** No options specified. Value is zero. */ + public final static int NO_OPTIONS = 0; + + /** Specify encoding in first bit. Value is one. */ + public final static int ENCODE = 1; + + + /** Specify decoding in first bit. Value is zero. */ + public final static int DECODE = 0; + + + /** Specify that data should be gzip-compressed in second bit. Value is two. */ + public final static int GZIP = 2; + + /** Specify that gzipped data should not be automatically gunzipped. */ + public final static int DONT_GUNZIP = 4; + + + /** Do break lines when encoding. Value is 8. */ + public final static int DO_BREAK_LINES = 8; + + /** + * Encode using Base64-like encoding that is URL- and Filename-safe as described + * in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * It is important to note that data encoded this way is not officially valid Base64, + * or at the very least should not be called Base64 without also specifying that is + * was encoded using the URL- and Filename-safe dialect. + */ + public final static int URL_SAFE = 16; + + + /** + * Encode using the special "ordered" dialect of Base64 described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + public final static int ORDERED = 32; + + +/* ******** P R I V A T E F I E L D S ******** */ + + + /** Maximum line length (76) of Base64 output. */ + private final static int MAX_LINE_LENGTH = 76; + + + /** The equals sign (=) as a byte. */ + private final static byte EQUALS_SIGN = (byte)'='; + + + /** The new line character (\n) as a byte. */ + private final static byte NEW_LINE = (byte)'\n'; + + + /** Preferred encoding. */ + private final static String PREFERRED_ENCODING = "US-ASCII"; + + + private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding + private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding + + +/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ + + /** The 64 valid Base64 values. */ + /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ + private final static byte[] _STANDARD_ALPHABET = { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' + }; + + + /** + * Translates a Base64 value to either its 6-bit reconstruction value + * or a negative number indicating some other meaning. + **/ + private final static byte[] _STANDARD_DECODABET = { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + 62, // Plus sign at decimal 43 + -9,-9,-9, // Decimal 44 - 46 + 63, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + +/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ + + /** + * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." + */ + private final static byte[] _URL_SAFE_ALPHABET = { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' + }; + + /** + * Used in decoding URL- and Filename-safe dialects of Base64. + */ + private final static byte[] _URL_SAFE_DECODABET = { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 62, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9, // Decimal 91 - 94 + 63, // Underscore at decimal 95 + -9, // Decimal 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + + +/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ + + /** + * I don't get the point of this technique, but someone requested it, + * and it is described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + private final static byte[] _ORDERED_ALPHABET = { + (byte)'-', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', + (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'_', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' + }; + + /** + * Used in decoding the "ordered" dialect of Base64. + */ + private final static byte[] _ORDERED_DECODABET = { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 0, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' + 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' + -9,-9,-9,-9, // Decimal 91 - 94 + 37, // Underscore at decimal 95 + -9, // Decimal 96 + 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' + 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + +/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ + + + /** + * Returns one of the _SOMETHING_ALPHABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URLSAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getAlphabet( int options ) { + if ((options & URL_SAFE) == URL_SAFE) { + return _URL_SAFE_ALPHABET; + } else if ((options & ORDERED) == ORDERED) { + return _ORDERED_ALPHABET; + } else { + return _STANDARD_ALPHABET; + } + } // end getAlphabet + + + /** + * Returns one of the _SOMETHING_DECODABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URL_SAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getDecodabet( int options ) { + if( (options & URL_SAFE) == URL_SAFE) { + return _URL_SAFE_DECODABET; + } else if ((options & ORDERED) == ORDERED) { + return _ORDERED_DECODABET; + } else { + return _STANDARD_DECODABET; + } + } // end getAlphabet + + + + /** Defeats instantiation. */ + private Base64(){} + + + + public static int getEncodedLengthWithoutPadding(int unencodedLength) { + int remainderBytes = unencodedLength % 3; + int paddingBytes = 0; + + if (remainderBytes != 0) + paddingBytes = 3 - remainderBytes; + + return (((int)((unencodedLength+2)/3))*4) - paddingBytes; + } + + public static int getEncodedBytesForTarget(int targetSize) { + return ((int)(targetSize * 3)) / 4; + } + + +/* ******** E N C O D I N G M E T H O D S ******** */ + + + /** + * Encodes up to the first three bytes of array threeBytes + * and returns a four-byte array in Base64 notation. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * The array threeBytes needs only be as big as + * numSigBytes. + * Code can reuse a byte array by passing a four-byte array as b4. + * + * @param b4 A reusable byte array to reduce array instantiation + * @param threeBytes the array to convert + * @param numSigBytes the number of significant bytes in your array + * @return four byte array in Base64 notation. + * @since 1.5.1 + */ + private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) { + encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); + return b4; + } // end encode3to4 + + + /** + *

Encodes up to three bytes of the array source + * and writes the resulting four Base64 bytes to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 3 for + * the source array or destOffset + 4 for + * the destination array. + * The actual number of significant bytes in your array is + * given by numSigBytes.

+ *

This is the lowest level of the encoding methods with + * all possible parameters.

+ * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param numSigBytes the number of significant bytes in your array + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the destination array + * @since 1.3 + */ + private static byte[] encode3to4( + byte[] source, int srcOffset, int numSigBytes, + byte[] destination, int destOffset, int options ) { + + byte[] ALPHABET = getAlphabet( options ); + + // 1 2 3 + // 01234567890123456789012345678901 Bit position + // --------000000001111111122222222 Array position from threeBytes + // --------| || || || | Six bit groups to index ALPHABET + // >>18 >>12 >> 6 >> 0 Right shift necessary + // 0x3f 0x3f 0x3f Additional AND + + // Create buffer with zero-padding if there are only one or two + // significant bytes passed in the array. + // We have to shift left 24 in order to flush out the 1's that appear + // when Java treats a value as negative that is cast from a byte to an int. + int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) + | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) + | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); + + switch( numSigBytes ) + { + case 3: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; + return destination; + + case 2: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + case 1: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = EQUALS_SIGN; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + default: + return destination; + } // end switch + } // end encode3to4 + + + + /** + * Performs Base64 encoding on the raw ByteBuffer, + * writing it to the encoded ByteBuffer. + * This is an experimental feature. Currently it does not + * pass along any options (such as {@link #DO_BREAK_LINES} + * or {@link #GZIP}. + * + * @param raw input buffer + * @param encoded output buffer + * @since 2.3 + */ + public static void encode( java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded ){ + byte[] raw3 = new byte[3]; + byte[] enc4 = new byte[4]; + + while( raw.hasRemaining() ){ + int rem = Math.min(3,raw.remaining()); + raw.get(raw3,0,rem); + Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS ); + encoded.put(enc4); + } // end input remaining + } + + + /** + * Performs Base64 encoding on the raw ByteBuffer, + * writing it to the encoded CharBuffer. + * This is an experimental feature. Currently it does not + * pass along any options (such as {@link #DO_BREAK_LINES} + * or {@link #GZIP}. + * + * @param raw input buffer + * @param encoded output buffer + * @since 2.3 + */ + public static void encode( java.nio.ByteBuffer raw, java.nio.CharBuffer encoded ){ + byte[] raw3 = new byte[3]; + byte[] enc4 = new byte[4]; + + while( raw.hasRemaining() ){ + int rem = Math.min(3,raw.remaining()); + raw.get(raw3,0,rem); + Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS ); + for( int i = 0; i < 4; i++ ){ + encoded.put( (char)(enc4[i] & 0xFF) ); + } + } // end input remaining + } + + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. + * + *

As of v 2.3, if the object + * cannot be serialized or there is another error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * The object is not GZip-compressed before being encoded. + * + * @param serializableObject The object to encode + * @return The Base64-encoded object + * @throws java.io.IOException if there is an error + * @throws NullPointerException if serializedObject is null + * @since 1.4 + */ + public static String encodeObject( java.io.Serializable serializableObject ) + throws java.io.IOException { + return encodeObject( serializableObject, NO_OPTIONS ); + } // end encodeObject + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. + * + *

As of v 2.3, if the object + * cannot be serialized or there is another error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * The object is not GZip-compressed before being encoded. + *

+ * Example options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DO_BREAK_LINES: break lines at 76 characters
+     * 
+ *

+ * Example: encodeObject( myObj, Base64.GZIP ) or + *

+ * Example: encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES ) + * + * @param serializableObject The object to encode + * @param options Specified options + * @return The Base64-encoded object + * @see Base64#GZIP + * @see Base64#DO_BREAK_LINES + * @throws java.io.IOException if there is an error + * @since 2.0 + */ + public static String encodeObject( java.io.Serializable serializableObject, int options ) + throws java.io.IOException { + + if( serializableObject == null ){ + throw new NullPointerException( "Cannot serialize a null object." ); + } // end if: null + + // Streams + java.io.ByteArrayOutputStream baos = null; + java.io.OutputStream b64os = null; + java.util.zip.GZIPOutputStream gzos = null; + java.io.ObjectOutputStream oos = null; + + + try { + // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | options ); + if( (options & GZIP) != 0 ){ + // Gzip + gzos = new java.util.zip.GZIPOutputStream(b64os); + oos = new java.io.ObjectOutputStream( gzos ); + } else { + // Not gzipped + oos = new java.io.ObjectOutputStream( b64os ); + } + oos.writeObject( serializableObject ); + } // end try + catch( java.io.IOException e ) { + // Catch it and then throw it immediately so that + // the finally{} block is called for cleanup. + throw e; + } // end catch + finally { + try{ oos.close(); } catch( Exception e ){} + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue){ + // Fall back to some Java default + return new String( baos.toByteArray() ); + } // end catch + + } // end encode + + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @return The data in Base64-encoded form + * @throws NullPointerException if source array is null + * @since 1.4 + */ + public static String encodeBytes( byte[] source ) { + // Since we're not going to have the GZIP encoding turned on, + // we're not going to have an java.io.IOException thrown, so + // we should not force the user to have to catch it. + String encoded = null; + try { + encoded = encodeBytes(source, 0, source.length, NO_OPTIONS); + } catch (java.io.IOException ex) { + assert false : ex.getMessage(); + } // end catch + assert encoded != null; + return encoded; + } // end encodeBytes + + + public static String encodeBytesWithoutPadding(byte[] source, int offset, int length) { + String encoded = null; + + try { + encoded = encodeBytes(source, offset, length, NO_OPTIONS); + } catch (java.io.IOException ex) { + assert false : ex.getMessage(); + } + + assert encoded != null; + + if (encoded.charAt(encoded.length()-2) == '=') return encoded.substring(0, encoded.length()-2); + else if (encoded.charAt(encoded.length()-1) == '=') return encoded.substring(0, encoded.length()-1); + else return encoded; + + } + + public static String encodeBytesWithoutPadding(byte[] source) { + return encodeBytesWithoutPadding(source, 0, source.length); + } + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Example options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DO_BREAK_LINES: break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) + * + * + *

As of v 2.3, if there is an error with the GZIP stream, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * + * @param source The data to convert + * @param options Specified options + * @return The Base64-encoded data as a String + * @see Base64#GZIP + * @see Base64#DO_BREAK_LINES + * @throws java.io.IOException if there is an error + * @throws NullPointerException if source array is null + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int options ) throws java.io.IOException { + return encodeBytes( source, 0, source.length, options ); + } // end encodeBytes + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + *

As of v 2.3, if there is an error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @return The Base64-encoded data as a String + * @throws NullPointerException if source array is null + * @throws IllegalArgumentException if source array, offset, or length are invalid + * @since 1.4 + */ + public static String encodeBytes( byte[] source, int off, int len ) { + // Since we're not going to have the GZIP encoding turned on, + // we're not going to have an java.io.IOException thrown, so + // we should not force the user to have to catch it. + String encoded = null; + try { + encoded = encodeBytes( source, off, len, NO_OPTIONS ); + } catch (java.io.IOException ex) { + assert false : ex.getMessage(); + } // end catch + assert encoded != null; + return encoded; + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Example options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DO_BREAK_LINES: break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) + * + * + *

As of v 2.3, if there is an error with the GZIP stream, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned a null value, but + * in retrospect that's a pretty poor way to handle it.

+ * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param options Specified options + * @return The Base64-encoded data as a String + * @see Base64#GZIP + * @see Base64#DO_BREAK_LINES + * @throws java.io.IOException if there is an error + * @throws NullPointerException if source array is null + * @throws IllegalArgumentException if source array, offset, or length are invalid + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { + byte[] encoded = encodeBytesToBytes( source, off, len, options ); + + // Return value according to relevant encoding. + try { + return new String( encoded, PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) { + return new String( encoded ); + } // end catch + + } // end encodeBytes + + + + + /** + * Similar to {@link #encodeBytes(byte[])} but returns + * a byte array instead of instantiating a String. This is more efficient + * if you're working with I/O streams and have large data sets to encode. + * + * + * @param source The data to convert + * @return The Base64-encoded data as a byte[] (of ASCII characters) + * @throws NullPointerException if source array is null + * @since 2.3.1 + */ + public static byte[] encodeBytesToBytes( byte[] source ) { + byte[] encoded = null; + try { + encoded = encodeBytesToBytes( source, 0, source.length, Base64.NO_OPTIONS ); + } catch( java.io.IOException ex ) { + assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); + } + return encoded; + } + + + /** + * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns + * a byte array instead of instantiating a String. This is more efficient + * if you're working with I/O streams and have large data sets to encode. + * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param options Specified options + * @return The Base64-encoded data as a String + * @see Base64#GZIP + * @see Base64#DO_BREAK_LINES + * @throws java.io.IOException if there is an error + * @throws NullPointerException if source array is null + * @throws IllegalArgumentException if source array, offset, or length are invalid + * @since 2.3.1 + */ + public static byte[] encodeBytesToBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { + + if( source == null ){ + throw new NullPointerException( "Cannot serialize a null array." ); + } // end if: null + + if( off < 0 ){ + throw new IllegalArgumentException( "Cannot have negative offset: " + off ); + } // end if: off < 0 + + if( len < 0 ){ + throw new IllegalArgumentException( "Cannot have length offset: " + len ); + } // end if: len < 0 + + if( off + len > source.length ){ + throw new IllegalArgumentException( + String.format( "Cannot have offset of %d and length of %d with array of length %d", off,len,source.length)); + } // end if: off < 0 + + + + // Compress? + if( (options & GZIP) != 0 ) { + java.io.ByteArrayOutputStream baos = null; + java.util.zip.GZIPOutputStream gzos = null; + Base64.OutputStream b64os = null; + + try { + // GZip -> Base64 -> ByteArray + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | options ); + gzos = new java.util.zip.GZIPOutputStream( b64os ); + + gzos.write( source, off, len ); + gzos.close(); + } // end try + catch( java.io.IOException e ) { + // Catch it and then throw it immediately so that + // the finally{} block is called for cleanup. + throw e; + } // end catch + finally { + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + return baos.toByteArray(); + } // end if: compress + + // Else, don't compress. Better not to use streams at all then. + else { + boolean breakLines = (options & DO_BREAK_LINES) > 0; + + //int len43 = len * 4 / 3; + //byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding + // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines + // Try to determine more precisely how big the array needs to be. + // If we get it right, we don't have to do an array copy, and + // we save a bunch of memory. + int encLen = ( len / 3 ) * 4 + ( len % 3 > 0 ? 4 : 0 ); // Bytes needed for actual encoding + if( breakLines ){ + encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters + } + byte[] outBuff = new byte[ encLen ]; + + + int d = 0; + int e = 0; + int len2 = len - 2; + int lineLength = 0; + for( ; d < len2; d+=3, e+=4 ) { + encode3to4( source, d+off, 3, outBuff, e, options ); + + lineLength += 4; + if( breakLines && lineLength >= MAX_LINE_LENGTH ) + { + outBuff[e+4] = NEW_LINE; + e++; + lineLength = 0; + } // end if: end of line + } // en dfor: each piece of array + + if( d < len ) { + encode3to4( source, d+off, len - d, outBuff, e, options ); + e += 4; + } // end if: some padding needed + + + // Only resize array if we didn't guess it right. + if( e < outBuff.length - 1 ){ + byte[] finalOut = new byte[e]; + System.arraycopy(outBuff,0, finalOut,0,e); + //System.err.println("Having to resize array from " + outBuff.length + " to " + e ); + return finalOut; + } else { + //System.err.println("No need to resize array."); + return outBuff; + } + + } // end else: don't compress + + } // end encodeBytesToBytes + + + + + +/* ******** D E C O D I N G M E T H O D S ******** */ + + + /** + * Decodes four bytes from array source + * and writes the resulting bytes (up to three of them) + * to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 4 for + * the source array or destOffset + 3 for + * the destination array. + * This method returns the actual number of bytes that + * were converted from the Base64 encoding. + *

This is the lowest level of the decoding methods with + * all possible parameters.

+ * + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @param options alphabet type is pulled from this (standard, url-safe, ordered) + * @return the number of decoded bytes converted + * @throws NullPointerException if source or destination arrays are null + * @throws IllegalArgumentException if srcOffset or destOffset are invalid + * or there is not enough room in the array. + * @since 1.3 + */ + private static int decode4to3( + byte[] source, int srcOffset, + byte[] destination, int destOffset, int options ) { + + // Lots of error checking and exception throwing + if( source == null ){ + throw new NullPointerException( "Source array was null." ); + } // end if + if( destination == null ){ + throw new NullPointerException( "Destination array was null." ); + } // end if + if( srcOffset < 0 || srcOffset + 3 >= source.length ){ + throw new IllegalArgumentException( String.format( + "Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset ) ); + } // end if + if( destOffset < 0 || destOffset +2 >= destination.length ){ + throw new IllegalArgumentException( String.format( + "Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset ) ); + } // end if + + + byte[] DECODABET = getDecodabet( options ); + + // Example: Dk== + if( source[ srcOffset + 2] == EQUALS_SIGN ) { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + return 1; + } + + // Example: DkL= + else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); + return 2; + } + + // Example: DkLE + else { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) + // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) + | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); + + + destination[ destOffset ] = (byte)( outBuff >> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); + destination[ destOffset + 2 ] = (byte)( outBuff ); + + return 3; + } + } // end decodeToBytes + + + + + + /** + * Low-level access to decoding ASCII characters in + * the form of a byte array. Ignores GUNZIP option, if + * it's set. This is not generally a recommended method, + * although it is used internally as part of the decoding process. + * Special case: if len = 0, an empty array is returned. Still, + * if you need more speed and reduced memory footprint (and aren't + * gzipping), consider this method. + * + * @param source The Base64 encoded data + * @return decoded data + * @since 2.3.1 + */ + public static byte[] decode( byte[] source ){ + byte[] decoded = null; + try { + decoded = decode( source, 0, source.length, Base64.NO_OPTIONS ); + } catch( java.io.IOException ex ) { + assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); + } + return decoded; + } + + + /** + * Low-level access to decoding ASCII characters in + * the form of a byte array. Ignores GUNZIP option, if + * it's set. This is not generally a recommended method, + * although it is used internally as part of the decoding process. + * Special case: if len = 0, an empty array is returned. Still, + * if you need more speed and reduced memory footprint (and aren't + * gzipping), consider this method. + * + * @param source The Base64 encoded data + * @param off The offset of where to begin decoding + * @param len The length of characters to decode + * @param options Can specify options such as alphabet type to use + * @return decoded data + * @throws java.io.IOException If bogus characters exist in source data + * @since 1.3 + */ + public static byte[] decode( byte[] source, int off, int len, int options ) + throws java.io.IOException { + + // Lots of error checking and exception throwing + if( source == null ){ + throw new NullPointerException( "Cannot decode null source array." ); + } // end if + if( off < 0 || off + len > source.length ){ + throw new IllegalArgumentException( String.format( + "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len ) ); + } // end if + + if( len == 0 ){ + return new byte[0]; + }else if( len < 4 ){ + throw new IllegalArgumentException( + "Base64-encoded string must have at least four characters, but length specified was " + len ); + } // end if + + byte[] DECODABET = getDecodabet( options ); + + int len34 = len * 3 / 4; // Estimate on array size + byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output + int outBuffPosn = 0; // Keep track of where we're writing + + byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space + int b4Posn = 0; // Keep track of four byte input buffer + int i = 0; // Source array counter + byte sbiCrop = 0; // Low seven bits (ASCII) of input + byte sbiDecode = 0; // Special value from DECODABET + + for( i = off; i < off+len; i++ ) { // Loop through source + + sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits + sbiDecode = DECODABET[ sbiCrop ]; // Special value + + // White space, Equals sign, or legit Base64 character + // Note the values such as -5 and -9 in the + // DECODABETs at the top of the file. + if( sbiDecode >= WHITE_SPACE_ENC ) { + if( sbiDecode >= EQUALS_SIGN_ENC ) { + b4[ b4Posn++ ] = sbiCrop; // Save non-whitespace + if( b4Posn > 3 ) { // Time to decode? + outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); + b4Posn = 0; + + // If that was the equals sign, break out of 'for' loop + if( sbiCrop == EQUALS_SIGN ) { + break; + } // end if: equals sign + } // end if: quartet built + } // end if: equals sign or better + } // end if: white space, equals sign or better + else { + // There's a bad input character in the Base64 stream. + throw new java.io.IOException( String.format( + "Bad Base64 input character '%c' in array position %d", source[i], i ) ); + } // end else: + } // each input character + + byte[] out = new byte[ outBuffPosn ]; + System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); + return out; + } // end decode + + + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @return the decoded data + * @throws java.io.IOException If there is a problem + * @since 1.4 + */ + public static byte[] decode( String s ) throws java.io.IOException { + return decode( s, NO_OPTIONS ); + } + + + public static byte[] decodeWithoutPadding(String source) throws java.io.IOException { + int padding = source.length() % 4; + + if (padding == 1) source = source + "="; + else if (padding == 2) source = source + "=="; + else if (padding == 3) source = source + "="; + + return decode(source); + } + + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @param options encode options such as URL_SAFE + * @return the decoded data + * @throws java.io.IOException if there is an error + * @throws NullPointerException if s is null + * @since 1.4 + */ + public static byte[] decode( String s, int options ) throws java.io.IOException { + + if( s == null ){ + throw new NullPointerException( "Input string was null." ); + } // end if + + byte[] bytes; + try { + bytes = s.getBytes( PREFERRED_ENCODING ); + } // end try + catch( java.io.UnsupportedEncodingException uee ) { + bytes = s.getBytes(); + } // end catch + // + + // Decode + bytes = decode( bytes, 0, bytes.length, options ); + + // Check to see if it's gzip-compressed + // GZIP Magic Two-Byte Number: 0x8b1f (35615) + boolean dontGunzip = (options & DONT_GUNZIP) != 0; + if( (bytes != null) && (bytes.length >= 4) && (!dontGunzip) ) { + + int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) { + java.io.ByteArrayInputStream bais = null; + java.util.zip.GZIPInputStream gzis = null; + java.io.ByteArrayOutputStream baos = null; + byte[] buffer = new byte[2048]; + int length = 0; + + try { + baos = new java.io.ByteArrayOutputStream(); + bais = new java.io.ByteArrayInputStream( bytes ); + gzis = new java.util.zip.GZIPInputStream( bais ); + + while( ( length = gzis.read( buffer ) ) >= 0 ) { + baos.write(buffer,0,length); + } // end while: reading input + + // No error? Get new bytes. + bytes = baos.toByteArray(); + + } // end try + catch( java.io.IOException e ) { + e.printStackTrace(); + // Just return originally-decoded bytes + } // end catch + finally { + try{ baos.close(); } catch( Exception e ){} + try{ gzis.close(); } catch( Exception e ){} + try{ bais.close(); } catch( Exception e ){} + } // end finally + + } // end if: gzipped + } // end if: bytes.length >= 2 + + return bytes; + } // end decode + + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns null if there was an error. + * + * @param encodedObject The Base64 data to decode + * @return The decoded and deserialized object + * @throws NullPointerException if encodedObject is null + * @throws java.io.IOException if there is a general error + * @throws ClassNotFoundException if the decoded object is of a + * class that cannot be found by the JVM + * @since 1.5 + */ + public static Object decodeToObject( String encodedObject ) + throws java.io.IOException, java.lang.ClassNotFoundException { + return decodeToObject(encodedObject,NO_OPTIONS,null); + } + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns null if there was an error. + * If loader is not null, it will be the class loader + * used when deserializing. + * + * @param encodedObject The Base64 data to decode + * @param options Various parameters related to decoding + * @param loader Optional class loader to use in deserializing classes. + * @return The decoded and deserialized object + * @throws NullPointerException if encodedObject is null + * @throws java.io.IOException if there is a general error + * @throws ClassNotFoundException if the decoded object is of a + * class that cannot be found by the JVM + * @since 2.3.4 + */ + public static Object decodeToObject( + String encodedObject, int options, final ClassLoader loader ) + throws java.io.IOException, java.lang.ClassNotFoundException { + + // Decode and gunzip if necessary + byte[] objBytes = decode( encodedObject, options ); + + java.io.ByteArrayInputStream bais = null; + java.io.ObjectInputStream ois = null; + Object obj = null; + + try { + bais = new java.io.ByteArrayInputStream( objBytes ); + + // If no custom class loader is provided, use Java's builtin OIS. + if( loader == null ){ + ois = new java.io.ObjectInputStream( bais ); + } // end if: no loader provided + + // Else make a customized object input stream that uses + // the provided class loader. + else { + ois = new java.io.ObjectInputStream(bais){ + @Override + public Class resolveClass(java.io.ObjectStreamClass streamClass) + throws java.io.IOException, ClassNotFoundException { + Class c = Class.forName(streamClass.getName(), false, loader); + if( c == null ){ + return super.resolveClass(streamClass); + } else { + return c; // Class loader knows of this class. + } // end else: not null + } // end resolveClass + }; // end ois + } // end else: no custom class loader + + obj = ois.readObject(); + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and throw in order to execute finally{} + } // end catch + catch( java.lang.ClassNotFoundException e ) { + throw e; // Catch and throw in order to execute finally{} + } // end catch + finally { + try{ bais.close(); } catch( Exception e ){} + try{ ois.close(); } catch( Exception e ){} + } // end finally + + return obj; + } // end decodeObject + + + + /** + * Convenience method for encoding data to a file. + * + *

As of v 2.3, if there is a error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned false, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param dataToEncode byte array of data to encode in base64 form + * @param filename Filename for saving encoded data + * @throws java.io.IOException if there is an error + * @throws NullPointerException if dataToEncode is null + * @since 2.1 + */ + public static void encodeToFile( byte[] dataToEncode, String filename ) + throws java.io.IOException { + + if( dataToEncode == null ){ + throw new NullPointerException( "Data to encode was null." ); + } // end iff + + Base64.OutputStream bos = null; + try { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.ENCODE ); + bos.write( dataToEncode ); + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and throw to execute finally{} block + } // end catch: java.io.IOException + finally { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + } // end encodeToFile + + + /** + * Convenience method for decoding data to a file. + * + *

As of v 2.3, if there is a error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned false, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param dataToDecode Base64-encoded data as a string + * @param filename Filename for saving decoded data + * @throws java.io.IOException if there is an error + * @since 2.1 + */ + public static void decodeToFile( String dataToDecode, String filename ) + throws java.io.IOException { + + Base64.OutputStream bos = null; + try{ + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.DECODE ); + bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and throw to execute finally{} block + } // end catch: java.io.IOException + finally { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + } // end decodeToFile + + + + + /** + * Convenience method for reading a base64-encoded + * file and decoding it. + * + *

As of v 2.3, if there is a error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned false, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param filename Filename for reading encoded data + * @return decoded byte array + * @throws java.io.IOException if there is an error + * @since 2.1 + */ + public static byte[] decodeFromFile( String filename ) + throws java.io.IOException { + + byte[] decodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = null; + int length = 0; + int numBytes = 0; + + // Check for size of file + if( file.length() > Integer.MAX_VALUE ) + { + throw new java.io.IOException( "File is too big for this convenience method (" + file.length() + " bytes)." ); + } // end if: file too big for int index + buffer = new byte[ (int)file.length() ]; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.DECODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) { + length += numBytes; + } // end while + + // Save in a variable to return + decodedData = new byte[ length ]; + System.arraycopy( buffer, 0, decodedData, 0, length ); + + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and release to execute finally{} + } // end catch: java.io.IOException + finally { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return decodedData; + } // end decodeFromFile + + + + /** + * Convenience method for reading a binary file + * and base64-encoding it. + * + *

As of v 2.3, if there is a error, + * the method will throw an java.io.IOException. This is new to v2.3! + * In earlier versions, it just returned false, but + * in retrospect that's a pretty poor way to handle it.

+ * + * @param filename Filename for reading binary data + * @return base64-encoded string + * @throws java.io.IOException if there is an error + * @since 2.1 + */ + public static String encodeFromFile( String filename ) + throws java.io.IOException { + + String encodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1) + int length = 0; + int numBytes = 0; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.ENCODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) { + length += numBytes; + } // end while + + // Save in a variable to return + encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); + + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and release to execute finally{} + } // end catch: java.io.IOException + finally { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return encodedData; + } // end encodeFromFile + + /** + * Reads infile and encodes it to outfile. + * + * @param infile Input file + * @param outfile Output file + * @throws java.io.IOException if there is an error + * @since 2.2 + */ + public static void encodeFileToFile( String infile, String outfile ) + throws java.io.IOException { + + String encoded = Base64.encodeFromFile( infile ); + java.io.OutputStream out = null; + try{ + out = new java.io.BufferedOutputStream( + new java.io.FileOutputStream( outfile ) ); + out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output. + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and release to execute finally{} + } // end catch + finally { + try { out.close(); } + catch( Exception ex ){} + } // end finally + } // end encodeFileToFile + + + /** + * Reads infile and decodes it to outfile. + * + * @param infile Input file + * @param outfile Output file + * @throws java.io.IOException if there is an error + * @since 2.2 + */ + public static void decodeFileToFile( String infile, String outfile ) + throws java.io.IOException { + + byte[] decoded = Base64.decodeFromFile( infile ); + java.io.OutputStream out = null; + try{ + out = new java.io.BufferedOutputStream( + new java.io.FileOutputStream( outfile ) ); + out.write( decoded ); + } // end try + catch( java.io.IOException e ) { + throw e; // Catch and release to execute finally{} + } // end catch + finally { + try { out.close(); } + catch( Exception ex ){} + } // end finally + } // end decodeFileToFile + + + /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.InputStream} will read data from another + * java.io.InputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class InputStream extends java.io.FilterInputStream { + + private boolean encode; // Encoding or decoding + private int position; // Current position in the buffer + private byte[] buffer; // Small buffer holding converted data + private int bufferLength; // Length of buffer (3 or 4) + private int numSigBytes; // Number of meaningful bytes in the buffer + private int lineLength; + private boolean breakLines; // Break lines at less than 80 characters + private int options; // Record options used to create the stream. + private byte[] decodabet; // Local copies to avoid extra method calls + + + /** + * Constructs a {@link Base64.InputStream} in DECODE mode. + * + * @param in the java.io.InputStream from which to read data. + * @since 1.3 + */ + public InputStream( java.io.InputStream in ) { + this( in, DECODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.InputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DO_BREAK_LINES: break lines at 76 characters
+         *     (only meaningful when encoding)
+         * 
+ *

+ * Example: new Base64.InputStream( in, Base64.DECODE ) + * + * + * @param in the java.io.InputStream from which to read data. + * @param options Specified options + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DO_BREAK_LINES + * @since 2.0 + */ + public InputStream( java.io.InputStream in, int options ) { + + super( in ); + this.options = options; // Record for later + this.breakLines = (options & DO_BREAK_LINES) > 0; + this.encode = (options & ENCODE) > 0; + this.bufferLength = encode ? 4 : 3; + this.buffer = new byte[ bufferLength ]; + this.position = -1; + this.lineLength = 0; + this.decodabet = getDecodabet(options); + } // end constructor + + /** + * Reads enough of the input stream to convert + * to/from Base64 and returns the next byte. + * + * @return next byte + * @since 1.3 + */ + @Override + public int read() throws java.io.IOException { + + // Do we need to get data? + if( position < 0 ) { + if( encode ) { + byte[] b3 = new byte[3]; + int numBinaryBytes = 0; + for( int i = 0; i < 3; i++ ) { + int b = in.read(); + + // If end of stream, b is -1. + if( b >= 0 ) { + b3[i] = (byte)b; + numBinaryBytes++; + } else { + break; // out of for loop + } // end else: end of stream + + } // end for: each needed input byte + + if( numBinaryBytes > 0 ) { + encode3to4( b3, 0, numBinaryBytes, buffer, 0, options ); + position = 0; + numSigBytes = 4; + } // end if: got data + else { + return -1; // Must be end of stream + } // end else + } // end if: encoding + + // Else decoding + else { + byte[] b4 = new byte[4]; + int i = 0; + for( i = 0; i < 4; i++ ) { + // Read four "meaningful" bytes: + int b = 0; + do{ b = in.read(); } + while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC ); + + if( b < 0 ) { + break; // Reads a -1 if end of stream + } // end if: end of stream + + b4[i] = (byte)b; + } // end for: each needed input byte + + if( i == 4 ) { + numSigBytes = decode4to3( b4, 0, buffer, 0, options ); + position = 0; + } // end if: got four characters + else if( i == 0 ){ + return -1; + } // end else if: also padded correctly + else { + // Must have broken out from above. + throw new java.io.IOException( "Improperly padded Base64 input." ); + } // end + + } // end else: decode + } // end else: get data + + // Got data? + if( position >= 0 ) { + // End of relevant data? + if( /*!encode &&*/ position >= numSigBytes ){ + return -1; + } // end if: got data + + if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) { + lineLength = 0; + return '\n'; + } // end if + else { + lineLength++; // This isn't important when decoding + // but throwing an extra "if" seems + // just as wasteful. + + int b = buffer[ position++ ]; + + if( position >= bufferLength ) { + position = -1; + } // end if: end + + return b & 0xFF; // This is how you "cast" a byte that's + // intended to be unsigned. + } // end else + } // end if: position >= 0 + + // Else error + else { + throw new java.io.IOException( "Error in Base64 code reading stream." ); + } // end else + } // end read + + + /** + * Calls {@link #read()} repeatedly until the end of stream + * is reached or len bytes are read. + * Returns number of bytes read into array or -1 if + * end of stream is encountered. + * + * @param dest array to hold values + * @param off offset for array + * @param len max number of bytes to read into array + * @return bytes read into array or -1 if end of stream is encountered. + * @since 1.3 + */ + @Override + public int read( byte[] dest, int off, int len ) + throws java.io.IOException { + int i; + int b; + for( i = 0; i < len; i++ ) { + b = read(); + + if( b >= 0 ) { + dest[off + i] = (byte) b; + } + else if( i == 0 ) { + return -1; + } + else { + break; // Out of 'for' loop + } // Out of 'for' loop + } // end for: each byte read + return i; + } // end read + + } // end inner class InputStream + + + + + + + /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.OutputStream} will write data to another + * java.io.OutputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class OutputStream extends java.io.FilterOutputStream { + + private boolean encode; + private int position; + private byte[] buffer; + private int bufferLength; + private int lineLength; + private boolean breakLines; + private byte[] b4; // Scratch used in a few places + private boolean suspendEncoding; + private int options; // Record for later + private byte[] decodabet; // Local copies to avoid extra method calls + + /** + * Constructs a {@link Base64.OutputStream} in ENCODE mode. + * + * @param out the java.io.OutputStream to which data will be written. + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out ) { + this( out, ENCODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.OutputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DO_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         * 
+ *

+ * Example: new Base64.OutputStream( out, Base64.ENCODE ) + * + * @param out the java.io.OutputStream to which data will be written. + * @param options Specified options. + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DO_BREAK_LINES + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out, int options ) { + super( out ); + this.breakLines = (options & DO_BREAK_LINES) != 0; + this.encode = (options & ENCODE) != 0; + this.bufferLength = encode ? 3 : 4; + this.buffer = new byte[ bufferLength ]; + this.position = 0; + this.lineLength = 0; + this.suspendEncoding = false; + this.b4 = new byte[4]; + this.options = options; + this.decodabet = getDecodabet(options); + } // end constructor + + + /** + * Writes the byte to the output stream after + * converting to/from Base64 notation. + * When encoding, bytes are buffered three + * at a time before the output stream actually + * gets a write() call. + * When decoding, bytes are buffered four + * at a time. + * + * @param theByte the byte to write + * @since 1.3 + */ + @Override + public void write(int theByte) + throws java.io.IOException { + // Encoding suspended? + if( suspendEncoding ) { + this.out.write( theByte ); + return; + } // end if: supsended + + // Encode? + if( encode ) { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) { // Enough to encode. + + this.out.write( encode3to4( b4, buffer, bufferLength, options ) ); + + lineLength += 4; + if( breakLines && lineLength >= MAX_LINE_LENGTH ) { + this.out.write( NEW_LINE ); + lineLength = 0; + } // end if: end of line + + position = 0; + } // end if: enough to output + } // end if: encoding + + // Else, Decoding + else { + // Meaningful Base64 character? + if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) { // Enough to output. + + int len = Base64.decode4to3( buffer, 0, b4, 0, options ); + out.write( b4, 0, len ); + position = 0; + } // end if: enough to output + } // end if: meaningful base64 character + else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) { + throw new java.io.IOException( "Invalid character in Base64 data." ); + } // end else: not white space either + } // end else: decoding + } // end write + + + + /** + * Calls {@link #write(int)} repeatedly until len + * bytes are written. + * + * @param theBytes array from which to read bytes + * @param off offset for array + * @param len max number of bytes to read into array + * @since 1.3 + */ + @Override + public void write( byte[] theBytes, int off, int len ) + throws java.io.IOException { + // Encoding suspended? + if( suspendEncoding ) { + this.out.write( theBytes, off, len ); + return; + } // end if: supsended + + for( int i = 0; i < len; i++ ) { + write( theBytes[ off + i ] ); + } // end for: each byte written + + } // end write + + + + /** + * Method added by PHIL. [Thanks, PHIL. -Rob] + * This pads the buffer without closing the stream. + * @throws java.io.IOException if there's an error. + */ + public void flushBase64() throws java.io.IOException { + if( position > 0 ) { + if( encode ) { + out.write( encode3to4( b4, buffer, position, options ) ); + position = 0; + } // end if: encoding + else { + throw new java.io.IOException( "Base64 input not properly padded." ); + } // end else: decoding + } // end if: buffer partially full + + } // end flush + + + /** + * Flushes and closes (I think, in the superclass) the stream. + * + * @since 1.3 + */ + @Override + public void close() throws java.io.IOException { + // 1. Ensure that pending characters are written + flushBase64(); + + // 2. Actually close the stream + // Base class both flushes and closes. + super.close(); + + buffer = null; + out = null; + } // end close + + + + /** + * Suspends encoding of the stream. + * May be helpful if you need to embed a piece of + * base64-encoded data in a stream. + * + * @throws java.io.IOException if there's an error flushing + * @since 1.5.1 + */ + public void suspendEncoding() throws java.io.IOException { + flushBase64(); + this.suspendEncoding = true; + } // end suspendEncoding + + + /** + * Resumes encoding of the stream. + * May be helpful if you need to embed a piece of + * base64-encoded data in a stream. + * + * @since 1.5.1 + */ + public void resumeEncoding() { + this.suspendEncoding = false; + } // end resumeEncoding + + + + } // end inner class OutputStream + + +} // end class Base64 diff --git a/src/main/java/org/whispersystems/textsecure/internal/util/BlacklistingTrustManager.java b/src/main/java/org/whispersystems/textsecure/internal/util/BlacklistingTrustManager.java new file mode 100644 index 0000000000..42a5ec92c4 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/util/BlacklistingTrustManager.java @@ -0,0 +1,111 @@ +/** + * Copyright (C) 2014 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.internal.util; + +import org.whispersystems.textsecure.api.push.TrustStore; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.LinkedList; +import java.util.List; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +/** + * Trust manager that defers to a system X509 trust manager, and + * additionally rejects certificates if they have a blacklisted + * serial. + * + * @author Moxie Marlinspike + */ +public class BlacklistingTrustManager implements X509TrustManager { + + private static final List BLACKLIST = new LinkedList() {{ + add(new BigInteger("4098")); + }}; + + public static TrustManager[] createFor(TrustManager[] trustManagers) { + for (TrustManager trustManager : trustManagers) { + if (trustManager instanceof X509TrustManager) { + TrustManager[] results = new BlacklistingTrustManager[1]; + results[0] = new BlacklistingTrustManager((X509TrustManager)trustManager); + + return results; + } + } + + throw new AssertionError("No X509 Trust Managers!"); + } + + public static TrustManager[] createFor(TrustStore trustStore) { + try { + InputStream keyStoreInputStream = trustStore.getKeyStoreInputStream(); + KeyStore keyStore = KeyStore.getInstance("BKS"); + + keyStore.load(keyStoreInputStream, trustStore.getKeyStorePassword().toCharArray()); + + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509"); + trustManagerFactory.init(keyStore); + + return BlacklistingTrustManager.createFor(trustManagerFactory.getTrustManagers()); + } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private final X509TrustManager trustManager; + + public BlacklistingTrustManager(X509TrustManager trustManager) { + this.trustManager = trustManager; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + trustManager.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + trustManager.checkServerTrusted(chain, authType); + + for (X509Certificate certificate : chain) { + for (BigInteger blacklistedSerial : BLACKLIST) { + if (certificate.getSerialNumber().equals(blacklistedSerial)) { + throw new CertificateException("Blacklisted Serial: " + certificate.getSerialNumber()); + } + } + } + + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return trustManager.getAcceptedIssuers(); + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/util/Hex.java b/src/main/java/org/whispersystems/textsecure/internal/util/Hex.java new file mode 100644 index 0000000000..a855031e13 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/util/Hex.java @@ -0,0 +1,138 @@ +/** + * Copyright (C) 2011 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.internal.util; + +import java.io.IOException; + +/** + * Utility for generating hex dumps. + */ +public class Hex { + + private final static int HEX_DIGITS_START = 10; + private final static int ASCII_TEXT_START = HEX_DIGITS_START + (16*2 + (16/2)); + + final static String EOL = System.getProperty("line.separator"); + + private final static char[] HEX_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + public static String toString(byte[] bytes) { + return toString(bytes, 0, bytes.length); + } + + public static String toString(byte[] bytes, int offset, int length) { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < length; i++) { + appendHexChar(buf, bytes[offset + i]); + buf.append(' '); + } + return buf.toString(); + } + + public static String toStringCondensed(byte[] bytes) { + StringBuffer buf = new StringBuffer(); + for (int i=0;i> 1]; + + // two characters form the hex value. + for (int i = 0, j = 0; j < len; i++) { + int f = Character.digit(data[j], 16) << 4; + j++; + f = f | Character.digit(data[j], 16); + j++; + out[i] = (byte) (f & 0xFF); + } + + return out; + } + + public static String dump(byte[] bytes) { + return dump(bytes, 0, bytes.length); + } + + public static String dump(byte[] bytes, int offset, int length) { + StringBuffer buf = new StringBuffer(); + int lines = ((length - 1) / 16) + 1; + int lineOffset; + int lineLength; + + for (int i = 0; i < lines; i++) { + lineOffset = (i * 16) + offset; + lineLength = Math.min(16, (length - (i * 16))); + appendDumpLine(buf, i, bytes, lineOffset, lineLength); + buf.append(EOL); + } + + return buf.toString(); + } + + private static void appendDumpLine(StringBuffer buf, int line, byte[] bytes, int lineOffset, int lineLength) { + buf.append(HEX_DIGITS[(line >> 28) & 0xf]); + buf.append(HEX_DIGITS[(line >> 24) & 0xf]); + buf.append(HEX_DIGITS[(line >> 20) & 0xf]); + buf.append(HEX_DIGITS[(line >> 16) & 0xf]); + buf.append(HEX_DIGITS[(line >> 12) & 0xf]); + buf.append(HEX_DIGITS[(line >> 8) & 0xf]); + buf.append(HEX_DIGITS[(line >> 4) & 0xf]); + buf.append(HEX_DIGITS[(line ) & 0xf]); + buf.append(": "); + + for (int i = 0; i < 16; i++) { + int idx = i + lineOffset; + if (i < lineLength) { + int b = bytes[idx]; + appendHexChar(buf, b); + } else { + buf.append(" "); + } + if ((i % 2) == 1) { + buf.append(' '); + } + } + + for (int i = 0; i < 16 && i < lineLength; i++) { + int idx = i + lineOffset; + int b = bytes[idx]; + if (b >= 0x20 && b <= 0x7e) { + buf.append((char)b); + } else { + buf.append('.'); + } + } + } + + private static void appendHexChar(StringBuffer buf, int b) { + buf.append(HEX_DIGITS[(b >> 4) & 0xf]); + buf.append(HEX_DIGITS[b & 0xf]); + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/util/JsonUtil.java b/src/main/java/org/whispersystems/textsecure/internal/util/JsonUtil.java new file mode 100644 index 0000000000..65e066f66b --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/util/JsonUtil.java @@ -0,0 +1,75 @@ +package org.whispersystems.textsecure.internal.util; + +import android.util.Log; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; + +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.InvalidKeyException; + +import java.io.IOException; + +public class JsonUtil { + + private static final String TAG = JsonUtil.class.getSimpleName(); + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + static { + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + public static String toJson(Object object) { + try { + return objectMapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + Log.w(TAG, e); + return ""; + } + } + + public static T fromJson(String json, Class clazz) { + try { + return objectMapper.readValue(json, clazz); + } catch (IOException e) { + Log.w(TAG, e); + throw new JsonParseException(e); + } + } + + public static class JsonParseException extends RuntimeException { + public JsonParseException(Exception e) { + super(e); + } + } + + public static class IdentityKeySerializer extends JsonSerializer { + @Override + public void serialize(IdentityKey value, JsonGenerator gen, SerializerProvider serializers) + throws IOException + { + gen.writeString(Base64.encodeBytesWithoutPadding(value.serialize())); + } + } + + public static class IdentityKeyDeserializer extends JsonDeserializer { + @Override + public IdentityKey deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + try { + return new IdentityKey(Base64.decodeWithoutPadding(p.getValueAsString()), 0); + } catch (InvalidKeyException e) { + throw new IOException(e); + } + } + } + + +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/util/StaticCredentialsProvider.java b/src/main/java/org/whispersystems/textsecure/internal/util/StaticCredentialsProvider.java new file mode 100644 index 0000000000..281086d2b5 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/util/StaticCredentialsProvider.java @@ -0,0 +1,31 @@ +package org.whispersystems.textsecure.internal.util; + +import org.whispersystems.textsecure.api.util.CredentialsProvider; + +public class StaticCredentialsProvider implements CredentialsProvider { + + private final String user; + private final String password; + private final String signalingKey; + + public StaticCredentialsProvider(String user, String password, String signalingKey) { + this.user = user; + this.password = password; + this.signalingKey = signalingKey; + } + + @Override + public String getUser() { + return user; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getSignalingKey() { + return signalingKey; + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/util/Util.java b/src/main/java/org/whispersystems/textsecure/internal/util/Util.java new file mode 100644 index 0000000000..1147f1da7a --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/util/Util.java @@ -0,0 +1,128 @@ +/** + * Copyright (C) 2014 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.internal.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public class Util { + + public static byte[] join(byte[]... input) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (byte[] part : input) { + baos.write(part); + } + + return baos.toByteArray(); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + public static byte[][] split(byte[] input, int firstLength, int secondLength) { + byte[][] parts = new byte[2][]; + + 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[] trim(byte[] input, int length) { + byte[] result = new byte[length]; + System.arraycopy(input, 0, result, 0, result.length); + + return result; + } + + public static boolean isEmpty(String value) { + return value == null || value.trim().length() == 0; + } + + public static byte[] getSecretBytes(int size) { + try { + byte[] secret = new byte[size]; + SecureRandom.getInstance("SHA1PRNG").nextBytes(secret); + return secret; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + public static String readFully(InputStream in) throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int read; + + while ((read = in.read(buffer)) != -1) { + bout.write(buffer, 0, read); + } + + in.close(); + + 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; + + while ((read = in.read(buffer)) != -1) { + out.write(buffer, 0, read); + } + + in.close(); + out.close(); + } + + public static void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + } + + public static void wait(Object lock, long millis) { + try { + lock.wait(millis); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/websocket/OkHttpClientWrapper.java b/src/main/java/org/whispersystems/textsecure/internal/websocket/OkHttpClientWrapper.java new file mode 100644 index 0000000000..419ad407af --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/websocket/OkHttpClientWrapper.java @@ -0,0 +1,142 @@ +package org.whispersystems.textsecure.internal.websocket; + +import android.util.Log; + +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; +import com.squareup.okhttp.internal.ws.WebSocket; +import com.squareup.okhttp.internal.ws.WebSocketListener; + +import org.whispersystems.textsecure.api.push.TrustStore; +import org.whispersystems.textsecure.api.util.CredentialsProvider; +import org.whispersystems.textsecure.internal.util.BlacklistingTrustManager; +import org.whispersystems.textsecure.internal.util.Util; + +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; + +import okio.Buffer; +import okio.BufferedSource; + +public class OkHttpClientWrapper implements WebSocketListener { + + private static final String TAG = OkHttpClientWrapper.class.getSimpleName(); + + private final String uri; + private final TrustStore trustStore; + private final CredentialsProvider credentialsProvider; + private final WebSocketEventListener listener; + + private WebSocket webSocket; + private boolean closed; + private boolean connected; + + public OkHttpClientWrapper(String uri, TrustStore trustStore, + CredentialsProvider credentialsProvider, + WebSocketEventListener listener) + { + Log.w(TAG, "Connecting to: " + uri); + + this.uri = uri; + this.trustStore = trustStore; + this.credentialsProvider = credentialsProvider; + this.listener = listener; + } + + public void connect() { + new Thread() { + @Override + public void run() { + int attempt = 0; + + while ((webSocket = newSocket()) != null) { + try { + Response response = webSocket.connect(OkHttpClientWrapper.this); + + if (response.code() == 101) { + synchronized (OkHttpClientWrapper.this) { + if (closed) webSocket.close(1000, "OK"); + else connected = true; + } + + listener.onConnected(); + return; + } + + Log.w(TAG, "WebSocket Response: " + response.code()); + } catch (IOException e) { + Log.w(TAG, e); + } + + Util.sleep(Math.min(++attempt * 200, TimeUnit.SECONDS.toMillis(15))); + } + } + }.start(); + } + + public synchronized void disconnect() { + Log.w(TAG, "Calling disconnect()..."); + try { + closed = true; + if (webSocket != null && connected) { + webSocket.close(1000, "OK"); + } + } catch (IOException e) { + Log.w(TAG, e); + } + } + + public void sendMessage(byte[] message) throws IOException { + webSocket.sendMessage(WebSocket.PayloadType.BINARY, new Buffer().write(message)); + } + + @Override + public void onMessage(BufferedSource payload, WebSocket.PayloadType type) throws IOException { + Log.w(TAG, "onMessage: " + type); + if (type.equals(WebSocket.PayloadType.BINARY)) { + listener.onMessage(payload.readByteArray()); + } + + payload.close(); + } + + @Override + public void onClose(int code, String reason) { + Log.w(TAG, String.format("onClose(%d, %s)", code, reason)); + listener.onClose(); + } + + @Override + public void onFailure(IOException e) { + Log.w(TAG, e); + listener.onClose(); + } + + private synchronized WebSocket newSocket() { + if (closed) return null; + + String filledUri = String.format(uri, credentialsProvider.getUser(), credentialsProvider.getPassword()); + SSLSocketFactory socketFactory = createTlsSocketFactory(trustStore); + + return WebSocket.newWebSocket(new OkHttpClient().setSslSocketFactory(socketFactory), + new Request.Builder().url(filledUri).build()); + } + + private SSLSocketFactory createTlsSocketFactory(TrustStore trustStore) { + try { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, BlacklistingTrustManager.createFor(trustStore), null); + + return context.getSocketFactory(); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new AssertionError(e); + } + } + +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/websocket/WebSocketConnection.java b/src/main/java/org/whispersystems/textsecure/internal/websocket/WebSocketConnection.java new file mode 100644 index 0000000000..25b18695e4 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/websocket/WebSocketConnection.java @@ -0,0 +1,174 @@ +package org.whispersystems.textsecure.internal.websocket; + +import android.util.Log; + +import com.google.protobuf.InvalidProtocolBufferException; + +import org.whispersystems.textsecure.api.push.TrustStore; +import org.whispersystems.textsecure.api.util.CredentialsProvider; +import org.whispersystems.textsecure.internal.util.Util; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage; +import static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage; +import static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage; + +public class WebSocketConnection implements WebSocketEventListener { + + private static final String TAG = WebSocketConnection.class.getSimpleName(); + + private final LinkedList incomingRequests = new LinkedList<>(); + + private final String wsUri; + private final TrustStore trustStore; + private final CredentialsProvider credentialsProvider; + + private OkHttpClientWrapper client; + private KeepAliveSender keepAliveSender; + + public WebSocketConnection(String httpUri, TrustStore trustStore, CredentialsProvider credentialsProvider) { + this.trustStore = trustStore; + this.credentialsProvider = credentialsProvider; + this.wsUri = httpUri.replace("https://", "wss://") + .replace("http://", "ws://") + "/v1/websocket/?login=%s&password=%s"; + } + + public synchronized void connect() { + Log.w(TAG, "WSC connect()..."); + + if (client == null) { + client = new OkHttpClientWrapper(wsUri, trustStore, credentialsProvider, this); + client.connect(); + } + } + + public synchronized void disconnect() { + Log.w(TAG, "WSC disconnect()..."); + + if (client != null) { + client.disconnect(); + client = null; + } + + if (keepAliveSender != null) { + keepAliveSender.shutdown(); + keepAliveSender = null; + } + } + + public synchronized WebSocketRequestMessage readRequest(long timeoutMillis) + throws TimeoutException, IOException + { + if (client == null) { + throw new IOException("Connection closed!"); + } + + long startTime = System.currentTimeMillis(); + + while (client != null && incomingRequests.isEmpty() && elapsedTime(startTime) < timeoutMillis) { + Util.wait(this, Math.max(1, timeoutMillis - elapsedTime(startTime))); + } + + if (incomingRequests.isEmpty() && client == null) throw new IOException("Connection closed!"); + else if (incomingRequests.isEmpty()) throw new TimeoutException("Timeout exceeded"); + else return incomingRequests.removeFirst(); + } + + public synchronized void sendResponse(WebSocketResponseMessage response) throws IOException { + if (client == null) { + throw new IOException("Connection closed!"); + } + + WebSocketMessage message = WebSocketMessage.newBuilder() + .setType(WebSocketMessage.Type.RESPONSE) + .setResponse(response) + .build(); + + client.sendMessage(message.toByteArray()); + } + + private synchronized void sendKeepAlive() throws IOException { + if (keepAliveSender != null) { + client.sendMessage(WebSocketMessage.newBuilder() + .setType(WebSocketMessage.Type.REQUEST) + .setRequest(WebSocketRequestMessage.newBuilder() + .setId(System.currentTimeMillis()) + .setPath("/v1/keepalive") + .setVerb("GET") + .build()).build() + .toByteArray()); + } + } + + public synchronized void onMessage(byte[] payload) { + Log.w(TAG, "WSC onMessage()"); + try { + WebSocketMessage message = WebSocketMessage.parseFrom(payload); + + Log.w(TAG, "Message Type: " + message.getType().getNumber()); + + if (message.getType().getNumber() == WebSocketMessage.Type.REQUEST_VALUE) { + incomingRequests.add(message.getRequest()); + } + + notifyAll(); + } catch (InvalidProtocolBufferException e) { + Log.w(TAG, e); + } + } + + public synchronized void onClose() { + Log.w(TAG, "onClose()..."); + + if (client != null) { + client.disconnect(); + client = null; + connect(); + } + + if (keepAliveSender != null) { + keepAliveSender.shutdown(); + keepAliveSender = null; + } + + notifyAll(); + } + + public synchronized void onConnected() { + if (client != null && keepAliveSender == null) { + keepAliveSender = new KeepAliveSender(); + keepAliveSender.start(); + } + } + + private long elapsedTime(long startTime) { + return System.currentTimeMillis() - startTime; + } + + private class KeepAliveSender extends Thread { + + private AtomicBoolean stop = new AtomicBoolean(false); + + public void run() { + while (!stop.get()) { + try { + Thread.sleep(TimeUnit.SECONDS.toMillis(15)); + + Log.w(TAG, "Sending keep alive..."); + sendKeepAlive(); + } catch (Throwable e) { + Log.w(TAG, e); + } + } + } + + public void shutdown() { + stop.set(true); + } + } +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/websocket/WebSocketEventListener.java b/src/main/java/org/whispersystems/textsecure/internal/websocket/WebSocketEventListener.java new file mode 100644 index 0000000000..0e87b9417e --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/websocket/WebSocketEventListener.java @@ -0,0 +1,9 @@ +package org.whispersystems.textsecure.internal.websocket; + +public interface WebSocketEventListener { + + public void onMessage(byte[] payload); + public void onClose(); + public void onConnected(); + +} diff --git a/src/main/java/org/whispersystems/textsecure/internal/websocket/WebSocketProtos.java b/src/main/java/org/whispersystems/textsecure/internal/websocket/WebSocketProtos.java new file mode 100644 index 0000000000..8e0a0f28f1 --- /dev/null +++ b/src/main/java/org/whispersystems/textsecure/internal/websocket/WebSocketProtos.java @@ -0,0 +1,2471 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: WebSocketResources.proto + +package org.whispersystems.textsecure.internal.websocket; + +public final class WebSocketProtos { + private WebSocketProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface WebSocketRequestMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string verb = 1; + /** + * optional string verb = 1; + */ + boolean hasVerb(); + /** + * optional string verb = 1; + */ + java.lang.String getVerb(); + /** + * optional string verb = 1; + */ + com.google.protobuf.ByteString + getVerbBytes(); + + // optional string path = 2; + /** + * optional string path = 2; + */ + boolean hasPath(); + /** + * optional string path = 2; + */ + java.lang.String getPath(); + /** + * optional string path = 2; + */ + com.google.protobuf.ByteString + getPathBytes(); + + // optional bytes body = 3; + /** + * optional bytes body = 3; + */ + boolean hasBody(); + /** + * optional bytes body = 3; + */ + com.google.protobuf.ByteString getBody(); + + // optional uint64 id = 4; + /** + * optional uint64 id = 4; + */ + boolean hasId(); + /** + * optional uint64 id = 4; + */ + long getId(); + } + /** + * Protobuf type {@code textsecure.WebSocketRequestMessage} + */ + public static final class WebSocketRequestMessage extends + com.google.protobuf.GeneratedMessage + implements WebSocketRequestMessageOrBuilder { + // Use WebSocketRequestMessage.newBuilder() to construct. + private WebSocketRequestMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private WebSocketRequestMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final WebSocketRequestMessage defaultInstance; + public static WebSocketRequestMessage getDefaultInstance() { + return defaultInstance; + } + + public WebSocketRequestMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private WebSocketRequestMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + verb_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + path_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + body_ = input.readBytes(); + break; + } + case 32: { + bitField0_ |= 0x00000008; + id_ = input.readUInt64(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketRequestMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketRequestMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.class, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public WebSocketRequestMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new WebSocketRequestMessage(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string verb = 1; + public static final int VERB_FIELD_NUMBER = 1; + private java.lang.Object verb_; + /** + * optional string verb = 1; + */ + public boolean hasVerb() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string verb = 1; + */ + public java.lang.String getVerb() { + java.lang.Object ref = verb_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + verb_ = s; + } + return s; + } + } + /** + * optional string verb = 1; + */ + public com.google.protobuf.ByteString + getVerbBytes() { + java.lang.Object ref = verb_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + verb_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional string path = 2; + public static final int PATH_FIELD_NUMBER = 2; + private java.lang.Object path_; + /** + * optional string path = 2; + */ + public boolean hasPath() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string path = 2; + */ + public java.lang.String getPath() { + java.lang.Object ref = path_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + path_ = s; + } + return s; + } + } + /** + * optional string path = 2; + */ + public com.google.protobuf.ByteString + getPathBytes() { + java.lang.Object ref = path_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + path_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional bytes body = 3; + public static final int BODY_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString body_; + /** + * optional bytes body = 3; + */ + public boolean hasBody() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes body = 3; + */ + public com.google.protobuf.ByteString getBody() { + return body_; + } + + // optional uint64 id = 4; + public static final int ID_FIELD_NUMBER = 4; + private long id_; + /** + * optional uint64 id = 4; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional uint64 id = 4; + */ + public long getId() { + return id_; + } + + private void initFields() { + verb_ = ""; + path_ = ""; + body_ = com.google.protobuf.ByteString.EMPTY; + id_ = 0L; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getVerbBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getPathBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, body_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeUInt64(4, id_); + } + 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, getVerbBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getPathBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, body_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(4, id_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.WebSocketRequestMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketRequestMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketRequestMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.class, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder.class); + } + + // Construct using org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + verb_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + path_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + body_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + id_ = 0L; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketRequestMessage_descriptor; + } + + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage getDefaultInstanceForType() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); + } + + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage build() { + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage buildPartial() { + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage result = new org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.verb_ = verb_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.path_ = path_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.body_ = body_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.id_ = id_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage) { + return mergeFrom((org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage other) { + if (other == org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance()) return this; + if (other.hasVerb()) { + bitField0_ |= 0x00000001; + verb_ = other.verb_; + onChanged(); + } + if (other.hasPath()) { + bitField0_ |= 0x00000002; + path_ = other.path_; + onChanged(); + } + if (other.hasBody()) { + setBody(other.getBody()); + } + if (other.hasId()) { + setId(other.getId()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string verb = 1; + private java.lang.Object verb_ = ""; + /** + * optional string verb = 1; + */ + public boolean hasVerb() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string verb = 1; + */ + public java.lang.String getVerb() { + java.lang.Object ref = verb_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + verb_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string verb = 1; + */ + public com.google.protobuf.ByteString + getVerbBytes() { + java.lang.Object ref = verb_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + verb_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string verb = 1; + */ + public Builder setVerb( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + verb_ = value; + onChanged(); + return this; + } + /** + * optional string verb = 1; + */ + public Builder clearVerb() { + bitField0_ = (bitField0_ & ~0x00000001); + verb_ = getDefaultInstance().getVerb(); + onChanged(); + return this; + } + /** + * optional string verb = 1; + */ + public Builder setVerbBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + verb_ = value; + onChanged(); + return this; + } + + // optional string path = 2; + private java.lang.Object path_ = ""; + /** + * optional string path = 2; + */ + public boolean hasPath() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string path = 2; + */ + public java.lang.String getPath() { + java.lang.Object ref = path_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + path_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string path = 2; + */ + public com.google.protobuf.ByteString + getPathBytes() { + java.lang.Object ref = path_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + path_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string path = 2; + */ + public Builder setPath( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + path_ = value; + onChanged(); + return this; + } + /** + * optional string path = 2; + */ + public Builder clearPath() { + bitField0_ = (bitField0_ & ~0x00000002); + path_ = getDefaultInstance().getPath(); + onChanged(); + return this; + } + /** + * optional string path = 2; + */ + public Builder setPathBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + path_ = value; + onChanged(); + return this; + } + + // optional bytes body = 3; + private com.google.protobuf.ByteString body_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes body = 3; + */ + public boolean hasBody() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes body = 3; + */ + public com.google.protobuf.ByteString getBody() { + return body_; + } + /** + * optional bytes body = 3; + */ + public Builder setBody(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + body_ = value; + onChanged(); + return this; + } + /** + * optional bytes body = 3; + */ + public Builder clearBody() { + bitField0_ = (bitField0_ & ~0x00000004); + body_ = getDefaultInstance().getBody(); + onChanged(); + return this; + } + + // optional uint64 id = 4; + private long id_ ; + /** + * optional uint64 id = 4; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional uint64 id = 4; + */ + public long getId() { + return id_; + } + /** + * optional uint64 id = 4; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000008; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint64 id = 4; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000008); + id_ = 0L; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.WebSocketRequestMessage) + } + + static { + defaultInstance = new WebSocketRequestMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.WebSocketRequestMessage) + } + + public interface WebSocketResponseMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint64 id = 1; + /** + * optional uint64 id = 1; + */ + boolean hasId(); + /** + * optional uint64 id = 1; + */ + long getId(); + + // optional uint32 status = 2; + /** + * optional uint32 status = 2; + */ + boolean hasStatus(); + /** + * optional uint32 status = 2; + */ + int getStatus(); + + // optional string message = 3; + /** + * optional string message = 3; + */ + boolean hasMessage(); + /** + * optional string message = 3; + */ + java.lang.String getMessage(); + /** + * optional string message = 3; + */ + com.google.protobuf.ByteString + getMessageBytes(); + + // optional bytes body = 4; + /** + * optional bytes body = 4; + */ + boolean hasBody(); + /** + * optional bytes body = 4; + */ + com.google.protobuf.ByteString getBody(); + } + /** + * Protobuf type {@code textsecure.WebSocketResponseMessage} + */ + public static final class WebSocketResponseMessage extends + com.google.protobuf.GeneratedMessage + implements WebSocketResponseMessageOrBuilder { + // Use WebSocketResponseMessage.newBuilder() to construct. + private WebSocketResponseMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private WebSocketResponseMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final WebSocketResponseMessage defaultInstance; + public static WebSocketResponseMessage getDefaultInstance() { + return defaultInstance; + } + + public WebSocketResponseMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private WebSocketResponseMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt64(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + status_ = input.readUInt32(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + message_ = input.readBytes(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + body_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketResponseMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketResponseMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.class, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public WebSocketResponseMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new WebSocketResponseMessage(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint64 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private long id_; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + + // optional uint32 status = 2; + public static final int STATUS_FIELD_NUMBER = 2; + private int status_; + /** + * optional uint32 status = 2; + */ + public boolean hasStatus() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 status = 2; + */ + public int getStatus() { + return status_; + } + + // optional string message = 3; + public static final int MESSAGE_FIELD_NUMBER = 3; + private java.lang.Object message_; + /** + * optional string message = 3; + */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string message = 3; + */ + public java.lang.String getMessage() { + java.lang.Object ref = message_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + message_ = s; + } + return s; + } + } + /** + * optional string message = 3; + */ + public com.google.protobuf.ByteString + getMessageBytes() { + java.lang.Object ref = message_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + message_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional bytes body = 4; + public static final int BODY_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString body_; + /** + * optional bytes body = 4; + */ + public boolean hasBody() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes body = 4; + */ + public com.google.protobuf.ByteString getBody() { + return body_; + } + + private void initFields() { + id_ = 0L; + status_ = 0; + message_ = ""; + body_ = 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.writeUInt64(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, status_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, getMessageBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, body_); + } + 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 + .computeUInt64Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, status_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getMessageBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, body_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.WebSocketResponseMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketResponseMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketResponseMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.class, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder.class); + } + + // Construct using org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + status_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + message_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + body_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketResponseMessage_descriptor; + } + + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage getDefaultInstanceForType() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); + } + + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage build() { + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage buildPartial() { + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage result = new org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage(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.status_ = status_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.message_ = message_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.body_ = body_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage) { + return mergeFrom((org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage other) { + if (other == org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasStatus()) { + setStatus(other.getStatus()); + } + if (other.hasMessage()) { + bitField0_ |= 0x00000004; + message_ = other.message_; + onChanged(); + } + if (other.hasBody()) { + setBody(other.getBody()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint64 id = 1; + private long id_ ; + /** + * optional uint64 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1; + */ + public long getId() { + return id_; + } + /** + * optional uint64 id = 1; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint64 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0L; + onChanged(); + return this; + } + + // optional uint32 status = 2; + private int status_ ; + /** + * optional uint32 status = 2; + */ + public boolean hasStatus() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 status = 2; + */ + public int getStatus() { + return status_; + } + /** + * optional uint32 status = 2; + */ + public Builder setStatus(int value) { + bitField0_ |= 0x00000002; + status_ = value; + onChanged(); + return this; + } + /** + * optional uint32 status = 2; + */ + public Builder clearStatus() { + bitField0_ = (bitField0_ & ~0x00000002); + status_ = 0; + onChanged(); + return this; + } + + // optional string message = 3; + private java.lang.Object message_ = ""; + /** + * optional string message = 3; + */ + public boolean hasMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string message = 3; + */ + public java.lang.String getMessage() { + java.lang.Object ref = message_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + message_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string message = 3; + */ + public com.google.protobuf.ByteString + getMessageBytes() { + java.lang.Object ref = message_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + message_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string message = 3; + */ + public Builder setMessage( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + message_ = value; + onChanged(); + return this; + } + /** + * optional string message = 3; + */ + public Builder clearMessage() { + bitField0_ = (bitField0_ & ~0x00000004); + message_ = getDefaultInstance().getMessage(); + onChanged(); + return this; + } + /** + * optional string message = 3; + */ + public Builder setMessageBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + message_ = value; + onChanged(); + return this; + } + + // optional bytes body = 4; + private com.google.protobuf.ByteString body_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes body = 4; + */ + public boolean hasBody() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes body = 4; + */ + public com.google.protobuf.ByteString getBody() { + return body_; + } + /** + * optional bytes body = 4; + */ + public Builder setBody(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + body_ = value; + onChanged(); + return this; + } + /** + * optional bytes body = 4; + */ + public Builder clearBody() { + bitField0_ = (bitField0_ & ~0x00000008); + body_ = getDefaultInstance().getBody(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.WebSocketResponseMessage) + } + + static { + defaultInstance = new WebSocketResponseMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.WebSocketResponseMessage) + } + + public interface WebSocketMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .textsecure.WebSocketMessage.Type type = 1; + /** + * optional .textsecure.WebSocketMessage.Type type = 1; + */ + boolean hasType(); + /** + * optional .textsecure.WebSocketMessage.Type type = 1; + */ + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Type getType(); + + // optional .textsecure.WebSocketRequestMessage request = 2; + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + boolean hasRequest(); + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage getRequest(); + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder getRequestOrBuilder(); + + // optional .textsecure.WebSocketResponseMessage response = 3; + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + boolean hasResponse(); + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage getResponse(); + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder getResponseOrBuilder(); + } + /** + * Protobuf type {@code textsecure.WebSocketMessage} + */ + public static final class WebSocketMessage extends + com.google.protobuf.GeneratedMessage + implements WebSocketMessageOrBuilder { + // Use WebSocketMessage.newBuilder() to construct. + private WebSocketMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private WebSocketMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final WebSocketMessage defaultInstance; + public static WebSocketMessage getDefaultInstance() { + return defaultInstance; + } + + public WebSocketMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private WebSocketMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + int rawValue = input.readEnum(); + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Type value = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = value; + } + break; + } + case 18: { + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = request_.toBuilder(); + } + request_ = input.readMessage(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(request_); + request_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 26: { + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder subBuilder = null; + if (((bitField0_ & 0x00000004) == 0x00000004)) { + subBuilder = response_.toBuilder(); + } + response_ = input.readMessage(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(response_); + response_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000004; + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.class, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public WebSocketMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new WebSocketMessage(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code textsecure.WebSocketMessage.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * UNKNOWN = 0; + */ + UNKNOWN(0, 0), + /** + * REQUEST = 1; + */ + REQUEST(1, 1), + /** + * RESPONSE = 2; + */ + RESPONSE(2, 2), + ; + + /** + * UNKNOWN = 0; + */ + public static final int UNKNOWN_VALUE = 0; + /** + * REQUEST = 1; + */ + public static final int REQUEST_VALUE = 1; + /** + * RESPONSE = 2; + */ + public static final int RESPONSE_VALUE = 2; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 0: return UNKNOWN; + case 1: return REQUEST; + case 2: return RESPONSE; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:textsecure.WebSocketMessage.Type) + } + + private int bitField0_; + // optional .textsecure.WebSocketMessage.Type type = 1; + public static final int TYPE_FIELD_NUMBER = 1; + private org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Type type_; + /** + * optional .textsecure.WebSocketMessage.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .textsecure.WebSocketMessage.Type type = 1; + */ + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Type getType() { + return type_; + } + + // optional .textsecure.WebSocketRequestMessage request = 2; + public static final int REQUEST_FIELD_NUMBER = 2; + private org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage request_; + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + public boolean hasRequest() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage getRequest() { + return request_; + } + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder getRequestOrBuilder() { + return request_; + } + + // optional .textsecure.WebSocketResponseMessage response = 3; + public static final int RESPONSE_FIELD_NUMBER = 3; + private org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage response_; + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + public boolean hasResponse() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage getResponse() { + return response_; + } + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder getResponseOrBuilder() { + return response_; + } + + private void initFields() { + type_ = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Type.UNKNOWN; + request_ = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); + response_ = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); + } + 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.writeEnum(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(2, request_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeMessage(3, response_); + } + 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 + .computeEnumSize(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, request_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, response_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code textsecure.WebSocketMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.class, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Builder.class); + } + + // Construct using org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getRequestFieldBuilder(); + getResponseFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + type_ = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Type.UNKNOWN; + bitField0_ = (bitField0_ & ~0x00000001); + if (requestBuilder_ == null) { + request_ = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); + } else { + requestBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + if (responseBuilder_ == null) { + response_ = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); + } else { + responseBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.internal_static_textsecure_WebSocketMessage_descriptor; + } + + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage getDefaultInstanceForType() { + return org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.getDefaultInstance(); + } + + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage build() { + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage buildPartial() { + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage result = new org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + if (requestBuilder_ == null) { + result.request_ = request_; + } else { + result.request_ = requestBuilder_.build(); + } + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + if (responseBuilder_ == null) { + result.response_ = response_; + } else { + result.response_ = responseBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage) { + return mergeFrom((org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage other) { + if (other == org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasRequest()) { + mergeRequest(other.getRequest()); + } + if (other.hasResponse()) { + mergeResponse(other.getResponse()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .textsecure.WebSocketMessage.Type type = 1; + private org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Type type_ = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Type.UNKNOWN; + /** + * optional .textsecure.WebSocketMessage.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .textsecure.WebSocketMessage.Type type = 1; + */ + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Type getType() { + return type_; + } + /** + * optional .textsecure.WebSocketMessage.Type type = 1; + */ + public Builder setType(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value; + onChanged(); + return this; + } + /** + * optional .textsecure.WebSocketMessage.Type type = 1; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketMessage.Type.UNKNOWN; + onChanged(); + return this; + } + + // optional .textsecure.WebSocketRequestMessage request = 2; + private org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage request_ = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder> requestBuilder_; + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + public boolean hasRequest() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage getRequest() { + if (requestBuilder_ == null) { + return request_; + } else { + return requestBuilder_.getMessage(); + } + } + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + public Builder setRequest(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage value) { + if (requestBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + request_ = value; + onChanged(); + } else { + requestBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + public Builder setRequest( + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder builderForValue) { + if (requestBuilder_ == null) { + request_ = builderForValue.build(); + onChanged(); + } else { + requestBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + public Builder mergeRequest(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage value) { + if (requestBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002) && + request_ != org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance()) { + request_ = + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.newBuilder(request_).mergeFrom(value).buildPartial(); + } else { + request_ = value; + } + onChanged(); + } else { + requestBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + public Builder clearRequest() { + if (requestBuilder_ == null) { + request_ = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.getDefaultInstance(); + onChanged(); + } else { + requestBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder getRequestBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return getRequestFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder getRequestOrBuilder() { + if (requestBuilder_ != null) { + return requestBuilder_.getMessageOrBuilder(); + } else { + return request_; + } + } + /** + * optional .textsecure.WebSocketRequestMessage request = 2; + */ + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder> + getRequestFieldBuilder() { + if (requestBuilder_ == null) { + requestBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessage.Builder, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketRequestMessageOrBuilder>( + request_, + getParentForChildren(), + isClean()); + request_ = null; + } + return requestBuilder_; + } + + // optional .textsecure.WebSocketResponseMessage response = 3; + private org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage response_ = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder> responseBuilder_; + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + public boolean hasResponse() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage getResponse() { + if (responseBuilder_ == null) { + return response_; + } else { + return responseBuilder_.getMessage(); + } + } + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + public Builder setResponse(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage value) { + if (responseBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + response_ = value; + onChanged(); + } else { + responseBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + public Builder setResponse( + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder builderForValue) { + if (responseBuilder_ == null) { + response_ = builderForValue.build(); + onChanged(); + } else { + responseBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + public Builder mergeResponse(org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage value) { + if (responseBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004) && + response_ != org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance()) { + response_ = + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.newBuilder(response_).mergeFrom(value).buildPartial(); + } else { + response_ = value; + } + onChanged(); + } else { + responseBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000004; + return this; + } + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + public Builder clearResponse() { + if (responseBuilder_ == null) { + response_ = org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.getDefaultInstance(); + onChanged(); + } else { + responseBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder getResponseBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return getResponseFieldBuilder().getBuilder(); + } + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + public org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder getResponseOrBuilder() { + if (responseBuilder_ != null) { + return responseBuilder_.getMessageOrBuilder(); + } else { + return response_; + } + } + /** + * optional .textsecure.WebSocketResponseMessage response = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder> + getResponseFieldBuilder() { + if (responseBuilder_ == null) { + responseBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessage.Builder, org.whispersystems.textsecure.internal.websocket.WebSocketProtos.WebSocketResponseMessageOrBuilder>( + response_, + getParentForChildren(), + isClean()); + response_ = null; + } + return responseBuilder_; + } + + // @@protoc_insertion_point(builder_scope:textsecure.WebSocketMessage) + } + + static { + defaultInstance = new WebSocketMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.WebSocketMessage) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_WebSocketRequestMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_WebSocketRequestMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_WebSocketResponseMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_WebSocketResponseMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_WebSocketMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_WebSocketMessage_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\030WebSocketResources.proto\022\ntextsecure\"O" + + "\n\027WebSocketRequestMessage\022\014\n\004verb\030\001 \001(\t\022" + + "\014\n\004path\030\002 \001(\t\022\014\n\004body\030\003 \001(\014\022\n\n\002id\030\004 \001(\004\"" + + "U\n\030WebSocketResponseMessage\022\n\n\002id\030\001 \001(\004\022" + + "\016\n\006status\030\002 \001(\r\022\017\n\007message\030\003 \001(\t\022\014\n\004body" + + "\030\004 \001(\014\"\341\001\n\020WebSocketMessage\022/\n\004type\030\001 \001(" + + "\0162!.textsecure.WebSocketMessage.Type\0224\n\007" + + "request\030\002 \001(\0132#.textsecure.WebSocketRequ" + + "estMessage\0226\n\010response\030\003 \001(\0132$.textsecur" + + "e.WebSocketResponseMessage\".\n\004Type\022\013\n\007UN", + "KNOWN\020\000\022\013\n\007REQUEST\020\001\022\014\n\010RESPONSE\020\002BC\n0or" + + "g.whispersystems.textsecure.internal.web" + + "socketB\017WebSocketProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_textsecure_WebSocketRequestMessage_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_textsecure_WebSocketRequestMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_WebSocketRequestMessage_descriptor, + new java.lang.String[] { "Verb", "Path", "Body", "Id", }); + internal_static_textsecure_WebSocketResponseMessage_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_textsecure_WebSocketResponseMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_WebSocketResponseMessage_descriptor, + new java.lang.String[] { "Id", "Status", "Message", "Body", }); + internal_static_textsecure_WebSocketMessage_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_textsecure_WebSocketMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_WebSocketMessage_descriptor, + new java.lang.String[] { "Type", "Request", "Response", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +}