Enable TextSecure universal transport.

This commit is contained in:
Moxie Marlinspike 2013-07-12 17:40:41 -07:00
parent d7070e7ecf
commit 53803630d4
8 changed files with 126 additions and 149 deletions

View File

@ -3,23 +3,25 @@ package org.whispersystems.textsecure.push;
import java.util.LinkedList;
import java.util.List;
public class OutgoingGcmMessage {
public class OutgoingPushMessage {
private List<String> destinations;
private String messageText;
private List<String> attachments;
public OutgoingGcmMessage(List<String> destinations, String messageText, List<String> attachments) {
public OutgoingPushMessage(List<String> destinations, String messageText,
List<String> attachments)
{
this.destinations = destinations;
this.messageText = messageText;
this.attachments = attachments;
}
public OutgoingGcmMessage(String destination, String messageText) {
public OutgoingPushMessage(String destination, String messageText) {
this.destinations = new LinkedList<String>();
this.destinations.add(destination);
this.messageText = messageText;
this.attachments = new LinkedList<String>();
this.messageText = messageText;
this.destinations.add(destination);
}
public List<String> getDestinations() {

View File

@ -1,7 +1,6 @@
package org.whispersystems.textsecure.push;
import android.content.Context;
import android.content.res.AssetManager;
import android.util.Base64;
import android.util.Log;
@ -72,7 +71,7 @@ public class PushServiceSocket {
public void sendMessage(String recipient, String messageText)
throws IOException, RateLimitException
{
OutgoingGcmMessage message = new OutgoingGcmMessage(recipient, messageText);
OutgoingPushMessage message = new OutgoingPushMessage(recipient, messageText);
String responseText = makeRequest(MESSAGE_PATH, "POST", new Gson().toJson(message));
GcmMessageResponse response = new Gson().fromJson(responseText, GcmMessageResponse.class);

View File

@ -1,85 +0,0 @@
package org.thoughtcrime.securesms.gcm;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.telephony.SmsManager;
import android.util.Log;
import org.whispersystems.textsecure.directory.NumberFilter;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.push.RateLimitException;
import org.whispersystems.textsecure.util.PhoneNumberFormatter;
import java.io.IOException;
import java.util.ArrayList;
public class OptimizingTransport {
public static void sendTextMessage(Context context, String destinationAddress, String message,
PendingIntent sentIntent, PendingIntent deliveredIntent)
{
String localNumber = TextSecurePreferences.getLocalNumber(context);
String canonicalizedDestinationAddress = PhoneNumberFormatter.formatNumber(destinationAddress, localNumber);
NumberFilter filter = NumberFilter.getInstance(context);
Log.w("OptimzingTransport", "Outgoing message: " + canonicalizedDestinationAddress);
if (filter.containsNumber(canonicalizedDestinationAddress)) {
Log.w("OptimzingTransport", "In the filter, sending GCM...");
sendGcmTextMessage(context, destinationAddress, message, sentIntent, deliveredIntent);
} else {
Log.w("OptimzingTransport", "Not in the filter, sending SMS...");
sendSmsTextMessage(destinationAddress, message, sentIntent, deliveredIntent);
}
}
public static void sendMultipartTextMessage(Context context,
String recipient,
ArrayList<String> messages,
ArrayList<PendingIntent> sentIntents,
ArrayList<PendingIntent> deliveredIntents)
{
// FIXME
sendTextMessage(context, recipient, messages.get(0), sentIntents.get(0), deliveredIntents == null ? null : deliveredIntents.get(0));
}
private static void sendGcmTextMessage(Context context, String recipient, String messageText,
PendingIntent sentIntent, PendingIntent deliveredIntent)
{
try {
String localNumber = TextSecurePreferences.getLocalNumber(context);
String password = TextSecurePreferences.getPushServerPassword(context);
if (localNumber == null || password == null) {
Log.w("OptimzingTransport", "No credentials, falling back to SMS...");
sendSmsTextMessage(recipient, messageText, sentIntent, deliveredIntent);
return;
}
PushServiceSocket pushServiceSocket = new PushServiceSocket(context, localNumber, password);
pushServiceSocket.sendMessage(PhoneNumberFormatter.formatNumber(recipient, localNumber), messageText);
sentIntent.send(Activity.RESULT_OK);
} catch (IOException ioe) {
Log.w("OptimizingTransport", ioe);
Log.w("OptimzingTransport", "IOException, falling back to SMS...");
sendSmsTextMessage(recipient, messageText, sentIntent, deliveredIntent);
} catch (PendingIntent.CanceledException e) {
Log.w("OptimizingTransport", e);
} catch (RateLimitException e) {
Log.w("OptimzingTransport", e);
Log.w("OptimzingTransport", "Rate Limit Exceeded, falling back to SMS...");
sendSmsTextMessage(recipient, messageText, sentIntent, deliveredIntent);
}
}
private static void sendSmsTextMessage(String recipient, String message,
PendingIntent sentIntent, PendingIntent deliveredIntent)
{
SmsManager.getDefault().sendTextMessage(recipient, null, message, sentIntent, deliveredIntent);
}
}

View File

@ -0,0 +1,32 @@
package org.thoughtcrime.securesms.transport;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.service.SmsListener;
public abstract class BaseTransport {
protected Intent constructSentIntent(Context context, long messageId, long type) {
Intent pending = new Intent(SendReceiveService.SENT_SMS_ACTION,
Uri.parse("custom://" + messageId + System.currentTimeMillis()),
context, SmsListener.class);
pending.putExtra("type", type);
pending.putExtra("message_id", messageId);
return pending;
}
protected Intent constructDeliveredIntent(Context context, long messageId, long type) {
Intent pending = new Intent(SendReceiveService.DELIVERED_SMS_ACTION,
Uri.parse("custom://" + messageId + System.currentTimeMillis()),
context, SmsListener.class);
pending.putExtra("type", type);
pending.putExtra("message_id", messageId);
return pending;
}
}

View File

@ -1,24 +0,0 @@
package org.thoughtcrime.securesms.transport;
import android.content.Context;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import java.io.IOException;
public class GcmTransport {
private final Context context;
private final MasterSecret masterSecret;
public GcmTransport(Context context, MasterSecret masterSecret) {
this.context = context.getApplicationContext();
this.masterSecret = masterSecret;
}
public void deliver(SmsMessageRecord message) throws IOException {
}
}

View File

@ -0,0 +1,43 @@
package org.thoughtcrime.securesms.transport;
import android.content.Context;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.push.PushServiceSocket;
import org.whispersystems.textsecure.push.RateLimitException;
import org.whispersystems.textsecure.util.PhoneNumberFormatter;
import java.io.IOException;
public class PushTransport extends BaseTransport {
private final Context context;
private final MasterSecret masterSecret;
public PushTransport(Context context, MasterSecret masterSecret) {
this.context = context.getApplicationContext();
this.masterSecret = masterSecret;
}
public void deliver(SmsMessageRecord message) throws IOException {
try {
String localNumber = TextSecurePreferences.getLocalNumber(context);
String password = TextSecurePreferences.getPushServerPassword(context);
PushServiceSocket socket = new PushServiceSocket(context, localNumber, password);
String recipientNumber = message.getIndividualRecipient().getNumber();
String recipientCanonicalNumber = PhoneNumberFormatter.formatNumber(recipientNumber,
localNumber);
socket.sendMessage(recipientCanonicalNumber, message.getBody().getBody());
context.sendBroadcast(constructSentIntent(context, message.getId(), message.getType()));
} catch (RateLimitException e) {
Log.w("PushTransport", e);
throw new IOException("Rate limit exceeded.");
}
}
}

View File

@ -20,7 +20,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.ArrayList;
public class SmsTransport {
public class SmsTransport extends BaseTransport {
private final Context context;
private final MasterSecret masterSecret;
@ -114,11 +114,10 @@ public class SmsTransport {
private ArrayList<PendingIntent> constructSentIntents(long messageId, long type, ArrayList<String> messages) {
ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(messages.size());
for (int i=0;i<messages.size();i++) {
Intent pending = new Intent(SendReceiveService.SENT_SMS_ACTION, Uri.parse("custom://" + messageId + System.currentTimeMillis()), context, SmsDeliveryListener.class);
pending.putExtra("type", type);
pending.putExtra("message_id", messageId);
sentIntents.add(PendingIntent.getBroadcast(context, 0, pending, 0));
for (String message : messages) {
sentIntents.add(PendingIntent.getBroadcast(context, 0,
constructSentIntent(context, messageId, type),
0));
}
return sentIntents;
@ -131,11 +130,10 @@ public class SmsTransport {
ArrayList<PendingIntent> deliveredIntents = new ArrayList<PendingIntent>(messages.size());
for (int i=0;i<messages.size();i++) {
Intent pending = new Intent(SendReceiveService.DELIVERED_SMS_ACTION, Uri.parse("custom://" + messageId + System.currentTimeMillis()), context, SmsDeliveryListener.class);
pending.putExtra("type", type);
pending.putExtra("message_id", messageId);
deliveredIntents.add(PendingIntent.getBroadcast(context, 0, pending, 0));
for (String message : messages) {
deliveredIntents.add(PendingIntent.getBroadcast(context, 0,
constructDeliveredIntent(context, messageId, type),
0));
}
return deliveredIntents;

View File

@ -1,38 +1,50 @@
package org.thoughtcrime.securesms.transport;
import android.content.Context;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.directory.NumberFilter;
import org.whispersystems.textsecure.util.PhoneNumberFormatter;
import java.io.IOException;
public class UniversalTransport {
private final Context context;
private final GcmTransport gcmTransport;
private final SmsTransport smsTransport;
private final Context context;
private final PushTransport pushTransport;
private final SmsTransport smsTransport;
public UniversalTransport(Context context, MasterSecret masterSecret) {
this.context = context;
this.gcmTransport = new GcmTransport(context, masterSecret);
this.smsTransport = new SmsTransport(context, masterSecret);
this.context = context;
this.pushTransport = new PushTransport(context, masterSecret);
this.smsTransport = new SmsTransport(context, masterSecret);
}
public void deliver(SmsMessageRecord message) throws UndeliverableMessageException {
smsTransport.deliver(message);
// Recipient recipient = message.getIndividualRecipient();
// String number = PhoneNumberFormatter.formatNumber(context, recipient.getNumber());
//
// if (NumberFilter.getInstance(context).containsNumber(number)) {
// try {
// Log.w("UniversalTransport", "Delivering with GCM...");
// gcmTransport.deliver(message);
// } catch (IOException ioe) {
// Log.w("UniversalTransport", ioe);
// smsTransport.deliver(message);
// }
// } else {
// Log.w("UniversalTransport", "Delivering with SMS...");
// smsTransport.deliver(message);
// }
if (!TextSecurePreferences.isPushRegistered(context)) {
smsTransport.deliver(message);
return;
}
Recipient recipient = message.getIndividualRecipient();
String localNumber = TextSecurePreferences.getLocalNumber(context);
String number = PhoneNumberFormatter.formatNumber(recipient.getNumber(), localNumber);
if (NumberFilter.getInstance(context).containsNumber(number)) {
try {
Log.w("UniversalTransport", "Delivering with GCM...");
pushTransport.deliver(message);
} catch (IOException ioe) {
Log.w("UniversalTransport", ioe);
smsTransport.deliver(message);
}
} else {
Log.w("UniversalTransport", "Delivering with SMS...");
smsTransport.deliver(message);
}
}
}