com.liato.bankdroid.banking.banks.lansforsakringar.Lansforsakringar.java Source code

Java tutorial

Introduction

Here is the source code for com.liato.bankdroid.banking.banks.lansforsakringar.Lansforsakringar.java

Source

/*
 * Copyright (C) 2010 Nullbyte <http://nullbyte.eu>
 *
 * 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.
 */

package com.liato.bankdroid.banking.banks.lansforsakringar;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.message.BasicNameValuePair;

import android.content.Context;
import android.text.InputType;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.liato.bankdroid.Helpers;
import com.liato.bankdroid.R;
import com.liato.bankdroid.banking.Account;
import com.liato.bankdroid.banking.Bank;
import com.liato.bankdroid.banking.Transaction;
import com.liato.bankdroid.banking.banks.lansforsakringar.model.request.AccountsRequest;
import com.liato.bankdroid.banking.banks.lansforsakringar.model.request.ChallengeRequest;
import com.liato.bankdroid.banking.banks.lansforsakringar.model.request.LoginRequest;
import com.liato.bankdroid.banking.banks.lansforsakringar.model.request.TransactionsRequest;
import com.liato.bankdroid.banking.banks.lansforsakringar.model.response.AccountsResponse;
import com.liato.bankdroid.banking.banks.lansforsakringar.model.response.ChallengeResponse;
import com.liato.bankdroid.banking.banks.lansforsakringar.model.response.LoginResponse;
import com.liato.bankdroid.banking.banks.lansforsakringar.model.response.NumberResponse;
import com.liato.bankdroid.banking.banks.lansforsakringar.model.response.TransactionsResponse;
import com.liato.bankdroid.banking.exceptions.BankChoiceException;
import com.liato.bankdroid.banking.exceptions.BankException;
import com.liato.bankdroid.banking.exceptions.LoginException;
import com.liato.bankdroid.provider.IBankTypes;

import eu.nullbyte.android.urllib.CertificateReader;
import eu.nullbyte.android.urllib.Urllib;

public class Lansforsakringar extends Bank {
    private static final String TAG = "Lansforsakringar";
    private static final String NAME = "Lnsfrskringar";
    private static final String NAME_SHORT = "lansforsakringar";
    private static final String URL = "https://mobil.lansforsakringar.se/lf-mobile/pages/login.faces";
    private static final int BANKTYPE_ID = IBankTypes.LANSFORSAKRINGAR;
    private static final int INPUT_TYPE_USERNAME = InputType.TYPE_CLASS_PHONE;
    private static final int INPUT_TYPE_PASSWORD = InputType.TYPE_CLASS_PHONE;
    private static final String INPUT_HINT_USERNAME = "MMDDXXXX";

    private static final String API_BASEURL = "https://mobil.lansforsakringar.se/appoutlet/";

    private Pattern reViewState = Pattern.compile("(?:__|javax\\.faces\\.)VIEWSTATE\"\\s+.*?value=\"([^\"]+)\"",
            Pattern.CASE_INSENSITIVE);
    private Pattern reLoginToken = Pattern.compile("login:loginToken\"\\s+.*?value=\"([^\"]+)\"",
            Pattern.CASE_INSENSITIVE);
    private ObjectMapper mObjectMapper = new ObjectMapper();
    private HashMap<String, String> mAccountLedger = new HashMap<String, String>();

    public Lansforsakringar(Context context) {
        super(context);
        super.TAG = TAG;
        super.NAME = NAME;
        super.NAME_SHORT = NAME_SHORT;
        super.BANKTYPE_ID = BANKTYPE_ID;
        super.URL = URL;
        super.INPUT_TYPE_USERNAME = INPUT_TYPE_USERNAME;
        super.INPUT_TYPE_PASSWORD = INPUT_TYPE_PASSWORD;
        super.INPUT_HINT_USERNAME = INPUT_HINT_USERNAME;
        mObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mObjectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
        mObjectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
    }

    public Lansforsakringar(String username, String password, Context context)
            throws BankException, LoginException, BankChoiceException {
        this(context);
        this.update(username, password);
    }

    @Override
    protected LoginPackage preLogin() throws BankException, ClientProtocolException, IOException {
        Urllib weblogin = new Urllib(context,
                CertificateReader.getCertificates(context, R.raw.cert_lansforsakringar));
        weblogin.setAllowCircularRedirects(true);

        String response = weblogin.open("https://mobil.lansforsakringar.se/lf-mobile/pages/login.faces");
        Matcher matcher = reViewState.matcher(response);
        if (!matcher.find()) {
            throw new BankException(res.getText(R.string.unable_to_find).toString() + " ViewState.");
        }
        String viewState = matcher.group(1);
        matcher = reLoginToken.matcher(response);
        if (!matcher.find()) {
            throw new BankException(res.getText(R.string.unable_to_find).toString() + " LoginToken.");
        }
        String loginToken = matcher.group(1);

        List<NameValuePair> postData = new ArrayList<NameValuePair>();
        postData.add(new BasicNameValuePair("login:userId", username));
        postData.add(new BasicNameValuePair("login:pin", password));
        postData.add(new BasicNameValuePair("login", "login"));
        postData.add(new BasicNameValuePair("javax.faces.ViewState", viewState));
        postData.add(new BasicNameValuePair("login:time", Long.toString(System.currentTimeMillis())));
        postData.add(new BasicNameValuePair("login:loginToken", loginToken));
        postData.add(new BasicNameValuePair("login:loginButton", "login:loginButton"));
        return new LoginPackage(weblogin, postData, response, weblogin.getCurrentURI());
    }

    public Urllib login() throws LoginException, BankException {
        urlopen = new Urllib(context, CertificateReader.getCertificates(context, R.raw.cert_lansforsakringar));
        urlopen.addHeader("Content-Type", "application/json; charset=UTF-8");
        urlopen.addHeader("DeviceId", UUID.randomUUID().toString());
        urlopen.addHeader("deviceInfo", "Galaxy Nexus;4.1.1;1.8;Portrait");
        //TODO: Change user-agent to "lf-android-app" if they block Bankdroid
        //urlopen.setUserAgent("lf-android-app");

        NumberResponse nr = readJsonValue(API_BASEURL + "security/client", null, NumberResponse.class);
        ChallengeResponse cr = readJsonValue(API_BASEURL + "security/client", objectAsJson(
                new ChallengeRequest(nr.getNumber(), nr.getNumberPair(), generateChallenge(nr.getNumber()))),
                ChallengeResponse.class);
        urlopen.addHeader("Ctoken", cr.getToken());
        try {
            LoginResponse lr = readJsonValue(API_BASEURL + "security/user",
                    objectAsJson(new LoginRequest(username, password)), LoginResponse.class);
            urlopen.addHeader("Utoken", lr.getTicket());
        } catch (Exception e) {
            throw new LoginException(res.getText(R.string.invalid_username_password).toString());
        }
        return urlopen;
    }

    private <T> T readJsonValue(InputStream is, Class<T> valueType) throws BankException {
        try {
            return mObjectMapper.readValue(is, valueType);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BankException(e.getMessage());
        }
    }

    private <T> T readJsonValue(String url, String postData, Class<T> valueType) throws BankException {
        try {
            return readJsonValue(urlopen.openStream(url, postData, false), valueType);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BankException(e.getMessage());
        }
    }

    public String objectAsJson(Object value) {
        try {
            return mObjectMapper.writeValueAsString(value);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    private String generateChallenge(int originalChallenge) {
        try {
            String h = Integer.toHexString(originalChallenge + (1000 * 20 / 4) + 100 * (18 / 3) + 10 * (2 / 2) + 6);
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] messageDigest = md.digest(h.getBytes());
            BigInteger number = new BigInteger(1, messageDigest);
            String md5 = number.toString(16);
            while (md5.length() < 40)
                md5 = "0" + md5;
            return md5;
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "";

    }

    @Override
    public void update() throws BankException, LoginException, BankChoiceException {
        super.update();
        if (username == null || password == null || username.length() == 0 || password.length() == 0) {
            throw new LoginException(res.getText(R.string.invalid_username_password).toString());
        }

        urlopen = login();

        mAccountLedger.clear();
        AccountsResponse ar = readJsonValue(API_BASEURL + "account/bytype",
                objectAsJson(new AccountsRequest(AccountsRequest.Type.CHECKING)), AccountsResponse.class);
        for (com.liato.bankdroid.banking.banks.lansforsakringar.model.response.Account a : ar.getAccounts()) {
            accounts.add(new Account(a.getAccountName(), new BigDecimal(a.getBalance()), a.getAccountNumber()));
            //a.getLedger() should be saved to database, used when fetching transactions
            mAccountLedger.put(a.getAccountNumber(), a.getLedger());
            balance = balance.add(new BigDecimal(a.getBalance()));
        }
        ar = readJsonValue(API_BASEURL + "account/bytype",
                objectAsJson(new AccountsRequest(AccountsRequest.Type.SAVING)), AccountsResponse.class);
        for (com.liato.bankdroid.banking.banks.lansforsakringar.model.response.Account a : ar.getAccounts()) {
            accounts.add(new Account(a.getAccountName(), new BigDecimal(a.getBalance()), a.getAccountNumber()));
            mAccountLedger.put(a.getAccountNumber(), a.getLedger());
            balance = balance.add(new BigDecimal(a.getBalance()));
        }
        if (accounts.isEmpty()) {
            throw new BankException(res.getText(R.string.no_accounts_found).toString());
        }
        super.updateComplete();
    }

    @Override
    public void updateTransactions(Account account, Urllib urlopen) throws LoginException, BankException {
        super.updateTransactions(account, urlopen);
        // No transaction history for funds and loans
        if (account.getType() != Account.REGULAR)
            return;

        ArrayList<Transaction> transactions = new ArrayList<Transaction>();
        //TODO: Get upcoming transactions?
        //TransactionsResponse tr = readJsonValue(API_BASEURL + "account/upcoming", objectAsJson(new UpcomingTransactionsRequest(account.getId())), TransactionsResponse.class);

        try {
            TransactionsResponse tr = readJsonValue(API_BASEURL + "account/transaction",
                    objectAsJson(new TransactionsRequest(0,
                            mAccountLedger.containsKey(account.getId()) ? mAccountLedger.get(account.getId())
                                    : "DEPIOSIT",
                            account.getId())),
                    TransactionsResponse.class);
            for (com.liato.bankdroid.banking.banks.lansforsakringar.model.response.Transaction t : tr
                    .getTransactions()) {
                //TODO: Set locale to Europe/Stockholm on date?
                transactions.add(new Transaction(Helpers.formatDate(new Date(t.getTransactiondate())), t.getText(),
                        new BigDecimal(t.getAmmount())));
            }
            account.setTransactions(transactions);
        } catch (BankException e) {
            // No transactions for account if this fails.
            // readJsonValue will print the stack trace
        }

        super.updateComplete();
    }
}