/*
		TWRP 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.

		TWRP 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 TWRP.  If not, see <http://www.gnu.org/licenses/>.
*/

#define __STDC_FORMAT_MACROS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <inttypes.h>

// for setcap and getcap
#include <sys/capability.h>
#include <sys/xattr.h>
#include <linux/xattr.h>

#include "orscmd.h"
#include "../variables.h"

void print_version(void) {
	printf("TWRP openrecoveryscript command line tool, TWRP version %s\n\n", TW_VERSION_STR);
}

void print_usage(void) {
	print_version();
	printf("Allows command line usage of TWRP via openrecoveryscript commands.\n");
	printf("Some common commands include:\n");
	printf("  install /path/to/update.zip\n");
	printf("  backup <SDCRBAEM> [backupname]\n");
	printf("  restore <SDCRBAEM> [backupname]\n");
	printf("  wipe <partition name>\n");
	printf("  sideload\n");
	printf("  set <variable> [value]\n");
	printf("  decrypt <password>\n");
	printf("  remountrw\n");
	printf("\nSee more documentation at https://twrp.me/faq/openrecoveryscript.html\n");
}

int do_setcap(const char* filename, const char* capabilities)
{
	uint64_t caps;
	if (sscanf(capabilities, "%" SCNi64, &caps) != 1)
	{
		printf("setcap: invalid capabilities \"%s\"\n", filename);
		return 1;
	}
	struct vfs_cap_data cap_data;
	memset(&cap_data, 0, sizeof(cap_data));
	cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
	cap_data.data[0].permitted = (uint32_t) (caps & 0xffffffff);
	cap_data.data[0].inheritable = 0;
	cap_data.data[1].permitted = (uint32_t) (caps >> 32);
	cap_data.data[1].inheritable = 0;
	if (setxattr(filename, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) < 0) {
		printf("setcap of %s to %" PRIx64 " failed: %s\n",
				filename, caps, strerror(errno));
		return 1;
	}
	return 0;
}

int do_getcap(const char* filename)
{
	struct vfs_cap_data cap_data;
	memset(&cap_data, 0, sizeof(cap_data));
	int rc = getxattr(filename, XATTR_NAME_CAPS, &cap_data, sizeof(vfs_cap_data));
	if (rc > 0)
	{
		uint64_t caps = (uint64_t) cap_data.data[1].permitted << 32 | cap_data.data[0].permitted;
		printf("0x%" PRIx64 "\n", caps);
	}
	else
		printf("getcap of %s failed: %s\n", filename, strerror(errno));

	return rc > 0;
}

int main(int argc, char **argv) {
	int read_fd, write_fd, index;
	char command[1024], result[512];

	if (argc < 2 || strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "?") == 0 || strcmp(argv[1], "-h") == 0) {
		print_usage();
		return 0;
	}
	if (strcmp(argv[1], "version") == 0 || strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) {
		print_version();
		return 0;
	}

	if (strcmp(argv[1], "setcap") == 0) {
		if (argc != 4)
		{
			printf("Usage: setcap filename capabilities\n\n"
				"capabilities must be specified as a number. Prefix with 0x for hexadecimal.\n");
			return 1;
		}
		return do_setcap(argv[2], argv[3]);
	}

	if (strcmp(argv[1], "getcap") == 0) {
		if (argc != 3)
		{
			printf("Usage: getcap filename\n");
			return 1;
		}
		return do_getcap(argv[2]);
	}

	sprintf(command, "%s", argv[1]);
	for (index = 2; index < argc; index++) {
		sprintf(command, "%s %s", command, argv[index]);
	}

	write_fd = open(ORS_INPUT_FILE, O_WRONLY);
	if (write_fd < 0) {
		printf("TWRP does not appear to be running. Waiting for TWRP to start . . .\n");
		printf("Press CTRL + C to quit.\n");
		while (write_fd < 0)
			write_fd = open(ORS_INPUT_FILE, O_WRONLY);
	}
	if (write(write_fd, command, sizeof(command)) != sizeof(command)) {
		printf("Error sending command.\n");
		close(write_fd);
		return -1;
	}
	read_fd = open(ORS_OUTPUT_FILE, O_RDONLY);
	if (read_fd < 0) {
		printf("Unable to open %s for read.\n", ORS_OUTPUT_FILE);
		return -1;
	}
	memset(&result, 0, sizeof(result));
	while (read(read_fd, &result, sizeof(result)) > 0) {
		result[510] = '\n';
		result[511] = '\0';
		printf("%s", result);
		memset(&result, 0, sizeof(result));
	}
	close(write_fd);
	close(read_fd);
	return 0;
}
