/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * unity-webapps-permissions.c
 * Copyright (C) Canonical LTD 2011
 *
 * Author: Robert Carr <racarr@canonical.com>
 *
 unity-webapps is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * unity-webapps 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
 */

#include "unity-webapps-permissions.h"
#include <gio/gio.h>
#define G_SETTINGS_ENABLE_BACKEND
#include <gio/gsettingsbackend.h>
#include <json-glib/json-glib.h>

#define UNITY_WEBAPPS_SCHEMA_NAME "com.canonical.unity.webapps"
#define DONTASK_DOMAINS_KEY "dontask-domains"
#define ALLOWED_DOMAINS_KEY "allowed-domains"
#define INTEGRATION_ALLOWED_KEY "integration-allowed"
#define PREAUTHORIZED_DOMAINS_KEY "preauthorized-domains"

static gboolean _unity_webapps_permissions_use_memory_backend = FALSE;
static const gchar *_unity_webapps_permissions_test_schema_path = NULL;
static GSettingsBackend *_unity_webapps_permissions_memory_backend = NULL;
void unity_webapps_permissions_tests_use_memory_backend(const gchar *schema_path)
{
	_unity_webapps_permissions_use_memory_backend = TRUE;
	_unity_webapps_permissions_memory_backend = g_memory_settings_backend_new();
	_unity_webapps_permissions_test_schema_path = schema_path;
}

gboolean
unity_webapps_permissions_schema_exists ()
{
	const gchar *const *schemas;
	guint i, len;

	// Skip the test when running unit tests & using custom backends/paths
	if (_unity_webapps_permissions_use_memory_backend
		&& _unity_webapps_permissions_test_schema_path)
		return TRUE;

	schemas = g_settings_list_schemas();

	len = g_strv_length ((gchar  **)schemas);

	for (i = 0; i < len; i++)
		{
			
			if (g_strcmp0 (schemas[i], UNITY_WEBAPPS_SCHEMA_NAME) == 0)
				{
					return TRUE;
				}
		}
	g_critical("Failed to find '%s' schema", UNITY_WEBAPPS_SCHEMA_NAME);

	return FALSE;
}

static void
filter_settings_strv (GSettings *settings, const gchar *key, const gchar *str)
{
	gchar **it1, **it2;
	gchar **strv = g_settings_get_strv(settings, key);

	for (it1 = strv, it2 = strv; it2 && *it2; it2++)
		{
			if (g_strcmp0 (*it2, str) != 0)
				{
					*it1 = *it2;
					it1++;
				}
		}
	*it1 = NULL;

	g_settings_set_strv (settings, key, (const gchar *const *)strv);
	g_strfreev (strv);
}

static gboolean
is_domain_found_for_keyname(GSettings * settings,
							const char * const keyname,
							const char * const domain)
{
	gboolean ret;
	gint i;
	gchar **domains;

	g_return_val_if_fail(settings != NULL, FALSE);
	g_return_val_if_fail(keyname != NULL, FALSE);
	g_return_val_if_fail(domain != NULL, FALSE);

	domains = g_settings_get_strv(settings, keyname);

	if (!domains)
		{
			return FALSE;
		}

	ret = FALSE;

	for (i = 0; domains[i] != NULL; i++)
		{
			if (g_strcmp0(domains[i], domain) == 0)
				{
					ret = TRUE;
					break;
				}
		}

	g_strfreev (domains);

	return ret;
}

static GSettings *
_unity_webapps_permissions_new_settings(void)
{
	GSettings *settings = NULL;
	if (_unity_webapps_permissions_use_memory_backend
		&& _unity_webapps_permissions_test_schema_path)
		{
			GSettingsSchemaSource *source =
				g_settings_schema_source_new_from_directory(
				    _unity_webapps_permissions_test_schema_path, NULL, TRUE, NULL);

			GSettingsSchema *schema =
				g_settings_schema_source_lookup(source, UNITY_WEBAPPS_SCHEMA_NAME, FALSE);

			settings = g_settings_new_full(schema,
										   _unity_webapps_permissions_memory_backend,
										   NULL);
		}
	else
		settings = g_settings_new(UNITY_WEBAPPS_SCHEMA_NAME);
	return settings;
}

/*
 * Add a domain to the list of "dontask" domains which should be blacklisted from requesting permissions
 */
void
unity_webapps_permissions_dontask_domain (const gchar *domain)
{
	GSettings *settings;
	gchar **dontask_domains, **new_dontask_domains;
	int i, length;

	if (unity_webapps_permissions_schema_exists () == FALSE)
		{
			return;
		}

	settings = _unity_webapps_permissions_new_settings();
	if (!settings)
		return;

	g_settings_delay (settings);

	filter_settings_strv (settings, ALLOWED_DOMAINS_KEY, domain);

	dontask_domains = NULL;
	new_dontask_domains = NULL;

	if (!is_domain_found_for_keyname (settings, DONTASK_DOMAINS_KEY, domain))
		{
			dontask_domains = g_settings_get_strv (settings, DONTASK_DOMAINS_KEY);
			
			length = g_strv_length (dontask_domains);
			
			new_dontask_domains = g_malloc (sizeof(gchar *) * (length + 2));
			
			for (i = 0; i < length; i++)
				{
					new_dontask_domains[i] = g_strdup (dontask_domains[i]);
				}
			
			new_dontask_domains[length] = g_strdup (domain);
			new_dontask_domains[length+1] = NULL;
			
			g_settings_set_strv(settings, DONTASK_DOMAINS_KEY, (const gchar * const*)new_dontask_domains);
		}

	g_settings_apply (settings);
	g_object_unref (G_OBJECT (settings));
	
    g_strfreev (dontask_domains);
    g_strfreev (new_dontask_domains);
}

/*
 * Add a domain to the list of domains which have been granted permissions
 */
void
unity_webapps_permissions_allow_domain (const gchar *domain)
{
	GSettings *settings;
	gchar **allowed_domains, **new_allowed_domains;
	int i, length;

	if (unity_webapps_permissions_schema_exists () == FALSE)
		{
			return;
		}

	settings = _unity_webapps_permissions_new_settings();
	if (!settings)
		return;

	g_settings_delay (settings);

	filter_settings_strv (settings, DONTASK_DOMAINS_KEY, domain);

	allowed_domains = NULL;
	new_allowed_domains = NULL;

	if (!is_domain_found_for_keyname (settings, ALLOWED_DOMAINS_KEY, domain))
		{
			allowed_domains = g_settings_get_strv(settings, ALLOWED_DOMAINS_KEY);

			length = g_strv_length (allowed_domains);
			
			new_allowed_domains = g_malloc (sizeof(gchar *) * (length + 2));
			
			for (i = 0; i < length; i++)
				{
					new_allowed_domains[i] = g_strdup (allowed_domains[i]);
				}
			
			new_allowed_domains[length] = g_strdup (domain);
			new_allowed_domains[length+1] = NULL;
			
			g_settings_set_strv(settings, ALLOWED_DOMAINS_KEY, (const gchar * const*)new_allowed_domains);
		}

	g_settings_apply (settings);

    g_object_unref (G_OBJECT (settings));

    g_strfreev (allowed_domains);
    g_strfreev (new_allowed_domains);
}

/*
 * Checks if a domain is in the list of domains allowed permissions.
 */
gboolean
unity_webapps_permissions_get_domain_allowed (const gchar *domain)
{
  GSettings *settings;
  gboolean ret, dontask;
	
  dontask = unity_webapps_permissions_get_domain_dontask (domain);
  if (dontask == TRUE)
	{
	  return FALSE;
	}

  if (unity_webapps_permissions_schema_exists () == FALSE)
	{
	  return FALSE;
	}

  settings = _unity_webapps_permissions_new_settings();
  if (!settings)
	  return FALSE;

  ret = is_domain_found_for_keyname(settings, ALLOWED_DOMAINS_KEY, domain)
	  || is_domain_found_for_keyname(settings, PREAUTHORIZED_DOMAINS_KEY, domain);

  g_object_unref (G_OBJECT (settings));

  return ret;
}

gboolean unity_webapps_permissions_get_domain_preauthorized (const gchar *domain)
{
  GSettings *settings;
  gboolean ret;
	
  if (unity_webapps_permissions_schema_exists () == FALSE)
	{
	  return FALSE;
	}

  settings = _unity_webapps_permissions_new_settings();
  if (!settings)
	  return FALSE;

  ret = is_domain_found_for_keyname(settings, PREAUTHORIZED_DOMAINS_KEY, domain);

  g_object_unref (G_OBJECT (settings));

  return ret;
}

/*
 * Checks if a domain is in the list of blacklisted dontask domains
 */
gboolean
unity_webapps_permissions_get_domain_dontask (const gchar *domain)
{
	GSettings *settings;
	gboolean ret;

	if (unity_webapps_permissions_schema_exists () == FALSE)
		{
			return TRUE;
		}

	settings = _unity_webapps_permissions_new_settings();
	if (!settings)
		return TRUE;

	ret = is_domain_found_for_keyname(settings, DONTASK_DOMAINS_KEY, domain);

	g_object_unref (G_OBJECT (settings));

	return ret;
}

static void
_json_object_set_strv_memeber (JsonObject *obj, const gchar* name, gchar **strv)
{
	JsonArray *array;
	gchar **it;

	array = json_array_new ();
	for (it = strv; it && *it; it++)
		{
			json_array_add_string_element (array, *it);
		}
	json_object_set_array_member (obj, name, array);
	//	json_array_unref (array);
}

gchar*
unity_webapps_permissions_get_all_domains (void)
{
	gchar *res;
	GSettings *settings;
	JsonGenerator *generator;
	JsonNode *root;
	JsonObject *obj;
	gchar **allowed = NULL, **dontask = NULL;

	if (unity_webapps_permissions_schema_exists ())
		{
			settings = _unity_webapps_permissions_new_settings();
			if (!settings)
				return NULL;

			dontask = g_settings_get_strv(settings, DONTASK_DOMAINS_KEY);
			allowed = g_settings_get_strv(settings, ALLOWED_DOMAINS_KEY);

			g_object_unref (G_OBJECT (settings));
		}

	root = json_node_new (JSON_NODE_OBJECT);
	obj = json_object_new ();
	json_node_set_object (root, obj);
	json_object_unref (obj);

	_json_object_set_strv_memeber (obj, "allowed", allowed);
	_json_object_set_strv_memeber (obj, "dontask", dontask);

	generator = json_generator_new ();
	json_generator_set_root (generator, root);

	res = json_generator_to_data (generator, NULL);

	json_node_free (root);
	g_object_unref (generator);
	g_strfreev (allowed);
	g_strfreev (dontask);

	return res;
}

/**
 * Removes a given domain name from the list of internal permission sets (either allowed
 * or not allowed).
 *
 */
void unity_webapps_permissions_remove_domain_from_permissions (const gchar *domain)
{
	GSettings *settings = NULL;

	g_return_if_fail(NULL != domain);

	if (unity_webapps_permissions_schema_exists () == FALSE)
		{
			return;
		}

	settings = _unity_webapps_permissions_new_settings();
	if (!settings)
		return;

	g_settings_delay (settings);

	filter_settings_strv (settings, DONTASK_DOMAINS_KEY, domain);
	filter_settings_strv (settings, ALLOWED_DOMAINS_KEY, domain);

	g_settings_apply (settings);

    g_object_unref (G_OBJECT (settings));
}

gboolean unity_webapps_permissions_is_integration_allowed(void)
{
	GSettings *settings = NULL;

	if (unity_webapps_permissions_schema_exists () == FALSE)
		{
			return TRUE;
		}

	settings = _unity_webapps_permissions_new_settings();
	if (!settings)
		return TRUE;

	gboolean isallowed = g_settings_get_boolean(settings, INTEGRATION_ALLOWED_KEY);

    g_object_unref (G_OBJECT (settings));
	return isallowed;
}

void unity_webapps_permissions_set_integration_allowed(gboolean allowed)
{
	GSettings *settings = NULL;

	if (unity_webapps_permissions_schema_exists () == FALSE)
		{
			return;
		}

	settings = _unity_webapps_permissions_new_settings();
	if (!settings)
		return;

	g_settings_set_boolean(settings, INTEGRATION_ALLOWED_KEY, allowed);

    g_object_unref (G_OBJECT (settings));
}

