Initial fixes for MMS retrieval.

1) Parse the APN information based on what the ConnectionManager
tells us.

2) Accept email addresses as a valid Recipient format.
This commit is contained in:
Moxie Marlinspike 2012-09-30 11:46:45 -07:00
parent 59e7226183
commit cf9dc51f31
9 changed files with 224 additions and 217 deletions

View File

@ -421,9 +421,9 @@ public class MmsDatabase extends Database {
database.delete(TABLE_NAME, null, null); database.delete(TABLE_NAME, null, null);
} }
public Cursor getCarrierMmsInformation() { public Cursor getCarrierMmsInformation(String apn) {
Uri uri = Uri.withAppendedPath(Uri.parse("content://telephony/carriers"), "current"); Uri uri = Uri.withAppendedPath(Uri.parse("content://telephony/carriers"), "current");
String selection = "type = 'mms'"; String selection = (apn == null || apn.trim().length() == 0) ? null : String.format("apn = '%s'", apn.trim());
return context.getContentResolver().query(uri, null, selection, null, null); return context.getContentResolver().query(uri, null, selection, null, null);
} }

View File

@ -16,9 +16,10 @@
*/ */
package org.thoughtcrime.securesms.mms; package org.thoughtcrime.securesms.mms;
import java.io.DataInputStream; import android.content.Context;
import java.io.IOException; import android.database.Cursor;
import java.net.InetAddress; import android.net.ConnectivityManager;
import android.net.Uri;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
@ -42,19 +43,20 @@ import org.thoughtcrime.securesms.util.Conversions;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
import android.content.Context; import java.io.DataInputStream;
import android.database.Cursor; import java.io.IOException;
import android.net.ConnectivityManager; import java.net.InetAddress;
import android.net.Uri;
public class MmsCommunication { public class MmsCommunication {
protected static MmsConnectionParameters getMmsConnectionParameters(Context context) throws MmsException { protected static MmsConnectionParameters getMmsConnectionParameters(Context context, String apn)
Cursor cursor = DatabaseFactory.getMmsDatabase(context).getCarrierMmsInformation(); throws MmsException
{
Cursor cursor = DatabaseFactory.getMmsDatabase(context).getCarrierMmsInformation(apn);
try { try {
if (cursor == null || !cursor.moveToFirst()) if (cursor == null || !cursor.moveToFirst())
throw new MmsException("No carrier MMS information available."); throw new MmsException("No carrier MMS information available.");
do { do {
String mmsc = cursor.getString(cursor.getColumnIndexOrThrow("mmsc")); String mmsc = cursor.getString(cursor.getColumnIndexOrThrow("mmsc"));

View File

@ -16,9 +16,8 @@
*/ */
package org.thoughtcrime.securesms.mms; package org.thoughtcrime.securesms.mms;
import java.io.IOException; import android.content.Context;
import java.net.URI; import android.util.Log;
import java.net.URISyntaxException;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.HttpRequest; import org.apache.http.HttpRequest;
@ -30,12 +29,15 @@ import org.apache.http.client.methods.HttpGet;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
import android.content.Context; import java.io.IOException;
import android.util.Log; import java.net.URI;
import java.net.URISyntaxException;
public class MmsDownloadHelper extends MmsCommunication { public class MmsDownloadHelper extends MmsCommunication {
private static byte[] makeRequest(MmsConnectionParameters connectionParameters, String url) throws ClientProtocolException, IOException { private static byte[] makeRequest(MmsConnectionParameters connectionParameters, String url)
throws ClientProtocolException, IOException
{
try { try {
HttpClient client = constructHttpClient(connectionParameters); HttpClient client = constructHttpClient(connectionParameters);
URI hostUrl = new URI(url); URI hostUrl = new URI(url);
@ -58,9 +60,9 @@ public class MmsDownloadHelper extends MmsCommunication {
} }
} }
public static byte[] retrieveMms(Context context, String url) throws IOException { public static byte[] retrieveMms(Context context, String url, String apn) throws IOException {
try { try {
MmsConnectionParameters connectionParameters = getMmsConnectionParameters(context); MmsConnectionParameters connectionParameters = getMmsConnectionParameters(context, apn);
checkRouteToHost(context, connectionParameters, url); checkRouteToHost(context, connectionParameters, url);
return makeRequest(connectionParameters, url); return makeRequest(connectionParameters, url);

View File

@ -16,9 +16,8 @@
*/ */
package org.thoughtcrime.securesms.mms; package org.thoughtcrime.securesms.mms;
import java.io.IOException; import android.content.Context;
import java.net.URI; import android.util.Log;
import java.net.URISyntaxException;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
@ -27,11 +26,12 @@ import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ByteArrayEntity;
import org.thoughtcrime.securesms.util.Hex;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
import android.content.Context;
import android.util.Log; import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
public class MmsSendHelper extends MmsCommunication { public class MmsSendHelper extends MmsCommunication {
@ -63,10 +63,10 @@ public class MmsSendHelper extends MmsCommunication {
} }
} }
public static byte[] sendMms(Context context, byte[] mms) throws IOException { public static byte[] sendMms(Context context, byte[] mms, String apn) throws IOException {
Log.w("MmsSender", "Sending MMS of length: " + mms.length); Log.w("MmsSender", "Sending MMS of length: " + mms.length);
try { try {
MmsConnectionParameters parameters = getMmsConnectionParameters(context); MmsConnectionParameters parameters = getMmsConnectionParameters(context, apn);
checkRouteToHost(context, parameters, parameters.getMmsc()); checkRouteToHost(context, parameters, parameters.getMmsc());
return makePost(parameters, mms); return makePost(parameters, mms);
} catch (MmsException me) { } catch (MmsException me) {

View File

@ -19,7 +19,6 @@ package org.thoughtcrime.securesms.recipients;
import android.content.Context; import android.content.Context;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.telephony.PhoneNumberUtils;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
@ -146,7 +145,7 @@ public class RecipientFactory {
int end = recipient.indexOf('>'); int end = recipient.indexOf('>');
String value = recipient.substring(begin + 1, end); String value = recipient.substring(begin + 1, end);
if (PhoneNumberUtils.isWellFormedSmsAddress(value)) if (NumberUtil.isValidSmsOrEmail(value))
return value; return value;
else else
throw new RecipientFormattingException("Bracketed value: " + value + " is not valid."); throw new RecipientFormattingException("Bracketed value: " + value + " is not valid.");

View File

@ -16,8 +16,9 @@
*/ */
package org.thoughtcrime.securesms.service; package org.thoughtcrime.securesms.service;
import java.io.IOException; import android.content.Context;
import java.util.LinkedList; import android.content.Intent;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.DecryptingQueue; import org.thoughtcrime.securesms.crypto.DecryptingQueue;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
@ -29,9 +30,9 @@ import org.thoughtcrime.securesms.protocol.WirePrefix;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.PduParser; import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.RetrieveConf; import ws.com.google.android.mms.pdu.RetrieveConf;
import android.content.Context;
import android.content.Intent; import java.io.IOException;
import android.util.Log; import java.util.LinkedList;
public class MmsDownloader extends MmscProcessor { public class MmsDownloader extends MmscProcessor {
@ -66,7 +67,8 @@ public class MmsDownloader extends MmscProcessor {
try { try {
byte[] pdu = MmsDownloadHelper.retrieveMms(context, item.getContentLocation()); byte[] pdu = MmsDownloadHelper.retrieveMms(context, item.getContentLocation(),
getApnInformation());
RetrieveConf retrieved = (RetrieveConf)new PduParser(pdu).parse(); RetrieveConf retrieved = (RetrieveConf)new PduParser(pdu).parse();
for (int i=0;i<retrieved.getBody().getPartsNum();i++) { for (int i=0;i<retrieved.getBody().getPartsNum();i++) {

View File

@ -16,6 +16,10 @@
*/ */
package org.thoughtcrime.securesms.service; package org.thoughtcrime.securesms.service;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
@ -24,9 +28,6 @@ import ws.com.google.android.mms.pdu.GenericPdu;
import ws.com.google.android.mms.pdu.NotificationInd; import ws.com.google.android.mms.pdu.NotificationInd;
import ws.com.google.android.mms.pdu.PduHeaders; import ws.com.google.android.mms.pdu.PduHeaders;
import ws.com.google.android.mms.pdu.PduParser; import ws.com.google.android.mms.pdu.PduParser;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class MmsReceiver { public class MmsReceiver {
@ -55,9 +56,9 @@ public class MmsReceiver {
MmsDatabase database; MmsDatabase database;
if (masterSecret != null) if (masterSecret != null)
database = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret); database = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret);
else else
database = DatabaseFactory.getMmsDatabase(context); database = DatabaseFactory.getMmsDatabase(context);
long messageId = database.insertMessageReceived((NotificationInd)pdu); long messageId = database.insertMessageReceived((NotificationInd)pdu);
MessageNotifier.updateNotification(context, true); MessageNotifier.updateNotification(context, true);

View File

@ -16,16 +16,16 @@
*/ */
package org.thoughtcrime.securesms.service; package org.thoughtcrime.securesms.service;
import java.io.IOException; import android.content.Context;
import java.util.Arrays; import android.content.Intent;
import java.util.LinkedList; import android.telephony.TelephonyManager;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SessionCipher; import org.thoughtcrime.securesms.crypto.SessionCipher;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.mms.MmsSendHelper; import org.thoughtcrime.securesms.mms.MmsSendHelper;
import org.thoughtcrime.securesms.mms.PngTransport;
import org.thoughtcrime.securesms.mms.TextTransport; import org.thoughtcrime.securesms.mms.TextTransport;
import org.thoughtcrime.securesms.protocol.WirePrefix; import org.thoughtcrime.securesms.protocol.WirePrefix;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
@ -33,7 +33,6 @@ import org.thoughtcrime.securesms.util.Hex;
import ws.com.google.android.mms.ContentType; import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.CharacterSets;
import ws.com.google.android.mms.pdu.EncodedStringValue; import ws.com.google.android.mms.pdu.EncodedStringValue;
import ws.com.google.android.mms.pdu.PduBody; import ws.com.google.android.mms.pdu.PduBody;
import ws.com.google.android.mms.pdu.PduComposer; import ws.com.google.android.mms.pdu.PduComposer;
@ -42,10 +41,10 @@ import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.PduPart; import ws.com.google.android.mms.pdu.PduPart;
import ws.com.google.android.mms.pdu.SendConf; import ws.com.google.android.mms.pdu.SendConf;
import ws.com.google.android.mms.pdu.SendReq; import ws.com.google.android.mms.pdu.SendReq;
import android.content.Context;
import android.content.Intent; import java.io.IOException;
import android.telephony.TelephonyManager; import java.util.Arrays;
import android.util.Log; import java.util.LinkedList;
public class MmsSender extends MmscProcessor { public class MmsSender extends MmscProcessor {
@ -61,23 +60,23 @@ public class MmsSender extends MmscProcessor {
MmsDatabase database = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret); MmsDatabase database = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret);
try { try {
SendReq[] sendRequests; SendReq[] sendRequests;
if (messageId == -1) { if (messageId == -1) {
sendRequests = database.getOutgoingMessages(); sendRequests = database.getOutgoingMessages();
} else { } else {
sendRequests = new SendReq[1]; sendRequests = new SendReq[1];
sendRequests[0] = database.getSendRequest(messageId); sendRequests[0] = database.getSendRequest(messageId);
} }
if (sendRequests.length > 0) if (sendRequests.length > 0)
handleSendMms(sendRequests); handleSendMms(sendRequests);
} catch (MmsException me) { } catch (MmsException me) {
Log.w("MmsSender", me); Log.w("MmsSender", me);
if (messageId != -1) if (messageId != -1)
database.markAsSentFailed(messageId); database.markAsSentFailed(messageId);
} }
} else if (intent.getAction().equals(SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION)) { } else if (intent.getAction().equals(SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION)) {
handleConnectivityChange(masterSecret); handleConnectivityChange(masterSecret);
} }
@ -94,7 +93,7 @@ public class MmsSender extends MmscProcessor {
private void handleSendMms(SendReq[] sendRequests) { private void handleSendMms(SendReq[] sendRequests) {
if (!isConnectivityPossible()) { if (!isConnectivityPossible()) {
for (int i=0;i<sendRequests.length;i++) for (int i=0;i<sendRequests.length;i++)
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(sendRequests[i].getDatabaseMessageId()); DatabaseFactory.getMmsDatabase(context).markAsSentFailed(sendRequests[i].getDatabaseMessageId());
} else { } else {
pendingMessages.add(sendRequests); pendingMessages.add(sendRequests);
issueConnectivityRequest(); issueConnectivityRequest();
@ -138,35 +137,35 @@ public class MmsSender extends MmscProcessor {
private void sendMms(MmsDatabase db, SendReq pdu, String number, long messageId, boolean secure) { private void sendMms(MmsDatabase db, SendReq pdu, String number, long messageId, boolean secure) {
try { try {
if (number != null && number.trim().length() != 0) if (number != null && number.trim().length() != 0)
pdu.setFrom(new EncodedStringValue(number)); pdu.setFrom(new EncodedStringValue(number));
byte[] response = MmsSendHelper.sendMms(context, new PduComposer(context, pdu).make()); byte[] response = MmsSendHelper.sendMms(context, new PduComposer(context, pdu).make(), getApnInformation());
SendConf conf = (SendConf) new PduParser(response).parse(); SendConf conf = (SendConf) new PduParser(response).parse();
for (int i=0;i<pdu.getBody().getPartsNum();i++) { for (int i=0;i<pdu.getBody().getPartsNum();i++) {
Log.w("MmsSender", "Sent MMS part of content-type: " + new String(pdu.getBody().getPart(i).getContentType())); Log.w("MmsSender", "Sent MMS part of content-type: " + new String(pdu.getBody().getPart(i).getContentType()));
} }
if (conf == null) { if (conf == null) {
db.markAsSentFailed(messageId); db.markAsSentFailed(messageId);
Log.w("MmsSender", "No M-Send.conf received in response to send."); Log.w("MmsSender", "No M-Send.conf received in response to send.");
return; return;
} else if (conf.getResponseStatus() != PduHeaders.RESPONSE_STATUS_OK) { } else if (conf.getResponseStatus() != PduHeaders.RESPONSE_STATUS_OK) {
Log.w("MmsSender", "Got bad response: " + conf.getResponseStatus()); Log.w("MmsSender", "Got bad response: " + conf.getResponseStatus());
db.updateResponseStatus(messageId, conf.getResponseStatus()); db.updateResponseStatus(messageId, conf.getResponseStatus());
db.markAsSentFailed(messageId); db.markAsSentFailed(messageId);
return; return;
} else if (isInconsistentResponse(pdu, conf)) { } else if (isInconsistentResponse(pdu, conf)) {
db.markAsSentFailed(messageId); db.markAsSentFailed(messageId);
Log.w("MmsSender", "Got a response for the wrong transaction?"); Log.w("MmsSender", "Got a response for the wrong transaction?");
return; return;
} else { } else {
Log.w("MmsSender", "Successful send! " + messageId); Log.w("MmsSender", "Successful send! " + messageId);
if (!secure) if (!secure)
db.markAsSent(messageId, conf.getMessageId(), conf.getResponseStatus()); db.markAsSent(messageId, conf.getMessageId(), conf.getResponseStatus());
else else
db.markAsSecureSent(messageId, conf.getMessageId(), conf.getResponseStatus()); db.markAsSecureSent(messageId, conf.getMessageId(), conf.getResponseStatus());
} }
} catch (IOException ioe) { } catch (IOException ioe) {
Log.w("MmsSender", ioe); Log.w("MmsSender", ioe);
db.markAsSentFailed(messageId); db.markAsSentFailed(messageId);
@ -185,7 +184,7 @@ public class MmsSender extends MmscProcessor {
long messageBox = request.getDatabaseMessageBox(); long messageBox = request.getDatabaseMessageBox();
if (MmsDatabase.Types.isSecureMmsBox(messageBox)) if (MmsDatabase.Types.isSecureMmsBox(messageBox))
request = getEncryptedMms(masterSecret, request, messageId); request = getEncryptedMms(masterSecret, request, messageId);
sendMms(db, request, number, messageId, MmsDatabase.Types.isSecureMmsBox(messageBox)); sendMms(db, request, number, messageId, MmsDatabase.Types.isSecureMmsBox(messageBox));
} }
@ -195,7 +194,7 @@ public class MmsSender extends MmscProcessor {
} }
@Override @Override
protected String getConnectivityAction() { protected String getConnectivityAction() {
return SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION; return SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION;
} }

View File

@ -16,8 +16,6 @@
*/ */
package org.thoughtcrime.securesms.service; package org.thoughtcrime.securesms.service;
import java.util.LinkedList;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -48,6 +46,10 @@ public abstract class MmscProcessor {
this.wakeLock.setReferenceCounted(false); this.wakeLock.setReferenceCounted(false);
} }
protected String getApnInformation() {
return connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS).getExtraInfo();
}
protected boolean isConnected() { protected boolean isConnected() {
NetworkInfo info = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS); NetworkInfo info = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS);
@ -102,7 +104,7 @@ public abstract class MmscProcessor {
private class ConnectivityListener extends BroadcastReceiver { private class ConnectivityListener extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Log.w("MmsService", "Dispatching connectivity change..."); Log.w("MmsService", "Dispatching connectivity change...");
issueConnectivityChange(); issueConnectivityChange();
Log.w("MmsService", "Dispatched..."); Log.w("MmsService", "Dispatched...");