Credential.java revision af955ffa0082189fb688429732427c333f2491ce
171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvistpackage com.android.server.wifi.hotspot2.pps;
271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
303e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpandeimport android.net.wifi.WifiEnterpriseConfig;
471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvistimport android.util.Base64;
5af955ffa0082189fb688429732427c333f2491ceVinit Deshpandeimport android.util.Log;
671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
703e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpandeimport com.android.server.wifi.anqp.eap.EAP;
871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvistimport com.android.server.wifi.anqp.eap.EAPMethod;
903e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpandeimport com.android.server.wifi.anqp.eap.InnerAuthEAP;
1077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport com.android.server.wifi.anqp.eap.AuthParam;
1103e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpandeimport com.android.server.wifi.anqp.eap.NonEAPInnerAuth;
1271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvistimport com.android.server.wifi.hotspot2.Utils;
1371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvistimport com.android.server.wifi.hotspot2.omadm.OMAException;
1471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
1571a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvistimport java.nio.charset.StandardCharsets;
1677f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.util.ArrayList;
1777f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.util.List;
1877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvistimport java.util.Set;
1971a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
2071a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvistpublic class Credential {
2171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    public enum CertType {IEEE, x509v3}
2271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
2371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final long mCtime;
2471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final long mExpTime;
2571a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final String mRealm;
2671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final boolean mCheckAAACert;
2771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
2871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final String mUserName;
2971a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final String mPassword;
3071a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final boolean mMachineManaged;
3171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final String mSTokenApp;
3271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final boolean mShare;
3371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final EAPMethod mEAPMethod;
3471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
3571a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final CertType mCertType;
3671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final byte[] mFingerPrint;
3771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
3871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    private final String mImsi;
3971a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
4071a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    public Credential(long ctime, long expTime, String realm, boolean checkAAACert,
4171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                      EAPMethod eapMethod, String userName, String password,
4271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                      boolean machineManaged, String stApp, boolean share) {
4371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mCtime = ctime;
4471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mExpTime = expTime;
4571a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mRealm = realm;
4671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mCheckAAACert = checkAAACert;
4771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mEAPMethod = eapMethod;
4871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mUserName = userName;
4971a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        byte[] pwOctets = Base64.decode(password, Base64.DEFAULT);
5071a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mPassword = new String(pwOctets, StandardCharsets.UTF_8);
5171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mMachineManaged = machineManaged;
5271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mSTokenApp = stApp;
5371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mShare = share;
5471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
5571a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mCertType = null;
5671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mFingerPrint = null;
5771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
5871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mImsi = null;
5971a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    }
6071a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
6171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    public Credential(long ctime, long expTime, String realm, boolean checkAAACert,
6271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                      EAPMethod eapMethod, Credential.CertType certType, byte[] fingerPrint) {
6371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mCtime = ctime;
6471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mExpTime = expTime;
6571a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mRealm = realm;
6671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mCheckAAACert = checkAAACert;
6771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mEAPMethod = eapMethod;
6871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mCertType = certType;
6971a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mFingerPrint = fingerPrint;
7071a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
7171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mUserName = null;
7271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mPassword = null;
7371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mMachineManaged = false;
7471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mSTokenApp = null;
7571a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mShare = false;
7671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
7771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mImsi = null;
7871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    }
7971a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
8071a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    public Credential(long ctime, long expTime, String realm, boolean checkAAACert,
8171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                      EAPMethod eapMethod, String imsi) {
8271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mCtime = ctime;
8371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mExpTime = expTime;
8471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mRealm = realm;
8571a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mCheckAAACert = checkAAACert;
8671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mEAPMethod = eapMethod;
8771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mImsi = imsi;
8871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
8971a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mCertType = null;
9071a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mFingerPrint = null;
9171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
9271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mUserName = null;
9371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mPassword = null;
9471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mMachineManaged = false;
9571a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mSTokenApp = null;
9671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        mShare = false;
9771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    }
9871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
9903e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande    public Credential(WifiEnterpriseConfig enterpriseConfig) {
10003e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mCtime = 0;
10103e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mExpTime = 0;
10203e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mRealm = enterpriseConfig.getRealm();
10303e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mCheckAAACert = true;
10403e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mEAPMethod = mapEapMethod(enterpriseConfig.getEapMethod(),
10503e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande                enterpriseConfig.getPhase2Method());
10603e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mCertType = mapCertType(enterpriseConfig.getEapMethod());
10703e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mFingerPrint = null;
10803e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mImsi = enterpriseConfig.getPlmn();
10903e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mUserName = null;
11003e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mPassword = null;
11103e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mMachineManaged = false;
11203e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mSTokenApp = null;
11303e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        mShare = false;
11403e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande    }
11503e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande
11671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    public static CertType mapCertType(String certType) throws OMAException {
11771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        if (certType.equalsIgnoreCase("x509v3")) {
11871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist            return CertType.x509v3;
11971a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        } else if (certType.equalsIgnoreCase("802.1ar")) {
12071a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist            return CertType.IEEE;
12171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        } else {
12271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist            throw new OMAException("Invalid cert type: '" + certType + "'");
12371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        }
12471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    }
12571a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
12603e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande    private static EAPMethod mapEapMethod(int eapMethod, int phase2Method) {
12703e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        if (eapMethod == WifiEnterpriseConfig.Eap.TLS) {
12803e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            return new EAPMethod(EAP.EAPMethodID.EAP_TLS, null);
12903e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        } else if (eapMethod == WifiEnterpriseConfig.Eap.TTLS) {
13003e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            /* keep this table in sync with WifiEnterpriseConfig.Phase2 enum */
13103e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            final String innnerMethods[] = { null, "PAP", "MS-CHAP", "MS-CHAP-V2", null };
132af955ffa0082189fb688429732427c333f2491ceVinit Deshpande            return new EAPMethod(EAP.EAPMethodID.EAP_TTLS,
13303e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande                    new NonEAPInnerAuth(innnerMethods[phase2Method]));
13403e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        } else if (eapMethod == WifiEnterpriseConfig.Eap.PEAP) {
13503e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            /* restricting passpoint implementation from using PEAP */
13603e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            return null;
13703e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        } else if (eapMethod == WifiEnterpriseConfig.Eap.PWD) {
13803e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            /* restricting passpoint implementation from using EAP_PWD */
13903e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            return null;
14003e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        } else if (eapMethod == WifiEnterpriseConfig.Eap.SIM) {
14103e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            return new EAPMethod(EAP.EAPMethodID.EAP_SIM, null);
14203e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        } else if (eapMethod == WifiEnterpriseConfig.Eap.AKA) {
14303e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            return new EAPMethod(EAP.EAPMethodID.EAP_AKA, null);
14403e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        }
14503e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        /*
14603e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            TODO: Uncomment this when AKA_PRIME is defined in WifiEnterpriseConfig
14703e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        else if (eapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME) {
14803e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            return new EAPMethod(EAP.EAPMethodID.EAP_AKAPrim, null);
14903e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        }
15003e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        */
15103e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande
152af955ffa0082189fb688429732427c333f2491ceVinit Deshpande        Log.d("PARSE-LOG", "Invalid eap method");
15303e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        return null;
15403e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande    }
15503e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande
15603e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande    private static CertType mapCertType(int eapMethod) {
15703e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        if (eapMethod == WifiEnterpriseConfig.Eap.TLS
15803e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande                || eapMethod == WifiEnterpriseConfig.Eap.TTLS) {
15903e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            return CertType.x509v3;
16003e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        } else {
161af955ffa0082189fb688429732427c333f2491ceVinit Deshpande            Log.d("PARSE-LOG", "Invalid cert type" + eapMethod);
16203e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande            return null;
16303e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande        }
16403e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande    }
16503e92b925a595a1a467290a2d54ca2602cce9b9eVinit Deshpande
16671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    public EAPMethod getEAPMethod() {
16771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        return mEAPMethod;
16871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    }
16971a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
17071a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    public String getRealm() {
17171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        return mRealm;
17271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    }
17371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
17471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    public String getImsi() {
17571a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        return mImsi;
17671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    }
17771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist
17877f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    public String getUserName() {
17977f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        return mUserName;
18077f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
18177f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
18277f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    public String getPassword() {
18377f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist        return mPassword;
18477f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist    }
18577f2b82a2e80af8da52c22d69a76def6d4209757Jan Nordqvist
18671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    @Override
18771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    public String toString() {
18871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist        return "Credential{" +
18971a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                "mCtime=" + Utils.toUTCString(mCtime) +
19071a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                ", mExpTime=" + Utils.toUTCString(mExpTime) +
19171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                ", mRealm='" + mRealm + '\'' +
19271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                ", mCheckAAACert=" + mCheckAAACert +
19371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                ", mUserName='" + mUserName + '\'' +
19471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                ", mPassword='" + mPassword + '\'' +
19571a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                ", mMachineManaged=" + mMachineManaged +
19671a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                ", mSTokenApp='" + mSTokenApp + '\'' +
19771a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                ", mShare=" + mShare +
19871a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                ", mEAPMethod=" + mEAPMethod +
19971a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                ", mCertType=" + mCertType +
20071a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                ", mFingerPrint=" + Utils.toHexString(mFingerPrint) +
20171a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                ", mImsi='" + mImsi + '\'' +
20271a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist                '}';
20371a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist    }
20471a988c8e9859244b83cd55bb6b6ee913fcaf95cJan Nordqvist}
205