// $codepro.audit.disable logExceptions package cgeo.geocaching; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LogTypeTrackable; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.files.LocParser; import cgeo.geocaching.geopoint.DistanceParser; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.utils.BaseUtils; 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.entity.StringEntity; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.DefaultHttpClient; 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.res.Resources; 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.widget.EditText; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; 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.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; public class cgBase { private static final String passMatch = "(?<=[\\?&])[Pp]ass(w(or)?d)?=[^&#$]+"; public final static Map cacheTypes = new HashMap(); public final static Map cacheIDs = new HashMap(); static { for (CacheType ct : CacheType.values()) { cacheTypes.put(ct.pattern, ct.id); cacheIDs.put(ct.id, ct.guid); } } public final static Map cacheTypesInv = new HashMap(); public final static Map cacheIDsChoices = new HashMap(); public final static Map cacheSizesInv = new HashMap(); public final static Map waypointTypes = new HashMap(); public final static Map logTypes = new HashMap(); public final static Map logTypes0 = new HashMap(); public final static Map logTypes1 = new HashMap(); public final static Map logTypes2 = new HashMap(); public 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); } public final static SimpleDateFormat dateTbIn1 = new SimpleDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009 public final static SimpleDateFormat dateTbIn2 = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009 public final static SimpleDateFormat dateSqlIn = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 2010-07-25 14:44:01 private Resources res = null; private static final Pattern patternLoggedIn = Pattern.compile("You are logged in as[^<]*]*>([^<]+)[^<]*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); private static final Pattern patternLogged2In = Pattern.compile("\\W*Hello,[^<]*]+>([^<]+)[^<]*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); private static final Pattern patternViewstateFieldCount = Pattern.compile("id=\"__VIEWSTATEFIELDCOUNT\"[^(value)]+value=\"(\\d+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); private static final Pattern patternViewstates = Pattern.compile("id=\"__VIEWSTATE(\\d*)\"[^(value)]+value=\"([^\"]+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); private static final Pattern patternUserToken = Pattern.compile("userToken\\s*=\\s*'([^']+)'"); public static final float miles2km = 1.609344f; public static final float feet2km = 0.0003048f; public static final float yards2km = 0.0009144f; public static final double deg2rad = Math.PI / 180; public static final double rad2deg = 180 / Math.PI; public static final float erad = 6371.0f; private cgeoapplication app = null; public String version = null; /** * FIXME: browser id should become part of settings (where it can be created more easily depending on the current * settings) */ private static String idBrowser = "Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.86 Safari/533.4"; Context context = null; final private static Map gcIcons = new HashMap(); public static final int LOG_FOUND_IT = 2; public static final int LOG_DIDNT_FIND_IT = 3; public static final int LOG_NOTE = 4; public static final int LOG_PUBLISH_LISTING = 1003; // unknown ID; used number doesn't match any GC.com's ID public static final int LOG_ENABLE_LISTING = 23; public static final int LOG_ARCHIVE = 5; public static final int LOG_TEMP_DISABLE_LISTING = 22; public static final int LOG_NEEDS_ARCHIVE = 7; public static final int LOG_WILL_ATTEND = 9; public static final int LOG_ATTENDED = 10; public static final int LOG_RETRIEVED_IT = 13; public static final int LOG_PLACED_IT = 14; public static final int LOG_GRABBED_IT = 19; public static final int LOG_NEEDS_MAINTENANCE = 45; public static final int LOG_OWNER_MAINTENANCE = 46; public static final int LOG_UPDATE_COORDINATES = 47; public static final int LOG_DISCOVERED_IT = 48; public static final int LOG_POST_REVIEWER_NOTE = 18; public static final int LOG_VISIT = 1001; // unknown ID; used number doesn't match any GC.com's ID public static final int LOG_WEBCAM_PHOTO_TAKEN = 11; public static final int LOG_ANNOUNCEMENT = 74; private static final int NB_DOWNLOAD_RETRIES = 4; public static final int UPDATE_LOAD_PROGRESS_DETAIL = 42186; public cgBase(cgeoapplication appIn) { context = appIn.getBaseContext(); res = appIn.getBaseContext().getResources(); // setup cache type mappings final String CACHETYPE_ALL_GUID = "9a79e6ce-3344-409c-bbe9-496530baf758"; cacheIDs.put("all", CACHETYPE_ALL_GUID); cacheIDsChoices.put(res.getString(R.string.all), CACHETYPE_ALL_GUID); for (CacheType ct : CacheType.values()) { String l10n = res.getString(ct.stringId); cacheTypesInv.put(ct.id, l10n); cacheIDsChoices.put(l10n, ct.guid); } for (CacheSize cs : CacheSize.values()) { cacheSizesInv.put(cs, res.getString(cs.stringId)); } // waypoint types for (WaypointType wt : WaypointType.values()) { if (wt != WaypointType.OWN) { waypointTypes.put(wt, res.getString(wt.stringId)); } } // log types logTypes.put("icon_smile", LOG_FOUND_IT); logTypes.put("icon_sad", LOG_DIDNT_FIND_IT); logTypes.put("icon_note", LOG_NOTE); logTypes.put("icon_greenlight", LOG_PUBLISH_LISTING); logTypes.put("icon_enabled", LOG_ENABLE_LISTING); logTypes.put("traffic_cone", LOG_ARCHIVE); logTypes.put("icon_disabled", LOG_TEMP_DISABLE_LISTING); logTypes.put("icon_remove", LOG_NEEDS_ARCHIVE); logTypes.put("icon_rsvp", LOG_WILL_ATTEND); logTypes.put("icon_attended", LOG_ATTENDED); logTypes.put("picked_up", LOG_RETRIEVED_IT); logTypes.put("dropped_off", LOG_PLACED_IT); logTypes.put("transfer", LOG_GRABBED_IT); logTypes.put("icon_needsmaint", LOG_NEEDS_MAINTENANCE); logTypes.put("icon_maint", LOG_OWNER_MAINTENANCE); logTypes.put("coord_update", LOG_UPDATE_COORDINATES); logTypes.put("icon_discovered", LOG_DISCOVERED_IT); logTypes.put("big_smile", LOG_POST_REVIEWER_NOTE); logTypes.put("icon_visited", LOG_VISIT); // unknown ID; used number doesn't match any GC.com's ID logTypes.put("icon_camera", LOG_WEBCAM_PHOTO_TAKEN); // unknown ID; used number doesn't match any GC.com's ID logTypes.put("icon_announcement", LOG_ANNOUNCEMENT); // unknown ID; used number doesn't match any GC.com's ID logTypes0.put("found it", LOG_FOUND_IT); logTypes0.put("didn't find it", LOG_DIDNT_FIND_IT); logTypes0.put("write note", LOG_NOTE); logTypes0.put("publish listing", LOG_PUBLISH_LISTING); logTypes0.put("enable listing", LOG_ENABLE_LISTING); logTypes0.put("archive", LOG_ARCHIVE); logTypes0.put("temporarily disable listing", LOG_TEMP_DISABLE_LISTING); logTypes0.put("needs archived", LOG_NEEDS_ARCHIVE); logTypes0.put("will attend", LOG_WILL_ATTEND); logTypes0.put("attended", LOG_ATTENDED); logTypes0.put("retrieved it", LOG_RETRIEVED_IT); logTypes0.put("placed it", LOG_PLACED_IT); logTypes0.put("grabbed it", LOG_GRABBED_IT); logTypes0.put("needs maintenance", LOG_NEEDS_MAINTENANCE); logTypes0.put("owner maintenance", LOG_OWNER_MAINTENANCE); logTypes0.put("update coordinates", LOG_UPDATE_COORDINATES); logTypes0.put("discovered it", LOG_DISCOVERED_IT); logTypes0.put("post reviewer note", LOG_POST_REVIEWER_NOTE); logTypes0.put("visit", LOG_VISIT); // unknown ID; used number doesn't match any GC.com's ID logTypes0.put("webcam photo taken", LOG_WEBCAM_PHOTO_TAKEN); // unknown ID; used number doesn't match any GC.com's ID logTypes0.put("announcement", LOG_ANNOUNCEMENT); // unknown ID; used number doesn't match any GC.com's ID logTypes1.put(LOG_FOUND_IT, res.getString(R.string.log_found)); logTypes1.put(LOG_DIDNT_FIND_IT, res.getString(R.string.log_dnf)); logTypes1.put(LOG_NOTE, res.getString(R.string.log_note)); logTypes1.put(LOG_PUBLISH_LISTING, res.getString(R.string.log_published)); logTypes1.put(LOG_ENABLE_LISTING, res.getString(R.string.log_enabled)); logTypes1.put(LOG_ARCHIVE, res.getString(R.string.log_archived)); logTypes1.put(LOG_TEMP_DISABLE_LISTING, res.getString(R.string.log_disabled)); logTypes1.put(LOG_NEEDS_ARCHIVE, res.getString(R.string.log_needs_archived)); logTypes1.put(LOG_WILL_ATTEND, res.getString(R.string.log_attend)); logTypes1.put(LOG_ATTENDED, res.getString(R.string.log_attended)); logTypes1.put(LOG_RETRIEVED_IT, res.getString(R.string.log_retrieved)); logTypes1.put(LOG_PLACED_IT, res.getString(R.string.log_placed)); logTypes1.put(LOG_GRABBED_IT, res.getString(R.string.log_grabbed)); logTypes1.put(LOG_NEEDS_MAINTENANCE, res.getString(R.string.log_maintenance_needed)); logTypes1.put(LOG_OWNER_MAINTENANCE, res.getString(R.string.log_maintained)); logTypes1.put(LOG_UPDATE_COORDINATES, res.getString(R.string.log_update)); logTypes1.put(LOG_DISCOVERED_IT, res.getString(R.string.log_discovered)); logTypes1.put(LOG_POST_REVIEWER_NOTE, res.getString(R.string.log_reviewed)); logTypes1.put(LOG_VISIT, res.getString(R.string.log_taken)); logTypes1.put(LOG_WEBCAM_PHOTO_TAKEN, res.getString(R.string.log_webcam)); logTypes1.put(LOG_ANNOUNCEMENT, res.getString(R.string.log_announcement)); logTypes2.put(LOG_FOUND_IT, res.getString(R.string.log_found)); // traditional, multi, mystery, earth, wherigo, virtual, letterbox logTypes2.put(LOG_DIDNT_FIND_IT, res.getString(R.string.log_dnf)); // traditional, multi, mystery, earth, wherigo, virtual, letterbox, webcam logTypes2.put(LOG_NOTE, res.getString(R.string.log_note)); // traditional, multi, mystery, earth, wherigo, virtual, event, letterbox, webcam, trackable logTypes2.put(LOG_PUBLISH_LISTING, res.getString(R.string.log_published)); // X logTypes2.put(LOG_ENABLE_LISTING, res.getString(R.string.log_enabled)); // owner logTypes2.put(LOG_ARCHIVE, res.getString(R.string.log_archived)); // traditional, multi, mystery, earth, event, wherigo, virtual, letterbox, webcam logTypes2.put(LOG_TEMP_DISABLE_LISTING, res.getString(R.string.log_disabled)); // owner logTypes2.put(LOG_NEEDS_ARCHIVE, res.getString(R.string.log_needs_archived)); // traditional, multi, mystery, earth, event, wherigo, virtual, letterbox, webcam logTypes2.put(LOG_WILL_ATTEND, res.getString(R.string.log_attend)); // event logTypes2.put(LOG_ATTENDED, res.getString(R.string.log_attended)); // event logTypes2.put(LOG_WEBCAM_PHOTO_TAKEN, res.getString(R.string.log_webcam)); // webcam logTypes2.put(LOG_RETRIEVED_IT, res.getString(R.string.log_retrieved)); //trackable logTypes2.put(LOG_GRABBED_IT, res.getString(R.string.log_grabbed)); //trackable logTypes2.put(LOG_NEEDS_MAINTENANCE, res.getString(R.string.log_maintenance_needed)); // traditional, mystery, multi, wherigo, virtual, letterbox, webcam logTypes2.put(LOG_OWNER_MAINTENANCE, res.getString(R.string.log_maintained)); // owner logTypes2.put(LOG_DISCOVERED_IT, res.getString(R.string.log_discovered)); //trackable logTypes2.put(LOG_POST_REVIEWER_NOTE, res.getString(R.string.log_reviewed)); // X logTypes2.put(LOG_ANNOUNCEMENT, res.getString(R.string.log_announcement)); // X // init app = appIn; 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; } if (Settings.isBrowser()) { final long rndBrowser = Math.round(Math.random() * 6); switch ((int) rndBrowser) { case 0: idBrowser = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.1 (KHTML, like Gecko) Chrome/5.0.322.2 Safari/533.1"; break; case 1: idBrowser = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MDDC)"; break; case 2: idBrowser = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3"; break; case 3: idBrowser = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-us) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10"; break; case 4: idBrowser = "Mozilla/5.0 (iPod; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11a Safari/525.20"; break; case 5: idBrowser = "Mozilla/5.0 (Linux; U; Android 1.1; en-gb; dream) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2"; break; case 6: idBrowser = "Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.86 Safari/533.4"; break; default: idBrowser = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.307.11 Safari/532.9"; break; } } } public static String hidePassword(final String message) { return message.replaceAll(passMatch, "password=***"); } public void sendLoadProgressDetail(final Handler handler, final int str) { if (null != handler) { handler.obtainMessage(UPDATE_LOAD_PROGRESS_DETAIL, res.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 int count = 1; final Matcher matcherViewstateCount = patternViewstateFieldCount.matcher(page); if (matcherViewstateCount.find()) { count = Integer.parseInt(matcherViewstateCount.group(1)); } String[] viewstates = new String[count]; // Get the viewstates int no; final Matcher matcherViewstates = patternViewstates.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 setViewstates(final String[] viewstates, final Parameters params) { 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) { setViewstates(getViewstates(page), params); } /** * 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 class loginThread extends Thread { @Override public void run() { login(); } } public static StatusCode login() { HttpResponse loginResponse = null; String loginData = null; String[] viewstates = null; final ImmutablePair loginStart = Settings.getLogin(); if (loginStart == null) { return StatusCode.NO_LOGIN_INFO_STORED; // no login information stored } loginResponse = request("https://www.geocaching.com/login/default.aspx", null, false, false, false); loginData = getResponseData(loginResponse); if (StringUtils.isNotBlank(loginData)) { if (checkLogin(loginData)) { Log.i(Settings.tag, "Already logged in Geocaching.com as " + loginStart.left); switchToEnglish(viewstates); return StatusCode.NO_ERROR; // logged in } viewstates = getViewstates(loginData); if (isEmpty(viewstates)) { Log.e(Settings.tag, "cgeoBase.login: Failed to find viewstates"); return StatusCode.LOGIN_PARSE_ERROR; // no viewstates } } else { Log.e(Settings.tag, "cgeoBase.login: Failed to retrieve login page (1st)"); return StatusCode.CONNECTION_FAILED; // no loginpage } final ImmutablePair login = Settings.getLogin(); if (login == null || StringUtils.isEmpty(login.left) || StringUtils.isEmpty(login.right)) { Log.e(Settings.tag, "cgeoBase.login: No login information stored"); return StatusCode.NO_LOGIN_INFO_STORED; } clearCookies(); 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"); setViewstates(viewstates, params); loginResponse = postRequest("https://www.geocaching.com/login/default.aspx", params); loginData = getResponseData(loginResponse); if (StringUtils.isNotBlank(loginData)) { if (checkLogin(loginData)) { Log.i(Settings.tag, "Successfully logged in Geocaching.com as " + login.left); switchToEnglish(getViewstates(loginData)); 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)"); return StatusCode.COMMUNICATION_ERROR; // no login page } } public static boolean checkLogin(String page) { if (StringUtils.isBlank(page)) { Log.e(Settings.tag, "cgeoBase.checkLogin: No page given"); return false; } // on every page final Matcher matcherLogged2In = patternLogged2In.matcher(page); if (matcherLogged2In.find()) { return true; } // after login final Matcher matcherLoggedIn = patternLoggedIn.matcher(page); if (matcherLoggedIn.find()) { return true; } return false; } public static String switchToEnglish(final String[] viewstates) { final Parameters params = new Parameters( "__EVENTTARGET", "ctl00$uxLocaleList$uxLocaleList$ctl00$uxLocaleItem", // switch to english "__EVENTARGUMENT", ""); setViewstates(viewstates, params); return cgBase.getResponseData(postRequest("http://www.geocaching.com/default.aspx", params)); } public static cgCacheWrap parseSearch(final cgSearchThread thread, final String url, String page, final boolean showCaptcha) { if (StringUtils.isBlank(page)) { Log.e(Settings.tag, "cgeoBase.parseSearch: No page given"); return null; } final cgCacheWrap caches = new cgCacheWrap(); final List cids = new ArrayList(); final List guids = new ArrayList(); String recaptchaChallenge = null; String recaptchaText = null; caches.url = url; final Pattern patternCacheType = Pattern.compile("[^<]*]+>[^<]*\"([^\"]+)\"]*>[^<]*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternGuidAndDisabled = Pattern.compile("]*>[^<]*[^<]*([^<]*)?([^<]*)([^<]*)?[^<]+
([^<]*)]+>([^<]*)([^<]*]+>)?[^<]*
[^<]*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternTbs = Pattern.compile("]*>(.*)", Pattern.CASE_INSENSITIVE); final Pattern patternTbsInside = Pattern.compile("(\"([^\"]+)\"[^<]*)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternDirection = Pattern.compile("]*>", Pattern.CASE_INSENSITIVE); final Pattern patternCode = Pattern.compile("\\|\\W*(GC[a-z0-9]+)[^\\|]*\\|", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternId = Pattern.compile("name=\"CID\"[^v]*value=\"([0-9]+)\"", Pattern.CASE_INSENSITIVE); final Pattern patternFavourite = Pattern.compile("([0-9]+)", Pattern.CASE_INSENSITIVE); final Pattern patternTotalCnt = Pattern.compile("Total Records[^<]*(\\d+)<\\/b>", Pattern.CASE_INSENSITIVE); final Pattern patternRecaptcha = Pattern.compile("]*src=\"[^\"]*/recaptcha/api/challenge\\?k=([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); final Pattern patternRecaptchaChallenge = Pattern.compile("challenge : '([^']+)'", Pattern.CASE_INSENSITIVE); caches.viewstates = getViewstates(page); // recaptcha if (showCaptcha) { try { String recaptchaJsParam = null; final Matcher matcherRecaptcha = patternRecaptcha.matcher(page); while (matcherRecaptcha.find()) { if (matcherRecaptcha.groupCount() > 0) { recaptchaJsParam = matcherRecaptcha.group(1); } } 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)) { final Matcher matcherRecaptchaChallenge = patternRecaptchaChallenge.matcher(recaptchaJs); while (matcherRecaptchaChallenge.find()) { if (matcherRecaptchaChallenge.groupCount() > 0) { recaptchaChallenge = matcherRecaptchaChallenge.group(1).trim(); } } } } } catch (Exception e) { // failed to parse recaptcha challenge Log.w(Settings.tag, "cgeoBase.parseSearch: Failed to parse recaptcha challenge"); } 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 caches; } 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.guid = matcherGuidAndDisabled.group(1); if (matcherGuidAndDisabled.group(4) != null) { cache.name = Html.fromHtml(matcherGuidAndDisabled.group(4).trim()).toString(); } if (matcherGuidAndDisabled.group(6) != null) { cache.location = Html.fromHtml(matcherGuidAndDisabled.group(6).trim()).toString(); } final String attr = matcherGuidAndDisabled.group(2); if (attr != null) { if (attr.contains("Strike")) { cache.disabled = true; } else { cache.disabled = false; } if (attr.contains("OldWarning")) { cache.archived = true; } else { cache.archived = false; } } } } } 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.disabled || cache.archived)) { // skip disabled and archived caches cache = null; continue; } String inventoryPre = null; // GC* code try { final Matcher matcherCode = patternCode.matcher(row); while (matcherCode.find()) { if (matcherCode.groupCount() > 0) { cache.geocode = matcherCode.group(1).toUpperCase(); } } } catch (Exception e) { // failed to parse code Log.w(Settings.tag, "cgeoBase.parseSearch: Failed to parse cache code"); } // cache type try { final Matcher matcherCacheType = patternCacheType.matcher(row); while (matcherCacheType.find()) { if (matcherCacheType.groupCount() > 0) { cache.type = cacheTypes.get(matcherCacheType.group(1).toLowerCase()); } } } catch (Exception e) { // failed to parse type Log.w(Settings.tag, "cgeoBase.parseSearch: Failed to parse cache type"); } // cache direction - image if (Settings.getLoadDirImg()) { try { final Matcher matcherDirection = patternDirection.matcher(row); while (matcherDirection.find()) { if (matcherDirection.groupCount() > 0) { cache.directionImg = matcherDirection.group(1); } } } catch (Exception e) { // failed to parse direction image Log.w(Settings.tag, "cgeoBase.parseSearch: Failed to parse cache direction image"); } } // cache inventory try { final Matcher matcherTbs = patternTbs.matcher(row); while (matcherTbs.find()) { if (matcherTbs.groupCount() > 0) { cache.inventoryItems = Integer.parseInt(matcherTbs.group(1)); inventoryPre = matcherTbs.group(2); } } } catch (Exception e) { // failed to parse inventory Log.w(Settings.tag, "cgeoBase.parseSearch: Failed to parse cache inventory (1)"); } if (StringUtils.isNotBlank(inventoryPre)) { try { final Matcher matcherTbsInside = patternTbsInside.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.inventoryItems <= 0) { cache.inventoryItems = 1; } } } } } catch (Exception e) { // failed to parse cache inventory info Log.w(Settings.tag, "cgeoBase.parseSearch: Failed to parse cache inventory info"); } } // premium cache cache.members = row.contains("/images/small_profile.gif"); // found it cache.found = row.contains("/images/icons/icon_smile"); // own it cache.own = row.contains("/images/silk/star.png"); // id try { final Matcher matcherId = patternId.matcher(row); while (matcherId.find()) { if (matcherId.groupCount() > 0) { cache.cacheId = matcherId.group(1); cids.add(cache.cacheId); } } } catch (Exception e) { // failed to parse cache id Log.w(Settings.tag, "cgeoBase.parseSearch: Failed to parse cache id"); } // favourite count try { final Matcher matcherFavourite = patternFavourite.matcher(row); while (matcherFavourite.find()) { if (matcherFavourite.groupCount() > 0) { cache.favouriteCnt = Integer.parseInt(matcherFavourite.group(1)); } } } catch (Exception e) { // failed to parse favourite count Log.w(Settings.tag, "cgeoBase.parseSearch: Failed to parse favourite count"); } if (cache.nameSp == null) { cache.nameSp = (new Spannable.Factory()).newSpannable(cache.name); if (cache.disabled || cache.archived) { // strike cache.nameSp.setSpan(new StrikethroughSpan(), 0, cache.nameSp.toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } caches.cacheList.add(cache); } // total caches found try { final Matcher matcherTotalCnt = patternTotalCnt.matcher(page); while (matcherTotalCnt.find()) { if (matcherTotalCnt.groupCount() > 0) { if (matcherTotalCnt.group(1) != null) { caches.totalCnt = Integer.valueOf(matcherTotalCnt.group(1)); } } } } catch (Exception e) { // failed to parse cache count 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(caches.viewstates)) { params.put("__VIEWSTATE", caches.viewstates[0]); if (caches.viewstates.length > 1) { for (int i = 1; i < caches.viewstates.length; i++) { params.put("__VIEWSTATE" + i, caches.viewstates[i]); } params.put("__VIEWSTATEFIELDCOUNT", "" + caches.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."); caches.error = StatusCode.UNAPPROVED_LICENSE; return caches; } } LocParser.parseLoc(caches, coordinates); } catch (Exception e) { Log.e(Settings.tag, "cgBase.parseSearch.CIDs: " + e.toString()); } } // get direction images if (Settings.getLoadDirImg()) { for (cgCache oneCache : caches.cacheList) { if (oneCache.coords == null && oneCache.directionImg != null) { cgDirectionImg.getDrawable(oneCache.geocode, oneCache.directionImg); } } } // 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 for (cgCache oneCache : caches.cacheList) { if (ratings.containsKey(oneCache.guid)) { cgRating thisRating = ratings.get(oneCache.guid); oneCache.rating = thisRating.rating; oneCache.votes = thisRating.votes; oneCache.myVote = thisRating.myVote; } } } } catch (Exception e) { Log.e(Settings.tag, "cgBase.parseSearch.GCvote: " + e.toString()); } } return caches; } public static cgCacheWrap parseMapJSON(final String uri, final String data) { if (StringUtils.isEmpty(data)) { Log.e(Settings.tag, "cgeoBase.parseMapJSON: No page given"); return null; } final cgCacheWrap caches = new cgCacheWrap(); caches.url = uri; try { final JSONObject yoDawg = new JSONObject(data); final String json = yoDawg.getString("d"); if (StringUtils.isBlank(json)) { Log.e(Settings.tag, "cgeoBase.parseMapJSON: No JSON inside JSON"); return null; } final JSONObject dataJSON = new JSONObject(json); final JSONObject extra = dataJSON.getJSONObject("cs"); if (extra != null && extra.length() > 0) { int count = extra.getInt("count"); if (count > 0 && extra.has("cc")) { final JSONArray cachesData = extra.getJSONArray("cc"); if (cachesData != null && cachesData.length() > 0) { JSONObject oneCache = null; for (int i = 0; i < count; i++) { oneCache = cachesData.getJSONObject(i); if (oneCache == null) { break; } final cgCache cacheToAdd = new cgCache(); cacheToAdd.reliableLatLon = false; cacheToAdd.geocode = oneCache.getString("gc"); cacheToAdd.coords = new Geopoint(oneCache.getDouble("lat"), oneCache.getDouble("lon")); cacheToAdd.name = oneCache.getString("nn"); cacheToAdd.found = oneCache.getBoolean("f"); cacheToAdd.own = oneCache.getBoolean("o"); cacheToAdd.disabled = !oneCache.getBoolean("ia"); int ctid = oneCache.getInt("ctid"); if (ctid == 2) { cacheToAdd.type = CacheType.TRADITIONAL.id; } else if (ctid == 3) { cacheToAdd.type = CacheType.MULTI.id; } else if (ctid == 4) { cacheToAdd.type = CacheType.VIRTUAL.id; } else if (ctid == 5) { cacheToAdd.type = CacheType.LETTERBOX.id; } else if (ctid == 6) { cacheToAdd.type = CacheType.EVENT.id; } else if (ctid == 8) { cacheToAdd.type = CacheType.MYSTERY.id; } else if (ctid == 11) { cacheToAdd.type = CacheType.WEBCAM.id; } else if (ctid == 13) { cacheToAdd.type = CacheType.CITO.id; } else if (ctid == 137) { cacheToAdd.type = CacheType.EARTH.id; } else if (ctid == 453) { cacheToAdd.type = CacheType.MEGA_EVENT.id; } else if (ctid == 1858) { cacheToAdd.type = CacheType.WHERIGO.id; } else if (ctid == 3653) { cacheToAdd.type = CacheType.LOSTANDFOUND.id; } else { cacheToAdd.type = CacheType.UNKNOWN.id; } caches.cacheList.add(cacheToAdd); } } } else { Log.w(Settings.tag, "There are no caches in viewport"); } caches.totalCnt = caches.cacheList.size(); } } catch (Exception e) { Log.e(Settings.tag, "cgBase.parseMapJSON", e); } return caches; } public cgCacheWrap parseCache(final String page, final int reason, final Handler 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 cgCacheWrap caches = new cgCacheWrap(); final cgCache cache = new cgCache(); if (page.contains("Cache is Unpublished")) { caches.error = StatusCode.UNPUBLISHED_CACHE; return caches; } if (page.contains("Sorry, the owner of this listing has made it viewable to Premium Members only.")) { caches.error = StatusCode.PREMIUM_ONLY; return caches; } if (page.contains("has chosen to make this cache listing visible to Premium Members only.")) { caches.error = StatusCode.PREMIUM_ONLY; return caches; } cache.disabled = page.contains("
  • This cache is temporarily unavailable."); cache.archived = page.contains("
  • This cache has been archived,"); cache.members = BaseUtils.matches(page, GCConstants.PATTERN_MEMBERS); cache.favourite = BaseUtils.matches(page, GCConstants.PATTERN_FAVORITE); cache.reason = reason; // cache geocode cache.geocode = BaseUtils.getMatch(page, GCConstants.PATTERN_GEOCODE, true, cache.geocode); // cache id cache.cacheId = BaseUtils.getMatch(page, GCConstants.PATTERN_CACHEID, true, cache.cacheId); // cache guid cache.guid = BaseUtils.getMatch(page, GCConstants.PATTERN_GUID, true, cache.guid); // name cache.name = Html.fromHtml(BaseUtils.getMatch(page, GCConstants.PATTERN_NAME, true, cache.name)).toString(); // owner real name // was cache.ownerReal = URLDecoder.decode(BaseUtils.getMatch(page, Constants.PATTERN_OWNERREAL, 1, cache.ownerReal)); cache.ownerReal = BaseUtils.getMatch(page, GCConstants.PATTERN_OWNERREAL, true, cache.ownerReal); final String username = Settings.getUsername(); if (cache.ownerReal != null && username != null && cache.ownerReal.equalsIgnoreCase(username)) { cache.own = true; } int pos = -1; String tableInside = page; 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("