Java tutorial
package com.ntsync.android.sync.client; /* * Copyright (C) 2014 Markus Grieder * * This file is based on NetworkUtilities.java from the SampleSyncAdapter-Example in Android SDK * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. */ /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.SecureRandom; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.crypto.SecretKey; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.StatusLine; import org.apache.http.auth.AuthenticationException; import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.protocol.ClientContext; import org.apache.http.cookie.Cookie; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.cookie.BasicClientCookie; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.spongycastle.crypto.CryptoException; import org.spongycastle.crypto.Digest; import org.spongycastle.crypto.agreement.srp.SRP6Client; import org.spongycastle.util.BigIntegers; import org.xml.sax.SAXException; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorException; import android.accounts.NetworkErrorException; import android.accounts.OperationCanceledException; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.SyncResult; import android.os.Bundle; import android.text.TextUtils; import android.util.Base64; import android.util.Log; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.ntsync.android.sync.BuildConfig; import com.ntsync.android.sync.platform.ErrorHandler; import com.ntsync.android.sync.platform.SystemHelper; import com.ntsync.android.sync.shared.Constants; import com.ntsync.android.sync.shared.LogHelper; import com.ntsync.shared.ContactGroup; import com.ntsync.shared.HeaderCreateException; import com.ntsync.shared.HeaderParseException; import com.ntsync.shared.Pair; import com.ntsync.shared.PayPalConfirmationResult; import com.ntsync.shared.Price; import com.ntsync.shared.RawContact; import com.ntsync.shared.RequestGenerator; import com.ntsync.shared.RequestGenerator.SyncPrepErrorStatistic; import com.ntsync.shared.RequestGenerator.SyncResponse; import com.ntsync.shared.Restrictions; import com.ntsync.shared.SRP6Helper; import com.ntsync.shared.SyncAnchor; import com.ntsync.shared.SyncDataHelper; import com.ntsync.shared.UserRegistrationState; /** * Provides utility methods for communicating with the server. */ public final class NetworkUtilities { private static final int INT_LEN = 4; /** The tag used to log to adb console. */ private static final String TAG = "NetworkUtilities"; private static final String SYNC_CIENTID_KEY = "com.myllih.android.sync.clientid"; private static final String BASE_URL; private static final String COOKIE_SESSION_NAME = "JSESSIONID"; static { if (Constants.USE_RELEASE_CONFIG) { BASE_URL = "https://api.ntsync.com"; } else { BASE_URL = "https://192.168.0.10:8443/syncserver-web"; } } /** URI for sync service */ private static final String SYNC_BASE_URI = BASE_URL + "/sync"; /** URI for authentication service */ private static final String AUTH1_URI = SYNC_BASE_URI + "/authstep1"; private static final String AUTH2_URI = SYNC_BASE_URI + "/authstep2"; private static final String CREATEUSERNAME_URI = SYNC_BASE_URI + "/user/createusername"; private static final String REGISTRATION_URI = SYNC_BASE_URI + "/user/registrate"; private static final String SYNC_URI = SYNC_BASE_URI + "/send"; private static final String PWDSALT_URI = SYNC_BASE_URI + "/salt"; private static final String RESTRICTIONS_URI = SYNC_BASE_URI + "/restrictions"; private static final String PRICE_URI = SYNC_BASE_URI + "/price/"; private static final String PWDVERIFYPAYMENT_URI = SYNC_BASE_URI + "/paypal/verify"; private static HttpClient client = null; private static final Object CL_LOCK = new Object(); private static final Map<String, CookieStore> COOKIES = new HashMap<String, CookieStore>(); private NetworkUtilities() { } private static HttpClient getHttpClient(Context context) { HttpClient newClient; synchronized (CL_LOCK) { if (client == null) { client = new MyHttpClient(context); } newClient = client; } return newClient; } /** * CookieStore per AccountName to prevent mixing of the sessions. * * @param accountName * accountName or null (default) * @return */ private static HttpContext createHttpContext(String accountName, String authtoken) { BasicHttpContext ctx = new BasicHttpContext(); CookieStore store; synchronized (CL_LOCK) { store = COOKIES.get(accountName); if (store == null) { store = new BasicCookieStore(); COOKIES.put(accountName, store); } } ctx.setAttribute(ClientContext.COOKIE_STORE, store); if (authtoken != null) { boolean add = true; for (Cookie cookie : store.getCookies()) { if (COOKIE_SESSION_NAME.equals(cookie.getName())) { if (authtoken.equals(cookie.getValue())) { add = false; } break; } } if (add) { BasicClientCookie sessionCookie = new BasicClientCookie(COOKIE_SESSION_NAME, authtoken); sessionCookie.setSecure(true); store.addCookie(sessionCookie); } } return ctx; } /** * Connects to the test server, authenticates the provided username and * password. * * @param username * The server account username * @param password * The server account password * @return String The authentication token returned by the server and the * SRP-Password (or null) * @throws ServerException * @throws NetworkErrorException * when communication failed */ public static Pair<String, byte[]> authenticate(Context context, String username, String password) throws NetworkErrorException, ServerException { return authenticate(context, username, password, null); } /** * * @param context * @param username * @param srpPassword * @return String The authentication token returned by the server or null * @throws NetworkErrorException * when communication failed * @throws ServerException */ public static String authenticate(Context context, String username, byte[] srpPassword) throws NetworkErrorException, ServerException { Pair<String, byte[]> data = authenticate(context, username, null, srpPassword); return data != null ? data.left : null; } @SuppressLint("TrulyRandom") private static Pair<String, byte[]> authenticate(Context context, String username, String password, byte[] srpPassword) throws NetworkErrorException, ServerException { LogHelper.logD(TAG, "Authenticating to: {}", AUTH1_URI); if (username == null) { Log.e(TAG, "Username or password is null."); return null; } if (password == null && srpPassword == null) { Log.e(TAG, "Either password or srpPassword is needed."); } Pair<String, byte[]> returnData = null; try { // Step 1 byte[] content = sendAuthStep1(context, username); int pwdSaltLen = SRP6Helper.PWD_SALT_LENGTH; if (content == null || content.length <= INT_LEN + pwdSaltLen) { Log.i(TAG, "Authentification failed."); return null; } int index = 0; byte[] pwdSalt = new byte[pwdSaltLen]; System.arraycopy(content, index, pwdSalt, 0, pwdSaltLen); index += pwdSaltLen; int len = SyncDataHelper.readInt(content, index); index += INT_LEN; if (len + index >= content.length) { Log.i(TAG, "Invalid datastructure. "); return null; } byte[] salt = new byte[len]; System.arraycopy(content, index, salt, 0, salt.length); index += len; byte[] serverB = new byte[content.length - index]; System.arraycopy(content, index, serverB, 0, serverB.length); byte[] validSRPPassword = srpPassword; if (validSRPPassword == null) { validSRPPassword = SRP6Helper.createSRPPassword(password, pwdSalt); } // Step 2 BigInteger srpB = new BigInteger(1, serverB); SRP6Client srpClient = new SRP6Client(); Digest srpHashFn = SRP6Helper.createDigest(); srpClient.init(SRP6Helper.N_2024, SRP6Helper.G_2024, srpHashFn, new SecureRandom()); BigInteger srpA = srpClient.generateClientCredentials(salt, username.getBytes("UTF-8"), validSRPPassword); BigInteger clientS = srpClient.calculateSecret(srpB); BigInteger srpK = SRP6Helper.createHash(srpHashFn, clientS); BigInteger clientM = SRP6Helper.createClientM(srpHashFn, username, new BigInteger(1, salt), srpA, srpB, srpK); // Send Proof of Session and get Session-Id and Session-Prof of // Server content = sendAuthStep2(context, clientM, username, srpA); if (content == null || content.length == 0) { Log.i(TAG, "Authentification failed."); return null; } len = SyncDataHelper.readInt(content, 0); if (len + INT_LEN >= content.length) { Log.i(TAG, "Invalid datastructure. "); return null; } byte[] sessionId = new byte[len]; System.arraycopy(content, INT_LEN, sessionId, 0, sessionId.length); byte[] serverMByte = new byte[content.length - INT_LEN - sessionId.length]; System.arraycopy(content, INT_LEN + sessionId.length, serverMByte, 0, serverMByte.length); String authtoken = new String(sessionId, SyncDataHelper.DEFAULT_CHARSET_NAME); BigInteger verifServerM = SRP6Helper.createServerM(SRP6Helper.createDigest(), srpA, clientM, srpK); BigInteger serverM = new BigInteger(1, serverMByte); if (!verifServerM.equals(serverM)) { Log.i(TAG, "Server verification failed. CalcServerM:" + verifServerM + " ServerM:" + verifServerM); authtoken = null; } else if ((authtoken != null) && (authtoken.length() > 0)) { Log.v(TAG, "Successful authentication"); } else { Log.i(TAG, "Error authenticating. Authtoken:" + authtoken); authtoken = null; } if (authtoken != null) { returnData = new Pair<String, byte[]>(authtoken, validSRPPassword); } } catch (CryptoException e) { LogHelper.logWCause(TAG, "Invalid server credentials", e); } catch (UnsupportedEncodingException e) { throw new RuntimeException("No support for UTF-8 available.", e); } return returnData; } private static byte[] sendAuthStep2(Context context, BigInteger clientM, String accountName, BigInteger srpA) throws UnsupportedEncodingException, ServerException, NetworkErrorException { final HttpPost post = new HttpPost(AUTH2_URI); List<BasicNameValuePair> values = new ArrayList<BasicNameValuePair>(); values.add(new BasicNameValuePair("m", Base64.encodeToString(BigIntegers.asUnsignedByteArray(clientM), Base64.DEFAULT))); values.add(new BasicNameValuePair("srpA", Base64.encodeToString(BigIntegers.asUnsignedByteArray(srpA), Base64.DEFAULT))); HttpEntity postEntity = new UrlEncodedFormEntity(values, SyncDataHelper.DEFAULT_CHARSET_NAME); post.setHeader(postEntity.getContentEncoding()); post.setEntity(postEntity); try { final HttpResponse resp = getHttpClient(context).execute(post, createHttpContext(accountName, null)); return getResponse(resp); } catch (IOException ex) { throw new NetworkErrorException(ex); } } private static byte[] sendAuthStep1(Context context, String username) throws UnsupportedEncodingException, ServerException, NetworkErrorException { final HttpPost post = new HttpPost(AUTH1_URI); List<BasicNameValuePair> values = new ArrayList<BasicNameValuePair>(); values.add(new BasicNameValuePair("name", username)); HttpEntity postEntity = new UrlEncodedFormEntity(values, SyncDataHelper.DEFAULT_CHARSET_NAME); post.setHeader(postEntity.getContentEncoding()); // Explicit set no session post.setEntity(postEntity); try { final HttpResponse resp = getHttpClient(context).execute(post, createHttpContext(username, null)); return getResponse(resp); } catch (IOException ex) { throw new NetworkErrorException(ex); } } private static byte[] getResponse(HttpResponse resp) throws IOException, ServerException { byte[] content = null; HttpEntity entity = null; try { entity = resp.getEntity(); StatusLine statusLine = resp.getStatusLine(); if (statusLine != null) { if (statusLine.getStatusCode() == HttpStatus.SC_OK) { content = EntityUtils.toByteArray(entity); } else if (statusLine.getStatusCode() != HttpStatus.SC_UNAUTHORIZED) { throw new ServerException("Server error with State:" + statusLine.getStatusCode()); } } } finally { consumeContent(entity); } return content; } public static Pair<UserRegistrationState, String> registrateUser(Context context, String email, byte[] srpPassword, byte[] pwdSalt, String name) throws ServerException, NetworkErrorException { String username; UserRegistrationState state; int retryCount = 5; boolean retry; do { retry = false; state = null; try { // create new Username username = createUsername(context, email); if (username == null) { return new Pair<UserRegistrationState, String>(UserRegistrationState.EMAIL_INVALID, null); } Pair<byte[], BigInteger> verif = SRP6Helper.createUserVerification(username, srpPassword); final HttpPost post = new HttpPost(REGISTRATION_URI); List<BasicNameValuePair> values = new ArrayList<BasicNameValuePair>(); values.add(new BasicNameValuePair("username", username)); values.add(new BasicNameValuePair("email", email)); values.add(new BasicNameValuePair("verif", Base64.encodeToString(BigIntegers.asUnsignedByteArray(verif.right), Base64.DEFAULT))); values.add(new BasicNameValuePair("srpSalt", Base64.encodeToString(verif.left, Base64.DEFAULT))); values.add(new BasicNameValuePair("pwdSalt", Base64.encodeToString(pwdSalt, Base64.DEFAULT))); values.add(new BasicNameValuePair("personname", name)); values.add(new BasicNameValuePair("locale", Locale.getDefault().toString())); HttpEntity postEntity = new UrlEncodedFormEntity(values, SyncDataHelper.DEFAULT_CHARSET_NAME); post.setHeader(postEntity.getContentEncoding()); post.setEntity(postEntity); byte[] content; final HttpResponse resp = getHttpClient(context).execute(post, createHttpContext(username, null)); content = getResponse(resp); if (content != null && content.length > 0) { String respCode = new String(content, SyncDataHelper.DEFAULT_CHARSET_NAME); state = UserRegistrationState.fromErrorVal(respCode); } if (state == null) { throw new ServerException("No valid response"); } } catch (UnsupportedEncodingException ex) { throw new RuntimeException(ex); } catch (IOException ex) { throw new NetworkErrorException(ex); } if (state == UserRegistrationState.USERNAME_IN_USE) { // Create a new username when alredy in use (concurrent catch of // username) retry = true; } retryCount--; } while (retry && retryCount > 0); return new Pair<UserRegistrationState, String>(state, username); } private static String createUsername(Context context, String email) throws ServerException, NetworkErrorException, UnsupportedEncodingException { final HttpPost post = new HttpPost(CREATEUSERNAME_URI); if (Log.isLoggable(TAG, Log.INFO)) { Log.i(TAG, "createUsername with URI: " + post.getURI()); } List<BasicNameValuePair> values = new ArrayList<BasicNameValuePair>(); values.add(new BasicNameValuePair("email", email)); HttpEntity postEntity = new UrlEncodedFormEntity(values, SyncDataHelper.DEFAULT_CHARSET_NAME); post.setHeader(postEntity.getContentEncoding()); post.setEntity(postEntity); String username = null; byte[] content; try { final HttpResponse resp = getHttpClient(context).execute(post, createHttpContext(null, null)); content = getResponse(resp); } catch (IOException ex) { throw new NetworkErrorException(ex); } if (content != null && content.length > 0) { username = new String(content, SyncDataHelper.DEFAULT_CHARSET_NAME); } return username; } /** * * @param accountManager * @param account * @return null if client has not yet a clientId */ private static String getClientId(AccountManager accountManager, Account account) { String clientIdString = accountManager.getUserData(account, SYNC_CIENTID_KEY); if (!TextUtils.isEmpty(clientIdString)) { return clientIdString; } return null; } private static void setClientId(AccountManager accountManager, Account account, String clientId) { accountManager.setUserData(account, SYNC_CIENTID_KEY, clientId); } /** * Perform 2-way sync with the server-side contacts. We send a request that * includes all the locally-dirty contacts so that the server can process * those changes, and we receive (and return) a list of contacts that were * updated on the server-side that need to be updated locally. * * @param account * The account being synced * @param authtoken * The authtoken stored in the AccountManager for this account * @param serverSyncState * A token returned from the server on the last sync * @param dirtyContacts * A list of the contacts to send to the server * @param newIdMap * Map of RawId to ServerId * @param explizitPhotoSave * @return A list of contacts that we need to update locally. Null if * processing of server-results failed. * @throws ParserConfigurationException * @throws TransformerException * @throws AuthenticatorException * @throws OperationCanceledException * when Authentication was canceled from user * @throws SAXException * @throws ServerException * @throws NetworkErrorException * @throws HeaderParseException * @throws HeaderCreateException */ public static SyncResponse syncContacts(Account account, String authtoken, SyncAnchor serverSyncState, List<RawContact> dirtyContacts, List<ContactGroup> dirtyGroups, SecretKey key, AccountManager accountManager, Context context, SyncResult syncResult, String pwdSaltHexStr, Map<Long, String> newIdMap, Restrictions restr, boolean explizitPhotoSave) throws AuthenticationException, OperationCanceledException, AuthenticatorException, ServerException, NetworkErrorException, HeaderParseException, HeaderCreateException { String clientId = getClientId(accountManager, account); SyncPrepErrorStatistic prepError = new SyncPrepErrorStatistic(); byte[] totBuffer = RequestGenerator.prepareServerRequest(serverSyncState, dirtyContacts, dirtyGroups, key, SystemHelper.getPkgVersion(context), clientId, pwdSaltHexStr, newIdMap, prepError, restr, explizitPhotoSave); syncResult.stats.numSkippedEntries += prepError.getIgnoredRows(); String currAuthtoken = authtoken; SyncResponse syncResponse = null; boolean retry; int retrycount = 0; do { retry = false; HttpEntity entity = new ByteArrayEntity(totBuffer); // Send the updated friends data to the server final HttpPost post = new HttpPost(SYNC_URI); post.setHeader("Content-Encoding", "application/octect-stream"); post.setEntity(entity); HttpEntity respEntity = null; try { final HttpResponse resp = getHttpClient(context).execute(post, createHttpContext(account.name, currAuthtoken)); respEntity = resp.getEntity(); if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { final byte[] response = EntityUtils.toByteArray(respEntity); syncResponse = processServerResponse(account, key, accountManager, clientId, response, syncResult); if (Log.isLoggable(TAG, Log.INFO)) { Log.i(TAG, "Response-Length: " + response.length); } } else { if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { currAuthtoken = retryAuthentification(retrycount, accountManager, currAuthtoken, account.name, resp); retry = true; } else { throw new ServerException( "Server error in sending dirty contacts: " + resp.getStatusLine()); } } } catch (IOException ex) { throw new NetworkErrorException(ex); } finally { consumeContent(respEntity); } retrycount++; } while (retry); return syncResponse; } private static SyncResponse processServerResponse(Account account, SecretKey key, AccountManager accountManager, String clientId, final byte[] response, SyncResult syncResult) throws HeaderParseException { SyncResponse serverResponse = RequestGenerator.processServerResponse(key, clientId, response); // Neue ClientId bernehmen if (serverResponse.clientId != null && TextUtils.isEmpty(clientId)) { setClientId(accountManager, account, serverResponse.clientId); } syncResult.stats.numSkippedEntries = +serverResponse.skippedResponse; return serverResponse; } /** * * @param authtoken * @return KeySalt and KeyCheck. null if server was returning nothing * @throws IOException * @throws AuthenticationException * @throws AuthenticatorException * @throws OperationCanceledException * @throws ServerException * @throws NetworkErrorException */ public static byte[] getKeySalt(Context context, String accountName, String authtoken) throws AuthenticationException, OperationCanceledException, ServerException, NetworkErrorException { boolean retry; int retryCount = 0; String currAuthtoken = authtoken; byte[] pwdSalt = null; do { retry = false; final HttpGet get = new HttpGet(PWDSALT_URI); HttpEntity entity = null; try { final HttpResponse resp = getHttpClient(context).execute(get, createHttpContext(accountName, currAuthtoken)); entity = resp.getEntity(); int statusCode = resp.getStatusLine().getStatusCode(); if (statusCode == HttpStatus.SC_OK) { byte[] respBytes = EntityUtils.toByteArray(entity); if (BuildConfig.DEBUG) { Log.v(TAG, "Get Response-Lenght:" + (respBytes != null ? respBytes.length : "null")); } pwdSalt = respBytes == null || respBytes.length == 0 ? null : respBytes; } else if (statusCode == HttpStatus.SC_UNAUTHORIZED) { AccountManager accountManager = AccountManager.get(context); currAuthtoken = retryAuthentification(retryCount, accountManager, currAuthtoken, accountName, resp); retry = true; } else { throw new ServerException("Server error in query getPwdSalt: " + resp.getStatusLine()); } } catch (IOException ex) { throw new NetworkErrorException(ex); } finally { consumeContent(entity); } retryCount++; } while (retry); return pwdSalt; } private static void consumeContent(HttpEntity entity) { try { if (entity != null) { entity.consumeContent(); } } catch (IOException ex) { LogHelper.logW(TAG, "Could not consume HttpEntity", ex); } } private static String retryAuthentification(int retryCount, AccountManager accountManager, String authtoken, String accountName, HttpResponse response) throws AuthenticationException, OperationCanceledException, NetworkErrorException, ServerException { accountManager.invalidateAuthToken(Constants.ACCOUNT_TYPE, authtoken); String newToken = null; if (retryCount == 0) { newToken = blockingGetAuthToken(accountManager, new Account(accountName, Constants.ACCOUNT_TYPE), null); } if (newToken == null) { throw new AuthenticationException(response.getStatusLine().toString()); } return newToken; } /** * * @param authtoken * @return null if server returned no restrictions. * @throws IOException * @throws AuthenticationException * @throws ParserConfigurationException * @throws SAXException * @throws AuthenticatorException * @throws OperationCanceledException * @throws NetworkErrorException * @throws ServerException */ public static Restrictions getRestrictions(Context context, Account account, String authtoken, AccountManager accountManager) throws AuthenticationException, OperationCanceledException, AuthenticatorException, NetworkErrorException, ServerException { String currAuthtoken = authtoken; HttpEntity entity = null; Restrictions restr = null; boolean retry; int retryCount = 0; do { retry = false; final HttpGet get = new HttpGet(RESTRICTIONS_URI); try { final HttpResponse resp = getHttpClient(context).execute(get, createHttpContext(account.name, currAuthtoken)); entity = resp.getEntity(); int statusCode = resp.getStatusLine().getStatusCode(); if (statusCode == HttpStatus.SC_OK) { byte[] respBytes = EntityUtils.toByteArray(entity); if (BuildConfig.DEBUG) { Log.v(TAG, "Get Response-Lenght:" + (respBytes != null ? respBytes.length : "null")); } if (respBytes != null && respBytes.length > 0) { restr = RequestGenerator.parseRestr(new ByteArrayInputStream(respBytes)); } } else if (statusCode == HttpStatus.SC_UNAUTHORIZED) { currAuthtoken = retryAuthentification(retryCount, accountManager, currAuthtoken, account.name, resp); retry = true; } else { throw new ServerException("Server error in query getRestrictions: " + resp.getStatusLine()); } } catch (IOException e) { throw new NetworkErrorException(e); } finally { consumeContent(entity); } retryCount++; } while (retry); return restr; } /** * Get current Prices for a Currency * * @param authtoken * can be null * @return null if server returned no prices. * @throws IOException * @throws AuthenticationException * @throws ParserConfigurationException * @throws SAXException * @throws AuthenticatorException * @throws OperationCanceledException * @throws ServerException * @throws NetworkErrorException */ public static List<Price> getPrices(Context context, Account account, String authtoken, AccountManager accountManager, String currency) throws OperationCanceledException, AuthenticationException, NetworkErrorException, ServerException { String currAuthtoken = authtoken; HttpEntity entity = null; List<Price> prices = null; boolean retry; int retryCount = 0; do { retry = false; final HttpGet get = new HttpGet(PRICE_URI + currency); LogHelper.logD(TAG, "getPrices with URI: {}", get.getURI()); try { final HttpResponse resp = getHttpClient(context).execute(get, createHttpContext(account.name, currAuthtoken)); entity = resp.getEntity(); int statusCode = resp.getStatusLine().getStatusCode(); if (statusCode == HttpStatus.SC_OK) { ObjectMapper mapper = new ObjectMapper(); Class<?> clz = Price.class; JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, clz); byte[] respBytes = EntityUtils.toByteArray(entity); prices = mapper.readValue(respBytes, type); } else if (statusCode == HttpStatus.SC_UNAUTHORIZED) { currAuthtoken = retryAuthentification(retryCount, accountManager, currAuthtoken, account.name, resp); retry = true; } else { throw new ServerException("Server error in query getPrices: " + resp.getStatusLine()); } } catch (IOException e) { throw new NetworkErrorException(e); } finally { consumeContent(entity); } retryCount++; } while (retry); return prices; } public static boolean savePwdSalt(Context context, String accountName, String authtoken, String newSalt, String oldSalt, boolean clearData, String pwdCheck) throws AuthenticationException, ServerException, NetworkErrorException { final HttpPost post = new HttpPost(PWDSALT_URI); List<BasicNameValuePair> values = new ArrayList<BasicNameValuePair>(); values.add(new BasicNameValuePair("newSalt", newSalt == null ? "" : newSalt)); values.add(new BasicNameValuePair("oldSalt", oldSalt == null ? "" : oldSalt)); values.add(new BasicNameValuePair("clearData", String.valueOf(clearData))); values.add(new BasicNameValuePair("pwdCheck", pwdCheck == null ? "" : pwdCheck)); HttpResponse resp = null; final String respStr; HttpEntity entity = null; try { HttpEntity postEntity = new UrlEncodedFormEntity(values, SyncDataHelper.DEFAULT_CHARSET_NAME); post.setHeader(postEntity.getContentEncoding()); post.setEntity(postEntity); resp = getHttpClient(context).execute(post, createHttpContext(accountName, authtoken)); entity = resp.getEntity(); respStr = EntityUtils.toString(entity); } catch (UnsupportedEncodingException ex) { throw new RuntimeException(ex); } catch (IOException ex) { throw new NetworkErrorException(ex); } finally { if (entity != null) { try { entity.consumeContent(); } catch (IOException ex) { LogHelper.logD(TAG, "Close Entity failed with: " + ex.getMessage(), ex); } } } boolean saltSaved = true; StatusLine statusLine = resp.getStatusLine(); int statusCode = statusLine.getStatusCode(); if (statusCode != HttpStatus.SC_OK) { if (statusCode == HttpStatus.SC_UNAUTHORIZED) { throw new AuthenticationException(statusLine.toString()); } else if (statusCode == HttpStatus.SC_BAD_REQUEST) { saltSaved = false; } else { throw new ServerException("Server error in query savePwdSalt: " + statusLine); } } else { if (respStr != null && !respStr.startsWith("OK")) { Log.w(TAG, "SaltSaved failed with: " + respStr); saltSaved = false; } } return saltSaved; } /** * * @param acm * @param account * * * @param Activity * if null show a notification when Login is needed otherwise * show the Login-Activity in the context of the provided * Activity. * * @return SessionId * @throws OperationCanceledException * @throws ServerException * @throws NetworkErrorException */ @SuppressWarnings("deprecation") public static String blockingGetAuthToken(AccountManager acm, Account account, Activity activity) throws OperationCanceledException, ServerException, NetworkErrorException { String authToken = null; try { Bundle result; if (activity == null) { // New is available from API 14 -> use deprecated API result = acm.getAuthToken(account, Constants.AUTHTOKEN_TYPE, true, null, null).getResult(); } else { result = acm.getAuthToken(account, Constants.AUTHTOKEN_TYPE, null, activity, null, null) .getResult(); } if (result != null) { if (result.containsKey(AccountManager.KEY_AUTHTOKEN)) { authToken = result.getString(AccountManager.KEY_AUTHTOKEN); } if (result.containsKey(AccountManager.KEY_ERROR_CODE)) { int errorCode = result.getInt(AccountManager.KEY_ERROR_CODE, -1); String msg = result.getString(AccountManager.KEY_ERROR_MESSAGE); if (errorCode == Constants.AUTH_ERRORCODE_SERVEREXCEPTION) { throw new ServerException(msg); } else { LogHelper.logE(TAG, "Authentification failed with unknown errorCode:" + errorCode + " Message:" + msg, null); } } } } catch (AuthenticatorException e) { LogHelper.logE(TAG, "Authentification failed.", e); // Should not happen -> report error ErrorHandler.reportException(e); } catch (IOException ex) { throw new NetworkErrorException(ex); } return authToken; } /** * Send a PayPalPayment to the server for verification an processing. * * @param priceId * @param jsonProofOfPayment * @return * @throws IOException * @throws AuthenticationException * @throws AuthenticatorException * @throws OperationCanceledException * @throws ServerException * @throws NetworkErrorException */ public static PayPalConfirmationResult verifyPayPalPayment(Context context, Account account, String priceId, String jsonProofOfPayment, String authtoken, AccountManager accountManager) throws AuthenticationException, OperationCanceledException, ServerException, NetworkErrorException { String currAuthtoken = authtoken; HttpEntity entity; PayPalConfirmationResult result = null; boolean retry; int retryCount = 0; do { retry = false; entity = null; final HttpPost post = new HttpPost(PWDVERIFYPAYMENT_URI); List<BasicNameValuePair> values = new ArrayList<BasicNameValuePair>(); values.add(new BasicNameValuePair("priceid", priceId)); values.add(new BasicNameValuePair("confirmation", jsonProofOfPayment)); HttpResponse resp = null; final String respStr; try { HttpEntity postEntity = new UrlEncodedFormEntity(values, SyncDataHelper.DEFAULT_CHARSET_NAME); post.setHeader(postEntity.getContentEncoding()); post.setEntity(postEntity); resp = getHttpClient(context).execute(post, createHttpContext(account.name, authtoken)); entity = resp.getEntity(); respStr = EntityUtils.toString(entity); StatusLine statusLine = resp.getStatusLine(); int statusCode = statusLine.getStatusCode(); if (statusCode == HttpStatus.SC_OK) { result = PayPalConfirmationResult.fromErrorVal(respStr); } else if (statusCode == HttpStatus.SC_UNAUTHORIZED) { currAuthtoken = retryAuthentification(retryCount, accountManager, currAuthtoken, account.name, resp); retry = true; } else { throw new ServerException("Server error in query verifyPayPalPayment: " + resp.getStatusLine()); } } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } catch (IOException e) { throw new NetworkErrorException(e); } finally { consumeContent(entity); } retryCount++; } while (retry); return result; } }