/*
    GQ -- a GTK-based LDAP client
    Copyright (C) 1998-2003 Bert Vermeulen
    Copyright (C) 2002-2003 Peter Stamfest
    Copyright (C) 2006,2007 Sven Herzberg

    This program is released under the Gnu General Public License with
    the additional exemption that compiling, linking, and/or using
    OpenSSL is allowed.

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

#include "gq-formfill.h"

#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>

#include <glib.h>
#include <glib/gi18n.h>

#include <lber.h>

#include "common.h"
#include "configfile.h"
#include "encode.h"
#include "errorchain.h"
#include "gq-ldap.h"
#include "gq-utilities.h"
#include "ldif.h"
#include "schema.h"
#include "syntax.h"
#include "tinput.h"

struct GqFormfillPrivate {
	GqServer *server;
	gchar* attrname;
#warning "FIXME: change type of flags"
	int flags;

	GQDisplayType displaytype;
	GType dt_handler; // GQTypeDisplayClass derivate
	struct syntax_handler *syntax;

	GList *values;
	GList *widgetList;
	int num_inputfields;

	GtkWidget *event_box;
	GtkWidget *label;
	GtkWidget *morebutton;
	GtkWidget *vbox;
};
#define P(i) (G_TYPE_INSTANCE_GET_PRIVATE((i), GQ_TYPE_FORMFILL, struct GqFormfillPrivate))

static GList *internalAttrs = NULL;

void init_internalAttrs() {
     internalAttrs = g_list_append(internalAttrs, "creatorsName");
     internalAttrs = g_list_append(internalAttrs, "creatorsName");
     internalAttrs = g_list_append(internalAttrs, "createTimestamp");
     internalAttrs = g_list_append(internalAttrs, "modifiersName");
     internalAttrs = g_list_append(internalAttrs, "modifyTimestamp");
     internalAttrs = g_list_append(internalAttrs, "subschemaSubentry");
}

#warning "FIXME: a hash table is quicker here"
gboolean isInternalAttr(const char *attr) {
     GList *filterout;
     gboolean filtered = FALSE;

     for (filterout = internalAttrs ; filterout ; 
	  filterout = filterout->next) {
	  if (strcasecmp(attr, filterout->data) == 0) {
	       filtered = TRUE;
	       break;
	  }
     }
     return filtered;
}

GqFormfill*
gq_formfill_new(void)
{
     GqFormfill *form;

     form = g_object_new(GQ_TYPE_FORMFILL, NULL);
     /* checking for NULL-ness actually is useless for memory
	allocated through g_new0 et. al. */
     g_assert(form);

#warning "FIXME: move into init()"
     gq_formfill_set_n_inputfields(form, 1);
     P(form)->displaytype = DISPLAYTYPE_ENTRY;
     P(form)->dt_handler = get_dt_handler(P(form)->displaytype);
#warning "FIXME: add GQ_FLAGS_NONE"
     P(form)->flags = 0;
     P(form)->values = NULL;

     P(form)->widgetList = NULL;

     return(form);
}

static GByteArray*
formfill_get_data_i(GqFormfill* self, guint i)
{
	GtkWidget* widget = g_list_nth_data(gq_formfill_get_widgets(self), i);
	GqDisplayTypeHandlerClass* handler = g_type_class_ref(gq_formfill_get_dt_handler(self));
	GByteArray* retval = handler->get_data(self, widget);
	g_type_class_unref(handler);
	return retval ? retval : g_byte_array_new();
}

gboolean
gq_formfill_equals_data(GqFormfill* form1,
			GqFormfill* form2)
{
	gint i;
	guint inputs;
	g_return_val_if_fail(GQ_IS_FORMFILL(form1), FALSE);
	g_return_val_if_fail(GQ_IS_FORMFILL(form2), FALSE);

	inputs = gq_formfill_get_n_inputfields(form1);
	if(inputs != gq_formfill_get_n_inputfields(form2)) {
		return FALSE;
	}

	for(i = 0; i < inputs; i++) {
		// FIXME: PERFORMANCE: could use an iterator here
		GByteArray* data1 = formfill_get_data_i(form1, i);
		GByteArray* data2 = formfill_get_data_i(form2, i);
		if(data1->len == data2->len) {
			if(!bcmp(data1->data, data2->data, data1->len)) {
				g_byte_array_free(data1, TRUE);
				g_byte_array_free(data2, TRUE);
				continue;
			}
		}
		g_byte_array_free(data1, TRUE);
		g_byte_array_free(data2, TRUE);

		return FALSE;
	}
	return TRUE;
}

gchar const*
gq_formfill_get_attrname(GqFormfill const* self)
{
	g_return_val_if_fail(GQ_IS_FORMFILL(self), NULL);

	return P(self)->attrname;
}

void
gq_formfill_set_attrname(GqFormfill * self,
			 gchar const* attrname)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));

	if(attrname == P(self)->attrname ||
	   (attrname && P(self)->attrname && !strcmp(attrname, P(self)->attrname)))
	{
		return;
	}

	g_free(P(self)->attrname);
	P(self)->attrname = g_strdup(attrname);

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "attrname");
}

GqDisplayType
gq_formfill_get_display_type(GqFormfill const* self)
{
	g_return_val_if_fail(GQ_IS_FORMFILL(self), GQ_DISPLAY_TYPE_ENTRY);

	return P(self)->displaytype;
}

void
gq_formfill_set_display_type(GqFormfill   * self,
			     GqDisplayType  dtype)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));

	if(P(self)->displaytype == dtype) {
		return;
	}

	P(self)->displaytype = dtype;

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "display-type");
}

GType
gq_formfill_get_dt_handler(GqFormfill const* self)
{
	g_return_val_if_fail(GQ_IS_FORMFILL(self), G_TYPE_INVALID);

	return P(self)->dt_handler;
}

void
gq_formfill_change_display_type(GqFormfill* self,
				GType       dtype)
{
     GQTypeDisplayClass* new_h = NULL;
     int error_context = 0;

	g_return_if_fail(GQ_IS_FORMFILL(self));

#warning "FIXME: provide a real widget for the chain"
     error_context = error_new_context(_("Changeing display type"),
					   NULL);

     new_h = g_type_class_ref(dtype);
     if (new_h) {
	  GByteArray *d;
	  GList *l, *newlist = NULL;
	  GQTypeDisplayClass *old_h = g_type_class_ref(gq_formfill_get_dt_handler(self));

/*	  printf("change displaytype of %s to %s\n",  */
/*		 form->attrname, new_h->name); */

	  /* walk the list of widgets */
	  for (l = P(self)->widgetList ; l ; l = l->next) {
	       GtkWidget *w = (GtkWidget*) l->data;
#warning "FIXME: is this possible?"
	       if (!w) continue;

	       /* retrieve "old" data */
	       d = old_h->get_data(self, w);
	       gtk_widget_destroy(w);

	       /* create new widget */
#warning "FIXME: call set_dt_handler() for EVERY iteration?"
	       gq_formfill_set_dt_handler(self, dtype);
	       w = new_h->get_widget(error_context, self, d, NULL, NULL);

	       if (gq_formfill_get_flags(self) & FLAG_NO_USER_MOD) {
		    gtk_widget_set_sensitive(w, 0);
	       }

	       gtk_widget_show(w);

	       gtk_object_set_data(GTK_OBJECT(w),
				   "formfill", self);

	       gtk_box_pack_start(GTK_BOX(gq_formfill_get_vbox(self)), w, TRUE, TRUE, 0);

	       /* insert into browser */
	       newlist = g_list_append(newlist, w);
	  }
	  g_list_free(P(self)->widgetList);
	  P(self)->widgetList = newlist;

	  g_type_class_unref(old_h);
     }

     error_flush(error_context);
     g_type_class_unref(new_h);
}

void
gq_formfill_set_dt_handler(GqFormfill* self,
			   GType       dt_handler)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));
	g_return_if_fail(g_type_is_a(dt_handler, GQ_TYPE_TYPE_DISPLAY));

	if(P(self)->dt_handler == dt_handler) {
		return;
	}

	P(self)->dt_handler = dt_handler;

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "dt-handler");
}

GtkWidget*
gq_formfill_get_event_box(GqFormfill const* self)
{
	g_return_val_if_fail(GQ_IS_FORMFILL(self), NULL);

	return P(self)->event_box;
}

void
gq_formfill_set_event_box(GqFormfill* self,
			  GtkWidget * event_box)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));
	g_return_if_fail(!event_box || GTK_IS_EVENT_BOX(event_box));

	if(event_box == P(self)->event_box) {
		return;
	}

	if(P(self)->event_box) {
		g_object_unref(P(self)->event_box);
		P(self)->event_box = NULL;
	}

	if(event_box) {
		P(self)->event_box = g_object_ref_sink(event_box);
	}

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "event-box");
}

int
gq_formfill_get_flags(GqFormfill const* self)
{
#warning "FIXME: return a flag here"
	g_return_val_if_fail(GQ_IS_FORMFILL(self), 0);

	return P(self)->flags;
}

void
gq_formfill_set_flag(GqFormfill* self,
		     int         flag)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));
#warning "FIXME: check flag range"

	if((P(self)->flags & flag) == flag) {
		return;
	}

	P(self)->flags |= flag;

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "flags");
}

void
gq_formfill_unset_flag(GqFormfill* self,
		       int         flag)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));
#warning "FIXME: check flag (to be set in the valid range)"

	if((P(self)->flags & flag) == 0) {
		return;
	}

	P(self)->flags &= ~flag;

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "flags");
}

GtkWidget*
gq_formfill_get_label(GqFormfill const* self)
{
	g_return_val_if_fail(GQ_IS_FORMFILL(self), NULL);

	return P(self)->label;
}

void
gq_formfill_set_label(GqFormfill* self,
		      GtkWidget * label)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));
	g_return_if_fail(!label || GTK_IS_LABEL(label));

	if(label == P(self)->label) {
		return;
	}

	if(P(self)->label) {
		g_object_unref(P(self)->label);
		P(self)->label = NULL;
	}

	if(label) {
		P(self)->label = g_object_ref_sink(label);
	}

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "label");
}

GtkWidget*
gq_formfill_get_morebutton(GqFormfill const* self)
{
	g_return_val_if_fail(GQ_IS_FORMFILL(self), NULL);

	return P(self)->morebutton;
}

void
gq_formfill_set_morebutton(GqFormfill* self,
			   GtkWidget * morebutton)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));
	g_return_if_fail(!morebutton || GTK_IS_BUTTON(morebutton));

	if(morebutton == P(self)->morebutton) {
		return;
	}

	if(P(self)->morebutton) {
		g_object_unref(P(self)->morebutton);
		P(self)->morebutton = NULL;
	}

	if(morebutton) {
		P(self)->morebutton = g_object_ref_sink(morebutton);
	}

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "morebutton");
}

int
gq_formfill_get_n_inputfields(GqFormfill const* self)
{
	g_return_val_if_fail(GQ_IS_FORMFILL(self), 0);

	return P(self)->num_inputfields;
}

void
gq_formfill_set_n_inputfields(GqFormfill* self,
			      int         n_fields)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));

	if(P(self)->num_inputfields == n_fields) {
		return;
	}

	P(self)->num_inputfields = n_fields;

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "n-inputfields");
}

GqServer*
gq_formfill_get_server(GqFormfill const* self)
{
	g_return_val_if_fail(GQ_IS_FORMFILL(self), NULL);

	return P(self)->server;
}

void
gq_formfill_set_server(GqFormfill* self,
		       GqServer  * server)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));
	g_return_if_fail(!server || GQ_IS_SERVER(server));

	if(server == P(self)->server) {
		return;
	}

	if(P(self)->server) {
		g_object_unref(P(self)->server);
		P(self)->server = NULL;
	}

	if(server) {
		P(self)->server = g_object_ref(server);
	}

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "server");
}

GqSyntax*
gq_formfill_get_syntax(GqFormfill const* self)
{
	g_return_val_if_fail(GQ_IS_FORMFILL(self), NULL);

	return P(self)->syntax;
}

void
gq_formfill_set_syntax(GqFormfill* self,
		       GqSyntax  * syntax)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));
	g_return_if_fail(!syntax || GQ_IS_SYNTAX(syntax));

	if(P(self)->syntax == syntax) {
		return;
	}

	if(P(self)->syntax) {
		P(self)->syntax = NULL;
	}

	if(syntax) {
		P(self)->syntax = syntax;
	}

	g_object_notify(G_OBJECT(self), "syntax");
}

GList*
gq_formfill_get_values(GqFormfill const* self)
{
	g_return_val_if_fail(GQ_IS_FORMFILL(self), NULL);

	return P(self)->values;
}

void
gq_formfill_add_value(GqFormfill* self,
		      GByteArray* value)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));
	g_return_if_fail(value);

	P(self)->values = g_list_append(P(self)->values, value);

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "values");
}

GtkWidget*
gq_formfill_get_vbox(GqFormfill const* self)
{
	g_return_val_if_fail(GQ_IS_FORMFILL(self), NULL);

	return P(self)->vbox;
}

void
gq_formfill_set_vbox(GqFormfill* self,
		     GtkWidget * vbox)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));
	g_return_if_fail(!vbox || GTK_IS_VBOX(vbox));

	if(vbox == P(self)->vbox) {
		return;
	}

	if(P(self)->vbox) {
		g_object_unref(P(self)->vbox);
		P(self)->vbox = NULL;
	}

	if(vbox) {
		P(self)->vbox = g_object_ref_sink(vbox);
	}

	g_object_notify(G_OBJECT(self), "vbox");
}

GList*
gq_formfill_get_widgets(GqFormfill const* self)
{
	g_return_val_if_fail(GQ_IS_FORMFILL(self), NULL);

	return P(self)->widgetList;
}

void
gq_formfill_clear_widgets(GqFormfill* self)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));

	g_list_free(P(self)->widgetList);
	P(self)->widgetList = NULL;

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "widgets");
}

void
gq_formfill_add_widget(GqFormfill* self,
		       GtkWidget * widget)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));
	g_return_if_fail(GTK_IS_WIDGET(widget));

	P(self)->widgetList = g_list_append(P(self)->widgetList, widget);

#warning "FIXME: enable"
	//g_object_notify(G_OBJECT(self), "widgets");
}

void free_formlist(GList *formlist)
{
     GList *f;

     for (f = formlist ; f ; f = f->next) {
	  free_formfill(GQ_FORMFILL(f->data));
     }
     g_list_free(formlist);
}


void
free_formfill_values(GqFormfill* self)
{
	g_return_if_fail(GQ_IS_FORMFILL(self));

	while(P(self)->values) {
		g_byte_array_free((GByteArray*)P(self)->values->data, TRUE);
		P(self)->values->data = NULL;
		P(self)->values = g_list_delete_link(P(self)->values, P(self)->values);
	}
}

/*
 * add form to formlist, checking first if the attribute type isn't
 * already in the list
 */
GList *formlist_append(GList *formlist, GqFormfill *form)
{
     GList *fl;
     GqFormfill *tmp;

     fl = formlist;
     while(fl) {

	  tmp = GQ_FORMFILL(fl->data);
	  if(!strcmp(P(tmp)->attrname, P(form)->attrname)) {
	       /* already have this attribute */
	       free_formfill(form);
	       return(formlist);
	  }

	  fl = fl->next;
     }

     formlist = g_list_append(formlist, form);

     return(formlist);
}

char *attr_strip(const char *attr)
{
     char *c = g_strdup(attr);
     char *d = g_utf8_strchr(c, -1, ';'); /* UTF-8: doesn't hurt */

     if (d) {
	  *d = 0;
     }
     return c;
}

static GList *formlist_from_entry_iteration(int error_context, 
					    LDAP *ld,
					    GqServer *server,
					    const char *dn, 
					    int ocvalues_only,
					    char **attrs,
					    GList *formlist)
{
     LDAPControl c;
     LDAPControl *ctrls[2] = { NULL, NULL } ;
     LDAPMessage *res, *entry;
     int rc, i;
     char *attr;
     GqFormfill *form;
     BerElement *ber;
     
     c.ldctl_oid		= LDAP_CONTROL_MANAGEDSAIT;
     c.ldctl_value.bv_val	= NULL;
     c.ldctl_value.bv_len	= 0;
     c.ldctl_iscritical	= 1;
     
     ctrls[0] = &c;

     rc = ldap_search_ext_s(ld,
			    dn,			/* search base */
			    LDAP_SCOPE_BASE,	/* scope */
			    "objectClass=*",	/* filter */
			    attrs,		/* attrs */
			    0,			/* attrsonly */
			    ctrls,		/* serverctrls */
			    NULL,		/* clientctrls */
			    NULL,		/* timeout */
			    LDAP_NO_LIMIT,	/* sizelimit */
			    &res);

     if(rc == LDAP_NOT_SUPPORTED) {
	  rc = ldap_search_s(ld, dn, LDAP_SCOPE_BASE, "(objectclass=*)",  
			     gq_server_get_hide_internals(server) ? NULL : attrs,
			     0, &res);
     }

     if (rc == LDAP_SERVER_DOWN) {
	  server->server_down++;
     } else if (rc != LDAP_SUCCESS) {
	  statusbar_msg("Searching for '%1$s' on '%2$s': %3$s", 
			dn, gq_server_get_name(server),
			ldap_err2string(rc));
	  return(formlist);
     } 

     if (rc == LDAP_SUCCESS) {
	  entry = ldap_first_entry(ld, res);
	  if(entry) {
	       char *cc;

	       for(attr = ldap_first_attribute(ld, entry, &ber); attr ;
		   attr = ldap_next_attribute(ld, entry, ber))
	       {
		    gboolean oc;
		    /* filter out some internal attributes... */
		    cc = attr_strip(attr);
		    if (gq_server_get_hide_internals(server)) {
			 if (isInternalAttr(cc)) {
			      ldap_memfree(attr);
			      if (cc) g_free(cc);
			      continue;
			 }
		    }

		    form = new_formfill();
		    g_assert(form);

		    gq_formfill_set_server(form, server);

		    gq_formfill_set_attrname(form, cc);
		    if (cc) g_free(cc);

		    oc  = strcasecmp(attr, "objectClass") == 0;

		    if(ocvalues_only == 0 || oc) {
			 struct berval **bervals = ldap_get_values_len(ld, res, attr);
			 struct berval **it;

			      for(it = bervals, i=0; it && *it; it++, i++) {
				   GByteArray *gb = g_byte_array_new();
				   g_byte_array_append(gb,
						       (guchar*)(*it)->bv_val,
						       (*it)->bv_len);
				   g_byte_array_append(gb, (guchar*)"", 1); // append NUL byte
				   P(form)->values = g_list_append(P(form)->values,
								gb);
			      }

			 if(bervals) {
			      gq_formfill_set_n_inputfields(form, i);
			      ldap_value_free_len(bervals);
			 }
		    }

		    set_displaytype(error_context, server, form);
		    formlist = g_list_append(formlist, form);
		    ldap_memfree(attr);
	       }
#ifndef HAVE_OPENLDAP12
	       /* this bombs out with openldap 1.2.x libs... */
	       /* PSt: is this still true??? - introduced a configure check */
	       if(ber) {
		    ber_free(ber, 0);
	       }
#endif
	  }
	  
	  if (res) ldap_msgfree(res);
	  res = NULL;
     } 
     return formlist;
}

GList *formlist_from_entry(int error_context, 
			   GqServer *server, const char *dn, 
			   int ocvalues_only)
{
     GList *formlist;
     LDAP *ld;
     char *attrs[]    = { LDAP_ALL_USER_ATTRIBUTES,
			  LDAP_ALL_OPERATIONAL_ATTRIBUTES,
			  "ref",
			  NULL,
     };
     char *ref_attrs[] = { LDAP_ALL_USER_ATTRIBUTES,
			   "ref",
			   NULL
     };

     formlist = NULL;

     set_busycursor();

     statusbar_msg(_("Fetching %1$s from %2$s"), dn, gq_server_get_name(server));

     if( (ld = open_connection(error_context, server)) == NULL) {
          set_normalcursor();
          return(NULL);
     }

     formlist = 
	  formlist_from_entry_iteration(error_context, 
					ld,
					server,
					dn,
					ocvalues_only,
					gq_server_get_hide_internals(server) ? ref_attrs : attrs,
					NULL);

     close_connection(server, FALSE);
     set_normalcursor();

#ifdef HAVE_LDAP_STR2OBJECTCLASS
	formlist = add_schema_attrs(error_context, server, formlist);
#endif

     return(formlist);
}


GList*
dup_formlist(GList* oldlist)
{
     GList *newlist, *oldval, *newval;
     GqFormfill *oldform, *newform;

     newlist = NULL;

     while(oldlist) {
	  oldform = GQ_FORMFILL(oldlist->data);
	  newform = new_formfill();
	  g_assert(newform);
#warning "FIXME: use dup_formfill() once written"
	  gq_formfill_set_server(newform, gq_formfill_get_server(oldform));
	  gq_formfill_set_attrname(newform, gq_formfill_get_attrname(oldform));

	  gq_formfill_set_n_inputfields(newform, gq_formfill_get_n_inputfields(oldform));
	  gq_formfill_set_display_type(newform, gq_formfill_get_display_type(oldform));
	  gq_formfill_set_dt_handler(newform, gq_formfill_get_dt_handler(oldform));
	  gq_formfill_set_flag(newform, gq_formfill_get_flags(oldform));
	  gq_formfill_unset_flag(newform, ~gq_formfill_get_flags(oldform));
	  newval = NULL;
	  for(oldval = P(oldform)->values; oldval; oldval = g_list_next(oldval)) {
	       if(oldval->data) {
		    GByteArray *old = (GByteArray*) (oldval->data);
		    GByteArray *gb = g_byte_array_new();
		    g_byte_array_append(gb, old->data, old->len);

		    newval = g_list_append(newval, gb);
	       }
	  }
	  P(newform)->values = newval;
	  newlist = g_list_append(newlist, newform);

	  oldlist = oldlist->next;
     }

     return(newlist);
}


void dump_formlist(GList *formlist)
{
     GList *values;
     GqFormfill *form;

     while(formlist) {
	  form = GQ_FORMFILL(formlist->data);
	  printf("%s\n", P(form)->attrname);
	  values = P(form)->values;
#warning "FIXME: for loop"
	  while(values) {
	       if(values->data) {
		    GByteArray *d = (GByteArray*) values->data;
		    printf("\t");
		    fwrite(d->data, d->len, 1, stdout);
		    printf("\n");
	       }
	       values = values->next;
	  }

	  formlist = formlist->next;
     }

}


GqFormfill*
lookup_attribute(GList      * formlist,
		 gchar const* attr)
{
	GqFormfill* form;
	gchar const* attrname;

	g_return_val_if_fail(attr, NULL);

	for(; formlist; formlist = g_list_next(formlist)) {
		form = GQ_FORMFILL(formlist->data);
		attrname = gq_formfill_get_attrname(form);

		if(attrname && !strcasecmp(attrname, attr)) {
			return form;
		}
	}

	return NULL;
}

/* Returns an existing formfill object (if any) for attribute attr
   from the passed-in list.  The server_schema gets consulted to also
   check for aliases. The canonical attribute type may be retrieved
   when passing a non NULL value for attrtype */
GqFormfill *lookup_attribute_using_schema(GList *formlist, 
					       const char *attr,
					       struct server_schema *schema,
					       LDAPAttributeType **attrtype)
{
     GqFormfill *form;

#ifdef HAVE_LDAP_STR2OBJECTCLASS
     char **n;
     LDAPAttributeType *trythese = NULL;

     if (attrtype) *attrtype = NULL;

     if (schema) {
	  trythese = find_canonical_at_by_at(schema, attr);
	  if (attrtype) *attrtype = trythese;
     }
#endif /* HAVE_LDAP_STR2OBJECTCLASS */

     for( ; formlist ; formlist = formlist->next ) {
	  form = GQ_FORMFILL(formlist->data);

#ifdef HAVE_LDAP_STR2OBJECTCLASS
	  if (trythese && P(form)->attrname) {
	       for (n = trythese->at_names ; n && *n ; n++) {
		    if(!strcasecmp(P(form)->attrname, *n)) return(form);
	       }
	  } else {
	       if(P(form)->attrname && !strcasecmp(P(form)->attrname, attr))
		    return(form);
	  }
#else /* HAVE_LDAP_STR2OBJECTCLASS */
	  if(form->attrname && !strcasecmp(form->attrname, attr))
	       return(form);
#endif /* HAVE_LDAP_STR2OBJECTCLASS */
     }

     return(NULL);
}

/*
 * sure it is nice to use serverschema for this
 *
 */
int find_displaytype(int error_context, GqServer *server, 
		     GqFormfill *form)
{
     return get_display_type_of_attr(error_context, server, gq_formfill_get_attrname(form));
}

/*
 * now we use the serverschema for this
 *
 * hint, hint
 *
 */
void set_displaytype(int error_context, 
		     GqServer *server, GqFormfill *form)
{
     struct attr_settings *as;

     /* this is ugly... all of this needs a cleanup!!!! - FIXME */
     LDAPAttributeType *at =
	  find_canonical_at_by_at(server->ss, P(form)->attrname);

     if (at && at->at_no_user_mod) {
	  gq_formfill_set_flag(form, FLAG_NO_USER_MOD);
     }

     P(form)->syntax = get_syntax_handler_of_attr(error_context, server, 
					       P(form)->attrname, NULL);

     as = lookup_attr_settings(P(form)->attrname);
     if (as && as->defaultDT != -1) {
	  gq_formfill_set_display_type(form, as->defaultDT);
     } else {
	  gq_formfill_set_display_type(form, find_displaytype(error_context, server, form));
     }
     P(form)->dt_handler = get_dt_handler(P(form)->displaytype);
#warning "FIXME: update the dt_handler from set_display_type() automatically"
}

/* GType */
G_DEFINE_TYPE(GqFormfill, gq_formfill, G_TYPE_OBJECT);

enum {
	PROP_0,
	PROP_VBOX
};

static void
gq_formfill_init(GqFormfill* self G_GNUC_UNUSED)
{}

static void
formfill_finalize(GObject* object)
{
	GqFormfill* self = GQ_FORMFILL(object);

	gq_formfill_set_attrname(self, NULL);

#warning "FIXME: check"
	free_formfill_values(self);

#warning "FIXME: destroy"
	gq_formfill_clear_widgets(self);
	gq_formfill_set_vbox(self, NULL);

#warning "FIXME: dispose"
	gq_formfill_set_server(self, NULL);

	G_OBJECT_CLASS(gq_formfill_parent_class)->finalize(object);
}

static void
gq_formfill_class_init(GqFormfillClass* self_class)
{
	GObjectClass* object_class = G_OBJECT_CLASS(self_class);
	object_class->finalize = formfill_finalize;

	g_object_class_install_property(object_class,
					PROP_VBOX,
					g_param_spec_object("vbox",
							    _("VBox"),
							    _("The VBox containing the widgets"),
							    GTK_TYPE_VBOX,
							    0));

	/* GqFormfillClass */
	g_type_class_add_private(self_class, sizeof(struct GqFormfillPrivate));
}

