package com.whatscloud.logic.sync.db; import android.content.Context; import android.os.Build; import android.text.TextUtils; import android.util.Log; import com.whatscloud.R; import com.whatscloud.config.db.SQLite3; import com.whatscloud.config.debug.Logging; import com.whatscloud.config.integration.WhatsAppInterface; import com.whatscloud.logic.root.RootCommand; import com.whatscloud.root.RootTools; import com.whatscloud.root.execution.Command; import java.io.*; import java.util.*; public class SQLite { Context mContext; public SQLite(Context context) { //-------------------------------- // Save context instance //-------------------------------- this.mContext = context; } public String getSQLCommand(String sql, String db) { //-------------------------------- // Build SQL command //-------------------------------- return SQLite3.PATH_TO_SQLITE3_BINARY + " " + SQLite3.SEPARATOR_PARAM + " " + db + " \"" + sql + "\""; } public List<HashMap<String, String>> select(final String[] columns, String tableName, String whereClause, String dbName) throws Exception { //-------------------------------- // Make sure we installed binaries! //-------------------------------- installBinaries(); //-------------------------------- // Create a list of rows //-------------------------------- final List<HashMap<String, String>> rows = new ArrayList<HashMap<String, String>>(); //-------------------------------- // Dirty hack to fix line breaks //-------------------------------- String columnsList = TextUtils.join(", ", columns).replace( "data", "replace( replace( data, X'0D', '' ), X'0A', '" + SQLite3.LINE_BREAK_CHAR + "' )" ); //-------------------------------- // Generate SQL statement //-------------------------------- String sql = "SELECT " + columnsList + " FROM " + tableName + " WHERE " + whereClause; //-------------------------------- // Execute the query //-------------------------------- Command sqlCommand = new Command(0, false, getSQLCommand(sql, dbName)) { @Override public void commandOutput(int id, String line) { //-------------------------------- // Add row to rows list //-------------------------------- rows.add(convertRowToHashMap(columns, line)); } @Override public void commandTerminated(int id, String error) { //-------------------------------- // Show error //-------------------------------- Log.e(Logging.TAG_NAME, error); } @Override public void commandCompleted(int id, int exitCode) {} }; //-------------------------------- // Execute the command //-------------------------------- RootTools.getShell(true).add(sqlCommand); //-------------------------------- // Wait for it... //-------------------------------- RootCommand.waitForFinish(sqlCommand); //-------------------------------- // Return rows //-------------------------------- return rows; } public String prepareForInsert(String data) { //-------------------------------- // Clean and wrap with quotes //-------------------------------- return "'" + data.replace("'", "''").replace("\"", "\\\"" ).replace("$", "\\$" ) + "'"; } public void insert(HashMap<String, String> row, String tableName, String dbName) throws Exception { //-------------------------------- // Make sure we installed binaries! //-------------------------------- installBinaries(); //-------------------------------- // Escape single-quotes //-------------------------------- for ( Map.Entry<String, String> column : row.entrySet() ) { //-------------------------------- // Apparently, SQLite3 escapes // single-quotes with another quote //-------------------------------- row.put(column.getKey(), prepareForInsert(column.getValue())); } //-------------------------------- // Convert columns to string //-------------------------------- String columnsList = TextUtils.join(", ", row.keySet()); //-------------------------------- // Convert values to string //-------------------------------- String valuesList = TextUtils.join(", ", row.values()); //-------------------------------- // Generate SQL statement //-------------------------------- String sql = "INSERT INTO " + tableName + " (" + columnsList + ") VALUES (" + valuesList + ")"; //-------------------------------- // Execute the query //-------------------------------- RootCommand.execute(getSQLCommand(sql, dbName)); } public void update(String tableName, HashMap<String, String> row, String whereClause, String dbName) throws Exception { //-------------------------------- // Make sure we installed binaries! //-------------------------------- installBinaries(); //-------------------------------- // Prepare set array //-------------------------------- List<String> set = new ArrayList<String>(); //-------------------------------- // Add items, escape single-quotes //-------------------------------- for ( Map.Entry<String, String> column : row.entrySet() ) { //-------------------------------- // Apparently, SQLite3 escapes // single-quotes with another quote //-------------------------------- set.add(column.getKey() + " = '" + column.getValue().replace("'", "''") + "'"); } //-------------------------------- // Convert to CSV string //-------------------------------- String setList = TextUtils.join(", ", set); //-------------------------------- // Generate SQL statement //-------------------------------- String sql = "UPDATE " + tableName + " SET " + setList + " WHERE " + whereClause; //-------------------------------- // Execute the query //-------------------------------- RootCommand.execute(getSQLCommand(sql, dbName)); } public HashMap<String, String> convertRowToHashMap(String[] columns, String line) { //-------------------------------- // Split row into columns //-------------------------------- List<String> values = new ArrayList<String>(Arrays.asList(line.split(SQLite3.SEPARATOR_CHAR))); //-------------------------------- // Not enough values? //-------------------------------- if ( values.size() < columns.length ) { //-------------------------------- // Add empty values //-------------------------------- for ( int i = values.size(); i < columns.length; i++ ) { values.add( "" ); } } //-------------------------------- // Create a new hash map //-------------------------------- HashMap<String, String> row = new HashMap<String, String>(); //-------------------------------- // Add all columns to hash map //-------------------------------- for ( int i = 0; i < values.size(); i++ ) { row.put(columns[i], values.get(i)); } //-------------------------------- // Return row //-------------------------------- return row; } public void installBinaries() throws Exception { //-------------------------------- // Make sure we have root! //-------------------------------- if (!RootTools.isRootAvailable()) { throw new Exception(mContext.getString(R.string.noRootError)); } //-------------------------------- // Make sure we have permission //-------------------------------- if (!RootTools.isAccessGiven()) { throw new Exception(mContext.getString(R.string.noRootGrantedError)); } //-------------------------------- // Install busybox binary //-------------------------------- installBinary(WhatsAppInterface.PATH_TO_BUSYBOX_BINARY, R.raw.busybox_binary); //-------------------------------- // Get default sqlite3 binary //-------------------------------- int resource = R.raw.sqlite3_binary_4x; //-------------------------------- // Add support pre-5.x devices // (PIE enforcement) //-------------------------------- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { resource = R.raw.sqlite3_binary_5x; } //-------------------------------- // Install sqlite3 binary //-------------------------------- installBinary(SQLite3.PATH_TO_SQLITE3_BINARY, resource); } void installBinary(String path, int resource) throws Exception { //-------------------------------- // Get path to file //-------------------------------- File installationPath = new File( path ); //-------------------------------- // File exists? //-------------------------------- if ( installationPath.exists() ) { return; } //-------------------------------- // Open file from raw assets //-------------------------------- InputStream rawResource = mContext.getResources().openRawResource(resource); //-------------------------------- // Create output stream //-------------------------------- OutputStream internalStorage = new FileOutputStream(installationPath); //-------------------------------- // Define temp length variable //-------------------------------- int length; //-------------------------------- // Define temporary byte buffer //-------------------------------- byte[] buffer = new byte[1024]; //-------------------------------- // Read from raw resource // until we reach EOF //-------------------------------- while ( ( length = rawResource.read( buffer ) ) > 0 ) { //-------------------------------- // Write to internal storage //-------------------------------- internalStorage.write(buffer, 0, length); } //-------------------------------- // Close both streams //-------------------------------- rawResource.close(); internalStorage.close(); //-------------------------------- // CHMOD 777 (so we can exec) //-------------------------------- String chmodCommand = "chmod 777 " + installationPath.getAbsolutePath(); //-------------------------------- // Execute the command //-------------------------------- RootCommand.execute(chmodCommand); } }