Created
May 27, 2017 07:10
-
-
Save johan974/88e09992b288560db519946ed8faf129 to your computer and use it in GitHub Desktop.
New logging process
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class OkHttpGeocaching { | |
PersistentCookieStore cookieStore; | |
boolean forceOkHttpLogin = false; | |
String username; | |
String password; | |
boolean forceLogin = false; | |
String gcId; | |
Context context; | |
String status = "not started"; | |
private OkHttpGeocaching() {} | |
public OkHttpGeocaching(Context theContext) { | |
context = theContext; | |
} | |
public String tryOkHttpOauth( String username, String password, boolean forceLogin, String gcId) { | |
this.username = username; | |
this.password = password; | |
this.forceLogin = forceLogin; | |
this.gcId = gcId; | |
try { | |
// ********************************************************************************** | |
// STEP 1 - login with a persistent cookie | |
Request requestShowLoginPage = new Request.Builder(). | |
url(GeocachingComAccess.login_url). | |
addHeader( "User-Agent", GeocachingComAccess.HTTP_USER_AGENT). | |
addHeader( "Pragma", "no-cache"). | |
addHeader( "Accept-Language", "en"). | |
addHeader("Content-Type", "application/x-www-form-urlencoded"). | |
build(); | |
status = "1 - starting"; | |
cookieStore = new PersistentCookieStore( context); | |
CookieManager cookieManager = new CookieManager( cookieStore, CookiePolicy.ACCEPT_ALL); | |
// To be removed in real life. This forces a new login by removing the persistant cookies. | |
cookieStore.removeAll(); | |
OkHttpClient okHttpClient = new OkHttpClient.Builder(). | |
cookieJar(new JavaNetCookieJar(cookieManager)). | |
readTimeout( 20, TimeUnit.SECONDS). | |
connectTimeout( 15, TimeUnit.SECONDS). | |
build(); | |
// Execute the call and receive a 200 | |
Call call = okHttpClient.newCall( requestShowLoginPage); | |
Response response = call.execute(); | |
if( response.code() != 200) { | |
Logger.v("Invalid login status: " + response.code()); | |
status = "2 - no login page"; | |
} else { | |
status = "2 - login page"; | |
} | |
Logger.v( "OkHttp/01: ok"); | |
printCookies( cookieManager); | |
// Check the login page is given with the request token | |
String text = response.body().string(); | |
String requestVerificationToken = HtmlTagRetriever.getHtmlTagValue( text, "input", "name", "__RequestVerificationToken", "value"); | |
if( requestVerificationToken == null || requestVerificationToken.isEmpty()) { | |
String errorMessage = "Could not find the new request verification token"; | |
Logger.v("login: -> " + errorMessage); | |
return "No request verification token"; | |
} | |
status = "2b - request verification token ok"; | |
RequestBody formBody = new FormBody.Builder(). | |
add( "__EVENTTARGET", ""). | |
add( "__EVENTARGUMENT", ""). | |
add( "Username", username). | |
add( "Password", password). | |
add( "__RequestVerificationToken", requestVerificationToken). | |
add( "Submit", "Log In"). | |
add( "ctl00$ContentBody$cbRememberMe", "on").build(); | |
Request requestLogin = new Request.Builder(). | |
url(GeocachingComAccess.login_url). | |
addHeader( "User-Agent", GeocachingComAccess.HTTP_USER_AGENT). | |
addHeader( "Pragma", "no-cache"). | |
addHeader( "Accept-Language", "en"). | |
addHeader("Content-Type", "application/x-www-form-urlencoded"). | |
post( formBody). | |
build(); | |
call = okHttpClient.newCall( requestLogin); | |
response = call.execute(); | |
if( response.code() != 200) { | |
Logger.v("Invalid login status: " + response.code()); | |
status = "3 - Invalid login"; | |
return status; | |
} | |
status = "3 - Valid login page"; | |
Logger.v( "OkHttp/01: ok"); | |
printCookies( cookieManager); | |
// 3 - check the login page is given with the request token | |
text = response.body().string(); | |
if( text.contains( "isLoggedIn: true") == false) { | |
Logger.v( "Is not logged in"); | |
status = "4 - Is not logged in"; | |
} else { | |
status = "4 - Is logged in"; | |
} | |
Logger.v( "OkHttp/02: content = " + text); | |
// ********************************************************************************** | |
// STEP 2 - re-use the persistent cookie + get access to a geocache | |
Request getPageHtml = new Request.Builder(). | |
url(GeocachingComAccess.lookup_url+ "/" + gcId). | |
addHeader( "User-Agent", GeocachingComAccess.HTTP_USER_AGENT). | |
addHeader( "Pragma", "no-cache"). | |
addHeader( "Accept-Language", "en"). | |
addHeader("Content-Type", "application/x-www-form-urlencoded"). | |
build(); | |
call = okHttpClient.newCall( getPageHtml); | |
response = call.execute(); | |
if( response.code() != 200) { | |
status = "5 - Invalid geocache html access: " + response.code(); | |
Logger.v( status); | |
return status; | |
} | |
status = "5 - Valid geocache page access"; | |
Logger.v( status); | |
text = response.body().string(); | |
String geocacheTitle; | |
Pattern pat = Pattern.compile(".*<meta .*?property=\"og:url\" content=\"[^&]+&title=([^&^\"]+).*"); | |
Matcher mat = pat.matcher( text); | |
if (mat.find()) { | |
geocacheTitle = mat.group(1); | |
status = "6 - Geocache html page found"; | |
} else { | |
status = "6 - Geocache html page NOT found"; | |
return status; | |
} | |
// https://www.geocaching.com/geocache/GC19A68_aardig?guid=0eae1de3-74c0-426b-a892-108b9630e2db" | |
// the guid is not needed | |
String urlGcIdAndTitle = GeocachingComAccess.gpx_url + "/" + gcId + "_" + geocacheTitle; | |
FormBody.Builder formBodyBuilder = new FormBody.Builder(); | |
formBodyBuilder.add( "__EVENTTARGET", ""). | |
add( "__EVENTARGUMENT", ""). | |
add( "ctl00$ContentBody$btnGPXDL", "GPX file"); | |
addViewstates( formBodyBuilder, text); | |
Request requestGpxFile = new Request.Builder(). | |
url( urlGcIdAndTitle). | |
addHeader( "User-Agent", GeocachingComAccess.HTTP_USER_AGENT). | |
addHeader( "Pragma", "no-cache"). | |
addHeader( "Accept-Language", "en"). | |
addHeader("Content-Type", "application/x-www-form-urlencoded"). | |
post( formBodyBuilder.build()). | |
build(); | |
call = okHttpClient.newCall( requestGpxFile); | |
response = call.execute(); | |
if( response.code() != 200) { | |
status = "7 - Cannot load GPX file" + response.code(); | |
Logger.v( status ); | |
return status; | |
} | |
status = "7 - Loading GPX file"; | |
text = response.body().string(); | |
if( text.contains( "<gpx xmlns:xsd")) { | |
status = "8 - GPX file is of GPX format"; | |
} else { | |
status = "8 - GPX file is NOT of GPX format"; | |
} | |
// ********************************************************************************** | |
// STEP 3 - Log a geocache | |
// A - build the requeste and get the log page (as a start) | |
long old_gc_gid = gccodeToGCId( gcId); // this method is also used in/by c:geo | |
String logUrl = "https://www.geocaching.com/seek/log.aspx?ID=" + old_gc_gid + "&lcn=1"; | |
// build the GET request | |
Request getLogHtml = new Request.Builder(). | |
url( logUrl). | |
addHeader( "User-Agent", GeocachingComAccess.HTTP_USER_AGENT). | |
addHeader( "Pragma", "no-cache"). | |
addHeader( "Accept-Language", "en"). | |
addHeader( "Content-Type", "application/x-www-form-urlencoded"). | |
build(); | |
call = okHttpClient.newCall( getLogHtml); | |
response = call.execute(); | |
if( response.code() != 200) { | |
status = "9 - Invalid access to log html page: " + response.code(); | |
Logger.v( status); | |
return status; | |
} | |
status = "9 - Valid access to log html page"; | |
Logger.v( status); | |
text = response.body().string(); | |
if( text.contains( "Geocaching - New Log for")) { | |
status = "10 - Valid contents of the log html page"; | |
Logger.v( status); | |
} else { | |
status = "10 - Invalid contents to log html page"; | |
Logger.v( status); | |
return status; | |
} | |
// B - POST for an OATH token: https://www.geocaching.com/account/oauth/token | |
RequestBody oauthFormBody = new FormBody.Builder().build(); // empty | |
Request requestOauthToken = new Request.Builder(). | |
url( "https://www.geocaching.com/account/oauth/token"). | |
addHeader( "User-Agent", GeocachingComAccess.HTTP_USER_AGENT). | |
addHeader( "Pragma", "no-cache"). | |
addHeader( "Accept-Language", "en"). | |
addHeader( "Accept", "application/json, text/javascript, */*; q=0.01"). | |
addHeader("Content-Type", "application/x-www-form-urlencoded"). | |
post( oauthFormBody). | |
build(); | |
call = okHttpClient.newCall( requestOauthToken); | |
response = call.execute(); | |
if( response.code() != 200) { | |
status = "11 - Invalid post for oauth"+ response.code(); | |
Logger.v( status); | |
return status; | |
} | |
status = "11 - Valid post for oauth"; | |
Logger.v( status); | |
text = response.body().string(); | |
JSONObject conversationObject = new JSONObject( text); | |
String accessToken = conversationObject.getString( "access_token"); | |
String tokenType = conversationObject.getString( "token_type"); | |
Logger.v( "Token type = " + tokenType); | |
Logger.v( "Accesws token = " + accessToken); | |
if( accessToken != null && accessToken.contains( "ey")) { | |
status = "12 - Valid access token"; | |
Logger.v( status); | |
} else { | |
status = "12 - Invalid access token"; | |
Logger.v( status); | |
return status; | |
} | |
// STEP 3 - POST voor de log https://www.geocaching.com/api/proxy/web/v1/Geocache/GC19A68/GeocacheLog | |
FormBody logFormBody = new FormBody.Builder(). | |
add( "logTextMaxLength", "4000"). // may not be needed. | |
add( "maxImages","1"). // may not be needed. | |
add( "geocache[id]", "" + old_gc_gid). | |
add( "geocache[referenceCode]", gcId). | |
add( "geocache[postedCoordinates][latitude]", "52.23305"). // for testing just hard coded. Normally available in the geocache | |
add( "geocache[postedCoordinates][longitude]", "6.114983"). | |
add( "geocache[callerSpecific][favorited]", "false"). | |
add( "geocache[owner][id]", "" + 1138217). // owner of the geocache | |
// add( "geocache[owner][referenceCode]", "PR1N06K"). // via the base32/64 ... from id to reference code | |
add( "geocache[geocacheType][id]", "" + 8). // here for testing. Normally available in the geocache | |
add( "geocache[geocacheType][name]", "Unknown+Cache"). // here for testing. Normally available in the geocache | |
add( "geocache[isEvent]", "false"). // based on the actual geocache type | |
/* DEPENDING ... if you are the user, you will see different fields | |
add( "logTypes[0][value]", "46"). for the caches of yourself | |
add( "logTypes[0][name]", "Owner+maintenance"). | |
add( "logTypes[0][selected]", "false"). | |
add( "logTypes[1][value]", "4"). | |
add( "logTypes[1][name]", "Write+note"). | |
add( "logTypes[1][selected]", "true"). | |
*/ | |
add( "logTypes[0][value]", "2"). // for geocaches not owned by yourself | |
add( "logTypes[0][name]", "Found+It"). | |
add( "logTypes[0][selected]", "true"). | |
add( "logTypes[1][value]", "3"). | |
add( "logTypes[1][name]", "Didn't+Find+It"). | |
add( "logTypes[1][selected]", "false"). | |
add( "logTypes[2][value]", "4"). | |
add( "logTypes[2][name]", "Write+note"). | |
add( "logTypes[2][selected]", "false"). | |
add( "logType", "2"). // redundant, again the type 2 (as above) is chosen. | |
// add( "ownerIsViewing", "true"). // if you are the owner of the geocache. Available in the geocache info. | |
add( "ownerIsViewing", "false"). // @for not owner | |
add( "logDate", "2017-05-25"). // log date, watch for the format ! | |
add( "logText", "Leuke+geocache+met+een+testbericht+versie2" /*+ new SimpleDateFormat( "yyyy-MM-dd_kk_mm_ss").format( new Date())*/). | |
add( "isWaiting", "true").build(); | |
// TAKE CARE: no viewstates were used here. In some old parts they are still used. | |
Request requestLog = new Request.Builder(). | |
url( "https://www.geocaching.com/api/proxy/web/v1/Geocache/" + gcId + "/GeocacheLog"). | |
addHeader( "User-Agent", GeocachingComAccess.HTTP_USER_AGENT). | |
addHeader( "Pragma", "no-cache"). | |
addHeader( "Accept-Language", "en"). | |
addHeader( "Accept", "*/*"). | |
addHeader( "Referer", "https://www.geocaching.com/play/geocache/gc19a68/log"). // normally put in by OkHttp, otherwise you can fit it in. Just test. | |
addHeader( "Authorization", "bearer " + accessToken). // A VERY IMPORTANT LINE !!!!!! | |
addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"). | |
post( logFormBody). | |
build(); | |
call = okHttpClient.newCall( requestLog); | |
response = call.execute(); | |
if( response.code() != 200) { | |
status = "13 - Cannot log the request" + response.code(); | |
Logger.v( status ); | |
return status; | |
} | |
status = "13 - Log request honored!!"; | |
return status; | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
// Step 3 - log the geocache | |
return status; | |
} | |
void printCookies( CookieManager cookieManager) { | |
if( cookieManager == null) { | |
Logger.v("Cookies: null"); | |
return ; | |
} | |
List<HttpCookie> cookies = cookieManager.getCookieStore().getCookies(); | |
if( cookies == null) { | |
Logger.v("Cookies: none"); | |
return ; | |
} | |
for( HttpCookie cookie : cookies) { | |
Logger.v(" > cookie: url " + cookie.getCommentURL() + ", name = " + cookie.getName()); | |
} | |
} | |
/* | |
View states | |
*/ | |
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 void addViewstates( FormBody.Builder formBodyBuilder, String webPage) { | |
String[] viewStates = getViewstates( webPage); | |
if( isEmpty( viewStates)) { | |
return; // no updates | |
} | |
formBodyBuilder.add( "__VIEWSTATE", viewStates[0]); | |
if (viewStates.length > 1) { | |
for (int i = 1; i < viewStates.length; i++) { | |
formBodyBuilder.add( "__VIEWSTATE" + i, viewStates[i]); | |
} | |
formBodyBuilder.add( "__VIEWSTATEFIELDCOUNT", viewStates.length + ""); | |
} | |
} | |
private 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]; | |
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); | |
} | |
// System.out.println("1 v " + no + "=" + matcherViewstates.group(2)); | |
viewstates[no] = matcherViewstates.group(2); | |
} | |
return viewstates; | |
} | |
private boolean isEmpty(String[] a) { | |
if( a == null) { | |
return true; | |
} | |
for( int i = 0; i < a.length; i++) { | |
if((a[i] == null) || (a[i].equals(""))) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/* | |
OLD GCID | |
*/ | |
private final static String SEQUENCE_GCID = "0123456789ABCDEFGHJKMNPQRTVWXYZ"; | |
private final static long GC_BASE31 = 31; | |
private final static long GC_BASE16 = 16; | |
public long gccodeToGCId( final String gccode) { | |
long base = GC_BASE31; | |
final String geocodeWO = gccode.substring(2).toUpperCase(Locale.US); | |
if ((geocodeWO.length() < 4) || (geocodeWO.length() == 4 && SEQUENCE_GCID.indexOf(geocodeWO.charAt(0)) < 16)) { | |
base = GC_BASE16; | |
} | |
long gcid = 0; | |
for (int p = 0; p < geocodeWO.length(); p++) { | |
gcid = base * gcid + SEQUENCE_GCID.indexOf(geocodeWO.charAt(p)); | |
} | |
if (base == GC_BASE31) { | |
gcid += Math.pow(16, 4) - 16 * Math.pow(31, 3); | |
} | |
return gcid; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This code demonstrates the new logging approach. This isolated code is based on OkHttp. It shows that logging is quite easy to do. | |
This code snippet is just for proofing that it works. The complete test (from sign-in, get geocache file, get GPX and logging) is added as a file. | |
// STEP 3 - Log a geocache | |
// A - build the requeste and get the log page (as a start) | |
long old_gc_gid = gccodeToGCId( gcId); // this method is also used in/by c:geo | |
String logUrl = "https://www.geocaching.com/seek/log.aspx?ID=" + old_gc_gid + "&lcn=1"; | |
// build the GET request | |
Request getLogHtml = new Request.Builder(). | |
url( logUrl). | |
addHeader( "User-Agent", GeocachingComAccess.HTTP_USER_AGENT). | |
addHeader( "Pragma", "no-cache"). | |
addHeader( "Accept-Language", "en"). | |
addHeader( "Content-Type", "application/x-www-form-urlencoded"). | |
build(); | |
call = okHttpClient.newCall( getLogHtml); | |
response = call.execute(); | |
if( response.code() != 200) { | |
status = "9 - Invalid access to log html page: " + response.code(); | |
Logger.v( status); | |
return status; | |
} | |
status = "9 - Valid access to log html page"; | |
Logger.v( status); | |
text = response.body().string(); | |
if( text.contains( "Geocaching - New Log for")) { | |
status = "10 - Valid contents of the log html page"; | |
Logger.v( status); | |
} else { | |
status = "10 - Invalid contents to log html page"; | |
Logger.v( status); | |
return status; | |
} | |
// B - POST for an OATH token: https://www.geocaching.com/account/oauth/token | |
RequestBody oauthFormBody = new FormBody.Builder().build(); // empty | |
Request requestOauthToken = new Request.Builder(). | |
url( "https://www.geocaching.com/account/oauth/token"). | |
addHeader( "User-Agent", GeocachingComAccess.HTTP_USER_AGENT). | |
addHeader( "Pragma", "no-cache"). | |
addHeader( "Accept-Language", "en"). | |
addHeader( "Accept", "application/json, text/javascript, */*; q=0.01"). | |
addHeader("Content-Type", "application/x-www-form-urlencoded"). | |
post( oauthFormBody). | |
build(); | |
call = okHttpClient.newCall( requestOauthToken); | |
response = call.execute(); | |
if( response.code() != 200) { | |
status = "11 - Invalid post for oauth"+ response.code(); | |
Logger.v( status); | |
return status; | |
} | |
status = "11 - Valid post for oauth"; | |
Logger.v( status); | |
text = response.body().string(); | |
JSONObject conversationObject = new JSONObject( text); | |
String accessToken = conversationObject.getString( "access_token"); | |
String tokenType = conversationObject.getString( "token_type"); | |
Logger.v( "Token type = " + tokenType); | |
Logger.v( "Accesws token = " + accessToken); | |
if( accessToken != null && accessToken.contains( "ey")) { | |
status = "12 - Valid access token"; | |
Logger.v( status); | |
} else { | |
status = "12 - Invalid access token"; | |
Logger.v( status); | |
return status; | |
} | |
// STEP 3 - POST voor de log https://www.geocaching.com/api/proxy/web/v1/Geocache/GC19A68/GeocacheLog | |
FormBody logFormBody = new FormBody.Builder(). | |
add( "logTextMaxLength", "4000"). // may not be needed. | |
add( "maxImages","1"). // may not be needed. | |
add( "geocache[id]", "" + old_gc_gid). | |
add( "geocache[referenceCode]", gcId). | |
add( "geocache[postedCoordinates][latitude]", "52.23305"). // for testing just hard coded. Normally available in the geocache | |
add( "geocache[postedCoordinates][longitude]", "6.114983"). | |
add( "geocache[callerSpecific][favorited]", "false"). | |
add( "geocache[owner][id]", "" + 1138217). // owner of the geocache | |
// add( "geocache[owner][referenceCode]", "PR1N06K"). // via the base32/64 ... from id to reference code | |
add( "geocache[geocacheType][id]", "" + 8). // here for testing. Normally available in the geocache | |
add( "geocache[geocacheType][name]", "Unknown+Cache"). // here for testing. Normally available in the geocache | |
add( "geocache[isEvent]", "false"). // based on the actual geocache type | |
/* DEPENDING ... if you are the user, you will see different fields | |
add( "logTypes[0][value]", "46"). for the caches of yourself | |
add( "logTypes[0][name]", "Owner+maintenance"). | |
add( "logTypes[0][selected]", "false"). | |
add( "logTypes[1][value]", "4"). | |
add( "logTypes[1][name]", "Write+note"). | |
add( "logTypes[1][selected]", "true"). | |
*/ | |
add( "logTypes[0][value]", "2"). // for geocaches not owned by yourself | |
add( "logTypes[0][name]", "Found+It"). | |
add( "logTypes[0][selected]", "true"). | |
add( "logTypes[1][value]", "3"). | |
add( "logTypes[1][name]", "Didn't+Find+It"). | |
add( "logTypes[1][selected]", "false"). | |
add( "logTypes[2][value]", "4"). | |
add( "logTypes[2][name]", "Write+note"). | |
add( "logTypes[2][selected]", "false"). | |
add( "logType", "2"). // redundant, again the type 2 (as above) is chosen. | |
// add( "ownerIsViewing", "true"). // if you are the owner of the geocache. Available in the geocache info. | |
add( "ownerIsViewing", "false"). // @for not owner | |
add( "logDate", "2017-05-25"). // log date, watch for the format ! | |
add( "logText", "Leuke+geocache+met+een+testbericht+versie2" /*+ new SimpleDateFormat( "yyyy-MM-dd_kk_mm_ss").format( new Date())*/). | |
add( "isWaiting", "true").build(); | |
// TAKE CARE: no viewstates were used here. In some old parts they are still used. | |
Request requestLog = new Request.Builder(). | |
url( "https://www.geocaching.com/api/proxy/web/v1/Geocache/" + gcId + "/GeocacheLog"). | |
addHeader( "User-Agent", GeocachingComAccess.HTTP_USER_AGENT). | |
addHeader( "Pragma", "no-cache"). | |
addHeader( "Accept-Language", "en"). | |
addHeader( "Accept", "*/*"). | |
addHeader( "Referer", "https://www.geocaching.com/play/geocache/gc19a68/log"). // normally put in by OkHttp, otherwise you can fit it in. Just test. | |
addHeader( "Authorization", "bearer " + accessToken). // A VERY IMPORTANT LINE !!!!!! | |
addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"). | |
post( logFormBody). | |
build(); | |
call = okHttpClient.newCall( requestLog); | |
response = call.execute(); | |
if( response.code() != 200) { | |
status = "13 - Cannot log the request" + response.code(); | |
Logger.v( status ); | |
return status; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment