1
0
mirror of https://github.com/nerzhul/ownCloud-SMS-App.git synced 2025-06-07 07:56:14 +00:00

SMS restore now used GoLang HTTP client. (#180)

This permits to remove the whole android HTTP client from source
This commit is contained in:
Loïc Blot 2018-02-10 15:59:46 +01:00 committed by GitHub
parent 8ad3b251b0
commit 61ed3d2791
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 65 additions and 263 deletions

View File

@ -22,7 +22,7 @@ android {
defaultConfig {
applicationId "fr.unix_experience.owncloud_sms"
versionCode 64
versionName "2.0.0"
versionName "2.0.1"
minSdkVersion 16
targetSdkVersion 27
maxSdkVersion 27

View File

@ -1,2 +1,2 @@
configurations.maybeCreate("default")
artifacts.add("default", file('ncsmsgo.aar'))
artifacts.add("default", file('ncsmsgo.aar'))

Binary file not shown.

View File

@ -7,14 +7,11 @@ import android.os.AsyncTask;
import android.provider.Telephony;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Iterator;
import fr.unix_experience.owncloud_sms.activities.remote_account.RestoreMessagesActivity;
import fr.unix_experience.owncloud_sms.enums.MailboxID;
import fr.unix_experience.owncloud_sms.providers.SmsDataProvider;
import ncsmsgo.SmsMessage;
import ncsmsgo.SmsMessagesResponse;
/*
* Copyright (c) 2014-2016, Loic Blot <loic.blot@unix-experience.fr>
@ -60,81 +57,68 @@ public interface ASyncSMSRecovery {
OCSMSOwnCloudClient client = new OCSMSOwnCloudClient(_context, _account);
SmsDataProvider smsDataProvider = new SmsDataProvider(_context);
JSONObject obj = client.retrieveSomeMessages(start, 500);
SmsMessagesResponse obj = client.retrieveSomeMessages(start, 500);
if (obj == null) {
Log.i(ASyncSMSRecovery.TAG, "Retrieved returns failure");
return null;
}
Integer nb = 0;
try {
while ((obj != null) && (obj.getLong("last_id") != start)) {
JSONObject messages = obj.getJSONObject("messages");
Iterator<?> keys = messages.keys();
while (keys.hasNext()) {
String key = (String)keys.next();
if (messages.get(key) instanceof JSONObject) {
JSONObject msg = messages.getJSONObject(key);
int mbid = msg.getInt("mailbox");
// Ignore invalid mailbox
if (mbid > MailboxID.ALL.getId()) {
Log.e(ASyncSMSRecovery.TAG, "Invalid mailbox found: " + msg.getString("mailbox"));
continue;
}
String address;
String body;
int type;
try {
address = msg.getString("address");
body = msg.getString("msg");
type = msg.getInt("type");
}
catch (JSONException e) {
Log.e(ASyncSMSRecovery.TAG, "Invalid SMS data found: " + e.getMessage());
continue;
}
MailboxID mailbox_id = MailboxID.fromInt(mbid);
// Ignore already existing messages
if (smsDataProvider.messageExists(address, body, key, mailbox_id)) {
publishProgress(nb);
continue;
}
ContentValues values = new ContentValues();
values.put(Telephony.Sms.ADDRESS, address);
values.put(Telephony.Sms.BODY, body);
values.put(Telephony.Sms.DATE, key);
values.put(Telephony.Sms.TYPE, type);
values.put(Telephony.Sms.SEEN, 1);
values.put(Telephony.Sms.READ, 1);
// @TODO verify message exists before inserting it
_context.getContentResolver().insert(Uri.parse(mailbox_id.getURI()), values);
nb++;
if ((nb % 5) == 0) {
publishProgress(nb);
}
}
while ((obj != null) && (obj.getLastID() != start)) {
SmsMessage message;
while ((message = obj.getNextMessage()) != null) {
int mbid = (int) message.getMailbox();
// Ignore invalid mailbox
if (mbid > MailboxID.ALL.getId()) {
Log.e(ASyncSMSRecovery.TAG, "Invalid mailbox found: " + mbid);
continue;
}
start = obj.getLong("last_id");
if (!new ConnectivityMonitor(_context).isValid()) {
Log.e(ASyncSMSRecovery.TAG, "Restore connectivity problems, aborting");
return null;
String address = message.getAddress();
String body = message.getMessage();
int type = (int) message.getType();
if (address.isEmpty() || body.isEmpty()) {
Log.e(ASyncSMSRecovery.TAG, "Invalid SMS message found: " + message.toString());
continue;
}
MailboxID mailbox_id = MailboxID.fromInt(mbid);
String date = Integer.toString((int) message.getDate());
// Ignore already existing messages
if (smsDataProvider.messageExists(address, body, date, mailbox_id)) {
publishProgress(nb);
continue;
}
ContentValues values = new ContentValues();
values.put(Telephony.Sms.ADDRESS, address);
values.put(Telephony.Sms.BODY, body);
values.put(Telephony.Sms.DATE, date);
values.put(Telephony.Sms.TYPE, type);
values.put(Telephony.Sms.SEEN, 1);
values.put(Telephony.Sms.READ, 1);
_context.getContentResolver().insert(Uri.parse(mailbox_id.getURI()), values);
nb++;
if ((nb % 10) == 0) {
publishProgress(nb);
}
obj = client.retrieveSomeMessages(start, 500);
}
} catch (JSONException e) {
Log.e(ASyncSMSRecovery.TAG, "Missing last_id field!");
start = obj.getLastID();
if (!new ConnectivityMonitor(_context).isValid()) {
Log.e(ASyncSMSRecovery.TAG, "Restore connectivity problems, aborting");
return null;
}
obj = client.retrieveSomeMessages(start, 500);
}
// Force this refresh to fix dates
_context.getContentResolver().delete(Uri.parse("content://sms/conversations/-1"), null, null);
_context.getContentResolver().delete(Uri.parse("content://sms/conversations/-1"),
null, null);
publishProgress(nb);

View File

@ -18,28 +18,9 @@ package fr.unix_experience.owncloud_sms.engine;
*/
import android.content.Context;
import android.util.Base64;
import android.util.Log;
import android.util.Pair;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.nio.charset.Charset;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.enums.OCSyncErrorType;
@ -48,66 +29,18 @@ import fr.unix_experience.owncloud_sms.providers.AndroidVersionProvider;
import ncsmsgo.SmsBuffer;
import ncsmsgo.SmsHTTPClient;
import ncsmsgo.SmsIDListResponse;
import ncsmsgo.SmsMessagesResponse;
import ncsmsgo.SmsPhoneListResponse;
import ncsmsgo.SmsPushResponse;
public class OCHttpClient {
private SmsHTTPClient _smsHttpClient;
private static final String TAG = OCHttpClient.class.getCanonicalName();
private static final String PARAM_PROTOCOL_VERSION = "http.protocol.version";
private final URL _url;
private final String _userAgent;
private final String _username;
private final String _password;
public OCHttpClient(Context context, URL serverURL, String accountName, String accountPassword) {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception ignored) {
}
_url = serverURL;
_username = accountName;
_password = accountPassword;
_smsHttpClient = new SmsHTTPClient();
// @TODO: at a point add a flag to permit insecure connections somewhere instead of trusting them
_smsHttpClient.init(_url.toString(), new AndroidVersionProvider(context).getVersionCode(),
_username, _password, false);
_userAgent = "nextcloud-phonesync (" + new AndroidVersionProvider(context).getVersionCode() + ")";
}
private Pair<Integer, JSONObject> get(String oc_call, boolean skipError) throws OCSyncException {
Log.i(OCHttpClient.TAG, "Perform GET " + _url + oc_call);
try {
return execute("GET",
new URL(_url.toString() + oc_call), "", skipError);
} catch (MalformedURLException e) {
Log.e(OCHttpClient.TAG, "Malformed URL provided, aborting. URL was: "
+ _url.toExternalForm() + oc_call);
}
return new Pair<>(0, null);
_smsHttpClient.init(serverURL.toString(), new AndroidVersionProvider(context).getVersionCode(),
accountName, accountPassword, false);
}
private void handleEarlyHTTPStatus(int httpStatus) throws OCSyncException {
@ -168,120 +101,10 @@ public class OCHttpClient {
return new Pair<>(httpStatus, splr);
}
Pair<Integer, JSONObject> getMessages(Long start, Integer limit) throws OCSyncException {
return get(_smsHttpClient.getMessagesCall()
.replace("[START]", start.toString())
.replace("[LIMIT]", limit.toString()), false);
}
public Pair<Integer, JSONObject> execute(String method, URL url, String requestBody, boolean skipError) throws OCSyncException {
Pair<Integer, JSONObject> response;
HttpURLConnection urlConnection = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
Log.e(OCHttpClient.TAG, "Failed to open connection to server: " + e);
throw new OCSyncException(R.string.err_sync_http_request_ioexception, OCSyncErrorType.IO);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
if (urlConnection == null) {
Log.e(OCHttpClient.TAG, "Failed to open connection to server: null urlConnection");
throw new OCSyncException(R.string.err_sync_http_request_ioexception, OCSyncErrorType.IO);
}
try {
urlConnection.setRequestMethod(method);
} catch (ProtocolException e) {
Log.e(OCHttpClient.TAG, "Fatal error when setting request method: " + e);
throw new OCSyncException(R.string.err_sync_http_request_protocol_exception, OCSyncErrorType.IO);
}
urlConnection.setRequestProperty("User-Agent", _userAgent);
urlConnection.setInstanceFollowRedirects(true);
if (!"GET".equals(method)) {
urlConnection.setDoOutput(true);
}
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Accept", "application/json");
if (!"GET".equals(method)) {
urlConnection.setRequestProperty("Transfer-Encoding", "chunked");
}
String basicAuth = "Basic " +
Base64.encodeToString((_username + ":" + _password).getBytes(), Base64.NO_WRAP);
urlConnection.setRequestProperty("Authorization", basicAuth);
urlConnection.setChunkedStreamingMode(0);
if (!"GET".equals(method)) {
try {
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
out.write(requestBody.getBytes(Charset.forName("UTF-8")));
out.close();
} catch (IOException e) {
Log.e(OCHttpClient.TAG, "Failed to open connection to server: " + e);
throw new OCSyncException(R.string.err_sync_http_write_failed, OCSyncErrorType.IO);
}
}
response = handleHTTPResponse(urlConnection, skipError);
return response;
}
private Pair<Integer, JSONObject> handleHTTPResponse(HttpURLConnection connection, Boolean skipError) throws OCSyncException {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line).append("\n");
}
String response = stringBuilder.toString();
int status = connection.getResponseCode();
switch (status) {
case 200: {
// Parse the response
try {
JSONObject jsonResponse = new JSONObject(response);
return new Pair<>(status, jsonResponse);
} catch (JSONException e) {
if (!skipError) {
if (response.contains("ownCloud") && response.contains("DOCTYPE")) {
Log.e(OCHttpClient.TAG, "OcSMS app not enabled or ownCloud upgrade is required");
throw new OCSyncException(R.string.err_sync_ocsms_not_installed_or_oc_upgrade_required,
OCSyncErrorType.SERVER_ERROR);
} else {
Log.e(OCHttpClient.TAG, "Unable to parse server response", e);
throw new OCSyncException(R.string.err_sync_http_request_parse_resp, OCSyncErrorType.PARSE);
}
}
}
break;
}
case 403: {
// Authentication failed
throw new OCSyncException(R.string.err_sync_auth_failed, OCSyncErrorType.AUTH);
}
default: {
// Unk error
Log.e(OCHttpClient.TAG, "Server set unhandled HTTP return code " + status);
Log.e(OCHttpClient.TAG, "Status code: " + status + ". Response message: " + response);
throw new OCSyncException(R.string.err_sync_http_request_returncode_unhandled, OCSyncErrorType.SERVER_ERROR);
}
}
reader.close();
}
catch (IOException e) {
throw new OCSyncException(R.string.err_sync_http_request_ioexception, OCSyncErrorType.IO);
}
return new Pair<>(0, null);
Pair<Integer, SmsMessagesResponse> getMessages(Long start, Integer limit) throws OCSyncException {
SmsMessagesResponse smr = _smsHttpClient.doGetMessagesCall(start, limit);
int httpStatus = (int) _smsHttpClient.getLastHTTPStatus();
handleEarlyHTTPStatus(httpStatus);
return new Pair<>(httpStatus, smr);
}
}

View File

@ -23,8 +23,6 @@ import android.content.Context;
import android.util.Log;
import android.util.Pair;
import org.json.JSONObject;
import java.net.MalformedURLException;
import java.net.URL;
@ -34,6 +32,7 @@ import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
import ncsmsgo.SmsBuffer;
import ncsmsgo.SmsIDListResponse;
import ncsmsgo.SmsMessagesResponse;
import ncsmsgo.SmsPhoneListResponse;
import ncsmsgo.SmsPushResponse;
@ -57,8 +56,6 @@ public class OCSMSOwnCloudClient {
_http = new OCHttpClient(context,
serverURL, accountManager.getUserData(account, "ocLogin"),
accountManager.getPassword(account));
_connectivityMonitor = new ConnectivityMonitor(_context);
} catch (MalformedURLException e) {
throw new IllegalStateException(context.getString(R.string.err_sync_account_unparsable));
}
@ -124,14 +121,14 @@ public class OCSMSOwnCloudClient {
Log.i(OCSMSOwnCloudClient.TAG, "LastMessageDate set to: " + smsBuffer.getLastMessageDate());
}
JSONObject retrieveSomeMessages(Long start, Integer limit) {
SmsMessagesResponse retrieveSomeMessages(Long start, Integer limit) {
// This is not allowed by server
if (limit > OCSMSOwnCloudClient.SERVER_RECOVERY_MSG_LIMIT) {
Log.e(OCSMSOwnCloudClient.TAG, "Message recovery limit exceeded");
return null;
}
Pair<Integer, JSONObject> response;
Pair<Integer, SmsMessagesResponse> response;
try {
response = _http.getMessages(start, limit);
} catch (OCSyncException e) {
@ -139,8 +136,7 @@ public class OCSMSOwnCloudClient {
return null;
}
if ((response.second == null) || !response.second.has("messages")
|| !response.second.has("last_id")) {
if (response.second == null) {
Log.e(OCSMSOwnCloudClient.TAG,
"Invalid response received from server, either messages or last_id field is missing.");
return null;
@ -151,7 +147,6 @@ public class OCSMSOwnCloudClient {
private final OCHttpClient _http;
private final Context _context;
private final ConnectivityMonitor _connectivityMonitor;
private Integer _serverAPIVersion;