diff options
| author | rsudev <rasch@munin-soft.de> | 2013-10-04 08:31:29 +0200 |
|---|---|---|
| committer | rsudev <rasch@munin-soft.de> | 2013-10-14 09:38:02 +0200 |
| commit | dfc984f715b443efca23615ae0dd763037bddc0f (patch) | |
| tree | bd94a4a3ddd38beaee1d2818d2543af8a4ca41fb /main/src | |
| parent | 27a6bfe43a395c5e86981c8c76763a6e52e8d3aa (diff) | |
| download | cgeo-dfc984f715b443efca23615ae0dd763037bddc0f.zip cgeo-dfc984f715b443efca23615ae0dd763037bddc0f.tar.gz cgeo-dfc984f715b443efca23615ae0dd763037bddc0f.tar.bz2 | |
Implements part of #3267, Detect and report time-skew that prevents okapi access
Reports back issues on the main screen, but not yet during the authorization process
Diffstat (limited to 'main/src')
4 files changed, 155 insertions, 17 deletions
diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java index e3cf1ca..686e314 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java @@ -31,9 +31,13 @@ import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.SynchronizedDateFormat; +import ch.boye.httpclientandroidlib.HttpResponse; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.FastDateFormat; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -129,13 +133,9 @@ final class OkapiClient { params.add("fields", getFullFields(ocapiConn)); params.add("attribution_append", "none"); - final JSONObject data = request(ocapiConn, OkapiService.SERVICE_CACHE, params); - - if (data == null) { - return null; - } + final JSONResult result = request(ocapiConn, OkapiService.SERVICE_CACHE, params); - return parseCache(data); + return result.isSuccess ? parseCache(result.data) : null; } public static List<Geocache> getCachesAround(final Geopoint center, final OCApiConnector connector) { @@ -180,7 +180,7 @@ final class OkapiClient { params.add("search_params", new JSONObject(valueMap).toString()); addRetrieveParams(params, connector); - final JSONObject data = request(connector, OkapiService.SERVICE_SEARCH_AND_RETRIEVE, params); + final JSONObject data = request(connector, OkapiService.SERVICE_SEARCH_AND_RETRIEVE, params).data; if (data == null) { return Collections.emptyList(); @@ -213,7 +213,7 @@ final class OkapiClient { final Parameters params = new Parameters("cache_code", cache.getGeocode()); params.add("watched", watched ? "true" : "false"); - final JSONObject data = request(connector, OkapiService.SERVICE_MARK_CACHE, params); + final JSONObject data = request(connector, OkapiService.SERVICE_MARK_CACHE, params).data; if (data == null) { return false; @@ -237,7 +237,7 @@ final class OkapiClient { params.add("password", logPassword); } - final JSONObject data = request(connector, OkapiService.SERVICE_SUBMIT_LOG, params); + final JSONObject data = request(connector, OkapiService.SERVICE_SUBMIT_LOG, params).data; if (data == null) { return new LogResult(StatusCode.LOG_POST_ERROR, ""); @@ -623,14 +623,15 @@ final class OkapiClient { return res.toString(); } - private static JSONObject request(final OCApiConnector connector, final OkapiService service, final Parameters params) { + @NonNull + private static JSONResult request(final OCApiConnector connector, final OkapiService service, final Parameters params) { if (connector == null) { - return null; + return new JSONResult(null); } final String host = connector.getHost(); if (StringUtils.isBlank(host)) { - return null; + return new JSONResult(null); } params.add("langpref", getPreferredLanguage()); @@ -643,7 +644,7 @@ final class OkapiClient { } final String uri = "http://" + host + service.methodName; - return Network.requestJSON(uri, params); + return new JSONResult(Network.getRequest(uri, params)); } private static String getPreferredLanguage() { @@ -695,12 +696,16 @@ final class OkapiClient { public static UserInfo getUserInfo(final OCApiLiveConnector connector) { final Parameters params = new Parameters("fields", USER_INFO_FIELDS); - final JSONObject data = request(connector, OkapiService.SERVICE_USER, params); + final JSONResult result = request(connector, OkapiService.SERVICE_USER, params); - if (data == null) { - return new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.FAILED); + if (!result.isSuccess) { + final OkapiError error = new OkapiError(result.data); + Log.e("OkapiClient.getUserInfo: error getting user info: '" + error.getMessage() + "'"); + return new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.getFromOkapiError(error.getResult())); } + JSONObject data = result.data; + String name = StringUtils.EMPTY; boolean successUserName = false; @@ -728,4 +733,28 @@ final class OkapiClient { return new UserInfo(name, finds, successUserName && successFinds ? UserInfoStatus.SUCCESSFUL : UserInfoStatus.FAILED); } + /** + * Encapsulates response state and content of an HTTP-request that expects a JSON result. <code>isSuccess</code> is + * only true, if the response state was success and <code>data</code> is not null. + */ + private static class JSONResult { + + public final boolean isSuccess; + public final JSONObject data; + + public JSONResult(final @Nullable HttpResponse response) { + boolean isSuccess = Network.isSuccess(response); + final String responseData = Network.getResponseDataAlways(response); + JSONObject data = null; + if (responseData != null) { + try { + data = new JSONObject(responseData); + } catch (final JSONException e) { + Log.w("JSONResult", e); + } + } + this.data = data; + this.isSuccess = isSuccess && data != null; + } + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiError.java b/main/src/cgeo/geocaching/connector/oc/OkapiError.java new file mode 100644 index 0000000..7faf2c7 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/oc/OkapiError.java @@ -0,0 +1,87 @@ +package cgeo.geocaching.connector.oc; + +import cgeo.geocaching.utils.Log; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Handles the JSON error response from OKAPI + */ +public class OkapiError { + + /** + * List of detected errors OKAPI might return + */ + public enum OkapiErrors { + NO_ERROR, + UNSPECIFIED, + INVALID_TIMESTAMP, + INVALID_TOKEN; + } + + @NonNull private final OkapiErrors state; + @NonNull private final String message; + + public OkapiError(@Nullable JSONObject data) { + + // A null-response is by definition an error (some exception occurred somewhere in the flow) + if (data == null) { + state = OkapiErrors.UNSPECIFIED; + message = StringUtils.EMPTY; + return; + } + // Second possibility: we get an error object as return (@see http://opencaching.pl/okapi/introduction.html#errors) + if (data.has("error")) { + String localmessage = null; + OkapiErrors localstate = OkapiErrors.UNSPECIFIED; + try { + JSONObject error = data.getJSONObject("error"); + // Check reason_stack element to look for the specific oauth problems we want to report back + if (error.has("reason_stack")) { + String reason = error.getString("reason_stack"); + if (StringUtils.contains(reason, "invalid_oauth_request")) { + if (StringUtils.contains(reason, "invalid_timestamp")) { + localstate = OkapiErrors.INVALID_TIMESTAMP; + } else if (StringUtils.contains(reason, "invalid_token")) { + localstate = OkapiErrors.INVALID_TOKEN; + } + } + } + // Check if we can extract a message as well + if (error.has("developer_message")) { + localmessage = error.getString("developer_message"); + assert localmessage != null; // by virtue of defaultString + } + } catch (JSONException ex) { + Log.d("OkapiError: Failed to parse JSON", ex); + localstate = OkapiErrors.UNSPECIFIED; + } + state = localstate; + message = StringUtils.defaultString(localmessage); + return; + } + + // Third possibility: some other response, everything is fine! + state = OkapiErrors.NO_ERROR; + message = StringUtils.EMPTY; + } + + public boolean isError() { + return state != OkapiErrors.NO_ERROR; + } + + @NonNull + public OkapiErrors getResult() { + return state; + } + + @NonNull + public String getMessage() { + return message; + } + +} diff --git a/main/src/cgeo/geocaching/connector/oc/UserInfo.java b/main/src/cgeo/geocaching/connector/oc/UserInfo.java index 0dc0440..c8b37cd 100644 --- a/main/src/cgeo/geocaching/connector/oc/UserInfo.java +++ b/main/src/cgeo/geocaching/connector/oc/UserInfo.java @@ -1,6 +1,7 @@ package cgeo.geocaching.connector.oc; import cgeo.geocaching.R; +import cgeo.geocaching.connector.oc.OkapiError.OkapiErrors; public class UserInfo { @@ -8,13 +9,28 @@ public class UserInfo { NOT_RETRIEVED(R.string.init_login_popup_working), SUCCESSFUL(R.string.init_login_popup_ok), FAILED(R.string.init_login_popup_failed), - NOT_SUPPORTED(R.string.init_login_popup_not_authorized); + NOT_SUPPORTED(R.string.init_login_popup_not_authorized), + INVALID_TIMESTAMP(R.string.init_login_popup_invalid_timestamp), + INVALID_TOKEN(R.string.init_login_popup_invalid_token); public final int resId; UserInfoStatus(int resId) { this.resId = resId; } + + public static UserInfoStatus getFromOkapiError(OkapiErrors result) { + switch (result) { + case NO_ERROR: + return SUCCESSFUL; + case INVALID_TIMESTAMP: + return INVALID_TIMESTAMP; + case INVALID_TOKEN: + return INVALID_TOKEN; + default: + return FAILED; + } + } } private final String name; diff --git a/main/src/cgeo/geocaching/network/Network.java b/main/src/cgeo/geocaching/network/Network.java index 1810216..e891d3b 100644 --- a/main/src/cgeo/geocaching/network/Network.java +++ b/main/src/cgeo/geocaching/network/Network.java @@ -34,6 +34,7 @@ import ch.boye.httpclientandroidlib.params.CoreProtocolPNames; import ch.boye.httpclientandroidlib.params.HttpParams; import ch.boye.httpclientandroidlib.protocol.HttpContext; import ch.boye.httpclientandroidlib.util.EntityUtils; + import org.apache.commons.lang3.CharEncoding; import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.Nullable; @@ -449,6 +450,11 @@ public abstract class Network { return Network.getResponseData(response, true); } + @Nullable + public static String getResponseDataAlways(@Nullable final HttpResponse response) { + return response != null ? getResponseDataNoError(response, false) : null; + } + /** * Get the body of a HTTP response. * |
