package com.evernote.client.android; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.text.TextUtils; import com.evernote.client.android.helper.Cat; import com.evernote.client.android.helper.EvernotePreconditions; import com.evernote.edam.userstore.BootstrapInfo; import com.evernote.edam.userstore.BootstrapProfile; import org.scribe.builder.ServiceBuilder; import org.scribe.builder.api.Api; import org.scribe.builder.api.EvernoteApi; import org.scribe.exceptions.OAuthException; import org.scribe.model.Token; import org.scribe.model.Verifier; import org.scribe.oauth.OAuthService; import org.scribe.utils.OAuthEncoder; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A helper class to handle OAuth requests. * * @author rwondratschek */ @SuppressWarnings("UnusedDeclaration") public class EvernoteOAuthHelper { /** * Server matched name for BootstrapProfile that matches china. */ public static final String CHINA_PROFILE_NAME = "Evernote-China"; protected static final String CALLBACK_SCHEME = "en-oauth"; protected static final Cat CAT = new Cat("OAuthHelper"); protected static final Pattern NOTE_STORE_REGEX = Pattern.compile("edam_noteStoreUrl=([^&]+)"); protected static final Pattern WEB_API_REGEX = Pattern.compile("edam_webApiUrlPrefix=([^&]+)"); protected static final Pattern USER_ID_REGEX = Pattern.compile("edam_userId=([^&]+)"); protected final EvernoteSession mSession; protected final String mConsumerKey; protected final String mConsumerSecret; protected final boolean mSupportAppLinkedNotebooks; protected final Locale mLocale; protected BootstrapProfile mBootstrapProfile; protected OAuthService mOAuthService; protected Token mRequestToken; public EvernoteOAuthHelper(EvernoteSession session, String consumerKey, String consumerSecret, boolean supportAppLinkedNotebooks) { this(session, consumerKey, consumerSecret, supportAppLinkedNotebooks, Locale.getDefault()); } public EvernoteOAuthHelper(EvernoteSession session, String consumerKey, String consumerSecret, boolean supportAppLinkedNotebooks, Locale locale) { mSession = EvernotePreconditions.checkNotNull(session); mConsumerKey = EvernotePreconditions.checkNotEmpty(consumerKey); mConsumerSecret = EvernotePreconditions.checkNotEmpty(consumerSecret); mSupportAppLinkedNotebooks = supportAppLinkedNotebooks; mLocale = EvernotePreconditions.checkNotNull(locale); } public List<BootstrapProfile> fetchBootstrapProfiles() throws Exception { //Network request BootstrapManager.BootstrapInfoWrapper infoWrapper = new BootstrapManager(mSession.getEvernoteService(), mSession, mLocale).getBootstrapInfo(); if (infoWrapper == null) { return null; } BootstrapInfo info = infoWrapper.getBootstrapInfo(); if (info == null) { return null; } return info.getProfiles(); } public BootstrapProfile getDefaultBootstrapProfile(List<BootstrapProfile> bootstrapProfiles) { EvernotePreconditions.checkCollectionNotEmpty(bootstrapProfiles, "bootstrapProfiles"); // return the first in the list, this is the preferred profile from the server return bootstrapProfiles.get(0); } public void setBootstrapProfile(BootstrapProfile bootstrapProfile) { mBootstrapProfile = EvernotePreconditions.checkNotNull(bootstrapProfile); } public void initialize() throws Exception { if (mBootstrapProfile == null) { List<BootstrapProfile> bootstrapProfiles = fetchBootstrapProfiles(); setBootstrapProfile(getDefaultBootstrapProfile(bootstrapProfiles)); } mOAuthService = createOAuthService(mBootstrapProfile, mConsumerKey, mConsumerSecret); } public Token createRequestToken() { mRequestToken = mOAuthService.getRequestToken(); return mRequestToken; } public String createAuthorizationUrl(Token requestToken) { String url = mOAuthService.getAuthorizationUrl(requestToken); if (mSupportAppLinkedNotebooks) { url += "&supportLinkedSandbox=true"; } return url; } public Intent startAuthorization(Activity activity) { try { initialize(); } catch (Exception e) { CAT.e(e); return null; } createRequestToken(); String authorizationUrl = createAuthorizationUrl(mRequestToken); return EvernoteUtil.createAuthorizationIntent(activity, authorizationUrl, mSession.isForceAuthenticationInThirdPartyApp()); } public boolean finishAuthorization(Activity activity, int resultCode, Intent data) { if (resultCode != Activity.RESULT_OK || data == null) { return false; } String url = data.getStringExtra(EvernoteUtil.EXTRA_OAUTH_CALLBACK_URL); if (TextUtils.isEmpty(url)) { return false; } Uri uri = Uri.parse(url); String verifierString = uri.getQueryParameter("oauth_verifier"); String appLnbString = uri.getQueryParameter("sandbox_lnb"); boolean isAppLinkedNotebook = !TextUtils.isEmpty(appLnbString) && "true".equalsIgnoreCase(appLnbString); if (TextUtils.isEmpty(verifierString)) { CAT.i("User did not authorize access"); return false; } Verifier verifier = new Verifier(verifierString); try { Token accessToken = mOAuthService.getAccessToken(mRequestToken, verifier); String rawResponse = accessToken.getRawResponse(); String authToken = accessToken.getToken(); String noteStoreUrl = extract(rawResponse, NOTE_STORE_REGEX); String webApiUrlPrefix = extract(rawResponse, WEB_API_REGEX); int userId = Integer.parseInt(extract(rawResponse, USER_ID_REGEX)); String evernoteHost = mBootstrapProfile.getSettings().getServiceHost(); AuthenticationResult authenticationResult = new AuthenticationResult(authToken, noteStoreUrl, webApiUrlPrefix, evernoteHost, userId, isAppLinkedNotebook); authenticationResult.persist(); mSession.setAuthenticationResult(authenticationResult); return true; } catch (Exception e) { CAT.e("Failed to obtain OAuth access token", e); } return false; } protected static OAuthService createOAuthService(BootstrapProfile bootstrapProfile, String consumerKey, String consumerSecret) { String host = bootstrapProfile.getSettings().getServiceHost(); if (host == null) { return null; } Uri uri = new Uri.Builder() .authority(host) .scheme("https") .build(); Class<? extends Api> apiClass; switch (uri.toString()) { case EvernoteSession.HOST_SANDBOX: apiClass = EvernoteApi.Sandbox.class; break; case EvernoteSession.HOST_PRODUCTION: apiClass = EvernoteApi.class; break; case EvernoteSession.HOST_CHINA: apiClass = EvernoteApi.Yinxiang.class; break; default: throw new IllegalArgumentException("Unsupported Evernote host: " + host); } return new ServiceBuilder() .provider(apiClass) .apiKey(consumerKey) .apiSecret(consumerSecret) .callback(CALLBACK_SCHEME + "://callback") .build(); } private static String extract(String response, Pattern p) { Matcher matcher = p.matcher(response); if (matcher.find() && matcher.groupCount() >= 1) { return OAuthEncoder.decode(matcher.group(1)); } else { throw new OAuthException("Response body is incorrect. Can't extract token and secret from this: " + response); } } }