/* Copyright (C) 2000,2001,2002 Manuel Amador (Rudd-O)
   This file is part of Directory administrator.

   Directory administrator 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.1 of
   the License, or (at your option) any later version.

   Directory administrator 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 Directory administrator; if not, send e-mail to amador@alomega.com
*/


#include <glib.h>
#include <gnome.h>
#include "appsupport.h"
#include "appglobals.h"
#include "groups.h"
#include "users.h"
#include "charset.h"
#include "mkntpwd.h"

#ifdef FREEBSD
#include <des.h>
#else
#include <crypt.h>
#endif /* FREEBSD */

#include <sys/time.h>
#include <unistd.h>
#include "md5.h"

//standard attributes to check on creation/modification
gchar *attrs2chk[] = { "uid", "uidnumber", "gidnumber", "givenname",
  "initials", "sn", "cn", "homedirectory",
  "loginshell", "mail", "maillocaladdress",
  "mailroutingaddress", "mailhost",
  "gecos", "userPassword", "authPassword", "shadowLastChange",
  "shadowmin", "shadowmax", "shadowwarning", "shadowinactive",
  "shadowexpire", "title", "physicaldeliveryofficename",
  "ou", "l",
  "telephonenumber", "facsimiletelephonenumber", "o", "homephone",
  "mobile", "smbHome", "homeDrive", "profilePath", "scriptPath", "lmPassword",
  "ntPassword", "rid", "objectclass", "host","employeeNumber",
  NULL
};

gchar *posixlist[] = {
  "uid", "uidnumber", "gidnumber", "homedirectory",
  "loginshell", "gecos", "userPassword", "authPassword",
  "shadowLastChange", "shadowmin", "shadowmax", "shadowwarning",
  "shadowinactive", "shadowexpire", NULL
};
gchar *sambalist[] = {
  "smbHome", "homeDrive", "profilePath", "scriptPath", "lmPassword",
  "ntPassword", "rid", NULL
};
gchar *maillist[] = {
  "maillocaladdress", "mailroutingaddress", "mailhost", NULL
};
gchar *accountlist[] = {
  "host", NULL
};
gchar *personlist[] = { "givenname", "initials", "sn", "cn",
  "mail", "title", "physicaldeliveryofficename", "ou", "l",
  "telephonenumber", "facsimiletelephonenumber", "o", "homephone",
  "mobile","employeenumber", NULL
};

gchar *handled_objectclasses[] = {
/*"person",*/"organizationalPerson","inetOrgPerson","account","posixAccount",
"shadowAccount","sambaAccount","inetLocalMailRecipient","top", NULL
};



/* This is from 'pam_ldap.c' by Luke Howard, <lukeh@padl.com>
   Used without explicit permission. */
/* i64c - convert an integer to a radix 64 character */
static int
i64c (int i)
{
  const char *base64 =
    "./01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  if (i < 0)
    i = 0;
  else if (i > 63)
    i = 63;

  return base64[i];
}

/* This is from 'pam_ldap.c' by Luke Howard, <lukeh@padl.com>
 Used with permission granted in the 'GNU Library General Public License'.
  THANKS, Luke! */

static char *
_get_salt (char salt[3])
{
  int i;
  int j;

  srand (time (NULL));

  for (j = 0; j < 2; j++)
    {
      i = rand () % 3;
      switch (i)
	{
	case 0:
	  i = (rand () % (57 - 46)) + 46;
	  break;
	case 1:
	  i = (rand () % (90 - 65)) + 65;
	  break;
	case 2:
	  i = (rand () % (122 - 97)) + 97;
	  break;
	}
      salt[j] = i;
    }
  salt[2] = '\0';
  return (salt);
}

static char *
_get_md5_salt (char saltbuf[16])
{
  md5_state_t state;
  md5_byte_t digest[16];
  struct timeval tv;
  int i;

  md5_init (&state);
  gettimeofday (&tv, NULL);
  md5_append (&state, (unsigned char *) &tv, sizeof (tv));
  i = getpid ();
  md5_append (&state, (unsigned char *) &i, sizeof (i));
  i = clock ();
  md5_append (&state, (unsigned char *) &i, sizeof (i));
  md5_append (&state, (unsigned char *) saltbuf, sizeof (saltbuf));
  md5_finish (&state, digest);

  strcpy (saltbuf, "$1$");
  for (i = 0; i < 8; i++)
    saltbuf[i + 3] = i64c (digest[i] & 0x3f);

  saltbuf[i + 3] = '\0';

  return saltbuf;
}

diradmin_user *
diradmin_user_new (gchar * dn, GList * allowed)
{

  diradmin_user *conn = g_new (diradmin_user, 1);
  conn->diradmin_user_data = NULL;
  conn->diradmin_user_allowedservers = NULL;
  conn->diradmin_user_objectclasses = NULL;

  conn->diradmin_user_data =
    pairs_list_set_attribute (conn->diradmin_user_data, "dn", dn);

  diradmin_user_revokeallservers (conn);

  diradmin_user_set_allowedservers (conn, allowed);

  return conn;
}

diradmin_user *
diradmin_user_duplicate (diradmin_user * tobeduped)
{

  diradmin_user *conn = g_new (diradmin_user, 1);
  conn->diradmin_user_data = NULL;
  conn->diradmin_user_allowedservers = NULL;
  conn->diradmin_user_objectclasses = NULL;

  conn->diradmin_user_data =
    pairs_list_duplicate (tobeduped->diradmin_user_data);

  diradmin_user_set_allowedservers (conn,
				    tobeduped->diradmin_user_allowedservers);
  diradmin_user_set_objectclasses (conn,
				   tobeduped->diradmin_user_objectclasses);
  conn->allserversallowed = tobeduped->allserversallowed;
  return conn;

}


//these three functions receive pointers to pointers as arguments for the datalist
void
diradmin_user_destroy (diradmin_user * todestroy)
{
  if (todestroy)
    {
      pairs_list_destroy (todestroy->diradmin_user_data);
      diradmin_user_set_allowedservers (todestroy, NULL);
      diradmin_user_set_objectclasses (todestroy, NULL);
      g_free (todestroy);
    }

}

gchar *
diradmin_user_get_attribute (diradmin_user * conn, gchar * attr)
{
//  g_print("getting attribute "); g_print(attr);
//  g_print(" to "); g_print(to); g_print("\n");
  g_assert (conn);
  g_assert (attr);
  return pairs_list_get_attribute (conn->diradmin_user_data, attr);
}

void
diradmin_user_set_attribute (diradmin_user * conn, gchar * attr, gchar * to)
{

  int i;

  g_assert (conn);
  g_assert (attr);
//  g_print("setting attribute "); g_print(attr);
//  g_print(" to "); g_print(to); g_print("\n");

  conn->diradmin_user_data =
    pairs_list_set_attribute (conn->diradmin_user_data, attr, to);


  diradmin_user_add_objectclass (conn, "top");
  for (i = 0; posixlist[i] != NULL; i++)
    if (g_strcasecmp (posixlist[i], attr) == 0)
      {
	diradmin_user_add_objectclass (conn, "posixAccount");
	diradmin_user_add_objectclass (conn, "shadowAccount");
      }
  for (i = 0; sambalist[i] != NULL; i++)
    if (g_strcasecmp (sambalist[i], attr) == 0)
      {
	diradmin_user_add_objectclass (conn, "sambaAccount");
      }
  for (i = 0; maillist[i] != NULL; i++)
    if (g_strcasecmp (maillist[i], attr) == 0)
      {
	diradmin_user_add_objectclass (conn, "inetLocalMailRecipient");
      }
  for (i = 0; accountlist[i] != NULL; i++)
    if (g_strcasecmp (accountlist[i], attr) == 0)
      {
	diradmin_user_add_objectclass (conn, "account");
      }
  for (i = 0; personlist[i] != NULL; i++)
    if (g_strcasecmp (personlist[i], attr) == 0)
      {
//	diradmin_user_add_objectclass (conn, "person");
	diradmin_user_add_objectclass (conn, "organizationalPerson");
	diradmin_user_add_objectclass (conn, "inetOrgPerson");
      }

}

void
diradmin_user_tighten_objectclasses (diradmin_user * u)
{

  int isneeded, i;
/*  para cada lista de atributos
  si el user no tiene ninguno
  remover esos object classes*/

  isneeded = FALSE;
  for (i = 0; posixlist[i] != NULL; i++)
    if (diradmin_user_get_attribute (u, posixlist[i]))
      isneeded = TRUE;
  if (isneeded == FALSE)
    {
//      g_print("\nTighten objectclasses: removing account classes");
      diradmin_user_remove_objectclass (u, "posixAccount");
      diradmin_user_remove_objectclass (u, "shadowAccount");
    }
  isneeded = FALSE;
  for (i = 0; sambalist[i] != NULL; i++)
    if (diradmin_user_get_attribute (u, sambalist[i]))
      isneeded = TRUE;
  if (isneeded == FALSE)
    {
//      g_print("\nTighten objectclasses: removing samba classes");
      diradmin_user_remove_objectclass (u, "sambaAccount");
    }
  isneeded = FALSE;
  for (i = 0; personlist[i] != NULL; i++)
    if (diradmin_user_get_attribute (u, personlist[i]))
      isneeded = TRUE;
  if (isneeded == FALSE)
    {
      //g_print("\nTighten objectclasses: removing person classes");
      //diradmin_user_remove_objectclass (u, "person");
      diradmin_user_remove_objectclass (u, "organizationalPerson");
      diradmin_user_remove_objectclass (u, "inetOrgPerson");
    }
  isneeded = FALSE;
  for (i = 0; maillist[i] != NULL; i++)
    if (diradmin_user_get_attribute (u, maillist[i]))
      isneeded = TRUE;
  if (isneeded == FALSE)
    {
      //g_print("\nTighten objectclasses: removing mail classes");
      diradmin_user_remove_objectclass (u, "inetLocalMailRecipient");
    }
}
void
diradmin_user_remove_attribute (diradmin_user * conn, gchar * attr)
{
  g_assert (conn);
  g_assert (attr);
  conn->diradmin_user_data =
    pairs_list_remove_attribute (conn->diradmin_user_data, attr);
}

void
diradmin_user_set_password (diradmin_user * conn, gchar * to)
{
  char seed[2], buf[96],buf2[96], saltbuf[16], md5crypt[64];
  g_print ("set password called with password: %s\n", to);
  if (preferences.passwordcrypttype == CRYPT)
    {
      snprintf (buf, sizeof buf, "{crypt}%s", crypt (to, _get_salt (seed)));
      snprintf (buf2, sizeof buf2, "CRYPT$%s", crypt (to, _get_salt (seed)));
    }
  else if (preferences.passwordcrypttype == MD5)
    {
      // Due to the way that md5 crypt (shadow) format works, we want
      // the prepend to be {crypt} instead of {md5}
      strncpy (md5crypt, to, sizeof (md5crypt) - 1);
      _get_md5_salt (saltbuf);
      g_print (crypt (md5crypt, saltbuf));
      snprintf (buf, sizeof buf, "{crypt}%s", crypt (md5crypt, saltbuf));
      snprintf (buf2, sizeof buf2, "MD5$%s", crypt (md5crypt, saltbuf));
    }
  else
    {
      snprintf (buf, sizeof buf, "%s", to);
      snprintf (buf2, sizeof buf2, "%s", to);
    }

  if (diradmin_user_has_objectclass(conn,"sambaaccount"))
  {
    char hash[17], hexstr[33];

    mklmhash(to, hash);
    to_hexstr(hash, hexstr);
    diradmin_user_set_attribute (conn, "lmPassword", hexstr);

    mknthash(to, hash);
    to_hexstr(hash, hexstr);
    diradmin_user_set_attribute (conn, "ntPassword", hexstr);
  }
  diradmin_user_set_attribute (conn, "userPassword", buf);
  if (preferences.rfc2307bis == TRUE)
	diradmin_user_set_attribute (conn, "authPassword", buf2);

  //this line of code is taken from pam_ldap.c
  snprintf (buf, sizeof buf, "%ld", time (NULL) / (60 * 60 * 24));

  diradmin_user_set_attribute (conn, "shadowLastChange", buf);
  g_print ("set password set attribute: %s\n",
	   diradmin_user_get_attribute (conn, "userPassword"));

}

diradmin_user *
diradmin_user_new_from_ldap (connection_profile * usethisone, char *userdn)
{
  //conn should already be connected, or else undefined behaviour!!!

  diradmin_user *user = NULL;

  int ldap_errors;

  GList* ocs = NULL;

  LDAP *h;
  LDAPMessage *searchresults = NULL;
  LDAPMessage *entry = NULL;

  char **value_collection = NULL;

  gchar *attribute;
  BerElement *attributehandler;
  gchar *localed_attr;
  int i = 0;
  int ocnum = 0;

  g_print ("\nFetching %s from directory\n", userdn);

  //check 4 connection
  h = connection_profile_get_ldap_handler (usethisone);
  g_assert (h);

  //look data up
  ldap_errors =
    ldap_search_s (h, userdn, LDAP_SCOPE_BASE, "(objectclass=*)", NULL, 0,
		   &searchresults);

  if (ldap_errors)
    {
      //any error?
      g_print ("LDAP error while creating a diradmin_user structure for ");
      g_print (userdn);
      g_print (": ");
      g_print (ldap_err2string (ldap_errors));
      g_print ("\n");
      //ldap_msgfree (searchresults);
	  if (ldap_errors ==  LDAP_SERVER_DOWN) {
		connection_profile_invalidate(usethisone);
	  }
	  return NULL;
    }
  else
    {
      //rock on dude, let's go ahead
      user = diradmin_user_new (userdn, NULL);

      //get only first entry
      entry = ldap_first_entry (h, searchresults);

      // loop thru attribute values

      attribute = ldap_first_attribute (h, entry, &attributehandler);

      g_assert (attribute);
      while (attribute)
	{
	  value_collection = ldap_get_values (h, entry, attribute);
	  g_assert (value_collection);
	  if (g_strcasecmp (attribute, "host") == 0)
	    {
	      for (i = 0; value_collection[i]; i++)
		{
		  //g_print ("\nServer: %s", value_collection[i]);
                  if (g_strcasecmp(value_collection[i],"*")==0)
                  diradmin_user_allowallservers(user);
                  else
		  diradmin_user_add_allowedserver (user, value_collection[i]);
		}
	    }
	  else if (g_strcasecmp (attribute, "objectclass") == 0)
	    {
	      for (i = 0; value_collection[i]; i++)
		{

/*diradmin_user_dump(user);*/
diradmin_user_set_objectclasses (user,NULL);
/* changed to bring only the USEFUL object classes that I know about */
   for (ocnum=0;handled_objectclasses[ocnum];ocnum++)
     if (g_strcasecmp(value_collection[i],handled_objectclasses[ocnum])==0)
       {
         //g_print ("\nRecognized object class: %s", value_collection[i]);
         ocs = g_list_append(ocs,g_strdup(value_collection[i]));
         // COMMENTED to track bug      diradmin_user_add_objectclass (user, value_collection[i]);
       }
         /* end change */
		}
	    }
	  else
	    {
	      g_assert (value_collection[0]);
              //detect if gecos
              if (g_strcasecmp (attribute, "gecos") == 0)
                 localed_attr = convert_from_ascii(value_collection[0]);
              else
                 localed_attr = convert_from_utf8(value_collection[0]);
	      diradmin_user_set_attribute (user, attribute,
					   localed_attr);
              g_free (localed_attr);
	      //g_print ("\n%s: %s", attribute, value_collection[0]);
	    }
	  ldap_value_free (value_collection);
	  attribute = ldap_next_attribute (h, entry, attributehandler);
	}

      ldap_msgfree (searchresults);
    }


  diradmin_user_set_objectclasses (user, ocs);

  /*g_print ("\n\n************STRAIGHT FROM THE DIRECTORY***********:");
  diradmin_user_dump(user);
  g_print ("\n\n************END      FROM THE DIRECTORY***********:");*/


  return (user);
}


GList *
diradmin_user_get_allowedservers (diradmin_user * conn)
{
  //return a reference to the allowedservers list.  shouldnt be manipulated outside.
  return g_list_first (conn->diradmin_user_allowedservers);
}

void
diradmin_user_add_allowedserver (diradmin_user * conn, gchar * allowedserver)
{
  diradmin_user_remove_allowedserver (conn, allowedserver);
  conn->diradmin_user_allowedservers =
    g_list_append (conn->diradmin_user_allowedservers,
		   g_strdup (allowedserver));
  //g_print ("\nAdding object class Account\n");
  diradmin_user_add_objectclass (conn, "account");
  conn->allserversallowed =FALSE;
}

void
diradmin_user_revokeallservers(diradmin_user*c) {
 //g_print("Se revocaron todos los servers");
 diradmin_user_set_allowedservers(c,NULL);
 diradmin_user_remove_objectclass(c,"account");
 c->allserversallowed = FALSE;

}

void
diradmin_user_allowallservers (diradmin_user * conn)
{
  diradmin_user_revokeallservers(conn);
  diradmin_user_add_objectclass(conn,"account");
  conn->allserversallowed=TRUE;

}

gboolean
diradmin_user_has_allservers (diradmin_user * conn) {
return conn->allserversallowed;
}

void
diradmin_user_remove_allowedserver (diradmin_user * conn, gchar
				    * allowedserver)
{
  GList *iterator = NULL;
  iterator = g_list_find_custom (conn->diradmin_user_allowedservers,
				 allowedserver, (GCompareFunc) g_strcasecmp);
  if (iterator)
    {
      conn->diradmin_user_allowedservers =
	g_list_remove_link (conn->diradmin_user_allowedservers, iterator);
      g_free (iterator->data);
      g_list_free_1 (iterator);
    }
  if (g_list_length (conn->diradmin_user_allowedservers) == 0)
    g_print ("\nRemoving object class Account\n");
  diradmin_user_remove_objectclass (conn, "account");

}

gboolean
diradmin_user_has_allowedserver (diradmin_user * conn, gchar * allowedserver)
{
  gchar *data;
  GList *iterator = NULL;
  iterator = g_list_first (conn->diradmin_user_allowedservers);
  while (iterator)
    {
      data = iterator->data;
      if (g_strcasecmp (allowedserver, iterator->data) == 0)
	{
	  return (TRUE);
	}
      iterator = g_list_next (iterator);
    }
  return (FALSE);
}

void
diradmin_user_set_allowedservers (diradmin_user * conn, GList
				  * allowedserverlist)
{
  //removes all allowedservers in the list.  that means frees its allocated storage.

  GList *newallowedservers = NULL;
  GList *allowedservers = NULL;


  //free the old allowedservers list
  allowedservers = g_list_first (diradmin_user_get_allowedservers (conn));
  while (allowedservers)
    {
      g_free (allowedservers->data);
      allowedservers = g_list_next (allowedservers);
    }
  g_list_free (g_list_first (diradmin_user_get_allowedservers (conn)));

  //make a copy of the passed allowedservers list into newallowedservers
  allowedservers = g_list_first (allowedserverlist);
  while (allowedservers)
    {
      newallowedservers =
	g_list_append (newallowedservers, g_strdup (allowedservers->data));
      allowedservers = g_list_next (allowedservers);
    }

  conn->diradmin_user_allowedservers = newallowedservers;
}

//objectclasses
GList *
diradmin_user_get_objectclasses (diradmin_user * conn)
{
  //return a reference to the allowedservers list.  shouldnt be manipulated outside.
  return g_list_first (conn->diradmin_user_objectclasses);
}

void
diradmin_user_add_objectclass (diradmin_user * conn, gchar * allowedserver)
{
  g_assert(conn);
  g_assert(allowedserver);

  diradmin_user_remove_objectclass (conn, allowedserver);
  conn->diradmin_user_objectclasses =
    g_list_append (conn->diradmin_user_objectclasses,
		   g_strdup (allowedserver));
  //g_print("\nAdd objectclass: adding %s class", allowedserver);


}

void
diradmin_user_remove_objectclass (diradmin_user * conn, gchar * allowedserver)
{
  GList *iterator = NULL;
  iterator = g_list_find_custom (conn->diradmin_user_objectclasses,
				 allowedserver, (GCompareFunc) g_strcasecmp);
  if (iterator)
    {
      conn->diradmin_user_objectclasses =
	g_list_remove_link (conn->diradmin_user_objectclasses, iterator);
      g_free (iterator->data);
      g_list_free_1 (iterator);
    }
}

gboolean
diradmin_user_has_objectclass (diradmin_user * conn, gchar * allowedserver)
{
  gchar *data;
  GList *iterator = NULL;

  iterator = g_list_first (conn->diradmin_user_objectclasses);
  while (iterator)
    {
      data = iterator->data;
      if (g_strcasecmp (allowedserver, iterator->data) == 0)
	{
	  return (TRUE);
	}
      iterator = g_list_next (iterator);
    }
  return (FALSE);
}

void
diradmin_user_set_objectclasses (diradmin_user * conn, GList
				 * allowedserverlist)
{

  //removes all allowedservers in the list.  that means frees its allocated storage.

  GList *newallowedservers = NULL;
  GList *allowedservers = NULL;

  //free the old allowedservers list
  allowedservers = g_list_first (diradmin_user_get_objectclasses (conn));
  while (allowedservers)
    {
      g_free (allowedservers->data);
      allowedservers = g_list_next (allowedservers);
    }
  g_list_free (g_list_first (conn->diradmin_user_objectclasses));

  //make a copy of the passed allowedservers list into newallowedservers
  allowedservers = g_list_first (allowedserverlist);
  while (allowedservers)
    {
      newallowedservers =
	g_list_append (newallowedservers, g_strdup (allowedservers->data));
      allowedservers = g_list_next (allowedservers);
    }

  conn->diradmin_user_objectclasses = newallowedservers;
}
void
diradmin_user_dump (diradmin_user * conn)
{

  GList *objectclasses = NULL;

  g_print("\nDump for user %s:", diradmin_user_get_attribute (conn,"dn"));

  objectclasses = g_list_first(diradmin_user_get_objectclasses (conn));
  while (objectclasses)
    {
      g_print("\n    Object class: %s" ,(char*)objectclasses->data);
      objectclasses = g_list_next (objectclasses);
    }

}



ldaptransaction *
diradmin_user_generate_ldapdiff (diradmin_user * oldone,
				 diradmin_user * newone)
{

  ldaptransaction *t;
  gchar **vals = NULL;
  GList *l = NULL;
  gint acnt = 0;
  gchar *oldattr = NULL;
  gchar *newattr = NULL;
//  gboolean samba_user = 0;
  gboolean holder;
  diradmin_user*m; gchar*n;
  gboolean ispassword;


  g_assert (oldone);
  g_assert (newone);

  /*g_print ("\n\nOld user dump:");
  diradmin_user_dump(oldone);
  g_print ("\n\nNew user dump:");
  diradmin_user_dump(newone);*/

  diradmin_user_tighten_objectclasses (newone);
//  samba_user = diradmin_user_has_objectclass (newone, "sambaaccount");

  t = ldaptransaction_new ();

  // veremos con objectclasses. poner los que no, quitar los que si

  acnt = 0;

  l = diradmin_user_get_objectclasses (oldone);
  vals = g_new0 (gchar *, g_list_length(l) + 1);
  for (l = g_list_first(l); l; l = g_list_next (l))
    if (diradmin_user_has_objectclass (newone, l->data) == FALSE)
      {
        vals[acnt] = g_strdup (l->data); acnt++;
      }

  if (acnt)
    ldaptransaction_delete (t, "objectclass", vals);
  else
    g_free(vals);

  acnt = 0;
  l = diradmin_user_get_objectclasses (newone);
  vals = g_new0 (gchar *, g_list_length(l) + 11);

  for (l = g_list_first(l); l; l = g_list_next (l)) {
    //g_print ("\nChecking if we need to add %s (%d)... ",(char*)l->data,g_list_length(l));
    m = oldone;
    n = l->data;
    holder = diradmin_user_has_objectclass (m,n);
    if (holder == FALSE)
      {
        //g_print ("YES");
        vals[acnt] = g_strdup (l->data); acnt++;
      }
  }

  if (acnt)
    ldaptransaction_add (t, "objectclass", vals);
  else
    g_free(vals);


  // veremos con servers. poner los que no, quitar los que si

  if (diradmin_user_has_allservers(newone))
   if (!diradmin_user_has_allservers(oldone)) {
	  vals = g_new0 (gchar *, 2);
	  vals[0] = g_strdup ("*");
	  ldaptransaction_add (t, "host", vals); }

  if (diradmin_user_has_allservers(oldone))
   if (!diradmin_user_has_allservers(newone)) {
	  vals = g_new0 (gchar *, 2);
	  vals[0] = g_strdup ("*");
	  ldaptransaction_delete (t, "host", vals);}

  acnt = 0;
  l = diradmin_user_get_allowedservers (oldone);
  vals = g_new0 (gchar *, g_list_length(l) + 1);
  for (l = g_list_first(l); l; l = g_list_next (l))
    if (diradmin_user_has_allowedserver (newone, l->data) == FALSE)
      {
        vals[acnt] = g_strdup (l->data); acnt++;
      }

  if (acnt)
    ldaptransaction_delete (t, "host", vals);
  else
    g_free(vals);

  acnt = 0;
  l = diradmin_user_get_allowedservers (newone);
  vals = g_new0 (gchar *, g_list_length(l) + 1);
  for (l = g_list_first(l); l; l = g_list_next (l))
    if (diradmin_user_has_allowedserver (oldone, l->data) == FALSE)
      {
        vals[acnt] = g_strdup (l->data); acnt++;
      }

  if (acnt)
    ldaptransaction_add (t, "host", vals);
  else
    g_free(vals);


  //chequeo de los atributos
	for (acnt = 0; attrs2chk[acnt]; acnt++)
	{
		oldattr = diradmin_user_get_attribute (oldone, attrs2chk[acnt]);
		newattr = diradmin_user_get_attribute (newone, attrs2chk[acnt]);
		ispassword = g_strcasecmp  (attrs2chk[acnt], "userPassword") == 0  ||  g_strcasecmp  (attrs2chk[acnt], "authPassword") == 0 ||  g_strcasecmp (attrs2chk[acnt], "ntPassword") == 0 || g_strcasecmp (attrs2chk[acnt], "lmPassword") == 0;

		// if any attribute isn't empty
		if (oldattr || newattr) {
			//g_print ("\nattribute %s:\n   old %s\n   new %s",
			//	 attrs2chk[acnt], oldattr, newattr);

			// if the attribute isn't a password or the date of last change, and the old attribute existed but the new one doesn't
			if (!ispassword  &&  g_strcasecmp (attrs2chk[acnt], "shadowLastChange") != 0  && oldattr != NULL && newattr == NULL) {
			vals = g_new0 (gchar *, 2);
				if (g_strcasecmp (attrs2chk[acnt], "gecos") == 0) {
					vals[0] = convert_to_ascii(oldattr);
				}
				else {
					vals[0] = convert_to_utf8(oldattr);
				}
				ldaptransaction_delete (t, attrs2chk[acnt], vals);
			}
			// else if the old attribute didn't exist but was added
			else if (oldattr == NULL && newattr != NULL)
			{
				vals = g_new0 (gchar *, 2);
				//if it's the gecos, please convert to ascii first
				if (g_strcasecmp (attrs2chk[acnt], "gecos") == 0) {
					vals[0] = convert_to_ascii(newattr);
				}
				else {
					vals[0] = convert_to_utf8(newattr);
				}
				ldaptransaction_replace (t, attrs2chk[acnt], vals);
			}
			// else if both existed but are different (excluding the case that the attribute we're checking is the DN)
			else if (oldattr && newattr && strcmp (oldattr, newattr) != 0   && strcmp (oldattr, "dn") != 0)
			{
			vals = g_new0 (gchar *, 2);
				//if it's the gecos, please convert to ascii first
				if (g_strcasecmp (attrs2chk[acnt], "gecos") == 0) {
					vals[0] = convert_to_ascii(newattr);
				}
				else {
					vals[0] = convert_to_utf8(newattr);
				}
				ldaptransaction_replace (t, attrs2chk[acnt], vals);
			}
		}
	}

	return (t);

}

diradmin_user *
create_user_struct_from_dialogbox (connection_profile * conn,
				   GtkWidget * dialogbox, gchar * dn)
{

  diradmin_user *newuser;

  gint iterator;
  gchar *uid = NULL;
  gchar *uidnumber = NULL;
  gchar *gidnumber = NULL;
  gchar *givenname = NULL;
  gchar *sn = NULL;
  gchar *cn = NULL;
  gchar *homedirectory = NULL;
  gchar *loginshell = NULL;
  gchar *userpassword = NULL;
  gchar *gecos = NULL;
  gchar *mail = NULL;
  gchar *mailhost = NULL;
  gchar *mailroutingaddress = NULL;
  gchar *shadowmin = NULL;
  gchar *shadowmax = NULL;
  gchar *shadowwarning = NULL;
  gchar *smbHome = NULL;
  gchar *homeDrive = NULL;
  gchar *profilePath = NULL;
  gchar *scriptPath = NULL;
  gchar *lmPassword = NULL;
  gchar *ntPassword = NULL;
  gchar *shadowinactive = NULL;

  char buf[32];
  time_t shadowexpiretime;

  GtkCList *allowedservers = NULL;
  gchar *allowedserver = NULL;

  allowedservers = (GtkCList *) lookup_widget (dialogbox, "allowedservers");

  uid = gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "uid")));
  uidnumber =
    gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "uidnumber")));
  gidnumber =
    cn_to_gidnumber (conn,
		     gtk_entry_get_text (GTK_ENTRY
					 (lookup_widget
					  (dialogbox, "gidnumber"))));
  sn = gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "sn")));
  cn = gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "cn")));
  givenname =
    gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "givenname")));
  homedirectory =
    gtk_entry_get_text (GTK_ENTRY
			(lookup_widget (dialogbox, "homedirectory")));
  loginshell =
    gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "loginshell")));

  gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "loginshell")));
  mail = gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "mail")));
  mailhost =
    gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "mailhost")));
  mailroutingaddress =
    gtk_entry_get_text (GTK_ENTRY
			(lookup_widget (dialogbox, "mailroutingaddress")));

  userpassword =
    gtk_entry_get_text (GTK_ENTRY
			(lookup_widget (dialogbox, "userpassword")));

  lmPassword =
    gtk_entry_get_text (GTK_ENTRY
			(lookup_widget (dialogbox, "userpassword")));
  ntPassword =
    gtk_entry_get_text (GTK_ENTRY
			(lookup_widget (dialogbox, "userpassword")));
  gecos = gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "cn")));

  newuser = diradmin_user_new (dn, NULL);

  diradmin_user_set_attribute (newuser, "uid", uid);
  diradmin_user_set_attribute (newuser, "uidnumber", uidnumber);
  diradmin_user_set_attribute (newuser, "gidnumber", gidnumber);
  diradmin_user_set_attribute (newuser, "givenname", givenname);
  diradmin_user_set_attribute (newuser, "sn", sn);
  diradmin_user_set_attribute (newuser, "cn", cn);
  diradmin_user_set_attribute (newuser, "homedirectory", homedirectory);
  diradmin_user_set_attribute (newuser, "loginshell", loginshell);
  diradmin_user_set_attribute (newuser, "gecos", gecos);
  diradmin_user_set_attribute (newuser, "mail", mail);

  g_free (gidnumber);		//mallocced by the function

  //shadow password setting attributes
  shadowmin =
    gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "shadowmin")));
  shadowmax =
    gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "shadowmax")));
  shadowwarning =
    gtk_entry_get_text (GTK_ENTRY
			(lookup_widget (dialogbox, "shadowwarning")));
  shadowinactive =
    gtk_entry_get_text (GTK_ENTRY
			(lookup_widget (dialogbox, "shadowinactive")));
  diradmin_user_set_attribute (newuser, "shadowmin", shadowmin);
  diradmin_user_set_attribute (newuser, "shadowmax", shadowmax);
  diradmin_user_set_attribute (newuser, "shadowwarning", shadowwarning);
  diradmin_user_set_attribute (newuser, "shadowinactive", shadowinactive);

//organizationalperson
  diradmin_user_set_attribute (newuser, "initials",
			       gtk_entry_get_text (GTK_ENTRY
						   (lookup_widget
						    (dialogbox,
						     "initials"))));
  diradmin_user_set_attribute (newuser, "title",
			       gtk_entry_get_text (GTK_ENTRY
						   (lookup_widget
						    (dialogbox, "title"))));
  diradmin_user_set_attribute (newuser, "physicaldeliveryofficename",
			       gtk_entry_get_text (GTK_ENTRY
						   (lookup_widget
						    (dialogbox,
						     "physicaldeliveryofficename"))));
  diradmin_user_set_attribute (newuser, "ou",
			       gtk_entry_get_text (GTK_ENTRY
						   (lookup_widget
						    (dialogbox, "ou"))));
  diradmin_user_set_attribute (newuser, "l",
			       gtk_entry_get_text (GTK_ENTRY
						   (lookup_widget
						    (dialogbox, "l"))));
  diradmin_user_set_attribute (newuser, "employeenumber",
			       gtk_entry_get_text (GTK_ENTRY
						   (lookup_widget
						    (dialogbox, "employeenumber"))));
  diradmin_user_set_attribute (newuser, "telephonenumber",
			       gtk_entry_get_text (GTK_ENTRY
						   (lookup_widget
						    (dialogbox,
						     "telephonenumber"))));
  diradmin_user_set_attribute (newuser, "facsimiletelephonenumber",
			       gtk_entry_get_text (GTK_ENTRY
						   (lookup_widget
						    (dialogbox,
						     "facsimiletelephonenumber"))));
  diradmin_user_set_attribute (newuser, "o",
			       gtk_entry_get_text (GTK_ENTRY
						   (lookup_widget
						    (dialogbox, "o"))));
  diradmin_user_set_attribute (newuser, "homephone",
			       gtk_entry_get_text (GTK_ENTRY
						   (lookup_widget
						    (dialogbox,
						     "homephone"))));
  diradmin_user_set_attribute (newuser, "mobile",
			       gtk_entry_get_text (GTK_ENTRY
						   (lookup_widget
						    (dialogbox, "mobile"))));

  if (GTK_TOGGLE_BUTTON
      (lookup_widget (dialogbox, "shadowexpirecheck"))->active)
    {
      shadowexpiretime =
	gnome_date_edit_get_date ((GnomeDateEdit *)
				  lookup_widget (dialogbox, "shadowexpire"));
      snprintf (buf, sizeof buf, "%ld", shadowexpiretime / (60 * 60 * 24 ) + 1);
      diradmin_user_set_attribute (newuser, "shadowexpire", buf);

    }

  //g_print ("constatando servers logontoallservers");

  if (!GTK_TOGGLE_BUTTON
      (lookup_widget (dialogbox, "logontoallservers"))->active)
    {
        //g_print("\n****Se desactiv todos los servers para este user\n");
	diradmin_user_revokeallservers (newuser);
    }

  if (GTK_TOGGLE_BUTTON
      (lookup_widget (dialogbox, "logontoallservers"))->active)
    {
        //g_print("\n****Se activ todos los servers para este user\n");
	diradmin_user_allowallservers (newuser);
    }
  else {
      for (iterator = 0; iterator < allowedservers->rows; iterator++)
	{
	  gtk_clist_get_text (allowedservers, iterator, 0, &allowedserver);
	  diradmin_user_add_allowedserver (newuser, allowedserver);
	}
  }

  if (GTK_TOGGLE_BUTTON
      (lookup_widget (dialogbox, "enablemailpolicycontrol"))->active)
    {
      diradmin_user_set_attribute (newuser, "maillocaladdress", mail);
      if (GTK_TOGGLE_BUTTON
	  (lookup_widget (dialogbox, "mailroutingaddressset"))->active)
	diradmin_user_set_attribute (newuser, "mailroutingaddress",
				     mailroutingaddress);
      if (GTK_TOGGLE_BUTTON
	  (lookup_widget (dialogbox, "mailhostset"))->active)
	diradmin_user_set_attribute (newuser, "mailhost", mailhost);
    }
  if (GTK_TOGGLE_BUTTON
      (lookup_widget (dialogbox, "enablesambaobjectclass"))->active)
    {
      char rid[11];

      smbHome =
	gtk_entry_get_text (GTK_ENTRY (lookup_widget (dialogbox, "smbHome")));
      homeDrive =
	gtk_entry_get_text (GTK_ENTRY
			    (lookup_widget (dialogbox, "homeDrive")));
      profilePath =
	gtk_entry_get_text (GTK_ENTRY
			    (lookup_widget (dialogbox, "profilePath")));
      scriptPath =
	gtk_entry_get_text (GTK_ENTRY
			    (lookup_widget (dialogbox, "scriptPath")));
      diradmin_user_set_attribute (newuser, "smbHome", smbHome);
      diradmin_user_set_attribute (newuser, "homeDrive", homeDrive);
      diradmin_user_set_attribute (newuser, "profilePath", profilePath);
      diradmin_user_set_attribute (newuser, "scriptPath", scriptPath);

      sprintf(rid, "%d", 2*atoi(uidnumber) + 1000);
      diradmin_user_set_attribute (newuser, "rid", rid);
    }

  if (strlen (userpassword) > 0)
    diradmin_user_set_password (newuser, userpassword);


  return (newuser);
}



ldaptransaction *
diradmin_user_create_ldapdiff (diradmin_user * newone)
{

  ldaptransaction *t = NULL;
  gchar **values;
  GList *l = NULL;
  gint acnt = 0;

  g_assert (newone);
  t = ldaptransaction_new ();
/*  l = diradmin_user_get_objectclasses (newone);
  for (l = diradmin_user_get_objectclasses (newone); l; l = g_list_next (l))
    {
      values = g_new0 (gchar *, 2);
      values[0] = g_strdup (l->data);
      ldaptransaction_add (t, "objectclass", values);
    }
  for (l = diradmin_user_get_allowedservers (newone); l; l = g_list_next (l))
    {
      values = g_new0 (gchar *, 2);
      values[0] = g_strdup (l->data);
      ldaptransaction_add (t, "host", values);
    }*/

  /* THIS GOT CHANGED!!!  -Rudd-O */
  l = diradmin_user_get_objectclasses (newone);
  values = g_new0(gchar*,g_list_length(l)+1);
  acnt = 0;
  for (l = g_list_first(l); l; l = g_list_next (l))
    {
      values[acnt] = g_strdup (l->data); acnt++;
    }
  ldaptransaction_add (t, "objectclass", values);

  if (diradmin_user_has_allservers(newone)) {
      values = g_new0 (gchar *, 2);
      values[0] = g_strdup ("*");
      ldaptransaction_add (t, "host", values);
  }

  l = diradmin_user_get_allowedservers (newone);
  if (g_list_length(l)) {
    values = g_new0(gchar*,g_list_length(l)+1);
    acnt = 0;
    for (l = g_list_first(l); l; l = g_list_next (l))
      {
        values[acnt] = g_strdup (l->data); acnt++;
      }
    ldaptransaction_add (t, "host", values);
  }
  /* change is up to here */

  for (acnt = 0; attrs2chk[acnt]; acnt++)
    if (diradmin_user_get_attribute (newone, attrs2chk[acnt]) != NULL)
      {
	values = g_new0 (gchar *, 2);

        if (g_strcasecmp (attrs2chk[acnt], "gecos") == 0)
             values[0] = convert_to_ascii(diradmin_user_get_attribute
(newone, attrs2chk[acnt]));
        else
             values[0] = convert_to_utf8(diradmin_user_get_attribute (newone, attrs2chk[acnt]));
//g_print("Attribute %s value %s\n",attrs2chk[acnt],values[0]);
	ldaptransaction_add (t, attrs2chk[acnt], values);
      }


  ldaptransaction_dump (t);
  return t;

}


syntax highlighted by Code2HTML, v. 0.9.1