diff --git a/build.gradle b/build.gradle index a53f83f..bf9fefc 100644 --- a/build.gradle +++ b/build.gradle @@ -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 diff --git a/ncsmsgo/build.gradle b/ncsmsgo/build.gradle index 054ca1a..3a5ce32 100644 --- a/ncsmsgo/build.gradle +++ b/ncsmsgo/build.gradle @@ -1,2 +1,2 @@ configurations.maybeCreate("default") -artifacts.add("default", file('ncsmsgo.aar')) \ No newline at end of file +artifacts.add("default", file('ncsmsgo.aar')) diff --git a/ncsmsgo/ncsmsgo.aar b/ncsmsgo/ncsmsgo.aar index 7084674..85f2fb0 100644 Binary files a/ncsmsgo/ncsmsgo.aar and b/ncsmsgo/ncsmsgo.aar differ diff --git a/src/main/java/fr/unix_experience/owncloud_sms/engine/ASyncSMSRecovery.java b/src/main/java/fr/unix_experience/owncloud_sms/engine/ASyncSMSRecovery.java index 3beb0b2..6a89d8b 100644 --- a/src/main/java/fr/unix_experience/owncloud_sms/engine/ASyncSMSRecovery.java +++ b/src/main/java/fr/unix_experience/owncloud_sms/engine/ASyncSMSRecovery.java @@ -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 @@ -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); diff --git a/src/main/java/fr/unix_experience/owncloud_sms/engine/OCHttpClient.java b/src/main/java/fr/unix_experience/owncloud_sms/engine/OCHttpClient.java index acc2990..e3c18c6 100644 --- a/src/main/java/fr/unix_experience/owncloud_sms/engine/OCHttpClient.java +++ b/src/main/java/fr/unix_experience/owncloud_sms/engine/OCHttpClient.java @@ -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 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 getMessages(Long start, Integer limit) throws OCSyncException { - return get(_smsHttpClient.getMessagesCall() - .replace("[START]", start.toString()) - .replace("[LIMIT]", limit.toString()), false); - } - - public Pair execute(String method, URL url, String requestBody, boolean skipError) throws OCSyncException { - Pair 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 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 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); } } diff --git a/src/main/java/fr/unix_experience/owncloud_sms/engine/OCSMSOwnCloudClient.java b/src/main/java/fr/unix_experience/owncloud_sms/engine/OCSMSOwnCloudClient.java index d69459e..fb68247 100644 --- a/src/main/java/fr/unix_experience/owncloud_sms/engine/OCSMSOwnCloudClient.java +++ b/src/main/java/fr/unix_experience/owncloud_sms/engine/OCSMSOwnCloudClient.java @@ -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 response; + Pair 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;