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

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

#include "oobs-object.h"
#include "oobs-object-private.h"
#include "oobs-list.h"
#include "oobs-list-private.h"
#include "oobs-smbconfig.h"
#include "oobs-share.h"
#include "oobs-share-smb.h"
#include "utils.h"

#define SMB_CONFIG_REMOTE_OBJECT "SMBConfig"
#define OOBS_SMB_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), OOBS_TYPE_SMB_CONFIG, OobsSMBConfigPrivate))

typedef struct _OobsSMBConfigPrivate OobsSMBConfigPrivate;

struct _OobsSMBConfigPrivate
{
  OobsList *shares_list;

  gchar *workgroup;
  gchar *desc;
  gboolean is_wins_server;
  gchar *wins_server;
};

static void oobs_smb_config_class_init (OobsSMBConfigClass *class);
static void oobs_smb_config_init       (OobsSMBConfig      *config);
static void oobs_smb_config_finalize   (GObject            *object);

static void oobs_smb_config_set_property (GObject      *object,
					  guint         prop_id,
					  const GValue *value,
					  GParamSpec   *pspec);
static void oobs_smb_config_get_property (GObject      *object,
					  guint         prop_id,
					  GValue       *value,
					  GParamSpec   *pspec);

static void oobs_smb_config_update     (OobsObject   *object);
static void oobs_smb_config_commit     (OobsObject   *object);

enum {
  PROP_0,
  PROP_WORKGROUP,
  PROP_DESC,
  PROP_IS_WINS_SERVER,
  PROP_WINS_SERVER
};

G_DEFINE_TYPE (OobsSMBConfig, oobs_smb_config, OOBS_TYPE_OBJECT);

static void
oobs_smb_config_class_init (OobsSMBConfigClass *class)
{
  GObjectClass *object_class = G_OBJECT_CLASS (class);
  OobsObjectClass *oobs_object_class = OOBS_OBJECT_CLASS (class);

  object_class->set_property = oobs_smb_config_set_property;
  object_class->get_property = oobs_smb_config_get_property;
  object_class->finalize     = oobs_smb_config_finalize;

  oobs_object_class->commit  = oobs_smb_config_commit;
  oobs_object_class->update  = oobs_smb_config_update;

  g_object_class_install_property (object_class,
				   PROP_WORKGROUP,
				   g_param_spec_string ("workgroup",
							"Workgroup",
							"Workgroup/domain the host is in",
							NULL,
							G_PARAM_READWRITE));
  g_object_class_install_property (object_class,
				   PROP_DESC,
				   g_param_spec_string ("description",
							"Description",
							"Description for this host",
							NULL,
							G_PARAM_READWRITE));
  g_object_class_install_property (object_class,
				   PROP_IS_WINS_SERVER,
				   g_param_spec_boolean ("is_wins_server",
							 "Is WINS server",
							 "Whether the host is the WINS server",
							 FALSE,
							 G_PARAM_READWRITE));
  g_object_class_install_property (object_class,
				   PROP_WINS_SERVER,
				   g_param_spec_string ("wins_server",
							"WINS server",
							"WINS server that the host will use",
							NULL,
							G_PARAM_READWRITE));
  g_type_class_add_private (object_class,
			    sizeof (OobsSMBConfigPrivate));
}

static void
oobs_smb_config_init (OobsSMBConfig *config)
{
  OobsSMBConfigPrivate *priv;

  g_return_if_fail (OOBS_IS_SMB_CONFIG (config));

  priv = OOBS_SMB_CONFIG_GET_PRIVATE (config);

  priv->shares_list = _oobs_list_new (OOBS_TYPE_SHARE_SMB);
  config->_priv = priv;
}

static void
oobs_smb_config_set_property (GObject      *object,
			      guint         prop_id,
			      const GValue *value,
			      GParamSpec   *pspec)
{
  OobsSMBConfigPrivate *priv;

  priv = OOBS_SMB_CONFIG (object)->_priv;

  switch (prop_id)
    {
    case PROP_WORKGROUP:
      priv->workgroup = g_value_dup_string (value);
      break;
    case PROP_DESC:
      priv->desc = g_value_dup_string (value);
      break;
    case PROP_IS_WINS_SERVER:
      priv->is_wins_server = g_value_get_boolean (value);
      break;
    case PROP_WINS_SERVER:
      priv->wins_server = g_value_dup_string (value);
      break;
    }
}

static void
oobs_smb_config_get_property (GObject      *object,
			      guint         prop_id,
			      GValue       *value,
			      GParamSpec   *pspec)
{
  OobsSMBConfigPrivate *priv;

  priv = OOBS_SMB_CONFIG (object)->_priv;

  switch (prop_id)
    {
    case PROP_WORKGROUP:
      g_value_set_string (value, priv->workgroup);
      break;
    case PROP_DESC:
      g_value_set_string (value, priv->desc);
      break;
    case PROP_IS_WINS_SERVER:
      g_value_set_boolean (value, priv->is_wins_server);
      break;
    case PROP_WINS_SERVER:
      g_value_set_string (value, priv->wins_server);
      break;
    }
}

static void
oobs_smb_config_finalize (GObject *object)
{
  OobsSMBConfigPrivate *priv;

  g_return_if_fail (OOBS_IS_SMB_CONFIG (object));

  priv = OOBS_SMB_CONFIG (object)->_priv;

  if (priv && priv->shares_list)
    g_object_unref (priv->shares_list);

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

static OobsShare*
create_share_from_dbus_reply (OobsObject      *object,
			      DBusMessage     *reply,
			      DBusMessageIter  struct_iter)
{
  DBusMessageIter iter;
  gchar *name, *comment, *path;
  OobsShareSMBFlags flags;
  gboolean value;

  name    = NULL;
  comment = NULL;
  path    = NULL;
  flags   = 0;

  dbus_message_iter_recurse (&struct_iter, &iter);

  dbus_message_iter_get_basic (&iter, &name);
  dbus_message_iter_next (&iter);

  dbus_message_iter_get_basic (&iter, &path);
  dbus_message_iter_next (&iter);
  
  dbus_message_iter_get_basic (&iter, &comment);
  dbus_message_iter_next (&iter);

  dbus_message_iter_get_basic (&iter, &value);
  dbus_message_iter_next (&iter);

  if (value)
    flags |= OOBS_SHARE_SMB_ENABLED;

  dbus_message_iter_get_basic (&iter, &value);
  dbus_message_iter_next (&iter);

  if (value)
    flags |= OOBS_SHARE_SMB_BROWSABLE;
  
  dbus_message_iter_get_basic (&iter, &value);
  dbus_message_iter_next (&iter);

  if (value)
    flags |= OOBS_SHARE_SMB_PUBLIC;
  
  dbus_message_iter_get_basic (&iter, &value);
  dbus_message_iter_next (&iter);

  if (value)
    flags |= OOBS_SHARE_SMB_WRITABLE;

  return oobs_share_smb_new (path, name, comment, flags);
}

static void
oobs_smb_config_update (OobsObject *object)
{
  OobsSMBConfigPrivate *priv;
  DBusMessage      *reply;
  DBusMessageIter   iter, array_iter;
  OobsListIter      list_iter;
  OobsShare        *share;
  const gchar      *str;

  priv  = OOBS_SMB_CONFIG (object)->_priv;
  reply = _oobs_object_get_dbus_message (object);

  /* First of all, free the previous shares list */
  oobs_list_clear (priv->shares_list);

  /* start recursing through the response array */
  dbus_message_iter_init    (reply, &iter);
  dbus_message_iter_recurse (&iter, &array_iter);
  dbus_message_iter_next (&iter);

  while (dbus_message_iter_get_arg_type (&array_iter) == DBUS_TYPE_STRUCT)
    {
      share = create_share_from_dbus_reply (object, reply, array_iter);

      if (share)
	{
	  oobs_list_append (priv->shares_list, &list_iter);
	  oobs_list_set    (priv->shares_list, &list_iter, G_OBJECT (share));
	  g_object_unref   (share);
	}

      dbus_message_iter_next (&array_iter);
    }

  str = utils_get_string (&iter);
  priv->workgroup = g_strdup (str);
  dbus_message_iter_next (&iter);

  str = utils_get_string (&iter);
  priv->desc = g_strdup (str);
  dbus_message_iter_next (&iter);

  dbus_message_iter_get_basic (&iter, &priv->is_wins_server);
  dbus_message_iter_next (&iter);

  str = utils_get_string (&iter);
  priv->wins_server = g_strdup (str);
  dbus_message_iter_next (&iter);
}

static void
create_dbus_struct_from_share (GObject         *share,
			       DBusMessage     *message,
			       DBusMessageIter *iter)
{
  DBusMessageIter struct_iter;
  gchar *name, *comment, *path;
  OobsShareSMBFlags flags;
  gboolean value;

  g_object_get (share,
		"name", &name,
		"comment", &comment,
		"path", &path,
		"flags", &flags,
		NULL);

  dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &struct_iter);

  dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &name);
  dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &path);
  dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &comment);

  value = (flags & OOBS_SHARE_SMB_ENABLED);
  dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &value);
  
  value = (flags & OOBS_SHARE_SMB_BROWSABLE);
  dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &value);

  value = (flags & OOBS_SHARE_SMB_PUBLIC);
  dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &value);

  value = (flags & OOBS_SHARE_SMB_WRITABLE);
  dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &value);

  dbus_message_iter_close_container (iter, &struct_iter);

  g_free (name);
  g_free (comment);
  g_free (path);
}

static void
oobs_smb_config_commit (OobsObject *object)
{
  OobsSMBConfigPrivate *priv;
  DBusMessage *message;
  DBusMessageIter iter, array_iter;
  OobsListIter list_iter;
  GObject *share;
  gboolean valid;

  priv = OOBS_SMB_CONFIG (object)->_priv;
  message = _oobs_object_get_dbus_message (object);

  dbus_message_iter_init_append (message, &iter);
  dbus_message_iter_open_container (&iter,
				    DBUS_TYPE_ARRAY,
				    DBUS_STRUCT_BEGIN_CHAR_AS_STRING
				    DBUS_TYPE_STRING_AS_STRING
				    DBUS_TYPE_STRING_AS_STRING
				    DBUS_TYPE_STRING_AS_STRING
				    DBUS_TYPE_INT32_AS_STRING
				    DBUS_TYPE_INT32_AS_STRING
				    DBUS_TYPE_INT32_AS_STRING
				    DBUS_TYPE_INT32_AS_STRING
				    DBUS_STRUCT_END_CHAR_AS_STRING,
				    &array_iter);

  valid = oobs_list_get_iter_first (priv->shares_list, &list_iter);

  while (valid)
    {
      share = oobs_list_get (priv->shares_list, &list_iter);
      create_dbus_struct_from_share (share, message, &array_iter);

      g_object_unref (share);
      valid = oobs_list_iter_next (priv->shares_list, &list_iter);
    }

  dbus_message_iter_close_container (&iter, &array_iter);

  utils_append_string (&iter, priv->workgroup);
  utils_append_string (&iter, priv->desc);
  dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &priv->is_wins_server);
  utils_append_string (&iter, priv->wins_server);
}

/**
 * oobs_smb_config_get:
 * @session: An #OobsSession.
 * 
 * Returns the #OobsSMBConfig singleton, which represents
 * the SMB system configuration.
 * 
 * Return Value: the singleton #OobsSMBConfig object.
 **/
OobsObject*
oobs_smb_config_get (OobsSession *session)
{
  static OobsObject *object = NULL;

  g_return_val_if_fail (session != NULL, NULL);
  g_return_val_if_fail (OOBS_IS_SESSION (session), NULL);

  if (!object)
    {
      object = g_object_new (OOBS_TYPE_SMB_CONFIG,
			     "remote-object", SMB_CONFIG_REMOTE_OBJECT,
			     "session",       session,
			     NULL);

      oobs_object_update (object);
    }

  return object;
}

/**
 * oobs_smb_config_get_shares:
 * @config: An #OobsSMBConfig.
 * 
 * Returns an #OobsList containing objects of type #OobsShareSMB.
 * 
 * Return Value: an #OobsList containing the SMB shares information.
 **/
OobsList*
oobs_smb_config_get_shares (OobsSMBConfig *config)
{
  OobsSMBConfigPrivate *priv;

  g_return_val_if_fail (OOBS_IS_SMB_CONFIG (config), NULL);

  priv = config->_priv;

  return priv->shares_list;
}

/**
 * oobs_smb_config_get_workgroup:
 * @config: An #OobsSMBConfig.
 * 
 * Returns the SMB workgroup for the host.
 * 
 * Return Value: A pointer to the workgroup as a string. This
 *               string must not be freed, modified or stored.
 **/
G_CONST_RETURN gchar*
oobs_smb_config_get_workgroup (OobsSMBConfig *config)
{
  OobsSMBConfigPrivate *priv;

  g_return_val_if_fail (OOBS_IS_SMB_CONFIG (config), NULL);

  priv = config->_priv;
  return priv->workgroup;
}

/**
 * oobs_smb_config_set_workgroup:
 * @config: An #OobsSMBConfig.
 * @workgroup: a new SMB workgroup for the host.
 * 
 * Sets a new SMB workgroup for the host, overwriting the
 * previous one.
 **/
void
oobs_smb_config_set_workgroup (OobsSMBConfig *config,
			       const gchar   *workgroup)
{
  g_return_if_fail (OOBS_IS_SMB_CONFIG (config));

  g_object_set (config, "workgroup", workgroup, NULL);
}

/**
 * oobs_smb_config_get_description:
 * @config: An #OobsSMBConfig.
 * 
 * Returns the SMB host description.
 * 
 * Return Value: A pointer to the host description as a string. This
 *               string must not be freed, modified or stored.
 **/
G_CONST_RETURN gchar*
oobs_smb_config_get_description (OobsSMBConfig *config)
{
  OobsSMBConfigPrivate *priv;

  g_return_val_if_fail (OOBS_IS_SMB_CONFIG (config), NULL);

  priv = config->_priv;
  return priv->desc;
}

/**
 * oobs_smb_config_set_description:
 * @config: An #OobsSMBConfig.
 * @description: a new host description.
 * 
 * Sets a new SMB host description, overwriting the previous one.
 **/
void
oobs_smb_config_set_description (OobsSMBConfig *config,
				 const gchar   *description)
{
  g_return_if_fail (OOBS_IS_SMB_CONFIG (config));

  g_object_set (config, "description", description, NULL);
}

/**
 * oobs_smb_config_get_is_wins_server:
 * @config: An #OobsSMBConfig.
 * 
 * Returns whether the host is a WINS server for the network.
 * 
 * Return Value: #TRUE if the host is a WINS server.
 **/
gboolean
oobs_smb_config_get_is_wins_server (OobsSMBConfig *config)
{
  OobsSMBConfigPrivate *priv;

  g_return_val_if_fail (OOBS_IS_SMB_CONFIG (config), FALSE);

  priv = config->_priv;
  return priv->is_wins_server;
}

/**
 * oobs_smb_config_set_is_wins_server:
 * @config: An #OobsSMBConfig.
 * @is_wins_server: #TRUE if the host is the WINS server for the network.
 * 
 * Sets whether the host is a WINS server for the network. Note that there
 * must be only one WINS server, so if there's already a WINS server in the
 * network, there wouldn't be the need to set the host as another WINS server.
 **/
void
oobs_smb_config_set_is_wins_server (OobsSMBConfig *config,
				    gboolean       is_wins_server)
{
  g_return_if_fail (OOBS_IS_SMB_CONFIG (config));

  g_object_set (config, "is-wins-server", is_wins_server, NULL);
}

/**
 * oobs_smb_config_get_wins_server:
 * @config: An #OobsSMBConfig.
 * 
 * Returns the WINS server the host is using. 
 * 
 * Return Value: A pointer to the wins server as a string. This
 *               string must not be freed, modified or stored.
 **/
G_CONST_RETURN gchar*
oobs_smb_config_get_wins_server (OobsSMBConfig *config)
{
  OobsSMBConfigPrivate *priv;

  g_return_val_if_fail (OOBS_IS_SMB_CONFIG (config), NULL);

  priv = config->_priv;
  return priv->wins_server;
}

/**
 * oobs_smb_config_set_wins_server:
 * @config: An #OobsSMBConfig.
 * @wins_server: a new WINS server.
 * 
 * Sets the WINS server that the host will use. This option is
 * mutually exclusive with oobs_smb_config_set_is_wins_server().
 **/
void
oobs_smb_config_set_wins_server (OobsSMBConfig *config,
				 const gchar   *wins_server)
{
  g_return_if_fail (OOBS_IS_SMB_CONFIG (config));

  g_object_set (config, "wins-server", wins_server, NULL);
}


syntax highlighted by Code2HTML, v. 0.9.1