/***************************************************************************
 *
 * libpolkit.c : Wraps a subset of methods on the PolicyKit daemon
 *
 * Copyright (C) 2006 David Zeuthen, <david@fubar.dk>
 *
 * Licensed under the Academic Free License version 2.1
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307	 USA
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <errno.h>

#include <glib.h>
#include <dbus/dbus-glib.h>

#include "libpolkit.h"

#define LIBPOLKIT_MAGIC 0x3117beef

#ifdef __SUNPRO_C
#define __FUNCTION__ __func__
#endif

#define LIBPOLKIT_CHECK_CONTEXT(_ctx_, _ret_)				\
	do {									\
		if (_ctx_ == NULL) {						\
			g_warning ("%s: given LibPolKitContext is NULL",     \
				   __FUNCTION__);			        \
			return _ret_;					        \
		}								\
		if (_ctx_->magic != LIBPOLKIT_MAGIC) {			\
			g_warning ("%s: given LibPolKitContext is invalid",  \
				   __FUNCTION__);			        \
			return _ret_;					        \
		}								\
	} while(0)


struct LibPolKitContext_s
{
	guint32 magic;
	DBusConnection *connection;
};

/** Get a new context.
 *
 *  @return                     Pointer to new context or NULL if an error occured
 */
LibPolKitContext *
libpolkit_new_context (DBusConnection *connection)
{
	LibPolKitContext *ctx;

	ctx = g_new0 (LibPolKitContext, 1);
	ctx->magic = LIBPOLKIT_MAGIC;
	ctx->connection = connection;

	return ctx;
}

/** Free a context
 *
 *  @param  ctx                 The context obtained from libpolkit_new_context
 *  @return                     Pointer to new context or NULL if an error occured
 */
gboolean
libpolkit_free_context (LibPolKitContext *ctx)
{
	LIBPOLKIT_CHECK_CONTEXT (ctx, FALSE);

	ctx->magic = 0;
	g_free (ctx);
	return TRUE;		
}

LibPolKitResult 
libpolkit_get_allowed_resources_for_privilege_for_uid (LibPolKitContext    *ctx,
						       const char          *user, 
						       const char          *privilege, 
						       GList              **result,
						       int                 *num_non_temporary)
{
	LibPolKitResult res;
	DBusMessage *message = NULL;
	DBusMessage *reply = NULL;
	DBusError error;
	char **resource_list;
	int num_resources;
	int i;

	LIBPOLKIT_CHECK_CONTEXT (ctx, LIBPOLKIT_RESULT_INVALID_CONTEXT);

	res = LIBPOLKIT_RESULT_ERROR;
	*result = NULL;

	message = dbus_message_new_method_call ("org.freedesktop.PolicyKit",
						"/org/freedesktop/PolicyKit/Manager",
						"org.freedesktop.PolicyKit.Manager",
						"GetAllowedResourcesForPrivilege");
	if (message == NULL) {
		g_warning ("Could not allocate D-BUS message");
		goto out;
	}

	if (!dbus_message_append_args (message, 
				       DBUS_TYPE_STRING, &user, 
				       DBUS_TYPE_STRING, &privilege,
				       DBUS_TYPE_INVALID)) {
		g_warning ("Could not append args to D-BUS message");
		goto out;
	}

	dbus_error_init (&error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection, message, -1, &error);
	if (dbus_error_is_set (&error)) {
		if (strcmp (error.name, "org.freedesktop.PolicyKit.Manager.NotPrivileged") == 0) {
			res = LIBPOLKIT_RESULT_NOT_PRIVILEGED;
		} else if (strcmp (error.name, "org.freedesktop.PolicyKit.Manager.Error") == 0) {
			res = LIBPOLKIT_RESULT_ERROR;
		}
		dbus_error_free (&error);
		goto out;
	}

	if (!dbus_message_get_args (reply, &error,
				    DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &resource_list, &num_resources,
				    DBUS_TYPE_INT32, num_non_temporary,
				    DBUS_TYPE_INVALID)) {
		g_warning ("Could not extract args from D-BUS message: %s : %s", error.name, error.message);
		dbus_error_free (&error);
		goto out;
	}

	for (i = 0; i < num_resources; i++) {
		*result = g_list_append (*result, g_strdup (resource_list[i]));
	}
	dbus_free_string_array (resource_list);

	res = LIBPOLKIT_RESULT_OK;

out:
	if (reply != NULL)
		dbus_message_unref (reply);
	if (message != NULL)
		dbus_message_unref (message);
	return res;
}

LibPolKitResult 
libpolkit_is_uid_allowed_for_privilege (LibPolKitContext    *ctx,
					pid_t                pid,
					const char          *user, 
					const char          *privilege, 
					const char          *resource,
					gboolean            *is_allowed,
					gboolean            *is_temporary)
{
	LibPolKitResult res;
	DBusMessage *message = NULL;
	DBusMessage *reply = NULL;
	DBusError error;
	const char *myresource = "";

	LIBPOLKIT_CHECK_CONTEXT (ctx, LIBPOLKIT_RESULT_INVALID_CONTEXT);

	res = LIBPOLKIT_RESULT_ERROR;
	*is_allowed = FALSE;
	*is_temporary = FALSE;

	message = dbus_message_new_method_call ("org.freedesktop.PolicyKit",
						"/org/freedesktop/PolicyKit/Manager",
						"org.freedesktop.PolicyKit.Manager",
						"IsUserPrivileged");
	if (message == NULL) {
		g_warning ("Could not allocate D-BUS message");
		goto out;
	}

	if (resource != NULL)
		myresource = resource;

	if (!dbus_message_append_args (message, 
				       DBUS_TYPE_INT32, &pid, 
				       DBUS_TYPE_STRING, &user, 
				       DBUS_TYPE_STRING, &privilege,
				       DBUS_TYPE_STRING, &myresource,
				       DBUS_TYPE_INVALID)) {
		g_warning ("Could not append args to D-BUS message");
		goto out;
	}

	dbus_error_init (&error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection, message, -1, &error);
	if (dbus_error_is_set (&error)) {
		if (strcmp (error.name, "org.freedesktop.PolicyKit.Manager.NoSuchUser") == 0) {
			res = LIBPOLKIT_RESULT_NO_SUCH_USER;
		} else if (strcmp (error.name, "org.freedesktop.PolicyKit.Manager.NoSuchPrivilege") == 0) {
			res = LIBPOLKIT_RESULT_NO_SUCH_PRIVILEGE;
		} else if (strcmp (error.name, "org.freedesktop.PolicyKit.Manager.NotPrivileged") == 0) {
			res = LIBPOLKIT_RESULT_NOT_PRIVILEGED;
		} else if (strcmp (error.name, "org.freedesktop.PolicyKit.Manager.Error") == 0) {
			res = LIBPOLKIT_RESULT_ERROR;
		}
		dbus_error_free (&error);
		goto out;
	}


	if (!dbus_message_get_args (reply, &error,
				    DBUS_TYPE_BOOLEAN, is_allowed,
				    DBUS_TYPE_BOOLEAN, is_temporary,
				    DBUS_TYPE_INVALID)) {
		g_warning ("Could not extract args from D-BUS message: %s : %s", error.name, error.message);
		dbus_error_free (&error);
		goto out;
	}

	res = LIBPOLKIT_RESULT_OK;

out:
	if (reply != NULL)
		dbus_message_unref (reply);
	if (message != NULL)
		dbus_message_unref (message);
	return res;
}

LibPolKitResult
libpolkit_revoke_temporary_privilege (LibPolKitContext      *ctx,
				      const char            *user, 
				      const char            *privilege, 
				      const char            *resource,
				      gboolean              *result)
{
	LibPolKitResult res;
	DBusMessage *message = NULL;
	DBusMessage *reply = NULL;
	DBusError error;
	const char *myresource = "";

	LIBPOLKIT_CHECK_CONTEXT (ctx, LIBPOLKIT_RESULT_INVALID_CONTEXT);

	res = LIBPOLKIT_RESULT_ERROR;
	*result = FALSE;

	message = dbus_message_new_method_call ("org.freedesktop.PolicyKit",
						"/org/freedesktop/PolicyKit/Manager",
						"org.freedesktop.PolicyKit.Manager",
						"RevokeTemporaryPrivilege");
	if (message == NULL) {
		g_warning ("Could not allocate D-BUS message");
		goto out;
	}

	if (resource != NULL)
		myresource = resource;

	if (!dbus_message_append_args (message, 
				       DBUS_TYPE_STRING, &user, 
				       DBUS_TYPE_STRING, &privilege,
				       DBUS_TYPE_STRING, &myresource,
				       DBUS_TYPE_INVALID)) {
		g_warning ("Could not append args to D-BUS message");
		goto out;
	}

	dbus_error_init (&error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection, message, -1, &error);
	if (dbus_error_is_set (&error)) {
		if (strcmp (error.name, "org.freedesktop.PolicyKit.Manager.NoSuchUser") == 0) {
			res = LIBPOLKIT_RESULT_NO_SUCH_USER;
		} else if (strcmp (error.name, "org.freedesktop.PolicyKit.Manager.NoSuchPrivilege") == 0) {
			res = LIBPOLKIT_RESULT_NO_SUCH_PRIVILEGE;
		} else if (strcmp (error.name, "org.freedesktop.PolicyKit.Manager.NotPrivileged") == 0) {
			res = LIBPOLKIT_RESULT_NOT_PRIVILEGED;
		} else if (strcmp (error.name, "org.freedesktop.PolicyKit.Manager.Error") == 0) {
			res = LIBPOLKIT_RESULT_ERROR;
		}
		dbus_error_free (&error);
		goto out;
	}


	if (!dbus_message_get_args (reply, &error,
				    DBUS_TYPE_BOOLEAN, result,
				    DBUS_TYPE_INVALID)) {
		g_warning ("Could not extract args from D-BUS message: %s : %s", error.name, error.message);
		dbus_error_free (&error);
		goto out;
	}

	res = LIBPOLKIT_RESULT_OK;

out:
	if (reply != NULL)
		dbus_message_unref (reply);
	if (message != NULL)
		dbus_message_unref (message);
	return res;	
}

LibPolKitResult
libpolkit_get_privilege_list (LibPolKitContext      *ctx,
			      GList                **result)
{
	LibPolKitResult res;
	DBusMessage *message = NULL;
	DBusMessage *reply = NULL;
	DBusError error;
	char **privilege_list;
	int num_privileges;
	int i;

	LIBPOLKIT_CHECK_CONTEXT (ctx, LIBPOLKIT_RESULT_INVALID_CONTEXT);

	res = LIBPOLKIT_RESULT_ERROR;
	*result = NULL;

	message = dbus_message_new_method_call ("org.freedesktop.PolicyKit",
						"/org/freedesktop/PolicyKit/Manager",
						"org.freedesktop.PolicyKit.Manager",
						"ListPrivileges");
	if (message == NULL) {
		g_warning ("Could not allocate D-BUS message");
		goto out;
	}

	dbus_error_init (&error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection, message, -1, &error);
	if (dbus_error_is_set (&error)) {
		if (strcmp (error.name, "org.freedesktop.PolicyKit.Manager.NotPrivileged") == 0) {
			res = LIBPOLKIT_RESULT_NOT_PRIVILEGED;
		} else if (strcmp (error.name, "org.freedesktop.PolicyKit.Manager.Error") == 0) {
			res = LIBPOLKIT_RESULT_ERROR;
		}
		dbus_error_free (&error);
		goto out;
	}

	if (!dbus_message_get_args (reply, &error,
				    DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &privilege_list, &num_privileges,
				    DBUS_TYPE_INVALID)) {
		g_warning ("Could not extract args from D-BUS message: %s : %s", error.name, error.message);
		dbus_error_free (&error);
		goto out;
	}

	for (i = 0; i < num_privileges; i++) {
		*result = g_list_append (*result, g_strdup (privilege_list[i]));
	}
	dbus_free_string_array (privilege_list);

	res = LIBPOLKIT_RESULT_OK;

out:
	if (reply != NULL)
		dbus_message_unref (reply);
	if (message != NULL)
		dbus_message_unref (message);
	return res;
}


syntax highlighted by Code2HTML, v. 0.9.1