package cgeo.geocaching; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.math.BigInteger; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.URL; import java.net.URLConnection; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.MessageDigest; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.json.JSONArray; import org.json.JSONObject; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.Uri; import android.os.Handler; import android.os.Message; import android.text.Html; import android.text.Spannable; import android.text.format.DateUtils; import android.text.style.StrikethroughSpan; import android.util.Log; import android.view.Display; import android.view.WindowManager; import cgeo.geocaching.activity.ActivityMixin; public class cgBase { public static HashMap cacheTypes = new HashMap(); public static HashMap cacheTypesInv = new HashMap(); public static HashMap cacheIDs = new HashMap(); public static HashMap cacheIDsChoices = new HashMap(); public static HashMap waypointTypes = new HashMap(); public static HashMap logTypes = new HashMap(); public static HashMap logTypes0 = new HashMap(); public static HashMap logTypes1 = new HashMap(); public static HashMap logTypes2 = new HashMap(); public static HashMap logTypesTrackable = new HashMap(); public static HashMap logTypesTrackableAction = new HashMap(); public static HashMap errorRetrieve = new HashMap(); public static SimpleDateFormat dateInBackslash = new SimpleDateFormat("MM/dd/yyyy"); public static SimpleDateFormat dateInDash = new SimpleDateFormat("yyyy-MM-dd"); public static SimpleDateFormat dateEvIn = new SimpleDateFormat("dd MMMMM yyyy", Locale.ENGLISH); // 28 March 2009 public static SimpleDateFormat dateTbIn1 = new SimpleDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009 public static SimpleDateFormat dateTbIn2 = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009 public static SimpleDateFormat dateSqlIn = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 2010-07-25 14:44:01 public static SimpleDateFormat dateGPXIn = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); // 2010-04-20T07:00:00Z private Resources res = null; private HashMap cookies = new HashMap(); private static final String passMatch = "[/\\?&]*[Pp]ass(word)?=[^&^#^$]+"; 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 patternViewstate = Pattern.compile("id=\"__VIEWSTATE\"[^(value)]+value=\"([^\"]+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); private static final Pattern patternViewstate1 = Pattern.compile("id=\"__VIEWSTATE1\"[^(value)]+value=\"([^\"]+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); private static final Pattern patternIsPremium = Pattern.compile(" gcIcons = new HashMap(); final private static HashMap wpIcons = 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; public cgBase(cgeoapplication appIn, cgSettings settingsIn, SharedPreferences prefsIn) { context = appIn.getBaseContext(); res = appIn.getBaseContext().getResources(); // cache types cacheTypes.put("traditional cache", "traditional"); cacheTypes.put("multi-cache", "multi"); cacheTypes.put("unknown cache", "mystery"); cacheTypes.put("letterbox hybrid", "letterbox"); cacheTypes.put("event cache", "event"); cacheTypes.put("mega-event cache", "mega"); cacheTypes.put("earthcache", "earth"); cacheTypes.put("cache in trash out event", "cito"); cacheTypes.put("webcam cache", "webcam"); cacheTypes.put("virtual cache", "virtual"); cacheTypes.put("wherigo cache", "wherigo"); cacheTypes.put("lost & found", "lostfound"); cacheTypes.put("project ape cache", "ape"); cacheTypes.put("groundspeak hq", "gchq"); cacheTypes.put("gps cache exhibit", "gps"); // cache types inverted cacheTypesInv.put("traditional", res.getString(R.string.traditional)); cacheTypesInv.put("multi", res.getString(R.string.multi)); cacheTypesInv.put("mystery", res.getString(R.string.mystery)); cacheTypesInv.put("letterbox", res.getString(R.string.letterbox)); cacheTypesInv.put("event", res.getString(R.string.event)); cacheTypesInv.put("mega", res.getString(R.string.mega)); cacheTypesInv.put("earth", res.getString(R.string.earth)); cacheTypesInv.put("cito", res.getString(R.string.cito)); cacheTypesInv.put("webcam", res.getString(R.string.webcam)); cacheTypesInv.put("virtual", res.getString(R.string.virtual)); cacheTypesInv.put("wherigo", res.getString(R.string.wherigo)); cacheTypesInv.put("lostfound", res.getString(R.string.lostfound)); cacheTypesInv.put("ape", res.getString(R.string.ape)); cacheTypesInv.put("gchq", res.getString(R.string.gchq)); cacheTypesInv.put("gps", res.getString(R.string.gps)); // cache ids cacheIDs.put("all", "9a79e6ce-3344-409c-bbe9-496530baf758"); // hard-coded also in cgSettings cacheIDs.put("traditional", "32bc9333-5e52-4957-b0f6-5a2c8fc7b257"); cacheIDs.put("multi", "a5f6d0ad-d2f2-4011-8c14-940a9ebf3c74"); cacheIDs.put("mystery", "40861821-1835-4e11-b666-8d41064d03fe"); cacheIDs.put("letterbox", "4bdd8fb2-d7bc-453f-a9c5-968563b15d24"); cacheIDs.put("event", "69eb8534-b718-4b35-ae3c-a856a55b0874"); cacheIDs.put("mega-event", "69eb8535-b718-4b35-ae3c-a856a55b0874"); cacheIDs.put("earth", "c66f5cf3-9523-4549-b8dd-759cd2f18db8"); cacheIDs.put("cito", "57150806-bc1a-42d6-9cf0-538d171a2d22"); cacheIDs.put("webcam", "31d2ae3c-c358-4b5f-8dcd-2185bf472d3d"); cacheIDs.put("virtual", "294d4360-ac86-4c83-84dd-8113ef678d7e"); cacheIDs.put("wherigo", "0544fa55-772d-4e5c-96a9-36a51ebcf5c9"); cacheIDs.put("lostfound", "3ea6533d-bb52-42fe-b2d2-79a3424d4728"); cacheIDs.put("ape", "2555690d-b2bc-4b55-b5ac-0cb704c0b768"); cacheIDs.put("gchq", "416f2494-dc17-4b6a-9bab-1a29dd292d8c"); cacheIDs.put("gps", "72e69af2-7986-4990-afd9-bc16cbbb4ce3"); // cache choices cacheIDsChoices.put(res.getString(R.string.all), cacheIDs.get("all")); cacheIDsChoices.put(res.getString(R.string.traditional), cacheIDs.get("traditional")); cacheIDsChoices.put(res.getString(R.string.multi), cacheIDs.get("multi")); cacheIDsChoices.put(res.getString(R.string.mystery), cacheIDs.get("mystery")); cacheIDsChoices.put(res.getString(R.string.letterbox), cacheIDs.get("letterbox")); cacheIDsChoices.put(res.getString(R.string.event), cacheIDs.get("event")); cacheIDsChoices.put(res.getString(R.string.mega), cacheIDs.get("mega")); cacheIDsChoices.put(res.getString(R.string.earth), cacheIDs.get("earth")); cacheIDsChoices.put(res.getString(R.string.cito), cacheIDs.get("cito")); cacheIDsChoices.put(res.getString(R.string.webcam), cacheIDs.get("webcam")); cacheIDsChoices.put(res.getString(R.string.virtual), cacheIDs.get("virtual")); cacheIDsChoices.put(res.getString(R.string.wherigo), cacheIDs.get("whereigo")); cacheIDsChoices.put(res.getString(R.string.lostfound), cacheIDs.get("lostfound")); cacheIDsChoices.put(res.getString(R.string.ape), cacheIDs.get("ape")); cacheIDsChoices.put(res.getString(R.string.gchq), cacheIDs.get("gchq")); cacheIDsChoices.put(res.getString(R.string.gps), cacheIDs.get("gps")); // waypoint types waypointTypes.put("flag", res.getString(R.string.wp_final)); waypointTypes.put("stage", res.getString(R.string.wp_stage)); waypointTypes.put("puzzle", res.getString(R.string.wp_puzzle)); waypointTypes.put("pkg", res.getString(R.string.wp_pkg)); waypointTypes.put("trailhead", res.getString(R.string.wp_trailhead)); waypointTypes.put("waypoint", res.getString(R.string.wp_waypoint)); // 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, unknown, earth, wherigo, virtual, letterbox logTypes2.put(LOG_DIDNT_FIND_IT, res.getString(R.string.log_dnf)); // traditional, multi, unknown, earth, wherigo, virtual, letterbox, webcam logTypes2.put(LOG_NOTE, res.getString(R.string.log_note)); // traditional, multi, unknown, 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, unknown, 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, unknown, 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, unknown, 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 // trackables for logs logTypesTrackable.put(0, res.getString(R.string.log_tb_nothing)); // do nothing logTypesTrackable.put(1, res.getString(R.string.log_tb_visit)); // visit cache logTypesTrackable.put(2, res.getString(R.string.log_tb_drop)); // drop here logTypesTrackableAction.put(0, ""); // do nothing logTypesTrackableAction.put(1, "_Visited"); // visit cache logTypesTrackableAction.put(2, "_DroppedOff"); // drop here // retrieving errors (because of ____ ) errorRetrieve.put(1, res.getString(R.string.err_none)); errorRetrieve.put(0, res.getString(R.string.err_start)); errorRetrieve.put(-1, res.getString(R.string.err_parse)); errorRetrieve.put(-2, res.getString(R.string.err_server)); errorRetrieve.put(-3, res.getString(R.string.err_login)); errorRetrieve.put(-4, res.getString(R.string.err_unknown)); errorRetrieve.put(-5, res.getString(R.string.err_comm)); errorRetrieve.put(-6, res.getString(R.string.err_wrong)); errorRetrieve.put(-7, res.getString(R.string.err_license)); // init app = appIn; settings = settingsIn; prefs = prefsIn; try { PackageManager manager = app.getPackageManager(); PackageInfo info = manager.getPackageInfo(app.getPackageName(), 0); version = info.versionName; } catch (Exception e) { // nothing } if (settings.asBrowser == 1) { final long rndBrowser = Math.round(Math.random() * 6); if (rndBrowser == 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"; } else if (rndBrowser == 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)"; } else if (rndBrowser == 2) { idBrowser = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3"; } else if (rndBrowser == 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"; } else if (rndBrowser == 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"; } else if (rndBrowser == 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"; } else if (rndBrowser == 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"; } else { 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"; } } } public static String findViewstate(String page, int index) { String viewstate = null; if (index == 0) { final Matcher matcherViewstate = patternViewstate.matcher(page); while (matcherViewstate.find()) { if (matcherViewstate.groupCount() > 0) { viewstate = matcherViewstate.group(1); } } } else if (index == 1) { final Matcher matcherViewstate = patternViewstate1.matcher(page); while (matcherViewstate.find()) { if (matcherViewstate.groupCount() > 0) { viewstate = matcherViewstate.group(1); } } } return viewstate; } public class loginThread extends Thread { @Override public void run() { login(); } } public int login() { final String host = "www.geocaching.com"; final String path = "/login/default.aspx"; cgResponse loginResponse = null; String loginData = null; String viewstate = null; String viewstate1 = null; final HashMap loginStart = settings.getLogin(); if (loginStart == null) { return -3; // no login information stored } loginResponse = request(true, host, path, "GET", new HashMap(), false, false, false); loginData = loginResponse.getData(); if (loginData != null && loginData.length() > 0) { if (checkLogin(loginData) == true) { Log.i(cgSettings.tag, "Already logged in Geocaching.com as " + loginStart.get("username")); switchToEnglish(viewstate, viewstate1); return 1; // logged in } viewstate = findViewstate(loginData, 0); viewstate1 = findViewstate(loginData, 1); if (viewstate == null || viewstate.length() == 0) { Log.e(cgSettings.tag, "cgeoBase.login: Failed to find viewstate"); return -1; // no viewstate } } else { Log.e(cgSettings.tag, "cgeoBase.login: Failed to retrieve login page (1st)"); return -2; // no loginpage } final HashMap login = settings.getLogin(); final HashMap params = new HashMap(); if (login == null || login.get("username") == null || login.get("username").length() == 0 || login.get("password") == null || login.get("password").length() == 0) { Log.e(cgSettings.tag, "cgeoBase.login: No login information stored"); return -3; } settings.deleteCookies(); params.put("__EVENTTARGET", ""); params.put("__EVENTARGUMENT", ""); params.put("__VIEWSTATE", viewstate); if (viewstate1 != null) { params.put("__VIEWSTATE1", viewstate1); params.put("__VIEWSTATEFIELDCOUNT", "2"); } params.put("ctl00$SiteContent$tbUsername", login.get("username")); params.put("ctl00$SiteContent$tbPassword", login.get("password")); params.put("ctl00$SiteContent$cbRememberMe", "on"); params.put("ctl00$SiteContent$btnSignIn", "Login"); loginResponse = request(true, host, path, "POST", params, false, false, false); loginData = loginResponse.getData(); if (loginData != null && loginData.length() > 0) { if (checkLogin(loginData) == true) { Log.i(cgSettings.tag, "Successfully logged in Geocaching.com as " + login.get("username")); switchToEnglish(findViewstate(loginData, 0), findViewstate(loginData, 1)); return 1; // logged in } else { if (loginData.indexOf("Your username/password combination does not match.") != -1) { Log.i(cgSettings.tag, "Failed to log in Geocaching.com as " + login.get("username") + " because of wrong username/password"); return -6; // wrong login } else { Log.i(cgSettings.tag, "Failed to log in Geocaching.com as " + login.get("username") + " for some unknown reason"); return -4; // can't login } } } else { Log.e(cgSettings.tag, "cgeoBase.login: Failed to retrieve login page (2nd)"); return -5; // no login page } } public static Boolean isPremium(String page) { if (checkLogin(page)) { final Matcher matcherIsPremium = patternIsPremium.matcher(page); return matcherIsPremium.find(); } else return false; } public static Boolean checkLogin(String page) { if (page == null || page.length() == 0) { Log.e(cgSettings.tag, "cgeoBase.checkLogin: No page given"); return false; } // on every page final Matcher matcherLogged2In = patternLogged2In.matcher(page); while (matcherLogged2In.find()) { return true; } // after login final Matcher matcherLoggedIn = patternLoggedIn.matcher(page); while (matcherLoggedIn.find()) { return true; } return false; } public String switchToEnglish(String viewstate, String viewstate1) { final String host = "www.geocaching.com"; final String path = "/default.aspx"; final HashMap params = new HashMap(); if (viewstate != null) { params.put("__VIEWSTATE", viewstate); } if (viewstate1 != null) { params.put("__VIEWSTATE1", viewstate1); params.put("__VIEWSTATEFIELDCOUNT", "2"); } params.put("__EVENTTARGET", "ctl00$uxLocaleList$uxLocaleList$ctl00$uxLocaleItem"); // switch to english params.put("__EVENTARGUMENT", ""); return request(false, host, path, "POST", params, false, false, false).getData(); } public cgCacheWrap parseSearch(cgSearchThread thread, String url, String page, boolean showCaptcha) { if (page == null || page.length() == 0) { Log.e(cgSettings.tag, "cgeoBase.parseSearch: No page given"); return null; } final cgCacheWrap caches = new cgCacheWrap(); final ArrayList cids = new ArrayList(); final ArrayList 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.viewstate = findViewstate(page, 0); caches.viewstate1 = findViewstate(page, 1); // recaptcha if (showCaptcha == true) { 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 String recaptchaJs = request(false, "www.google.com", "/recaptcha/api/challenge", "GET", "k=" + urlencode_rfc3986(recaptchaJsParam.trim()), 0, true).getData(); if (recaptchaJs != null && recaptchaJs.length() > 0) { 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(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse recaptcha challenge"); } if (thread != null && recaptchaChallenge != null && recaptchaChallenge.length() > 0) { thread.setChallenge(recaptchaChallenge); thread.notifyNeed(); } } int startPos = -1; int endPos = -1; startPos = page.indexOf("
"); endPos = page.indexOf("ctl00_ContentBody_UnitTxt"); if (startPos == -1 || endPos == -1) { Log.e(cgSettings.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") == true) { cache.disabled = true; } else { cache.disabled = false; } if (attr.contains("OldWarning") == true) { cache.archived = true; } else { cache.archived = false; } } } } } catch (Exception e) { // failed to parse GUID and/or Disabled Log.w(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse GUID and/or Disabled data"); } if (settings.excludeDisabled == 1 && (cache.disabled == true || cache.archived == true)) { // 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(cgSettings.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(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse cache type"); } // cache direction - image 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(cgSettings.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(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse cache inventory (1)"); } if (inventoryPre != null && inventoryPre.trim().length() > 0) { 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(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse cache inventory info"); } } // premium cache if (row.indexOf("/images/small_profile.gif") != -1) { cache.members = true; } else { cache.members = false; } // found it if (row.indexOf("/images/icons/icon_smile") != -1) { cache.found = true; } else { cache.found = false; } // own it if (row.indexOf("/images/silk/star.png") != -1) { cache.own = true; } else { cache.own = false; } // 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(cgSettings.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(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse favourite count"); } if (cache.nameSp == null) { cache.nameSp = (new Spannable.Factory()).newSpannable(cache.name); if (cache.disabled == true || cache.archived == true) { // strike cache.nameSp.setSpan(new StrikethroughSpan(), 0, cache.nameSp.toString().length(), Spannable.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 = new Integer(matcherTotalCnt.group(1)); } } } } catch (Exception e) { // failed to parse cache count Log.w(cgSettings.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 || (recaptchaChallenge != null && recaptchaText != null && recaptchaText.length() > 0))) { Log.i(cgSettings.tag, "Trying to get .loc for " + cids.size() + " caches"); try { // get coordinates for parsed caches final String host = "www.geocaching.com"; final String path = "/seek/nearest.aspx"; final StringBuilder params = new StringBuilder(); params.append("__EVENTTARGET="); params.append("&"); params.append("__EVENTARGUMENT="); params.append("&"); params.append("__VIEWSTATE="); params.append(urlencode_rfc3986(caches.viewstate)); if (caches.viewstate1 != null) { params.append("&"); params.append("__VIEWSTATE1="); params.append(urlencode_rfc3986(caches.viewstate1)); params.append("&"); params.append("__VIEWSTATEFIELDCOUNT=2"); } for (String cid : cids) { params.append("&"); params.append("CID="); params.append(urlencode_rfc3986(cid)); } if (recaptchaChallenge != null && recaptchaText != null && recaptchaText.length() > 0) { params.append("&"); params.append("recaptcha_challenge_field="); params.append(urlencode_rfc3986(recaptchaChallenge)); params.append("&"); params.append("recaptcha_response_field="); params.append(urlencode_rfc3986(recaptchaText)); } params.append("&"); params.append("ctl00%24ContentBody%24uxDownloadLoc=Download+Waypoints"); final String coordinates = request(false, host, path, "POST", params.toString(), 0, true).getData(); if (coordinates != null && coordinates.length() > 0) { if (coordinates.indexOf("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") > -1) { Log.i(cgSettings.tag, "User has not agreed to the license agreement. Can\'t download .loc file."); caches.error = errorRetrieve.get(-7); return caches; } } if (coordinates != null && coordinates.length() > 0) { final HashMap cidCoords = new HashMap(); final Pattern patternCidCode = Pattern.compile("name id=\"([^\"]+)\""); final Pattern patternCidLat = Pattern.compile("lat=\"([^\"]+)\""); final Pattern patternCidLon = Pattern.compile("lon=\"([^\"]+)\""); // premium only >> final Pattern patternCidDif = Pattern.compile("([^<]+)"); final Pattern patternCidTer = Pattern.compile("([^<]+)"); final Pattern patternCidCon = Pattern.compile("([^<]+)"); // >> premium only final String[] points = coordinates.split(""); // parse coordinates for (String point : points) { final cgCoord pointCoord = new cgCoord(); final Matcher matcherCidCode = patternCidCode.matcher(point); final Matcher matcherLatCode = patternCidLat.matcher(point); final Matcher matcherLonCode = patternCidLon.matcher(point); final Matcher matcherDifCode = patternCidDif.matcher(point); final Matcher matcherTerCode = patternCidTer.matcher(point); final Matcher matcherConCode = patternCidCon.matcher(point); HashMap tmp = null; if (matcherCidCode.find() == true) { pointCoord.name = matcherCidCode.group(1).trim().toUpperCase(); } if (matcherLatCode.find() == true) { tmp = parseCoordinate(matcherLatCode.group(1), "lat"); pointCoord.latitude = (Double) tmp.get("coordinate"); } if (matcherLonCode.find() == true) { tmp = parseCoordinate(matcherLonCode.group(1), "lon"); pointCoord.longitude = (Double) tmp.get("coordinate"); } if (matcherDifCode.find() == true) { pointCoord.difficulty = new Float(matcherDifCode.group(1)); } if (matcherTerCode.find() == true) { pointCoord.terrain = new Float(matcherTerCode.group(1)); } if (matcherConCode.find() == true) { final int size = Integer.parseInt(matcherConCode.group(1)); if (size == 1) { pointCoord.size = "not chosen"; } else if (size == 2) { pointCoord.size = "micro"; } else if (size == 3) { pointCoord.size = "regular"; } else if (size == 4) { pointCoord.size = "large"; } else if (size == 5) { pointCoord.size = "virtual"; } else if (size == 6) { pointCoord.size = "other"; } else if (size == 8) { pointCoord.size = "small"; } else { pointCoord.size = "unknown"; } } cidCoords.put(pointCoord.name, pointCoord); } Log.i(cgSettings.tag, "Coordinates found in .loc file: " + cidCoords.size()); // save found cache coordinates for (cgCache oneCache : caches.cacheList) { if (cidCoords.containsKey(oneCache.geocode) == true) { cgCoord thisCoords = cidCoords.get(oneCache.geocode); oneCache.latitude = thisCoords.latitude; oneCache.longitude = thisCoords.longitude; oneCache.difficulty = thisCoords.difficulty; oneCache.terrain = thisCoords.terrain; oneCache.size = thisCoords.size; } } } } catch (Exception e) { Log.e(cgSettings.tag, "cgBase.parseSearch.CIDs: " + e.toString()); } } // get direction images for (cgCache oneCache : caches.cacheList) { if (oneCache.latitude == null && oneCache.longitude == null && oneCache.direction == null && oneCache.directionImg != null) { cgDirectionImg.getDrawable(oneCache.geocode, oneCache.directionImg); } } // get ratings if (guids.size() > 0) { Log.i(cgSettings.tag, "Trying to get ratings for " + cids.size() + " caches"); try { final HashMap ratings = getRating(guids, null); if (ratings != null) { // save found cache coordinates for (cgCache oneCache : caches.cacheList) { if (ratings.containsKey(oneCache.guid) == true) { cgRating thisRating = ratings.get(oneCache.guid); oneCache.rating = thisRating.rating; oneCache.votes = thisRating.votes; oneCache.myVote = thisRating.myVote; } } } } catch (Exception e) { Log.e(cgSettings.tag, "cgBase.parseSearch.GCvote: " + e.toString()); } } return caches; } public static cgCacheWrap parseMapJSON(String url, String data) { if (data == null || data.length() == 0) { Log.e(cgSettings.tag, "cgeoBase.parseMapJSON: No page given"); return null; } final cgCacheWrap caches = new cgCacheWrap(); caches.url = url; try { final JSONObject yoDawg = new JSONObject(data); final String json = yoDawg.getString("d"); if (json == null || json.length() == 0) { Log.e(cgSettings.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.latitude = oneCache.getDouble("lat"); cacheToAdd.longitude = 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 = "traditional"; } else if (ctid == 3) { cacheToAdd.type = "multi"; } else if (ctid == 4) { cacheToAdd.type = "virtual"; } else if (ctid == 5) { cacheToAdd.type = "letterbox"; } else if (ctid == 6) { cacheToAdd.type = "event"; } else if (ctid == 8) { cacheToAdd.type = "mystery"; } else if (ctid == 11) { cacheToAdd.type = "webcam"; } else if (ctid == 13) { cacheToAdd.type = "cito"; } else if (ctid == 137) { cacheToAdd.type = "earth"; } else if (ctid == 453) { cacheToAdd.type = "mega"; } else if (ctid == 1858) { cacheToAdd.type = "wherigo"; } else if (ctid == 3653) { cacheToAdd.type = "lost"; } caches.cacheList.add(cacheToAdd); } } } else { Log.w(cgSettings.tag, "There are no caches in viewport"); } caches.totalCnt = caches.cacheList.size(); } } catch (Exception e) { Log.e(cgSettings.tag, "cgBase.parseMapJSON: " + e.toString()); } return caches; } public cgCacheWrap parseCache(String page, int reason) { if (page == null || page.length() == 0) { Log.e(cgSettings.tag, "cgeoBase.parseCache: No page given"); return null; } final Pattern patternGeocode = Pattern.compile("]*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternCacheId = Pattern.compile("/seek/log\\.aspx\\?ID=(\\d+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternCacheGuid = Pattern.compile(Pattern.quote("&wid=") + "([0-9a-z\\-]+)" + Pattern.quote("&"), Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternType = Pattern.compile("\"([^\"]+)\"]*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternName = Pattern.compile("]*>[^<]*([^<]+)<\\/span>[^<]*<\\/h2>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternSize = Pattern.compile("
[^<]*]*>[^S]*Size[^:]*:[^<]*]*>[^<]*\"Size:]*>[^<]*[^<]*[^<]*[^<]*

", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternDifficulty = Pattern.compile("]*>[^<]*\"[^\"]+\"[^]*>[^<]*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternTerrain = Pattern.compile("]*>[^<]*\"[^\"]+\"[^]*>[^<]*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternOwner = Pattern.compile("[^\\w]*An?([^\\w]*Event)?[^\\w]*cache[^\\w]*by[^<]*([^<]+)[^<]*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternOwnerReal = Pattern.compile("[^<]+", Pattern.CASE_INSENSITIVE); final Pattern patternHidden = Pattern.compile("]*>[^\\w]*Hidden[^:]*:[^\\d]*((\\d+)\\/(\\d+)\\/(\\d+))[^<]*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternHiddenEvent = Pattern.compile("]*>[^\\w]*Event[^\\w]*Date[^:]*:([^<]*)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternFavourite = Pattern.compile("]*>[^<]*[^\\d]*([0-9]+)[^\\d^<]*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternFound = Pattern.compile("

[^<]*]*>[^<]*[^<]*

", Pattern.CASE_INSENSITIVE); final Pattern patternLatLon = Pattern.compile("]*>()?([^<]*)(<\\/b>)?<\\/span>", Pattern.CASE_INSENSITIVE); final Pattern patternLocation = Pattern.compile("]*>In ([^<]*)", Pattern.CASE_INSENSITIVE); final Pattern patternHint = Pattern.compile("

([^<]*)?[^\\w]*Additional Hints([^<]*<\\/strong>)?[^\\(]*\\(]+>Encrypt\\)[^<]*<\\/p>[^<]*

]*>(.*)
[^<]*
]*>([^<]*)

", Pattern.CASE_INSENSITIVE); final Pattern patternDescShort = Pattern.compile("
[^<]*]*>((?:(?![^\\w^<]*
).)*)[^\\w^<]*
", Pattern.CASE_INSENSITIVE); final Pattern patternDesc = Pattern.compile("]*>" + "(.*)[^<]*
[^<]*

[^<]*

[^<]*

[^<]*[^\\w]*Additional Hints", Pattern.CASE_INSENSITIVE); final Pattern patternCountLogs = Pattern.compile("

(.*)<\\/p><\\/span>", Pattern.CASE_INSENSITIVE); final Pattern patternCountLog = Pattern.compile(" src=\"\\/images\\/icons\\/([^\\.]*).gif\" alt=\"[^\"]*\" title=\"[^\"]*\" />([0-9]*)[^0-9]+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternLogs = Pattern.compile("]*>[^<]*(.*)[^<]*
[^<]*]*>[^<]*[^<]*]*> ([a-zA-Z]+) (\\d+)(, (\\d+))? by ]+>([^<]+)[<^]*([^\\(]*\\((\\d+) found\\))?(]*>)+((?:(?!).)*)(]*>)+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); final Pattern patternLogImgs = Pattern.compile("a href=\"http://img.geocaching.com/cache/log/([^\"]+)\".+?([^<]*)", Pattern.CASE_INSENSITIVE); final Pattern patternAttributes = Pattern.compile("

[^<]*]+>[^\\w]*Attributes[^<]*

[^<]*
(([^<]*\"[^\"]+\"[^]*>)+)[^<]*]*>", Pattern.CASE_INSENSITIVE); final Pattern patternSpoilers = Pattern.compile("((]*>[^<]*]+>[^<]*[^>]+[^<]*[^<]*]*>([^<]*(]*>)+)?)+)[^<]*", Pattern.CASE_INSENSITIVE); final Pattern patternSpoilersInside = Pattern.compile("[^<]*]*>[^<]*]+>[^<]*([^>]+)[^<]*[^<]*]*>(([^<]*)()+)?", Pattern.CASE_INSENSITIVE); final Pattern patternInventory = Pattern.compile("[^\\w]*Inventory[^<]*[^<]*[^<]*
([^<]*