com.csipsimple.backup.SipProfileJson.java Source code

Java tutorial

Introduction

Here is the source code for com.csipsimple.backup.SipProfileJson.java

Source

/**
 * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
 * This file is part of CSipSimple.
 *
 *  CSipSimple 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.
 *  If you own a pjsip commercial license you can also redistribute it
 *  and/or modify it under the terms of the GNU Lesser General Public License
 *  as an android library.
 *
 *  CSipSimple 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 CSipSimple.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.csipsimple.backup;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.text.format.DateFormat;

import com.csipsimple.api.SipManager;
import com.csipsimple.api.SipProfile;
import com.csipsimple.db.DBProvider;
import com.csipsimple.models.Filter;
import com.csipsimple.utils.CallHandlerPlugin;
import com.csipsimple.utils.Log;
import com.csipsimple.utils.PreferencesWrapper;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public final class SipProfileJson {

    private final static String THIS_FILE = "SipProfileJson";
    private final static String FILTER_KEY = "filters";

    private SipProfileJson() {
    }

    private static JSONObject serializeBaseSipProfile(SipProfile profile) {

        ContentValues cv = profile.getDbContentValues();
        Columns cols = getSipProfileColumns(true);
        return cols.contentValueToJSON(cv);
    }

    private static JSONObject serializeBaseFilter(Filter filter) {

        ContentValues cv = filter.getDbContentValues();
        Columns cols = new Columns(Filter.FULL_PROJ, Filter.FULL_PROJ_TYPES);
        return cols.contentValueToJSON(cv);
    }

    public static JSONObject serializeSipProfile(Context context, SipProfile profile) {
        JSONObject jsonProfile = serializeBaseSipProfile(profile);
        JSONArray jsonFilters = new JSONArray();

        Cursor c = Filter.getFiltersCursorForAccount(context, profile.id);
        int numRows = c.getCount();
        c.moveToFirst();
        for (int i = 0; i < numRows; ++i) {
            Filter f = new Filter(c);
            try {
                jsonFilters.put(i, serializeBaseFilter(f));
            } catch (JSONException e) {
                Log.e(THIS_FILE, "Impossible to add fitler", e);
            }
            c.moveToNext();
        }
        c.close();

        try {
            jsonProfile.put(FILTER_KEY, jsonFilters);
        } catch (JSONException e) {
            Log.e(THIS_FILE, "Impossible to add fitlers", e);
        }

        return jsonProfile;
    }

    public static JSONArray serializeSipProfiles(Context ctxt) {

        JSONArray jsonSipProfiles = new JSONArray();
        Cursor c = ctxt.getContentResolver().query(SipProfile.ACCOUNT_URI, DBProvider.ACCOUNT_FULL_PROJECTION, null,
                null, null);
        if (c != null) {
            try {
                while (c.moveToNext()) {
                    SipProfile account = new SipProfile(c);
                    JSONObject p = serializeSipProfile(ctxt, account);
                    try {
                        jsonSipProfiles.put(jsonSipProfiles.length(), p);
                    } catch (JSONException e) {
                        Log.e(THIS_FILE, "Impossible to add profile", e);
                    }
                }

            } catch (Exception e) {
                Log.e(THIS_FILE, "Error on looping over sip profiles", e);
            } finally {
                c.close();
            }
        }

        // Add negative fake accounts

        Map<String, String> callHandlers = CallHandlerPlugin.getAvailableCallHandlers(ctxt);
        for (String packageName : callHandlers.keySet()) {
            final Long externalAccountId = CallHandlerPlugin.getAccountIdForCallHandler(ctxt, packageName);
            SipProfile gsmProfile = new SipProfile();
            gsmProfile.id = externalAccountId;
            JSONObject p = serializeSipProfile(ctxt, gsmProfile);
            try {
                jsonSipProfiles.put(jsonSipProfiles.length(), p);
            } catch (JSONException e) {
                Log.e(THIS_FILE, "Impossible to add profile", e);
            }
        }

        return jsonSipProfiles;
    }

    public static JSONObject serializeSipSettings(Context ctxt) {
        PreferencesWrapper prefs = new PreferencesWrapper(ctxt);
        return prefs.serializeSipSettings();
    }

    private static final String KEY_ACCOUNTS = "accounts";
    private static final String KEY_SETTINGS = "settings";

    /**
     * Save current sip configuration
     * 
     * @param ctxt
     * @return
     */
    public static boolean saveSipConfiguration(Context ctxt, String filePassword) {
        File dir = PreferencesWrapper.getConfigFolder(ctxt);
        if (dir != null) {
            Date d = new Date();
            File file = new File(dir.getAbsoluteFile() + File.separator + "backup_"
                    + DateFormat.format("yy-MM-dd_kkmmss", d) + ".json");
            Log.d(THIS_FILE, "Out dir " + file.getAbsolutePath());

            JSONObject configChain = new JSONObject();
            try {
                configChain.put(KEY_ACCOUNTS, serializeSipProfiles(ctxt));
            } catch (JSONException e) {
                Log.e(THIS_FILE, "Impossible to add profiles", e);
            }
            try {
                configChain.put(KEY_SETTINGS, serializeSipSettings(ctxt));
            } catch (JSONException e) {
                Log.e(THIS_FILE, "Impossible to add profiles", e);
            }

            try {
                // Create file
                OutputStream fos = new FileOutputStream(file);
                if (!TextUtils.isEmpty(filePassword)) {
                    Cipher c;
                    try {
                        c = Cipher.getInstance("AES");
                        SecretKeySpec k = new SecretKeySpec(filePassword.getBytes(), "AES");
                        c.init(Cipher.ENCRYPT_MODE, k);
                        fos = new CipherOutputStream(fos, c);
                    } catch (NoSuchAlgorithmException e) {
                        Log.e(THIS_FILE, "NoSuchAlgorithmException :: ", e);
                    } catch (NoSuchPaddingException e) {
                        Log.e(THIS_FILE, "NoSuchPaddingException :: ", e);
                    } catch (InvalidKeyException e) {
                        Log.e(THIS_FILE, "InvalidKeyException :: ", e);
                    }
                }
                FileWriter fstream = new FileWriter(file.getAbsoluteFile());
                BufferedWriter out = new BufferedWriter(fstream);
                out.write(configChain.toString(2));
                // Close the output stream
                out.close();
                return true;
            } catch (Exception e) {
                // Catch exception if any
                Log.e(THIS_FILE, "Impossible to save config to disk", e);
                return false;
            }
        }
        return false;
    }

    // --- RESTORE PART --- //
    private static boolean restoreSipProfile(JSONObject jsonObj, ContentResolver cr) {
        // Restore accounts
        Columns cols;
        ContentValues cv;

        cols = getSipProfileColumns(false);
        cv = cols.jsonToContentValues(jsonObj);

        long profileId = cv.getAsLong(SipProfile.FIELD_ID);
        if (profileId >= 0) {
            Uri insertedUri = cr.insert(SipProfile.ACCOUNT_URI, cv);
            profileId = ContentUris.parseId(insertedUri);
        }
        // TODO : else restore call handler in private db

        // Restore filters
        cols = new Columns(Filter.FULL_PROJ, Filter.FULL_PROJ_TYPES);
        try {
            JSONArray filtersObj = jsonObj.getJSONArray(FILTER_KEY);
            Log.d(THIS_FILE, "We have filters for " + profileId + " > " + filtersObj.length());
            for (int i = 0; i < filtersObj.length(); i++) {
                JSONObject filterObj = filtersObj.getJSONObject(i);
                // Log.d(THIS_FILE, "restoring "+filterObj.toString(4));
                cv = cols.jsonToContentValues(filterObj);
                cv.put(Filter.FIELD_ACCOUNT, profileId);
                cr.insert(SipManager.FILTER_URI, cv);
            }
        } catch (JSONException e) {
            Log.e(THIS_FILE, "Error while restoring filters", e);
        }

        return false;
    }

    public static void restoreSipSettings(Context ctxt, JSONObject settingsObj) {
        PreferencesWrapper prefs = new PreferencesWrapper(ctxt);
        prefs.restoreSipSettings(settingsObj);
    }

    /**
     * Restore a sip configuration
     * 
     * @param ctxt
     * @param fileToRestore
     * @return
     */
    public static boolean restoreSipConfiguration(Context ctxt, File fileToRestore, String filePassword) {
        if (fileToRestore == null || !fileToRestore.isFile()) {
            return false;
        }

        StringBuffer contentBuf = new StringBuffer();

        try {
            BufferedReader buf;
            String line;
            InputStream is = new FileInputStream(fileToRestore);
            if (!TextUtils.isEmpty(filePassword)) {
                Cipher c;
                try {
                    c = Cipher.getInstance("AES");
                    SecretKeySpec k = new SecretKeySpec(filePassword.getBytes(), "AES");
                    c.init(Cipher.ENCRYPT_MODE, k);
                    is = new CipherInputStream(is, c);
                } catch (NoSuchAlgorithmException e) {
                    Log.e(THIS_FILE, "NoSuchAlgorithmException :: ", e);
                } catch (NoSuchPaddingException e) {
                    Log.e(THIS_FILE, "NoSuchPaddingException :: ", e);
                } catch (InvalidKeyException e) {
                    Log.e(THIS_FILE, "InvalidKeyException :: ", e);
                }
            }

            InputStreamReader fr = new InputStreamReader(is);
            buf = new BufferedReader(fr);
            while ((line = buf.readLine()) != null) {
                contentBuf.append(line);
            }
            fr.close();
        } catch (FileNotFoundException e) {
            Log.e(THIS_FILE, "Error while restoring", e);
        } catch (IOException e) {
            Log.e(THIS_FILE, "Error while restoring", e);
        }

        JSONArray accounts = null;
        JSONObject settings = null;
        // Parse json if some string here
        if (contentBuf.length() > 0) {
            try {
                JSONObject mainJSONObject = new JSONObject(contentBuf.toString());
                // Retrieve accounts
                accounts = mainJSONObject.getJSONArray(KEY_ACCOUNTS);
                // Retrieve settings
                settings = mainJSONObject.getJSONObject(KEY_SETTINGS);

            } catch (JSONException e) {
                Log.e(THIS_FILE, "Error while parsing saved file", e);
            }
        } else {
            return false;
        }

        if (accounts != null && accounts.length() > 0) {
            restoreSipAccounts(ctxt, accounts);
        }

        if (settings != null) {
            restoreSipSettings(ctxt, settings);
            return true;
        }

        return false;
    }

    public static void restoreSipAccounts(Context ctxt, JSONArray accounts) {
        ContentResolver cr = ctxt.getContentResolver();
        // Clear old existing accounts
        cr.delete(SipProfile.ACCOUNT_URI, "1", null);
        cr.delete(SipManager.FILTER_URI, "1", null);

        // Add each accounts
        for (int i = 0; i < accounts.length(); i++) {
            try {
                JSONObject account = accounts.getJSONObject(i);
                restoreSipProfile(account, cr);
            } catch (JSONException e) {
                Log.e(THIS_FILE, "Unable to parse item " + i, e);
            }
        }
    }

    private static Columns getSipProfileColumns(boolean only_secure) {
        Columns cols = new Columns(DBProvider.ACCOUNT_FULL_PROJECTION, DBProvider.ACCOUNT_FULL_PROJECTION_TYPES);
        if (only_secure) {
            // Never backup password
            cols.removeColumn(SipProfile.FIELD_DATA);
        }
        return cols;
    }
}