diff options
Diffstat (limited to 'src/cgeo/geocaching/cgBase.java')
| -rw-r--r-- | src/cgeo/geocaching/cgBase.java | 9784 |
1 files changed, 4898 insertions, 4886 deletions
diff --git a/src/cgeo/geocaching/cgBase.java b/src/cgeo/geocaching/cgBase.java index 4ddae1b..6312acf 100644 --- a/src/cgeo/geocaching/cgBase.java +++ b/src/cgeo/geocaching/cgBase.java @@ -1,5 +1,34 @@ package cgeo.geocaching; +import cgeo.geocaching.activity.ActivityMixin; +import cgeo.geocaching.enumerations.CacheType; +import cgeo.geocaching.files.LocParser; +import cgeo.geocaching.geopoint.DistanceParser; +import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.utils.CollectionUtils; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +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.widget.EditText; + import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -48,176 +77,148 @@ import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.StringUtils; -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.widget.EditText; -import cgeo.geocaching.activity.ActivityMixin; -import cgeo.geocaching.enumerations.CacheType; -import cgeo.geocaching.files.LocParser; -import cgeo.geocaching.geopoint.DistanceParser; -import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.utils.CollectionUtils; - public class cgBase { - private final static Pattern patternGeocode = Pattern.compile("<meta name=\"og:url\" content=\"[^\"]+/(GC[0-9A-Z]+)\"[^>]*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private final static Pattern patternCacheId = Pattern.compile("/seek/log\\.aspx\\?ID=(\\d+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private final static Pattern patternCacheGuid = Pattern.compile(Pattern.quote("&wid=") + "([0-9a-z\\-]+)" + Pattern.quote("&"), Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private final static Pattern patternType = Pattern.compile("<img src=\"[^\"]*/WptTypes/\\d+\\.gif\" alt=\"([^\"]+)\" (title=\"[^\"]*\" )?width=\"32\" height=\"32\"[^>]*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - - private final static Pattern patternName = Pattern.compile("<h2[^>]*>[^<]*<span id=\"ctl00_ContentBody_CacheName\">([^<]+)<\\/span>[^<]*<\\/h2>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private final static Pattern patternSize = Pattern.compile("<div class=\"CacheSize[^\"]*\">[^<]*<p[^>]*>[^S]*Size[^:]*:[^<]*<span[^>]*>[^<]*<img src=\"[^\"]*/icons/container/[a-z_]+\\.gif\" alt=\"Size: ([^\"]+)\"[^>]*>[^<]*<small>[^<]*</small>[^<]*</span>[^<]*</p>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private final static Pattern patternDifficulty = Pattern.compile("<span id=\"ctl00_ContentBody_uxLegendScale\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\" alt=\"[^\"]+\"[^>]*>[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private final static Pattern patternTerrain = Pattern.compile("<span id=\"ctl00_ContentBody_Localize6\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\" alt=\"[^\"]+\"[^>]*>[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private final static Pattern patternOwner = Pattern.compile("<span class=\"minorCacheDetails\">\\W*An?(\\W*Event)?\\W*cache\\W*by[^<]*<a href=\"[^\"]+\">([^<]+)</a>[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private final static Pattern patternOwnerReal = Pattern.compile("<a id=\"ctl00_ContentBody_uxFindLinksHiddenByThisUser\" href=\"[^\"]*/seek/nearest\\.aspx\\?u=*([^\"]+)\">[^<]+</a>", Pattern.CASE_INSENSITIVE); - private final static Pattern patternHidden = Pattern.compile("<span[^>]*>\\W*Hidden[\\s:]*([^<]+)</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private final static Pattern patternHiddenEvent = Pattern.compile("<span[^>]*>\\W*Event\\W*Date[^:]*:([^<]*)</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private final static Pattern patternFavourite = Pattern.compile("<a id=\"uxFavContainerLink\"[^>]*>[^<]*<div[^<]*<span class=\"favorite-value\">[^\\d]*([0-9]+)[^\\d^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - - private final static Pattern patternFound = Pattern.compile("<p>[^<]*<a id=\"ctl00_ContentBody_hlFoundItLog\"[^<]*<img src=\".*/images/stockholm/16x16/check\\.gif\"[^>]*>[^<]*</a>[^<]*</p>", Pattern.CASE_INSENSITIVE); - private final static Pattern patternFoundAlternative = Pattern.compile("<div class=\"StatusInformationWidget FavoriteWidget\"", Pattern.CASE_INSENSITIVE); - private final static Pattern patternLatLon = Pattern.compile("<span id=\"ctl00_ContentBody_LatLon\"[^>]*>(<b>)?([^<]*)(<\\/b>)?<\\/span>", Pattern.CASE_INSENSITIVE); - private final static Pattern patternLocation = Pattern.compile("<span id=\"ctl00_ContentBody_Location\"[^>]*>In ([^<]*)", Pattern.CASE_INSENSITIVE); - private final static Pattern patternHint = Pattern.compile("<p>([^<]*<strong>)?\\W*Additional Hints([^<]*<\\/strong>)?[^\\(]*\\(<a[^>]+>Encrypt</a>\\)[^<]*<\\/p>[^<]*<div id=\"div_hint\"[^>]*>(.*)</div>[^<]*<div id=[\\'|\"]dk[\\'|\"]", Pattern.CASE_INSENSITIVE); - private final static Pattern patternPersonalNote = Pattern.compile("<p id=\"cache_note\"[^>]*>([^<]*)</p>", Pattern.CASE_INSENSITIVE); - private final static Pattern patternDescShort = Pattern.compile("<div class=\"UserSuppliedContent\">[^<]*<span id=\"ctl00_ContentBody_ShortDescription\"[^>]*>((?:(?!</span>[^\\w^<]*</div>).)*)</span>[^\\w^<]*</div>", Pattern.CASE_INSENSITIVE); - private final static Pattern patternDesc = Pattern.compile("<span id=\"ctl00_ContentBody_LongDescription\"[^>]*>" + "(.*)</span>[^<]*</div>[^<]*<p>[^<]*</p>[^<]*<p>[^<]*<strong>\\W*Additional Hints</strong>", Pattern.CASE_INSENSITIVE); - private final static Pattern patternCountLogs = Pattern.compile("<span id=\"ctl00_ContentBody_lblFindCounts\"><p(.+?)<\\/p><\\/span>", Pattern.CASE_INSENSITIVE); - private final static Pattern patternCountLog = Pattern.compile("src=\"\\/images\\/icons\\/(.+?).gif\"[^>]+> (\\d+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private final static Pattern patternLog = Pattern.compile("<tr><td class.+?<a href=\"/profile/\\?guid=.+?>(.+?)</a>.+?(?:logOwnerStats[^>]+><img[^>]+icon_smile.+?> ([,\\d]+).+?)?LogType.+?<img.+?/images/icons/([^\\.]+)\\..+?title=\"(.+?)\".+?LogDate.+?>(.+?)<.+?LogText.+?>(.*?)</p>(.*?)</div></div></div></td></tr>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private final static Pattern patternLogImgs = Pattern.compile("href=\"(http://img.geocaching.com/cache/log/.+?)\".+?<span>([^<]*)", Pattern.CASE_INSENSITIVE); - private final static Pattern patternAttributes = Pattern.compile("<h3 class=\"WidgetHeader\">[^<]*<img[^>]+>\\W*Attributes[^<]*</h3>[^<]*<div class=\"WidgetBody\">(([^<]*<img src=\"[^\"]+\" alt=\"[^\"]+\"[^>]*>)+)[^<]*<p", Pattern.CASE_INSENSITIVE); - private final static Pattern patternAttributesInside = Pattern.compile("[^<]*<img src=\"([^\"]+)\" alt=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); - private final static Pattern patternSpoilers = Pattern.compile("<span id=\"ctl00_ContentBody_Images\">((<a href=\"[^\"]+\"[^>]*>[^<]*<img[^>]+>[^<]*<span>[^>]+</span>[^<]*</a>[^<]*<br[^>]*>([^<]*(<br[^>]*>)+)?)+)[^<]*</span>", Pattern.CASE_INSENSITIVE); - private final static Pattern patternSpoilersInside = Pattern.compile("[^<]*<a href=\"([^\"]+)\"[^>]*>[^<]*<img[^>]+>[^<]*<span>([^>]+)</span>[^<]*</a>[^<]*<br[^>]*>(([^<]*)(<br[^<]*>)+)?", Pattern.CASE_INSENSITIVE); - private final static Pattern patternInventory = Pattern.compile("<span id=\"ctl00_ContentBody_uxTravelBugList_uxInventoryLabel\">\\W*Inventory[^<]*</span>[^<]*</h3>[^<]*<div class=\"WidgetBody\">([^<]*<ul>(([^<]*<li>[^<]*<a href=\"[^\"]+\"[^>]*>[^<]*<img src=\"[^\"]+\"[^>]*>[^<]*<span>[^<]+<\\/span>[^<]*<\\/a>[^<]*<\\/li>)+)[^<]*<\\/ul>)?", Pattern.CASE_INSENSITIVE); - private final static Pattern patternInventoryInside = Pattern.compile("[^<]*<li>[^<]*<a href=\"[a-z0-9\\-\\_\\.\\?\\/\\:\\@]*\\/track\\/details\\.aspx\\?guid=([0-9a-z\\-]+)[^\"]*\"[^>]*>[^<]*<img src=\"[^\"]+\"[^>]*>[^<]*<span>([^<]+)<\\/span>[^<]*<\\/a>[^<]*<\\/li>", Pattern.CASE_INSENSITIVE); - private final static Pattern patternOnWatchlist = Pattern.compile("<img\\s*src=\"\\/images\\/stockholm\\/16x16\\/icon_stop_watchlist.gif\"", Pattern.CASE_INSENSITIVE); - - private final static Pattern PATTERN_TRACKABLE_TrackableId = Pattern.compile("<a id=\"ctl00_ContentBody_LogLink\" title=\"[^\"]*\" href=\".*log\\.aspx\\?wid=([a-z0-9\\-]+)\"[^>]*>[^<]*</a>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_Geocode = Pattern.compile("<span id=\"ctl00_ContentBody_BugDetails_BugTBNum\" String=\"[^\"]*\">Use[^<]*<strong>(TB[0-9a-z]+)[^<]*</strong> to reference this item.[^<]*</span>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_Name = Pattern.compile("<h2>([^<]*<img[^>]*>)?[^<]*<span id=\"ctl00_ContentBody_lbHeading\">([^<]+)</span>[^<]*</h2>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_Owner = Pattern.compile("<dt>\\W*Owner:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugOwner\" title=\"[^\"]*\" href=\"[^\"]*/profile/\\?guid=([a-z0-9\\-]+)\">([^<]+)<\\/a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_Released = Pattern.compile("<dt>\\W*Released:[^<]*</dt>[^<]*<dd>[^<]*<span id=\"ctl00_ContentBody_BugDetails_BugReleaseDate\">([^<]+)<\\/span>[^<]*</dd>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_Origin = Pattern.compile("<dt>\\W*Origin:[^<]*</dt>[^<]*<dd>[^<]*<span id=\"ctl00_ContentBody_BugDetails_BugOrigin\">([^<]+)<\\/span>[^<]*</dd>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_SpottedCache = Pattern.compile("<dt>\\W*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\" title=\"[^\"]*\" href=\"[^\"]*/seek/cache_details.aspx\\?guid=([a-z0-9\\-]+)\">In ([^<]+)</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_SpottedUser = Pattern.compile("<dt>\\W*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\" href=\"[^\"]*/profile/\\?guid=([a-z0-9\\-]+)\">In the hands of ([^<]+).</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_SpottedUnknown = Pattern.compile("<dt>\\W*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\">Unknown Location[^<]*</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_SpottedOwner = Pattern.compile("<dt>\\W*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\">In the hands of the owner[^<]*</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_Goal = Pattern.compile("<h3>\\W*Current GOAL[^<]*</h3>[^<]*<p[^>]*>(.*)</p>[^<]*<h3>\\W*About This Item[^<]*</h3>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_DetailsImage = Pattern.compile("<h3>\\W*About This Item[^<]*</h3>([^<]*<p>([^<]*<img id=\"ctl00_ContentBody_BugDetails_BugImage\" class=\"[^\"]+\" src=\"([^\"]+)\"[^>]*>)?[^<]*</p>)?[^<]*<p[^>]*>(.*)</p>[^<]*<div id=\"ctl00_ContentBody_BugDetails_uxAbuseReport\">", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_Icon = Pattern.compile("<img id=\"ctl00_ContentBody_BugTypeImage\" class=\"TravelBugHeaderIcon\" src=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_Type = Pattern.compile("<img id=\"ctl00_ContentBody_BugTypeImage\" class=\"TravelBugHeaderIcon\" src=\"[^\"]+\" alt=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_Distance = Pattern.compile("<h4[^>]*\\W*Tracking History \\(([0-9\\.,]+(km|mi))[^\\)]*\\)", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_TRACKABLE_Log = Pattern.compile("<tr class=\"Data.+?src=\"/images/icons/([^\\.]+)\\.gif[^>]+> ([^<]+)</td>.+?guid.+?>([^<]+)</a>.+?(?:guid=([^\"]+)\">([^<]+)</a>.+?)?<td colspan=\"4\">(.+?)(?:<ul.+?ul>)?\\s*</td>\\s*</tr>", Pattern.CASE_INSENSITIVE); - - public final static Map<String, String> cacheTypes = new HashMap<String, String>(); - public final static Map<String, String> cacheTypesInv = new HashMap<String, String>(); - public final static Map<String, String> cacheIDs = new HashMap<String, String>(); - public final static Map<String, String> cacheIDsChoices = new HashMap<String, String>(); - public final static Map<String, String> waypointTypes = new HashMap<String, String>(); - public final static Map<String, Integer> logTypes = new HashMap<String, Integer>(); - public final static Map<String, Integer> logTypes0 = new HashMap<String, Integer>(); - public final static Map<Integer, String> logTypes1 = new HashMap<Integer, String>(); - public final static Map<Integer, String> logTypes2 = new HashMap<Integer, String>(); - public final static Map<Integer, String> logTypesTrackable = new HashMap<Integer, String>(); - public final static Map<Integer, String> logTypesTrackableAction = new HashMap<Integer, String>(); - public final static Map<Integer, String> errorRetrieve = new HashMap<Integer, String>(); - public final static Map<String, SimpleDateFormat> gcCustomDateFormats; - static { - final String[] formats = new String[] { - "MM/dd/yyyy", - "yyyy-MM-dd", - "yyyy/MM/dd", - "dd/MMM/yyyy", + private final static Pattern patternGeocode = Pattern.compile("<meta name=\"og:url\" content=\"[^\"]+/(GC[0-9A-Z]+)\"[^>]*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final static Pattern patternCacheId = Pattern.compile("/seek/log\\.aspx\\?ID=(\\d+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final static Pattern patternCacheGuid = Pattern.compile(Pattern.quote("&wid=") + "([0-9a-z\\-]+)" + Pattern.quote("&"), Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final static Pattern patternType = Pattern.compile("<img src=\"[^\"]*/WptTypes/\\d+\\.gif\" alt=\"([^\"]+)\" (title=\"[^\"]*\" )?width=\"32\" height=\"32\"[^>]*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + + private final static Pattern patternName = Pattern.compile("<h2[^>]*>[^<]*<span id=\"ctl00_ContentBody_CacheName\">([^<]+)<\\/span>[^<]*<\\/h2>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final static Pattern patternSize = Pattern.compile("<div class=\"CacheSize[^\"]*\">[^<]*<p[^>]*>[^S]*Size[^:]*:[^<]*<span[^>]*>[^<]*<img src=\"[^\"]*/icons/container/[a-z_]+\\.gif\" alt=\"Size: ([^\"]+)\"[^>]*>[^<]*<small>[^<]*</small>[^<]*</span>[^<]*</p>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final static Pattern patternDifficulty = Pattern.compile("<span id=\"ctl00_ContentBody_uxLegendScale\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\" alt=\"[^\"]+\"[^>]*>[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final static Pattern patternTerrain = Pattern.compile("<span id=\"ctl00_ContentBody_Localize6\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\" alt=\"[^\"]+\"[^>]*>[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final static Pattern patternOwner = Pattern.compile("<span class=\"minorCacheDetails\">\\W*An?(\\W*Event)?\\W*cache\\W*by[^<]*<a href=\"[^\"]+\">([^<]+)</a>[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final static Pattern patternOwnerReal = Pattern.compile("<a id=\"ctl00_ContentBody_uxFindLinksHiddenByThisUser\" href=\"[^\"]*/seek/nearest\\.aspx\\?u=*([^\"]+)\">[^<]+</a>", Pattern.CASE_INSENSITIVE); + private final static Pattern patternHidden = Pattern.compile("<span[^>]*>\\W*Hidden[\\s:]*([^<]+)</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final static Pattern patternHiddenEvent = Pattern.compile("<span[^>]*>\\W*Event\\W*Date[^:]*:([^<]*)</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final static Pattern patternFavourite = Pattern.compile("<a id=\"uxFavContainerLink\"[^>]*>[^<]*<div[^<]*<span class=\"favorite-value\">[^\\d]*([0-9]+)[^\\d^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + + private final static Pattern patternFound = Pattern.compile("<p>[^<]*<a id=\"ctl00_ContentBody_hlFoundItLog\"[^<]*<img src=\".*/images/stockholm/16x16/check\\.gif\"[^>]*>[^<]*</a>[^<]*</p>", Pattern.CASE_INSENSITIVE); + private final static Pattern patternFoundAlternative = Pattern.compile("<div class=\"StatusInformationWidget FavoriteWidget\"", Pattern.CASE_INSENSITIVE); + private final static Pattern patternLatLon = Pattern.compile("<span id=\"ctl00_ContentBody_LatLon\"[^>]*>(<b>)?([^<]*)(<\\/b>)?<\\/span>", Pattern.CASE_INSENSITIVE); + private final static Pattern patternLocation = Pattern.compile("<span id=\"ctl00_ContentBody_Location\"[^>]*>In ([^<]*)", Pattern.CASE_INSENSITIVE); + private final static Pattern patternHint = Pattern.compile("<p>([^<]*<strong>)?\\W*Additional Hints([^<]*<\\/strong>)?[^\\(]*\\(<a[^>]+>Encrypt</a>\\)[^<]*<\\/p>[^<]*<div id=\"div_hint\"[^>]*>(.*)</div>[^<]*<div id=[\\'|\"]dk[\\'|\"]", Pattern.CASE_INSENSITIVE); + private final static Pattern patternPersonalNote = Pattern.compile("<p id=\"cache_note\"[^>]*>([^<]*)</p>", Pattern.CASE_INSENSITIVE); + private final static Pattern patternDescShort = Pattern.compile("<div class=\"UserSuppliedContent\">[^<]*<span id=\"ctl00_ContentBody_ShortDescription\"[^>]*>((?:(?!</span>[^\\w^<]*</div>).)*)</span>[^\\w^<]*</div>", Pattern.CASE_INSENSITIVE); + private final static Pattern patternDesc = Pattern.compile("<span id=\"ctl00_ContentBody_LongDescription\"[^>]*>" + "(.*)</span>[^<]*</div>[^<]*<p>[^<]*</p>[^<]*<p>[^<]*<strong>\\W*Additional Hints</strong>", Pattern.CASE_INSENSITIVE); + private final static Pattern patternCountLogs = Pattern.compile("<span id=\"ctl00_ContentBody_lblFindCounts\"><p(.+?)<\\/p><\\/span>", Pattern.CASE_INSENSITIVE); + private final static Pattern patternCountLog = Pattern.compile("src=\"\\/images\\/icons\\/(.+?).gif\"[^>]+> (\\d+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final static Pattern patternLog = Pattern.compile("<tr><td class.+?<a href=\"/profile/\\?guid=.+?>(.+?)</a>.+?(?:logOwnerStats[^>]+><img[^>]+icon_smile.+?> ([,\\d]+).+?)?LogType.+?<img.+?/images/icons/([^\\.]+)\\..+?title=\"(.+?)\".+?LogDate.+?>(.+?)<.+?LogText.+?>(.*?)</p>(.*?)</div></div></div></td></tr>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final static Pattern patternLogImgs = Pattern.compile("href=\"(http://img.geocaching.com/cache/log/.+?)\".+?<span>([^<]*)", Pattern.CASE_INSENSITIVE); + private final static Pattern patternAttributes = Pattern.compile("<h3 class=\"WidgetHeader\">[^<]*<img[^>]+>\\W*Attributes[^<]*</h3>[^<]*<div class=\"WidgetBody\">(([^<]*<img src=\"[^\"]+\" alt=\"[^\"]+\"[^>]*>)+)[^<]*<p", Pattern.CASE_INSENSITIVE); + private final static Pattern patternAttributesInside = Pattern.compile("[^<]*<img src=\"([^\"]+)\" alt=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); + private final static Pattern patternSpoilers = Pattern.compile("<span id=\"ctl00_ContentBody_Images\">((<a href=\"[^\"]+\"[^>]*>[^<]*<img[^>]+>[^<]*<span>[^>]+</span>[^<]*</a>[^<]*<br[^>]*>([^<]*(<br[^>]*>)+)?)+)[^<]*</span>", Pattern.CASE_INSENSITIVE); + private final static Pattern patternSpoilersInside = Pattern.compile("[^<]*<a href=\"([^\"]+)\"[^>]*>[^<]*<img[^>]+>[^<]*<span>([^>]+)</span>[^<]*</a>[^<]*<br[^>]*>(([^<]*)(<br[^<]*>)+)?", Pattern.CASE_INSENSITIVE); + private final static Pattern patternInventory = Pattern.compile("<span id=\"ctl00_ContentBody_uxTravelBugList_uxInventoryLabel\">\\W*Inventory[^<]*</span>[^<]*</h3>[^<]*<div class=\"WidgetBody\">([^<]*<ul>(([^<]*<li>[^<]*<a href=\"[^\"]+\"[^>]*>[^<]*<img src=\"[^\"]+\"[^>]*>[^<]*<span>[^<]+<\\/span>[^<]*<\\/a>[^<]*<\\/li>)+)[^<]*<\\/ul>)?", Pattern.CASE_INSENSITIVE); + private final static Pattern patternInventoryInside = Pattern.compile("[^<]*<li>[^<]*<a href=\"[a-z0-9\\-\\_\\.\\?\\/\\:\\@]*\\/track\\/details\\.aspx\\?guid=([0-9a-z\\-]+)[^\"]*\"[^>]*>[^<]*<img src=\"[^\"]+\"[^>]*>[^<]*<span>([^<]+)<\\/span>[^<]*<\\/a>[^<]*<\\/li>", Pattern.CASE_INSENSITIVE); + private final static Pattern patternOnWatchlist = Pattern.compile("<img\\s*src=\"\\/images\\/stockholm\\/16x16\\/icon_stop_watchlist.gif\"", Pattern.CASE_INSENSITIVE); + + private final static Pattern PATTERN_TRACKABLE_TrackableId = Pattern.compile("<a id=\"ctl00_ContentBody_LogLink\" title=\"[^\"]*\" href=\".*log\\.aspx\\?wid=([a-z0-9\\-]+)\"[^>]*>[^<]*</a>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_Geocode = Pattern.compile("<span id=\"ctl00_ContentBody_BugDetails_BugTBNum\" String=\"[^\"]*\">Use[^<]*<strong>(TB[0-9a-z]+)[^<]*</strong> to reference this item.[^<]*</span>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_Name = Pattern.compile("<h2>([^<]*<img[^>]*>)?[^<]*<span id=\"ctl00_ContentBody_lbHeading\">([^<]+)</span>[^<]*</h2>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_Owner = Pattern.compile("<dt>\\W*Owner:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugOwner\" title=\"[^\"]*\" href=\"[^\"]*/profile/\\?guid=([a-z0-9\\-]+)\">([^<]+)<\\/a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_Released = Pattern.compile("<dt>\\W*Released:[^<]*</dt>[^<]*<dd>[^<]*<span id=\"ctl00_ContentBody_BugDetails_BugReleaseDate\">([^<]+)<\\/span>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_Origin = Pattern.compile("<dt>\\W*Origin:[^<]*</dt>[^<]*<dd>[^<]*<span id=\"ctl00_ContentBody_BugDetails_BugOrigin\">([^<]+)<\\/span>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_SpottedCache = Pattern.compile("<dt>\\W*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\" title=\"[^\"]*\" href=\"[^\"]*/seek/cache_details.aspx\\?guid=([a-z0-9\\-]+)\">In ([^<]+)</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_SpottedUser = Pattern.compile("<dt>\\W*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\" href=\"[^\"]*/profile/\\?guid=([a-z0-9\\-]+)\">In the hands of ([^<]+).</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_SpottedUnknown = Pattern.compile("<dt>\\W*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\">Unknown Location[^<]*</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_SpottedOwner = Pattern.compile("<dt>\\W*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\">In the hands of the owner[^<]*</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_Goal = Pattern.compile("<h3>\\W*Current GOAL[^<]*</h3>[^<]*<p[^>]*>(.*)</p>[^<]*<h3>\\W*About This Item[^<]*</h3>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_DetailsImage = Pattern.compile("<h3>\\W*About This Item[^<]*</h3>([^<]*<p>([^<]*<img id=\"ctl00_ContentBody_BugDetails_BugImage\" class=\"[^\"]+\" src=\"([^\"]+)\"[^>]*>)?[^<]*</p>)?[^<]*<p[^>]*>(.*)</p>[^<]*<div id=\"ctl00_ContentBody_BugDetails_uxAbuseReport\">", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_Icon = Pattern.compile("<img id=\"ctl00_ContentBody_BugTypeImage\" class=\"TravelBugHeaderIcon\" src=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_Type = Pattern.compile("<img id=\"ctl00_ContentBody_BugTypeImage\" class=\"TravelBugHeaderIcon\" src=\"[^\"]+\" alt=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_Distance = Pattern.compile("<h4[^>]*\\W*Tracking History \\(([0-9\\.,]+(km|mi))[^\\)]*\\)", Pattern.CASE_INSENSITIVE); + private final static Pattern PATTERN_TRACKABLE_Log = Pattern.compile("<tr class=\"Data.+?src=\"/images/icons/([^\\.]+)\\.gif[^>]+> ([^<]+)</td>.+?guid.+?>([^<]+)</a>.+?(?:guid=([^\"]+)\">([^<]+)</a>.+?)?<td colspan=\"4\">(.+?)(?:<ul.+?ul>)?\\s*</td>\\s*</tr>", Pattern.CASE_INSENSITIVE); + + public final static Map<String, String> cacheTypes = new HashMap<String, String>(); + public final static Map<String, String> cacheTypesInv = new HashMap<String, String>(); + public final static Map<String, String> cacheIDs = new HashMap<String, String>(); + public final static Map<String, String> cacheIDsChoices = new HashMap<String, String>(); + public final static Map<String, String> waypointTypes = new HashMap<String, String>(); + public final static Map<String, Integer> logTypes = new HashMap<String, Integer>(); + public final static Map<String, Integer> logTypes0 = new HashMap<String, Integer>(); + public final static Map<Integer, String> logTypes1 = new HashMap<Integer, String>(); + public final static Map<Integer, String> logTypes2 = new HashMap<Integer, String>(); + public final static Map<Integer, String> logTypesTrackable = new HashMap<Integer, String>(); + public final static Map<Integer, String> logTypesTrackableAction = new HashMap<Integer, String>(); + public final static Map<Integer, String> errorRetrieve = new HashMap<Integer, String>(); + public final static Map<String, SimpleDateFormat> 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<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>(); - - 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 Map<String, String> cookies = new HashMap<String, String>(); - private static final String passMatch = "[/\\?&]*[Pp]ass(word)?=[^&^#^$]+"; - private static final Pattern patternLoggedIn = Pattern.compile("<span class=\"Success\">You are logged in as[^<]*<strong[^>]*>([^<]+)</strong>[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private static final Pattern patternLogged2In = Pattern.compile("<strong>\\W*Hello,[^<]*<a[^>]+>([^<]+)</a>[^<]*</strong>", 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 patternIsPremium = Pattern.compile("<span id=\"ctl00_litPMLevel\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - 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; - private cgSettings settings = null; - private SharedPreferences prefs = null; - public String version = null; - private 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<String, Integer> gcIcons = new HashMap<String, Integer>(); - final private static Map<String, Integer> wpIcons = new HashMap<String, Integer>(); - - 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(); - - // setup cache type mappings - - final String CACHETYPE_ALL_GUID = "9a79e6ce-3344-409c-bbe9-496530baf758"; + "dd MMM yy", + "dd/MM/yyyy" + }; + + Map<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>(); + + 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 Map<String, String> cookies = new HashMap<String, String>(); + private static final String passMatch = "[/\\?&]*[Pp]ass(word)?=[^&^#^$]+"; + private static final Pattern patternLoggedIn = Pattern.compile("<span class=\"Success\">You are logged in as[^<]*<strong[^>]*>([^<]+)</strong>[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private static final Pattern patternLogged2In = Pattern.compile("<strong>\\W*Hello,[^<]*<a[^>]+>([^<]+)</a>[^<]*</strong>", 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 patternIsPremium = Pattern.compile("<span id=\"ctl00_litPMLevel\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + 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; + private cgSettings settings = null; + private SharedPreferences prefs = null; + public String version = null; + private 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<String, Integer> gcIcons = new HashMap<String, Integer>(); + final private static Map<String, Integer> wpIcons = new HashMap<String, Integer>(); + + 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(); + + // 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); @@ -230,1596 +231,1592 @@ public class cgBase { cacheIDsChoices.put(l10n, ct.guid); } - // 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"; - } - } - } - - /** - * 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) - // no viewstates were present - return null; - else - return viewstates; - } - - /** - * put viewstates into request parameters - */ - private static void setViewstates(String[] viewstates, Map<String, String> 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(String page, Map<String, String> 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 int login() { - final String host = "www.geocaching.com"; - final String path = "/login/default.aspx"; - cgResponse loginResponse = null; - String loginData = null; - - String[] viewstates = null; - - final Map<String, String> loginStart = settings.getLogin(); - - if (loginStart == null) { - return -3; // no login information stored - } - - loginResponse = request(true, host, path, "GET", new HashMap<String, String>(), false, false, false); - loginData = loginResponse.getData(); - if (StringUtils.isNotBlank(loginData)) { - if (checkLogin(loginData)) { - Log.i(cgSettings.tag, "Already logged in Geocaching.com as " + loginStart.get("username")); - - switchToEnglish(viewstates); - - return 1; // logged in - } - - viewstates = getViewstates(loginData); - - if (isEmpty(viewstates)) { - Log.e(cgSettings.tag, "cgeoBase.login: Failed to find viewstates"); - return -1; // no viewstates - } - } else { - Log.e(cgSettings.tag, "cgeoBase.login: Failed to retrieve login page (1st)"); - return -2; // no loginpage - } - - final Map<String, String> login = settings.getLogin(); - final Map<String, String> params = new HashMap<String, String>(); - - if (login == null || StringUtils.isEmpty(login.get("username")) || StringUtils.isEmpty(login.get("password"))) { - Log.e(cgSettings.tag, "cgeoBase.login: No login information stored"); - return -3; - } - - settings.deleteCookies(); - - params.put("__EVENTTARGET", ""); - params.put("__EVENTARGUMENT", ""); - setViewstates(viewstates, params); - 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 (StringUtils.isNotBlank(loginData)) { - if (checkLogin(loginData)) { - Log.i(cgSettings.tag, "Successfully logged in Geocaching.com as " + login.get("username")); - - switchToEnglish(getViewstates(loginData)); - - 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 (StringUtils.isBlank(page)) { - Log.e(cgSettings.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 String switchToEnglish(String[] viewstates) { - final String host = "www.geocaching.com"; - final String path = "/default.aspx"; - final Map<String, String> params = new HashMap<String, String>(); - - setViewstates(viewstates, params); - 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 (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgeoBase.parseSearch: No page given"); - return null; - } - - final cgCacheWrap caches = new cgCacheWrap(); - final List<String> cids = new ArrayList<String>(); - final List<String> guids = new ArrayList<String>(); - String recaptchaChallenge = null; - String recaptchaText = null; - - caches.url = url; - - final Pattern patternCacheType = Pattern.compile("<td class=\"Merge\">[^<]*<a href=\"[^\"]*/seek/cache_details\\.aspx\\?guid=[^\"]+\"[^>]+>[^<]*<img src=\"[^\"]*/images/wpttypes/[^\\.]+\\.gif\" alt=\"([^\"]+)\" title=\"[^\"]+\"[^>]*>[^<]*</a>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - final Pattern patternGuidAndDisabled = Pattern.compile("<img src=\"[^\"]*/images/wpttypes/[^>]*>[^<]*</a></td><td class=\"Merge\">[^<]*<a href=\"[^\"]*/seek/cache_details\\.aspx\\?guid=([a-z0-9\\-]+)\" class=\"lnk([^\"]*)\">([^<]*<span>)?([^<]*)(</span>[^<]*)?</a>[^<]+<br />([^<]*)<span[^>]+>([^<]*)</span>([^<]*<img[^>]+>)?[^<]*<br />[^<]*</td>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - final Pattern patternTbs = Pattern.compile("<a id=\"ctl00_ContentBody_dlResults_ctl[0-9]+_uxTravelBugList\" class=\"tblist\" data-tbcount=\"([0-9]+)\" data-id=\"[^\"]*\"[^>]*>(.*)</a>", Pattern.CASE_INSENSITIVE); - final Pattern patternTbsInside = Pattern.compile("(<img src=\"[^\"]+\" alt=\"([^\"]+)\" title=\"[^\"]*\" />[^<]*)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - final Pattern patternDirection = Pattern.compile("<img id=\"ctl00_ContentBody_dlResults_ctl[0-9]+_uxDistanceAndHeading\" title=\"[^\"]*\" src=\"[^\"]*/seek/CacheDir\\.ashx\\?k=([^\"]+)\"[^>]*>", 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("<span id=\"ctl00_ContentBody_dlResults_ctl[0-9]+_uxFavoritesValue\" title=\"[^\"]*\" class=\"favorite-rank\">([0-9]+)</span>", Pattern.CASE_INSENSITIVE); - final Pattern patternTotalCnt = Pattern.compile("<td class=\"PageBuilderWidget\"><span>Total Records[^<]*<b>(\\d+)<\\/b>", Pattern.CASE_INSENSITIVE); - final Pattern patternRecaptcha = Pattern.compile("<script[^>]*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 String recaptchaJs = request(false, "www.google.com", "/recaptcha/api/challenge", "GET", "k=" + urlencode_rfc3986(recaptchaJsParam.trim()), 0, true).getData(); - - 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(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse recaptcha challenge"); - } - - if (thread != null && StringUtils.isNotBlank(recaptchaChallenge)) { - thread.setChallenge(recaptchaChallenge); - thread.notifyNeed(); - } - } - - if (page.indexOf("SearchResultsTable") < 0) { - // there are no results. aborting here avoids a wrong error log in the next parsing step - return caches; - } - - int startPos = page.indexOf("<div id=\"ctl00_ContentBody_ResultsPanel\""); - if (startPos == -1) { - Log.e(cgSettings.tag, "cgeoBase.parseSearch: ID \"ctl00_ContentBody_dlResults\" not found on page"); - return null; - } - - page = page.substring(startPos); // cut on <table - - startPos = page.indexOf(">"); - int 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 <table> and </table> - - final String[] rows = page.split("<tr class="); - final int rows_count = rows.length; - - for (int z = 1; z < rows_count; z++) { - cgCache cache = new cgCache(); - String row = rows[z]; - - // check for cache type presence - if (row.indexOf("images/wpttypes") == -1) { - continue; - } - - try { - final Matcher matcherGuidAndDisabled = patternGuidAndDisabled.matcher(row); - - while (matcherGuidAndDisabled.find()) { - if (matcherGuidAndDisabled.groupCount() > 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(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse GUID and/or Disabled data"); - } - - if (settings.excludeDisabled == 1 && (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(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 - 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(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 (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(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 || cache.archived) { // 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 = Integer.valueOf(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 && StringUtils.isNotBlank(recaptchaText)))) { - 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=&__EVENTARGUMENT="); - if (ArrayUtils.isNotEmpty(caches.viewstates)) { - params.append("&__VIEWSTATE="); - params.append(urlencode_rfc3986(caches.viewstates[0])); - if (caches.viewstates.length > 1) { - for (int i = 1; i < caches.viewstates.length; i++) { - params.append("&__VIEWSTATE" + i + "="); - params.append(urlencode_rfc3986(caches.viewstates[i])); - } - params.append("&__VIEWSTATEFIELDCOUNT=" + caches.viewstates.length); - } - } - for (String cid : cids) { - params.append("&CID="); - params.append(urlencode_rfc3986(cid)); - } - - if (recaptchaChallenge != null && StringUtils.isNotBlank(recaptchaText)) { - params.append("&recaptcha_challenge_field="); - params.append(urlencode_rfc3986(recaptchaChallenge)); - params.append("&recaptcha_response_field="); - params.append(urlencode_rfc3986(recaptchaText)); - } - params.append("&ctl00%24ContentBody%24uxDownloadLoc=Download+Waypoints"); - - final String coordinates = request(false, host, path, "POST", params.toString(), 0, true).getData(); - - if ( StringUtils.isNotBlank(coordinates)) { - 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; - } - } - - LocParser.parseLoc(caches, coordinates); - } catch (Exception e) { - Log.e(cgSettings.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(cgSettings.tag, "Trying to get ratings for " + cids.size() + " caches"); - - try { - final Map<String, cgRating> ratings = getRating(guids, null); - - if (CollectionUtils.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(cgSettings.tag, "cgBase.parseSearch.GCvote: " + e.toString()); - } - } - - return caches; - } - - public static cgCacheWrap parseMapJSON(String url, String data) { - if (StringUtils.isEmpty(data)) { - 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 (StringUtils.isBlank(json)) { - 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.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 = "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 (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgeoBase.parseCache: No page given"); - return null; - } - - final cgCacheWrap caches = new cgCacheWrap(); - final cgCache cache = new cgCache(); - - if (page.indexOf("Cache is Unpublished") > -1) { - caches.error = "cache was unpublished"; - return caches; - } - - if (page.indexOf("Sorry, the owner of this listing has made it viewable to Premium Members only.") != -1) { - caches.error = "requested cache is for premium members only"; - return caches; - } - - if (page.indexOf("has chosen to make this cache listing visible to Premium Members only.") != -1) { - caches.error = "requested cache is for premium members only"; - return caches; - } - - if (page.indexOf("<li>This cache is temporarily unavailable.") != -1) { - cache.disabled = true; - } else { - cache.disabled = false; - } - - if (page.indexOf("<li>This cache has been archived,") != -1) { - cache.archived = true; - } else { - cache.archived = false; - } - - if (page.indexOf("<p class=\"Warning\">This is a Premium Member Only cache.</p>") != -1) { - cache.members = true; - } else { - cache.members = false; - } - - cache.reason = reason; - - // cache geocode - try { - final Matcher matcherGeocode = patternGeocode.matcher(page); - if (matcherGeocode.find() && matcherGeocode.groupCount() > 0) { - cache.geocode = getMatch(matcherGeocode.group(1)); - } - } catch (Exception e) { - // failed to parse cache geocode - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache geocode"); - } - - // cache id - try { - final Matcher matcherCacheId = patternCacheId.matcher(page); - if (matcherCacheId.find() && matcherCacheId.groupCount() > 0) { - cache.cacheId = getMatch(matcherCacheId.group(1)); - } - } catch (Exception e) { - // failed to parse cache id - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache id"); - } - - // cache guid - try { - final Matcher matcherCacheGuid = patternCacheGuid.matcher(page); - if (matcherCacheGuid.find() && matcherCacheGuid.groupCount() > 0) { - cache.guid = getMatch(matcherCacheGuid.group(1)); - } - } catch (Exception e) { - // failed to parse cache guid - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache guid"); - } - - // name - try { - final Matcher matcherName = patternName.matcher(page); - if (matcherName.find() && matcherName.groupCount() > 0) { - cache.name = Html.fromHtml(matcherName.group(1)).toString(); - } - } catch (Exception e) { - // failed to parse cache name - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache name"); - } - - // owner real name - try { - final Matcher matcherOwnerReal = patternOwnerReal.matcher(page); - if (matcherOwnerReal.find() && matcherOwnerReal.groupCount() > 0) { - cache.ownerReal = URLDecoder.decode(matcherOwnerReal.group(1)); - } - } catch (Exception e) { - // failed to parse owner real name - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache owner real name"); - } - - 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(cgSettings.tag, "cgeoBase.parseCache: ID \"cacheDetails\" not found on page"); - return null; - } - - tableInside = tableInside.substring(pos); - - pos = tableInside.indexOf("<div class=\"CacheInformationTable\""); - if (pos == -1) { - Log.e(cgSettings.tag, "cgeoBase.parseCache: ID \"CacheInformationTable\" not found on page"); - return null; - } - - tableInside = tableInside.substring(0, pos); - - if (StringUtils.isNotBlank(tableInside)) { - // cache terrain - try { - final Matcher matcherTerrain = patternTerrain.matcher(tableInside); - if (matcherTerrain.find() && matcherTerrain.groupCount() > 0) { - cache.terrain = new Float(Pattern.compile("_").matcher(matcherTerrain.group(1)).replaceAll(".")); - } - } catch (Exception e) { - // failed to parse terrain - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache terrain"); - } - - // cache difficulty - try { - final Matcher matcherDifficulty = patternDifficulty.matcher(tableInside); - if (matcherDifficulty.find() && matcherDifficulty.groupCount() > 0) { - cache.difficulty = new Float(Pattern.compile("_").matcher(matcherDifficulty.group(1)).replaceAll(".")); - } - } catch (Exception e) { - // failed to parse difficulty - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache difficulty"); - } - - // owner - try { - final Matcher matcherOwner = patternOwner.matcher(tableInside); - if (matcherOwner.find() && matcherOwner.groupCount() > 0) { - cache.owner = Html.fromHtml(matcherOwner.group(2)).toString(); - } - } catch (Exception e) { - // failed to parse owner - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache owner"); - } - - // hidden - try { - final Matcher matcherHidden = patternHidden.matcher(tableInside); - if (matcherHidden.find() && matcherHidden.groupCount() > 0) { - cache.hidden = parseGcCustomDate(matcherHidden.group(1)); - } - } catch (ParseException e) { - // failed to parse cache hidden date - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache hidden date"); - } - - if (cache.hidden == null) { - // event date - try { - final Matcher matcherHiddenEvent = patternHiddenEvent.matcher(tableInside); - if (matcherHiddenEvent.find() && matcherHiddenEvent.groupCount() > 0) { - cache.hidden = parseGcCustomDate(matcherHiddenEvent.group(1)); - } - } catch (ParseException e) { - // failed to parse cache event date - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache event date"); - } - } - - // favourite - try { - final Matcher matcherFavourite = patternFavourite.matcher(tableInside); - if (matcherFavourite.find() && matcherFavourite.groupCount() > 0) { - cache.favouriteCnt = Integer.parseInt(matcherFavourite.group(1)); - } - } catch (Exception e) { - // failed to parse favourite count - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse favourite count"); - } - - // cache size - try { - final Matcher matcherSize = patternSize.matcher(tableInside); - if (matcherSize.find() && matcherSize.groupCount() > 0) { - cache.size = getMatch(matcherSize.group(1)).toLowerCase(); - } - } catch (Exception e) { - // failed to parse size - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache size"); - } - } - - // cache found - cache.found = patternFound.matcher(page).find() || patternFoundAlternative.matcher(page).find(); - - // cache type - try { - final Matcher matcherType = patternType.matcher(page); - if (matcherType.find() && matcherType.groupCount() > 0) { - cache.type = cacheTypes.get(matcherType.group(1).toLowerCase()); - } - } catch (Exception e) { - // failed to parse type - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache type"); - } - - // on watchlist - try { - final Matcher matcher = patternOnWatchlist.matcher(page); - cache.onWatchlist = matcher.find(); - } catch (Exception e) { - // failed to parse watchlist state - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse watchlist state"); - } - - // latitude and logitude - try { - final Matcher matcherLatLon = patternLatLon.matcher(page); - if (matcherLatLon.find() && matcherLatLon.groupCount() > 0) { - cache.latlon = getMatch(matcherLatLon.group(2)); // first is <b> - - Map<String, Object> tmp = cgBase.parseLatlon(cache.latlon); - if (tmp.size() > 0) { - cache.coords = new Geopoint((Double) tmp.get("latitude"), (Double) tmp.get("longitude")); - cache.latitudeString = (String) tmp.get("latitudeString"); - cache.longitudeString = (String) tmp.get("longitudeString"); - cache.reliableLatLon = true; - } - tmp = null; - } - } catch (Exception e) { - // failed to parse latitude and/or longitude - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache coordinates"); - } - - // cache location - try { - final Matcher matcherLocation = patternLocation.matcher(page); - if (matcherLocation.find() && matcherLocation.groupCount() > 0) { - cache.location = getMatch(matcherLocation.group(1)); - } - } catch (Exception e) { - // failed to parse location - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache location"); - } - - // cache hint - try { - final Matcher matcherHint = patternHint.matcher(page); - if (matcherHint.find() && matcherHint.groupCount() > 2 && matcherHint.group(3) != null) { - // replace linebreak and paragraph tags - String hint = Pattern.compile("<(br|p)[^>]*>").matcher(matcherHint.group(3)).replaceAll("\n"); - if (hint != null) { - cache.hint = hint.replaceAll(Pattern.quote("</p>"), "").trim(); - } - } - } catch (Exception e) { - // failed to parse hint - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache hint"); - } - - checkFields(cache); - /* - // short info debug - Log.d(cgSettings.tag, "gc-code: " + cache.geocode); - Log.d(cgSettings.tag, "id: " + cache.cacheid); - Log.d(cgSettings.tag, "guid: " + cache.guid); - Log.d(cgSettings.tag, "name: " + cache.name); - Log.d(cgSettings.tag, "terrain: " + cache.terrain); - Log.d(cgSettings.tag, "difficulty: " + cache.difficulty); - Log.d(cgSettings.tag, "owner: " + cache.owner); - Log.d(cgSettings.tag, "owner (real): " + cache.ownerReal); - Log.d(cgSettings.tag, "hidden: " + dateOutShort.format(cache.hidden)); - Log.d(cgSettings.tag, "favorite: " + cache.favouriteCnt); - Log.d(cgSettings.tag, "size: " + cache.size); - if (cache.found) { - Log.d(cgSettings.tag, "found!"); - } else { - Log.d(cgSettings.tag, "not found"); - } - Log.d(cgSettings.tag, "type: " + cache.type); - Log.d(cgSettings.tag, "latitude: " + String.format("%.6f", cache.latitude)); - Log.d(cgSettings.tag, "longitude: " + String.format("%.6f", cache.longitude)); - Log.d(cgSettings.tag, "location: " + cache.location); - Log.d(cgSettings.tag, "hint: " + cache.hint); - */ - - // cache personal note - try { - final Matcher matcherPersonalNote = patternPersonalNote.matcher(page); - if (matcherPersonalNote.find() && matcherPersonalNote.groupCount() > 0) { - cache.personalNote = getMatch(matcherPersonalNote.group(1)); - } - } catch (Exception e) { - // failed to parse cache personal note - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache personal note"); - } - - // cache short description - try { - final Matcher matcherDescShort = patternDescShort.matcher(page); - if (matcherDescShort.find() && matcherDescShort.groupCount() > 0) { - cache.shortdesc = getMatch(matcherDescShort.group(1)); - } - } catch (Exception e) { - // failed to parse short description - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache short description"); - } - - // cache description - try { - final Matcher matcherDesc = patternDesc.matcher(page); - if (matcherDesc.find() && matcherDesc.groupCount() > 0) { - cache.description = getMatch(matcherDesc.group(1)); - } - } catch (Exception e) { - // failed to parse short description - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache description"); - } - - // cache attributes - try { - final Matcher matcherAttributes = patternAttributes.matcher(page); - if (matcherAttributes.find() && matcherAttributes.groupCount() > 0) { - final String attributesPre = matcherAttributes.group(1); - final Matcher matcherAttributesInside = patternAttributesInside.matcher(attributesPre); - - while (matcherAttributesInside.find()) { - if (matcherAttributesInside.groupCount() > 1 && matcherAttributesInside.group(2).equalsIgnoreCase("blank") != true) { - if (cache.attributes == null) { - cache.attributes = new ArrayList<String>(); - } - // by default, use the tooltip of the attribute - String attribute = matcherAttributesInside.group(2).toLowerCase(); - - // if the image name can be recognized, use the image name as attribute - String imageName = matcherAttributesInside.group(1).trim(); - if (imageName.length() > 0) { - int start = imageName.lastIndexOf('/'); - int end = imageName.lastIndexOf('.'); - if (start >= 0 && end>= 0) { - attribute = imageName.substring(start + 1, end).replace('-', '_').toLowerCase(); - } - } - cache.attributes.add(attribute); - } - } - } - } catch (Exception e) { - // failed to parse cache attributes - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache attributes"); - } - - // cache spoilers - try { - final Matcher matcherSpoilers = patternSpoilers.matcher(page); - if (matcherSpoilers.find() && matcherSpoilers.groupCount() > 0) { - final String spoilersPre = matcherSpoilers.group(1); - final Matcher matcherSpoilersInside = patternSpoilersInside.matcher(spoilersPre); - - while (matcherSpoilersInside.find()) { - if (matcherSpoilersInside.groupCount() > 0) { - final cgImage spoiler = new cgImage(); - spoiler.url = matcherSpoilersInside.group(1); - - if (matcherSpoilersInside.group(2) != null) { - spoiler.title = matcherSpoilersInside.group(2); - } - if (matcherSpoilersInside.group(4) != null) { - spoiler.description = matcherSpoilersInside.group(4); - } - - if (cache.spoilers == null) { - cache.spoilers = new ArrayList<cgImage>(); - } - cache.spoilers.add(spoiler); - } - } - } - } catch (Exception e) { - // failed to parse cache spoilers - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache spoilers"); - } - - // cache inventory - try { - cache.inventoryItems = 0; - - final Matcher matcherInventory = patternInventory.matcher(page); - if (matcherInventory.find()) { - if (cache.inventory == null) { - cache.inventory = new ArrayList<cgTrackable>(); - } - - if (matcherInventory.groupCount() > 1) { - final String inventoryPre = matcherInventory.group(2); - - if (StringUtils.isNotBlank(inventoryPre)) { - final Matcher matcherInventoryInside = patternInventoryInside.matcher(inventoryPre); - - while (matcherInventoryInside.find()) { - if (matcherInventoryInside.groupCount() > 0) { - final cgTrackable inventoryItem = new cgTrackable(); - inventoryItem.guid = matcherInventoryInside.group(1); - inventoryItem.name = matcherInventoryInside.group(2); - - cache.inventory.add(inventoryItem); - cache.inventoryItems++; - } - } - } - } - } - } catch (Exception e) { - // failed to parse cache inventory - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache inventory (2)"); - } - - // cache logs counts - try - { - final Matcher matcherLogCounts = patternCountLogs.matcher(page); - - if (matcherLogCounts.find()) - { - final Matcher matcherLog = patternCountLog.matcher(matcherLogCounts.group(1)); - - while (matcherLog.find()) - { - String typeStr = matcherLog.group(1); - String countStr = matcherLog.group(2); - - if (StringUtils.isNotBlank(typeStr) - && logTypes.containsKey(typeStr.toLowerCase()) - && StringUtils.isNotBlank(countStr)) - { - cache.logCounts.put(logTypes.get(typeStr.toLowerCase()), Integer.parseInt(countStr)); - } - } - } - } - catch (Exception e) - { - // failed to parse logs - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache log count"); - } - - // cache logs - try - { - /* - 1- Author - 2- Finds-count - 3- Log type image name (e.g. "icon_smile") - 4- Type string (e.g. "Found it") - 5- Date string (e.g. "04/28/2010") - 6- Log text - 7- The rest (e.g. log-images, maybe faster) - */ - final Matcher matcherLog = patternLog.matcher(page);//(matcherLogs.group(1)); - - while (matcherLog.find()) - { - final cgLog logDone = new cgLog(); - - final String logIconName = matcherLog.group(3).toLowerCase(); - if (logTypes.containsKey(logIconName)) - { - logDone.type = logTypes.get(logIconName); - } - else - { - logDone.type = logTypes.get("icon_note"); - } - - try - { - logDone.date = parseGcCustomDate(matcherLog.group(5)).getTime(); - } - catch (ParseException e) - { - Log.w(cgSettings.tag, "Failed to parse log date."); - } - - logDone.author = Html.fromHtml(matcherLog.group(1)).toString(); - - if (null != matcherLog.group(2)) - { - logDone.found = Integer.parseInt(matcherLog.group(2).replaceAll(",", "")); - } - - logDone.log = matcherLog.group(6); - - final Matcher matcherImg = patternLogImgs.matcher(matcherLog.group(7)); - while (matcherImg.find()) - { - final cgImage logImage = new cgImage(); - logImage.url = matcherImg.group(1); - logImage.title = matcherImg.group(2); - if (logDone.logImages == null) - { - logDone.logImages = new ArrayList<cgImage>(); - } - logDone.logImages.add(logImage); - } - - if (null == cache.logs) - { - cache.logs = new ArrayList<cgLog>(); - } - cache.logs.add(logDone); - } - } - catch (Exception e) - { - // failed to parse logs - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache logs", e); - } - - int wpBegin = 0; - int wpEnd = 0; - - wpBegin = page.indexOf("<table class=\"Table\" id=\"ctl00_ContentBody_Waypoints\">"); - if (wpBegin != -1) { // parse waypoints - final Pattern patternWpType = Pattern.compile("\\/wpttypes\\/sm\\/(.+)\\.jpg", Pattern.CASE_INSENSITIVE); - final Pattern patternWpPrefixOrLookupOrLatlon = Pattern.compile(">([^<]*<[^>]+>)?([^<]+)(<[^>]+>[^<]*)?<\\/td>", Pattern.CASE_INSENSITIVE); - final Pattern patternWpName = Pattern.compile(">[^<]*<a[^>]+>([^<]*)<\\/a>", Pattern.CASE_INSENSITIVE); - final Pattern patternWpNote = Pattern.compile("colspan=\"6\">(.*)<\\/td>", Pattern.CASE_INSENSITIVE); - - String wpList = page.substring(wpBegin); - - wpEnd = wpList.indexOf("</p>"); - if (wpEnd > -1 && wpEnd <= wpList.length()) { - wpList = wpList.substring(0, wpEnd); - } - - if (wpList.indexOf("No additional waypoints to display.") == -1) { - wpEnd = wpList.indexOf("</table>"); - wpList = wpList.substring(0, wpEnd); - - wpBegin = wpList.indexOf("<tbody>"); - wpEnd = wpList.indexOf("</tbody>"); - if (wpBegin >= 0 && wpEnd >= 0 && wpEnd <= wpList.length()) { - wpList = wpList.substring(wpBegin + 7, wpEnd); - } - - final String[] wpItems = wpList.split("<tr"); - - String[] wp; - for (int j = 1; j < wpItems.length; j++) { - final cgWaypoint waypoint = new cgWaypoint(); - - wp = wpItems[j].split("<td"); - - // waypoint type - try { - final Matcher matcherWpType = patternWpType.matcher(wp[3]); - if (matcherWpType.find() && matcherWpType.groupCount() > 0) { - waypoint.type = matcherWpType.group(1).trim(); - } - } catch (Exception e) { - // failed to parse type - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint type"); - } - - // waypoint prefix - try { - final Matcher matcherWpPrefix = patternWpPrefixOrLookupOrLatlon.matcher(wp[4]); - if (matcherWpPrefix.find() && matcherWpPrefix.groupCount() > 1) { - waypoint.prefix = matcherWpPrefix.group(2).trim(); - } - } catch (Exception e) { - // failed to parse prefix - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint prefix"); - } - - // waypoint lookup - try { - final Matcher matcherWpLookup = patternWpPrefixOrLookupOrLatlon.matcher(wp[5]); - if (matcherWpLookup.find() && matcherWpLookup.groupCount() > 1) { - waypoint.lookup = matcherWpLookup.group(2).trim(); - } - } catch (Exception e) { - // failed to parse lookup - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint lookup"); - } - - // waypoint name - try { - final Matcher matcherWpName = patternWpName.matcher(wp[6]); - while (matcherWpName.find()) { - if (matcherWpName.groupCount() > 0) { - waypoint.name = matcherWpName.group(1); - if (StringUtils.isNotBlank(waypoint.name)) { - waypoint.name = waypoint.name.trim(); - } - } - if (matcherWpName.find() && matcherWpName.groupCount() > 0) { - waypoint.name = matcherWpName.group(1).trim(); - } - } - } catch (Exception e) { - // failed to parse name - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint name"); - } - - // waypoint latitude and logitude - try { - final Matcher matcherWpLatLon = patternWpPrefixOrLookupOrLatlon.matcher(wp[7]); - if (matcherWpLatLon.find() && matcherWpLatLon.groupCount() > 1) { - waypoint.latlon = Html.fromHtml(matcherWpLatLon.group(2)).toString(); - - final Map<String, Object> tmp = cgBase.parseLatlon(waypoint.latlon); - if (tmp.size() > 0) { - waypoint.coords = new Geopoint((Double) tmp.get("latitude"), (Double) tmp.get("longitude")); - waypoint.latitudeString = (String) tmp.get("latitudeString"); - waypoint.longitudeString = (String) tmp.get("longitudeString"); - } - } - } catch (Exception e) { - // failed to parse latitude and/or longitude - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint coordinates"); - } - - j++; - if (wpItems.length > j) { - wp = wpItems[j].split("<td"); - } - - // waypoint note - try { - final Matcher matcherWpNote = patternWpNote.matcher(wp[3]); - if (matcherWpNote.find() && matcherWpNote.groupCount() > 0) { - waypoint.note = matcherWpNote.group(1).trim(); - } - } catch (Exception e) { - // failed to parse note - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint note"); - } - - if (cache.waypoints == null) { - cache.waypoints = new ArrayList<cgWaypoint>(); - } - cache.waypoints.add(waypoint); - } - } - } - - if (cache.coords != null) { - cache.elevation = getElevation(cache.coords); - } - - final cgRating rating = getRating(cache.guid, cache.geocode); - if (rating != null) { - cache.rating = rating.rating; - cache.votes = rating.votes; - cache.myVote = rating.myVote; - } - - cache.updated = System.currentTimeMillis(); - cache.detailedUpdate = System.currentTimeMillis(); - cache.detailed = true; - caches.cacheList.add(cache); - - return caches; - } - - private static void checkFields(cgCache cache) { - if (StringUtils.isBlank(cache.geocode)) { - Log.w(cgSettings.tag, "geo code not parsed correctly"); - } - if (StringUtils.isBlank(cache.name)) { - Log.w(cgSettings.tag, "name not parsed correctly"); - } - if (StringUtils.isBlank(cache.guid)) { - Log.w(cgSettings.tag, "guid not parsed correctly"); - } - if (cache.terrain == null || cache.terrain == 0.0) { - Log.w(cgSettings.tag, "terrain not parsed correctly"); - } - if (cache.difficulty == null || cache.difficulty == 0.0) { - Log.w(cgSettings.tag, "difficulty not parsed correctly"); - } - if (StringUtils.isBlank(cache.owner)) { - Log.w(cgSettings.tag, "owner not parsed correctly"); - } - if (StringUtils.isBlank(cache.ownerReal)) { - Log.w(cgSettings.tag, "owner real not parsed correctly"); - } - if (cache.hidden == null) { - Log.w(cgSettings.tag, "hidden not parsed correctly"); - } - if (cache.favouriteCnt == null) { - Log.w(cgSettings.tag, "favoriteCount not parsed correctly"); - } - if (StringUtils.isBlank(cache.size)) { - Log.w(cgSettings.tag, "size not parsed correctly"); - } - if (StringUtils.isBlank(cache.type)) { - Log.w(cgSettings.tag, "type not parsed correctly"); - } - if (cache.coords == null) { - Log.w(cgSettings.tag, "coordinates not parsed correctly"); - } - if (StringUtils.isBlank(cache.location)) { - Log.w(cgSettings.tag, "location not parsed correctly"); - } - } - - private static String getMatch(String match) { - // creating a new String via String constructor is necessary here!! - return new String(match.trim()); - // Java copies the whole page String, when matching with regular expressions - // later this would block the garbage collector, as we only need tiny parts of the page - // see http://developer.android.com/reference/java/lang/String.html#backing_array - - // And BTW: You cannot even see that effect in the debugger, but must use a separate memory profiler! - } - - public Date parseGcCustomDate(String input) - throws ParseException - { - if (StringUtils.isBlank(input)) - { - throw new ParseException("Input is null", 0); - } - - input = input.trim(); - - if (null != settings - //&& null != settings.getGcCustomDate() - && gcCustomDateFormats.containsKey(settings.getGcCustomDate())) - { - try + // 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"; + } + } + } + + /** + * 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) + // no viewstates were present + return null; + else + return viewstates; + } + + /** + * put viewstates into request parameters + */ + private static void setViewstates(String[] viewstates, Map<String, String> 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(String page, Map<String, String> 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 int login() { + final String host = "www.geocaching.com"; + final String path = "/login/default.aspx"; + cgResponse loginResponse = null; + String loginData = null; + + String[] viewstates = null; + + final Map<String, String> loginStart = settings.getLogin(); + + if (loginStart == null) { + return -3; // no login information stored + } + + loginResponse = request(true, host, path, "GET", new HashMap<String, String>(), false, false, false); + loginData = loginResponse.getData(); + if (StringUtils.isNotBlank(loginData)) { + if (checkLogin(loginData)) { + Log.i(cgSettings.tag, "Already logged in Geocaching.com as " + loginStart.get("username")); + + switchToEnglish(viewstates); + + return 1; // logged in + } + + viewstates = getViewstates(loginData); + + if (isEmpty(viewstates)) { + Log.e(cgSettings.tag, "cgeoBase.login: Failed to find viewstates"); + return -1; // no viewstates + } + } else { + Log.e(cgSettings.tag, "cgeoBase.login: Failed to retrieve login page (1st)"); + return -2; // no loginpage + } + + final Map<String, String> login = settings.getLogin(); + final Map<String, String> params = new HashMap<String, String>(); + + if (login == null || StringUtils.isEmpty(login.get("username")) || StringUtils.isEmpty(login.get("password"))) { + Log.e(cgSettings.tag, "cgeoBase.login: No login information stored"); + return -3; + } + + settings.deleteCookies(); + + params.put("__EVENTTARGET", ""); + params.put("__EVENTARGUMENT", ""); + setViewstates(viewstates, params); + 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 (StringUtils.isNotBlank(loginData)) { + if (checkLogin(loginData)) { + Log.i(cgSettings.tag, "Successfully logged in Geocaching.com as " + login.get("username")); + + switchToEnglish(getViewstates(loginData)); + + 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 (StringUtils.isBlank(page)) { + Log.e(cgSettings.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 String switchToEnglish(String[] viewstates) { + final String host = "www.geocaching.com"; + final String path = "/default.aspx"; + final Map<String, String> params = new HashMap<String, String>(); + + setViewstates(viewstates, params); + 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 (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgeoBase.parseSearch: No page given"); + return null; + } + + final cgCacheWrap caches = new cgCacheWrap(); + final List<String> cids = new ArrayList<String>(); + final List<String> guids = new ArrayList<String>(); + String recaptchaChallenge = null; + String recaptchaText = null; + + caches.url = url; + + final Pattern patternCacheType = Pattern.compile("<td class=\"Merge\">[^<]*<a href=\"[^\"]*/seek/cache_details\\.aspx\\?guid=[^\"]+\"[^>]+>[^<]*<img src=\"[^\"]*/images/wpttypes/[^\\.]+\\.gif\" alt=\"([^\"]+)\" title=\"[^\"]+\"[^>]*>[^<]*</a>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternGuidAndDisabled = Pattern.compile("<img src=\"[^\"]*/images/wpttypes/[^>]*>[^<]*</a></td><td class=\"Merge\">[^<]*<a href=\"[^\"]*/seek/cache_details\\.aspx\\?guid=([a-z0-9\\-]+)\" class=\"lnk([^\"]*)\">([^<]*<span>)?([^<]*)(</span>[^<]*)?</a>[^<]+<br />([^<]*)<span[^>]+>([^<]*)</span>([^<]*<img[^>]+>)?[^<]*<br />[^<]*</td>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternTbs = Pattern.compile("<a id=\"ctl00_ContentBody_dlResults_ctl[0-9]+_uxTravelBugList\" class=\"tblist\" data-tbcount=\"([0-9]+)\" data-id=\"[^\"]*\"[^>]*>(.*)</a>", Pattern.CASE_INSENSITIVE); + final Pattern patternTbsInside = Pattern.compile("(<img src=\"[^\"]+\" alt=\"([^\"]+)\" title=\"[^\"]*\" />[^<]*)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternDirection = Pattern.compile("<img id=\"ctl00_ContentBody_dlResults_ctl[0-9]+_uxDistanceAndHeading\" title=\"[^\"]*\" src=\"[^\"]*/seek/CacheDir\\.ashx\\?k=([^\"]+)\"[^>]*>", 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("<span id=\"ctl00_ContentBody_dlResults_ctl[0-9]+_uxFavoritesValue\" title=\"[^\"]*\" class=\"favorite-rank\">([0-9]+)</span>", Pattern.CASE_INSENSITIVE); + final Pattern patternTotalCnt = Pattern.compile("<td class=\"PageBuilderWidget\"><span>Total Records[^<]*<b>(\\d+)<\\/b>", Pattern.CASE_INSENSITIVE); + final Pattern patternRecaptcha = Pattern.compile("<script[^>]*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 String recaptchaJs = request(false, "www.google.com", "/recaptcha/api/challenge", "GET", "k=" + urlencode_rfc3986(recaptchaJsParam.trim()), 0, true).getData(); + + 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(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse recaptcha challenge"); + } + + if (thread != null && StringUtils.isNotBlank(recaptchaChallenge)) { + thread.setChallenge(recaptchaChallenge); + thread.notifyNeed(); + } + } + + if (page.indexOf("SearchResultsTable") < 0) { + // there are no results. aborting here avoids a wrong error log in the next parsing step + return caches; + } + + int startPos = page.indexOf("<div id=\"ctl00_ContentBody_ResultsPanel\""); + if (startPos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseSearch: ID \"ctl00_ContentBody_dlResults\" not found on page"); + return null; + } + + page = page.substring(startPos); // cut on <table + + startPos = page.indexOf(">"); + int 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 <table> and </table> + + final String[] rows = page.split("<tr class="); + final int rows_count = rows.length; + + for (int z = 1; z < rows_count; z++) { + cgCache cache = new cgCache(); + String row = rows[z]; + + // check for cache type presence + if (row.indexOf("images/wpttypes") == -1) { + continue; + } + + try { + final Matcher matcherGuidAndDisabled = patternGuidAndDisabled.matcher(row); + + while (matcherGuidAndDisabled.find()) { + if (matcherGuidAndDisabled.groupCount() > 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(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse GUID and/or Disabled data"); + } + + if (settings.excludeDisabled == 1 && (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(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 + if (settings.getLoadDirImg()) { - return gcCustomDateFormats.get(settings.getGcCustomDate()).parse(input); - } - catch (ParseException e) {} - } - - for (SimpleDateFormat format : gcCustomDateFormats.values()) - { - try - { - return format.parse(input); - } - catch (ParseException e) {} - } - - throw new ParseException("No matching pattern", 0); - } - - public void detectGcCustomDate() - { - final String host = "www.geocaching.com"; + 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 (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(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 || cache.archived) { // 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 = Integer.valueOf(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 && StringUtils.isNotBlank(recaptchaText)))) { + 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=&__EVENTARGUMENT="); + if (ArrayUtils.isNotEmpty(caches.viewstates)) { + params.append("&__VIEWSTATE="); + params.append(urlencode_rfc3986(caches.viewstates[0])); + if (caches.viewstates.length > 1) { + for (int i = 1; i < caches.viewstates.length; i++) { + params.append("&__VIEWSTATE" + i + "="); + params.append(urlencode_rfc3986(caches.viewstates[i])); + } + params.append("&__VIEWSTATEFIELDCOUNT=" + caches.viewstates.length); + } + } + for (String cid : cids) { + params.append("&CID="); + params.append(urlencode_rfc3986(cid)); + } + + if (recaptchaChallenge != null && StringUtils.isNotBlank(recaptchaText)) { + params.append("&recaptcha_challenge_field="); + params.append(urlencode_rfc3986(recaptchaChallenge)); + params.append("&recaptcha_response_field="); + params.append(urlencode_rfc3986(recaptchaText)); + } + params.append("&ctl00%24ContentBody%24uxDownloadLoc=Download+Waypoints"); + + final String coordinates = request(false, host, path, "POST", params.toString(), 0, true).getData(); + + if (StringUtils.isNotBlank(coordinates)) { + 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; + } + } + + LocParser.parseLoc(caches, coordinates); + } catch (Exception e) { + Log.e(cgSettings.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(cgSettings.tag, "Trying to get ratings for " + cids.size() + " caches"); + + try { + final Map<String, cgRating> ratings = getRating(guids, null); + + if (CollectionUtils.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(cgSettings.tag, "cgBase.parseSearch.GCvote: " + e.toString()); + } + } + + return caches; + } + + public static cgCacheWrap parseMapJSON(String url, String data) { + if (StringUtils.isEmpty(data)) { + 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 (StringUtils.isBlank(json)) { + 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.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 = "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 (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgeoBase.parseCache: No page given"); + return null; + } + + final cgCacheWrap caches = new cgCacheWrap(); + final cgCache cache = new cgCache(); + + if (page.indexOf("Cache is Unpublished") > -1) { + caches.error = "cache was unpublished"; + return caches; + } + + if (page.indexOf("Sorry, the owner of this listing has made it viewable to Premium Members only.") != -1) { + caches.error = "requested cache is for premium members only"; + return caches; + } + + if (page.indexOf("has chosen to make this cache listing visible to Premium Members only.") != -1) { + caches.error = "requested cache is for premium members only"; + return caches; + } + + if (page.indexOf("<li>This cache is temporarily unavailable.") != -1) { + cache.disabled = true; + } else { + cache.disabled = false; + } + + if (page.indexOf("<li>This cache has been archived,") != -1) { + cache.archived = true; + } else { + cache.archived = false; + } + + if (page.indexOf("<p class=\"Warning\">This is a Premium Member Only cache.</p>") != -1) { + cache.members = true; + } else { + cache.members = false; + } + + cache.reason = reason; + + // cache geocode + try { + final Matcher matcherGeocode = patternGeocode.matcher(page); + if (matcherGeocode.find() && matcherGeocode.groupCount() > 0) { + cache.geocode = getMatch(matcherGeocode.group(1)); + } + } catch (Exception e) { + // failed to parse cache geocode + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache geocode"); + } + + // cache id + try { + final Matcher matcherCacheId = patternCacheId.matcher(page); + if (matcherCacheId.find() && matcherCacheId.groupCount() > 0) { + cache.cacheId = getMatch(matcherCacheId.group(1)); + } + } catch (Exception e) { + // failed to parse cache id + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache id"); + } + + // cache guid + try { + final Matcher matcherCacheGuid = patternCacheGuid.matcher(page); + if (matcherCacheGuid.find() && matcherCacheGuid.groupCount() > 0) { + cache.guid = getMatch(matcherCacheGuid.group(1)); + } + } catch (Exception e) { + // failed to parse cache guid + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache guid"); + } + + // name + try { + final Matcher matcherName = patternName.matcher(page); + if (matcherName.find() && matcherName.groupCount() > 0) { + cache.name = Html.fromHtml(matcherName.group(1)).toString(); + } + } catch (Exception e) { + // failed to parse cache name + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache name"); + } + + // owner real name + try { + final Matcher matcherOwnerReal = patternOwnerReal.matcher(page); + if (matcherOwnerReal.find() && matcherOwnerReal.groupCount() > 0) { + cache.ownerReal = URLDecoder.decode(matcherOwnerReal.group(1)); + } + } catch (Exception e) { + // failed to parse owner real name + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache owner real name"); + } + + 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(cgSettings.tag, "cgeoBase.parseCache: ID \"cacheDetails\" not found on page"); + return null; + } + + tableInside = tableInside.substring(pos); + + pos = tableInside.indexOf("<div class=\"CacheInformationTable\""); + if (pos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseCache: ID \"CacheInformationTable\" not found on page"); + return null; + } + + tableInside = tableInside.substring(0, pos); + + if (StringUtils.isNotBlank(tableInside)) { + // cache terrain + try { + final Matcher matcherTerrain = patternTerrain.matcher(tableInside); + if (matcherTerrain.find() && matcherTerrain.groupCount() > 0) { + cache.terrain = new Float(Pattern.compile("_").matcher(matcherTerrain.group(1)).replaceAll(".")); + } + } catch (Exception e) { + // failed to parse terrain + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache terrain"); + } + + // cache difficulty + try { + final Matcher matcherDifficulty = patternDifficulty.matcher(tableInside); + if (matcherDifficulty.find() && matcherDifficulty.groupCount() > 0) { + cache.difficulty = new Float(Pattern.compile("_").matcher(matcherDifficulty.group(1)).replaceAll(".")); + } + } catch (Exception e) { + // failed to parse difficulty + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache difficulty"); + } + + // owner + try { + final Matcher matcherOwner = patternOwner.matcher(tableInside); + if (matcherOwner.find() && matcherOwner.groupCount() > 0) { + cache.owner = Html.fromHtml(matcherOwner.group(2)).toString(); + } + } catch (Exception e) { + // failed to parse owner + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache owner"); + } + + // hidden + try { + final Matcher matcherHidden = patternHidden.matcher(tableInside); + if (matcherHidden.find() && matcherHidden.groupCount() > 0) { + cache.hidden = parseGcCustomDate(matcherHidden.group(1)); + } + } catch (ParseException e) { + // failed to parse cache hidden date + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache hidden date"); + } + + if (cache.hidden == null) { + // event date + try { + final Matcher matcherHiddenEvent = patternHiddenEvent.matcher(tableInside); + if (matcherHiddenEvent.find() && matcherHiddenEvent.groupCount() > 0) { + cache.hidden = parseGcCustomDate(matcherHiddenEvent.group(1)); + } + } catch (ParseException e) { + // failed to parse cache event date + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache event date"); + } + } + + // favourite + try { + final Matcher matcherFavourite = patternFavourite.matcher(tableInside); + if (matcherFavourite.find() && matcherFavourite.groupCount() > 0) { + cache.favouriteCnt = Integer.parseInt(matcherFavourite.group(1)); + } + } catch (Exception e) { + // failed to parse favourite count + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse favourite count"); + } + + // cache size + try { + final Matcher matcherSize = patternSize.matcher(tableInside); + if (matcherSize.find() && matcherSize.groupCount() > 0) { + cache.size = getMatch(matcherSize.group(1)).toLowerCase(); + } + } catch (Exception e) { + // failed to parse size + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache size"); + } + } + + // cache found + cache.found = patternFound.matcher(page).find() || patternFoundAlternative.matcher(page).find(); + + // cache type + try { + final Matcher matcherType = patternType.matcher(page); + if (matcherType.find() && matcherType.groupCount() > 0) { + cache.type = cacheTypes.get(matcherType.group(1).toLowerCase()); + } + } catch (Exception e) { + // failed to parse type + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache type"); + } + + // on watchlist + try { + final Matcher matcher = patternOnWatchlist.matcher(page); + cache.onWatchlist = matcher.find(); + } catch (Exception e) { + // failed to parse watchlist state + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse watchlist state"); + } + + // latitude and logitude + try { + final Matcher matcherLatLon = patternLatLon.matcher(page); + if (matcherLatLon.find() && matcherLatLon.groupCount() > 0) { + cache.latlon = getMatch(matcherLatLon.group(2)); // first is <b> + + Map<String, Object> tmp = cgBase.parseLatlon(cache.latlon); + if (tmp.size() > 0) { + cache.coords = new Geopoint((Double) tmp.get("latitude"), (Double) tmp.get("longitude")); + cache.latitudeString = (String) tmp.get("latitudeString"); + cache.longitudeString = (String) tmp.get("longitudeString"); + cache.reliableLatLon = true; + } + tmp = null; + } + } catch (Exception e) { + // failed to parse latitude and/or longitude + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache coordinates"); + } + + // cache location + try { + final Matcher matcherLocation = patternLocation.matcher(page); + if (matcherLocation.find() && matcherLocation.groupCount() > 0) { + cache.location = getMatch(matcherLocation.group(1)); + } + } catch (Exception e) { + // failed to parse location + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache location"); + } + + // cache hint + try { + final Matcher matcherHint = patternHint.matcher(page); + if (matcherHint.find() && matcherHint.groupCount() > 2 && matcherHint.group(3) != null) { + // replace linebreak and paragraph tags + String hint = Pattern.compile("<(br|p)[^>]*>").matcher(matcherHint.group(3)).replaceAll("\n"); + if (hint != null) { + cache.hint = hint.replaceAll(Pattern.quote("</p>"), "").trim(); + } + } + } catch (Exception e) { + // failed to parse hint + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache hint"); + } + + checkFields(cache); + /* + * // short info debug + * Log.d(cgSettings.tag, "gc-code: " + cache.geocode); + * Log.d(cgSettings.tag, "id: " + cache.cacheid); + * Log.d(cgSettings.tag, "guid: " + cache.guid); + * Log.d(cgSettings.tag, "name: " + cache.name); + * Log.d(cgSettings.tag, "terrain: " + cache.terrain); + * Log.d(cgSettings.tag, "difficulty: " + cache.difficulty); + * Log.d(cgSettings.tag, "owner: " + cache.owner); + * Log.d(cgSettings.tag, "owner (real): " + cache.ownerReal); + * Log.d(cgSettings.tag, "hidden: " + dateOutShort.format(cache.hidden)); + * Log.d(cgSettings.tag, "favorite: " + cache.favouriteCnt); + * Log.d(cgSettings.tag, "size: " + cache.size); + * if (cache.found) { + * Log.d(cgSettings.tag, "found!"); + * } else { + * Log.d(cgSettings.tag, "not found"); + * } + * Log.d(cgSettings.tag, "type: " + cache.type); + * Log.d(cgSettings.tag, "latitude: " + String.format("%.6f", cache.latitude)); + * Log.d(cgSettings.tag, "longitude: " + String.format("%.6f", cache.longitude)); + * Log.d(cgSettings.tag, "location: " + cache.location); + * Log.d(cgSettings.tag, "hint: " + cache.hint); + */ + + // cache personal note + try { + final Matcher matcherPersonalNote = patternPersonalNote.matcher(page); + if (matcherPersonalNote.find() && matcherPersonalNote.groupCount() > 0) { + cache.personalNote = getMatch(matcherPersonalNote.group(1)); + } + } catch (Exception e) { + // failed to parse cache personal note + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache personal note"); + } + + // cache short description + try { + final Matcher matcherDescShort = patternDescShort.matcher(page); + if (matcherDescShort.find() && matcherDescShort.groupCount() > 0) { + cache.shortdesc = getMatch(matcherDescShort.group(1)); + } + } catch (Exception e) { + // failed to parse short description + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache short description"); + } + + // cache description + try { + final Matcher matcherDesc = patternDesc.matcher(page); + if (matcherDesc.find() && matcherDesc.groupCount() > 0) { + cache.description = getMatch(matcherDesc.group(1)); + } + } catch (Exception e) { + // failed to parse short description + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache description"); + } + + // cache attributes + try { + final Matcher matcherAttributes = patternAttributes.matcher(page); + if (matcherAttributes.find() && matcherAttributes.groupCount() > 0) { + final String attributesPre = matcherAttributes.group(1); + final Matcher matcherAttributesInside = patternAttributesInside.matcher(attributesPre); + + while (matcherAttributesInside.find()) { + if (matcherAttributesInside.groupCount() > 1 && matcherAttributesInside.group(2).equalsIgnoreCase("blank") != true) { + if (cache.attributes == null) { + cache.attributes = new ArrayList<String>(); + } + // by default, use the tooltip of the attribute + String attribute = matcherAttributesInside.group(2).toLowerCase(); + + // if the image name can be recognized, use the image name as attribute + String imageName = matcherAttributesInside.group(1).trim(); + if (imageName.length() > 0) { + int start = imageName.lastIndexOf('/'); + int end = imageName.lastIndexOf('.'); + if (start >= 0 && end >= 0) { + attribute = imageName.substring(start + 1, end).replace('-', '_').toLowerCase(); + } + } + cache.attributes.add(attribute); + } + } + } + } catch (Exception e) { + // failed to parse cache attributes + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache attributes"); + } + + // cache spoilers + try { + final Matcher matcherSpoilers = patternSpoilers.matcher(page); + if (matcherSpoilers.find() && matcherSpoilers.groupCount() > 0) { + final String spoilersPre = matcherSpoilers.group(1); + final Matcher matcherSpoilersInside = patternSpoilersInside.matcher(spoilersPre); + + while (matcherSpoilersInside.find()) { + if (matcherSpoilersInside.groupCount() > 0) { + final cgImage spoiler = new cgImage(); + spoiler.url = matcherSpoilersInside.group(1); + + if (matcherSpoilersInside.group(2) != null) { + spoiler.title = matcherSpoilersInside.group(2); + } + if (matcherSpoilersInside.group(4) != null) { + spoiler.description = matcherSpoilersInside.group(4); + } + + if (cache.spoilers == null) { + cache.spoilers = new ArrayList<cgImage>(); + } + cache.spoilers.add(spoiler); + } + } + } + } catch (Exception e) { + // failed to parse cache spoilers + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache spoilers"); + } + + // cache inventory + try { + cache.inventoryItems = 0; + + final Matcher matcherInventory = patternInventory.matcher(page); + if (matcherInventory.find()) { + if (cache.inventory == null) { + cache.inventory = new ArrayList<cgTrackable>(); + } + + if (matcherInventory.groupCount() > 1) { + final String inventoryPre = matcherInventory.group(2); + + if (StringUtils.isNotBlank(inventoryPre)) { + final Matcher matcherInventoryInside = patternInventoryInside.matcher(inventoryPre); + + while (matcherInventoryInside.find()) { + if (matcherInventoryInside.groupCount() > 0) { + final cgTrackable inventoryItem = new cgTrackable(); + inventoryItem.guid = matcherInventoryInside.group(1); + inventoryItem.name = matcherInventoryInside.group(2); + + cache.inventory.add(inventoryItem); + cache.inventoryItems++; + } + } + } + } + } + } catch (Exception e) { + // failed to parse cache inventory + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache inventory (2)"); + } + + // cache logs counts + try + { + final Matcher matcherLogCounts = patternCountLogs.matcher(page); + + if (matcherLogCounts.find()) + { + final Matcher matcherLog = patternCountLog.matcher(matcherLogCounts.group(1)); + + while (matcherLog.find()) + { + String typeStr = matcherLog.group(1); + String countStr = matcherLog.group(2); + + if (StringUtils.isNotBlank(typeStr) + && logTypes.containsKey(typeStr.toLowerCase()) + && StringUtils.isNotBlank(countStr)) + { + cache.logCounts.put(logTypes.get(typeStr.toLowerCase()), Integer.parseInt(countStr)); + } + } + } + } catch (Exception e) + { + // failed to parse logs + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache log count"); + } + + // cache logs + try + { + /* + * 1- Author + * 2- Finds-count + * 3- Log type image name (e.g. "icon_smile") + * 4- Type string (e.g. "Found it") + * 5- Date string (e.g. "04/28/2010") + * 6- Log text + * 7- The rest (e.g. log-images, maybe faster) + */ + final Matcher matcherLog = patternLog.matcher(page);//(matcherLogs.group(1)); + + while (matcherLog.find()) + { + final cgLog logDone = new cgLog(); + + final String logIconName = matcherLog.group(3).toLowerCase(); + if (logTypes.containsKey(logIconName)) + { + logDone.type = logTypes.get(logIconName); + } + else + { + logDone.type = logTypes.get("icon_note"); + } + + try + { + logDone.date = parseGcCustomDate(matcherLog.group(5)).getTime(); + } catch (ParseException e) + { + Log.w(cgSettings.tag, "Failed to parse log date."); + } + + logDone.author = Html.fromHtml(matcherLog.group(1)).toString(); + + if (null != matcherLog.group(2)) + { + logDone.found = Integer.parseInt(matcherLog.group(2).replaceAll(",", "")); + } + + logDone.log = matcherLog.group(6); + + final Matcher matcherImg = patternLogImgs.matcher(matcherLog.group(7)); + while (matcherImg.find()) + { + final cgImage logImage = new cgImage(); + logImage.url = matcherImg.group(1); + logImage.title = matcherImg.group(2); + if (logDone.logImages == null) + { + logDone.logImages = new ArrayList<cgImage>(); + } + logDone.logImages.add(logImage); + } + + if (null == cache.logs) + { + cache.logs = new ArrayList<cgLog>(); + } + cache.logs.add(logDone); + } + } catch (Exception e) + { + // failed to parse logs + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache logs", e); + } + + int wpBegin = 0; + int wpEnd = 0; + + wpBegin = page.indexOf("<table class=\"Table\" id=\"ctl00_ContentBody_Waypoints\">"); + if (wpBegin != -1) { // parse waypoints + final Pattern patternWpType = Pattern.compile("\\/wpttypes\\/sm\\/(.+)\\.jpg", Pattern.CASE_INSENSITIVE); + final Pattern patternWpPrefixOrLookupOrLatlon = Pattern.compile(">([^<]*<[^>]+>)?([^<]+)(<[^>]+>[^<]*)?<\\/td>", Pattern.CASE_INSENSITIVE); + final Pattern patternWpName = Pattern.compile(">[^<]*<a[^>]+>([^<]*)<\\/a>", Pattern.CASE_INSENSITIVE); + final Pattern patternWpNote = Pattern.compile("colspan=\"6\">(.*)<\\/td>", Pattern.CASE_INSENSITIVE); + + String wpList = page.substring(wpBegin); + + wpEnd = wpList.indexOf("</p>"); + if (wpEnd > -1 && wpEnd <= wpList.length()) { + wpList = wpList.substring(0, wpEnd); + } + + if (wpList.indexOf("No additional waypoints to display.") == -1) { + wpEnd = wpList.indexOf("</table>"); + wpList = wpList.substring(0, wpEnd); + + wpBegin = wpList.indexOf("<tbody>"); + wpEnd = wpList.indexOf("</tbody>"); + if (wpBegin >= 0 && wpEnd >= 0 && wpEnd <= wpList.length()) { + wpList = wpList.substring(wpBegin + 7, wpEnd); + } + + final String[] wpItems = wpList.split("<tr"); + + String[] wp; + for (int j = 1; j < wpItems.length; j++) { + final cgWaypoint waypoint = new cgWaypoint(); + + wp = wpItems[j].split("<td"); + + // waypoint type + try { + final Matcher matcherWpType = patternWpType.matcher(wp[3]); + if (matcherWpType.find() && matcherWpType.groupCount() > 0) { + waypoint.type = matcherWpType.group(1).trim(); + } + } catch (Exception e) { + // failed to parse type + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint type"); + } + + // waypoint prefix + try { + final Matcher matcherWpPrefix = patternWpPrefixOrLookupOrLatlon.matcher(wp[4]); + if (matcherWpPrefix.find() && matcherWpPrefix.groupCount() > 1) { + waypoint.prefix = matcherWpPrefix.group(2).trim(); + } + } catch (Exception e) { + // failed to parse prefix + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint prefix"); + } + + // waypoint lookup + try { + final Matcher matcherWpLookup = patternWpPrefixOrLookupOrLatlon.matcher(wp[5]); + if (matcherWpLookup.find() && matcherWpLookup.groupCount() > 1) { + waypoint.lookup = matcherWpLookup.group(2).trim(); + } + } catch (Exception e) { + // failed to parse lookup + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint lookup"); + } + + // waypoint name + try { + final Matcher matcherWpName = patternWpName.matcher(wp[6]); + while (matcherWpName.find()) { + if (matcherWpName.groupCount() > 0) { + waypoint.name = matcherWpName.group(1); + if (StringUtils.isNotBlank(waypoint.name)) { + waypoint.name = waypoint.name.trim(); + } + } + if (matcherWpName.find() && matcherWpName.groupCount() > 0) { + waypoint.name = matcherWpName.group(1).trim(); + } + } + } catch (Exception e) { + // failed to parse name + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint name"); + } + + // waypoint latitude and logitude + try { + final Matcher matcherWpLatLon = patternWpPrefixOrLookupOrLatlon.matcher(wp[7]); + if (matcherWpLatLon.find() && matcherWpLatLon.groupCount() > 1) { + waypoint.latlon = Html.fromHtml(matcherWpLatLon.group(2)).toString(); + + final Map<String, Object> tmp = cgBase.parseLatlon(waypoint.latlon); + if (tmp.size() > 0) { + waypoint.coords = new Geopoint((Double) tmp.get("latitude"), (Double) tmp.get("longitude")); + waypoint.latitudeString = (String) tmp.get("latitudeString"); + waypoint.longitudeString = (String) tmp.get("longitudeString"); + } + } + } catch (Exception e) { + // failed to parse latitude and/or longitude + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint coordinates"); + } + + j++; + if (wpItems.length > j) { + wp = wpItems[j].split("<td"); + } + + // waypoint note + try { + final Matcher matcherWpNote = patternWpNote.matcher(wp[3]); + if (matcherWpNote.find() && matcherWpNote.groupCount() > 0) { + waypoint.note = matcherWpNote.group(1).trim(); + } + } catch (Exception e) { + // failed to parse note + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint note"); + } + + if (cache.waypoints == null) { + cache.waypoints = new ArrayList<cgWaypoint>(); + } + cache.waypoints.add(waypoint); + } + } + } + + if (cache.coords != null) { + cache.elevation = getElevation(cache.coords); + } + + final cgRating rating = getRating(cache.guid, cache.geocode); + if (rating != null) { + cache.rating = rating.rating; + cache.votes = rating.votes; + cache.myVote = rating.myVote; + } + + cache.updated = System.currentTimeMillis(); + cache.detailedUpdate = System.currentTimeMillis(); + cache.detailed = true; + caches.cacheList.add(cache); + + return caches; + } + + private static void checkFields(cgCache cache) { + if (StringUtils.isBlank(cache.geocode)) { + Log.w(cgSettings.tag, "geo code not parsed correctly"); + } + if (StringUtils.isBlank(cache.name)) { + Log.w(cgSettings.tag, "name not parsed correctly"); + } + if (StringUtils.isBlank(cache.guid)) { + Log.w(cgSettings.tag, "guid not parsed correctly"); + } + if (cache.terrain == null || cache.terrain == 0.0) { + Log.w(cgSettings.tag, "terrain not parsed correctly"); + } + if (cache.difficulty == null || cache.difficulty == 0.0) { + Log.w(cgSettings.tag, "difficulty not parsed correctly"); + } + if (StringUtils.isBlank(cache.owner)) { + Log.w(cgSettings.tag, "owner not parsed correctly"); + } + if (StringUtils.isBlank(cache.ownerReal)) { + Log.w(cgSettings.tag, "owner real not parsed correctly"); + } + if (cache.hidden == null) { + Log.w(cgSettings.tag, "hidden not parsed correctly"); + } + if (cache.favouriteCnt == null) { + Log.w(cgSettings.tag, "favoriteCount not parsed correctly"); + } + if (StringUtils.isBlank(cache.size)) { + Log.w(cgSettings.tag, "size not parsed correctly"); + } + if (StringUtils.isBlank(cache.type)) { + Log.w(cgSettings.tag, "type not parsed correctly"); + } + if (cache.coords == null) { + Log.w(cgSettings.tag, "coordinates not parsed correctly"); + } + if (StringUtils.isBlank(cache.location)) { + Log.w(cgSettings.tag, "location not parsed correctly"); + } + } + + private static String getMatch(String match) { + // creating a new String via String constructor is necessary here!! + return new String(match.trim()); + // Java copies the whole page String, when matching with regular expressions + // later this would block the garbage collector, as we only need tiny parts of the page + // see http://developer.android.com/reference/java/lang/String.html#backing_array + + // And BTW: You cannot even see that effect in the debugger, but must use a separate memory profiler! + } + + public Date parseGcCustomDate(String input) + throws ParseException + { + if (StringUtils.isBlank(input)) + { + throw new ParseException("Input is null", 0); + } + + input = input.trim(); + + if (null != settings + //&& null != settings.getGcCustomDate() + && gcCustomDateFormats.containsKey(settings.getGcCustomDate())) + { + try + { + return gcCustomDateFormats.get(settings.getGcCustomDate()).parse(input); + } catch (ParseException e) { + } + } + + for (SimpleDateFormat format : gcCustomDateFormats.values()) + { + try + { + return format.parse(input); + } catch (ParseException e) { + } + } + + throw new ParseException("No matching pattern", 0); + } + + public void detectGcCustomDate() + { + final String host = "www.geocaching.com"; final String path = "/account/ManagePreferences.aspx"; final String result = request(false, host, path, "GET", null, false, false, false).getData(); @@ -1831,355 +1828,355 @@ public class cgBase { { settings.setGcCustomDate(matcher.group(1)); } - } - - public cgRating getRating(String guid, String geocode) { - List<String> guids = null; - List<String> geocodes = null; - - if (StringUtils.isNotBlank(guid)) { - guids = new ArrayList<String>(); - guids.add(guid); - } else if (StringUtils.isNotBlank(geocode)) { - geocodes = new ArrayList<String>(); - geocodes.add(geocode); - } else { - return null; - } - - final Map<String, cgRating> ratings = getRating(guids, geocodes); - if(ratings != null){ - for (Entry<String, cgRating> entry : ratings.entrySet()) { - return entry.getValue(); - } - } - - return null; - } - - public Map<String, cgRating> getRating(List<String> guids, List<String> geocodes) { - if (guids == null && geocodes == null) { - return null; - } - - final Map<String, cgRating> ratings = new HashMap<String, cgRating>(); - - try { - final Map<String, String> params = new HashMap<String, String>(); - if (settings.isLogin()) { - final Map<String, String> login = settings.getGCvoteLogin(); - if (login != null) { - params.put("userName", login.get("username")); - params.put("password", login.get("password")); - } - } - if (CollectionUtils.isNotEmpty(guids)) { - params.put("cacheIds", implode(",", guids.toArray())); - } else { - params.put("waypoints", implode(",", geocodes.toArray())); - } - params.put("version", "cgeo"); - final String votes = request(false, "gcvote.com", "/getVotes.php", "GET", params, false, false, false).getData(); - if (votes == null) { - return null; - } - - final Pattern patternLogIn = Pattern.compile("loggedIn='([^']+)'", Pattern.CASE_INSENSITIVE); - final Pattern patternGuid = Pattern.compile("cacheId='([^']+)'", Pattern.CASE_INSENSITIVE); - final Pattern patternRating = Pattern.compile("voteAvg='([0-9\\.]+)'", Pattern.CASE_INSENSITIVE); - final Pattern patternVotes = Pattern.compile("voteCnt='([0-9]+)'", Pattern.CASE_INSENSITIVE); - final Pattern patternVote = Pattern.compile("voteUser='([0-9\\.]+)'", Pattern.CASE_INSENSITIVE); - - String voteData = null; - final Pattern patternVoteElement = Pattern.compile("<vote ([^>]+)>", Pattern.CASE_INSENSITIVE); - final Matcher matcherVoteElement = patternVoteElement.matcher(votes); - while (matcherVoteElement.find()) { - if (matcherVoteElement.groupCount() > 0) { - voteData = matcherVoteElement.group(1); - } - - if (voteData == null) { - continue; - } - - String guid = null; - cgRating rating = new cgRating(); - boolean loggedIn = false; - - try { - final Matcher matcherGuid = patternGuid.matcher(voteData); - if (matcherGuid.find()) { - if (matcherGuid.groupCount() > 0) { - guid = (String) matcherGuid.group(1); - } - } - } catch (Exception e) { - Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse guid"); - } - - try { - final Matcher matcherLoggedIn = patternLogIn.matcher(votes); - if (matcherLoggedIn.find()) { - if (matcherLoggedIn.groupCount() > 0) { - if (matcherLoggedIn.group(1).equalsIgnoreCase("true")) { - loggedIn = true; - } - } - } - } catch (Exception e) { - Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse loggedIn"); - } - - try { - final Matcher matcherRating = patternRating.matcher(voteData); - if (matcherRating.find()) { - if (matcherRating.groupCount() > 0) { - rating.rating = Float.parseFloat(matcherRating.group(1)); - } - } - } catch (Exception e) { - Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse rating"); - } - - try { - final Matcher matcherVotes = patternVotes.matcher(voteData); - if (matcherVotes.find()) { - if (matcherVotes.groupCount() > 0) { - rating.votes = Integer.parseInt(matcherVotes.group(1)); - } - } - } catch (Exception e) { - Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse vote count"); - } - - if (loggedIn) { - try { - final Matcher matcherVote = patternVote.matcher(voteData); - if (matcherVote.find()) { - if (matcherVote.groupCount() > 0) { - rating.myVote = Float.parseFloat(matcherVote.group(1)); - } - } - } catch (Exception e) { - Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse user's vote"); - } - } - - if (StringUtils.isNotBlank(guid)) { - ratings.put(guid, rating); - } - } - } catch (Exception e) { - Log.e(cgSettings.tag, "cgBase.getRating: " + e.toString()); - } - - return ratings; - } - - public cgTrackable parseTrackable(String page) { - if (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgeoBase.parseTrackable: No page given"); - return null; - } - - final cgTrackable trackable = new cgTrackable(); - - // trackable geocode - try { - final Matcher matcherGeocode = PATTERN_TRACKABLE_Geocode.matcher(page); - if (matcherGeocode.find() && matcherGeocode.groupCount() > 0) { - trackable.geocode = matcherGeocode.group(1).toUpperCase(); - } - } catch (Exception e) { - // failed to parse trackable geocode - Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable geocode"); - } - - // trackable id - try { - final Matcher matcherTrackableId = PATTERN_TRACKABLE_TrackableId.matcher(page); - if (matcherTrackableId.find() && matcherTrackableId.groupCount() > 0) { - trackable.guid = matcherTrackableId.group(1); - } - } catch (Exception e) { - // failed to parse trackable id - Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable id"); - } - - // trackable icon - try { - final Matcher matcherTrackableIcon = PATTERN_TRACKABLE_Icon.matcher(page); - if (matcherTrackableIcon.find() && matcherTrackableIcon.groupCount() > 0) { - trackable.iconUrl = matcherTrackableIcon.group(1); - } - } catch (Exception e) { - // failed to parse trackable icon - Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable icon"); - } - - // trackable name - try { - final Matcher matcherName = PATTERN_TRACKABLE_Name.matcher(page); - if (matcherName.find() && matcherName.groupCount() > 1) { - trackable.name = matcherName.group(2); - } - } catch (Exception e) { - // failed to parse trackable name - Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable name"); - } - - // trackable type - if (StringUtils.isNotBlank(trackable.name)) { - try { - final Matcher matcherType = PATTERN_TRACKABLE_Type.matcher(page); - if (matcherType.find() && matcherType.groupCount() > 0) { - trackable.type = matcherType.group(1); - } - } catch (Exception e) { - // failed to parse trackable type - Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable type"); - } - } - - // trackable owner name - try { - final Matcher matcherOwner = PATTERN_TRACKABLE_Owner.matcher(page); - if (matcherOwner.find() && matcherOwner.groupCount() > 0) { - trackable.ownerGuid = matcherOwner.group(1); - trackable.owner = matcherOwner.group(2); - } - } catch (Exception e) { - // failed to parse trackable owner name - Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable owner name"); - } - - // trackable origin - try { - final Matcher matcherOrigin = PATTERN_TRACKABLE_Origin.matcher(page); - if (matcherOrigin.find() && matcherOrigin.groupCount() > 0) { - trackable.origin = matcherOrigin.group(1); - } - } catch (Exception e) { - // failed to parse trackable origin - Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable origin"); - } - - // trackable spotted - try { - final Matcher matcherSpottedCache = PATTERN_TRACKABLE_SpottedCache.matcher(page); - if (matcherSpottedCache.find() && matcherSpottedCache.groupCount() > 0) { - trackable.spottedGuid = matcherSpottedCache.group(1); - trackable.spottedName = matcherSpottedCache.group(2); - trackable.spottedType = cgTrackable.SPOTTED_CACHE; - } - - final Matcher matcherSpottedUser = PATTERN_TRACKABLE_SpottedUser.matcher(page); - if (matcherSpottedUser.find() && matcherSpottedUser.groupCount() > 0) { - trackable.spottedGuid = matcherSpottedUser.group(1); - trackable.spottedName = matcherSpottedUser.group(2); - trackable.spottedType = cgTrackable.SPOTTED_USER; - } - - final Matcher matcherSpottedUnknown = PATTERN_TRACKABLE_SpottedUnknown.matcher(page); - if (matcherSpottedUnknown.find()) { - trackable.spottedType = cgTrackable.SPOTTED_UNKNOWN; - } - - final Matcher matcherSpottedOwner = PATTERN_TRACKABLE_SpottedOwner.matcher(page); - if (matcherSpottedOwner.find()) { - trackable.spottedType = cgTrackable.SPOTTED_OWNER; - } - } catch (Exception e) { - // failed to parse trackable last known place - Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable last known place"); - } - - // released - try { - final Matcher matcherReleased = PATTERN_TRACKABLE_Released.matcher(page); - if (matcherReleased.find() && matcherReleased.groupCount() > 0 && matcherReleased.group(1) != null) { - try { - if (trackable.released == null) { - trackable.released = dateTbIn1.parse(matcherReleased.group(1)); - } - } catch (Exception e) { - // - } - - try { - if (trackable.released == null) { - trackable.released = dateTbIn2.parse(matcherReleased.group(1)); - } - } catch (Exception e) { - // - } - } - } catch (Exception e) { - // failed to parse trackable released date - Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable released date"); - } - - // trackable distance - try { - final Matcher matcherDistance = PATTERN_TRACKABLE_Distance.matcher(page); - if (matcherDistance.find() && matcherDistance.groupCount() > 0) { - try { - trackable.distance = DistanceParser.parseDistance(matcherDistance.group(1), settings.units); - } catch (NumberFormatException e) { - trackable.distance = null; - throw e; - } - } - } catch (Exception e) { - // failed to parse trackable distance - Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable distance"); - } - - // trackable goal - try { - final Matcher matcherGoal = PATTERN_TRACKABLE_Goal.matcher(page); - if (matcherGoal.find() && matcherGoal.groupCount() > 0) { - trackable.goal = matcherGoal.group(1); - } - } catch (Exception e) { - // failed to parse trackable goal - Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable goal"); - } - - // trackable details & image - try { - final Matcher matcherDetailsImage = PATTERN_TRACKABLE_DetailsImage.matcher(page); - if (matcherDetailsImage.find() && matcherDetailsImage.groupCount() > 0) { - final String image = matcherDetailsImage.group(3); - final String details = matcherDetailsImage.group(4); - - if (image != null) { - trackable.image = image; - } - if (details != null) { - trackable.details = details; - } - } - } catch (Exception e) { - // failed to parse trackable details & image - Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable details & image"); - } - - // trackable logs - try - { - final Matcher matcherLogs = PATTERN_TRACKABLE_Log.matcher(page); - /* - 1. Type (img) - 2. Date - 3. Author - 4. Cache-GUID - 5. Cache-name - 6. Logtext - */ - while (matcherLogs.find()) - { - final cgLog logDone = new cgLog(); + } + + public cgRating getRating(String guid, String geocode) { + List<String> guids = null; + List<String> geocodes = null; + + if (StringUtils.isNotBlank(guid)) { + guids = new ArrayList<String>(); + guids.add(guid); + } else if (StringUtils.isNotBlank(geocode)) { + geocodes = new ArrayList<String>(); + geocodes.add(geocode); + } else { + return null; + } + + final Map<String, cgRating> ratings = getRating(guids, geocodes); + if (ratings != null) { + for (Entry<String, cgRating> entry : ratings.entrySet()) { + return entry.getValue(); + } + } + + return null; + } + + public Map<String, cgRating> getRating(List<String> guids, List<String> geocodes) { + if (guids == null && geocodes == null) { + return null; + } + + final Map<String, cgRating> ratings = new HashMap<String, cgRating>(); + + try { + final Map<String, String> params = new HashMap<String, String>(); + if (settings.isLogin()) { + final Map<String, String> login = settings.getGCvoteLogin(); + if (login != null) { + params.put("userName", login.get("username")); + params.put("password", login.get("password")); + } + } + if (CollectionUtils.isNotEmpty(guids)) { + params.put("cacheIds", implode(",", guids.toArray())); + } else { + params.put("waypoints", implode(",", geocodes.toArray())); + } + params.put("version", "cgeo"); + final String votes = request(false, "gcvote.com", "/getVotes.php", "GET", params, false, false, false).getData(); + if (votes == null) { + return null; + } + + final Pattern patternLogIn = Pattern.compile("loggedIn='([^']+)'", Pattern.CASE_INSENSITIVE); + final Pattern patternGuid = Pattern.compile("cacheId='([^']+)'", Pattern.CASE_INSENSITIVE); + final Pattern patternRating = Pattern.compile("voteAvg='([0-9\\.]+)'", Pattern.CASE_INSENSITIVE); + final Pattern patternVotes = Pattern.compile("voteCnt='([0-9]+)'", Pattern.CASE_INSENSITIVE); + final Pattern patternVote = Pattern.compile("voteUser='([0-9\\.]+)'", Pattern.CASE_INSENSITIVE); + + String voteData = null; + final Pattern patternVoteElement = Pattern.compile("<vote ([^>]+)>", Pattern.CASE_INSENSITIVE); + final Matcher matcherVoteElement = patternVoteElement.matcher(votes); + while (matcherVoteElement.find()) { + if (matcherVoteElement.groupCount() > 0) { + voteData = matcherVoteElement.group(1); + } + + if (voteData == null) { + continue; + } + + String guid = null; + cgRating rating = new cgRating(); + boolean loggedIn = false; + + try { + final Matcher matcherGuid = patternGuid.matcher(voteData); + if (matcherGuid.find()) { + if (matcherGuid.groupCount() > 0) { + guid = (String) matcherGuid.group(1); + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse guid"); + } + + try { + final Matcher matcherLoggedIn = patternLogIn.matcher(votes); + if (matcherLoggedIn.find()) { + if (matcherLoggedIn.groupCount() > 0) { + if (matcherLoggedIn.group(1).equalsIgnoreCase("true")) { + loggedIn = true; + } + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse loggedIn"); + } + + try { + final Matcher matcherRating = patternRating.matcher(voteData); + if (matcherRating.find()) { + if (matcherRating.groupCount() > 0) { + rating.rating = Float.parseFloat(matcherRating.group(1)); + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse rating"); + } + + try { + final Matcher matcherVotes = patternVotes.matcher(voteData); + if (matcherVotes.find()) { + if (matcherVotes.groupCount() > 0) { + rating.votes = Integer.parseInt(matcherVotes.group(1)); + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse vote count"); + } + + if (loggedIn) { + try { + final Matcher matcherVote = patternVote.matcher(voteData); + if (matcherVote.find()) { + if (matcherVote.groupCount() > 0) { + rating.myVote = Float.parseFloat(matcherVote.group(1)); + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse user's vote"); + } + } + + if (StringUtils.isNotBlank(guid)) { + ratings.put(guid, rating); + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.getRating: " + e.toString()); + } + + return ratings; + } + + public cgTrackable parseTrackable(String page) { + if (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgeoBase.parseTrackable: No page given"); + return null; + } + + final cgTrackable trackable = new cgTrackable(); + + // trackable geocode + try { + final Matcher matcherGeocode = PATTERN_TRACKABLE_Geocode.matcher(page); + if (matcherGeocode.find() && matcherGeocode.groupCount() > 0) { + trackable.geocode = matcherGeocode.group(1).toUpperCase(); + } + } catch (Exception e) { + // failed to parse trackable geocode + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable geocode"); + } + + // trackable id + try { + final Matcher matcherTrackableId = PATTERN_TRACKABLE_TrackableId.matcher(page); + if (matcherTrackableId.find() && matcherTrackableId.groupCount() > 0) { + trackable.guid = matcherTrackableId.group(1); + } + } catch (Exception e) { + // failed to parse trackable id + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable id"); + } + + // trackable icon + try { + final Matcher matcherTrackableIcon = PATTERN_TRACKABLE_Icon.matcher(page); + if (matcherTrackableIcon.find() && matcherTrackableIcon.groupCount() > 0) { + trackable.iconUrl = matcherTrackableIcon.group(1); + } + } catch (Exception e) { + // failed to parse trackable icon + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable icon"); + } + + // trackable name + try { + final Matcher matcherName = PATTERN_TRACKABLE_Name.matcher(page); + if (matcherName.find() && matcherName.groupCount() > 1) { + trackable.name = matcherName.group(2); + } + } catch (Exception e) { + // failed to parse trackable name + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable name"); + } + + // trackable type + if (StringUtils.isNotBlank(trackable.name)) { + try { + final Matcher matcherType = PATTERN_TRACKABLE_Type.matcher(page); + if (matcherType.find() && matcherType.groupCount() > 0) { + trackable.type = matcherType.group(1); + } + } catch (Exception e) { + // failed to parse trackable type + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable type"); + } + } + + // trackable owner name + try { + final Matcher matcherOwner = PATTERN_TRACKABLE_Owner.matcher(page); + if (matcherOwner.find() && matcherOwner.groupCount() > 0) { + trackable.ownerGuid = matcherOwner.group(1); + trackable.owner = matcherOwner.group(2); + } + } catch (Exception e) { + // failed to parse trackable owner name + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable owner name"); + } + + // trackable origin + try { + final Matcher matcherOrigin = PATTERN_TRACKABLE_Origin.matcher(page); + if (matcherOrigin.find() && matcherOrigin.groupCount() > 0) { + trackable.origin = matcherOrigin.group(1); + } + } catch (Exception e) { + // failed to parse trackable origin + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable origin"); + } + + // trackable spotted + try { + final Matcher matcherSpottedCache = PATTERN_TRACKABLE_SpottedCache.matcher(page); + if (matcherSpottedCache.find() && matcherSpottedCache.groupCount() > 0) { + trackable.spottedGuid = matcherSpottedCache.group(1); + trackable.spottedName = matcherSpottedCache.group(2); + trackable.spottedType = cgTrackable.SPOTTED_CACHE; + } + + final Matcher matcherSpottedUser = PATTERN_TRACKABLE_SpottedUser.matcher(page); + if (matcherSpottedUser.find() && matcherSpottedUser.groupCount() > 0) { + trackable.spottedGuid = matcherSpottedUser.group(1); + trackable.spottedName = matcherSpottedUser.group(2); + trackable.spottedType = cgTrackable.SPOTTED_USER; + } + + final Matcher matcherSpottedUnknown = PATTERN_TRACKABLE_SpottedUnknown.matcher(page); + if (matcherSpottedUnknown.find()) { + trackable.spottedType = cgTrackable.SPOTTED_UNKNOWN; + } + + final Matcher matcherSpottedOwner = PATTERN_TRACKABLE_SpottedOwner.matcher(page); + if (matcherSpottedOwner.find()) { + trackable.spottedType = cgTrackable.SPOTTED_OWNER; + } + } catch (Exception e) { + // failed to parse trackable last known place + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable last known place"); + } + + // released + try { + final Matcher matcherReleased = PATTERN_TRACKABLE_Released.matcher(page); + if (matcherReleased.find() && matcherReleased.groupCount() > 0 && matcherReleased.group(1) != null) { + try { + if (trackable.released == null) { + trackable.released = dateTbIn1.parse(matcherReleased.group(1)); + } + } catch (Exception e) { + // + } + + try { + if (trackable.released == null) { + trackable.released = dateTbIn2.parse(matcherReleased.group(1)); + } + } catch (Exception e) { + // + } + } + } catch (Exception e) { + // failed to parse trackable released date + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable released date"); + } + + // trackable distance + try { + final Matcher matcherDistance = PATTERN_TRACKABLE_Distance.matcher(page); + if (matcherDistance.find() && matcherDistance.groupCount() > 0) { + try { + trackable.distance = DistanceParser.parseDistance(matcherDistance.group(1), settings.units); + } catch (NumberFormatException e) { + trackable.distance = null; + throw e; + } + } + } catch (Exception e) { + // failed to parse trackable distance + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable distance"); + } + + // trackable goal + try { + final Matcher matcherGoal = PATTERN_TRACKABLE_Goal.matcher(page); + if (matcherGoal.find() && matcherGoal.groupCount() > 0) { + trackable.goal = matcherGoal.group(1); + } + } catch (Exception e) { + // failed to parse trackable goal + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable goal"); + } + + // trackable details & image + try { + final Matcher matcherDetailsImage = PATTERN_TRACKABLE_DetailsImage.matcher(page); + if (matcherDetailsImage.find() && matcherDetailsImage.groupCount() > 0) { + final String image = matcherDetailsImage.group(3); + final String details = matcherDetailsImage.group(4); + + if (image != null) { + trackable.image = image; + } + if (details != null) { + trackable.details = details; + } + } + } catch (Exception e) { + // failed to parse trackable details & image + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable details & image"); + } + + // trackable logs + try + { + final Matcher matcherLogs = PATTERN_TRACKABLE_Log.matcher(page); + /* + * 1. Type (img) + * 2. Date + * 3. Author + * 4. Cache-GUID + * 5. Cache-name + * 6. Logtext + */ + while (matcherLogs.find()) + { + final cgLog logDone = new cgLog(); if (logTypes.containsKey(matcherLogs.group(1).toLowerCase())) { @@ -2190,1276 +2187,1277 @@ public class cgBase { logDone.type = logTypes.get("icon_note"); } - logDone.author = Html.fromHtml(matcherLogs.group(3)).toString(); - - try - { - logDone.date = parseGcCustomDate(matcherLogs.group(2)).getTime(); - } - catch (ParseException e) {} - - logDone.log = matcherLogs.group(6).trim(); - - if (matcherLogs.group(4) != null && matcherLogs.group(5) != null) - { - logDone.cacheGuid = matcherLogs.group(4); - logDone.cacheName = matcherLogs.group(5); - } - - trackable.logs.add(logDone); - } - } catch (Exception e) { - // failed to parse logs - Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache logs"); - } - - app.saveTrackable(trackable); - - return trackable; - } - - public static List<Integer> parseTypes(String page) { - if (StringUtils.isEmpty(page)) { - return null; - } - - final List<Integer> types = new ArrayList<Integer>(); - - final Pattern typeBoxPattern = Pattern.compile("<select name=\"ctl00\\$ContentBody\\$LogBookPanel1\\$ddLogType\" id=\"ctl00_ContentBody_LogBookPanel1_ddLogType\"[^>]*>" - + "(([^<]*<option[^>]*>[^<]+</option>)+)[^<]*</select>", Pattern.CASE_INSENSITIVE); - final Matcher typeBoxMatcher = typeBoxPattern.matcher(page); - String typesText = null; - if (typeBoxMatcher.find()) { - if (typeBoxMatcher.groupCount() > 0) { - typesText = typeBoxMatcher.group(1); - } - } - - if (typesText != null) { - final Pattern typePattern = Pattern.compile("<option( selected=\"selected\")? value=\"(\\d+)\">[^<]+</option>", Pattern.CASE_INSENSITIVE); - final Matcher typeMatcher = typePattern.matcher(typesText); - while (typeMatcher.find()) { - if (typeMatcher.groupCount() > 1) { - final int type = Integer.parseInt(typeMatcher.group(2)); - - if (type > 0) { - types.add(type); - } - } - } - } - - return types; - } - - public static List<cgTrackableLog> parseTrackableLog(String page) { - if (StringUtils.isEmpty(page)) { - return null; - } - - final List<cgTrackableLog> trackables = new ArrayList<cgTrackableLog>(); - - int startPos = -1; - int endPos = -1; - - startPos = page.indexOf("<table id=\"tblTravelBugs\""); - if (startPos == -1) { - Log.e(cgSettings.tag, "cgeoBase.parseTrackableLog: ID \"tblTravelBugs\" not found on page"); - return null; - } - - page = page.substring(startPos); // cut on <table - - endPos = page.indexOf("</table>"); - if (endPos == -1) { - Log.e(cgSettings.tag, "cgeoBase.parseTrackableLog: end of ID \"tblTravelBugs\" not found on page"); - return null; - } - - page = page.substring(0, endPos); // cut on </table> - - startPos = page.indexOf("<tbody>"); - if (startPos == -1) { - Log.e(cgSettings.tag, "cgeoBase.parseTrackableLog: tbody not found on page"); - return null; - } - - page = page.substring(startPos); // cut on <tbody> - - endPos = page.indexOf("</tbody>"); - if (endPos == -1) { - Log.e(cgSettings.tag, "cgeoBase.parseTrackableLog: end of tbody not found on page"); - return null; - } - - page = page.substring(0, endPos); // cut on </tbody> - - final Pattern trackablePattern = Pattern.compile("<tr id=\"ctl00_ContentBody_LogBookPanel1_uxTrackables_repTravelBugs_ctl[0-9]+_row\"[^>]*>" - + "[^<]*<td>[^<]*<a href=\"[^\"]+\">([A-Z0-9]+)</a>[^<]*</td>[^<]*<td>([^<]+)</td>[^<]*<td>" - + "[^<]*<select name=\"ctl00\\$ContentBody\\$LogBookPanel1\\$uxTrackables\\$repTravelBugs\\$ctl([0-9]+)\\$ddlAction\"[^>]*>" - + "([^<]*<option value=\"([0-9]+)(_[a-z]+)?\">[^<]+</option>)+" - + "[^<]*</select>[^<]*</td>[^<]*</tr>", Pattern.CASE_INSENSITIVE); - final Matcher trackableMatcher = trackablePattern.matcher(page); - while (trackableMatcher.find()) { - if (trackableMatcher.groupCount() > 0) { - final cgTrackableLog trackable = new cgTrackableLog(); - - if (trackableMatcher.group(1) != null) { - trackable.trackCode = trackableMatcher.group(1); - } else { - continue; - } - if (trackableMatcher.group(2) != null) { - trackable.name = Html.fromHtml(trackableMatcher.group(2)).toString(); - } else { - continue; - } - if (trackableMatcher.group(3) != null) { - trackable.ctl = Integer.valueOf(trackableMatcher.group(3)); - } else { - continue; - } - if (trackableMatcher.group(5) != null) { - trackable.id = Integer.valueOf(trackableMatcher.group(5)); - } else { - continue; - } - - Log.i(cgSettings.tag, "Trackable in inventory (#" + trackable.ctl + "/" + trackable.id + "): " + trackable.trackCode + " - " + trackable.name); - - trackables.add(trackable); - } - } - - return trackables; - } - - public static String stripParagraphs(String text) { - if (StringUtils.isBlank(text)) { - return ""; - } - - final Pattern patternP = Pattern.compile("(<p>|</p>|<br \\/>|<br>)", Pattern.CASE_INSENSITIVE); - final Pattern patternP2 = Pattern.compile("([ ]+)", Pattern.CASE_INSENSITIVE); - final Matcher matcherP = patternP.matcher(text); - final Matcher matcherP2 = patternP2.matcher(text); - - matcherP.replaceAll(" "); - matcherP2.replaceAll(" "); - - return text.trim(); - } - - public static String stripTags(String text) { - if (StringUtils.isBlank(text)) { - return ""; - } - - final Pattern patternP = Pattern.compile("(<[^>]+>)", Pattern.CASE_INSENSITIVE); - final Matcher matcherP = patternP.matcher(text); - - matcherP.replaceAll(" "); - - return text.trim(); - } - - public String getHumanDistance(Float distance) { - if (distance == null) { - return "?"; - } - - if (settings.units == cgSettings.unitsImperial) { - distance /= miles2km; - if (distance > 100) { - return String.format(Locale.getDefault(), "%.0f", Double.valueOf(Math.round(distance))) + " mi"; - } else if (distance > 0.5) { - return String.format(Locale.getDefault(), "%.1f", Double.valueOf(Math.round(distance * 10.0) / 10.0)) + " mi"; - } else if (distance > 0.1) { - return String.format(Locale.getDefault(), "%.2f", Double.valueOf(Math.round(distance * 100.0) / 100.0)) + " mi"; - } else if (distance > 0.05) { - return String.format(Locale.getDefault(), "%.0f", Double.valueOf(Math.round(distance * 5280.0))) + " ft"; - } else if (distance > 0.01) { - return String.format(Locale.getDefault(), "%.1f", Double.valueOf(Math.round(distance * 5280 * 10.0) / 10.0)) + " ft"; - } else { - return String.format(Locale.getDefault(), "%.2f", Double.valueOf(Math.round(distance * 5280 * 100.0) / 100.0)) + " ft"; - } - } else { - if (distance > 100) { - return String.format(Locale.getDefault(), "%.0f", Double.valueOf(Math.round(distance))) + " km"; - } else if (distance > 10) { - return String.format(Locale.getDefault(), "%.1f", Double.valueOf(Math.round(distance * 10.0) / 10.0)) + " km"; - } else if (distance > 1) { - return String.format(Locale.getDefault(), "%.2f", Double.valueOf(Math.round(distance * 100.0) / 100.0)) + " km"; - } else if (distance > 0.1) { - return String.format(Locale.getDefault(), "%.0f", Double.valueOf(Math.round(distance * 1000.0))) + " m"; - } else if (distance > 0.01) { - return String.format(Locale.getDefault(), "%.1f", Double.valueOf(Math.round(distance * 1000.0 * 10.0) / 10.0)) + " m"; - } else { - return String.format(Locale.getDefault(), "%.2f", Double.valueOf(Math.round(distance * 1000.0 * 100.0) / 100.0)) + " m"; - } - } - } - - public String getHumanSpeed(float speed) { - double kph = speed * 3.6; - String unit = "km/h"; - - if (this.settings.units == cgSettings.unitsImperial) { - kph /= miles2km; - unit = "mph"; - } - - if (kph < 10.0) { - return String.format(Locale.getDefault(), "%.1f", Double.valueOf((Math.round(kph * 10.0) / 10.0))) + " " + unit; - } else { - return String.format(Locale.getDefault(), "%.0f", Double.valueOf(Math.round(kph))) + " " + unit; - } - } - - public static Map<String, Object> parseLatlon(String latlon) { - final Map<String, Object> result = new HashMap<String, Object>(); - final Pattern patternLatlon = Pattern.compile("([NS])[^\\d]*(\\d+)[^°]*° (\\d+)\\.(\\d+) ([WE])[^\\d]*(\\d+)[^°]*° (\\d+)\\.(\\d+)", Pattern.CASE_INSENSITIVE); - final Matcher matcherLatlon = patternLatlon.matcher(latlon); - - while (matcherLatlon.find()) { - if (matcherLatlon.groupCount() > 0) { - result.put("latitudeString", (String) (matcherLatlon.group(1) + " " + matcherLatlon.group(2) + "° " + matcherLatlon.group(3) + "." + matcherLatlon.group(4))); - result.put("longitudeString", (String) (matcherLatlon.group(5) + " " + matcherLatlon.group(6) + "° " + matcherLatlon.group(7) + "." + matcherLatlon.group(8))); - int latNegative = -1; - int lonNegative = -1; - if (matcherLatlon.group(1).equalsIgnoreCase("N")) { - latNegative = 1; - } - if (matcherLatlon.group(5).equalsIgnoreCase("E")) { - lonNegative = 1; - } - result.put("latitude", Double.valueOf(latNegative * (Float.valueOf(matcherLatlon.group(2)) + Float.valueOf(matcherLatlon.group(3) + "." + matcherLatlon.group(4)) / 60))); - result.put("longitude", Double.valueOf(lonNegative * (Float.valueOf(matcherLatlon.group(6)) + Float.valueOf(matcherLatlon.group(7) + "." + matcherLatlon.group(8)) / 60))); - } else { - Log.w(cgSettings.tag, "cgBase.parseLatlon: Failed to parse coordinates."); - } - } - - return result; - } - - private static String formatCoordinate(final Double coordIn, final boolean degrees, final String direction, final String digitsFormat) { - if (coordIn == null) { - return ""; - } - StringBuilder formatted = new StringBuilder(direction); - - double coordAbs = Math.abs(coordIn); - Locale locale = Locale.getDefault(); - double floor = Math.floor(coordAbs); - - formatted.append(String.format(locale, digitsFormat, floor)); - - if (degrees) { - formatted.append("° "); - } else { - formatted.append(" "); - } - formatted.append(String.format(locale, "%06.3f", ((coordAbs - floor) * 60))); - - return formatted.toString(); - } - - public static String formatLatitude(final Double coord, final boolean degrees) { - return formatCoordinate(coord, degrees, (coord >= 0) ? "N " : "S ", "%02.0f"); - } - - public static String formatLongitude(final Double coord, final boolean degrees) { - return formatCoordinate(coord, degrees, (coord >= 0) ? "E " : "W ", "%03.0f"); - } - - public static String formatCoords(final Geopoint coords, final boolean degrees) { - return formatLatitude(coords.getLatitude(), degrees) + " | " + formatLongitude(coords.getLongitude(), degrees); - } - - // TODO Use android.util.Pair<Double, String> if needed rather than a Map here. - public static Map<String, Object> parseCoordinate(String coord, String latlon) { - final Map<String, Object> coords = new HashMap<String, Object>(); - - final Pattern patternA = Pattern.compile("^([NSWE])[^\\d]*(\\d+)°? +(\\d+)([\\.|,](\\d+))?$", Pattern.CASE_INSENSITIVE); - final Pattern patternB = Pattern.compile("^([NSWE])[^\\d]*(\\d+)([\\.|,](\\d+))?$", Pattern.CASE_INSENSITIVE); - final Pattern patternC = Pattern.compile("^(-?\\d+)([\\.|,](\\d+))?$", Pattern.CASE_INSENSITIVE); - final Pattern patternD = Pattern.compile("^([NSWE])[^\\d]*(\\d+)°?$", Pattern.CASE_INSENSITIVE); - final Pattern patternE = Pattern.compile("^(-?\\d+)°?$", Pattern.CASE_INSENSITIVE); - final Pattern patternF = Pattern.compile("^([NSWE])[^\\d]*(\\d+)$", Pattern.CASE_INSENSITIVE); - final Pattern pattern0 = Pattern.compile("^(-?\\d+)([\\.|,](\\d+))?$", Pattern.CASE_INSENSITIVE); - - coord = coord.trim().toUpperCase(); - - final Matcher matcherA = patternA.matcher(coord); - final Matcher matcherB = patternB.matcher(coord); - final Matcher matcherC = patternC.matcher(coord); - final Matcher matcherD = patternD.matcher(coord); - final Matcher matcherE = patternE.matcher(coord); - final Matcher matcherF = patternF.matcher(coord); - final Matcher matcher0 = pattern0.matcher(coord); - - int latlonNegative; - if (matcherA.find() && matcherA.groupCount() > 0) { - if (matcherA.group(1).equalsIgnoreCase("N") || matcherA.group(1).equalsIgnoreCase("E")) { - latlonNegative = 1; - } else { - latlonNegative = -1; - } - - if (matcherA.groupCount() < 5 || matcherA.group(5) == null) { - coords.put("coordinate", Double.valueOf(latlonNegative * (Double.valueOf(matcherA.group(2)) + Double.valueOf(matcherA.group(3) + ".0") / 60))); - coords.put("string", matcherA.group(1) + " " + matcherA.group(2) + "° " + matcherA.group(3) + ".000"); - } else { - coords.put("coordinate", Double.valueOf(latlonNegative * (Double.valueOf(matcherA.group(2)) + Double.valueOf(matcherA.group(3) + "." + matcherA.group(5)) / 60))); - coords.put("string", matcherA.group(1) + " " + matcherA.group(2) + "° " + matcherA.group(3) + "." + matcherA.group(5)); - } - - return coords; - } else if (matcherB.find() && matcherB.groupCount() > 0) { - if (matcherB.group(1).equalsIgnoreCase("N") || matcherB.group(1).equalsIgnoreCase("E")) { - latlonNegative = 1; - } else { - latlonNegative = -1; - } - - if (matcherB.groupCount() < 4 || matcherB.group(4) == null) { - coords.put("coordinate", Double.valueOf(latlonNegative * (Double.valueOf(matcherB.group(2) + ".0")))); - } else { - coords.put("coordinate", Double.valueOf(latlonNegative * (Double.valueOf(matcherB.group(2) + "." + matcherB.group(4))))); - } - } else if (matcherC.find() && matcherC.groupCount() > 0) { - if (matcherC.groupCount() < 3 || matcherC.group(3) == null) { - coords.put("coordinate", Double.valueOf(new Float(matcherC.group(1) + ".0"))); - } else { - coords.put("coordinate", Double.valueOf(new Float(matcherC.group(1) + "." + matcherC.group(3)))); - } - } else if (matcherD.find() && matcherD.groupCount() > 0) { - if (matcherD.group(1).equalsIgnoreCase("N") || matcherD.group(1).equalsIgnoreCase("E")) { - latlonNegative = 1; - } else { - latlonNegative = -1; - } - - coords.put("coordinate", Double.valueOf(latlonNegative * (Double.valueOf(matcherB.group(2))))); - } else if (matcherE.find() && matcherE.groupCount() > 0) { - coords.put("coordinate", Double.valueOf(matcherE.group(1))); - } else if (matcherF.find() && matcherF.groupCount() > 0) { - if (matcherF.group(1).equalsIgnoreCase("N") || matcherF.group(1).equalsIgnoreCase("E")) { - latlonNegative = 1; - } else { - latlonNegative = -1; - } - - coords.put("coordinate", Double.valueOf(latlonNegative * (Double.valueOf(matcherB.group(2))))); - } else { - return null; - } - - if (matcher0.find() && matcher0.groupCount() > 0) { - String tmpDir = null; - Float tmpCoord; - if (matcher0.groupCount() < 3 || matcher0.group(3) == null) { - tmpCoord = new Float("0.0"); - } else { - tmpCoord = new Float("0." + matcher0.group(3)); - } - - if (latlon.equalsIgnoreCase("lat")) { - if (matcher0.group(1).equals("+")) { - tmpDir = "N"; - } - if (matcher0.group(1).equals("-")) { - tmpDir = "S"; - } - } else if (latlon.equalsIgnoreCase("lon")) { - if (matcher0.group(1).equals("+")) { - tmpDir = "E"; - } - if (matcher0.group(1).equals("-")) { - tmpDir = "W"; - } - } - - coords.put("string", tmpDir + " " + matcher0.group(1) + "° " + (Math.round(tmpCoord / (1 / 60) * 1000) * 1000)); - - return coords; - } else { - return new HashMap<String, Object>(); - } - } - - public UUID searchByNextPage(cgSearchThread thread, final UUID searchId, int reason, boolean showCaptcha) { - final String[] viewstates = app.getViewstates(searchId); - - String url = app.getUrl(searchId); - - if (StringUtils.isBlank(url)) { - Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: No url found"); - return searchId; - } - - if (isEmpty(viewstates)) { - Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: No viewstate given"); - return searchId; - } - - String host = "www.geocaching.com"; - String path = "/"; - final String method = "POST"; - - int dash = -1; - if (url.indexOf("http://") > -1) { - url = url.substring(7); - } - - dash = url.indexOf("/"); - if (dash > -1) { - host = url.substring(0, dash); - url = url.substring(dash); - } else { - host = url; - url = ""; - } - - dash = url.indexOf("?"); - if (dash > -1) { - path = url.substring(0, dash); - } else { - path = url; - } - - final Map<String, String> params = new HashMap<String, String>(); - setViewstates(viewstates, params); - params.put("__EVENTTARGET", "ctl00$ContentBody$pgrBottom$ctl08"); - params.put("__EVENTARGUMENT", ""); - - String page = request(false, host, path, method, params, false, false, true).getData(); - if (checkLogin(page) == false) { - int loginState = login(); - if (loginState == 1) { - page = request(false, host, path, method, params, false, false, true).getData(); - } else if (loginState == -3) { - Log.i(cgSettings.tag, "Working as guest."); - } else { - app.setError(searchId, errorRetrieve.get(loginState)); - Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: Can not log in geocaching"); - return searchId; - } - } - - if (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: No data from server"); - return searchId; - } - - final cgCacheWrap caches = parseSearch(thread, url, page, showCaptcha); - if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { - Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: No cache parsed"); - return searchId; - } - - // save to application - app.setError(searchId, caches.error); - app.setViewstates(searchId, caches.viewstates); - - final List<cgCache> cacheList = new ArrayList<cgCache>(); - for (cgCache cache : caches.cacheList) { - app.addGeocode(searchId, cache.geocode); - cacheList.add(cache); - } - - app.addSearch(searchId, cacheList, true, reason); - - return searchId; - } - - public UUID searchByGeocode(Map<String, String> parameters, int reason, boolean forceReload) { - final cgSearch search = new cgSearch(); - String geocode = parameters.get("geocode"); - String guid = parameters.get("guid"); - - if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid)) { - Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No geocode nor guid given"); - return null; - } - - if (forceReload == false && reason == 0 && (app.isOffline(geocode, guid) || app.isThere(geocode, guid, true, true))) { - if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(guid)) { - geocode = app.getGeocode(guid); - } - - List<cgCache> cacheList = new ArrayList<cgCache>(); - cacheList.add(app.getCacheByGeocode(geocode, true, true, true, true, true, true)); - search.addGeocode(geocode); - - app.addSearch(search, cacheList, false, reason); - - cacheList.clear(); - cacheList = null; - - return search.getCurrentId(); - } - - final String host = "www.geocaching.com"; - final String path = "/seek/cache_details.aspx"; - final String method = "GET"; - final Map<String, String> params = new HashMap<String, String>(); - if (StringUtils.isNotBlank(geocode)) { - params.put("wp", geocode); - } else if (StringUtils.isNotBlank(guid)) { - params.put("guid", guid); - } - params.put("decrypt", "y"); - params.put("log", "y"); // download logs (more than 5 - params.put("numlogs", "35"); // 35 logs - - String page = requestLogged(false, host, path, method, params, false, false, false); - - if (StringUtils.isEmpty(page)) { - if (app.isThere(geocode, guid, true, false)) { - if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(guid)) { - Log.i(cgSettings.tag, "Loading old cache from cache."); - - geocode = app.getGeocode(guid); - } - - final List<cgCache> cacheList = new ArrayList<cgCache>(); - cacheList.add(app.getCacheByGeocode(geocode)); - search.addGeocode(geocode); - search.error = null; - - app.addSearch(search, cacheList, false, reason); - - cacheList.clear(); - - return search.getCurrentId(); - } - - Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No data from server"); - return null; - } - - final cgCacheWrap caches = parseCache(page, reason); - if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { - if (caches != null && StringUtils.isNotBlank(caches.error)) { - search.error = caches.error; - } - if (caches != null && StringUtils.isNotBlank(caches.url)) { - search.url = caches.url; - } - - app.addSearch(search, null, true, reason); - - Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No cache parsed"); - return null; - } - - if (app == null) { - Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No application found"); - return null; - } - - List<cgCache> cacheList = processSearchResults(search, caches, 0, 0, null); - - app.addSearch(search, cacheList, true, reason); - - page = null; - cacheList.clear(); - - return search.getCurrentId(); - } - - public UUID searchByOffline(Map<String, Object> parameters) { - if (app == null) { - Log.e(cgSettings.tag, "cgeoBase.searchByOffline: No application found"); - return null; - } - - Geopoint coords = null; - String cachetype = null; - Integer list = 1; - - if (parameters.containsKey("latitude") && parameters.containsKey("longitude")) { - coords = new Geopoint((Double) parameters.get("latitude"), (Double) parameters.get("longitude")); - } - - if (parameters.containsKey("cachetype")) { - cachetype = (String) parameters.get("cachetype"); - } - - if (parameters.containsKey("list")) { - list = (Integer) parameters.get("list"); - } - - final cgSearch search = app.getBatchOfStoredCaches(true, coords, cachetype, list); - search.totalCnt = app.getAllStoredCachesCount(true, cachetype, list); - - return search.getCurrentId(); - } - - public UUID searchByHistory(Map<String, Object> parameters) { - if (app == null) { - Log.e(cgSettings.tag, "cgeoBase.searchByHistory: No application found"); - return null; - } - - String cachetype = null; - - if (parameters.containsKey("cachetype")) { - cachetype = (String) parameters.get("cachetype"); - } - - final cgSearch search = app.getHistoryOfCaches(true, cachetype); - search.totalCnt = app.getAllHistoricCachesCount(true, cachetype); - - return search.getCurrentId(); - } - - public UUID searchByCoords(cgSearchThread thread, Map<String, String> parameters, int reason, boolean showCaptcha) { - final cgSearch search = new cgSearch(); - final String latitude = parameters.get("latitude"); - final String longitude = parameters.get("longitude"); - String cacheType = parameters.get("cachetype"); - - if (StringUtils.isBlank(latitude)) { - Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No latitude given"); - return null; - } - - if (StringUtils.isBlank(longitude)) { - Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No longitude given"); - return null; - } - - if (StringUtils.isBlank(cacheType)) { - cacheType = null; - } - - final String host = "www.geocaching.com"; - final String path = "/seek/nearest.aspx"; - final String method = "GET"; - final Map<String, String> params = new HashMap<String, String>(); - if (cacheType != null && cacheIDs.containsKey(cacheType)) { - params.put("tx", cacheIDs.get(cacheType)); - } else { - params.put("tx", cacheIDs.get("all")); - } - params.put("lat", latitude); - params.put("lng", longitude); - - final String url = "http://" + host + path + "?" + prepareParameters(params, false, true); - String page = requestLogged(false, host, path, method, params, false, false, true); - - if (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No data from server"); - return null; - } - - final cgCacheWrap caches = parseSearch(thread, url, page, showCaptcha); - if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { - Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No cache parsed"); - } - - if (app == null) { - Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No application found"); - return null; - } - - List<cgCache> cacheList = processSearchResults(search, caches, settings.excludeDisabled, 0, null); - - app.addSearch(search, cacheList, true, reason); - - return search.getCurrentId(); - } - - public UUID searchByKeyword(cgSearchThread thread, Map<String, String> parameters, int reason, boolean showCaptcha) { - final cgSearch search = new cgSearch(); - final String keyword = parameters.get("keyword"); - String cacheType = parameters.get("cachetype"); - - if (StringUtils.isBlank(keyword)) { - Log.e(cgSettings.tag, "cgeoBase.searchByKeyword: No keyword given"); - return null; - } - - if (StringUtils.isBlank(cacheType)) { - cacheType = null; - } - - final String host = "www.geocaching.com"; - final String path = "/seek/nearest.aspx"; - final String method = "GET"; - final Map<String, String> params = new HashMap<String, String>(); - if (cacheType != null && cacheIDs.containsKey(cacheType)) { - params.put("tx", cacheIDs.get(cacheType)); - } else { - params.put("tx", cacheIDs.get("all")); - } - params.put("key", keyword); - - final String url = "http://" + host + path + "?" + prepareParameters(params, false, true); - String page = requestLogged(false, host, path, method, params, false, false, true); - - if (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgeoBase.searchByKeyword: No data from server"); - return null; - } - - final cgCacheWrap caches = parseSearch(thread, url, page, showCaptcha); - if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { - Log.e(cgSettings.tag, "cgeoBase.searchByKeyword: No cache parsed"); - } - - if (app == null) { - Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No application found"); - return null; - } - - List<cgCache> cacheList = processSearchResults(search, caches, settings.excludeDisabled, 0, null); - - app.addSearch(search, cacheList, true, reason); - - return search.getCurrentId(); - } - - public UUID searchByUsername(cgSearchThread thread, Map<String, String> parameters, int reason, boolean showCaptcha) { - final cgSearch search = new cgSearch(); - final String userName = parameters.get("username"); - String cacheType = parameters.get("cachetype"); - - if (StringUtils.isBlank(userName)) { - Log.e(cgSettings.tag, "cgeoBase.searchByUsername: No user name given"); - return null; - } - - if (StringUtils.isBlank(cacheType)) { - cacheType = null; - } - - final String host = "www.geocaching.com"; - final String path = "/seek/nearest.aspx"; - final String method = "GET"; - final Map<String, String> params = new HashMap<String, String>(); - if (cacheType != null && cacheIDs.containsKey(cacheType)) { - params.put("tx", cacheIDs.get(cacheType)); - } else { - params.put("tx", cacheIDs.get("all")); - } - params.put("ul", userName); - - boolean my = false; - if (userName.equalsIgnoreCase(settings.getLogin().get("username"))) { - my = true; - Log.i(cgSettings.tag, "cgBase.searchByUsername: Overriding users choice, downloading all caches."); - } - - final String url = "http://" + host + path + "?" + prepareParameters(params, my, true); - String page = requestLogged(false, host, path, method, params, false, my, true); - - if (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgeoBase.searchByUsername: No data from server"); - return null; - } - - final cgCacheWrap caches = parseSearch(thread, url, page, showCaptcha); - if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { - Log.e(cgSettings.tag, "cgeoBase.searchByUsername: No cache parsed"); - } - - if (app == null) { - Log.e(cgSettings.tag, "cgeoBase.searchByUsername: No application found"); - return null; - } - - List<cgCache> cacheList = processSearchResults(search, caches, settings.excludeDisabled, 0, null); - - app.addSearch(search, cacheList, true, reason); - - return search.getCurrentId(); - } - - public UUID searchByOwner(cgSearchThread thread, Map<String, String> parameters, int reason, boolean showCaptcha) { - final cgSearch search = new cgSearch(); - final String userName = parameters.get("username"); - String cacheType = parameters.get("cachetype"); - - if (StringUtils.isBlank(userName)) { - Log.e(cgSettings.tag, "cgeoBase.searchByOwner: No user name given"); - return null; - } - - if (StringUtils.isBlank(cacheType)) { - cacheType = null; - } - - final String host = "www.geocaching.com"; - final String path = "/seek/nearest.aspx"; - final String method = "GET"; - final Map<String, String> params = new HashMap<String, String>(); - if (cacheType != null && cacheIDs.containsKey(cacheType)) { - params.put("tx", cacheIDs.get(cacheType)); - } else { - params.put("tx", cacheIDs.get("all")); - } - params.put("u", userName); - - final String url = "http://" + host + path + "?" + prepareParameters(params, false, true); - String page = requestLogged(false, host, path, method, params, false, false, true); - - if (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgeoBase.searchByOwner: No data from server"); - return null; - } - - final cgCacheWrap caches = parseSearch(thread, url, page, showCaptcha); - if (caches == null || caches.cacheList == null) { - Log.e(cgSettings.tag, "cgeoBase.searchByOwner: No cache parsed"); - } - - if (app == null) { - Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No application found"); - return null; - } - - List<cgCache> cacheList = processSearchResults(search, caches, settings.excludeDisabled, 0, null); - - app.addSearch(search, cacheList, true, reason); - - return search.getCurrentId(); - } - - public UUID searchByViewport(Map<String, String> parameters, int reason) { - final cgSearch search = new cgSearch(); - final String latMin = parameters.get("latitude-min"); - final String latMax = parameters.get("latitude-max"); - final String lonMin = parameters.get("longitude-min"); - final String lonMax = parameters.get("longitude-max"); - - String usertoken = null; - if (parameters.get("usertoken") != null) { - usertoken = parameters.get("usertoken"); - } else { - usertoken = ""; - } - - String page = null; - - if (StringUtils.isBlank(latMin) || StringUtils.isBlank(latMax) || StringUtils.isBlank(lonMin) || StringUtils.isBlank(lonMax)) { - Log.e(cgSettings.tag, "cgeoBase.searchByViewport: Not enough parameters to recognize viewport"); - return null; - } - - final String host = "www.geocaching.com"; - final String path = "/map/default.aspx/MapAction"; - - String params = "{\"dto\":{\"data\":{\"c\":1,\"m\":\"\",\"d\":\"" + latMax + "|" + latMin + "|" + lonMax + "|" + lonMin + "\"},\"ut\":\"" + usertoken + "\"}}"; - - final String url = "http://" + host + path + "?" + params; - page = requestJSONgc(host, path, params); - - if (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgeoBase.searchByViewport: No data from server"); - return null; - } - - final cgCacheWrap caches = parseMapJSON(url, page); - if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { - Log.e(cgSettings.tag, "cgeoBase.searchByViewport: No cache parsed"); - } - - if (app == null) { - Log.e(cgSettings.tag, "cgeoBase.searchByViewport: No application found"); - return null; - } - - List<cgCache> cacheList = processSearchResults(search, caches, settings.excludeDisabled, settings.excludeMine, settings.cacheType); - - app.addSearch(search, cacheList, true, reason); - - return search.getCurrentId(); - } - - public List<cgUser> getGeocachersInViewport(String username, Double latMin, Double latMax, Double lonMin, Double lonMax) { - final List<cgUser> users = new ArrayList<cgUser>(); - - if (username == null) { - return users; - } - if (latMin == null || latMax == null || lonMin == null || lonMax == null) { - return users; - } - - final String host = "api.go4cache.com"; - final String path = "/get.php"; - final String method = "POST"; - final Map<String, String> params = new HashMap<String, String>(); - - params.put("u", username); - params.put("ltm", String.format((Locale) null, "%.6f", latMin)); - params.put("ltx", String.format((Locale) null, "%.6f", latMax)); - params.put("lnm", String.format((Locale) null, "%.6f", lonMin)); - params.put("lnx", String.format((Locale) null, "%.6f", lonMax)); - - final String data = request(false, host, path, method, params, false, false, false).getData(); - - if (StringUtils.isBlank(data)) { - Log.e(cgSettings.tag, "cgeoBase.getGeocachersInViewport: No data from server"); - return null; - } - - try { - final JSONObject dataJSON = new JSONObject(data); - - final JSONArray usersData = dataJSON.getJSONArray("users"); - if (usersData != null && usersData.length() > 0) { - int count = usersData.length(); - JSONObject oneUser = null; - for (int i = 0; i < count; i++) { - final cgUser user = new cgUser(); - oneUser = usersData.getJSONObject(i); - if (oneUser != null) { - final String located = oneUser.getString("located"); - if (located != null) { - user.located = dateSqlIn.parse(located); - } else { - user.located = new Date(); - } - user.username = oneUser.getString("user"); - user.coords = new Geopoint(oneUser.getDouble("latitude"), oneUser.getDouble("longitude")); - user.action = oneUser.getString("action"); - user.client = oneUser.getString("client"); - - if (user.coords != null) { - users.add(user); - } - } - } - } - } catch (Exception e) { - Log.e(cgSettings.tag, "cgBase.getGeocachersInViewport: " + e.toString()); - } - - return users; - } - - public List<cgCache> processSearchResults(cgSearch search, cgCacheWrap caches, int excludeDisabled, int excludeMine, String cacheType) { - List<cgCache> cacheList = new ArrayList<cgCache>(); - if (caches != null) { - if (StringUtils.isNotBlank(caches.error)) { - search.error = caches.error; - } - if (StringUtils.isNotBlank(caches.url)) { - search.url = caches.url; - } - search.viewstates = caches.viewstates; - search.totalCnt = caches.totalCnt; - - if (CollectionUtils.isNotEmpty(caches.cacheList)) { - for (cgCache cache : caches.cacheList) { - if ((excludeDisabled == 0 || (excludeDisabled == 1 && cache.disabled == false)) - && (excludeMine == 0 || (excludeMine == 1 && cache.own == false)) - && (excludeMine == 0 || (excludeMine == 1 && cache.found == false)) - && (cacheType == null || (cacheType.equals(cache.type)))) { - search.addGeocode(cache.geocode); - cacheList.add(cache); - } - } - } - } - return cacheList; - } - - public cgTrackable searchTrackable(Map<String, String> parameters) { - final String geocode = parameters.get("geocode"); - final String guid = parameters.get("guid"); - final String id = parameters.get("id"); - cgTrackable trackable = new cgTrackable(); - - if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid) && StringUtils.isBlank(id)) { - Log.e(cgSettings.tag, "cgeoBase.searchTrackable: No geocode nor guid nor id given"); - return null; - } - - final String host = "www.geocaching.com"; - final String path = "/track/details.aspx"; - final String method = "GET"; - final Map<String, String> params = new HashMap<String, String>(); - if (StringUtils.isNotBlank(geocode)) { - params.put("tracker", geocode); - } else if (StringUtils.isNotBlank(guid)) { - params.put("guid", guid); - } else if (StringUtils.isNotBlank(id)) { - params.put("id", id); - } - - String page = requestLogged(false, host, path, method, params, false, false, false); - - if (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgeoBase.searchTrackable: No data from server"); - return trackable; - } - - trackable = parseTrackable(page); - if (trackable == null) { - Log.e(cgSettings.tag, "cgeoBase.searchTrackable: No trackable parsed"); - return trackable; - } - - return trackable; - } - - public int postLog(cgeoapplication app, String geocode, String cacheid, String[] viewstates, - int logType, int year, int month, int day, String log, List<cgTrackableLog> trackables) { - if (isEmpty(viewstates)) { - Log.e(cgSettings.tag, "cgeoBase.postLog: No viewstate given"); - return 1000; - } - - if (logTypes2.containsKey(logType) == false) { - Log.e(cgSettings.tag, "cgeoBase.postLog: Unknown logtype"); - return 1000; - } - - if (StringUtils.isBlank(log)) { - Log.e(cgSettings.tag, "cgeoBase.postLog: No log text given"); - return 1001; - } - - // fix log (non-Latin characters converted to HTML entities) - final int logLen = log.length(); - final StringBuilder logUpdated = new StringBuilder(); - - for (int i = 0; i < logLen; i++) { - char c = log.charAt(i); - - if (c > 300) { - logUpdated.append("&#"); - logUpdated.append(Integer.toString((int) c)); - logUpdated.append(';'); - } else { - logUpdated.append(c); - } - } - log = logUpdated.toString(); - - log = log.replace("\n", "\r\n"); // windows' eol - - if (trackables != null) { - Log.i(cgSettings.tag, "Trying to post log for cache #" + cacheid + " - action: " + logType + "; date: " + year + "." + month + "." + day + ", log: " + log + "; trackables: " + trackables.size()); - } else { - Log.i(cgSettings.tag, "Trying to post log for cache #" + cacheid + " - action: " + logType + "; date: " + year + "." + month + "." + day + ", log: " + log + "; trackables: 0"); - } - - final String host = "www.geocaching.com"; - final String path = "/seek/log.aspx?ID=" + cacheid; - final String method = "POST"; - final Map<String, String> params = new HashMap<String, String>(); - - setViewstates(viewstates, params); - params.put("__EVENTTARGET", ""); - params.put("__EVENTARGUMENT", ""); - params.put("__LASTFOCUS", ""); - params.put("ctl00$ContentBody$LogBookPanel1$ddLogType", Integer.toString(logType)); - params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", String.format("%02d", month) + "/" + String.format("%02d", day) + "/" + String.format("%04d", year)); - params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Month", Integer.toString(month)); - params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Day", Integer.toString(day)); - params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Year", Integer.toString(year)); - params.put("ctl00$ContentBody$LogBookPanel1$uxLogInfo", log); - params.put("ctl00$ContentBody$LogBookPanel1$LogButton", "Submit Log Entry"); - params.put("ctl00$ContentBody$uxVistOtherListingGC", ""); - if (trackables != null && trackables.isEmpty() == false) { // we have some trackables to proceed - final StringBuilder hdnSelected = new StringBuilder(); - - for (cgTrackableLog tb : trackables) { - final String action = Integer.toString(tb.id) + logTypesTrackableAction.get(tb.action); - - if (tb.action > 0) { - hdnSelected.append(action); - hdnSelected.append(','); - } - } - - params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnSelectedActions", hdnSelected.toString()); // selected trackables - params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnCurrentFilter", ""); - } - - String page = request(false, host, path, method, params, false, false, false).getData(); - if (checkLogin(page) == false) { - int loginState = login(); - if (loginState == 1) { - page = request(false, host, path, method, params, false, false, false).getData(); - } else { - Log.e(cgSettings.tag, "cgeoBase.postLog: Can not log in geocaching (error: " + loginState + ")"); - return loginState; - } - } - - if (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgeoBase.postLog: No data from server"); - return 1002; - } - - // maintenance, archived needs to be confirmed - final Pattern pattern = Pattern.compile("<span id=\"ctl00_ContentBody_LogBookPanel1_lbConfirm\"[^>]*>([^<]*<font[^>]*>)?([^<]+)(</font>[^<]*)?</span>", Pattern.CASE_INSENSITIVE); - final Matcher matcher = pattern.matcher(page); - - try { - if (matcher.find() && matcher.groupCount() > 0) { - final String[] viewstatesConfirm = getViewstates(page); - - if (isEmpty(viewstatesConfirm)) { - Log.e(cgSettings.tag, "cgeoBase.postLog: No viewstate for confirm log"); - return 1000; - } - - params.clear(); - setViewstates(viewstatesConfirm, params); - params.put("__EVENTTARGET", ""); - params.put("__EVENTARGUMENT", ""); - params.put("__LASTFOCUS", ""); - params.put("ctl00$ContentBody$LogBookPanel1$btnConfirm", "Yes"); - params.put("ctl00$ContentBody$LogBookPanel1$uxLogInfo", log); - params.put("ctl00$ContentBody$uxVistOtherListingGC", ""); - if (trackables != null && trackables.isEmpty() == false) { // we have some trackables to proceed - final StringBuilder hdnSelected = new StringBuilder(); - - for (cgTrackableLog tb : trackables) { - String ctl = null; - final String action = Integer.toString(tb.id) + logTypesTrackableAction.get(tb.action); - - if (tb.ctl < 10) { - ctl = "0" + Integer.toString(tb.ctl); - } else { - ctl = Integer.toString(tb.ctl); - } - - params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$repTravelBugs$ctl" + ctl + "$ddlAction", action); - if (tb.action > 0) { - hdnSelected.append(action); - hdnSelected.append(','); - } - } - - params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnSelectedActions", hdnSelected.toString()); // selected trackables - params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnCurrentFilter", ""); - } - - page = request(false, host, path, method, params, false, false, false).getData(); - } - } catch (Exception e) { - Log.e(cgSettings.tag, "cgeoBase.postLog.confim: " + e.toString()); - } - - try { - final Pattern patternOk = Pattern.compile("<h2[^>]*>[^<]*<span id=\"ctl00_ContentBody_lbHeading\"[^>]*>[^<]*</span>[^<]*</h2>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - final Matcher matcherOk = patternOk.matcher(page); - if (matcherOk.find()) { - Log.i(cgSettings.tag, "Log successfully posted to cache #" + cacheid); - - if (app != null && geocode != null) { - app.saveVisitDate(geocode); - } - - return 1; - } - } catch (Exception e) { - Log.e(cgSettings.tag, "cgeoBase.postLog.check: " + e.toString()); - } - - Log.e(cgSettings.tag, "cgeoBase.postLog: Failed to post log because of unknown error"); - return 1000; - } - - public int postLogTrackable(String tbid, String trackingCode, String[] viewstates, - int logType, int year, int month, int day, String log) { - if (isEmpty(viewstates)) { - Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: No viewstate given"); - return 1000; - } - - if (logTypes2.containsKey(logType) == false) { - Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: Unknown logtype"); - return 1000; - } - - if (StringUtils.isBlank(log)) { - Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: No log text given"); - return 1001; - } - - Log.i(cgSettings.tag, "Trying to post log for trackable #" + trackingCode + " - action: " + logType + "; date: " + year + "." + month + "." + day + ", log: " + log); - - log = log.replace("\n", "\r\n"); // windows' eol - - final Calendar currentDate = Calendar.getInstance(); - final String host = "www.geocaching.com"; - final String path = "/track/log.aspx?wid=" + tbid; - final String method = "POST"; - final Map<String, String> params = new HashMap<String, String>(); - - setViewstates(viewstates, params); - params.put("__EVENTTARGET", ""); - params.put("__EVENTARGUMENT", ""); - params.put("__LASTFOCUS", ""); - params.put("ctl00$ContentBody$LogBookPanel1$ddLogType", Integer.toString(logType)); - params.put("ctl00$ContentBody$LogBookPanel1$tbCode", trackingCode); - if (currentDate.get(Calendar.YEAR) == year && (currentDate.get(Calendar.MONTH) + 1) == month && currentDate.get(Calendar.DATE) == day) { - params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", ""); - } else { - params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", Integer.toString(month) + "/" + Integer.toString(day) + "/" + Integer.toString(year)); - } - params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Day", Integer.toString(day)); - params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Month", Integer.toString(month)); - params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Year", Integer.toString(year)); - params.put("ctl00$ContentBody$LogBookPanel1$uxLogInfo", log); - params.put("ctl00$ContentBody$LogBookPanel1$LogButton", "Submit Log Entry"); - params.put("ctl00$ContentBody$uxVistOtherListingGC", ""); - - String page = request(false, host, path, method, params, false, false, false).getData(); - if (checkLogin(page) == false) { - int loginState = login(); - if (loginState == 1) { - page = request(false, host, path, method, params, false, false, false).getData(); - } else { - Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: Can not log in geocaching (error: " + loginState + ")"); - return loginState; - } - } - - if (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: No data from server"); - return 1002; - } - - try { - final Pattern patternOk = Pattern.compile("<div id=[\"|']ctl00_ContentBody_LogBookPanel1_ViewLogPanel[\"|']>", Pattern.CASE_INSENSITIVE); - final Matcher matcherOk = patternOk.matcher(page); - if (matcherOk.find()) { - Log.i(cgSettings.tag, "Log successfully posted to trackable #" + trackingCode); - return 1; - } - } catch (Exception e) { - Log.e(cgSettings.tag, "cgeoBase.postLogTrackable.check: " + e.toString()); - } - - Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: Failed to post log because of unknown error"); - return 1000; - } + logDone.author = Html.fromHtml(matcherLogs.group(3)).toString(); + + try + { + logDone.date = parseGcCustomDate(matcherLogs.group(2)).getTime(); + } catch (ParseException e) { + } + + logDone.log = matcherLogs.group(6).trim(); + + if (matcherLogs.group(4) != null && matcherLogs.group(5) != null) + { + logDone.cacheGuid = matcherLogs.group(4); + logDone.cacheName = matcherLogs.group(5); + } + + trackable.logs.add(logDone); + } + } catch (Exception e) { + // failed to parse logs + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache logs"); + } + + app.saveTrackable(trackable); + + return trackable; + } + + public static List<Integer> parseTypes(String page) { + if (StringUtils.isEmpty(page)) { + return null; + } + + final List<Integer> types = new ArrayList<Integer>(); + + final Pattern typeBoxPattern = Pattern.compile("<select name=\"ctl00\\$ContentBody\\$LogBookPanel1\\$ddLogType\" id=\"ctl00_ContentBody_LogBookPanel1_ddLogType\"[^>]*>" + + "(([^<]*<option[^>]*>[^<]+</option>)+)[^<]*</select>", Pattern.CASE_INSENSITIVE); + final Matcher typeBoxMatcher = typeBoxPattern.matcher(page); + String typesText = null; + if (typeBoxMatcher.find()) { + if (typeBoxMatcher.groupCount() > 0) { + typesText = typeBoxMatcher.group(1); + } + } + + if (typesText != null) { + final Pattern typePattern = Pattern.compile("<option( selected=\"selected\")? value=\"(\\d+)\">[^<]+</option>", Pattern.CASE_INSENSITIVE); + final Matcher typeMatcher = typePattern.matcher(typesText); + while (typeMatcher.find()) { + if (typeMatcher.groupCount() > 1) { + final int type = Integer.parseInt(typeMatcher.group(2)); + + if (type > 0) { + types.add(type); + } + } + } + } + + return types; + } + + public static List<cgTrackableLog> parseTrackableLog(String page) { + if (StringUtils.isEmpty(page)) { + return null; + } + + final List<cgTrackableLog> trackables = new ArrayList<cgTrackableLog>(); + + int startPos = -1; + int endPos = -1; + + startPos = page.indexOf("<table id=\"tblTravelBugs\""); + if (startPos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseTrackableLog: ID \"tblTravelBugs\" not found on page"); + return null; + } + + page = page.substring(startPos); // cut on <table + + endPos = page.indexOf("</table>"); + if (endPos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseTrackableLog: end of ID \"tblTravelBugs\" not found on page"); + return null; + } + + page = page.substring(0, endPos); // cut on </table> + + startPos = page.indexOf("<tbody>"); + if (startPos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseTrackableLog: tbody not found on page"); + return null; + } + + page = page.substring(startPos); // cut on <tbody> + + endPos = page.indexOf("</tbody>"); + if (endPos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseTrackableLog: end of tbody not found on page"); + return null; + } + + page = page.substring(0, endPos); // cut on </tbody> + + final Pattern trackablePattern = Pattern.compile("<tr id=\"ctl00_ContentBody_LogBookPanel1_uxTrackables_repTravelBugs_ctl[0-9]+_row\"[^>]*>" + + "[^<]*<td>[^<]*<a href=\"[^\"]+\">([A-Z0-9]+)</a>[^<]*</td>[^<]*<td>([^<]+)</td>[^<]*<td>" + + "[^<]*<select name=\"ctl00\\$ContentBody\\$LogBookPanel1\\$uxTrackables\\$repTravelBugs\\$ctl([0-9]+)\\$ddlAction\"[^>]*>" + + "([^<]*<option value=\"([0-9]+)(_[a-z]+)?\">[^<]+</option>)+" + + "[^<]*</select>[^<]*</td>[^<]*</tr>", Pattern.CASE_INSENSITIVE); + final Matcher trackableMatcher = trackablePattern.matcher(page); + while (trackableMatcher.find()) { + if (trackableMatcher.groupCount() > 0) { + final cgTrackableLog trackable = new cgTrackableLog(); + + if (trackableMatcher.group(1) != null) { + trackable.trackCode = trackableMatcher.group(1); + } else { + continue; + } + if (trackableMatcher.group(2) != null) { + trackable.name = Html.fromHtml(trackableMatcher.group(2)).toString(); + } else { + continue; + } + if (trackableMatcher.group(3) != null) { + trackable.ctl = Integer.valueOf(trackableMatcher.group(3)); + } else { + continue; + } + if (trackableMatcher.group(5) != null) { + trackable.id = Integer.valueOf(trackableMatcher.group(5)); + } else { + continue; + } + + Log.i(cgSettings.tag, "Trackable in inventory (#" + trackable.ctl + "/" + trackable.id + "): " + trackable.trackCode + " - " + trackable.name); + + trackables.add(trackable); + } + } + + return trackables; + } + + public static String stripParagraphs(String text) { + if (StringUtils.isBlank(text)) { + return ""; + } + + final Pattern patternP = Pattern.compile("(<p>|</p>|<br \\/>|<br>)", Pattern.CASE_INSENSITIVE); + final Pattern patternP2 = Pattern.compile("([ ]+)", Pattern.CASE_INSENSITIVE); + final Matcher matcherP = patternP.matcher(text); + final Matcher matcherP2 = patternP2.matcher(text); + + matcherP.replaceAll(" "); + matcherP2.replaceAll(" "); + + return text.trim(); + } + + public static String stripTags(String text) { + if (StringUtils.isBlank(text)) { + return ""; + } + + final Pattern patternP = Pattern.compile("(<[^>]+>)", Pattern.CASE_INSENSITIVE); + final Matcher matcherP = patternP.matcher(text); + + matcherP.replaceAll(" "); + + return text.trim(); + } + + public String getHumanDistance(Float distance) { + if (distance == null) { + return "?"; + } + + if (settings.units == cgSettings.unitsImperial) { + distance /= miles2km; + if (distance > 100) { + return String.format(Locale.getDefault(), "%.0f", Double.valueOf(Math.round(distance))) + " mi"; + } else if (distance > 0.5) { + return String.format(Locale.getDefault(), "%.1f", Double.valueOf(Math.round(distance * 10.0) / 10.0)) + " mi"; + } else if (distance > 0.1) { + return String.format(Locale.getDefault(), "%.2f", Double.valueOf(Math.round(distance * 100.0) / 100.0)) + " mi"; + } else if (distance > 0.05) { + return String.format(Locale.getDefault(), "%.0f", Double.valueOf(Math.round(distance * 5280.0))) + " ft"; + } else if (distance > 0.01) { + return String.format(Locale.getDefault(), "%.1f", Double.valueOf(Math.round(distance * 5280 * 10.0) / 10.0)) + " ft"; + } else { + return String.format(Locale.getDefault(), "%.2f", Double.valueOf(Math.round(distance * 5280 * 100.0) / 100.0)) + " ft"; + } + } else { + if (distance > 100) { + return String.format(Locale.getDefault(), "%.0f", Double.valueOf(Math.round(distance))) + " km"; + } else if (distance > 10) { + return String.format(Locale.getDefault(), "%.1f", Double.valueOf(Math.round(distance * 10.0) / 10.0)) + " km"; + } else if (distance > 1) { + return String.format(Locale.getDefault(), "%.2f", Double.valueOf(Math.round(distance * 100.0) / 100.0)) + " km"; + } else if (distance > 0.1) { + return String.format(Locale.getDefault(), "%.0f", Double.valueOf(Math.round(distance * 1000.0))) + " m"; + } else if (distance > 0.01) { + return String.format(Locale.getDefault(), "%.1f", Double.valueOf(Math.round(distance * 1000.0 * 10.0) / 10.0)) + " m"; + } else { + return String.format(Locale.getDefault(), "%.2f", Double.valueOf(Math.round(distance * 1000.0 * 100.0) / 100.0)) + " m"; + } + } + } + + public String getHumanSpeed(float speed) { + double kph = speed * 3.6; + String unit = "km/h"; + + if (this.settings.units == cgSettings.unitsImperial) { + kph /= miles2km; + unit = "mph"; + } + + if (kph < 10.0) { + return String.format(Locale.getDefault(), "%.1f", Double.valueOf((Math.round(kph * 10.0) / 10.0))) + " " + unit; + } else { + return String.format(Locale.getDefault(), "%.0f", Double.valueOf(Math.round(kph))) + " " + unit; + } + } + + public static Map<String, Object> parseLatlon(String latlon) { + final Map<String, Object> result = new HashMap<String, Object>(); + final Pattern patternLatlon = Pattern.compile("([NS])[^\\d]*(\\d+)[^°]*° (\\d+)\\.(\\d+) ([WE])[^\\d]*(\\d+)[^°]*° (\\d+)\\.(\\d+)", Pattern.CASE_INSENSITIVE); + final Matcher matcherLatlon = patternLatlon.matcher(latlon); + + while (matcherLatlon.find()) { + if (matcherLatlon.groupCount() > 0) { + result.put("latitudeString", (String) (matcherLatlon.group(1) + " " + matcherLatlon.group(2) + "° " + matcherLatlon.group(3) + "." + matcherLatlon.group(4))); + result.put("longitudeString", (String) (matcherLatlon.group(5) + " " + matcherLatlon.group(6) + "° " + matcherLatlon.group(7) + "." + matcherLatlon.group(8))); + int latNegative = -1; + int lonNegative = -1; + if (matcherLatlon.group(1).equalsIgnoreCase("N")) { + latNegative = 1; + } + if (matcherLatlon.group(5).equalsIgnoreCase("E")) { + lonNegative = 1; + } + result.put("latitude", Double.valueOf(latNegative * (Float.valueOf(matcherLatlon.group(2)) + Float.valueOf(matcherLatlon.group(3) + "." + matcherLatlon.group(4)) / 60))); + result.put("longitude", Double.valueOf(lonNegative * (Float.valueOf(matcherLatlon.group(6)) + Float.valueOf(matcherLatlon.group(7) + "." + matcherLatlon.group(8)) / 60))); + } else { + Log.w(cgSettings.tag, "cgBase.parseLatlon: Failed to parse coordinates."); + } + } + + return result; + } + + private static String formatCoordinate(final Double coordIn, final boolean degrees, final String direction, final String digitsFormat) { + if (coordIn == null) { + return ""; + } + StringBuilder formatted = new StringBuilder(direction); + + double coordAbs = Math.abs(coordIn); + Locale locale = Locale.getDefault(); + double floor = Math.floor(coordAbs); + + formatted.append(String.format(locale, digitsFormat, floor)); + + if (degrees) { + formatted.append("° "); + } else { + formatted.append(" "); + } + formatted.append(String.format(locale, "%06.3f", ((coordAbs - floor) * 60))); + + return formatted.toString(); + } + + public static String formatLatitude(final Double coord, final boolean degrees) { + return formatCoordinate(coord, degrees, (coord >= 0) ? "N " : "S ", "%02.0f"); + } + + public static String formatLongitude(final Double coord, final boolean degrees) { + return formatCoordinate(coord, degrees, (coord >= 0) ? "E " : "W ", "%03.0f"); + } + + public static String formatCoords(final Geopoint coords, final boolean degrees) { + return formatLatitude(coords.getLatitude(), degrees) + " | " + formatLongitude(coords.getLongitude(), degrees); + } + + // TODO Use android.util.Pair<Double, String> if needed rather than a Map here. + public static Map<String, Object> parseCoordinate(String coord, String latlon) { + final Map<String, Object> coords = new HashMap<String, Object>(); + + final Pattern patternA = Pattern.compile("^([NSWE])[^\\d]*(\\d+)°? +(\\d+)([\\.|,](\\d+))?$", Pattern.CASE_INSENSITIVE); + final Pattern patternB = Pattern.compile("^([NSWE])[^\\d]*(\\d+)([\\.|,](\\d+))?$", Pattern.CASE_INSENSITIVE); + final Pattern patternC = Pattern.compile("^(-?\\d+)([\\.|,](\\d+))?$", Pattern.CASE_INSENSITIVE); + final Pattern patternD = Pattern.compile("^([NSWE])[^\\d]*(\\d+)°?$", Pattern.CASE_INSENSITIVE); + final Pattern patternE = Pattern.compile("^(-?\\d+)°?$", Pattern.CASE_INSENSITIVE); + final Pattern patternF = Pattern.compile("^([NSWE])[^\\d]*(\\d+)$", Pattern.CASE_INSENSITIVE); + final Pattern pattern0 = Pattern.compile("^(-?\\d+)([\\.|,](\\d+))?$", Pattern.CASE_INSENSITIVE); + + coord = coord.trim().toUpperCase(); + + final Matcher matcherA = patternA.matcher(coord); + final Matcher matcherB = patternB.matcher(coord); + final Matcher matcherC = patternC.matcher(coord); + final Matcher matcherD = patternD.matcher(coord); + final Matcher matcherE = patternE.matcher(coord); + final Matcher matcherF = patternF.matcher(coord); + final Matcher matcher0 = pattern0.matcher(coord); + + int latlonNegative; + if (matcherA.find() && matcherA.groupCount() > 0) { + if (matcherA.group(1).equalsIgnoreCase("N") || matcherA.group(1).equalsIgnoreCase("E")) { + latlonNegative = 1; + } else { + latlonNegative = -1; + } + + if (matcherA.groupCount() < 5 || matcherA.group(5) == null) { + coords.put("coordinate", Double.valueOf(latlonNegative * (Double.valueOf(matcherA.group(2)) + Double.valueOf(matcherA.group(3) + ".0") / 60))); + coords.put("string", matcherA.group(1) + " " + matcherA.group(2) + "° " + matcherA.group(3) + ".000"); + } else { + coords.put("coordinate", Double.valueOf(latlonNegative * (Double.valueOf(matcherA.group(2)) + Double.valueOf(matcherA.group(3) + "." + matcherA.group(5)) / 60))); + coords.put("string", matcherA.group(1) + " " + matcherA.group(2) + "° " + matcherA.group(3) + "." + matcherA.group(5)); + } + + return coords; + } else if (matcherB.find() && matcherB.groupCount() > 0) { + if (matcherB.group(1).equalsIgnoreCase("N") || matcherB.group(1).equalsIgnoreCase("E")) { + latlonNegative = 1; + } else { + latlonNegative = -1; + } + + if (matcherB.groupCount() < 4 || matcherB.group(4) == null) { + coords.put("coordinate", Double.valueOf(latlonNegative * (Double.valueOf(matcherB.group(2) + ".0")))); + } else { + coords.put("coordinate", Double.valueOf(latlonNegative * (Double.valueOf(matcherB.group(2) + "." + matcherB.group(4))))); + } + } else if (matcherC.find() && matcherC.groupCount() > 0) { + if (matcherC.groupCount() < 3 || matcherC.group(3) == null) { + coords.put("coordinate", Double.valueOf(new Float(matcherC.group(1) + ".0"))); + } else { + coords.put("coordinate", Double.valueOf(new Float(matcherC.group(1) + "." + matcherC.group(3)))); + } + } else if (matcherD.find() && matcherD.groupCount() > 0) { + if (matcherD.group(1).equalsIgnoreCase("N") || matcherD.group(1).equalsIgnoreCase("E")) { + latlonNegative = 1; + } else { + latlonNegative = -1; + } + + coords.put("coordinate", Double.valueOf(latlonNegative * (Double.valueOf(matcherB.group(2))))); + } else if (matcherE.find() && matcherE.groupCount() > 0) { + coords.put("coordinate", Double.valueOf(matcherE.group(1))); + } else if (matcherF.find() && matcherF.groupCount() > 0) { + if (matcherF.group(1).equalsIgnoreCase("N") || matcherF.group(1).equalsIgnoreCase("E")) { + latlonNegative = 1; + } else { + latlonNegative = -1; + } + + coords.put("coordinate", Double.valueOf(latlonNegative * (Double.valueOf(matcherB.group(2))))); + } else { + return null; + } + + if (matcher0.find() && matcher0.groupCount() > 0) { + String tmpDir = null; + Float tmpCoord; + if (matcher0.groupCount() < 3 || matcher0.group(3) == null) { + tmpCoord = new Float("0.0"); + } else { + tmpCoord = new Float("0." + matcher0.group(3)); + } + + if (latlon.equalsIgnoreCase("lat")) { + if (matcher0.group(1).equals("+")) { + tmpDir = "N"; + } + if (matcher0.group(1).equals("-")) { + tmpDir = "S"; + } + } else if (latlon.equalsIgnoreCase("lon")) { + if (matcher0.group(1).equals("+")) { + tmpDir = "E"; + } + if (matcher0.group(1).equals("-")) { + tmpDir = "W"; + } + } + + coords.put("string", tmpDir + " " + matcher0.group(1) + "° " + (Math.round(tmpCoord / (1 / 60) * 1000) * 1000)); + + return coords; + } else { + return new HashMap<String, Object>(); + } + } + + public UUID searchByNextPage(cgSearchThread thread, final UUID searchId, int reason, boolean showCaptcha) { + final String[] viewstates = app.getViewstates(searchId); + + String url = app.getUrl(searchId); + + if (StringUtils.isBlank(url)) { + Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: No url found"); + return searchId; + } + + if (isEmpty(viewstates)) { + Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: No viewstate given"); + return searchId; + } + + String host = "www.geocaching.com"; + String path = "/"; + final String method = "POST"; + + int dash = -1; + if (url.indexOf("http://") > -1) { + url = url.substring(7); + } + + dash = url.indexOf("/"); + if (dash > -1) { + host = url.substring(0, dash); + url = url.substring(dash); + } else { + host = url; + url = ""; + } + + dash = url.indexOf("?"); + if (dash > -1) { + path = url.substring(0, dash); + } else { + path = url; + } + + final Map<String, String> params = new HashMap<String, String>(); + setViewstates(viewstates, params); + params.put("__EVENTTARGET", "ctl00$ContentBody$pgrBottom$ctl08"); + params.put("__EVENTARGUMENT", ""); + + String page = request(false, host, path, method, params, false, false, true).getData(); + if (checkLogin(page) == false) { + int loginState = login(); + if (loginState == 1) { + page = request(false, host, path, method, params, false, false, true).getData(); + } else if (loginState == -3) { + Log.i(cgSettings.tag, "Working as guest."); + } else { + app.setError(searchId, errorRetrieve.get(loginState)); + Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: Can not log in geocaching"); + return searchId; + } + } + + if (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: No data from server"); + return searchId; + } + + final cgCacheWrap caches = parseSearch(thread, url, page, showCaptcha); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: No cache parsed"); + return searchId; + } + + // save to application + app.setError(searchId, caches.error); + app.setViewstates(searchId, caches.viewstates); + + final List<cgCache> cacheList = new ArrayList<cgCache>(); + for (cgCache cache : caches.cacheList) { + app.addGeocode(searchId, cache.geocode); + cacheList.add(cache); + } + + app.addSearch(searchId, cacheList, true, reason); + + return searchId; + } + + public UUID searchByGeocode(Map<String, String> parameters, int reason, boolean forceReload) { + final cgSearch search = new cgSearch(); + String geocode = parameters.get("geocode"); + String guid = parameters.get("guid"); + + if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid)) { + Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No geocode nor guid given"); + return null; + } + + if (forceReload == false && reason == 0 && (app.isOffline(geocode, guid) || app.isThere(geocode, guid, true, true))) { + if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(guid)) { + geocode = app.getGeocode(guid); + } + + List<cgCache> cacheList = new ArrayList<cgCache>(); + cacheList.add(app.getCacheByGeocode(geocode, true, true, true, true, true, true)); + search.addGeocode(geocode); + + app.addSearch(search, cacheList, false, reason); + + cacheList.clear(); + cacheList = null; + + return search.getCurrentId(); + } + + final String host = "www.geocaching.com"; + final String path = "/seek/cache_details.aspx"; + final String method = "GET"; + final Map<String, String> params = new HashMap<String, String>(); + if (StringUtils.isNotBlank(geocode)) { + params.put("wp", geocode); + } else if (StringUtils.isNotBlank(guid)) { + params.put("guid", guid); + } + params.put("decrypt", "y"); + params.put("log", "y"); // download logs (more than 5 + params.put("numlogs", "35"); // 35 logs + + String page = requestLogged(false, host, path, method, params, false, false, false); + + if (StringUtils.isEmpty(page)) { + if (app.isThere(geocode, guid, true, false)) { + if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(guid)) { + Log.i(cgSettings.tag, "Loading old cache from cache."); + + geocode = app.getGeocode(guid); + } + + final List<cgCache> cacheList = new ArrayList<cgCache>(); + cacheList.add(app.getCacheByGeocode(geocode)); + search.addGeocode(geocode); + search.error = null; + + app.addSearch(search, cacheList, false, reason); + + cacheList.clear(); + + return search.getCurrentId(); + } + + Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No data from server"); + return null; + } + + final cgCacheWrap caches = parseCache(page, reason); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + if (caches != null && StringUtils.isNotBlank(caches.error)) { + search.error = caches.error; + } + if (caches != null && StringUtils.isNotBlank(caches.url)) { + search.url = caches.url; + } + + app.addSearch(search, null, true, reason); + + Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No cache parsed"); + return null; + } + + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No application found"); + return null; + } + + List<cgCache> cacheList = processSearchResults(search, caches, 0, 0, null); + + app.addSearch(search, cacheList, true, reason); + + page = null; + cacheList.clear(); + + return search.getCurrentId(); + } + + public UUID searchByOffline(Map<String, Object> parameters) { + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByOffline: No application found"); + return null; + } + + Geopoint coords = null; + String cachetype = null; + Integer list = 1; + + if (parameters.containsKey("latitude") && parameters.containsKey("longitude")) { + coords = new Geopoint((Double) parameters.get("latitude"), (Double) parameters.get("longitude")); + } + + if (parameters.containsKey("cachetype")) { + cachetype = (String) parameters.get("cachetype"); + } + + if (parameters.containsKey("list")) { + list = (Integer) parameters.get("list"); + } + + final cgSearch search = app.getBatchOfStoredCaches(true, coords, cachetype, list); + search.totalCnt = app.getAllStoredCachesCount(true, cachetype, list); + + return search.getCurrentId(); + } + + public UUID searchByHistory(Map<String, Object> parameters) { + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByHistory: No application found"); + return null; + } + + String cachetype = null; + + if (parameters.containsKey("cachetype")) { + cachetype = (String) parameters.get("cachetype"); + } + + final cgSearch search = app.getHistoryOfCaches(true, cachetype); + search.totalCnt = app.getAllHistoricCachesCount(true, cachetype); + + return search.getCurrentId(); + } + + public UUID searchByCoords(cgSearchThread thread, Map<String, String> parameters, int reason, boolean showCaptcha) { + final cgSearch search = new cgSearch(); + final String latitude = parameters.get("latitude"); + final String longitude = parameters.get("longitude"); + String cacheType = parameters.get("cachetype"); + + if (StringUtils.isBlank(latitude)) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No latitude given"); + return null; + } + + if (StringUtils.isBlank(longitude)) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No longitude given"); + return null; + } + + if (StringUtils.isBlank(cacheType)) { + cacheType = null; + } + + final String host = "www.geocaching.com"; + final String path = "/seek/nearest.aspx"; + final String method = "GET"; + final Map<String, String> params = new HashMap<String, String>(); + if (cacheType != null && cacheIDs.containsKey(cacheType)) { + params.put("tx", cacheIDs.get(cacheType)); + } else { + params.put("tx", cacheIDs.get("all")); + } + params.put("lat", latitude); + params.put("lng", longitude); + + final String url = "http://" + host + path + "?" + prepareParameters(params, false, true); + String page = requestLogged(false, host, path, method, params, false, false, true); + + if (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No data from server"); + return null; + } + + final cgCacheWrap caches = parseSearch(thread, url, page, showCaptcha); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No cache parsed"); + } + + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No application found"); + return null; + } + + List<cgCache> cacheList = processSearchResults(search, caches, settings.excludeDisabled, 0, null); + + app.addSearch(search, cacheList, true, reason); + + return search.getCurrentId(); + } + + public UUID searchByKeyword(cgSearchThread thread, Map<String, String> parameters, int reason, boolean showCaptcha) { + final cgSearch search = new cgSearch(); + final String keyword = parameters.get("keyword"); + String cacheType = parameters.get("cachetype"); + + if (StringUtils.isBlank(keyword)) { + Log.e(cgSettings.tag, "cgeoBase.searchByKeyword: No keyword given"); + return null; + } + + if (StringUtils.isBlank(cacheType)) { + cacheType = null; + } + + final String host = "www.geocaching.com"; + final String path = "/seek/nearest.aspx"; + final String method = "GET"; + final Map<String, String> params = new HashMap<String, String>(); + if (cacheType != null && cacheIDs.containsKey(cacheType)) { + params.put("tx", cacheIDs.get(cacheType)); + } else { + params.put("tx", cacheIDs.get("all")); + } + params.put("key", keyword); + + final String url = "http://" + host + path + "?" + prepareParameters(params, false, true); + String page = requestLogged(false, host, path, method, params, false, false, true); + + if (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgeoBase.searchByKeyword: No data from server"); + return null; + } + + final cgCacheWrap caches = parseSearch(thread, url, page, showCaptcha); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + Log.e(cgSettings.tag, "cgeoBase.searchByKeyword: No cache parsed"); + } + + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No application found"); + return null; + } + + List<cgCache> cacheList = processSearchResults(search, caches, settings.excludeDisabled, 0, null); + + app.addSearch(search, cacheList, true, reason); + + return search.getCurrentId(); + } + + public UUID searchByUsername(cgSearchThread thread, Map<String, String> parameters, int reason, boolean showCaptcha) { + final cgSearch search = new cgSearch(); + final String userName = parameters.get("username"); + String cacheType = parameters.get("cachetype"); + + if (StringUtils.isBlank(userName)) { + Log.e(cgSettings.tag, "cgeoBase.searchByUsername: No user name given"); + return null; + } + + if (StringUtils.isBlank(cacheType)) { + cacheType = null; + } + + final String host = "www.geocaching.com"; + final String path = "/seek/nearest.aspx"; + final String method = "GET"; + final Map<String, String> params = new HashMap<String, String>(); + if (cacheType != null && cacheIDs.containsKey(cacheType)) { + params.put("tx", cacheIDs.get(cacheType)); + } else { + params.put("tx", cacheIDs.get("all")); + } + params.put("ul", userName); + + boolean my = false; + if (userName.equalsIgnoreCase(settings.getLogin().get("username"))) { + my = true; + Log.i(cgSettings.tag, "cgBase.searchByUsername: Overriding users choice, downloading all caches."); + } + + final String url = "http://" + host + path + "?" + prepareParameters(params, my, true); + String page = requestLogged(false, host, path, method, params, false, my, true); + + if (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgeoBase.searchByUsername: No data from server"); + return null; + } + + final cgCacheWrap caches = parseSearch(thread, url, page, showCaptcha); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + Log.e(cgSettings.tag, "cgeoBase.searchByUsername: No cache parsed"); + } + + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByUsername: No application found"); + return null; + } + + List<cgCache> cacheList = processSearchResults(search, caches, settings.excludeDisabled, 0, null); + + app.addSearch(search, cacheList, true, reason); + + return search.getCurrentId(); + } + + public UUID searchByOwner(cgSearchThread thread, Map<String, String> parameters, int reason, boolean showCaptcha) { + final cgSearch search = new cgSearch(); + final String userName = parameters.get("username"); + String cacheType = parameters.get("cachetype"); + + if (StringUtils.isBlank(userName)) { + Log.e(cgSettings.tag, "cgeoBase.searchByOwner: No user name given"); + return null; + } + + if (StringUtils.isBlank(cacheType)) { + cacheType = null; + } + + final String host = "www.geocaching.com"; + final String path = "/seek/nearest.aspx"; + final String method = "GET"; + final Map<String, String> params = new HashMap<String, String>(); + if (cacheType != null && cacheIDs.containsKey(cacheType)) { + params.put("tx", cacheIDs.get(cacheType)); + } else { + params.put("tx", cacheIDs.get("all")); + } + params.put("u", userName); + + final String url = "http://" + host + path + "?" + prepareParameters(params, false, true); + String page = requestLogged(false, host, path, method, params, false, false, true); + + if (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgeoBase.searchByOwner: No data from server"); + return null; + } + + final cgCacheWrap caches = parseSearch(thread, url, page, showCaptcha); + if (caches == null || caches.cacheList == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByOwner: No cache parsed"); + } + + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No application found"); + return null; + } + + List<cgCache> cacheList = processSearchResults(search, caches, settings.excludeDisabled, 0, null); + + app.addSearch(search, cacheList, true, reason); + + return search.getCurrentId(); + } + + public UUID searchByViewport(Map<String, String> parameters, int reason) { + final cgSearch search = new cgSearch(); + final String latMin = parameters.get("latitude-min"); + final String latMax = parameters.get("latitude-max"); + final String lonMin = parameters.get("longitude-min"); + final String lonMax = parameters.get("longitude-max"); + + String usertoken = null; + if (parameters.get("usertoken") != null) { + usertoken = parameters.get("usertoken"); + } else { + usertoken = ""; + } + + String page = null; + + if (StringUtils.isBlank(latMin) || StringUtils.isBlank(latMax) || StringUtils.isBlank(lonMin) || StringUtils.isBlank(lonMax)) { + Log.e(cgSettings.tag, "cgeoBase.searchByViewport: Not enough parameters to recognize viewport"); + return null; + } + + final String host = "www.geocaching.com"; + final String path = "/map/default.aspx/MapAction"; + + String params = "{\"dto\":{\"data\":{\"c\":1,\"m\":\"\",\"d\":\"" + latMax + "|" + latMin + "|" + lonMax + "|" + lonMin + "\"},\"ut\":\"" + usertoken + "\"}}"; + + final String url = "http://" + host + path + "?" + params; + page = requestJSONgc(host, path, params); + + if (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgeoBase.searchByViewport: No data from server"); + return null; + } + + final cgCacheWrap caches = parseMapJSON(url, page); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + Log.e(cgSettings.tag, "cgeoBase.searchByViewport: No cache parsed"); + } + + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByViewport: No application found"); + return null; + } + + List<cgCache> cacheList = processSearchResults(search, caches, settings.excludeDisabled, settings.excludeMine, settings.cacheType); + + app.addSearch(search, cacheList, true, reason); + + return search.getCurrentId(); + } + + public List<cgUser> getGeocachersInViewport(String username, Double latMin, Double latMax, Double lonMin, Double lonMax) { + final List<cgUser> users = new ArrayList<cgUser>(); + + if (username == null) { + return users; + } + if (latMin == null || latMax == null || lonMin == null || lonMax == null) { + return users; + } + + final String host = "api.go4cache.com"; + final String path = "/get.php"; + final String method = "POST"; + final Map<String, String> params = new HashMap<String, String>(); + + params.put("u", username); + params.put("ltm", String.format((Locale) null, "%.6f", latMin)); + params.put("ltx", String.format((Locale) null, "%.6f", latMax)); + params.put("lnm", String.format((Locale) null, "%.6f", lonMin)); + params.put("lnx", String.format((Locale) null, "%.6f", lonMax)); + + final String data = request(false, host, path, method, params, false, false, false).getData(); + + if (StringUtils.isBlank(data)) { + Log.e(cgSettings.tag, "cgeoBase.getGeocachersInViewport: No data from server"); + return null; + } + + try { + final JSONObject dataJSON = new JSONObject(data); + + final JSONArray usersData = dataJSON.getJSONArray("users"); + if (usersData != null && usersData.length() > 0) { + int count = usersData.length(); + JSONObject oneUser = null; + for (int i = 0; i < count; i++) { + final cgUser user = new cgUser(); + oneUser = usersData.getJSONObject(i); + if (oneUser != null) { + final String located = oneUser.getString("located"); + if (located != null) { + user.located = dateSqlIn.parse(located); + } else { + user.located = new Date(); + } + user.username = oneUser.getString("user"); + user.coords = new Geopoint(oneUser.getDouble("latitude"), oneUser.getDouble("longitude")); + user.action = oneUser.getString("action"); + user.client = oneUser.getString("client"); + + if (user.coords != null) { + users.add(user); + } + } + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.getGeocachersInViewport: " + e.toString()); + } + + return users; + } + + public List<cgCache> processSearchResults(cgSearch search, cgCacheWrap caches, int excludeDisabled, int excludeMine, String cacheType) { + List<cgCache> cacheList = new ArrayList<cgCache>(); + if (caches != null) { + if (StringUtils.isNotBlank(caches.error)) { + search.error = caches.error; + } + if (StringUtils.isNotBlank(caches.url)) { + search.url = caches.url; + } + search.viewstates = caches.viewstates; + search.totalCnt = caches.totalCnt; + + if (CollectionUtils.isNotEmpty(caches.cacheList)) { + for (cgCache cache : caches.cacheList) { + if ((excludeDisabled == 0 || (excludeDisabled == 1 && cache.disabled == false)) + && (excludeMine == 0 || (excludeMine == 1 && cache.own == false)) + && (excludeMine == 0 || (excludeMine == 1 && cache.found == false)) + && (cacheType == null || (cacheType.equals(cache.type)))) { + search.addGeocode(cache.geocode); + cacheList.add(cache); + } + } + } + } + return cacheList; + } + + public cgTrackable searchTrackable(Map<String, String> parameters) { + final String geocode = parameters.get("geocode"); + final String guid = parameters.get("guid"); + final String id = parameters.get("id"); + cgTrackable trackable = new cgTrackable(); + + if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid) && StringUtils.isBlank(id)) { + Log.e(cgSettings.tag, "cgeoBase.searchTrackable: No geocode nor guid nor id given"); + return null; + } + + final String host = "www.geocaching.com"; + final String path = "/track/details.aspx"; + final String method = "GET"; + final Map<String, String> params = new HashMap<String, String>(); + if (StringUtils.isNotBlank(geocode)) { + params.put("tracker", geocode); + } else if (StringUtils.isNotBlank(guid)) { + params.put("guid", guid); + } else if (StringUtils.isNotBlank(id)) { + params.put("id", id); + } + + String page = requestLogged(false, host, path, method, params, false, false, false); + + if (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgeoBase.searchTrackable: No data from server"); + return trackable; + } + + trackable = parseTrackable(page); + if (trackable == null) { + Log.e(cgSettings.tag, "cgeoBase.searchTrackable: No trackable parsed"); + return trackable; + } + + return trackable; + } + + public int postLog(cgeoapplication app, String geocode, String cacheid, String[] viewstates, + int logType, int year, int month, int day, String log, List<cgTrackableLog> trackables) { + if (isEmpty(viewstates)) { + Log.e(cgSettings.tag, "cgeoBase.postLog: No viewstate given"); + return 1000; + } + + if (logTypes2.containsKey(logType) == false) { + Log.e(cgSettings.tag, "cgeoBase.postLog: Unknown logtype"); + return 1000; + } + + if (StringUtils.isBlank(log)) { + Log.e(cgSettings.tag, "cgeoBase.postLog: No log text given"); + return 1001; + } + + // fix log (non-Latin characters converted to HTML entities) + final int logLen = log.length(); + final StringBuilder logUpdated = new StringBuilder(); + + for (int i = 0; i < logLen; i++) { + char c = log.charAt(i); + + if (c > 300) { + logUpdated.append("&#"); + logUpdated.append(Integer.toString((int) c)); + logUpdated.append(';'); + } else { + logUpdated.append(c); + } + } + log = logUpdated.toString(); + + log = log.replace("\n", "\r\n"); // windows' eol + + if (trackables != null) { + Log.i(cgSettings.tag, "Trying to post log for cache #" + cacheid + " - action: " + logType + "; date: " + year + "." + month + "." + day + ", log: " + log + "; trackables: " + trackables.size()); + } else { + Log.i(cgSettings.tag, "Trying to post log for cache #" + cacheid + " - action: " + logType + "; date: " + year + "." + month + "." + day + ", log: " + log + "; trackables: 0"); + } + + final String host = "www.geocaching.com"; + final String path = "/seek/log.aspx?ID=" + cacheid; + final String method = "POST"; + final Map<String, String> params = new HashMap<String, String>(); + + setViewstates(viewstates, params); + params.put("__EVENTTARGET", ""); + params.put("__EVENTARGUMENT", ""); + params.put("__LASTFOCUS", ""); + params.put("ctl00$ContentBody$LogBookPanel1$ddLogType", Integer.toString(logType)); + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", String.format("%02d", month) + "/" + String.format("%02d", day) + "/" + String.format("%04d", year)); + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Month", Integer.toString(month)); + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Day", Integer.toString(day)); + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Year", Integer.toString(year)); + params.put("ctl00$ContentBody$LogBookPanel1$uxLogInfo", log); + params.put("ctl00$ContentBody$LogBookPanel1$LogButton", "Submit Log Entry"); + params.put("ctl00$ContentBody$uxVistOtherListingGC", ""); + if (trackables != null && trackables.isEmpty() == false) { // we have some trackables to proceed + final StringBuilder hdnSelected = new StringBuilder(); + + for (cgTrackableLog tb : trackables) { + final String action = Integer.toString(tb.id) + logTypesTrackableAction.get(tb.action); + + if (tb.action > 0) { + hdnSelected.append(action); + hdnSelected.append(','); + } + } + + params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnSelectedActions", hdnSelected.toString()); // selected trackables + params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnCurrentFilter", ""); + } + + String page = request(false, host, path, method, params, false, false, false).getData(); + if (checkLogin(page) == false) { + int loginState = login(); + if (loginState == 1) { + page = request(false, host, path, method, params, false, false, false).getData(); + } else { + Log.e(cgSettings.tag, "cgeoBase.postLog: Can not log in geocaching (error: " + loginState + ")"); + return loginState; + } + } + + if (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgeoBase.postLog: No data from server"); + return 1002; + } + + // maintenance, archived needs to be confirmed + final Pattern pattern = Pattern.compile("<span id=\"ctl00_ContentBody_LogBookPanel1_lbConfirm\"[^>]*>([^<]*<font[^>]*>)?([^<]+)(</font>[^<]*)?</span>", Pattern.CASE_INSENSITIVE); + final Matcher matcher = pattern.matcher(page); + + try { + if (matcher.find() && matcher.groupCount() > 0) { + final String[] viewstatesConfirm = getViewstates(page); + + if (isEmpty(viewstatesConfirm)) { + Log.e(cgSettings.tag, "cgeoBase.postLog: No viewstate for confirm log"); + return 1000; + } + + params.clear(); + setViewstates(viewstatesConfirm, params); + params.put("__EVENTTARGET", ""); + params.put("__EVENTARGUMENT", ""); + params.put("__LASTFOCUS", ""); + params.put("ctl00$ContentBody$LogBookPanel1$btnConfirm", "Yes"); + params.put("ctl00$ContentBody$LogBookPanel1$uxLogInfo", log); + params.put("ctl00$ContentBody$uxVistOtherListingGC", ""); + if (trackables != null && trackables.isEmpty() == false) { // we have some trackables to proceed + final StringBuilder hdnSelected = new StringBuilder(); + + for (cgTrackableLog tb : trackables) { + String ctl = null; + final String action = Integer.toString(tb.id) + logTypesTrackableAction.get(tb.action); + + if (tb.ctl < 10) { + ctl = "0" + Integer.toString(tb.ctl); + } else { + ctl = Integer.toString(tb.ctl); + } + + params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$repTravelBugs$ctl" + ctl + "$ddlAction", action); + if (tb.action > 0) { + hdnSelected.append(action); + hdnSelected.append(','); + } + } + + params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnSelectedActions", hdnSelected.toString()); // selected trackables + params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnCurrentFilter", ""); + } + + page = request(false, host, path, method, params, false, false, false).getData(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.postLog.confim: " + e.toString()); + } + + try { + final Pattern patternOk = Pattern.compile("<h2[^>]*>[^<]*<span id=\"ctl00_ContentBody_lbHeading\"[^>]*>[^<]*</span>[^<]*</h2>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Matcher matcherOk = patternOk.matcher(page); + if (matcherOk.find()) { + Log.i(cgSettings.tag, "Log successfully posted to cache #" + cacheid); + + if (app != null && geocode != null) { + app.saveVisitDate(geocode); + } + + return 1; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.postLog.check: " + e.toString()); + } + + Log.e(cgSettings.tag, "cgeoBase.postLog: Failed to post log because of unknown error"); + return 1000; + } + + public int postLogTrackable(String tbid, String trackingCode, String[] viewstates, + int logType, int year, int month, int day, String log) { + if (isEmpty(viewstates)) { + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: No viewstate given"); + return 1000; + } + + if (logTypes2.containsKey(logType) == false) { + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: Unknown logtype"); + return 1000; + } + + if (StringUtils.isBlank(log)) { + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: No log text given"); + return 1001; + } + + Log.i(cgSettings.tag, "Trying to post log for trackable #" + trackingCode + " - action: " + logType + "; date: " + year + "." + month + "." + day + ", log: " + log); + + log = log.replace("\n", "\r\n"); // windows' eol + + final Calendar currentDate = Calendar.getInstance(); + final String host = "www.geocaching.com"; + final String path = "/track/log.aspx?wid=" + tbid; + final String method = "POST"; + final Map<String, String> params = new HashMap<String, String>(); + + setViewstates(viewstates, params); + params.put("__EVENTTARGET", ""); + params.put("__EVENTARGUMENT", ""); + params.put("__LASTFOCUS", ""); + params.put("ctl00$ContentBody$LogBookPanel1$ddLogType", Integer.toString(logType)); + params.put("ctl00$ContentBody$LogBookPanel1$tbCode", trackingCode); + if (currentDate.get(Calendar.YEAR) == year && (currentDate.get(Calendar.MONTH) + 1) == month && currentDate.get(Calendar.DATE) == day) { + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", ""); + } else { + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", Integer.toString(month) + "/" + Integer.toString(day) + "/" + Integer.toString(year)); + } + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Day", Integer.toString(day)); + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Month", Integer.toString(month)); + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Year", Integer.toString(year)); + params.put("ctl00$ContentBody$LogBookPanel1$uxLogInfo", log); + params.put("ctl00$ContentBody$LogBookPanel1$LogButton", "Submit Log Entry"); + params.put("ctl00$ContentBody$uxVistOtherListingGC", ""); + + String page = request(false, host, path, method, params, false, false, false).getData(); + if (checkLogin(page) == false) { + int loginState = login(); + if (loginState == 1) { + page = request(false, host, path, method, params, false, false, false).getData(); + } else { + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: Can not log in geocaching (error: " + loginState + ")"); + return loginState; + } + } + + if (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: No data from server"); + return 1002; + } + + try { + final Pattern patternOk = Pattern.compile("<div id=[\"|']ctl00_ContentBody_LogBookPanel1_ViewLogPanel[\"|']>", Pattern.CASE_INSENSITIVE); + final Matcher matcherOk = patternOk.matcher(page); + if (matcherOk.find()) { + Log.i(cgSettings.tag, "Log successfully posted to trackable #" + trackingCode); + return 1; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable.check: " + e.toString()); + } + + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: Failed to post log because of unknown error"); + return 1000; + } /** * Adds the cache to the watchlist of the user. - * - * @param cache the cache to add - * @return -1: error occured + * + * @param cache + * the cache to add + * @return -1: error occured */ - public int addToWatchlist(cgCache cache) { + public int addToWatchlist(cgCache cache) { String page = requestLogged(false, "www.geocaching.com", "/my/watchlist.aspx?w=" + cache.cacheId, "POST", null, false, false, false); if (StringUtils.isBlank(page)) { Log.e(cgSettings.tag, "cgBase.addToWatchlist: No data from server"); - return -1; // error + return -1; // error } boolean guidOnPage = cache.isGuidContainedInPage(page); @@ -3470,1524 +3468,1538 @@ public class cgBase { Log.e(cgSettings.tag, "cgBase.addToWatchlist: cache is not on watchlist"); } return guidOnPage ? 1 : -1; // on watchlist (=added) / else: error - } - - /** - * Removes the cache from the watchlist - * - * @param cache the cache to remove - * @return -1: error occured - */ - public int removeFromWatchlist(cgCache cache) { - String host = "www.geocaching.com"; - String path = "/my/watchlist.aspx?ds=1&action=rem&id=" + cache.cacheId; - String method = "POST"; - - String page = requestLogged(false, host, path, method, null, false, false, false); - - if (StringUtils.isBlank(page)) { - Log.e(cgSettings.tag, "cgBase.removeFromWatchlist: No data from server"); - return -1; // error - } - - // removing cache from list needs approval by hitting "Yes" button - final Map<String, String> params = new HashMap<String, String>(); - transferViewstates(page, params); - params.put("__EVENTTARGET", ""); - params.put("__EVENTARGUMENT", ""); - params.put("ctl00$ContentBody$btnYes", "Yes"); - - page = request(false, host, path, method, params, false, false, false).getData(); - boolean guidOnPage = cache.isGuidContainedInPage(page); - if (! guidOnPage) { - Log.i(cgSettings.tag, "cgBase.removeFromWatchlist: cache removed from watchlist"); - cache.onWatchlist = false; - } else { - Log.e(cgSettings.tag, "cgBase.removeFromWatchlist: cache not removed from watchlist"); - } - return guidOnPage ? -1 : 0; // on watchlist (=error) / not on watchlist - } - - final public static HostnameVerifier doNotVerify = new HostnameVerifier() { - - public boolean verify(String hostname, SSLSession session) { - return true; - } - }; - - public static void trustAllHosts() { - TrustManager[] trustAllCerts = new TrustManager[]{ - new X509TrustManager() { - - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return new java.security.cert.X509Certificate[]{}; - } - - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - } - - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - } - } - }; - - try { - SSLContext sc = SSLContext.getInstance("TLS"); - sc.init(null, trustAllCerts, new java.security.SecureRandom()); - HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); - } catch (Exception e) { - Log.e(cgSettings.tag, "cgBase.trustAllHosts: " + e.toString()); - } - } - - public static void postTweetCache(cgeoapplication app, cgSettings settings, String geocode) { - final cgCache cache = app.getCacheByGeocode(geocode); - String name = cache.name; - if (name.length() > 84) { - name = name.substring(0, 81) + "..."; - } - final String status = "I found " + name + " (http://coord.info/" + cache.geocode.toUpperCase() + ")! #cgeo #geocaching"; // 56 chars + cache name - - postTweet(app, settings, status, null); - } - - public static void postTweetTrackable(cgeoapplication app, cgSettings settings, String geocode) { - final cgTrackable trackable = app.getTrackableByGeocode(geocode); - String name = trackable.name; - if (name.length() > 82) { - name = name.substring(0, 79) + "..."; - } - final String status = "I touched " + name + " (http://coord.info/" + trackable.geocode.toUpperCase() + ")! #cgeo #geocaching"; // 58 chars + trackable name - - postTweet(app, settings, status, null); - } - - public static void postTweet(cgeoapplication app, cgSettings settings, String status, final Geopoint coords) { - if (app == null) { - return; - } - if (settings == null || StringUtils.isBlank(settings.tokenPublic) || StringUtils.isBlank(settings.tokenSecret)) { - return; - } - - try { - Map<String, String> parameters = new HashMap<String, String>(); - - parameters.put("status", status); - if (coords != null) { - parameters.put("lat", String.format("%.6f", coords.getLatitude())); - parameters.put("long", String.format("%.6f", coords.getLongitude())); - parameters.put("display_coordinates", "true"); - } - - final String paramsDone = cgOAuth.signOAuth("api.twitter.com", "/1/statuses/update.json", "POST", false, parameters, settings.tokenPublic, settings.tokenSecret); - - HttpURLConnection connection = null; - try { - final StringBuffer buffer = new StringBuffer(); - final URL u = new URL("http://api.twitter.com/1/statuses/update.json"); - final URLConnection uc = u.openConnection(); - - uc.setRequestProperty("Host", "api.twitter.com"); - - connection = (HttpURLConnection) uc; - connection.setReadTimeout(30000); - connection.setRequestMethod("POST"); - HttpURLConnection.setFollowRedirects(true); - connection.setDoInput(true); - connection.setDoOutput(true); - - final OutputStream out = connection.getOutputStream(); - final OutputStreamWriter wr = new OutputStreamWriter(out); - wr.write(paramsDone); - wr.flush(); - wr.close(); - - Log.i(cgSettings.tag, "Twitter.com: " + connection.getResponseCode() + " " + connection.getResponseMessage()); - - InputStream ins; - final String encoding = connection.getContentEncoding(); - - if (encoding != null && encoding.equalsIgnoreCase("gzip")) { - ins = new GZIPInputStream(connection.getInputStream()); - } else if (encoding != null && encoding.equalsIgnoreCase("deflate")) { - ins = new InflaterInputStream(connection.getInputStream(), new Inflater(true)); - } else { - ins = connection.getInputStream(); - } - - final InputStreamReader inr = new InputStreamReader(ins); - final BufferedReader br = new BufferedReader(inr); - - readIntoBuffer(br, buffer); - - br.close(); - ins.close(); - inr.close(); - connection.disconnect(); - } catch (IOException e) { - Log.e(cgSettings.tag, "cgBase.postTweet.IO: " + connection.getResponseCode() + ": " + connection.getResponseMessage() + " ~ " + e.toString()); - - final InputStream ins = connection.getErrorStream(); - final StringBuffer buffer = new StringBuffer(); - final InputStreamReader inr = new InputStreamReader(ins); - final BufferedReader br = new BufferedReader(inr); - - readIntoBuffer(br, buffer); - - br.close(); - ins.close(); - inr.close(); - } catch (Exception e) { - Log.e(cgSettings.tag, "cgBase.postTweet.inner: " + e.toString()); - } - - connection.disconnect(); - } catch (Exception e) { - Log.e(cgSettings.tag, "cgBase.postTweet: " + e.toString()); - } - } - - private static void readIntoBuffer(BufferedReader br, StringBuffer buffer) throws IOException { - int bufferSize = 1024*16; - char[] bytes = new char[bufferSize]; - int bytesRead; - while ((bytesRead = br.read(bytes)) > 0) { - if (bytesRead == bufferSize) { - buffer.append(bytes); - } - else { - buffer.append(bytes, 0, bytesRead); - } - } - } - - public static String getLocalIpAddress() { - try { - for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { - NetworkInterface intf = en.nextElement(); - for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) { - InetAddress inetAddress = enumIpAddr.nextElement(); - if (!inetAddress.isLoopbackAddress()) { - return inetAddress.getHostAddress(); - } - } - } - } catch (SocketException e) { - // nothing - } - - return null; - } - - public static String implode(String delim, Object[] array) { - StringBuilder out = new StringBuilder(); - - try { - for (int i = 0; i < array.length; i++) { - if (i != 0) { - out.append(delim); - } - out.append(array[i].toString()); - } - } catch (Exception e) { - Log.e(cgSettings.tag, "cgeoBase.implode: " + e.toString()); - } - return out.toString(); - } - - public static String urlencode_rfc3986(String text) { - final String encoded = URLEncoder.encode(text).replace("+", "%20").replaceAll("%7E", "~"); - - return encoded; - } - - public String prepareParameters(Map<String, String> params, boolean my, boolean addF) { - String paramsDone = null; - - if (my != true && settings.excludeMine > 0) { - if (params == null) { - params = new HashMap<String, String>(); - } - if (addF) { - params.put("f", "1"); - } - - Log.i(cgSettings.tag, "Skipping caches found or hidden by user."); - } - - if (params != null) { - Set<Map.Entry<String, String>> entrySet = params.entrySet(); - List<String> paramsEncoded = new ArrayList<String>(); - - for(Map.Entry<String, String> entry : entrySet) - { - String key = entry.getKey(); - String value = entry.getValue(); - - if (key.charAt(0) == '^') { - key = ""; - } - if (value == null) { - value = ""; - } - - paramsEncoded.add(key + "=" + urlencode_rfc3986(value)); - } - - paramsDone = implode("&", paramsEncoded.toArray()); - } else { - paramsDone = ""; - } - - return paramsDone; - } - - public String[] requestViewstates(boolean secure, String host, String path, String method, Map<String, String> params, boolean xContentType, boolean my) { - final cgResponse response = request(secure, host, path, method, params, xContentType, my, false); - - return getViewstates(response.getData()); - } - - public String requestLogged(boolean secure, String host, String path, String method, Map<String, String> params, boolean xContentType, boolean my, boolean addF) { - cgResponse response = request(secure, host, path, method, params, xContentType, my, addF); - String data = response.getData(); - - if (checkLogin(data) == false) { - int loginState = login(); - if (loginState == 1) { - response = request(secure, host, path, method, params, xContentType, my, addF); - data = response.getData(); - } else { - Log.i(cgSettings.tag, "Working as guest."); - } - } - - return data; - } - - public cgResponse request(boolean secure, String host, String path, String method, Map<String, String> params, boolean xContentType, boolean my, boolean addF) { - // prepare parameters - final String paramsDone = prepareParameters(params, my, addF); - - return request(secure, host, path, method, paramsDone, 0, xContentType); - } - - public cgResponse request(boolean secure, String host, String path, String method, Map<String, String> params, int requestId, boolean xContentType, boolean my, boolean addF) { - // prepare parameters - final String paramsDone = prepareParameters(params, my, addF); - - return request(secure, host, path, method, paramsDone, requestId, xContentType); - } - - public cgResponse request(boolean secure, String host, String path, String method, String params, int requestId, Boolean xContentType) { - URL u = null; - int httpCode = -1; - String httpMessage = null; - String httpLocation = null; - - if (requestId == 0) { - requestId = (int) (Math.random() * 1000); - } - - if (method == null || (method.equalsIgnoreCase("GET") == false && method.equalsIgnoreCase("POST") == false)) { - method = "POST"; - } else { - method = method.toUpperCase(); - } - - // https - String scheme = "http://"; - if (secure) { - scheme = "https://"; - } - - String cookiesDone = getCookiesAsString(); - - URLConnection uc = null; - HttpURLConnection connection = null; - Integer timeout = 30000; - StringBuffer buffer = null; - - for (int i = 0; i < 5; i++) { - if (i > 0) { - Log.w(cgSettings.tag, "Failed to download data, retrying. Attempt #" + (i + 1)); - } - - buffer = new StringBuffer(); - timeout = 30000 + (i * 10000); - - try { - if (method.equals("GET")) { - // GET - u = new URL(scheme + host + path + "?" + params); - uc = u.openConnection(); - - uc.setRequestProperty("Host", host); - uc.setRequestProperty("Cookie", cookiesDone); - if (xContentType) { - uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - } - - if (settings.asBrowser == 1) { - uc.setRequestProperty("Accept", "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"); - // uc.setRequestProperty("Accept-Encoding", "gzip"); // not supported via cellular network - uc.setRequestProperty("Accept-Charset", "utf-8, iso-8859-1, utf-16, *;q=0.7"); - uc.setRequestProperty("Accept-Language", "en-US"); - uc.setRequestProperty("User-Agent", idBrowser); - uc.setRequestProperty("Connection", "keep-alive"); - uc.setRequestProperty("Keep-Alive", "300"); - } - - connection = (HttpURLConnection) uc; - connection.setReadTimeout(timeout); - connection.setRequestMethod(method); - HttpURLConnection.setFollowRedirects(false); - connection.setDoInput(true); - connection.setDoOutput(false); - } else { - // POST - u = new URL(scheme + host + path); - uc = u.openConnection(); - - uc.setRequestProperty("Host", host); - uc.setRequestProperty("Cookie", cookiesDone); - if (xContentType) { - uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - } - - if (settings.asBrowser == 1) { - uc.setRequestProperty("Accept", "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"); - // uc.setRequestProperty("Accept-Encoding", "gzip"); // not supported via cellular network - uc.setRequestProperty("Accept-Charset", "utf-8, iso-8859-1, utf-16, *;q=0.7"); - uc.setRequestProperty("Accept-Language", "en-US"); - uc.setRequestProperty("User-Agent", idBrowser); - uc.setRequestProperty("Connection", "keep-alive"); - uc.setRequestProperty("Keep-Alive", "300"); - } - - connection = (HttpURLConnection) uc; - connection.setReadTimeout(timeout); - connection.setRequestMethod(method); - HttpURLConnection.setFollowRedirects(false); - connection.setDoInput(true); - connection.setDoOutput(true); - - final OutputStream out = connection.getOutputStream(); - final OutputStreamWriter wr = new OutputStreamWriter(out); - wr.write(params); - wr.flush(); - wr.close(); - } - - String headerName = null; - final SharedPreferences.Editor prefsEditor = prefs.edit(); - for (int j = 1; (headerName = uc.getHeaderFieldKey(j)) != null; j++) { - if (headerName != null && headerName.equalsIgnoreCase("Set-Cookie")) { - int index; - String cookie = uc.getHeaderField(j); - - index = cookie.indexOf(";"); - if (index > -1) { - cookie = cookie.substring(0, cookie.indexOf(";")); - } - - index = cookie.indexOf("="); - if (index > - 1 && cookie.length() > (index + 1)) { - String name = cookie.substring(0, cookie.indexOf("=")); - String value = cookie.substring(cookie.indexOf("=") + 1, cookie.length()); - - cookies.put(name, value); - prefsEditor.putString("cookie_" + name, value); - } - } - } - prefsEditor.commit(); - - InputStream ins = getInputstreamFromConnection(connection); - final InputStreamReader inr = new InputStreamReader(ins); - final BufferedReader br = new BufferedReader(inr, 16 * 1024); - - readIntoBuffer(br, buffer); - - httpCode = connection.getResponseCode(); - httpMessage = connection.getResponseMessage(); - httpLocation = uc.getHeaderField("Location"); - - final String paramsLog = params.replaceAll(passMatch, "password=***"); - Log.i(cgSettings.tag + "|" + requestId, "[" + method + " " + (int)(params.length() / 1024) + "k | " + httpCode + " | " + (int)(buffer.length() / 1024) + "k] Downloaded " + scheme + host + path + "?" + paramsLog); - - connection.disconnect(); - br.close(); - ins.close(); - inr.close(); - } catch (IOException e) { - Log.e(cgSettings.tag, "cgeoBase.request.IOException: " + e.toString()); - } catch (Exception e) { - Log.e(cgSettings.tag, "cgeoBase.request: " + e.toString()); - } - - if (buffer.length() > 0) { - break; - } - } - - cgResponse response = new cgResponse(); - - try { - if (httpCode == 302 && httpLocation != null) { - final Uri newLocation = Uri.parse(httpLocation); - if (newLocation.isRelative()) { - response = request(secure, host, path, "GET", new HashMap<String, String>(), requestId, false, false, false); - } else { - boolean secureRedir = false; - if (newLocation.getScheme().equals("https")) { - secureRedir = true; - } - response = request(secureRedir, newLocation.getHost(), newLocation.getPath(), "GET", new HashMap<String, String>(), requestId, false, false, false); - } - } else { - if (StringUtils.isNotEmpty(buffer)) { - replaceWhitespace(buffer); - String data = buffer.toString(); - buffer = null; - - if (data != null) { - response.setData(data); - } else { - response.setData(""); - } - response.setStatusCode(httpCode); - response.setStatusMessage(httpMessage); - response.setUrl(u.toString()); - } - } - } catch (Exception e) { - Log.e(cgSettings.tag, "cgeoBase.page: " + e.toString()); - } - - return response; - } - - private String getCookiesAsString() { - // prepare cookies - String cookiesDone = null; - if (cookies == null || cookies.isEmpty()) { - if (cookies == null) { - cookies = new HashMap<String, String>(); - } - - final Map<String, ?> prefsAll = prefs.getAll(); - final Set<? extends Map.Entry<String, ?>> entrySet = prefsAll.entrySet(); - - for(Map.Entry<String, ?> entry : entrySet){ - String key = entry.getKey(); - if (key.matches("cookie_.+")) { - final String cookieKey = key.substring(7); - final String cookieValue = (String) entry.getValue(); - - cookies.put(cookieKey, cookieValue); - } - } - } - - if (cookies != null) { - final Set<Map.Entry<String, String>> entrySet = cookies.entrySet(); - final List<String> cookiesEncoded = new ArrayList<String>(); - - for(Map.Entry<String, String> entry : entrySet){ - cookiesEncoded.add(entry.getKey() + "=" + entry.getValue()); - } - - if (cookiesEncoded.size() > 0) { - cookiesDone = implode("; ", cookiesEncoded.toArray()); - } - } - - if (cookiesDone == null) { - Map<String, ?> prefsValues = prefs.getAll(); - - if (CollectionUtils.isNotEmpty(prefsValues)) { - final Set<? extends Map.Entry<String, ?>> entrySet = prefsValues.entrySet(); - final List<String> cookiesEncoded = new ArrayList<String>(); - - for(Map.Entry<String, ?> entry : entrySet){ - String key = entry.getKey(); - if (key.length() > 7 && key.substring(0, 7).equals("cookie_")) { - cookiesEncoded.add(key + "=" + entry.getValue()); - } - } - - if (cookiesEncoded.size() > 0) { - cookiesDone = implode("; ", cookiesEncoded.toArray()); - } - } - } - - if (cookiesDone == null) { - cookiesDone = ""; - } - return cookiesDone; - } - - /** - * Replace the characters \n, \r and \t with a space - * @param buffer The data - */ - public static void replaceWhitespace(final StringBuffer buffer) { - final int length = buffer.length(); - final char[] chars = new char[length]; - buffer.getChars(0, length, chars, 0); - int resultSize = 0; - boolean lastWasWhitespace = false; - for (char c : chars) { - if (c == ' ' || c == '\n' || c == '\r' || c == '\t') { - if (!lastWasWhitespace) { - chars[resultSize++] =' '; - } - lastWasWhitespace = true; - } else { - chars[resultSize++] = c; - lastWasWhitespace = false; - } - } - buffer.setLength(0); - buffer.append(chars); - } - - public String requestJSONgc(String host, String path, String params) { - int httpCode = -1; - String httpLocation = null; - - String cookiesDone = getCookiesAsString(); - - URLConnection uc = null; - HttpURLConnection connection = null; - Integer timeout = 30000; - final StringBuffer buffer = new StringBuffer(); - - for (int i = 0; i < 3; i++) { - if (i > 0) { - Log.w(cgSettings.tag, "Failed to download data, retrying. Attempt #" + (i + 1)); - } - - buffer.delete(0, buffer.length()); - timeout = 30000 + (i * 15000); - - try { - // POST - final URL u = new URL("http://" + host + path); - uc = u.openConnection(); - - uc.setRequestProperty("Host", host); - uc.setRequestProperty("Cookie", cookiesDone); - uc.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); - uc.setRequestProperty("X-Requested-With", "XMLHttpRequest"); - uc.setRequestProperty("Accept", "application/json, text/javascript, */*; q=0.01"); - uc.setRequestProperty("Referer", host + "/" + path); - - if (settings.asBrowser == 1) { - uc.setRequestProperty("Accept-Charset", "utf-8, iso-8859-1, utf-16, *;q=0.7"); - uc.setRequestProperty("Accept-Language", "en-US"); - uc.setRequestProperty("User-Agent", idBrowser); - uc.setRequestProperty("Connection", "keep-alive"); - uc.setRequestProperty("Keep-Alive", "300"); - } - - connection = (HttpURLConnection) uc; - connection.setReadTimeout(timeout); - connection.setRequestMethod("POST"); - HttpURLConnection.setFollowRedirects(false); // TODO: Fix these (FilCab) - connection.setDoInput(true); - connection.setDoOutput(true); - - final OutputStream out = connection.getOutputStream(); - final OutputStreamWriter wr = new OutputStreamWriter(out); - wr.write(params); - wr.flush(); - wr.close(); - - String headerName = null; - final SharedPreferences.Editor prefsEditor = prefs.edit(); - for (int j = 1; (headerName = uc.getHeaderFieldKey(j)) != null; j++) { - if (headerName != null && headerName.equalsIgnoreCase("Set-Cookie")) { - int index; - String cookie = uc.getHeaderField(j); - - index = cookie.indexOf(";"); - if (index > -1) { - cookie = cookie.substring(0, cookie.indexOf(";")); - } - - index = cookie.indexOf("="); - if (index > - 1 && cookie.length() > (index + 1)) { - String name = cookie.substring(0, cookie.indexOf("=")); - String value = cookie.substring(cookie.indexOf("=") + 1, cookie.length()); - - cookies.put(name, value); - prefsEditor.putString("cookie_" + name, value); - } - } - } - prefsEditor.commit(); - - InputStream ins = getInputstreamFromConnection(connection); - final InputStreamReader inr = new InputStreamReader(ins); - final BufferedReader br = new BufferedReader(inr); - - readIntoBuffer(br, buffer); - - httpCode = connection.getResponseCode(); - httpLocation = uc.getHeaderField("Location"); - - final String paramsLog = params.replaceAll(passMatch, "password=***"); - Log.i(cgSettings.tag + " | JSON", "[POST " + (int)(params.length() / 1024) + "k | " + httpCode + " | " + (int)(buffer.length() / 1024) + "k] Downloaded " + "http://" + host + path + "?" + paramsLog); - - connection.disconnect(); - br.close(); - ins.close(); - inr.close(); - } catch (IOException e) { - Log.e(cgSettings.tag, "cgeoBase.requestJSONgc.IOException: " + e.toString()); - } catch (Exception e) { - Log.e(cgSettings.tag, "cgeoBase.requestJSONgc: " + e.toString()); - } - - if (buffer != null && buffer.length() > 0) { - break; - } - } - - String page = null; - if (httpCode == 302 && httpLocation != null) { - final Uri newLocation = Uri.parse(httpLocation); - if (newLocation.isRelative()) { - page = requestJSONgc(host, path, params); - } else { - page = requestJSONgc(newLocation.getHost(), newLocation.getPath(), params); - } - } else { - replaceWhitespace(buffer); - page = buffer.toString(); - } - - if (page != null) { - return page; - } else { - return ""; - } - } - - private static InputStream getInputstreamFromConnection(HttpURLConnection connection) throws IOException { - final String encoding = connection.getContentEncoding(); - InputStream ins; - - if (encoding != null && encoding.equalsIgnoreCase("gzip")) { - ins = new GZIPInputStream(connection.getInputStream()); - } else if (encoding != null && encoding.equalsIgnoreCase("deflate")) { - ins = new InflaterInputStream(connection.getInputStream(), new Inflater(true)); - } else { - ins = connection.getInputStream(); - } - return ins; - } - - public static String requestJSON(String host, String path, String params) { - return requestJSON("http://", host, path, "GET", params); - } - - public static String requestJSON(String scheme, String host, String path, String method, String params) { - int httpCode = -1; - //String httpLocation = null; - - if (method == null) { - method = "GET"; - } else { - method = method.toUpperCase(); - } - - boolean methodPost = false; - if (method.equalsIgnoreCase("POST")) { - methodPost = true; - } - - URLConnection uc = null; - HttpURLConnection connection = null; - Integer timeout = 30000; - final StringBuffer buffer = new StringBuffer(); - - for (int i = 0; i < 3; i++) { - if (i > 0) { - Log.w(cgSettings.tag, "Failed to download data, retrying. Attempt #" + (i + 1)); - } - - buffer.delete(0, buffer.length()); - timeout = 30000 + (i * 15000); - - try { - try { - URL u = null; - if (methodPost) { - u = new URL(scheme + host + path); - } else { - u = new URL(scheme + host + path + "?" + params); - } - - if (u.getProtocol().toLowerCase().equals("https")) { - trustAllHosts(); - HttpsURLConnection https = (HttpsURLConnection) u.openConnection(); - https.setHostnameVerifier(doNotVerify); - uc = https; - } else { - uc = (HttpURLConnection) u.openConnection(); - } - - uc.setRequestProperty("Host", host); - uc.setRequestProperty("Accept", "application/json, text/javascript, */*; q=0.01"); - if (methodPost) { - uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - uc.setRequestProperty("Content-Length", Integer.toString(params.length())); - uc.setRequestProperty("X-HTTP-Method-Override", "GET"); - } else { - uc.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); - } - uc.setRequestProperty("X-Requested-With", "XMLHttpRequest"); - - connection = (HttpURLConnection) uc; - connection.setReadTimeout(timeout); - connection.setRequestMethod(method); - HttpURLConnection.setFollowRedirects(false); // TODO: Fix these (FilCab) - connection.setDoInput(true); - if (methodPost) { - connection.setDoOutput(true); - - final OutputStream out = connection.getOutputStream(); - final OutputStreamWriter wr = new OutputStreamWriter(out); - wr.write(params); - wr.flush(); - wr.close(); - } else { - connection.setDoOutput(false); - } - - - InputStream ins = getInputstreamFromConnection(connection); - final InputStreamReader inr = new InputStreamReader(ins); - final BufferedReader br = new BufferedReader(inr, 1024); - - readIntoBuffer(br, buffer); - - httpCode = connection.getResponseCode(); - - final String paramsLog = params.replaceAll(passMatch, "password=***"); - Log.i(cgSettings.tag + " | JSON", "[POST " + (int)(params.length() / 1024) + "k | " + httpCode + " | " + (int)(buffer.length() / 1024) + "k] Downloaded " + "http://" + host + path + "?" + paramsLog); - - connection.disconnect(); - br.close(); - ins.close(); - inr.close(); - } catch (IOException e) { - httpCode = connection.getResponseCode(); - - Log.e(cgSettings.tag, "cgeoBase.requestJSON.IOException: " + httpCode + ": " + connection.getResponseMessage() + " ~ " + e.toString()); - } - } catch (Exception e) { - Log.e(cgSettings.tag, "cgeoBase.requestJSON: " + e.toString()); - } - - if (StringUtils.isNotBlank(buffer)) { - break; - } - - if (httpCode == 403) { - // we're not allowed to download content, so let's move - break; - } - } - - String page = null; - //This is reported as beeing deadCode (httpLocation is always null) - //2011-08-09 - 302 is redirect so something should probably be done - /*if (httpCode == 302 && httpLocation != null) { - final Uri newLocation = Uri.parse(httpLocation); - if (newLocation.isRelative()) { - page = requestJSONgc(host, path, params); - } else { - page = requestJSONgc(newLocation.getHost(), newLocation.getPath(), params); - } - } else {*/ - replaceWhitespace(buffer); - page = buffer.toString(); - //} - - if (page != null) { - return page; - } else { - return ""; - } - } - - public static String rot13(String text) { - final StringBuilder result = new StringBuilder(); - // plaintext flag (do not convert) - boolean plaintext = false; - - int length = text.length(); - for (int index = 0; index < length; index++) { - int c = text.charAt(index); - if (c == '[') { - plaintext = true; - } else if (c == ']') { - plaintext = false; - } else if (!plaintext) { - int capitalized = c & 32; - c &= ~capitalized; - c = ((c >= 'A') && (c <= 'Z') ? ((c - 'A' + 13) % 26 + 'A') : c) - | capitalized; - } - result.append((char) c); - } - return result.toString(); - } - - public static String md5(String text) { - String hashed = ""; - - try { - MessageDigest digest = MessageDigest.getInstance("MD5"); - digest.update(text.getBytes(), 0, text.length()); - hashed = new BigInteger(1, digest.digest()).toString(16); - } catch (Exception e) { - Log.e(cgSettings.tag, "cgBase.md5: " + e.toString()); - } - - return hashed; - } - - public static String sha1(String text) { - String hashed = ""; - - try { - MessageDigest digest = MessageDigest.getInstance("SHA-1"); - digest.update(text.getBytes(), 0, text.length()); - hashed = new BigInteger(1, digest.digest()).toString(16); - } catch (Exception e) { - Log.e(cgSettings.tag, "cgBase.sha1: " + e.toString()); - } - - return hashed; - } - - public static byte[] hashHmac(String text, String salt) { - byte[] macBytes = {}; - - try { - SecretKeySpec secretKeySpec = new SecretKeySpec(salt.getBytes(), "HmacSHA1"); - Mac mac = Mac.getInstance("HmacSHA1"); - mac.init(secretKeySpec); - macBytes = mac.doFinal(text.getBytes()); - } catch (Exception e) { - Log.e(cgSettings.tag, "cgBase.hashHmac: " + e.toString()); - } - - return macBytes; - } - - public static boolean deleteDirectory(File path) { - if (path.exists()) { - File[] files = path.listFiles(); - - for (File file : files) { - if (file.isDirectory()) { - deleteDirectory(file); - } else { - file.delete(); - } - } - } - - return (path.delete()); - } - - public void storeCache(cgeoapplication app, Activity activity, cgCache cache, String geocode, int listId, Handler handler) { - try { - // get cache details, they may not yet be complete - if (cache != null) { - // only reload the cache, if it was already stored or has not all details (by checking the description) - if (cache.reason > 0 || StringUtils.isBlank(cache.description)) { - final Map<String, String> params = new HashMap<String, String>(); - params.put("geocode", cache.geocode); - final UUID searchId = searchByGeocode(params, listId, false); - cache = app.getCache(searchId); - } - } else if (StringUtils.isNotBlank(geocode)) { - final Map<String, String> params = new HashMap<String, String>(); - params.put("geocode", geocode); - final UUID searchId = searchByGeocode(params, listId, false); - cache = app.getCache(searchId); - } - - if (cache == null) { - if (handler != null) { - handler.sendMessage(new Message()); - } - - return; - } - - final cgHtmlImg imgGetter = new cgHtmlImg(activity, cache.geocode, false, listId, true); - - // store images from description - if (StringUtils.isNotBlank(cache.description)) { - Html.fromHtml(cache.description, imgGetter, null); - } - - // store spoilers - if (CollectionUtils.isNotEmpty(cache.spoilers)) { - for (cgImage oneSpoiler : cache.spoilers) { - imgGetter.getDrawable(oneSpoiler.url); - } - } - - // store images from logs - if (settings.storelogimages) { - for (cgLog log : cache.logs) { - if (CollectionUtils.isNotEmpty(log.logImages)) { - for (cgImage oneLogImg : log.logImages) { - imgGetter.getDrawable(oneLogImg.url); - } - } - } - } - - // store map previews - StaticMapsProvider.downloadMaps(cache, settings, activity); - - app.markStored(cache.geocode, listId); - app.removeCacheFromCache(cache.geocode); - - if (handler != null) { - handler.sendMessage(new Message()); - } - } catch (Exception e) { - Log.e(cgSettings.tag, "cgBase.storeCache: " + e.toString()); - } - } - - public static void dropCache(cgeoapplication app, Activity activity, cgCache cache, Handler handler) { - try { - app.markDropped(cache.geocode); - app.removeCacheFromCache(cache.geocode); - - handler.sendMessage(new Message()); - } catch (Exception e) { - Log.e(cgSettings.tag, "cgBase.dropCache: " + e.toString()); - } - } - - public static boolean isInViewPort(int centerLat1, int centerLon1, int centerLat2, int centerLon2, int spanLat1, int spanLon1, int spanLat2, int spanLon2) { - try { - // expects coordinates in E6 format - final int left1 = centerLat1 - (spanLat1 / 2); - final int right1 = centerLat1 + (spanLat1 / 2); - final int top1 = centerLon1 + (spanLon1 / 2); - final int bottom1 = centerLon1 - (spanLon1 / 2); - - final int left2 = centerLat2 - (spanLat2 / 2); - final int right2 = centerLat2 + (spanLat2 / 2); - final int top2 = centerLon2 + (spanLon2 / 2); - final int bottom2 = centerLon2 - (spanLon2 / 2); - - if (left2 <= left1) { - return false; - } - if (right2 >= right1) { - return false; - } - if (top2 >= top1) { - return false; - } - if (bottom2 <= bottom1) { - return false; - } - - return true; - } catch (Exception e) { - Log.e(cgSettings.tag, "cgBase.isInViewPort: " + e.toString()); - return false; - } - } - - public static boolean isCacheInViewPort(int centerLat, int centerLon, int spanLat, int spanLon, final Geopoint cacheCoords) { - if (cacheCoords == null) { - return false; - } - - // viewport is defined by center, span and some (10%) reserve on every side - int minLat = centerLat - (spanLat / 2) - (spanLat / 10); - int maxLat = centerLat + (spanLat / 2) + (spanLat / 10); - int minLon = centerLon - (spanLon / 2) - (spanLon / 10); - int maxLon = centerLon + (spanLon / 2) + (spanLon / 10); - final int cLat = cacheCoords.getLatitudeE6(); - final int cLon = cacheCoords.getLongitudeE6(); - int mid = 0; - - if (maxLat < minLat) { - mid = minLat; - minLat = maxLat; - maxLat = mid; - } - - if (maxLon < minLon) { - mid = minLon; - minLon = maxLon; - maxLon = mid; - } - - boolean latOk = false; - boolean lonOk = false; - - if (cLat >= minLat && cLat <= maxLat) { - latOk = true; - } - if (cLon >= minLon && cLon <= maxLon) { - lonOk = true; - } - - if (latOk && lonOk) { - return true; - } else { - return false; - } - } - private static char[] base64map1 = new char[64]; - - static { - int i = 0; - for (char c = 'A'; c <= 'Z'; c++) { - base64map1[i++] = c; - } - for (char c = 'a'; c <= 'z'; c++) { - base64map1[i++] = c; - } - for (char c = '0'; c <= '9'; c++) { - base64map1[i++] = c; - } - base64map1[i++] = '+'; - base64map1[i++] = '/'; - } - private static byte[] base64map2 = new byte[128]; - - static { - for (int i = 0; i < base64map2.length; i++) { - base64map2[i] = -1; - } - for (int i = 0; i < 64; i++) { - base64map2[base64map1[i]] = (byte) i; - } - } - - public static String base64Encode(byte[] in) { - int iLen = in.length; - int oDataLen = (iLen * 4 + 2) / 3; // output length without padding - int oLen = ((iLen + 2) / 3) * 4; // output length including padding - char[] out = new char[oLen]; - int ip = 0; - int op = 0; - - while (ip < iLen) { - int i0 = in[ip++] & 0xff; - int i1 = ip < iLen ? in[ip++] & 0xff : 0; - int i2 = ip < iLen ? in[ip++] & 0xff : 0; - int o0 = i0 >>> 2; - int o1 = ((i0 & 3) << 4) | (i1 >>> 4); - int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); - int o3 = i2 & 0x3F; - out[op++] = base64map1[o0]; - out[op++] = base64map1[o1]; - out[op] = op < oDataLen ? base64map1[o2] : '='; - op++; - out[op] = op < oDataLen ? base64map1[o3] : '='; - op++; - } - - return new String(out); - } - - public static byte[] base64Decode(String text) { - char[] in = text.toCharArray(); - - int iLen = in.length; - if (iLen % 4 != 0) { - throw new IllegalArgumentException("Length of Base64 encoded input string is not a multiple of 4."); - } - while (iLen > 0 && in[iLen - 1] == '=') { - iLen--; - } - int oLen = (iLen * 3) / 4; - byte[] out = new byte[oLen]; - int ip = 0; - int op = 0; - while (ip < iLen) { - int i0 = in[ip++]; - int i1 = in[ip++]; - int i2 = ip < iLen ? in[ip++] : 'A'; - int i3 = ip < iLen ? in[ip++] : 'A'; - if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) { - throw new IllegalArgumentException("Illegal character in Base64 encoded data."); - } - int b0 = base64map2[i0]; - int b1 = base64map2[i1]; - int b2 = base64map2[i2]; - int b3 = base64map2[i3]; - if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) { - throw new IllegalArgumentException("Illegal character in Base64 encoded data."); - } - int o0 = (b0 << 2) | (b1 >>> 4); - int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); - int o2 = ((b2 & 3) << 6) | b3; - out[op++] = (byte) o0; - if (op < oLen) { - out[op++] = (byte) o1; - } - if (op < oLen) { - out[op++] = (byte) o2; - } - } - return out; - } - - public static int getCacheIcon(final String type) { - fillIconsMap(); - Integer iconId = gcIcons.get("type_" + type); - if (iconId != null) { - return iconId; - } - // fallback to traditional if some icon type is not correct - return gcIcons.get("type_traditional"); - } - - public static int getMarkerIcon(final boolean cache, final String type, final boolean own, final boolean found, final boolean disabled) { - fillIconsMap(); - - if (wpIcons.isEmpty()) { - wpIcons.put("waypoint", R.drawable.marker_waypoint_waypoint); - wpIcons.put("flag", R.drawable.marker_waypoint_flag); - wpIcons.put("pkg", R.drawable.marker_waypoint_pkg); - wpIcons.put("puzzle", R.drawable.marker_waypoint_puzzle); - wpIcons.put("stage", R.drawable.marker_waypoint_stage); - wpIcons.put("trailhead", R.drawable.marker_waypoint_trailhead); - } - - int icon = -1; - String iconTxt = null; - - if (cache) { - if (StringUtils.isNotBlank(type)) { - if (own) { - iconTxt = type + "-own"; - } else if (found) { - iconTxt = type + "-found"; - } else if (disabled) { - iconTxt = type + "-disabled"; - } else { - iconTxt = type; - } - } else { - iconTxt = "traditional"; - } - - if (gcIcons.containsKey(iconTxt)) { - icon = gcIcons.get(iconTxt); - } else { - icon = gcIcons.get("traditional"); - } - } else { - if (StringUtils.isNotBlank(type)) { - iconTxt = type; - } else { - iconTxt = "waypoint"; - } - - if (wpIcons.containsKey(iconTxt)) { - icon = wpIcons.get(iconTxt); - } else { - icon = wpIcons.get("waypoint"); - } - } - - return icon; - } - - private static void fillIconsMap() { - if (gcIcons.isEmpty()) { - gcIcons.put("type_ape", R.drawable.type_ape); - gcIcons.put("type_cito", R.drawable.type_cito); - gcIcons.put("type_earth", R.drawable.type_earth); - gcIcons.put("type_event", R.drawable.type_event); - gcIcons.put("type_letterbox", R.drawable.type_letterbox); - gcIcons.put("type_locationless", R.drawable.type_locationless); - gcIcons.put("type_mega", R.drawable.type_mega); - gcIcons.put("type_multi", R.drawable.type_multi); - gcIcons.put("type_traditional", R.drawable.type_traditional); - gcIcons.put("type_virtual", R.drawable.type_virtual); - gcIcons.put("type_webcam", R.drawable.type_webcam); - gcIcons.put("type_wherigo", R.drawable.type_wherigo); - gcIcons.put("type_mystery", R.drawable.type_mystery); - gcIcons.put("type_gchq", R.drawable.type_hq); - // default markers - gcIcons.put("ape", R.drawable.marker_cache_ape); - gcIcons.put("cito", R.drawable.marker_cache_cito); - gcIcons.put("earth", R.drawable.marker_cache_earth); - gcIcons.put("event", R.drawable.marker_cache_event); - gcIcons.put("letterbox", R.drawable.marker_cache_letterbox); - gcIcons.put("locationless", R.drawable.marker_cache_locationless); - gcIcons.put("mega", R.drawable.marker_cache_mega); - gcIcons.put("multi", R.drawable.marker_cache_multi); - gcIcons.put("traditional", R.drawable.marker_cache_traditional); - gcIcons.put("virtual", R.drawable.marker_cache_virtual); - gcIcons.put("webcam", R.drawable.marker_cache_webcam); - gcIcons.put("wherigo", R.drawable.marker_cache_wherigo); - gcIcons.put("mystery", R.drawable.marker_cache_mystery); - gcIcons.put("gchq", R.drawable.marker_cache_gchq); - // own cache markers - gcIcons.put("ape-own", R.drawable.marker_cache_ape_own); - gcIcons.put("cito-own", R.drawable.marker_cache_cito_own); - gcIcons.put("earth-own", R.drawable.marker_cache_earth_own); - gcIcons.put("event-own", R.drawable.marker_cache_event_own); - gcIcons.put("letterbox-own", R.drawable.marker_cache_letterbox_own); - gcIcons.put("locationless-own", R.drawable.marker_cache_locationless_own); - gcIcons.put("mega-own", R.drawable.marker_cache_mega_own); - gcIcons.put("multi-own", R.drawable.marker_cache_multi_own); - gcIcons.put("traditional-own", R.drawable.marker_cache_traditional_own); - gcIcons.put("virtual-own", R.drawable.marker_cache_virtual_own); - gcIcons.put("webcam-own", R.drawable.marker_cache_webcam_own); - gcIcons.put("wherigo-own", R.drawable.marker_cache_wherigo_own); - gcIcons.put("mystery-own", R.drawable.marker_cache_mystery_own); - gcIcons.put("gchq-own", R.drawable.marker_cache_gchq_own); - // found cache markers - gcIcons.put("ape-found", R.drawable.marker_cache_ape_found); - gcIcons.put("cito-found", R.drawable.marker_cache_cito_found); - gcIcons.put("earth-found", R.drawable.marker_cache_earth_found); - gcIcons.put("event-found", R.drawable.marker_cache_event_found); - gcIcons.put("letterbox-found", R.drawable.marker_cache_letterbox_found); - gcIcons.put("locationless-found", R.drawable.marker_cache_locationless_found); - gcIcons.put("mega-found", R.drawable.marker_cache_mega_found); - gcIcons.put("multi-found", R.drawable.marker_cache_multi_found); - gcIcons.put("traditional-found", R.drawable.marker_cache_traditional_found); - gcIcons.put("virtual-found", R.drawable.marker_cache_virtual_found); - gcIcons.put("webcam-found", R.drawable.marker_cache_webcam_found); - gcIcons.put("wherigo-found", R.drawable.marker_cache_wherigo_found); - gcIcons.put("mystery-found", R.drawable.marker_cache_mystery_found); - gcIcons.put("gchq-found", R.drawable.marker_cache_gchq_found); - // disabled cache markers - gcIcons.put("ape-disabled", R.drawable.marker_cache_ape_disabled); - gcIcons.put("cito-disabled", R.drawable.marker_cache_cito_disabled); - gcIcons.put("earth-disabled", R.drawable.marker_cache_earth_disabled); - gcIcons.put("event-disabled", R.drawable.marker_cache_event_disabled); - gcIcons.put("letterbox-disabled", R.drawable.marker_cache_letterbox_disabled); - gcIcons.put("locationless-disabled", R.drawable.marker_cache_locationless_disabled); - gcIcons.put("mega-disabled", R.drawable.marker_cache_mega_disabled); - gcIcons.put("multi-disabled", R.drawable.marker_cache_multi_disabled); - gcIcons.put("traditional-disabled", R.drawable.marker_cache_traditional_disabled); - gcIcons.put("virtual-disabled", R.drawable.marker_cache_virtual_disabled); - gcIcons.put("webcam-disabled", R.drawable.marker_cache_webcam_disabled); - gcIcons.put("wherigo-disabled", R.drawable.marker_cache_wherigo_disabled); - gcIcons.put("mystery-disabled", R.drawable.marker_cache_mystery_disabled); - gcIcons.put("gchq-disabled", R.drawable.marker_cache_gchq_disabled); - } - } - - public static boolean runNavigation(Activity activity, Resources res, cgSettings settings, final Geopoint coords) { - return runNavigation(activity, res, settings, coords, null); - } - - public static boolean runNavigation(Activity activity, Resources res, cgSettings settings, final Geopoint coords, final Geopoint coordsNow) { - if (activity == null) { - return false; - } - if (settings == null) { - return false; - } - - // Google Navigation - if (settings.useGNavigation == 1) { - try { - activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("google.navigation:ll=" + coords.getLatitude() + "," + coords.getLongitude()))); - - return true; - } catch (Exception e) { - // nothing - } - } - - // Google Maps Directions - try { - if (coordsNow != null) { - activity.startActivity(new Intent(Intent.ACTION_VIEW, - Uri.parse("http://maps.google.com/maps?f=d&saddr=" + coordsNow.getLatitude() + "," + coordsNow.getLongitude() + - "&daddr=" + coords.getLatitude() + "," + coords.getLongitude()))); - } else { - activity.startActivity(new Intent(Intent.ACTION_VIEW, - Uri.parse("http://maps.google.com/maps?f=d&daddr=" + coords.getLatitude() + "," + coords.getLongitude()))); - } - - return true; - } catch (Exception e) { - // nothing - } - - Log.i(cgSettings.tag, "cgBase.runNavigation: No navigation application available."); - - if (res != null) { - ActivityMixin.showToast(activity, res.getString(R.string.err_navigation_no)); - } - - return false; - } - - public String getMapUserToken(Handler noTokenHandler) { - final cgResponse response = request(false, "www.geocaching.com", "/map/default.aspx", "GET", "", 0, false); - final String data = response.getData(); - String usertoken = null; - - if (StringUtils.isNotBlank(data)) { - final Pattern pattern = Pattern.compile("var userToken[^=]*=[^']*'([^']+)';", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - - final Matcher matcher = pattern.matcher(data); - while (matcher.find()) { - if (matcher.groupCount() > 0) { - usertoken = matcher.group(1); - } - } - } - - if (noTokenHandler != null && StringUtils.isBlank(usertoken)) { - noTokenHandler.sendEmptyMessage(0); - } - - return usertoken; - } - - public static Double getElevation(final Geopoint coords) { - try { - final String host = "maps.googleapis.com"; - final String path = "/maps/api/elevation/json"; - final String params = "sensor=false&locations=" + - String.format((Locale) null, "%.6f", coords.getLatitude()) + "," + - String.format((Locale) null, "%.6f", coords.getLongitude()); - - final String data = requestJSON(host, path, params); - - if (StringUtils.isBlank(data)) { - return null; - } - - JSONObject response = new JSONObject(data); - String status = response.getString("status"); - - if (status == null || status.equalsIgnoreCase("OK") == false) { - return null; - } - - if (response.has("results")) { - JSONArray results = response.getJSONArray("results"); - JSONObject result = results.getJSONObject(0); - return result.getDouble("elevation"); - } - } catch (Exception e) { - Log.w(cgSettings.tag, "cgBase.getElevation: " + e.toString()); - } - - return null; - } - - /** - * Generate a time string according to system-wide settings (locale, 12/24 hour) - * such as "13:24". - * - * @param date milliseconds since the epoch - * @return the formatted string - */ - public String formatTime(long date) { - return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_TIME); - } - - /** - * Generate a date string according to system-wide settings (locale, date format) - * such as "20 December" or "20 December 2010". The year will only be included when necessary. - * - * @param date milliseconds since the epoch - * @return the formatted string - */ - public String formatDate(long date) { - return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_DATE); - } - - /** - * Generate a date string according to system-wide settings (locale, date format) - * such as "20 December 2010". The year will always be included, making it suitable - * to generate long-lived log entries. - * - * @param date milliseconds since the epoch - * @return the formatted string - */ - public String formatFullDate(long date) { - return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_DATE - | DateUtils.FORMAT_SHOW_YEAR); - } - - /** - * Generate a numeric date string according to system-wide settings (locale, date format) - * such as "10/20/2010". - * - * @param date milliseconds since the epoch - * @return the formatted string - */ - public String formatShortDate(long date) { - return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_DATE - | DateUtils.FORMAT_NUMERIC_DATE); - } - - /** - * Generate a numeric date and time string according to system-wide settings (locale, - * date format) such as "7 sept. à 12:35". - * - * @param context a Context - * @param date milliseconds since the epoch - * @return the formatted string - */ - public static String formatShortDateTime(Context context, long date) { - return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL); - } - - /** - * TODO This method is only needed until the settings are a singleton - * @return - */ - public String getUserName() { - return settings.getUsername(); - } - - /** - * insert text into the EditText at the current cursor position - * @param editText - * @param insertText - * @param addSpace add a space character, if there is no whitespace before the current cursor position - */ - static void insertAtPosition(final EditText editText, String insertText, final boolean addSpace) { - int selectionStart = editText.getSelectionStart(); - int selectionEnd = editText.getSelectionEnd(); - int start = Math.min(selectionStart, selectionEnd); - int end = Math.max(selectionStart, selectionEnd); - - String content = editText.getText().toString(); - if (start > 0 && !Character.isWhitespace(content.charAt(start - 1))) { - insertText = " " + insertText; - } - - editText.getText().replace(start, end, insertText); - int newCursor = start + insertText.length(); - editText.setSelection(newCursor, newCursor); - } + } + + /** + * Removes the cache from the watchlist + * + * @param cache + * the cache to remove + * @return -1: error occured + */ + public int removeFromWatchlist(cgCache cache) { + String host = "www.geocaching.com"; + String path = "/my/watchlist.aspx?ds=1&action=rem&id=" + cache.cacheId; + String method = "POST"; + + String page = requestLogged(false, host, path, method, null, false, false, false); + + if (StringUtils.isBlank(page)) { + Log.e(cgSettings.tag, "cgBase.removeFromWatchlist: No data from server"); + return -1; // error + } + + // removing cache from list needs approval by hitting "Yes" button + final Map<String, String> params = new HashMap<String, String>(); + transferViewstates(page, params); + params.put("__EVENTTARGET", ""); + params.put("__EVENTARGUMENT", ""); + params.put("ctl00$ContentBody$btnYes", "Yes"); + + page = request(false, host, path, method, params, false, false, false).getData(); + boolean guidOnPage = cache.isGuidContainedInPage(page); + if (!guidOnPage) { + Log.i(cgSettings.tag, "cgBase.removeFromWatchlist: cache removed from watchlist"); + cache.onWatchlist = false; + } else { + Log.e(cgSettings.tag, "cgBase.removeFromWatchlist: cache not removed from watchlist"); + } + return guidOnPage ? -1 : 0; // on watchlist (=error) / not on watchlist + } + + final public static HostnameVerifier doNotVerify = new HostnameVerifier() { + + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + + public static void trustAllHosts() { + TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[] {}; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + } + }; + + try { + SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.trustAllHosts: " + e.toString()); + } + } + + public static void postTweetCache(cgeoapplication app, cgSettings settings, String geocode) { + final cgCache cache = app.getCacheByGeocode(geocode); + String name = cache.name; + if (name.length() > 84) { + name = name.substring(0, 81) + "..."; + } + final String status = "I found " + name + " (http://coord.info/" + cache.geocode.toUpperCase() + ")! #cgeo #geocaching"; // 56 chars + cache name + + postTweet(app, settings, status, null); + } + + public static void postTweetTrackable(cgeoapplication app, cgSettings settings, String geocode) { + final cgTrackable trackable = app.getTrackableByGeocode(geocode); + String name = trackable.name; + if (name.length() > 82) { + name = name.substring(0, 79) + "..."; + } + final String status = "I touched " + name + " (http://coord.info/" + trackable.geocode.toUpperCase() + ")! #cgeo #geocaching"; // 58 chars + trackable name + + postTweet(app, settings, status, null); + } + + public static void postTweet(cgeoapplication app, cgSettings settings, String status, final Geopoint coords) { + if (app == null) { + return; + } + if (settings == null || StringUtils.isBlank(settings.tokenPublic) || StringUtils.isBlank(settings.tokenSecret)) { + return; + } + + try { + Map<String, String> parameters = new HashMap<String, String>(); + + parameters.put("status", status); + if (coords != null) { + parameters.put("lat", String.format("%.6f", coords.getLatitude())); + parameters.put("long", String.format("%.6f", coords.getLongitude())); + parameters.put("display_coordinates", "true"); + } + + final String paramsDone = cgOAuth.signOAuth("api.twitter.com", "/1/statuses/update.json", "POST", false, parameters, settings.tokenPublic, settings.tokenSecret); + + HttpURLConnection connection = null; + try { + final StringBuffer buffer = new StringBuffer(); + final URL u = new URL("http://api.twitter.com/1/statuses/update.json"); + final URLConnection uc = u.openConnection(); + + uc.setRequestProperty("Host", "api.twitter.com"); + + connection = (HttpURLConnection) uc; + connection.setReadTimeout(30000); + connection.setRequestMethod("POST"); + HttpURLConnection.setFollowRedirects(true); + connection.setDoInput(true); + connection.setDoOutput(true); + + final OutputStream out = connection.getOutputStream(); + final OutputStreamWriter wr = new OutputStreamWriter(out); + wr.write(paramsDone); + wr.flush(); + wr.close(); + + Log.i(cgSettings.tag, "Twitter.com: " + connection.getResponseCode() + " " + connection.getResponseMessage()); + + InputStream ins; + final String encoding = connection.getContentEncoding(); + + if (encoding != null && encoding.equalsIgnoreCase("gzip")) { + ins = new GZIPInputStream(connection.getInputStream()); + } else if (encoding != null && encoding.equalsIgnoreCase("deflate")) { + ins = new InflaterInputStream(connection.getInputStream(), new Inflater(true)); + } else { + ins = connection.getInputStream(); + } + + final InputStreamReader inr = new InputStreamReader(ins); + final BufferedReader br = new BufferedReader(inr); + + readIntoBuffer(br, buffer); + + br.close(); + ins.close(); + inr.close(); + connection.disconnect(); + } catch (IOException e) { + Log.e(cgSettings.tag, "cgBase.postTweet.IO: " + connection.getResponseCode() + ": " + connection.getResponseMessage() + " ~ " + e.toString()); + + final InputStream ins = connection.getErrorStream(); + final StringBuffer buffer = new StringBuffer(); + final InputStreamReader inr = new InputStreamReader(ins); + final BufferedReader br = new BufferedReader(inr); + + readIntoBuffer(br, buffer); + + br.close(); + ins.close(); + inr.close(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.postTweet.inner: " + e.toString()); + } + + connection.disconnect(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.postTweet: " + e.toString()); + } + } + + private static void readIntoBuffer(BufferedReader br, StringBuffer buffer) throws IOException { + int bufferSize = 1024 * 16; + char[] bytes = new char[bufferSize]; + int bytesRead; + while ((bytesRead = br.read(bytes)) > 0) { + if (bytesRead == bufferSize) { + buffer.append(bytes); + } + else { + buffer.append(bytes, 0, bytesRead); + } + } + } + + public static String getLocalIpAddress() { + try { + for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { + NetworkInterface intf = en.nextElement(); + for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) { + InetAddress inetAddress = enumIpAddr.nextElement(); + if (!inetAddress.isLoopbackAddress()) { + return inetAddress.getHostAddress(); + } + } + } + } catch (SocketException e) { + // nothing + } + + return null; + } + + public static String implode(String delim, Object[] array) { + StringBuilder out = new StringBuilder(); + + try { + for (int i = 0; i < array.length; i++) { + if (i != 0) { + out.append(delim); + } + out.append(array[i].toString()); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.implode: " + e.toString()); + } + return out.toString(); + } + + public static String urlencode_rfc3986(String text) { + final String encoded = URLEncoder.encode(text).replace("+", "%20").replaceAll("%7E", "~"); + + return encoded; + } + + public String prepareParameters(Map<String, String> params, boolean my, boolean addF) { + String paramsDone = null; + + if (my != true && settings.excludeMine > 0) { + if (params == null) { + params = new HashMap<String, String>(); + } + if (addF) { + params.put("f", "1"); + } + + Log.i(cgSettings.tag, "Skipping caches found or hidden by user."); + } + + if (params != null) { + Set<Map.Entry<String, String>> entrySet = params.entrySet(); + List<String> paramsEncoded = new ArrayList<String>(); + + for (Map.Entry<String, String> entry : entrySet) + { + String key = entry.getKey(); + String value = entry.getValue(); + + if (key.charAt(0) == '^') { + key = ""; + } + if (value == null) { + value = ""; + } + + paramsEncoded.add(key + "=" + urlencode_rfc3986(value)); + } + + paramsDone = implode("&", paramsEncoded.toArray()); + } else { + paramsDone = ""; + } + + return paramsDone; + } + + public String[] requestViewstates(boolean secure, String host, String path, String method, Map<String, String> params, boolean xContentType, boolean my) { + final cgResponse response = request(secure, host, path, method, params, xContentType, my, false); + + return getViewstates(response.getData()); + } + + public String requestLogged(boolean secure, String host, String path, String method, Map<String, String> params, boolean xContentType, boolean my, boolean addF) { + cgResponse response = request(secure, host, path, method, params, xContentType, my, addF); + String data = response.getData(); + + if (checkLogin(data) == false) { + int loginState = login(); + if (loginState == 1) { + response = request(secure, host, path, method, params, xContentType, my, addF); + data = response.getData(); + } else { + Log.i(cgSettings.tag, "Working as guest."); + } + } + + return data; + } + + public cgResponse request(boolean secure, String host, String path, String method, Map<String, String> params, boolean xContentType, boolean my, boolean addF) { + // prepare parameters + final String paramsDone = prepareParameters(params, my, addF); + + return request(secure, host, path, method, paramsDone, 0, xContentType); + } + + public cgResponse request(boolean secure, String host, String path, String method, Map<String, String> params, int requestId, boolean xContentType, boolean my, boolean addF) { + // prepare parameters + final String paramsDone = prepareParameters(params, my, addF); + + return request(secure, host, path, method, paramsDone, requestId, xContentType); + } + + public cgResponse request(boolean secure, String host, String path, String method, String params, int requestId, Boolean xContentType) { + URL u = null; + int httpCode = -1; + String httpMessage = null; + String httpLocation = null; + + if (requestId == 0) { + requestId = (int) (Math.random() * 1000); + } + + if (method == null || (method.equalsIgnoreCase("GET") == false && method.equalsIgnoreCase("POST") == false)) { + method = "POST"; + } else { + method = method.toUpperCase(); + } + + // https + String scheme = "http://"; + if (secure) { + scheme = "https://"; + } + + String cookiesDone = getCookiesAsString(); + + URLConnection uc = null; + HttpURLConnection connection = null; + Integer timeout = 30000; + StringBuffer buffer = null; + + for (int i = 0; i < 5; i++) { + if (i > 0) { + Log.w(cgSettings.tag, "Failed to download data, retrying. Attempt #" + (i + 1)); + } + + buffer = new StringBuffer(); + timeout = 30000 + (i * 10000); + + try { + if (method.equals("GET")) { + // GET + u = new URL(scheme + host + path + "?" + params); + uc = u.openConnection(); + + uc.setRequestProperty("Host", host); + uc.setRequestProperty("Cookie", cookiesDone); + if (xContentType) { + uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + } + + if (settings.asBrowser == 1) { + uc.setRequestProperty("Accept", "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"); + // uc.setRequestProperty("Accept-Encoding", "gzip"); // not supported via cellular network + uc.setRequestProperty("Accept-Charset", "utf-8, iso-8859-1, utf-16, *;q=0.7"); + uc.setRequestProperty("Accept-Language", "en-US"); + uc.setRequestProperty("User-Agent", idBrowser); + uc.setRequestProperty("Connection", "keep-alive"); + uc.setRequestProperty("Keep-Alive", "300"); + } + + connection = (HttpURLConnection) uc; + connection.setReadTimeout(timeout); + connection.setRequestMethod(method); + HttpURLConnection.setFollowRedirects(false); + connection.setDoInput(true); + connection.setDoOutput(false); + } else { + // POST + u = new URL(scheme + host + path); + uc = u.openConnection(); + + uc.setRequestProperty("Host", host); + uc.setRequestProperty("Cookie", cookiesDone); + if (xContentType) { + uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + } + + if (settings.asBrowser == 1) { + uc.setRequestProperty("Accept", "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"); + // uc.setRequestProperty("Accept-Encoding", "gzip"); // not supported via cellular network + uc.setRequestProperty("Accept-Charset", "utf-8, iso-8859-1, utf-16, *;q=0.7"); + uc.setRequestProperty("Accept-Language", "en-US"); + uc.setRequestProperty("User-Agent", idBrowser); + uc.setRequestProperty("Connection", "keep-alive"); + uc.setRequestProperty("Keep-Alive", "300"); + } + + connection = (HttpURLConnection) uc; + connection.setReadTimeout(timeout); + connection.setRequestMethod(method); + HttpURLConnection.setFollowRedirects(false); + connection.setDoInput(true); + connection.setDoOutput(true); + + final OutputStream out = connection.getOutputStream(); + final OutputStreamWriter wr = new OutputStreamWriter(out); + wr.write(params); + wr.flush(); + wr.close(); + } + + String headerName = null; + final SharedPreferences.Editor prefsEditor = prefs.edit(); + for (int j = 1; (headerName = uc.getHeaderFieldKey(j)) != null; j++) { + if (headerName != null && headerName.equalsIgnoreCase("Set-Cookie")) { + int index; + String cookie = uc.getHeaderField(j); + + index = cookie.indexOf(";"); + if (index > -1) { + cookie = cookie.substring(0, cookie.indexOf(";")); + } + + index = cookie.indexOf("="); + if (index > -1 && cookie.length() > (index + 1)) { + String name = cookie.substring(0, cookie.indexOf("=")); + String value = cookie.substring(cookie.indexOf("=") + 1, cookie.length()); + + cookies.put(name, value); + prefsEditor.putString("cookie_" + name, value); + } + } + } + prefsEditor.commit(); + + InputStream ins = getInputstreamFromConnection(connection); + final InputStreamReader inr = new InputStreamReader(ins); + final BufferedReader br = new BufferedReader(inr, 16 * 1024); + + readIntoBuffer(br, buffer); + + httpCode = connection.getResponseCode(); + httpMessage = connection.getResponseMessage(); + httpLocation = uc.getHeaderField("Location"); + + final String paramsLog = params.replaceAll(passMatch, "password=***"); + Log.i(cgSettings.tag + "|" + requestId, "[" + method + " " + (int) (params.length() / 1024) + "k | " + httpCode + " | " + (int) (buffer.length() / 1024) + "k] Downloaded " + scheme + host + path + "?" + paramsLog); + + connection.disconnect(); + br.close(); + ins.close(); + inr.close(); + } catch (IOException e) { + Log.e(cgSettings.tag, "cgeoBase.request.IOException: " + e.toString()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.request: " + e.toString()); + } + + if (buffer.length() > 0) { + break; + } + } + + cgResponse response = new cgResponse(); + + try { + if (httpCode == 302 && httpLocation != null) { + final Uri newLocation = Uri.parse(httpLocation); + if (newLocation.isRelative()) { + response = request(secure, host, path, "GET", new HashMap<String, String>(), requestId, false, false, false); + } else { + boolean secureRedir = false; + if (newLocation.getScheme().equals("https")) { + secureRedir = true; + } + response = request(secureRedir, newLocation.getHost(), newLocation.getPath(), "GET", new HashMap<String, String>(), requestId, false, false, false); + } + } else { + if (StringUtils.isNotEmpty(buffer)) { + replaceWhitespace(buffer); + String data = buffer.toString(); + buffer = null; + + if (data != null) { + response.setData(data); + } else { + response.setData(""); + } + response.setStatusCode(httpCode); + response.setStatusMessage(httpMessage); + response.setUrl(u.toString()); + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.page: " + e.toString()); + } + + return response; + } + + private String getCookiesAsString() { + // prepare cookies + String cookiesDone = null; + if (cookies == null || cookies.isEmpty()) { + if (cookies == null) { + cookies = new HashMap<String, String>(); + } + + final Map<String, ?> prefsAll = prefs.getAll(); + final Set<? extends Map.Entry<String, ?>> entrySet = prefsAll.entrySet(); + + for (Map.Entry<String, ?> entry : entrySet) { + String key = entry.getKey(); + if (key.matches("cookie_.+")) { + final String cookieKey = key.substring(7); + final String cookieValue = (String) entry.getValue(); + + cookies.put(cookieKey, cookieValue); + } + } + } + + if (cookies != null) { + final Set<Map.Entry<String, String>> entrySet = cookies.entrySet(); + final List<String> cookiesEncoded = new ArrayList<String>(); + + for (Map.Entry<String, String> entry : entrySet) { + cookiesEncoded.add(entry.getKey() + "=" + entry.getValue()); + } + + if (cookiesEncoded.size() > 0) { + cookiesDone = implode("; ", cookiesEncoded.toArray()); + } + } + + if (cookiesDone == null) { + Map<String, ?> prefsValues = prefs.getAll(); + + if (CollectionUtils.isNotEmpty(prefsValues)) { + final Set<? extends Map.Entry<String, ?>> entrySet = prefsValues.entrySet(); + final List<String> cookiesEncoded = new ArrayList<String>(); + + for (Map.Entry<String, ?> entry : entrySet) { + String key = entry.getKey(); + if (key.length() > 7 && key.substring(0, 7).equals("cookie_")) { + cookiesEncoded.add(key + "=" + entry.getValue()); + } + } + + if (cookiesEncoded.size() > 0) { + cookiesDone = implode("; ", cookiesEncoded.toArray()); + } + } + } + + if (cookiesDone == null) { + cookiesDone = ""; + } + return cookiesDone; + } + + /** + * Replace the characters \n, \r and \t with a space + * + * @param buffer + * The data + */ + public static void replaceWhitespace(final StringBuffer buffer) { + final int length = buffer.length(); + final char[] chars = new char[length]; + buffer.getChars(0, length, chars, 0); + int resultSize = 0; + boolean lastWasWhitespace = false; + for (char c : chars) { + if (c == ' ' || c == '\n' || c == '\r' || c == '\t') { + if (!lastWasWhitespace) { + chars[resultSize++] = ' '; + } + lastWasWhitespace = true; + } else { + chars[resultSize++] = c; + lastWasWhitespace = false; + } + } + buffer.setLength(0); + buffer.append(chars); + } + + public String requestJSONgc(String host, String path, String params) { + int httpCode = -1; + String httpLocation = null; + + String cookiesDone = getCookiesAsString(); + + URLConnection uc = null; + HttpURLConnection connection = null; + Integer timeout = 30000; + final StringBuffer buffer = new StringBuffer(); + + for (int i = 0; i < 3; i++) { + if (i > 0) { + Log.w(cgSettings.tag, "Failed to download data, retrying. Attempt #" + (i + 1)); + } + + buffer.delete(0, buffer.length()); + timeout = 30000 + (i * 15000); + + try { + // POST + final URL u = new URL("http://" + host + path); + uc = u.openConnection(); + + uc.setRequestProperty("Host", host); + uc.setRequestProperty("Cookie", cookiesDone); + uc.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + uc.setRequestProperty("X-Requested-With", "XMLHttpRequest"); + uc.setRequestProperty("Accept", "application/json, text/javascript, */*; q=0.01"); + uc.setRequestProperty("Referer", host + "/" + path); + + if (settings.asBrowser == 1) { + uc.setRequestProperty("Accept-Charset", "utf-8, iso-8859-1, utf-16, *;q=0.7"); + uc.setRequestProperty("Accept-Language", "en-US"); + uc.setRequestProperty("User-Agent", idBrowser); + uc.setRequestProperty("Connection", "keep-alive"); + uc.setRequestProperty("Keep-Alive", "300"); + } + + connection = (HttpURLConnection) uc; + connection.setReadTimeout(timeout); + connection.setRequestMethod("POST"); + HttpURLConnection.setFollowRedirects(false); // TODO: Fix these (FilCab) + connection.setDoInput(true); + connection.setDoOutput(true); + + final OutputStream out = connection.getOutputStream(); + final OutputStreamWriter wr = new OutputStreamWriter(out); + wr.write(params); + wr.flush(); + wr.close(); + + String headerName = null; + final SharedPreferences.Editor prefsEditor = prefs.edit(); + for (int j = 1; (headerName = uc.getHeaderFieldKey(j)) != null; j++) { + if (headerName != null && headerName.equalsIgnoreCase("Set-Cookie")) { + int index; + String cookie = uc.getHeaderField(j); + + index = cookie.indexOf(";"); + if (index > -1) { + cookie = cookie.substring(0, cookie.indexOf(";")); + } + + index = cookie.indexOf("="); + if (index > -1 && cookie.length() > (index + 1)) { + String name = cookie.substring(0, cookie.indexOf("=")); + String value = cookie.substring(cookie.indexOf("=") + 1, cookie.length()); + + cookies.put(name, value); + prefsEditor.putString("cookie_" + name, value); + } + } + } + prefsEditor.commit(); + + InputStream ins = getInputstreamFromConnection(connection); + final InputStreamReader inr = new InputStreamReader(ins); + final BufferedReader br = new BufferedReader(inr); + + readIntoBuffer(br, buffer); + + httpCode = connection.getResponseCode(); + httpLocation = uc.getHeaderField("Location"); + + final String paramsLog = params.replaceAll(passMatch, "password=***"); + Log.i(cgSettings.tag + " | JSON", "[POST " + (int) (params.length() / 1024) + "k | " + httpCode + " | " + (int) (buffer.length() / 1024) + "k] Downloaded " + "http://" + host + path + "?" + paramsLog); + + connection.disconnect(); + br.close(); + ins.close(); + inr.close(); + } catch (IOException e) { + Log.e(cgSettings.tag, "cgeoBase.requestJSONgc.IOException: " + e.toString()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.requestJSONgc: " + e.toString()); + } + + if (buffer != null && buffer.length() > 0) { + break; + } + } + + String page = null; + if (httpCode == 302 && httpLocation != null) { + final Uri newLocation = Uri.parse(httpLocation); + if (newLocation.isRelative()) { + page = requestJSONgc(host, path, params); + } else { + page = requestJSONgc(newLocation.getHost(), newLocation.getPath(), params); + } + } else { + replaceWhitespace(buffer); + page = buffer.toString(); + } + + if (page != null) { + return page; + } else { + return ""; + } + } + + private static InputStream getInputstreamFromConnection(HttpURLConnection connection) throws IOException { + final String encoding = connection.getContentEncoding(); + InputStream ins; + + if (encoding != null && encoding.equalsIgnoreCase("gzip")) { + ins = new GZIPInputStream(connection.getInputStream()); + } else if (encoding != null && encoding.equalsIgnoreCase("deflate")) { + ins = new InflaterInputStream(connection.getInputStream(), new Inflater(true)); + } else { + ins = connection.getInputStream(); + } + return ins; + } + + public static String requestJSON(String host, String path, String params) { + return requestJSON("http://", host, path, "GET", params); + } + + public static String requestJSON(String scheme, String host, String path, String method, String params) { + int httpCode = -1; + //String httpLocation = null; + + if (method == null) { + method = "GET"; + } else { + method = method.toUpperCase(); + } + + boolean methodPost = false; + if (method.equalsIgnoreCase("POST")) { + methodPost = true; + } + + URLConnection uc = null; + HttpURLConnection connection = null; + Integer timeout = 30000; + final StringBuffer buffer = new StringBuffer(); + + for (int i = 0; i < 3; i++) { + if (i > 0) { + Log.w(cgSettings.tag, "Failed to download data, retrying. Attempt #" + (i + 1)); + } + + buffer.delete(0, buffer.length()); + timeout = 30000 + (i * 15000); + + try { + try { + URL u = null; + if (methodPost) { + u = new URL(scheme + host + path); + } else { + u = new URL(scheme + host + path + "?" + params); + } + + if (u.getProtocol().toLowerCase().equals("https")) { + trustAllHosts(); + HttpsURLConnection https = (HttpsURLConnection) u.openConnection(); + https.setHostnameVerifier(doNotVerify); + uc = https; + } else { + uc = (HttpURLConnection) u.openConnection(); + } + + uc.setRequestProperty("Host", host); + uc.setRequestProperty("Accept", "application/json, text/javascript, */*; q=0.01"); + if (methodPost) { + uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + uc.setRequestProperty("Content-Length", Integer.toString(params.length())); + uc.setRequestProperty("X-HTTP-Method-Override", "GET"); + } else { + uc.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + } + uc.setRequestProperty("X-Requested-With", "XMLHttpRequest"); + + connection = (HttpURLConnection) uc; + connection.setReadTimeout(timeout); + connection.setRequestMethod(method); + HttpURLConnection.setFollowRedirects(false); // TODO: Fix these (FilCab) + connection.setDoInput(true); + if (methodPost) { + connection.setDoOutput(true); + + final OutputStream out = connection.getOutputStream(); + final OutputStreamWriter wr = new OutputStreamWriter(out); + wr.write(params); + wr.flush(); + wr.close(); + } else { + connection.setDoOutput(false); + } + + InputStream ins = getInputstreamFromConnection(connection); + final InputStreamReader inr = new InputStreamReader(ins); + final BufferedReader br = new BufferedReader(inr, 1024); + + readIntoBuffer(br, buffer); + + httpCode = connection.getResponseCode(); + + final String paramsLog = params.replaceAll(passMatch, "password=***"); + Log.i(cgSettings.tag + " | JSON", "[POST " + (int) (params.length() / 1024) + "k | " + httpCode + " | " + (int) (buffer.length() / 1024) + "k] Downloaded " + "http://" + host + path + "?" + paramsLog); + + connection.disconnect(); + br.close(); + ins.close(); + inr.close(); + } catch (IOException e) { + httpCode = connection.getResponseCode(); + + Log.e(cgSettings.tag, "cgeoBase.requestJSON.IOException: " + httpCode + ": " + connection.getResponseMessage() + " ~ " + e.toString()); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.requestJSON: " + e.toString()); + } + + if (StringUtils.isNotBlank(buffer)) { + break; + } + + if (httpCode == 403) { + // we're not allowed to download content, so let's move + break; + } + } + + String page = null; + //This is reported as beeing deadCode (httpLocation is always null) + //2011-08-09 - 302 is redirect so something should probably be done + /* + * if (httpCode == 302 && httpLocation != null) { + * final Uri newLocation = Uri.parse(httpLocation); + * if (newLocation.isRelative()) { + * page = requestJSONgc(host, path, params); + * } else { + * page = requestJSONgc(newLocation.getHost(), newLocation.getPath(), params); + * } + * } else { + */ + replaceWhitespace(buffer); + page = buffer.toString(); + //} + + if (page != null) { + return page; + } else { + return ""; + } + } + + public static String rot13(String text) { + final StringBuilder result = new StringBuilder(); + // plaintext flag (do not convert) + boolean plaintext = false; + + int length = text.length(); + for (int index = 0; index < length; index++) { + int c = text.charAt(index); + if (c == '[') { + plaintext = true; + } else if (c == ']') { + plaintext = false; + } else if (!plaintext) { + int capitalized = c & 32; + c &= ~capitalized; + c = ((c >= 'A') && (c <= 'Z') ? ((c - 'A' + 13) % 26 + 'A') : c) + | capitalized; + } + result.append((char) c); + } + return result.toString(); + } + + public static String md5(String text) { + String hashed = ""; + + try { + MessageDigest digest = MessageDigest.getInstance("MD5"); + digest.update(text.getBytes(), 0, text.length()); + hashed = new BigInteger(1, digest.digest()).toString(16); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.md5: " + e.toString()); + } + + return hashed; + } + + public static String sha1(String text) { + String hashed = ""; + + try { + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + digest.update(text.getBytes(), 0, text.length()); + hashed = new BigInteger(1, digest.digest()).toString(16); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.sha1: " + e.toString()); + } + + return hashed; + } + + public static byte[] hashHmac(String text, String salt) { + byte[] macBytes = {}; + + try { + SecretKeySpec secretKeySpec = new SecretKeySpec(salt.getBytes(), "HmacSHA1"); + Mac mac = Mac.getInstance("HmacSHA1"); + mac.init(secretKeySpec); + macBytes = mac.doFinal(text.getBytes()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.hashHmac: " + e.toString()); + } + + return macBytes; + } + + public static boolean deleteDirectory(File path) { + if (path.exists()) { + File[] files = path.listFiles(); + + for (File file : files) { + if (file.isDirectory()) { + deleteDirectory(file); + } else { + file.delete(); + } + } + } + + return (path.delete()); + } + + public void storeCache(cgeoapplication app, Activity activity, cgCache cache, String geocode, int listId, Handler handler) { + try { + // get cache details, they may not yet be complete + if (cache != null) { + // only reload the cache, if it was already stored or has not all details (by checking the description) + if (cache.reason > 0 || StringUtils.isBlank(cache.description)) { + final Map<String, String> params = new HashMap<String, String>(); + params.put("geocode", cache.geocode); + final UUID searchId = searchByGeocode(params, listId, false); + cache = app.getCache(searchId); + } + } else if (StringUtils.isNotBlank(geocode)) { + final Map<String, String> params = new HashMap<String, String>(); + params.put("geocode", geocode); + final UUID searchId = searchByGeocode(params, listId, false); + cache = app.getCache(searchId); + } + + if (cache == null) { + if (handler != null) { + handler.sendMessage(new Message()); + } + + return; + } + + final cgHtmlImg imgGetter = new cgHtmlImg(activity, cache.geocode, false, listId, true); + + // store images from description + if (StringUtils.isNotBlank(cache.description)) { + Html.fromHtml(cache.description, imgGetter, null); + } + + // store spoilers + if (CollectionUtils.isNotEmpty(cache.spoilers)) { + for (cgImage oneSpoiler : cache.spoilers) { + imgGetter.getDrawable(oneSpoiler.url); + } + } + + // store images from logs + if (settings.storelogimages) { + for (cgLog log : cache.logs) { + if (CollectionUtils.isNotEmpty(log.logImages)) { + for (cgImage oneLogImg : log.logImages) { + imgGetter.getDrawable(oneLogImg.url); + } + } + } + } + + // store map previews + StaticMapsProvider.downloadMaps(cache, settings, activity); + + app.markStored(cache.geocode, listId); + app.removeCacheFromCache(cache.geocode); + + if (handler != null) { + handler.sendMessage(new Message()); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.storeCache: " + e.toString()); + } + } + + public static void dropCache(cgeoapplication app, Activity activity, cgCache cache, Handler handler) { + try { + app.markDropped(cache.geocode); + app.removeCacheFromCache(cache.geocode); + + handler.sendMessage(new Message()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.dropCache: " + e.toString()); + } + } + + public static boolean isInViewPort(int centerLat1, int centerLon1, int centerLat2, int centerLon2, int spanLat1, int spanLon1, int spanLat2, int spanLon2) { + try { + // expects coordinates in E6 format + final int left1 = centerLat1 - (spanLat1 / 2); + final int right1 = centerLat1 + (spanLat1 / 2); + final int top1 = centerLon1 + (spanLon1 / 2); + final int bottom1 = centerLon1 - (spanLon1 / 2); + + final int left2 = centerLat2 - (spanLat2 / 2); + final int right2 = centerLat2 + (spanLat2 / 2); + final int top2 = centerLon2 + (spanLon2 / 2); + final int bottom2 = centerLon2 - (spanLon2 / 2); + + if (left2 <= left1) { + return false; + } + if (right2 >= right1) { + return false; + } + if (top2 >= top1) { + return false; + } + if (bottom2 <= bottom1) { + return false; + } + + return true; + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.isInViewPort: " + e.toString()); + return false; + } + } + + public static boolean isCacheInViewPort(int centerLat, int centerLon, int spanLat, int spanLon, final Geopoint cacheCoords) { + if (cacheCoords == null) { + return false; + } + + // viewport is defined by center, span and some (10%) reserve on every side + int minLat = centerLat - (spanLat / 2) - (spanLat / 10); + int maxLat = centerLat + (spanLat / 2) + (spanLat / 10); + int minLon = centerLon - (spanLon / 2) - (spanLon / 10); + int maxLon = centerLon + (spanLon / 2) + (spanLon / 10); + final int cLat = cacheCoords.getLatitudeE6(); + final int cLon = cacheCoords.getLongitudeE6(); + int mid = 0; + + if (maxLat < minLat) { + mid = minLat; + minLat = maxLat; + maxLat = mid; + } + + if (maxLon < minLon) { + mid = minLon; + minLon = maxLon; + maxLon = mid; + } + + boolean latOk = false; + boolean lonOk = false; + + if (cLat >= minLat && cLat <= maxLat) { + latOk = true; + } + if (cLon >= minLon && cLon <= maxLon) { + lonOk = true; + } + + if (latOk && lonOk) { + return true; + } else { + return false; + } + } + + private static char[] base64map1 = new char[64]; + + static { + int i = 0; + for (char c = 'A'; c <= 'Z'; c++) { + base64map1[i++] = c; + } + for (char c = 'a'; c <= 'z'; c++) { + base64map1[i++] = c; + } + for (char c = '0'; c <= '9'; c++) { + base64map1[i++] = c; + } + base64map1[i++] = '+'; + base64map1[i++] = '/'; + } + private static byte[] base64map2 = new byte[128]; + + static { + for (int i = 0; i < base64map2.length; i++) { + base64map2[i] = -1; + } + for (int i = 0; i < 64; i++) { + base64map2[base64map1[i]] = (byte) i; + } + } + + public static String base64Encode(byte[] in) { + int iLen = in.length; + int oDataLen = (iLen * 4 + 2) / 3; // output length without padding + int oLen = ((iLen + 2) / 3) * 4; // output length including padding + char[] out = new char[oLen]; + int ip = 0; + int op = 0; + + while (ip < iLen) { + int i0 = in[ip++] & 0xff; + int i1 = ip < iLen ? in[ip++] & 0xff : 0; + int i2 = ip < iLen ? in[ip++] & 0xff : 0; + int o0 = i0 >>> 2; + int o1 = ((i0 & 3) << 4) | (i1 >>> 4); + int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); + int o3 = i2 & 0x3F; + out[op++] = base64map1[o0]; + out[op++] = base64map1[o1]; + out[op] = op < oDataLen ? base64map1[o2] : '='; + op++; + out[op] = op < oDataLen ? base64map1[o3] : '='; + op++; + } + + return new String(out); + } + + public static byte[] base64Decode(String text) { + char[] in = text.toCharArray(); + + int iLen = in.length; + if (iLen % 4 != 0) { + throw new IllegalArgumentException("Length of Base64 encoded input string is not a multiple of 4."); + } + while (iLen > 0 && in[iLen - 1] == '=') { + iLen--; + } + int oLen = (iLen * 3) / 4; + byte[] out = new byte[oLen]; + int ip = 0; + int op = 0; + while (ip < iLen) { + int i0 = in[ip++]; + int i1 = in[ip++]; + int i2 = ip < iLen ? in[ip++] : 'A'; + int i3 = ip < iLen ? in[ip++] : 'A'; + if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) { + throw new IllegalArgumentException("Illegal character in Base64 encoded data."); + } + int b0 = base64map2[i0]; + int b1 = base64map2[i1]; + int b2 = base64map2[i2]; + int b3 = base64map2[i3]; + if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) { + throw new IllegalArgumentException("Illegal character in Base64 encoded data."); + } + int o0 = (b0 << 2) | (b1 >>> 4); + int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); + int o2 = ((b2 & 3) << 6) | b3; + out[op++] = (byte) o0; + if (op < oLen) { + out[op++] = (byte) o1; + } + if (op < oLen) { + out[op++] = (byte) o2; + } + } + return out; + } + + public static int getCacheIcon(final String type) { + fillIconsMap(); + Integer iconId = gcIcons.get("type_" + type); + if (iconId != null) { + return iconId; + } + // fallback to traditional if some icon type is not correct + return gcIcons.get("type_traditional"); + } + + public static int getMarkerIcon(final boolean cache, final String type, final boolean own, final boolean found, final boolean disabled) { + fillIconsMap(); + + if (wpIcons.isEmpty()) { + wpIcons.put("waypoint", R.drawable.marker_waypoint_waypoint); + wpIcons.put("flag", R.drawable.marker_waypoint_flag); + wpIcons.put("pkg", R.drawable.marker_waypoint_pkg); + wpIcons.put("puzzle", R.drawable.marker_waypoint_puzzle); + wpIcons.put("stage", R.drawable.marker_waypoint_stage); + wpIcons.put("trailhead", R.drawable.marker_waypoint_trailhead); + } + + int icon = -1; + String iconTxt = null; + + if (cache) { + if (StringUtils.isNotBlank(type)) { + if (own) { + iconTxt = type + "-own"; + } else if (found) { + iconTxt = type + "-found"; + } else if (disabled) { + iconTxt = type + "-disabled"; + } else { + iconTxt = type; + } + } else { + iconTxt = "traditional"; + } + + if (gcIcons.containsKey(iconTxt)) { + icon = gcIcons.get(iconTxt); + } else { + icon = gcIcons.get("traditional"); + } + } else { + if (StringUtils.isNotBlank(type)) { + iconTxt = type; + } else { + iconTxt = "waypoint"; + } + + if (wpIcons.containsKey(iconTxt)) { + icon = wpIcons.get(iconTxt); + } else { + icon = wpIcons.get("waypoint"); + } + } + + return icon; + } + + private static void fillIconsMap() { + if (gcIcons.isEmpty()) { + gcIcons.put("type_ape", R.drawable.type_ape); + gcIcons.put("type_cito", R.drawable.type_cito); + gcIcons.put("type_earth", R.drawable.type_earth); + gcIcons.put("type_event", R.drawable.type_event); + gcIcons.put("type_letterbox", R.drawable.type_letterbox); + gcIcons.put("type_locationless", R.drawable.type_locationless); + gcIcons.put("type_mega", R.drawable.type_mega); + gcIcons.put("type_multi", R.drawable.type_multi); + gcIcons.put("type_traditional", R.drawable.type_traditional); + gcIcons.put("type_virtual", R.drawable.type_virtual); + gcIcons.put("type_webcam", R.drawable.type_webcam); + gcIcons.put("type_wherigo", R.drawable.type_wherigo); + gcIcons.put("type_mystery", R.drawable.type_mystery); + gcIcons.put("type_gchq", R.drawable.type_hq); + // default markers + gcIcons.put("ape", R.drawable.marker_cache_ape); + gcIcons.put("cito", R.drawable.marker_cache_cito); + gcIcons.put("earth", R.drawable.marker_cache_earth); + gcIcons.put("event", R.drawable.marker_cache_event); + gcIcons.put("letterbox", R.drawable.marker_cache_letterbox); + gcIcons.put("locationless", R.drawable.marker_cache_locationless); + gcIcons.put("mega", R.drawable.marker_cache_mega); + gcIcons.put("multi", R.drawable.marker_cache_multi); + gcIcons.put("traditional", R.drawable.marker_cache_traditional); + gcIcons.put("virtual", R.drawable.marker_cache_virtual); + gcIcons.put("webcam", R.drawable.marker_cache_webcam); + gcIcons.put("wherigo", R.drawable.marker_cache_wherigo); + gcIcons.put("mystery", R.drawable.marker_cache_mystery); + gcIcons.put("gchq", R.drawable.marker_cache_gchq); + // own cache markers + gcIcons.put("ape-own", R.drawable.marker_cache_ape_own); + gcIcons.put("cito-own", R.drawable.marker_cache_cito_own); + gcIcons.put("earth-own", R.drawable.marker_cache_earth_own); + gcIcons.put("event-own", R.drawable.marker_cache_event_own); + gcIcons.put("letterbox-own", R.drawable.marker_cache_letterbox_own); + gcIcons.put("locationless-own", R.drawable.marker_cache_locationless_own); + gcIcons.put("mega-own", R.drawable.marker_cache_mega_own); + gcIcons.put("multi-own", R.drawable.marker_cache_multi_own); + gcIcons.put("traditional-own", R.drawable.marker_cache_traditional_own); + gcIcons.put("virtual-own", R.drawable.marker_cache_virtual_own); + gcIcons.put("webcam-own", R.drawable.marker_cache_webcam_own); + gcIcons.put("wherigo-own", R.drawable.marker_cache_wherigo_own); + gcIcons.put("mystery-own", R.drawable.marker_cache_mystery_own); + gcIcons.put("gchq-own", R.drawable.marker_cache_gchq_own); + // found cache markers + gcIcons.put("ape-found", R.drawable.marker_cache_ape_found); + gcIcons.put("cito-found", R.drawable.marker_cache_cito_found); + gcIcons.put("earth-found", R.drawable.marker_cache_earth_found); + gcIcons.put("event-found", R.drawable.marker_cache_event_found); + gcIcons.put("letterbox-found", R.drawable.marker_cache_letterbox_found); + gcIcons.put("locationless-found", R.drawable.marker_cache_locationless_found); + gcIcons.put("mega-found", R.drawable.marker_cache_mega_found); + gcIcons.put("multi-found", R.drawable.marker_cache_multi_found); + gcIcons.put("traditional-found", R.drawable.marker_cache_traditional_found); + gcIcons.put("virtual-found", R.drawable.marker_cache_virtual_found); + gcIcons.put("webcam-found", R.drawable.marker_cache_webcam_found); + gcIcons.put("wherigo-found", R.drawable.marker_cache_wherigo_found); + gcIcons.put("mystery-found", R.drawable.marker_cache_mystery_found); + gcIcons.put("gchq-found", R.drawable.marker_cache_gchq_found); + // disabled cache markers + gcIcons.put("ape-disabled", R.drawable.marker_cache_ape_disabled); + gcIcons.put("cito-disabled", R.drawable.marker_cache_cito_disabled); + gcIcons.put("earth-disabled", R.drawable.marker_cache_earth_disabled); + gcIcons.put("event-disabled", R.drawable.marker_cache_event_disabled); + gcIcons.put("letterbox-disabled", R.drawable.marker_cache_letterbox_disabled); + gcIcons.put("locationless-disabled", R.drawable.marker_cache_locationless_disabled); + gcIcons.put("mega-disabled", R.drawable.marker_cache_mega_disabled); + gcIcons.put("multi-disabled", R.drawable.marker_cache_multi_disabled); + gcIcons.put("traditional-disabled", R.drawable.marker_cache_traditional_disabled); + gcIcons.put("virtual-disabled", R.drawable.marker_cache_virtual_disabled); + gcIcons.put("webcam-disabled", R.drawable.marker_cache_webcam_disabled); + gcIcons.put("wherigo-disabled", R.drawable.marker_cache_wherigo_disabled); + gcIcons.put("mystery-disabled", R.drawable.marker_cache_mystery_disabled); + gcIcons.put("gchq-disabled", R.drawable.marker_cache_gchq_disabled); + } + } + + public static boolean runNavigation(Activity activity, Resources res, cgSettings settings, final Geopoint coords) { + return runNavigation(activity, res, settings, coords, null); + } + + public static boolean runNavigation(Activity activity, Resources res, cgSettings settings, final Geopoint coords, final Geopoint coordsNow) { + if (activity == null) { + return false; + } + if (settings == null) { + return false; + } + + // Google Navigation + if (settings.useGNavigation == 1) { + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("google.navigation:ll=" + coords.getLatitude() + "," + coords.getLongitude()))); + + return true; + } catch (Exception e) { + // nothing + } + } + + // Google Maps Directions + try { + if (coordsNow != null) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, + Uri.parse("http://maps.google.com/maps?f=d&saddr=" + coordsNow.getLatitude() + "," + coordsNow.getLongitude() + + "&daddr=" + coords.getLatitude() + "," + coords.getLongitude()))); + } else { + activity.startActivity(new Intent(Intent.ACTION_VIEW, + Uri.parse("http://maps.google.com/maps?f=d&daddr=" + coords.getLatitude() + "," + coords.getLongitude()))); + } + + return true; + } catch (Exception e) { + // nothing + } + + Log.i(cgSettings.tag, "cgBase.runNavigation: No navigation application available."); + + if (res != null) { + ActivityMixin.showToast(activity, res.getString(R.string.err_navigation_no)); + } + + return false; + } + + public String getMapUserToken(Handler noTokenHandler) { + final cgResponse response = request(false, "www.geocaching.com", "/map/default.aspx", "GET", "", 0, false); + final String data = response.getData(); + String usertoken = null; + + if (StringUtils.isNotBlank(data)) { + final Pattern pattern = Pattern.compile("var userToken[^=]*=[^']*'([^']+)';", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + + final Matcher matcher = pattern.matcher(data); + while (matcher.find()) { + if (matcher.groupCount() > 0) { + usertoken = matcher.group(1); + } + } + } + + if (noTokenHandler != null && StringUtils.isBlank(usertoken)) { + noTokenHandler.sendEmptyMessage(0); + } + + return usertoken; + } + + public static Double getElevation(final Geopoint coords) { + try { + final String host = "maps.googleapis.com"; + final String path = "/maps/api/elevation/json"; + final String params = "sensor=false&locations=" + + String.format((Locale) null, "%.6f", coords.getLatitude()) + "," + + String.format((Locale) null, "%.6f", coords.getLongitude()); + + final String data = requestJSON(host, path, params); + + if (StringUtils.isBlank(data)) { + return null; + } + + JSONObject response = new JSONObject(data); + String status = response.getString("status"); + + if (status == null || status.equalsIgnoreCase("OK") == false) { + return null; + } + + if (response.has("results")) { + JSONArray results = response.getJSONArray("results"); + JSONObject result = results.getJSONObject(0); + return result.getDouble("elevation"); + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.getElevation: " + e.toString()); + } + + return null; + } + + /** + * Generate a time string according to system-wide settings (locale, 12/24 hour) + * such as "13:24". + * + * @param date + * milliseconds since the epoch + * @return the formatted string + */ + public String formatTime(long date) { + return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_TIME); + } + + /** + * Generate a date string according to system-wide settings (locale, date format) + * such as "20 December" or "20 December 2010". The year will only be included when necessary. + * + * @param date + * milliseconds since the epoch + * @return the formatted string + */ + public String formatDate(long date) { + return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_DATE); + } + + /** + * Generate a date string according to system-wide settings (locale, date format) + * such as "20 December 2010". The year will always be included, making it suitable + * to generate long-lived log entries. + * + * @param date + * milliseconds since the epoch + * @return the formatted string + */ + public String formatFullDate(long date) { + return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_DATE + | DateUtils.FORMAT_SHOW_YEAR); + } + + /** + * Generate a numeric date string according to system-wide settings (locale, date format) + * such as "10/20/2010". + * + * @param date + * milliseconds since the epoch + * @return the formatted string + */ + public String formatShortDate(long date) { + return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_DATE + | DateUtils.FORMAT_NUMERIC_DATE); + } + + /** + * Generate a numeric date and time string according to system-wide settings (locale, + * date format) such as "7 sept. à 12:35". + * + * @param context + * a Context + * @param date + * milliseconds since the epoch + * @return the formatted string + */ + public static String formatShortDateTime(Context context, long date) { + return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL); + } + + /** + * TODO This method is only needed until the settings are a singleton + * + * @return + */ + public String getUserName() { + return settings.getUsername(); + } + + /** + * insert text into the EditText at the current cursor position + * + * @param editText + * @param insertText + * @param addSpace + * add a space character, if there is no whitespace before the current cursor position + */ + static void insertAtPosition(final EditText editText, String insertText, final boolean addSpace) { + int selectionStart = editText.getSelectionStart(); + int selectionEnd = editText.getSelectionEnd(); + int start = Math.min(selectionStart, selectionEnd); + int end = Math.max(selectionStart, selectionEnd); + + String content = editText.getText().toString(); + if (start > 0 && !Character.isWhitespace(content.charAt(start - 1))) { + insertText = " " + insertText; + } + + editText.getText().replace(start, end, insertText); + int newCursor = start + insertText.length(); + editText.setSelection(newCursor, newCursor); + } } |
