diff options
Diffstat (limited to 'main/src/cgeo/geocaching/connector')
| -rw-r--r-- | main/src/cgeo/geocaching/connector/ConnectorFactory.java | 2 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/connector/ec/ECApi.java | 172 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/connector/ec/ECConnector.java | 183 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/connector/ec/ECConstants.java | 19 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/connector/ec/ECGPXParser.java | 10 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/connector/ec/ECLogin.java | 184 |
6 files changed, 570 insertions, 0 deletions
diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java index 28974eb..5c5d9db 100644 --- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java +++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java @@ -8,6 +8,7 @@ import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.connector.capability.ISearchByCenter; import cgeo.geocaching.connector.capability.ISearchByKeyword; import cgeo.geocaching.connector.capability.ISearchByViewPort; +import cgeo.geocaching.connector.ec.ECConnector; import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.connector.oc.OCApiConnector; import cgeo.geocaching.connector.oc.OCApiConnector.ApiSupport; @@ -30,6 +31,7 @@ public final class ConnectorFactory { private static final UnknownConnector UNKNOWN_CONNECTOR = new UnknownConnector(); private static final IConnector[] CONNECTORS = new IConnector[] { GCConnector.getInstance(), + ECConnector.getInstance(), new OCApiLiveConnector("opencaching.de", "www.opencaching.de", "OC", "CC BY-NC-ND, alle Logeinträge © jeweiliger Autor", R.string.oc_de_okapi_consumer_key, R.string.oc_de_okapi_consumer_secret, R.string.pref_connectorOCActive, R.string.pref_ocde_tokenpublic, R.string.pref_ocde_tokensecret, ApiSupport.current), diff --git a/main/src/cgeo/geocaching/connector/ec/ECApi.java b/main/src/cgeo/geocaching/connector/ec/ECApi.java new file mode 100644 index 0000000..8c1a428 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/ec/ECApi.java @@ -0,0 +1,172 @@ +package cgeo.geocaching.connector.ec; + +import cgeo.geocaching.DataStore; +import cgeo.geocaching.Geocache; +import cgeo.geocaching.enumerations.CacheType; +import cgeo.geocaching.enumerations.LoadFlags.SaveFlag; +import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.list.StoredList; +import cgeo.geocaching.network.Network; +import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.utils.Log; + +import ch.boye.httpclientandroidlib.HttpResponse; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; + +public class ECApi { + + private static final String API_URL = "http://extremcaching.com/exports/api.php"; + + public static String cleanCode(String geocode) { + return geocode.replace("EC", ""); + } + + public static Geocache searchByGeoCode(final String geocode) { + final Parameters params = new Parameters("id", cleanCode(geocode)); + params.add("cgeo", "1"); + final HttpResponse response = apiRequest("http://extremcaching.com/exports/gpx.php", params); + + final Collection<Geocache> caches = importCachesFromGPXResponse(response); + if (CollectionUtils.isNotEmpty(caches)) { + return caches.iterator().next(); + } + return null; + } + + public static Collection<Geocache> searchByBBox(final Viewport viewport) { + + if (viewport.getLatitudeSpan() == 0 || viewport.getLongitudeSpan() == 0) { + return Collections.emptyList(); + } + + final Parameters params = new Parameters("fnc", "bbox"); + params.add("lat1", String.valueOf(viewport.getLatitudeMin())); + params.add("lat2", String.valueOf(viewport.getLatitudeMax())); + params.add("lon1", String.valueOf(viewport.getLongitudeMin())); + params.add("lon2", String.valueOf(viewport.getLongitudeMax())); + final HttpResponse response = apiRequest(params); + + return importCachesFromJSON(response); + } + + + public static Collection<Geocache> searchByCenter(final Geopoint center) { + + final Parameters params = new Parameters("fnc", "center"); + params.add("distance", "20"); + params.add("lat", String.valueOf(center.getLatitude())); + params.add("lon", String.valueOf(center.getLongitude())); + final HttpResponse response = apiRequest(params); + + return importCachesFromJSON(response); + } + + + private static HttpResponse apiRequest(final Parameters params) { + return apiRequest(API_URL, params, false); + } + + private static HttpResponse apiRequest(final String uri, final Parameters params) { + return apiRequest(uri, params, false); + } + + private static HttpResponse apiRequest(final String uri, final Parameters params, final boolean isRetry) { + final HttpResponse response = Network.getRequest(uri, params); + + if (response == null) { + return null; + } + if (!isRetry && response.getStatusLine().getStatusCode() == 403) { + apiRequest(uri, params, true); + } + if (response.getStatusLine().getStatusCode() != 200) { + return null; + } + return response; + } + + private static Collection<Geocache> importCachesFromGPXResponse(final HttpResponse response) { + if (response == null) { + return Collections.emptyList(); + } + Collection<Geocache> caches; + try { + caches = new ECGPXParser(StoredList.TEMPORARY_LIST_ID).parse(response.getEntity().getContent(), null); + } catch (Exception e) { + Log.e("Error importing gpx from extremcaching.com", e); + return Collections.emptyList(); + } + return caches; + } + + private static List<Geocache> importCachesFromJSON(final HttpResponse response) { + + if (response != null) { + try { + final String data = Network.getResponseDataAlways(response); + if (data == null || StringUtils.isBlank(data) || StringUtils.equals(data, "[]")) { + return Collections.emptyList(); + } + final JSONArray json = new JSONArray(data); + final List<Geocache> caches = new ArrayList<Geocache>(json.length()); + for (int i = 0; i < json.length(); i++) { + final Geocache cache = parseCache(json.getJSONObject(i)); + caches.add(cache); + } + return caches; + } catch (final JSONException e) { + Log.w("JSONResult", e); + } + } + + return Collections.emptyList(); + + } + + private static Geocache parseCache(final JSONObject response) { + final Geocache cache = new Geocache(); + cache.setReliableLatLon(true); + try { + cache.setGeocode("EC" + response.getString("cache_id")); + cache.setName(response.getString("title")); + cache.setCoords(new Geopoint(response.getString("lat"), response.getString("lon"))); + cache.setType(getCacheType(response.getString("type"))); + cache.setDifficulty((float) response.getDouble("difficulty")); + cache.setTerrain((float) response.getDouble("terrain")); + cache.setFound(response.getInt("found") == 1 ? true : false); + + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_CACHE)); + } catch (final JSONException e) { + Log.e("ECApi.parseCache", e); + } + return cache; + } + + private static CacheType getCacheType(final String cacheType) { + if (cacheType.equalsIgnoreCase("Tradi")) { + return CacheType.TRADITIONAL; + } + if (cacheType.equalsIgnoreCase("Multi")) { + return CacheType.MULTI; + } + if (cacheType.equalsIgnoreCase("Event")) { + return CacheType.EVENT; + } + if (cacheType.equalsIgnoreCase("Mystery")) { + return CacheType.MYSTERY; + } + return CacheType.UNKNOWN; + } +} diff --git a/main/src/cgeo/geocaching/connector/ec/ECConnector.java b/main/src/cgeo/geocaching/connector/ec/ECConnector.java new file mode 100644 index 0000000..3b6376a --- /dev/null +++ b/main/src/cgeo/geocaching/connector/ec/ECConnector.java @@ -0,0 +1,183 @@ +package cgeo.geocaching.connector.ec; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.Geocache; +import cgeo.geocaching.ICache; +import cgeo.geocaching.R; +import cgeo.geocaching.SearchResult; +import cgeo.geocaching.connector.AbstractConnector; +import cgeo.geocaching.connector.capability.ILogin; +import cgeo.geocaching.connector.capability.ISearchByCenter; +import cgeo.geocaching.connector.capability.ISearchByGeocode; +import cgeo.geocaching.connector.capability.ISearchByViewPort; +import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.settings.SettingsActivity; +import cgeo.geocaching.utils.CancellableHandler; + +import org.apache.commons.lang3.StringUtils; + +import android.content.Context; +import android.os.Handler; + +import java.util.Collection; +import java.util.regex.Pattern; + +public class ECConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ILogin { + + private static final String CACHE_URL = "http://extremcaching.com/index.php/output-2/"; + + /** + * Pattern for EC codes + */ + private final static Pattern PATTERN_EC_CODE = Pattern.compile("EC[0-9]+", Pattern.CASE_INSENSITIVE); + + + private ECConnector() { + // singleton + } + + /** + * initialization on demand holder pattern + */ + private static class Holder { + private static final ECConnector INSTANCE = new ECConnector(); + } + + public static ECConnector getInstance() { + return Holder.INSTANCE; + } + + @Override + public boolean canHandle(String geocode) { + if (geocode == null) { + return false; + } + return ECConnector.PATTERN_EC_CODE.matcher(geocode).matches(); + } + + @Override + public String getCacheUrl(Geocache cache) { + return CACHE_URL + cache.getGeocode().replace("EC", ""); + } + + @Override + public String getName() { + return "extremcaching.com"; + } + + @Override + public String getHost() { + return "extremcaching.com"; + } + + @Override + public SearchResult searchByGeocode(final String geocode, final String guid, final CancellableHandler handler) { + + CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_loadpage); + + final Geocache cache = ECApi.searchByGeoCode(geocode); + if (cache == null) { + return null; + } + final SearchResult searchResult = new SearchResult(cache); + + return searchResult; + } + + @Override + public SearchResult searchByViewport(Viewport viewport, String[] tokens) { + final Collection<Geocache> caches = ECApi.searchByBBox(viewport); + if (caches == null) { + return null; + } + final SearchResult searchResult = new SearchResult(caches); + return searchResult.filterSearchResults(false, false, Settings.getCacheType()); + } + + @Override + public SearchResult searchByCenter(Geopoint center) { + final Collection<Geocache> caches = ECApi.searchByCenter(center); + if (caches == null) { + return null; + } + final SearchResult searchResult = new SearchResult(caches); + return searchResult.filterSearchResults(false, false, Settings.getCacheType()); + } + + @Override + public boolean isOwner(final ICache cache) { + //return StringUtils.equalsIgnoreCase(cache.getOwnerUserId(), Settings.getUsername()); + return false; + } + + @Override + protected String getCacheUrlPrefix() { + return CACHE_URL; + } + + @Override + public boolean isActivated() { + return Settings.isECConnectorActive(); + } + + @Override + public boolean login(Handler handler, Context fromActivity) { + // login + final StatusCode status = ECLogin.login(); + + if (status == StatusCode.NO_ERROR) { + CgeoApplication.getInstance().checkLogin = false; + } + + if (CgeoApplication.getInstance().showLoginToast && handler != null) { + handler.sendMessage(handler.obtainMessage(0, status)); + CgeoApplication.getInstance().showLoginToast = false; + + // invoke settings activity to insert login details + if (status == StatusCode.NO_LOGIN_INFO_STORED && fromActivity != null) { + SettingsActivity.jumpToServicesPage(fromActivity); + } + } + return status == StatusCode.NO_ERROR; + } + + @Override + public String getUserName() { + return ECLogin.getActualUserName(); + } + + @Override + public int getCachesFound() { + return ECLogin.getActualCachesFound(); + } + + @Override + public String getLoginStatusString() { + return ECLogin.getActualStatus(); + } + + @Override + public boolean isLoggedIn() { + return ECLogin.isActualLoginStatus(); + } + + @Override + public int getCacheMapMarkerId(boolean disabled) { + final String icons = Settings.getECIconSet(); + if (StringUtils.equals(icons, "1")) { + if (disabled) { + return R.drawable.marker_disabled_other; + } + return R.drawable.marker_other; + } + + if (disabled) { + return R.drawable.marker_disabled_oc; + } + return R.drawable.marker_oc; + } + +} diff --git a/main/src/cgeo/geocaching/connector/ec/ECConstants.java b/main/src/cgeo/geocaching/connector/ec/ECConstants.java new file mode 100644 index 0000000..9b7353b --- /dev/null +++ b/main/src/cgeo/geocaching/connector/ec/ECConstants.java @@ -0,0 +1,19 @@ +package cgeo.geocaching.connector.ec; + +import java.util.regex.Pattern; + +/** + * For further information about patterns have a look at + * http://download.oracle.com/javase/1.4.2/docs/api/java/util/regex/Pattern.html + */ +public final class ECConstants { + + public static final Pattern PATTERN_LOGIN_NAME = Pattern.compile("\"mod_login_greetingfrontpage-teaser\">Hallo, ([^<]+)</span>"); + public static final Pattern PATTERN_LOGIN_SECURITY = Pattern.compile("<input type=\"hidden\" name=\"return\" value=\"(.*)\" />[^<]*<input type=\"hidden\" name=\"(.*)\" value=\"1\" />"); + public static final Pattern PATTERN_CACHES_FOUND = Pattern.compile("Gefundene Caches::</label><div class=\"cb_field\"><div id=\"cbfv_71\">([0-9]+)</div>"); + + private ECConstants() { + // this class shall not have instances + } + +} diff --git a/main/src/cgeo/geocaching/connector/ec/ECGPXParser.java b/main/src/cgeo/geocaching/connector/ec/ECGPXParser.java new file mode 100644 index 0000000..f2ffb5e --- /dev/null +++ b/main/src/cgeo/geocaching/connector/ec/ECGPXParser.java @@ -0,0 +1,10 @@ +package cgeo.geocaching.connector.ec; + +import cgeo.geocaching.files.GPX10Parser; + +public class ECGPXParser extends GPX10Parser { + + public ECGPXParser(int listIdIn) { + super(listIdIn); + } +} diff --git a/main/src/cgeo/geocaching/connector/ec/ECLogin.java b/main/src/cgeo/geocaching/connector/ec/ECLogin.java new file mode 100644 index 0000000..8eaa8ee --- /dev/null +++ b/main/src/cgeo/geocaching/connector/ec/ECLogin.java @@ -0,0 +1,184 @@ +package cgeo.geocaching.connector.ec; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.R; +import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.network.Cookies; +import cgeo.geocaching.network.Network; +import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.TextUtils; + +import ch.boye.httpclientandroidlib.HttpResponse; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.Nullable; + +import java.util.regex.Matcher; + +public abstract class ECLogin { + + // false = not logged in + private static boolean actualLoginStatus = false; + private static String actualUserName = StringUtils.EMPTY; + private static int actualCachesFound = -1; + private static String actualStatus = StringUtils.EMPTY; + + + public static StatusCode login() { + return login(true); + } + + private static StatusCode login(boolean retry) { + final ImmutablePair<String, String> login = Settings.getECLogin(); + + if (StringUtils.isEmpty(login.left) || StringUtils.isEmpty(login.right)) { + clearLoginInfo(); + Log.e("ECLogin.login: No login information stored"); + return StatusCode.NO_LOGIN_INFO_STORED; + } + + ECLogin.setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_working)); + //HttpResponse loginResponse = Network.getRequest("http://extremcaching.com/component/users/?view=login"); + HttpResponse loginResponse = Network.getRequest("https://extremcaching.com/community/profil1"); + String loginData = Network.getResponseData(loginResponse); + + if (StringUtils.isBlank(loginData)) { + Log.e("ECLogin.login: Failed to retrieve login page (1st)"); + return StatusCode.CONNECTION_FAILED_EC; // no login page + } + + if (ECLogin.getLoginStatus(loginData)) { + Log.i("Already logged in Extremcaching.com as " + login.left); + return StatusCode.NO_ERROR; // logged in + } + + //Cookies.clearCookies(); + //Settings.setCookieStore(null); + + final Parameters params = new Parameters( + "username", login.left, + "password", login.right, + "remember", "yes"); + + Matcher m = ECConstants.PATTERN_LOGIN_SECURITY.matcher(loginData); + if (m.find() && m.groupCount() == 2) { + params.add("return", m.group(1)); + params.add(m.group(2), "1"); + } else { + Log.e("ECLogin.login security tokens in login form not found"); + return StatusCode.COMMUNICATION_ERROR; + } + + loginResponse = Network.postRequest("http://extremcaching.com/component/users/?task=user.login", params); + loginData = Network.getResponseData(loginResponse); + + if (StringUtils.isBlank(loginData)) { + Log.e("ECLogin.login: Failed to retrieve login page (2nd)"); + return StatusCode.COMMUNICATION_ERROR; // no login page + } + assert loginData != null; // Caught above + + if (ECLogin.getLoginStatus(loginData)) { + Log.i("Successfully logged in Extremcaching.com as " + login.left); + + Settings.setCookieStore(Cookies.dumpCookieStore()); + + return StatusCode.NO_ERROR; // logged in + } + + if (loginData.contains("Benutzername und Passwort falsch")) { + Log.i("Failed to log in Extremcaching.com as " + login.left + " because of wrong username/password"); + return StatusCode.WRONG_LOGIN_DATA; // wrong login + } + + Log.i("Failed to log in Extremcaching.com as " + login.left + " for some unknown reason"); + if (retry) { + return login(false); + } + + return StatusCode.UNKNOWN_ERROR; // can't login + } + + private static void resetLoginStatus() { + Cookies.clearCookies(); + Settings.setCookieStore(null); + + setActualLoginStatus(false); + } + + private static void clearLoginInfo() { + resetLoginStatus(); + + setActualCachesFound(-1); + setActualStatus(CgeoApplication.getInstance().getString(R.string.err_login)); + } + + static void setActualCachesFound(final int found) { + actualCachesFound = found; + } + + public static String getActualStatus() { + return actualStatus; + } + + private static void setActualStatus(final String status) { + actualStatus = status; + } + + public static boolean isActualLoginStatus() { + return actualLoginStatus; + } + + private static void setActualLoginStatus(boolean loginStatus) { + actualLoginStatus = loginStatus; + } + + public static String getActualUserName() { + return actualUserName; + } + + private static void setActualUserName(String userName) { + actualUserName = userName; + } + + public static int getActualCachesFound() { + return actualCachesFound; + } + + /** + * Check if the user has been logged in when he retrieved the data. + * + * @param page + * @return <code>true</code> if user is logged in, <code>false</code> otherwise + */ + public static boolean getLoginStatus(@Nullable final String page) { + if (StringUtils.isBlank(page)) { + Log.e("ECLogin.getLoginStatus: No page given"); + return false; + } + assert page != null; + + setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_ok)); + + // on every page except login page + setActualLoginStatus(TextUtils.matches(page, ECConstants.PATTERN_LOGIN_NAME)); + if (isActualLoginStatus()) { + setActualUserName(TextUtils.getMatch(page, ECConstants.PATTERN_LOGIN_NAME, true, "???")); + int cachesCount = 0; + try { + cachesCount = Integer.parseInt(TextUtils.getMatch(page, ECConstants.PATTERN_CACHES_FOUND, true, "0")); + } catch (final NumberFormatException e) { + Log.e("ECLogin.getLoginStatus: bad cache count", e); + } + setActualCachesFound(cachesCount); + return true; + } + + setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_failed)); + return false; + } + +} |
