diff options
author | Vincent Breitmoser <valodim@mugenguild.com> | 2015-08-29 13:28:56 +0200 |
---|---|---|
committer | Vincent Breitmoser <valodim@mugenguild.com> | 2015-08-29 13:28:56 +0200 |
commit | a6e25e6448ab162b351288ee0c241512e05c3611 (patch) | |
tree | d7eec4fcd2d98bc9d00e880ab3c103c86a8edbe2 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources | |
parent | 765ec094c9415fcaddd65b7b743179b2ea7dc098 (diff) | |
parent | 5b75b542e8d2d467ce9e34bd9df2038d6c88885e (diff) | |
download | open-keychain-a6e25e6448ab162b351288ee0c241512e05c3611.tar.gz open-keychain-a6e25e6448ab162b351288ee0c241512e05c3611.tar.bz2 open-keychain-a6e25e6448ab162b351288ee0c241512e05c3611.zip |
Merge branch 'linked-identities' (and fix OperationHelper ids)
Merge Linked Identities.
Also includes an important fix for OperationHelper ids, which had an
error in the bit mask logic.
Conflicts:
Graphics/update-drawables.sh
OpenKeychain/build.gradle
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportFragment.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java
OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java
OpenKeychain/src/main/res/anim/fade_in.xml
OpenKeychain/src/main/res/anim/fade_out.xml
OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml
OpenKeychain/src/main/res/layout/encrypt_decrypt_overview_fragment.xml
OpenKeychain/src/main/res/layout/view_key_fragment.xml
OpenKeychain/src/main/res/menu/key_view.xml
OpenKeychain/src/main/res/values/strings.xml
OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
README.md
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources')
4 files changed, 692 insertions, 0 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java new file mode 100644 index 000000000..86b672cc1 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java @@ -0,0 +1,130 @@ +package org.sufficientlysecure.keychain.linked.resources; + +import android.content.Context; +import android.support.annotation.DrawableRes; +import android.support.annotation.StringRes; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.linked.LinkedTokenResource; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; + +import java.net.URI; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.measite.minidns.Client; +import de.measite.minidns.DNSMessage; +import de.measite.minidns.Question; +import de.measite.minidns.Record; +import de.measite.minidns.Record.CLASS; +import de.measite.minidns.Record.TYPE; +import de.measite.minidns.record.TXT; + +public class DnsResource extends LinkedTokenResource { + + final static Pattern magicPattern = + Pattern.compile("openpgpid\\+token=([a-zA-Z0-9]+)(?:#|;)([a-zA-Z0-9]+)"); + + String mFqdn; + CLASS mClass; + TYPE mType; + + DnsResource(Set<String> flags, HashMap<String, String> params, URI uri, + String fqdn, CLASS clazz, TYPE type) { + super(flags, params, uri); + + mFqdn = fqdn; + mClass = clazz; + mType = type; + } + + public static String generateText(byte[] fingerprint) { + + return String.format("openpgp4fpr=%s", + KeyFormattingUtils.convertFingerprintToHex(fingerprint)); + + } + + public static DnsResource createNew (String domain) { + HashSet<String> flags = new HashSet<>(); + HashMap<String,String> params = new HashMap<>(); + URI uri = URI.create("dns:" + domain + "?TYPE=TXT"); + return create(flags, params, uri); + } + + public static DnsResource create(Set<String> flags, HashMap<String,String> params, URI uri) { + if ( ! ("dns".equals(uri.getScheme()) + && (flags == null || flags.isEmpty()) + && (params == null || params.isEmpty()))) { + return null; + } + + // + String spec = uri.getSchemeSpecificPart(); + // If there are // at the beginning, this includes an authority - we don't support those! + if (spec.startsWith("//")) { + return null; + } + + String[] pieces = spec.split("\\?", 2); + // In either case, part before a ? is the fqdn + String fqdn = pieces[0]; + // There may be a query part + /* + if (pieces.length > 1) { + // parse CLASS and TYPE query paramters + } + */ + + CLASS clazz = CLASS.IN; + TYPE type = TYPE.TXT; + + return new DnsResource(flags, params, uri, fqdn, clazz, type); + } + + @SuppressWarnings("unused") + public String getFqdn() { + return mFqdn; + } + + @Override + protected String fetchResource (Context context, OperationLog log, int indent) { + + Client c = new Client(); + DNSMessage msg = c.query(new Question(mFqdn, mType, mClass)); + Record aw = msg.getAnswers()[0]; + TXT txt = (TXT) aw.getPayload(); + return txt.getText().toLowerCase(); + + } + + @Override + protected Matcher matchResource(OperationLog log, int indent, String res) { + return magicPattern.matcher(res); + } + + @Override + public @StringRes + int getVerifiedText(boolean isSecret) { + return isSecret ? R.string.linked_verified_secret_dns : R.string.linked_verified_dns; + } + + @Override + public @DrawableRes int getDisplayIcon() { + return R.drawable.linked_dns; + } + + @Override + public String getDisplayTitle(Context context) { + return context.getString(R.string.linked_title_dns); + } + + @Override + public String getDisplayComment(Context context) { + return mFqdn; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java new file mode 100644 index 000000000..82240c405 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java @@ -0,0 +1,95 @@ +package org.sufficientlysecure.keychain.linked.resources; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.support.annotation.DrawableRes; +import android.support.annotation.StringRes; + +import org.apache.http.client.methods.HttpGet; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.linked.LinkedTokenResource; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; + +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +public class GenericHttpsResource extends LinkedTokenResource { + + GenericHttpsResource(Set<String> flags, HashMap<String,String> params, URI uri) { + super(flags, params, uri); + } + + public static String generateText (Context context, byte[] fingerprint) { + String token = LinkedTokenResource.generate(fingerprint); + + return String.format(context.getResources().getString(R.string.linked_id_generic_text), + token, "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint).substring(24)); + } + + @SuppressWarnings("deprecation") // HttpGet is deprecated + @Override + protected String fetchResource (Context context, OperationLog log, int indent) + throws HttpStatusException, IOException { + + log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); + HttpGet httpGet = new HttpGet(mSubUri); + return getResponseBody(context, httpGet); + + } + + public static GenericHttpsResource createNew (URI uri) { + HashSet<String> flags = new HashSet<>(); + flags.add("generic"); + HashMap<String,String> params = new HashMap<>(); + return create(flags, params, uri); + } + + public static GenericHttpsResource create(Set<String> flags, HashMap<String,String> params, URI uri) { + if ( ! ("https".equals(uri.getScheme()) + && flags != null && flags.size() == 1 && flags.contains("generic") + && (params == null || params.isEmpty()))) { + return null; + } + return new GenericHttpsResource(flags, params, uri); + } + + @Override + public @DrawableRes + int getDisplayIcon() { + return R.drawable.linked_https; + } + + @Override + public @StringRes + int getVerifiedText(boolean isSecret) { + return isSecret ? R.string.linked_verified_secret_https : R.string.linked_verified_https; + } + + @Override + public String getDisplayTitle(Context context) { + return context.getString(R.string.linked_title_https); + } + + @Override + public String getDisplayComment(Context context) { + return mSubUri.toString(); + } + + @Override + public boolean isViewable() { + return true; + } + + @Override + public Intent getViewIntent() { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(mSubUri.toString())); + return intent; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java new file mode 100644 index 000000000..30ce075b4 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java @@ -0,0 +1,217 @@ +package org.sufficientlysecure.keychain.linked.resources; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.support.annotation.DrawableRes; +import android.support.annotation.StringRes; + +import org.apache.http.client.methods.HttpGet; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.linked.LinkedTokenResource; +import org.sufficientlysecure.keychain.util.Log; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class GithubResource extends LinkedTokenResource { + + final String mHandle; + final String mGistId; + + GithubResource(Set<String> flags, HashMap<String,String> params, URI uri, + String handle, String gistId) { + super(flags, params, uri); + + mHandle = handle; + mGistId = gistId; + } + + public static String generate(Context context, byte[] fingerprint) { + String token = LinkedTokenResource.generate(fingerprint); + + return String.format(context.getResources().getString(R.string.linked_id_github_text), token); + } + + @SuppressWarnings("deprecation") // HttpGet is deprecated + @Override + protected String fetchResource (Context context, OperationLog log, int indent) + throws HttpStatusException, IOException, JSONException { + + log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); + indent += 1; + + HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + mGistId); + String response = getResponseBody(context, httpGet); + + JSONObject obj = new JSONObject(response); + + JSONObject owner = obj.getJSONObject("owner"); + if (!mHandle.equals(owner.getString("login"))) { + log.add(LogType.MSG_LV_ERROR_GITHUB_HANDLE, indent); + return null; + } + + JSONObject files = obj.getJSONObject("files"); + Iterator<String> it = files.keys(); + if (it.hasNext()) { + // TODO can there be multiple candidates? + JSONObject file = files.getJSONObject(it.next()); + return file.getString("content"); + } + + log.add(LogType.MSG_LV_ERROR_GITHUB_NOT_FOUND, indent); + return null; + + } + + @SuppressWarnings("deprecation") + public static GithubResource searchInGithubStream( + Context context, String screenName, String needle, OperationLog log) { + + // narrow the needle down to important part + Matcher matcher = magicPattern.matcher(needle); + if (!matcher.find()) { + throw new AssertionError("Needle must contain token pattern! This is a programming error, please report."); + } + needle = matcher.group(); + + try { + + JSONArray array; { + HttpGet httpGet = + new HttpGet("https://api.github.com/users/" + screenName + "/gists"); + httpGet.setHeader("Content-Type", "application/json"); + httpGet.setHeader("User-Agent", "OpenKeychain"); + + String response = getResponseBody(context, httpGet); + array = new JSONArray(response); + } + + for (int i = 0, j = Math.min(array.length(), 5); i < j; i++) { + JSONObject obj = array.getJSONObject(i); + + JSONObject files = obj.getJSONObject("files"); + Iterator<String> it = files.keys(); + if (it.hasNext()) { + + JSONObject file = files.getJSONObject(it.next()); + String type = file.getString("type"); + if (!"text/plain".equals(type)) { + continue; + } + String id = obj.getString("id"); + HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + id); + httpGet.setHeader("User-Agent", "OpenKeychain"); + + JSONObject gistObj = new JSONObject(getResponseBody(context, httpGet)); + JSONObject gistFiles = gistObj.getJSONObject("files"); + Iterator<String> gistIt = gistFiles.keys(); + if (!gistIt.hasNext()) { + continue; + } + // TODO can there be multiple candidates? + JSONObject gistFile = gistFiles.getJSONObject(gistIt.next()); + String content = gistFile.getString("content"); + if (!content.contains(needle)) { + continue; + } + + URI uri = URI.create("https://gist.github.com/" + screenName + "/" + id); + return create(uri); + } + } + + // update the results with the body of the response + log.add(LogType.MSG_LV_FETCH_ERROR_NOTHING, 2); + return null; + + } catch (HttpStatusException e) { + // log verbose output to logcat + Log.e(Constants.TAG, "http error (" + e.getStatus() + "): " + e.getReason()); + log.add(LogType.MSG_LV_FETCH_ERROR, 2, Integer.toString(e.getStatus())); + } catch (MalformedURLException e) { + log.add(LogType.MSG_LV_FETCH_ERROR_URL, 2); + } catch (IOException e) { + Log.e(Constants.TAG, "io error", e); + log.add(LogType.MSG_LV_FETCH_ERROR_IO, 2); + } catch (JSONException e) { + Log.e(Constants.TAG, "json error", e); + log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, 2); + } + + return null; + } + + public static GithubResource create(URI uri) { + return create(new HashSet<String>(), new HashMap<String,String>(), uri); + } + + public static GithubResource create(Set<String> flags, HashMap<String,String> params, URI uri) { + + // no params or flags + if (!flags.isEmpty() || !params.isEmpty()) { + return null; + } + + Pattern p = Pattern.compile("https://gist\\.github\\.com/([a-zA-Z0-9_]+)/([0-9a-f]+)"); + Matcher match = p.matcher(uri.toString()); + if (!match.matches()) { + return null; + } + String handle = match.group(1); + String gistId = match.group(2); + + return new GithubResource(flags, params, uri, handle, gistId); + + } + + + @Override + public @DrawableRes + int getDisplayIcon() { + return R.drawable.linked_github; + } + + @Override + public @StringRes + int getVerifiedText(boolean isSecret) { + return isSecret ? R.string.linked_verified_secret_github : R.string.linked_verified_github; + } + + @Override + public String getDisplayTitle(Context context) { + return context.getString(R.string.linked_title_github); + } + + @Override + public String getDisplayComment(Context context) { + return mHandle; + } + + @Override + public boolean isViewable() { + return true; + } + + @Override + public Intent getViewIntent() { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(mSubUri.toString())); + return intent; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java new file mode 100644 index 000000000..73e3d3643 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java @@ -0,0 +1,250 @@ +package org.sufficientlysecure.keychain.linked.resources; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.support.annotation.DrawableRes; +import android.support.annotation.StringRes; +import android.util.Log; + +import com.textuality.keybase.lib.JWalk; + +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.linked.LinkedTokenResource; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TwitterResource extends LinkedTokenResource { + + public static final String[] CERT_PINS = null; /*(new String[] { + // Symantec Class 3 Secure Server CA - G4 + "513fb9743870b73440418d30930699ff" + };*/ + + final String mHandle; + final String mTweetId; + + TwitterResource(Set<String> flags, HashMap<String,String> params, + URI uri, String handle, String tweetId) { + super(flags, params, uri); + + mHandle = handle; + mTweetId = tweetId; + } + + public static TwitterResource create(URI uri) { + return create(new HashSet<String>(), new HashMap<String,String>(), uri); + } + + public static TwitterResource create(Set<String> flags, HashMap<String,String> params, URI uri) { + + // no params or flags + if (!flags.isEmpty() || !params.isEmpty()) { + return null; + } + + Pattern p = Pattern.compile("https://twitter\\.com/([a-zA-Z0-9_]+)/status/([0-9]+)"); + Matcher match = p.matcher(uri.toString()); + if (!match.matches()) { + return null; + } + String handle = match.group(1); + String tweetId = match.group(2); + + return new TwitterResource(flags, params, uri, handle, tweetId); + + } + + @SuppressWarnings("deprecation") + @Override + protected String fetchResource(Context context, OperationLog log, int indent) + throws IOException, HttpStatusException, JSONException { + + String authToken; + try { + authToken = getAuthToken(context); + } catch (IOException | HttpStatusException | JSONException e) { + log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, indent); + return null; + } + + HttpGet httpGet = + new HttpGet("https://api.twitter.com/1.1/statuses/show.json" + + "?id=" + mTweetId + + "&include_entities=false"); + + // construct a normal HTTPS request and include an Authorization + // header with the value of Bearer <> + httpGet.setHeader("Authorization", "Bearer " + authToken); + httpGet.setHeader("Content-Type", "application/json"); + + try { + String response = getResponseBody(context, httpGet, CERT_PINS); + JSONObject obj = new JSONObject(response); + JSONObject user = obj.getJSONObject("user"); + if (!mHandle.equalsIgnoreCase(user.getString("screen_name"))) { + log.add(LogType.MSG_LV_ERROR_TWITTER_HANDLE, indent); + return null; + } + + // update the results with the body of the response + return obj.getString("text"); + } catch (JSONException e) { + log.add(LogType.MSG_LV_ERROR_TWITTER_RESPONSE, indent); + return null; + } + + } + + @Override + public @DrawableRes int getDisplayIcon() { + return R.drawable.linked_twitter; + } + + @Override + public @StringRes + int getVerifiedText(boolean isSecret) { + return isSecret ? R.string.linked_verified_secret_twitter : R.string.linked_verified_twitter; + } + + @Override + public String getDisplayTitle(Context context) { + return context.getString(R.string.linked_title_twitter); + } + + @Override + public String getDisplayComment(Context context) { + return "@" + mHandle; + } + + @Override + public boolean isViewable() { + return true; + } + + @Override + public Intent getViewIntent() { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(mSubUri.toString())); + return intent; + } + + @SuppressWarnings("deprecation") + public static TwitterResource searchInTwitterStream( + Context context, String screenName, String needle, OperationLog log) { + + String authToken; + try { + authToken = getAuthToken(context); + } catch (IOException | HttpStatusException | JSONException e) { + log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, 1); + return null; + } + + HttpGet httpGet = + new HttpGet("https://api.twitter.com/1.1/statuses/user_timeline.json" + + "?screen_name=" + screenName + + "&count=15" + + "&include_rts=false" + + "&trim_user=true" + + "&exclude_replies=true"); + + // construct a normal HTTPS request and include an Authorization + // header with the value of Bearer <> + httpGet.setHeader("Authorization", "Bearer " + authToken); + httpGet.setHeader("Content-Type", "application/json"); + + try { + String response = getResponseBody(context, httpGet, CERT_PINS); + JSONArray array = new JSONArray(response); + + for (int i = 0; i < array.length(); i++) { + JSONObject obj = array.getJSONObject(i); + String tweet = obj.getString("text"); + if (tweet.contains(needle)) { + String id = obj.getString("id_str"); + URI uri = URI.create("https://twitter.com/" + screenName + "/status/" + id); + return create(uri); + } + } + + // update the results with the body of the response + log.add(LogType.MSG_LV_FETCH_ERROR_NOTHING, 1); + return null; + + } catch (HttpStatusException e) { + // log verbose output to logcat + Log.e(Constants.TAG, "http error (" + e.getStatus() + "): " + e.getReason()); + log.add(LogType.MSG_LV_FETCH_ERROR, 1, Integer.toString(e.getStatus())); + } catch (MalformedURLException e) { + log.add(LogType.MSG_LV_FETCH_ERROR_URL, 1); + } catch (IOException e) { + Log.e(Constants.TAG, "io error", e); + log.add(LogType.MSG_LV_FETCH_ERROR_IO, 1); + } catch (JSONException e) { + Log.e(Constants.TAG, "json error", e); + log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, 1); + } + + return null; + } + + private static String cachedAuthToken; + + @SuppressWarnings("deprecation") + private static String getAuthToken(Context context) + throws IOException, HttpStatusException, JSONException { + if (cachedAuthToken != null) { + return cachedAuthToken; + } + String base64Encoded = rot13("D293FQqanH0jH29KIaWJER5DomqSGRE2Ewc1LJACn3cbD1c" + + "Fq1bmqSAQAz5MI2cIHKOuo3cPoRAQI1OyqmIVFJS6LHMXq2g6MRLkIj") + "=="; + + // Step 2: Obtain a bearer token + HttpPost httpPost = new HttpPost("https://api.twitter.com/oauth2/token"); + httpPost.setHeader("Authorization", "Basic " + base64Encoded); + httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); + httpPost.setEntity(new StringEntity("grant_type=client_credentials")); + JSONObject rawAuthorization = new JSONObject(getResponseBody(context, httpPost, CERT_PINS)); + + // Applications should verify that the value associated with the + // token_type key of the returned object is bearer + if (!"bearer".equals(JWalk.getString(rawAuthorization, "token_type"))) { + throw new JSONException("Expected bearer token in response!"); + } + + cachedAuthToken = rawAuthorization.getString("access_token"); + return cachedAuthToken; + + } + + public static String rot13(String input) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c >= 'a' && c <= 'm') c += 13; + else if (c >= 'A' && c <= 'M') c += 13; + else if (c >= 'n' && c <= 'z') c -= 13; + else if (c >= 'N' && c <= 'Z') c -= 13; + sb.append(c); + } + return sb.toString(); + } + +} |