Support encrypted backup files

Also includes features merged from:

Update twrpTar by kokotas

Revised function entryExists().
Added function to get archive's uncompressed size.
Added option to exclude item(s) from the archive.
Revised forks()

http://review.teamw.in/#/c/590/

Change-Id: I01fa2c81643161984eff2625247af75990684bd9
diff --git a/twrp-functions.cpp b/twrp-functions.cpp
index a0194b3..d9014b3 100644
--- a/twrp-functions.cpp
+++ b/twrp-functions.cpp
@@ -12,17 +12,23 @@
 #include <sys/sendfile.h>
 #include <sys/stat.h>
 #include <sys/vfs.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 #ifdef ANDROID_RB_POWEROFF
 	#include "cutils/android_reboot.h"
 #endif
 #include <iostream>
 #include <fstream>
+#include <sstream>
 #include "twrp-functions.hpp"
 #include "partitions.hpp"
 #include "twcommon.h"
 #include "data.hpp"
 #include "variables.h"
 #include "bootloader.h"
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+	#include "openaes/inc/oaes_lib.h"
+#endif
 
 extern "C" {
 	#include "libcrecovery/common.h"
@@ -455,6 +461,30 @@
 	return -1;
 }
 
+vector<string> TWFunc::split_string(const string &in, char del, bool skip_empty) {
+	vector<string> res;
+
+	if (in.empty() || del == '\0')
+		return res;
+
+	string field;
+	istringstream f(in);
+	if (del == '\n') {
+		while(getline(f, field)) {
+			if (field.empty() && skip_empty)
+				continue;
+			res.push_back(field);
+		}
+	} else {
+		while(getline(f, field, del)) {
+			if (field.empty() && skip_empty)
+				continue;
+			res.push_back(field);
+		}
+	}
+	return res;
+}
+
 timespec TWFunc::timespec_diff(timespec& start, timespec& end)
 {
 	timespec temp;
@@ -678,3 +708,172 @@
 		return false;
 	return true;
 }
+
+int TWFunc::Get_File_Type(string fn) {
+	string::size_type i = 0;
+	int firstbyte = 0, secondbyte = 0;
+	char header[3];
+        
+	ifstream f;
+	f.open(fn.c_str(), ios::in | ios::binary);
+	f.get(header, 3);
+	f.close();
+	firstbyte = header[i] & 0xff;
+	secondbyte = header[++i] & 0xff;
+
+	if (firstbyte == 0x1f && secondbyte == 0x8b) {
+		return 1; // Compressed
+	} else if (firstbyte == 0x4f && secondbyte == 0x41) {
+		return 2; // Encrypted
+	} else {
+		return 0; // Unknown
+	}
+
+	return 0;
+}
+
+int TWFunc::Try_Decrypting_File(string fn, string password) {
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+	OAES_CTX * ctx = NULL;
+	uint8_t _key_data[32] = "";
+	FILE *f;
+	uint8_t buffer[4096];
+	uint8_t *buffer_out = NULL;
+	uint8_t *ptr = NULL;
+	size_t read_len = 0, out_len = 0;
+	int firstbyte = 0, secondbyte = 0, key_len;
+	size_t _j = 0;
+	size_t _key_data_len = 0;
+
+	// mostly kanged from OpenAES oaes.c
+	for( _j = 0; _j < 32; _j++ )
+		_key_data[_j] = _j + 1;
+	_key_data_len = password.size();
+	if( 16 >= _key_data_len )
+		_key_data_len = 16;
+	else if( 24 >= _key_data_len )
+		_key_data_len = 24;
+	else
+	_key_data_len = 32;
+	memcpy(_key_data, password.c_str(), password.size());
+
+	ctx = oaes_alloc();
+	if (ctx == NULL) {
+		LOGERR("Failed to allocate OAES\n");
+		return -1;
+	}
+
+	oaes_key_import_data(ctx, _key_data, _key_data_len);
+
+	f = fopen(fn.c_str(), "rb");
+	if (f == NULL) {
+		LOGERR("Failed to open '%s' to try decrypt\n", fn.c_str());
+		return -1;
+	}
+	read_len = fread(buffer, sizeof(uint8_t), 4096, f);
+	if (read_len <= 0) {
+		LOGERR("Read size during try decrypt failed\n");
+		fclose(f);
+		return -1;
+	}
+	if (oaes_decrypt(ctx, buffer, read_len, NULL, &out_len) != OAES_RET_SUCCESS) {
+		LOGERR("Error: Failed to retrieve required buffer size for trying decryption.\n");
+		fclose(f);
+		return -1;
+	}
+	buffer_out = (uint8_t *) calloc(out_len, sizeof(char));
+	if (buffer_out == NULL) {
+		LOGERR("Failed to allocate output buffer for try decrypt.\n");
+		fclose(f);
+		return -1;
+	}
+	if (oaes_decrypt(ctx, buffer, read_len, buffer_out, &out_len) != OAES_RET_SUCCESS) {
+		LOGERR("Failed to decrypt file '%s'\n", fn.c_str());
+		fclose(f);
+		free(buffer_out);
+		return 0;
+	}
+	fclose(f);
+	if (out_len < 2) {
+		LOGINFO("Successfully decrypted '%s' but read length %i too small.\n", fn.c_str(), out_len);
+		free(buffer_out);
+		return 1; // Decrypted successfully
+	}
+	ptr = buffer_out;
+	firstbyte = *ptr & 0xff;
+	ptr++;
+	secondbyte = *ptr & 0xff;
+	if (firstbyte == 0x1f && secondbyte == 0x8b) {
+		LOGINFO("Successfully decrypted '%s' and file is compressed.\n", fn.c_str());
+		free(buffer_out);
+		return 3; // Compressed
+	}
+	if (out_len >= 262) {
+		ptr = buffer_out + 257;
+		if (strncmp((char*)ptr, "ustar", 5) == 0) {
+			LOGINFO("Successfully decrypted '%s' and file is tar format.\n", fn.c_str());
+			free(buffer_out);
+			return 2; // Tar
+		}
+	}
+	free(buffer_out);
+	LOGINFO("No errors decrypting '%s' but no known file format.\n", fn.c_str());
+	return 1; // Decrypted successfully
+#else
+	LOGERR("Encrypted backup support not included.\n");
+	return -1;
+#endif
+}
+
+bool TWFunc::Try_Decrypting_Backup(string Restore_Path, string Password) {
+	DIR* d;
+
+	string Filename;
+	Restore_Path += "/";
+	d = opendir(Restore_Path.c_str());
+	if (d == NULL) {
+		LOGERR("Error opening '%s'\n", Restore_Path.c_str());
+		return false;
+	}
+
+	struct dirent* de;
+	while ((de = readdir(d)) != NULL) {
+		Filename = Restore_Path;
+		Filename += de->d_name;
+		if (TWFunc::Get_File_Type(Filename) == 2) {
+			if (TWFunc::Try_Decrypting_File(Filename, Password) < 2) {
+				DataManager::SetValue("tw_restore_password", ""); // Clear the bad password
+				DataManager::SetValue("tw_restore_display", "");  // Also clear the display mask
+				closedir(d);
+				return false;
+			}
+		}
+	}
+	closedir(d);
+	return true;
+}
+
+int TWFunc::Wait_For_Child(pid_t pid, int *status, string Child_Name) {
+	pid_t rc_pid;
+
+	rc_pid = waitpid(pid, status, 0);
+	if (rc_pid > 0) {
+		if (WEXITSTATUS(*status) == 0)
+			LOGINFO("%s process ended with RC=%d\n", Child_Name.c_str(), WEXITSTATUS(*status)); // Success
+		else if (WIFSIGNALED(*status)) {
+			LOGINFO("%s process ended with signal: %d\n", Child_Name.c_str(), WTERMSIG(*status)); // Seg fault or some other non-graceful termination
+			return -1;
+		} else if (WEXITSTATUS(*status) != 0) {
+			LOGINFO("%s process ended with ERROR=%d\n", Child_Name.c_str(), WEXITSTATUS(*status)); // Graceful exit, but there was an error
+			return -1;
+		}
+	} else { // no PID returned
+		if (errno == ECHILD)
+			LOGINFO("%s no child process exist\n", Child_Name.c_str());
+		else {
+			LOGINFO("%s Unexpected error\n", Child_Name.c_str());
+			return -1;
+		}
+	}
+	return 0;
+}