/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
/* Copyright (C) 2005 Carlos Garnacho
 *
 * This program 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 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.
 *
 * Authors: Carlos Garnacho Parro  <carlosg@gnome.org>
 */

#if defined(HAVE_CONFIG_H)
#include <config.h>
#endif

#include <glib-object.h>
#include <unistd.h>
#include "oobs-session.h"
#include "oobs-group.h"
#include "oobs-user.h"
#include "oobs-session.h"
#include "oobs-usersconfig.h"
#include "oobs-groupsconfig-private.h"
#include "oobs-defines.h"
#include "utils.h"
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif

#define OOBS_GROUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), OOBS_TYPE_GROUP, OobsGroupPrivate))

typedef struct _OobsGroupPrivate OobsGroupPrivate;

struct _OobsGroupPrivate {
  OobsObject *config;
  gint   key;
  gchar *groupname;
  gchar *password;
  gid_t  gid;

  GList *users;

  gboolean use_md5;
};

static void oobs_group_class_init (OobsGroupClass *class);
static void oobs_group_init       (OobsGroup      *group);
static void oobs_group_finalize   (GObject       *object);

static void oobs_group_set_property (GObject      *object,
				     guint         prop_id,
				     const GValue *value,
				     GParamSpec   *pspec);
static void oobs_group_get_property (GObject      *object,
				     guint         prop_id,
				     GValue       *value,
				     GParamSpec   *pspec);
static GObject* oobs_group_constructor (GType                  type,
					guint                  n_construct_properties,
					GObjectConstructParam *construct_params);

enum {
  PROP_0,
  PROP_GROUPNAME,
  PROP_PASSWORD,
  PROP_CRYPTED_PASSWORD,
  PROP_GID,
};

G_DEFINE_TYPE (OobsGroup, oobs_group, G_TYPE_OBJECT);

static void
oobs_group_class_init (OobsGroupClass *class)
{
  GObjectClass *object_class = G_OBJECT_CLASS (class);

  object_class->constructor  = oobs_group_constructor;
  object_class->set_property = oobs_group_set_property;
  object_class->get_property = oobs_group_get_property;
  object_class->finalize     = oobs_group_finalize;

  g_object_class_install_property (object_class,
				   PROP_GROUPNAME,
				   g_param_spec_string ("name",
							"Groupname",
							"Name for the group",
							NULL,
							G_PARAM_READWRITE));
  g_object_class_install_property (object_class,
				   PROP_PASSWORD,
				   g_param_spec_string ("password",
							"Password",
							"Password for the group",
							NULL,
							G_PARAM_WRITABLE));
  g_object_class_install_property (object_class,
				   PROP_CRYPTED_PASSWORD,
				   g_param_spec_string ("crypted-password",
							"Crypted password",
							"Crypted password for the group",
							NULL,
							G_PARAM_READWRITE));
  g_object_class_install_property (object_class,
				   PROP_GID,
				   g_param_spec_int ("gid",
						     "GID",
						     "Main group GID for the group",
						     0, OOBS_MAX_GID, OOBS_MAX_GID,
						     G_PARAM_READWRITE));
  g_type_class_add_private (object_class,
			    sizeof (OobsGroupPrivate));
}

static void
oobs_group_init (OobsGroup *group)
{
  OobsGroupPrivate *priv;
  OobsObject *users_config;
  OobsSession *session;

  g_return_if_fail (OOBS_IS_GROUP (group));

  session = oobs_session_get ();

  priv = OOBS_GROUP_GET_PRIVATE (group);
  priv->config    = oobs_groups_config_get (session);
  priv->groupname = NULL;
  priv->password  = NULL;
  priv->users     = NULL;

  users_config = oobs_users_config_get (oobs_session_get ());
  g_object_get (users_config, "use-md5", &priv->use_md5, NULL);
  group->_priv = priv;
}

static void
oobs_group_set_property (GObject      *object,
			 guint         prop_id,
			 const GValue *value,
			 GParamSpec   *pspec)
{
  OobsGroup *group;
  OobsGroupPrivate *priv;
  gchar *salt, *str;

  g_return_if_fail (OOBS_IS_GROUP (object));

  group = OOBS_GROUP (object);
  priv = group->_priv;

  switch (prop_id)
    {
    case PROP_GROUPNAME:
      g_free (priv->groupname);
      priv->groupname = g_value_dup_string (value);
      break;
    case PROP_PASSWORD:
      g_free (priv->password);

      if (priv->use_md5)
	{
	  salt = utils_get_random_string (5);
	  str = g_strdup_printf ("$1$%s", salt);
	  priv->password = g_strdup (crypt (g_value_get_string (value), str));

	  g_free (str);
	}
      else
	{
	  salt = utils_get_random_string (2);
	  priv->password = g_strdup (crypt (g_value_get_string (value), salt));
	}

      g_free (salt);
      break;
    case PROP_CRYPTED_PASSWORD:
      g_free (priv->password);
      priv->password = g_value_dup_string (value);
      break;
    case PROP_GID:
      priv->gid = g_value_get_int (value);
      break;
    }
}

static void
oobs_group_get_property (GObject      *object,
			 guint         prop_id,
			 GValue       *value,
			 GParamSpec   *pspec)
{
  OobsGroup *group;
  OobsGroupPrivate *priv;

  g_return_if_fail (OOBS_IS_GROUP (object));

  group = OOBS_GROUP (object);
  priv = group->_priv;

  switch (prop_id)
    {
    case PROP_GROUPNAME:
      g_value_set_string (value, priv->groupname);
      break;
    case PROP_CRYPTED_PASSWORD:
      g_value_set_string (value, priv->password);
      break;
    case PROP_GID:
      g_value_set_int (value, priv->gid);
      break;
    }
}

static void
oobs_group_finalize (GObject *object)
{
  OobsGroup        *group;
  OobsGroupPrivate *priv;

  g_return_if_fail (OOBS_IS_GROUP (object));

  group = OOBS_GROUP (object);
  priv = group->_priv;

  if (priv)
    {
      g_free (priv->groupname);
      g_free (priv->password);

      if (priv->users)
	{
	  g_list_foreach (priv->users, (GFunc) g_object_unref, NULL);
	  g_list_free (priv->users);
	}
    }

  if (G_OBJECT_CLASS (oobs_group_parent_class)->finalize)
    (* G_OBJECT_CLASS (oobs_group_parent_class)->finalize) (object);
}

static GObject*
oobs_group_constructor (GType                  type,
			guint                  n_construct_properties,
			GObjectConstructParam *construct_params)
{
  GObject *object;
  OobsGroup *group;
  OobsGroupPrivate *priv;

  object = (* G_OBJECT_CLASS (oobs_group_parent_class)->constructor) (type,
								      n_construct_properties,
								      construct_params);
  group = OOBS_GROUP (object);
  priv = group->_priv;
  group->id = _oobs_groups_config_get_id (OOBS_GROUPS_CONFIG (priv->config));

  return object;

}

/**
 * oobs_group_new:
 * @name: group name.
 * 
 * Returns a newly allocated #OobsGroup with the name specified by @name.
 * 
 * Return Value: A new #OobsGroup.
 **/
OobsGroup*
oobs_group_new (const gchar *name)
{
  /* FIXME: should check name length */

  return g_object_new (OOBS_TYPE_GROUP,
		       "name", name,
		       NULL);
}

/**
 * oobs_group_get_name:
 * @group: An #OobsGroup.
 * 
 * Returns the name of the group represented by #OobsGroup.
 * 
 * Return Value: A pointer to the group name as a string.
 *               This string must not be freed, modified or stored.
 **/
G_CONST_RETURN gchar*
oobs_group_get_name (OobsGroup *group)
{
  OobsGroupPrivate *priv;

  g_return_val_if_fail (group != NULL, NULL);
  g_return_val_if_fail (OOBS_IS_GROUP (group), NULL);

  priv = group->_priv;

  return priv->groupname;
}

/**
 * oobs_group_set_name:
 * @group: An #OobsGroup.
 * @name: A new name for #group.
 * 
 * Sets the name of #group to be #name,
 * overwriting the previous one.
 **/
void
oobs_group_set_name (OobsGroup *group, const gchar *name)
{
  g_return_if_fail (group != NULL);
  g_return_if_fail (OOBS_IS_GROUP (group));
  g_return_if_fail (name != NULL);

  /* FIXME: should check name length */

  g_object_set (G_OBJECT (group), "name", name, NULL);
}

/**
 * oobs_group_set_password:
 * @group: An #OobsGroup.
 * @password: A new password for #group.
 * 
 * Sets the group password for the group defined
 * by #OobsGroup, overwriting the previous one.
 **/
void
oobs_group_set_password (OobsGroup *group, const gchar *password)
{
  g_return_if_fail (group != NULL);
  g_return_if_fail (OOBS_IS_GROUP (group));

  g_object_set (G_OBJECT (group), "password", password, NULL);
}

/**
 * oobs_group_set_crypted_password:
 * @group: An #OobsGroup.
 * @crypted_password: a new crypted password for #group.
 * 
 * Sets an already crypted password for the group
 * defined by #OobsGroup, overwriting the previous one.
 **/
void
oobs_group_set_crypted_password (OobsGroup   *group,
				 const gchar *crypted_password)
{
  g_return_if_fail (group != NULL);
  g_return_if_fail (OOBS_IS_GROUP (group));

  g_object_set (G_OBJECT (group), "crypted-password", crypted_password, NULL);
}

/**
 * oobs_group_get_gid:
 * @group: An #OobsGroup.
 * 
 * Returns the group ID (GID) associated to #OobsGroup
 * 
 * Return Value: the #group GID.
 **/
gid_t
oobs_group_get_gid (OobsGroup *group)
{
  OobsGroupPrivate *priv;

  g_return_val_if_fail (group != NULL, OOBS_MAX_GID);
  g_return_val_if_fail (OOBS_IS_GROUP (group), OOBS_MAX_GID);

  priv = group->_priv;

  return priv->gid;
}

/**
 * oobs_group_set_gid:
 * @group: An #OobsGroup.
 * @gid: A new GID for #group.
 * 
 * Sets the group ID (GID) of #group to be #gid.
 **/
void
oobs_group_set_gid (OobsGroup *group, gid_t gid)
{
  g_return_if_fail (group != NULL);
  g_return_if_fail (OOBS_IS_GROUP (group));

  g_object_set (G_OBJECT (group), "gid", gid, NULL);
}

/**
 * oobs_group_get_users:
 * @group: An #OobsGroup.
 * 
 * Returns a #GList containing pointers to the #OobsUser objects
 * that represent the users represented by the group.
 * 
 * Return Value: a newly allocated #GList, use g_list_free() to free it.
 **/
GList*
oobs_group_get_users (OobsGroup *group)
{
  OobsGroupPrivate *priv;

  g_return_val_if_fail (OOBS_IS_GROUP (group), NULL);

  priv = group->_priv;
  return g_list_copy (priv->users);
}

/**
 * oobs_group_add_user:
 * @group: An #OobsGroup.
 * @user: An #OobsUser to add to the group.
 * 
 * Adds a new user to the group. If the user is
 * already in the group, it does nothing.
 **/
void
oobs_group_add_user (OobsGroup *group,
		     OobsUser  *user)
{
  OobsGroupPrivate *priv;

  g_return_if_fail (OOBS_IS_GROUP (group));
  g_return_if_fail (OOBS_IS_USER (user));
  
  priv = group->_priv;

  /* try to avoid several instances */
  if (!g_list_find (priv->users, user))
    priv->users = g_list_prepend (priv->users, g_object_ref (user));
}

/**
 * oobs_group_remove_user:
 * @group: An #OobsGroup.
 * @user: An #OobsUser to remove from the group.
 * 
 * Removes an user from the group. If the user isn't a
 * member of this group, this function does nothing.
 **/
void
oobs_group_remove_user (OobsGroup *group,
			OobsUser  *user)
{
  OobsGroupPrivate *priv;

  g_return_if_fail (OOBS_IS_GROUP (group));
  g_return_if_fail (OOBS_IS_USER (user));
  
  priv = group->_priv;

  /* there might be several instances */
  priv->users = g_list_remove_all (priv->users, user);
}


syntax highlighted by Code2HTML, v. 0.9.1