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

Refactor the whole OCHttpClient to use modern android http client

This commit is contained in:
Loic Blot 2017-11-03 12:16:43 +01:00
parent 605fbf5ad7
commit 5cb6c63f16
No known key found for this signature in database
GPG Key ID: EFAA458E8C153987
3 changed files with 173 additions and 298 deletions

View File

@ -32,6 +32,7 @@ import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
@ -43,15 +44,15 @@ import android.widget.TextView;
import com.dd.processbutton.iml.ActionProcessButton;
import org.apache.commons.httpclient.methods.GetMethod;
import org.json.JSONObject;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.defines.DefaultPrefs;
import fr.unix_experience.owncloud_sms.engine.OCHttpClient;
import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
/**
* A login screen that offers login via email/password.
@ -268,21 +269,17 @@ public class LoginActivity extends AppCompatActivity {
protected Boolean doInBackground(Void... params) {
_returnCode = 0;
OCHttpClient http = new OCHttpClient(getBaseContext(), _serverURL, _login, _password);
GetMethod testMethod = null;
try {
testMethod = http.getVersion();
_returnCode = http.execute(testMethod);
Pair<Integer, JSONObject> response = http.getVersion();
_returnCode = response.first;
} catch (IllegalArgumentException e) {
Log.w(TAG, "Failed to getVersion, IllegalArgumentException occured: " + e.getMessage());
_returnCode = 597;
} catch (IOException e) {
Log.w(TAG, "Failed to login, IOException occured: " + e.getMessage());
} catch (OCSyncException e) {
Log.w(TAG, "Failed to login, OCSyncException occured: " + e.getMessage());
_returnCode = 599;
}
if (testMethod != null)
testMethod.releaseConnection();
return (_returnCode == 200);
}

View File

@ -20,31 +20,30 @@ 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.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.HttpVersion;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
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.URL;
import java.nio.charset.Charset;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.enums.OCSyncErrorType;
import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
import fr.unix_experience.owncloud_sms.providers.AndroidVersionProvider;
public class OCHttpClient extends HttpClient {
public class OCHttpClient {
static {
System.loadLibrary("nativesms");
}
@ -56,7 +55,8 @@ public class OCHttpClient extends HttpClient {
private static final String TAG = OCHttpClient.class.getCanonicalName();
private static final String PARAM_PROTOCOL_VERSION = "http.protocol.version";
private final URL _serverURI;
private final URL _url;
private final String _userAgent;
private final String _username;
private final String _password;
@ -67,92 +67,147 @@ public class OCHttpClient extends HttpClient {
private static final String OC_V2_GET_MESSAGES_SENDQUEUE = "/index.php/apps/ocsms/api/v2/messages/sendqueue?format=json";
public OCHttpClient(Context context, URL serverURL, String accountName, String accountPassword) {
super(new MultiThreadedHttpConnectionManager());
Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory)new EasySSLProtocolSocketFactory(), 443);
Protocol.registerProtocol("https", easyhttps);
_serverURI = serverURL;
_url = serverURL;
_username = accountName;
_password = accountPassword;
getParams().setParameter(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true);
getParams().setParameter(OCHttpClient.PARAM_PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
getParams().setParameter(HttpMethodParams.USER_AGENT,
"nextcloud-phonesync (" + new AndroidVersionProvider(context).getVersionCode() + ")");
_userAgent = "nextcloud-phonesync (" + new AndroidVersionProvider(context).getVersionCode() + ")";
}
private GetMethod get(String oc_call) {
Log.i(OCHttpClient.TAG, "Create GET " + _serverURI + oc_call);
return new GetMethod(_serverURI.toString() + oc_call);
}
GetMethod getAllSmsIds() {
return get(OCHttpClient.getAllSmsIdsCall());
}
public GetMethod getVersion() {
return get(OCHttpClient.getVersionCall());
}
PostMethod pushSms(StringRequestEntity ent) {
PostMethod post = new PostMethod(_serverURI.toString() + OCHttpClient.getPushRoute());
post.setRequestEntity(ent);
return post;
}
GetMethod getPhoneList() {
return get(OCHttpClient.OC_V2_GET_PHONELIST);
}
GetMethod getMessages(Long start, Integer limit) {
return get(OCHttpClient.OC_V2_GET_MESSAGES.
replace("[START]", start.toString()).replace("[LIMIT]", limit.toString()));
}
private int followRedirections(HttpMethod httpMethod) throws IOException {
int redirectionsCount = 0;
int status = httpMethod.getStatusCode();
while ((redirectionsCount < 3) &&
((status == HttpStatus.SC_MOVED_PERMANENTLY) ||
(status == HttpStatus.SC_MOVED_TEMPORARILY) ||
(status == HttpStatus.SC_TEMPORARY_REDIRECT))
) {
Header location = httpMethod.getResponseHeader("Location");
if (location == null) {
location = httpMethod.getResponseHeader("location");
}
if (location == null) {
Log.e(OCHttpClient.TAG, "No valid location header found when redirecting.");
return 500;
}
try {
httpMethod.setURI(new URI(location.getValue()));
} catch (URIException e) {
Log.e(OCHttpClient.TAG, "Invalid URI in 302 FOUND response");
return 500;
}
status = executeMethod(httpMethod);
redirectionsCount++;
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);
}
if (((redirectionsCount >= 3) && (status == HttpStatus.SC_MOVED_PERMANENTLY)) ||
(status == HttpStatus.SC_MOVED_TEMPORARILY) ||
(status == HttpStatus.SC_TEMPORARY_REDIRECT)) {
Log.e(OCHttpClient.TAG, "Too many redirection done. Aborting, please ensure your server is " +
"correctly configured");
return 400;
}
return status;
return new Pair<>(0, null);
}
public int execute(HttpMethod req) throws IOException {
String basicAuth = "Basic " +
Base64.encodeToString((_username + ":" + _password).getBytes(), Base64.NO_WRAP);
req.setDoAuthentication(true);
req.addRequestHeader("Authorization", basicAuth);
executeMethod(req);
return followRedirections(req);
private Pair<Integer, JSONObject> post(String oc_call, String data) throws OCSyncException {
Log.i(OCHttpClient.TAG, "Perform GET " + _url + oc_call);
try {
return execute("POST",
new URL(_url.toString() + oc_call), data, false);
} catch (MalformedURLException e) {
Log.e(OCHttpClient.TAG, "Malformed URL provided, aborting. URL was: "
+ _url.toExternalForm() + oc_call);
}
return new Pair<>(0, null);
}
Pair<Integer, JSONObject> getAllSmsIds() throws OCSyncException {
return get(OCHttpClient.getAllSmsIdsCall(), false);
}
public Pair<Integer, JSONObject> getVersion() throws OCSyncException {
return get(OCHttpClient.getVersionCall(), true);
}
Pair<Integer, JSONObject> pushSms(String smsBuf) throws OCSyncException {
return post(OCHttpClient.getPushRoute(), smsBuf);
}
Pair<Integer, JSONObject> getPhoneList() throws OCSyncException {
return get(OCHttpClient.OC_V2_GET_PHONELIST, true);
}
Pair<Integer, JSONObject> getMessages(Long start, Integer limit) throws OCSyncException {
return get(OCHttpClient.OC_V2_GET_MESSAGES
.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();
urlConnection.setRequestMethod(method);
urlConnection.setRequestProperty("User-Agent", _userAgent);
urlConnection.setInstanceFollowRedirects(true);
urlConnection.setDoOutput(true);
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Accept", "application/json");
String basicAuth = "Basic " +
Base64.encodeToString((_username + ":" + _password).getBytes(), Base64.NO_WRAP);
urlConnection.setRequestProperty("Authorization", basicAuth);
urlConnection.setChunkedStreamingMode(0);
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
out.write(requestBody.getBytes(Charset.forName("UTF-8")));
response = handleHTTPResponse(urlConnection, skipError);
} catch (IOException e) {
throw new OCSyncException(R.string.err_sync_http_request_ioexception, OCSyncErrorType.IO);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return response;
}
private Pair<Integer, JSONObject> handleHTTPResponse(HttpURLConnection connection, Boolean skipError) throws OCSyncException {
BufferedReader reader;
String response;
try {
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line).append("\n");
}
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);
}
}
}
catch (IOException e) {
throw new OCSyncException(R.string.err_sync_http_request_ioexception, OCSyncErrorType.IO);
}
return new Pair<>(0, null);
}
}

View File

@ -21,18 +21,12 @@ import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.util.Log;
import android.util.Pair;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URL;
@ -69,14 +63,14 @@ public class OCSMSOwnCloudClient {
}
public Integer getServerAPIVersion() throws OCSyncException {
doHttpRequest(_http.getVersion(), true);
if (_jsonQueryBuffer == null) {
Pair<Integer, JSONObject> response = _http.getVersion();
if (response.second == null) {
// Return default version
return 1;
}
try {
_serverAPIVersion = _jsonQueryBuffer.getInt("version");
_serverAPIVersion = response.second.getInt("version");
}
catch (JSONException e) {
Log.e(OCSMSOwnCloudClient.TAG, "No version received from server, assuming version 1", e);
@ -87,13 +81,13 @@ public class OCSMSOwnCloudClient {
}
JSONArray getServerPhoneNumbers() throws OCSyncException {
doHttpRequest(_http.getPhoneList(), true);
if (_jsonQueryBuffer == null) {
Pair<Integer, JSONObject> response = _http.getPhoneList();
if (response.second == null) {
return null;
}
try {
return _jsonQueryBuffer.getJSONArray("phoneList");
return response.second.getJSONArray("phoneList");
} catch (JSONException e) {
Log.e(OCSMSOwnCloudClient.TAG, "No phonelist received from server, empty it", e);
return null;
@ -110,52 +104,10 @@ public class OCSMSOwnCloudClient {
}
}
private AndroidSmsFetcher collectMessages(SmsBuffer smsBuffer) throws OCSyncException {
JSONObject smsBoxes = new JSONObject();
JSONArray inboxSmsList = null, sentSmsList = null, draftsSmsList = null;
try {
smsBoxes = _jsonQueryBuffer.getJSONObject("smslist");
} catch (JSONException e) {
try {
_jsonQueryBuffer.getJSONArray("smslist");
} catch (JSONException e2) {
Log.e(OCSMSOwnCloudClient.TAG, "Invalid datas received from server (doPushRequest, get SMS list)", e);
throw new OCSyncException(R.string.err_sync_get_smslist, OCSyncErrorType.PARSE);
}
}
try {
inboxSmsList = smsBoxes.getJSONArray("inbox");
} catch (JSONException e) {
Log.i(OCSMSOwnCloudClient.TAG, "No inbox Sms received from server (doPushRequest, get SMS list)");
}
try {
sentSmsList = smsBoxes.getJSONArray("sent");
} catch (JSONException e) {
Log.i(OCSMSOwnCloudClient.TAG, "No sent Sms received from server (doPushRequest, get SMS list)");
}
try {
draftsSmsList = smsBoxes.getJSONArray("drafts");
} catch (JSONException e) {
Log.i(OCSMSOwnCloudClient.TAG, "No drafts Sms received from server (doPushRequest, get SMS list)");
}
AndroidSmsFetcher fetcher = new AndroidSmsFetcher(_context);
fetcher.setExistingInboxMessages(inboxSmsList);
fetcher.setExistingSentMessages(sentSmsList);
fetcher.setExistingDraftsMessages(draftsSmsList);
fetcher.fetchAllMessages(smsBuffer);
return fetcher;
}
private void doPushRequestV1(SmsBuffer smsBuffer) throws OCSyncException {
if (smsBuffer == null) {
doHttpRequest(_http.getAllSmsIds());
if (_jsonQueryBuffer == null) {
Pair<Integer, JSONObject> response = _http.getAllSmsIds();
if (response.second == null) {
return;
}
@ -168,15 +120,9 @@ public class OCSMSOwnCloudClient {
return;
}
PostMethod post = createPushRequest(smsBuffer);
if (post == null) {
Log.e(OCSMSOwnCloudClient.TAG,"Push request for POST is null");
throw new OCSyncException(R.string.err_sync_craft_http_request, OCSyncErrorType.IO);
}
Pair<Integer, JSONObject> response = _http.pushSms(smsBuffer.asRawJsonString());
doHttpRequest(post);
if (_jsonQueryBuffer == null) {
if (response.second == null) {
Log.e(OCSMSOwnCloudClient.TAG,"Request failed. It doesn't return a valid JSON Object");
throw new OCSyncException(R.string.err_sync_push_request, OCSyncErrorType.IO);
}
@ -184,8 +130,8 @@ public class OCSMSOwnCloudClient {
Boolean pushStatus;
String pushMessage;
try {
pushStatus = _jsonQueryBuffer.getBoolean("status");
pushMessage = _jsonQueryBuffer.getString("msg");
pushStatus = response.second.getBoolean("status");
pushMessage = response.second.getString("msg");
}
catch (JSONException e) {
Log.e(OCSMSOwnCloudClient.TAG, "Invalid datas received from server", e);
@ -199,26 +145,6 @@ public class OCSMSOwnCloudClient {
Log.i(OCSMSOwnCloudClient.TAG, "LastMessageDate set to: " + smsBuffer.getLastMessageDate());
}
private PostMethod createPushRequest(SmsBuffer smsBuffer) throws OCSyncException {
return _http.pushSms(createJSONRequestEntity(smsBuffer));
}
private StringRequestEntity createJSONRequestEntity(SmsBuffer smsBuffer) throws OCSyncException {
StringRequestEntity requestEntity;
try {
requestEntity = new StringRequestEntity(
smsBuffer.asRawJsonString(),
"application/json",
"UTF-8");
} catch (UnsupportedEncodingException e) {
Log.e(OCSMSOwnCloudClient.TAG,"Unsupported encoding when generating request");
throw new OCSyncException(R.string.err_sync_create_json_request_encoding, OCSyncErrorType.PARSE);
}
return requestEntity;
}
JSONObject retrieveSomeMessages(Long start, Integer limit) {
// This is not allowed by server
if (limit > OCSMSOwnCloudClient.SERVER_RECOVERY_MSG_LIMIT) {
@ -226,132 +152,29 @@ public class OCSMSOwnCloudClient {
return null;
}
Pair<Integer, JSONObject> response;
try {
doHttpRequest(_http.getMessages(start, limit));
response = _http.getMessages(start, limit);
} catch (OCSyncException e) {
_jsonQueryBuffer = null;
Log.e(OCSMSOwnCloudClient.TAG, "Request failed.");
return null;
}
if (!_jsonQueryBuffer.has("messages") || !_jsonQueryBuffer.has("last_id")) {
if ((response.second == null) || !response.second.has("messages")
|| !response.second.has("last_id")) {
Log.e(OCSMSOwnCloudClient.TAG,
"Invalid response received from server, either messages or last_id field is missing.");
return null;
}
return _jsonQueryBuffer;
return response.second;
}
private void doHttpRequest(HttpMethod req) throws OCSyncException {
doHttpRequest(req, false);
}
// skipError permit to skip invalid JSON datas
private void doHttpRequest(HttpMethod req, Boolean skipError) throws OCSyncException {
// Reinit the queryBuffer
_jsonQueryBuffer = null;
int status = 0;
// We try maximumHttpReqTries because sometimes network is slow or unstable
int tryNb = 0;
while (tryNb < OCSMSOwnCloudClient.maximumHttpReqTries) {
tryNb++;
if (!_connectivityMonitor.isValid()) {
if (tryNb == OCSMSOwnCloudClient.maximumHttpReqTries) {
req.releaseConnection();
throw new OCSyncException(R.string.err_sync_no_connection_available, OCSyncErrorType.IO);
}
continue;
}
try {
status = _http.execute(req);
Log.i(OCSMSOwnCloudClient.TAG, "HTTP Request done at try " + tryNb);
// Force loop exit
tryNb = OCSMSOwnCloudClient.maximumHttpReqTries;
} catch (ConnectException | HttpException e) {
Log.e(OCSMSOwnCloudClient.TAG, "Unable to perform a connection to ownCloud instance", e);
// If it's the last try
if (tryNb == OCSMSOwnCloudClient.maximumHttpReqTries) {
req.releaseConnection();
throw new OCSyncException(R.string.err_sync_http_request_connect, OCSyncErrorType.IO);
}
} catch (IOException e) {
Log.e(OCSMSOwnCloudClient.TAG, "Unable to perform a connection to ownCloud instance", e);
// If it's the last try
if (tryNb == OCSMSOwnCloudClient.maximumHttpReqTries) {
req.releaseConnection();
throw new OCSyncException(R.string.err_sync_http_request_ioexception, OCSyncErrorType.IO);
}
}
}
handleHTTPResponse(req, status, skipError);
}
private void handleHTTPResponse(HttpMethod req, int status, Boolean skipError) throws OCSyncException {
switch (status) {
case 200: {
String response = getResponseBody(req);
// Parse the response
try {
_jsonQueryBuffer = new JSONObject(response);
} catch (JSONException e) {
if (!skipError) {
if (response.contains("ownCloud") && response.contains("DOCTYPE")) {
Log.e(OCSMSOwnCloudClient.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(OCSMSOwnCloudClient.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
String response = getResponseBody(req);
Log.e(OCSMSOwnCloudClient.TAG, "Server set unhandled HTTP return code " + status);
if (response != null) {
Log.e(OCSMSOwnCloudClient.TAG, "Status code: " + status + ". Response message: " + response);
} else {
Log.e(OCSMSOwnCloudClient.TAG, "Status code: " + status);
}
throw new OCSyncException(R.string.err_sync_http_request_returncode_unhandled, OCSyncErrorType.SERVER_ERROR);
}
}
}
private String getResponseBody(HttpMethod req) throws OCSyncException {
try {
return req.getResponseBodyAsString();
} catch (IOException e) {
Log.e(OCSMSOwnCloudClient.TAG, "Unable to parse server response", e);
throw new OCSyncException(R.string.err_sync_http_request_resp, OCSyncErrorType.IO);
}
}
private static final int maximumHttpReqTries = 3;
private final OCHttpClient _http;
private final Context _context;
private final ConnectivityMonitor _connectivityMonitor;
private Integer _serverAPIVersion;
private JSONObject _jsonQueryBuffer;
private static final String TAG = OCSMSOwnCloudClient.class.getSimpleName();