mirror of
https://github.com/cwinfo/envayasms.git
synced 2024-12-04 20:45:32 +00:00
MMS fixes: prevent httpclient from using transfer-encoding: chunked since nginx doesn't supportit; allow forwarding mms when content-location not set
This commit is contained in:
parent
dbf35364ca
commit
73bc3c9fc6
@ -2,7 +2,7 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.envaya.kalsms"
|
package="org.envaya.kalsms"
|
||||||
android:versionCode="3"
|
android:versionCode="3"
|
||||||
android:versionName="2.0-beta 2">
|
android:versionName="2.0-beta 3">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="4" />
|
<uses-sdk android:minSdkVersion="4" />
|
||||||
|
|
||||||
|
@ -657,6 +657,15 @@ public final class App extends Application {
|
|||||||
|
|
||||||
private HttpClient httpClient;
|
private HttpClient httpClient;
|
||||||
|
|
||||||
|
public HttpParams getDefaultHttpParams()
|
||||||
|
{
|
||||||
|
HttpParams httpParams = new BasicHttpParams();
|
||||||
|
HttpConnectionParams.setConnectionTimeout(httpParams, 8000);
|
||||||
|
HttpConnectionParams.setSoTimeout(httpParams, 8000);
|
||||||
|
HttpProtocolParams.setContentCharset(httpParams, "UTF-8");
|
||||||
|
return httpParams;
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized HttpClient getHttpClient()
|
public synchronized HttpClient getHttpClient()
|
||||||
{
|
{
|
||||||
if (httpClient == null)
|
if (httpClient == null)
|
||||||
@ -664,10 +673,7 @@ public final class App extends Application {
|
|||||||
// via http://thinkandroid.wordpress.com/2009/12/31/creating-an-http-client-example/
|
// via http://thinkandroid.wordpress.com/2009/12/31/creating-an-http-client-example/
|
||||||
// also http://hc.apache.org/httpclient-3.x/threading.html
|
// also http://hc.apache.org/httpclient-3.x/threading.html
|
||||||
|
|
||||||
HttpParams httpParams = new BasicHttpParams();
|
HttpParams httpParams = getDefaultHttpParams();
|
||||||
HttpConnectionParams.setConnectionTimeout(httpParams, 8000);
|
|
||||||
HttpConnectionParams.setSoTimeout(httpParams, 8000);
|
|
||||||
HttpProtocolParams.setContentCharset(httpParams, "utf-8");
|
|
||||||
|
|
||||||
SchemeRegistry registry = new SchemeRegistry();
|
SchemeRegistry registry = new SchemeRegistry();
|
||||||
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
|
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
|
||||||
|
@ -34,9 +34,10 @@ public class CheckMmsInboxService extends IntentService
|
|||||||
{
|
{
|
||||||
List<IncomingMms> messages = mmsUtils.getMessagesInInbox();
|
List<IncomingMms> messages = mmsUtils.getMessagesInInbox();
|
||||||
for (IncomingMms mms : messages)
|
for (IncomingMms mms : messages)
|
||||||
{
|
{
|
||||||
if (mmsUtils.isNewMms(mms))
|
if (mmsUtils.isNewMms(mms))
|
||||||
{
|
{
|
||||||
|
app.log("New MMS id=" + mms.getId() + " in inbox");
|
||||||
// prevent forwarding MMS messages that existed in inbox
|
// prevent forwarding MMS messages that existed in inbox
|
||||||
// before KalSMS started, or re-forwarding MMS multiple
|
// before KalSMS started, or re-forwarding MMS multiple
|
||||||
// times if we don't delete them.
|
// times if we don't delete them.
|
||||||
|
@ -11,7 +11,6 @@ import java.util.List;
|
|||||||
import org.apache.http.entity.mime.FormBodyPart;
|
import org.apache.http.entity.mime.FormBodyPart;
|
||||||
import org.apache.http.entity.mime.content.ByteArrayBody;
|
import org.apache.http.entity.mime.content.ByteArrayBody;
|
||||||
import org.apache.http.entity.mime.content.ContentBody;
|
import org.apache.http.entity.mime.content.ContentBody;
|
||||||
import org.apache.http.entity.mime.content.InputStreamBody;
|
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.envaya.kalsms.task.ForwarderTask;
|
import org.envaya.kalsms.task.ForwarderTask;
|
||||||
|
|
||||||
@ -106,21 +105,24 @@ public class IncomingMms extends IncomingMessage {
|
|||||||
{
|
{
|
||||||
if (contentType != null)
|
if (contentType != null)
|
||||||
{
|
{
|
||||||
contentType += "; charset=utf-8";
|
contentType += "; charset=UTF-8";
|
||||||
}
|
}
|
||||||
|
|
||||||
body = new ByteArrayBody(text.getBytes(), contentType, partName);
|
body = new ByteArrayBody(text.getBytes(), contentType, partName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// avoid using InputStreamBody because it forces the HTTP request
|
||||||
|
// to be sent using Transfer-Encoding: chunked, which is not
|
||||||
|
// supported by some web servers (including nginx)
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
body = new InputStreamBody(part.openInputStream(),
|
body = new ByteArrayBody(part.getData(), contentType, partName);
|
||||||
contentType, partName);
|
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
catch (IOException ex)
|
||||||
{
|
{
|
||||||
app.logError("Error opening data for " + part.toString(), ex);
|
app.logError("Error reading data for " + part.toString(), ex);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,6 @@ final class MmsObserver extends ContentObserver {
|
|||||||
@Override
|
@Override
|
||||||
public void onChange(final boolean selfChange) {
|
public void onChange(final boolean selfChange) {
|
||||||
super.onChange(selfChange);
|
super.onChange(selfChange);
|
||||||
|
|
||||||
if (!selfChange)
|
if (!selfChange)
|
||||||
{
|
{
|
||||||
// check MMS inbox in an IntentService since it may be slow
|
// check MMS inbox in an IntentService since it may be slow
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package org.envaya.kalsms;
|
package org.envaya.kalsms;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
public class MmsPart {
|
public class MmsPart {
|
||||||
@ -11,6 +13,7 @@ public class MmsPart {
|
|||||||
private String name;
|
private String name;
|
||||||
private String text;
|
private String text;
|
||||||
private String cid;
|
private String cid;
|
||||||
|
private String dataFile;
|
||||||
|
|
||||||
public MmsPart(App app, long partId)
|
public MmsPart(App app, long partId)
|
||||||
{
|
{
|
||||||
@ -88,6 +91,60 @@ public class MmsPart {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDataFile(String dataFile)
|
||||||
|
{
|
||||||
|
this.dataFile = dataFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDataFile()
|
||||||
|
{
|
||||||
|
return dataFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getDataLength()
|
||||||
|
{
|
||||||
|
if (dataFile != null)
|
||||||
|
{
|
||||||
|
return new File(dataFile).length();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getData() throws IOException
|
||||||
|
{
|
||||||
|
int length = (int)getDataLength();
|
||||||
|
byte[] bytes = new byte[length];
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
int bytesRead = 0;
|
||||||
|
|
||||||
|
InputStream in = openInputStream();
|
||||||
|
|
||||||
|
while (offset < bytes.length)
|
||||||
|
{
|
||||||
|
bytesRead = in.read(bytes, offset, bytes.length - offset);
|
||||||
|
|
||||||
|
if (bytesRead < 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
in.close();
|
||||||
|
|
||||||
|
if (offset < bytes.length)
|
||||||
|
{
|
||||||
|
throw new IOException("Failed to read complete data of MMS part");
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For multimedia parts, the _data column of the MMS Parts table contains the
|
* For multimedia parts, the _data column of the MMS Parts table contains the
|
||||||
* path on the Android filesystem containing that file, and openInputStream
|
* path on the Android filesystem containing that file, and openInputStream
|
||||||
|
@ -4,6 +4,7 @@ package org.envaya.kalsms;
|
|||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -29,7 +30,7 @@ public class MmsUtils
|
|||||||
private static final int MESSAGE_TYPE_RETRIEVE_CONF = 0x84;
|
private static final int MESSAGE_TYPE_RETRIEVE_CONF = 0x84;
|
||||||
|
|
||||||
// todo -- prevent unbounded growth?
|
// todo -- prevent unbounded growth?
|
||||||
private final Set<String> seenMmsContentLocations = new HashSet<String>();
|
private final Set<Long> seenMmsIds = new HashSet<Long>();
|
||||||
|
|
||||||
private App app;
|
private App app;
|
||||||
private ContentResolver contentResolver;
|
private ContentResolver contentResolver;
|
||||||
@ -43,7 +44,7 @@ public class MmsUtils
|
|||||||
private List<MmsPart> getMmsParts(long id)
|
private List<MmsPart> getMmsParts(long id)
|
||||||
{
|
{
|
||||||
Cursor cur = contentResolver.query(PART_URI, new String[] {
|
Cursor cur = contentResolver.query(PART_URI, new String[] {
|
||||||
"_id", "ct", "name", "text", "cid"
|
"_id", "ct", "name", "text", "cid", "_data"
|
||||||
}, "mid = ?", new String[] { "" + id }, null);
|
}, "mid = ?", new String[] { "" + id }, null);
|
||||||
|
|
||||||
// assume that if there is at least one part saved in database
|
// assume that if there is at least one part saved in database
|
||||||
@ -58,7 +59,9 @@ public class MmsUtils
|
|||||||
MmsPart part = new MmsPart(app, partId);
|
MmsPart part = new MmsPart(app, partId);
|
||||||
part.setContentType(cur.getString(1));
|
part.setContentType(cur.getString(1));
|
||||||
part.setName(cur.getString(2));
|
part.setName(cur.getString(2));
|
||||||
|
|
||||||
|
part.setDataFile(cur.getString(5));
|
||||||
|
|
||||||
// todo interpret charset like com.google.android.mms.pdu.EncodedStringValue
|
// todo interpret charset like com.google.android.mms.pdu.EncodedStringValue
|
||||||
part.setText(cur.getString(3));
|
part.setText(cur.getString(3));
|
||||||
|
|
||||||
@ -105,18 +108,18 @@ public class MmsUtils
|
|||||||
|
|
||||||
Cursor c = contentResolver.query(INBOX_URI,
|
Cursor c = contentResolver.query(INBOX_URI,
|
||||||
new String[] {"_id", "ct_l"},
|
new String[] {"_id", "ct_l"},
|
||||||
"m_type = ? AND ct_l is not NULL", new String[] { m_type }, null);
|
"m_type = ? ", new String[] { m_type }, null);
|
||||||
|
|
||||||
List<IncomingMms> messages = new ArrayList<IncomingMms>();
|
List<IncomingMms> messages = new ArrayList<IncomingMms>();
|
||||||
|
|
||||||
while (c.moveToNext())
|
while (c.moveToNext())
|
||||||
{
|
{
|
||||||
long id = c.getLong(0);
|
long id = c.getLong(0);
|
||||||
|
|
||||||
IncomingMms mms = new IncomingMms(app, getSenderNumber(id), id);
|
IncomingMms mms = new IncomingMms(app, getSenderNumber(id), id);
|
||||||
|
|
||||||
mms.setContentLocation(c.getString(1));
|
mms.setContentLocation(c.getString(1));
|
||||||
|
|
||||||
for (MmsPart part : getMmsParts(id))
|
for (MmsPart part : getMmsParts(id))
|
||||||
{
|
{
|
||||||
mms.addPart(part);
|
mms.addPart(part);
|
||||||
@ -129,48 +132,39 @@ public class MmsUtils
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean deleteFromInbox(IncomingMms mms)
|
public synchronized boolean deleteFromInbox(IncomingMms mms)
|
||||||
{
|
{
|
||||||
String contentLocation = mms.getContentLocation();
|
long id = mms.getId();
|
||||||
|
|
||||||
int res;
|
Uri uri = Uri.parse("content://mms/inbox/" + id);
|
||||||
if (contentLocation != null)
|
int res = contentResolver.delete(uri, null, null);
|
||||||
{
|
|
||||||
Uri uri = Uri.parse("content://mms/inbox");
|
if (res > 0)
|
||||||
|
{
|
||||||
/*
|
app.log("MMS id="+id+" deleted from inbox");
|
||||||
* Delete by content location (ct_l) rather than _id so that
|
|
||||||
* M-Notification.ind and M-Retrieve.conf messages are both deleted
|
// remove id from set because Messaging app reuses ids
|
||||||
* (otherwise it would remain in Messaging inbox with a Download button)
|
// of deleted messages.
|
||||||
*/
|
// TODO: handle reuse of IDs deleted directly through Messaging
|
||||||
|
// app while KalSMS is running
|
||||||
res = contentResolver.delete(uri,
|
seenMmsIds.remove(id);
|
||||||
"ct_l = ?",
|
|
||||||
new String[] { contentLocation });
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
app.log("mms has no content-location");
|
app.log("MMS id="+id+" could not be deleted from inbox");
|
||||||
Uri uri = Uri.parse("content://mms/inbox/" + mms.getId());
|
|
||||||
res = contentResolver.delete(uri, null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.log(res + " rows deleted");
|
|
||||||
return res > 0;
|
return res > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void markOldMms(IncomingMms mms)
|
public synchronized void markOldMms(IncomingMms mms)
|
||||||
{
|
{
|
||||||
String contentLocation = mms.getContentLocation();
|
long id = mms.getId();
|
||||||
if (contentLocation != null)
|
seenMmsIds.add(id);
|
||||||
{
|
|
||||||
seenMmsContentLocations.add(contentLocation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean isNewMms(IncomingMms mms)
|
public synchronized boolean isNewMms(IncomingMms mms)
|
||||||
{
|
{
|
||||||
String contentLocation = mms.getContentLocation();
|
long id = mms.getId();
|
||||||
return contentLocation != null && !seenMmsContentLocations.contains(contentLocation);
|
return !seenMmsIds.contains(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import android.os.AsyncTask;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -19,13 +20,17 @@ import javax.xml.parsers.DocumentBuilder;
|
|||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.HttpVersion;
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.entity.mime.FormBodyPart;
|
import org.apache.http.entity.mime.FormBodyPart;
|
||||||
import org.apache.http.entity.mime.MultipartEntity;
|
import org.apache.http.entity.mime.MultipartEntity;
|
||||||
import org.apache.http.entity.mime.content.StringBody;
|
import org.apache.http.entity.mime.content.StringBody;
|
||||||
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
|
import org.apache.http.params.HttpParams;
|
||||||
|
import org.apache.http.params.HttpProtocolParams;
|
||||||
import org.envaya.kalsms.App;
|
import org.envaya.kalsms.App;
|
||||||
import org.envaya.kalsms.Base64Coder;
|
import org.envaya.kalsms.Base64Coder;
|
||||||
import org.envaya.kalsms.OutgoingMessage;
|
import org.envaya.kalsms.OutgoingMessage;
|
||||||
@ -109,27 +114,30 @@ public class HttpTask extends AsyncTask<String, Void, HttpResponse> {
|
|||||||
{
|
{
|
||||||
MultipartEntity entity = new MultipartEntity();//HttpMultipartMode.BROWSER_COMPATIBLE);
|
MultipartEntity entity = new MultipartEntity();//HttpMultipartMode.BROWSER_COMPATIBLE);
|
||||||
|
|
||||||
|
Charset charset = Charset.forName("UTF-8");
|
||||||
|
|
||||||
for (BasicNameValuePair param : params)
|
for (BasicNameValuePair param : params)
|
||||||
{
|
{
|
||||||
entity.addPart(param.getName(), new StringBody(param.getValue()));
|
entity.addPart(param.getName(), new StringBody(param.getValue(), charset));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (FormBodyPart formPart : formParts)
|
for (FormBodyPart formPart : formParts)
|
||||||
{
|
{
|
||||||
entity.addPart(formPart);
|
entity.addPart(formPart);
|
||||||
}
|
}
|
||||||
post.setEntity(entity);
|
post.setEntity(entity);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
post.setEntity(new UrlEncodedFormEntity(params));
|
post.setEntity(new UrlEncodedFormEntity(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HttpClient client = app.getHttpClient();
|
||||||
|
|
||||||
String signature = getSignature();
|
String signature = getSignature();
|
||||||
|
|
||||||
post.setHeader("X-Kalsms-Signature", signature);
|
post.setHeader("X-Kalsms-Signature", signature);
|
||||||
|
|
||||||
HttpClient client = app.getHttpClient();
|
|
||||||
HttpResponse response = client.execute(post);
|
HttpResponse response = client.execute(post);
|
||||||
|
|
||||||
int statusCode = response.getStatusLine().getStatusCode();
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
@ -3,14 +3,11 @@ package org.envaya.kalsms.ui;
|
|||||||
import org.envaya.kalsms.task.HttpTask;
|
import org.envaya.kalsms.task.HttpTask;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.text.Html;
|
|
||||||
import android.text.method.ScrollingMovementMethod;
|
import android.text.method.ScrollingMovementMethod;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@ -18,6 +15,7 @@ import android.view.MenuItem;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import java.util.List;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.envaya.kalsms.App;
|
import org.envaya.kalsms.App;
|
||||||
@ -92,13 +90,13 @@ public class Main extends Activity {
|
|||||||
case R.id.check_now:
|
case R.id.check_now:
|
||||||
app.checkOutgoingMessages();
|
app.checkOutgoingMessages();
|
||||||
return true;
|
return true;
|
||||||
case R.id.retry_now:
|
case R.id.retry_now:
|
||||||
app.retryStuckMessages();
|
app.retryStuckMessages();
|
||||||
return true;
|
return true;
|
||||||
case R.id.forward_inbox:
|
case R.id.forward_inbox:
|
||||||
startActivity(new Intent(this, ForwardInbox.class));
|
startActivity(new Intent(this, ForwardInbox.class));
|
||||||
return true;
|
return true;
|
||||||
case R.id.help:
|
case R.id.help:
|
||||||
startActivity(new Intent(this, Help.class));
|
startActivity(new Intent(this, Help.class));
|
||||||
return true;
|
return true;
|
||||||
case R.id.test:
|
case R.id.test:
|
||||||
|
Loading…
Reference in New Issue
Block a user