/*
 *********************************************************
 *   Copyright 2004 Sveasoft AB  All Rights Reserved *
 *********************************************************

 This is UNPUBLISHED SOURCE CODE of Sveasoft AB

 Written by Jesse Brown <wolf@daforest.org> with parts by "lonewolf"

 Some parts copied from upgrade.c

 This software should be used as a reference only, and it not
 intended for production use!
*/

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>

#include <broadcom.h>
#include <cyutils.h>

#define MIN_BUF_SIZE    4096

extern struct nvram_tuple router_defaults[];
static int restore_ret;

int sr_file_write(webs_t wp, int *total, char *boundary)
{
	char upload_fifo[] = "/tmp/nvramrestore.tgz";
	FILE *fifo = NULL;
	char *restore_argv[4];
	pid_t pid;
	char *buf = NULL;
	int count, ret = 0;
	long flags = -1;
	int size = BUFSIZ;

	restore_argv[0] = "tar";
	restore_argv[1] = "-zxf";
	restore_argv[2] = upload_fifo;
	restore_argv[3] = NULL;

	if (!(fifo = fopen(upload_fifo, "w"))) {
			ret = errno;
			goto err;
	}

	/* Set nonblock on the socket so we can timeout */
	if(!do_ssl){
		if ((flags = fcntl(fileno(wp), F_GETFL)) < 0 ||
			fcntl(fileno(wp), F_SETFL, flags | O_NONBLOCK) < 0) {
				ret = errno;
				goto err;
		}
	}

	if (size < MIN_BUF_SIZE)
                size = MIN_BUF_SIZE;
        if ((buf = malloc(size)) == NULL) {
                ret = ENOMEM;
                goto err;
	}

	while (total && *total)
	{
		if(do_ssl)
		{
			if (size > *total) size=*total;
			count = wfread(buf, 1, size, wp);
		}
		else
		{
			if (waitfor(fileno(wp), 5) <= 0)
			{
			        cprintf("waitfor timeout 5 secs\n");
			        break;
			}
			count = safe_fread(buf, 1, size, wp);
			if (!count && (ferror(wp) || feof(wp)))
				break;
		}

			*total -= count;
			safe_fwrite(buf, 1, count, fifo);

	}
	fclose(fifo);
	fifo = NULL;

	chdir("/tmp");

	ret = _eval(restore_argv, NULL, 0, &pid);

	waitpid(pid, &ret, 0);
	cprintf("done\n");

	if(!do_ssl){
			/* Reset nonblock on the socket */
			if (fcntl(fileno(wp), F_SETFL, flags) < 0) {
				ret = errno;
				goto err;
			}
	}
	
err:
	if (buf)
		free(buf);
	if (fifo)
		fclose(fifo);

	unlink(upload_fifo);

	return ret;
}


void nv_file_in(char *url, webs_t wp, int len, char *boundary)
{
	struct nvram_tuple *v;
	char buf[1024], *var;
	int nvrtotal=0;
	FILE *fp = NULL;
	restore_ret = EINVAL;

        /* Look for our part */
	while (len > 0) {
		if (!wfgets(buf, MIN(len + 1, sizeof(buf)), wp))
			return;
		len -= strlen(buf);
		if (!strncasecmp(buf, "Content-Disposition:", 20)){
			if(strstr(buf, "name=\"file\"")){
				break;
			}
		}
	}
	
	/* Skip boundary and headers */
	while (len > 0) {
		if (!wfgets(buf, sizeof(buf), wp))
			return;
		len -= strlen(buf);
		if (!strcmp(buf, "\n") || !strcmp(buf, "\r\n"))
			break;
	}

	restore_ret = sr_file_write(wp, &len, boundary);

	restore_ret += chdir("/tmp/nvram");

	if(restore_ret == 0) {
		system("/bin/cat /tmp/nvram/* > /tmp/nvr-total");

		fp = fopen("/tmp/nvr-total","r");
		while((fgetc(fp))!=-1)
			nvrtotal++;
		fclose(fp);

		// Keep the nvram total at safe value (1768 bytes under)
		if(nvrtotal > 31000)
			restore_ret = 99;

		unlink("/tmp/nvr-total");
	}

	if(restore_ret == 0) {
		for(v = router_defaults ; v->name; v++) {
			var = v->name;
			file2nvram(var, var);
		}
	}

	/* Slurp anything remaining in the request */
	while (len--)
		if(do_ssl)
#ifdef HAVE_OPENSSL
		     BIO_gets((BIO *)wp,buf,1);
#elif defined(HAVE_MATRIXSSL)
		     matrixssl_gets(wp,buf,1);
#else
;
#endif
	     else
		(void) fgetc(wp);

	chdir("/www");
}

void sr_config_cgi(char *path, webs_t wp)
{
        if(restore_ret != 0)
                do_ej("Fail.asp", wp);
        else
                do_ej("Success.asp", wp);

        websDone(wp, 200);

	/* Reboot if successful */
	if (restore_ret == 0){
		nvram_commit();
		sleep(1);
		sys_reboot();
	}
}

int nv_file_out(char *path, webs_t wp)
{
	FILE *fp = NULL;
	int ret=0;
	char *var, *vertag;
	pid_t pid;
	struct nvram_tuple *v;
	char *backup_argv[5];

	backup_argv[0] = "tar";
	backup_argv[1] = "-zcf";
	backup_argv[2] = "/tmp/nvrambak.tgz";
	backup_argv[3] = "nvram";
	backup_argv[4] = NULL;

	system("rm -rf /tmp/nvrambak.tgz");
	system("mkdir /tmp/nvram");
	system("touch /tmp/nvram/1-DO-NOT-EDIT-THESE-FILES");
	chdir("/tmp/nvram");
	time_t nvbaktime;

	for(v = router_defaults ; v->name; v++) {
			var = v->name;
			nvram2file(var, var);
	}

	if( (fp=fopen("/tmp/nvram/1-version.txt", "w")) ){
		time(&nvbaktime);
		if(time(0) > (unsigned long)60*60*24*365)
			sprintf(vertag,"Version: %s Built: %s %s NVBakTime: %s\n",CYBERTAN_VERSION,__DATE__,__TIME__,rfctime(&nvbaktime));
		else
			sprintf(vertag,"Version: %s Built: %s %s NVBakTime: Not Available\n",CYBERTAN_VERSION,__DATE__,__TIME__);

		fputs(vertag, fp);
		fclose(fp);
	}

	chdir("/tmp");
	ret = _eval(backup_argv, NULL, 0, &pid);

	waitpid(pid, &ret, 0);

	system("sync");
	system("rm -rf /tmp/nvram");
	system("rm -rf /tmp/nvram");

	chdir("/www");

        do_file("/tmp/nvrambak.tgz", wp);

	return ret;
}
