// $codepro.audit.disable logExceptions package cgeo.geocaching; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.GCConnector; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.LoadFlags.RemoveFlag; import cgeo.geocaching.enumerations.LoadFlags.SaveFlag; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.LogTypeTrackable; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.files.LocParser; import cgeo.geocaching.gcvote.GCVote; import cgeo.geocaching.gcvote.GCVoteRating; import cgeo.geocaching.geopoint.DistanceParser; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter.Format; import cgeo.geocaching.network.HtmlImage; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.twitter.Twitter; import cgeo.geocaching.ui.DirectionImage; import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.CancellableHandler; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.cookie.Cookie; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.cookie.BasicClientCookie; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.CoreConnectionPNames; import org.apache.http.params.CoreProtocolPNames; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.net.Uri; import android.os.Handler; import android.os.Message; import android.text.Html; import android.text.Spannable; import android.text.Spanned; import android.text.format.DateUtils; import android.text.style.StrikethroughSpan; import android.util.Log; import android.view.LayoutInflater; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.URLDecoder; import java.net.URLEncoder; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; public class cgBase { private static final String passMatch = "(?<=[\\?&])[Pp]ass(w(or)?d)?=[^&#$]+"; private final static Map gcCustomDateFormats; static { final String[] formats = new String[] { "MM/dd/yyyy", "yyyy-MM-dd", "yyyy/MM/dd", "dd/MMM/yyyy", "MMM/dd/yyyy", "dd MMM yy", "dd/MM/yyyy" }; Map map = new HashMap(); for (String format : formats) { map.put(format, new SimpleDateFormat(format, Locale.ENGLISH)); } gcCustomDateFormats = Collections.unmodifiableMap(map); } private final static SimpleDateFormat dateTbIn1 = new SimpleDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009 private final static SimpleDateFormat dateTbIn2 = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009 public static String version = null; private static Context context; private static Resources res; private static final int NB_DOWNLOAD_RETRIES = 4; public static final int UPDATE_LOAD_PROGRESS_DETAIL = 42186; // false = not logged in private static boolean actualLoginStatus = false; private static String actualUserName = ""; private static String actualMemberStatus = ""; private static int actualCachesFound = -1; private static String actualStatus = ""; private cgBase() { //initialize(app); throw new UnsupportedOperationException(); // static class, not to be instantiated } /** * Called from AbstractActivity.onCreate() and AbstractListActivity.onCreate() * * @param app */ public static void initialize(final cgeoapplication app) { context = app.getBaseContext(); res = app.getBaseContext().getResources(); try { final PackageManager manager = app.getPackageManager(); final PackageInfo info = manager.getPackageInfo(app.getPackageName(), 0); version = info.versionName; } catch (PackageManager.NameNotFoundException e) { Log.e(Settings.tag, "unable to get version information", e); version = null; } } public static String hidePassword(final String message) { return message.replaceAll(passMatch, "password=***"); } public static void sendLoadProgressDetail(final Handler handler, final int str) { if (null != handler) { handler.obtainMessage(UPDATE_LOAD_PROGRESS_DETAIL, cgeoapplication.getInstance().getString(str)).sendToTarget(); } } /** * read all viewstates from page * * @return String[] with all view states */ public static String[] getViewstates(String page) { // Get the number of viewstates. // If there is only one viewstate, __VIEWSTATEFIELDCOUNT is not present if (page == null) { // no network access return null; } int count = 1; final Matcher matcherViewstateCount = GCConstants.PATTERN_VIEWSTATEFIELDCOUNT.matcher(page); if (matcherViewstateCount.find()) { count = Integer.parseInt(matcherViewstateCount.group(1)); } String[] viewstates = new String[count]; // Get the viewstates int no; final Matcher matcherViewstates = GCConstants.PATTERN_VIEWSTATES.matcher(page); while (matcherViewstates.find()) { String sno = matcherViewstates.group(1); // number of viewstate if ("".equals(sno)) { no = 0; } else { no = Integer.parseInt(sno); } viewstates[no] = matcherViewstates.group(2); } if (viewstates.length != 1 || viewstates[0] != null) { return viewstates; } // no viewstates were present return null; } /** * put viewstates into request parameters */ private static void putViewstates(final Parameters params, final String[] viewstates) { if (ArrayUtils.isEmpty(viewstates)) { return; } params.put("__VIEWSTATE", viewstates[0]); if (viewstates.length > 1) { for (int i = 1; i < viewstates.length; i++) { params.put("__VIEWSTATE" + i, viewstates[i]); } params.put("__VIEWSTATEFIELDCOUNT", viewstates.length + ""); } } /** * transfers the viewstates variables from a page (response) to parameters * (next request) */ public static void transferViewstates(final String page, final Parameters params) { putViewstates(params, getViewstates(page)); } /** * checks if an Array of Strings is empty or not. Empty means: * - Array is null * - or all elements are null or empty strings */ public static boolean isEmpty(String[] a) { if (a == null) { return true; } for (String s : a) { if (StringUtils.isNotEmpty(s)) { return false; } } return true; } public static StatusCode login() { final ImmutablePair login = Settings.getLogin(); if (login == null || StringUtils.isEmpty(login.left) || StringUtils.isEmpty(login.right)) { cgBase.setActualStatus(res.getString(R.string.err_login)); Log.e(Settings.tag, "cgeoBase.login: No login information stored"); return StatusCode.NO_LOGIN_INFO_STORED; } // res is null during the unit tests if (res != null) { cgBase.setActualStatus(res.getString(R.string.init_login_popup_working)); } HttpResponse loginResponse = request("https://www.geocaching.com/login/default.aspx", null, false, false, false); String loginData = getResponseData(loginResponse); if (loginResponse != null && loginResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) { return StatusCode.MAINTENANCE; } if (StringUtils.isBlank(loginData)) { Log.e(Settings.tag, "cgeoBase.login: Failed to retrieve login page (1st)"); return StatusCode.CONNECTION_FAILED; // no loginpage } if (getLoginStatus(loginData)) { Log.i(Settings.tag, "Already logged in Geocaching.com as " + login.left); switchToEnglish(loginData); return StatusCode.NO_ERROR; // logged in } clearCookies(); Settings.setCookieStore(null); final Parameters params = new Parameters( "__EVENTTARGET", "", "__EVENTARGUMENT", "", "ctl00$ContentBody$tbUsername", login.left, "ctl00$ContentBody$tbPassword", login.right, "ctl00$ContentBody$cbRememberMe", "on", "ctl00$ContentBody$btnSignIn", "Login"); final String[] viewstates = getViewstates(loginData); if (isEmpty(viewstates)) { Log.e(Settings.tag, "cgeoBase.login: Failed to find viewstates"); return StatusCode.LOGIN_PARSE_ERROR; // no viewstates } putViewstates(params, viewstates); loginResponse = postRequest("https://www.geocaching.com/login/default.aspx", params); loginData = getResponseData(loginResponse); if (StringUtils.isNotBlank(loginData)) { if (getLoginStatus(loginData)) { Log.i(Settings.tag, "Successfully logged in Geocaching.com as " + login.left); switchToEnglish(loginData); Settings.setCookieStore(dumpCookieStore()); return StatusCode.NO_ERROR; // logged in } else { if (loginData.contains("Your username/password combination does not match.")) { Log.i(Settings.tag, "Failed to log in Geocaching.com as " + login.left + " because of wrong username/password"); return StatusCode.WRONG_LOGIN_DATA; // wrong login } else { Log.i(Settings.tag, "Failed to log in Geocaching.com as " + login.left + " for some unknown reason"); return StatusCode.UNKNOWN_ERROR; // can't login } } } else { Log.e(Settings.tag, "cgeoBase.login: Failed to retrieve login page (2nd)"); // FIXME: should it be CONNECTION_FAILED to match the first attempt? return StatusCode.COMMUNICATION_ERROR; // no login page } } public static StatusCode logout() { HttpResponse logoutResponse = request("https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fdefault.aspx%3f", null, false, false, false); String logoutData = getResponseData(logoutResponse); if (logoutResponse != null && logoutResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(logoutData, GCConstants.PATTERN_MAINTENANCE)) { return StatusCode.MAINTENANCE; } clearCookies(); Settings.setCookieStore(null); return StatusCode.NO_ERROR; } /** * Check if the user has been logged in when he retrieved the data. * * @param page * @return true if user is logged in, false otherwise */ private static boolean getLoginStatus(final String page) { if (StringUtils.isBlank(page)) { Log.e(Settings.tag, "cgeoBase.checkLogin: No page given"); return false; } // res is null during the unit tests if (res != null) { setActualStatus(res.getString(R.string.init_login_popup_ok)); } // on every page except login page setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME)); if (isActualLoginStatus()) { setActualUserName(BaseUtils.getMatch(page, GCConstants.PATTERN_LOGIN_NAME, true, "???")); setActualMemberStatus(BaseUtils.getMatch(page, GCConstants.PATTERN_MEMBER_STATUS, true, "???")); setActualCachesFound(Integer.parseInt(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0").replaceAll("[,.]", ""))); return true; } // login page setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME_LOGIN_PAGE)); if (isActualLoginStatus()) { setActualUserName(Settings.getUsername()); setActualMemberStatus(Settings.getMemberStatus()); // number of caches found is not part of this page return true; } // res is null during the unit tests if (res != null) { setActualStatus(res.getString(R.string.init_login_popup_failed)); } return false; } public static void switchToEnglish(String previousPage) { final String ENGLISH = "English▼"; if (previousPage != null && previousPage.indexOf(ENGLISH) >= 0) { Log.i(Settings.tag, "Geocaching.com language already set to English"); // get find count getLoginStatus(getResponseData(request("http://www.geocaching.com/email/", null, false))); } else { final String page = getResponseData(request("http://www.geocaching.com/default.aspx", null, false)); getLoginStatus(page); if (page == null) { Log.e(Settings.tag, "Failed to read viewstates to set geocaching.com language"); } final Parameters params = new Parameters( "__EVENTTARGET", "ctl00$uxLocaleList$uxLocaleList$ctl00$uxLocaleItem", // switch to english "__EVENTARGUMENT", ""); transferViewstates(page, params); final HttpResponse response = postRequest("http://www.geocaching.com/default.aspx", params); if (!isSuccess(response)) { Log.e(Settings.tag, "Failed to set geocaching.com language to English"); } } } private static SearchResult parseSearch(final cgSearchThread thread, final String url, final String pageContent, final boolean showCaptcha) { if (StringUtils.isBlank(pageContent)) { Log.e(Settings.tag, "cgeoBase.parseSearch: No page given"); return null; } final List cids = new ArrayList(); final List guids = new ArrayList(); String recaptchaChallenge = null; String recaptchaText = null; String page = pageContent; final SearchResult searchResult = new SearchResult(); searchResult.setUrl(url); searchResult.viewstates = getViewstates(page); // recaptcha if (showCaptcha) { String recaptchaJsParam = BaseUtils.getMatch(page, GCConstants.PATTERN_SEARCH_RECAPTCHA, false, null); if (recaptchaJsParam != null) { final Parameters params = new Parameters("k", recaptchaJsParam.trim()); final String recaptchaJs = cgBase.getResponseData(request("http://www.google.com/recaptcha/api/challenge", params, true)); if (StringUtils.isNotBlank(recaptchaJs)) { recaptchaChallenge = BaseUtils.getMatch(recaptchaJs, GCConstants.PATTERN_SEARCH_RECAPTCHACHALLENGE, true, 1, recaptchaChallenge, true); } } if (thread != null && StringUtils.isNotBlank(recaptchaChallenge)) { thread.setChallenge(recaptchaChallenge); thread.notifyNeed(); } } if (!page.contains("SearchResultsTable")) { // there are no results. aborting here avoids a wrong error log in the next parsing step return searchResult; } int startPos = page.indexOf("
"); int endPos = page.indexOf("ctl00_ContentBody_UnitTxt"); if (startPos == -1 || endPos == -1) { Log.e(Settings.tag, "cgeoBase.parseSearch: ID \"ctl00_ContentBody_UnitTxt\" not found on page"); return null; } page = page.substring(startPos + 1, endPos - startPos + 1); // cut between and
final String[] rows = page.split(" 0) { guids.add(matcherGuidAndDisabled.group(1)); cache.setGuid(matcherGuidAndDisabled.group(1)); if (matcherGuidAndDisabled.group(4) != null) { cache.setName(Html.fromHtml(matcherGuidAndDisabled.group(4).trim()).toString()); } if (matcherGuidAndDisabled.group(6) != null) { cache.setLocation(Html.fromHtml(matcherGuidAndDisabled.group(6).trim()).toString()); } final String attr = matcherGuidAndDisabled.group(2); if (attr != null) { cache.setDisabled(attr.contains("Strike")); cache.setArchived(attr.contains("OldWarning")); } } } } catch (Exception e) { // failed to parse GUID and/or Disabled Log.w(Settings.tag, "cgeoBase.parseSearch: Failed to parse GUID and/or Disabled data"); } if (Settings.isExcludeDisabledCaches() && (cache.isDisabled() || cache.isArchived())) { // skip disabled and archived caches continue; } String inventoryPre = null; cache.setGeocode(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_GEOCODE, true, 1, cache.getGeocode(), true).toUpperCase()); // cache type cache.setType(CacheType.getByPattern(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_TYPE, true, 1, null, true))); // cache direction - image if (Settings.getLoadDirImg()) { cache.setDirectionImg(URLDecoder.decode(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION, true, 1, cache.getDirectionImg(), true))); } // cache inventory final Matcher matcherTbs = GCConstants.PATTERN_SEARCH_TRACKABLES.matcher(row); while (matcherTbs.find()) { if (matcherTbs.groupCount() > 0) { cache.setInventoryItems(Integer.parseInt(matcherTbs.group(1))); inventoryPre = matcherTbs.group(2); } } if (StringUtils.isNotBlank(inventoryPre)) { final Matcher matcherTbsInside = GCConstants.PATTERN_SEARCH_TRACKABLESINSIDE.matcher(inventoryPre); while (matcherTbsInside.find()) { if (matcherTbsInside.groupCount() == 2 && matcherTbsInside.group(2) != null) { final String inventoryItem = matcherTbsInside.group(2).toLowerCase(); if (inventoryItem.equals("premium member only cache")) { continue; } else { if (cache.getInventoryItems() <= 0) { cache.setInventoryItems(1); } } } } } // premium cache cache.setPremiumMembersOnly(row.contains("/images/small_profile.gif")); // found it cache.setFound(row.contains("/images/icons/icon_smile")); // own it cache.setOwn(row.contains("/images/silk/star.png")); // id String result = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_ID, null); if (null != result) { cache.setCacheId(result); cids.add(cache.getCacheId()); } // favourite count try { result = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_FAVORITE, false, 1, null, true); if (null != result) { cache.setFavoritePoints(Integer.parseInt(result)); } } catch (NumberFormatException e) { Log.w(Settings.tag, "cgeoBase.parseSearch: Failed to parse favourite count"); } if (cache.getNameSp() == null) { cache.setNameSp((new Spannable.Factory()).newSpannable(cache.getName())); if (cache.isDisabled() || cache.isArchived()) { // strike cache.getNameSp().setSpan(new StrikethroughSpan(), 0, cache.getNameSp().toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } // location is reliable because the search return correct coords independant of the login status cache.setReliableLatLon(true); searchResult.addCache(cache); } // total caches found try { String result = BaseUtils.getMatch(page, GCConstants.PATTERN_SEARCH_TOTALCOUNT, false, 1, null, true); if (null != result) { searchResult.totalCnt = Integer.parseInt(result); } } catch (NumberFormatException e) { Log.w(Settings.tag, "cgeoBase.parseSearch: Failed to parse cache count"); } if (thread != null && recaptchaChallenge != null) { if (thread.getText() == null) { thread.waitForUser(); } recaptchaText = thread.getText(); } if (cids.size() > 0 && (recaptchaChallenge == null || StringUtils.isNotBlank(recaptchaText))) { Log.i(Settings.tag, "Trying to get .loc for " + cids.size() + " caches"); try { // get coordinates for parsed caches final Parameters params = new Parameters( "__EVENTTARGET", "", "__EVENTARGUMENT", ""); if (ArrayUtils.isNotEmpty(searchResult.viewstates)) { params.put("__VIEWSTATE", searchResult.viewstates[0]); if (searchResult.viewstates.length > 1) { for (int i = 1; i < searchResult.viewstates.length; i++) { params.put("__VIEWSTATE" + i, searchResult.viewstates[i]); } params.put("__VIEWSTATEFIELDCOUNT", "" + searchResult.viewstates.length); } } for (String cid : cids) { params.put("CID", cid); } if (recaptchaChallenge != null && StringUtils.isNotBlank(recaptchaText)) { params.put("recaptcha_challenge_field", recaptchaChallenge); params.put("recaptcha_response_field", recaptchaText); } params.put("ctl00$ContentBody$uxDownloadLoc", "Download Waypoints"); final String coordinates = getResponseData(postRequest("http://www.geocaching.com/seek/nearest.aspx", params)); if (StringUtils.isNotBlank(coordinates)) { if (coordinates.contains("You have not agreed to the license agreement. The license agreement is required before you can start downloading GPX or LOC files from Geocaching.com")) { Log.i(Settings.tag, "User has not agreed to the license agreement. Can\'t download .loc file."); searchResult.error = StatusCode.UNAPPROVED_LICENSE; return searchResult; } } LocParser.parseLoc(searchResult, coordinates); } catch (Exception e) { Log.e(Settings.tag, "cgBase.parseSearch.CIDs: " + e.toString()); } } // get direction images if (Settings.getLoadDirImg()) { final Set caches = cgeoapplication.getInstance().loadCaches(searchResult.getGeocodes(), LoadFlags.LOAD_CACHE_OR_DB); for (cgCache cache : caches) { if (cache.getCoords() == null && StringUtils.isNotEmpty(cache.getDirectionImg())) { DirectionImage.getDrawable(cache.getGeocode(), cache.getDirectionImg()); } } } if (Settings.isRatingWanted()) { // get ratings if (guids.size() > 0) { Log.i(Settings.tag, "Trying to get ratings for " + cids.size() + " caches"); try { final Map ratings = GCVote.getRating(guids, null); if (MapUtils.isNotEmpty(ratings)) { // save found cache coordinates final Set caches = cgeoapplication.getInstance().loadCaches(searchResult.getGeocodes(), LoadFlags.LOAD_CACHE_OR_DB); for (cgCache cache : caches) { if (ratings.containsKey(cache.getGuid())) { GCVoteRating rating = ratings.get(cache.getGuid()); cache.setRating(rating.getRating()); cache.setVotes(rating.getVotes()); cache.setMyVote(rating.getMyVote()); } } } } catch (Exception e) { Log.e(Settings.tag, "cgBase.parseSearch.GCvote: " + e.toString()); } } } return searchResult; } public static SearchResult parseCache(final String page, final CancellableHandler handler) { final SearchResult searchResult = parseCacheFromText(page, handler); if (searchResult != null && !searchResult.getGeocodes().isEmpty()) { final cgCache cache = searchResult.getFirstCacheFromResult(LoadFlags.LOAD_CACHE_OR_DB); getExtraOnlineInfo(cache, page, handler); cache.setUpdated(System.currentTimeMillis()); cache.setDetailedUpdate(cache.getUpdated()); cache.setDetailed(true); if (CancellableHandler.isCancelled(handler)) { return null; } // save full detailed caches cgeoapplication.getInstance().saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); } return searchResult; } static SearchResult parseCacheFromText(final String page, final CancellableHandler handler) { sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_details); if (StringUtils.isBlank(page)) { Log.e(Settings.tag, "cgeoBase.parseCache: No page given"); return null; } final SearchResult searchResult = new SearchResult(); if (page.contains("Cache is Unpublished")) { searchResult.error = StatusCode.UNPUBLISHED_CACHE; return searchResult; } if (page.contains("Sorry, the owner of this listing has made it viewable to Premium Members only.")) { searchResult.error = StatusCode.PREMIUM_ONLY; return searchResult; } if (page.contains("has chosen to make this cache listing visible to Premium Members only.")) { searchResult.error = StatusCode.PREMIUM_ONLY; return searchResult; } final cgCache cache = new cgCache(); cache.setDisabled(page.contains("
  • This cache is temporarily unavailable.")); cache.setArchived(page.contains("
  • This cache has been archived,")); cache.setPremiumMembersOnly(BaseUtils.matches(page, GCConstants.PATTERN_PREMIUMMEMBERS)); cache.setFavorite(BaseUtils.matches(page, GCConstants.PATTERN_FAVORITE)); // cache geocode cache.setGeocode(BaseUtils.getMatch(page, GCConstants.PATTERN_GEOCODE, true, cache.getGeocode())); // cache id cache.setCacheId(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHEID, true, cache.getCacheId())); // cache guid cache.setGuid(BaseUtils.getMatch(page, GCConstants.PATTERN_GUID, true, cache.getGuid())); // name cache.setName(Html.fromHtml(BaseUtils.getMatch(page, GCConstants.PATTERN_NAME, true, cache.getName())).toString()); // owner real name cache.setOwnerReal(URLDecoder.decode(BaseUtils.getMatch(page, GCConstants.PATTERN_OWNERREAL, true, cache.getOwnerReal()))); final String username = Settings.getUsername(); if (cache.getOwnerReal() != null && username != null && cache.getOwnerReal().equalsIgnoreCase(username)) { cache.setOwn(true); } String tableInside = page; int pos = tableInside.indexOf("id=\"cacheDetails\""); if (pos == -1) { Log.e(Settings.tag, "cgeoBase.parseCache: ID \"cacheDetails\" not found on page"); return null; } tableInside = tableInside.substring(pos); pos = tableInside.indexOf("