2007-07-09 [colin] 2.10.0cvs12
authorColin Leroy <colin@colino.net>
Mon, 9 Jul 2007 19:59:36 +0000 (19:59 +0000)
committerColin Leroy <colin@colino.net>
Mon, 9 Jul 2007 19:59:36 +0000 (19:59 +0000)
* src/Makefile.am
* src/addritem.c
* src/addritem.h
* src/editaddress.c
* src/editaddress_other_attributes_ldap.c
* src/editaddress_other_attributes_ldap.h
* src/editldap.c
* src/ldapctrl.c
* src/ldapctrl.h
* src/ldapupdate.c
Add capability to edit all inetOrgPerson
attributes on LDAP. Patch by Michael
Rasmussen

13 files changed:
ChangeLog
PATCHSETS
configure.ac
src/Makefile.am
src/addritem.c
src/addritem.h
src/editaddress.c
src/editaddress_other_attributes_ldap.c [new file with mode: 0644]
src/editaddress_other_attributes_ldap.h [new file with mode: 0644]
src/editldap.c
src/ldapctrl.c
src/ldapctrl.h
src/ldapupdate.c

index d60e7721cb403fc711790d0812763c87d9606aba..465e47229790d5b7c11cf8ccdab793bb1edc7652 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2007-07-09 [colin]     2.10.0cvs12
+
+       * src/Makefile.am
+       * src/addritem.c
+       * src/addritem.h
+       * src/editaddress.c
+       * src/editaddress_other_attributes_ldap.c
+       * src/editaddress_other_attributes_ldap.h
+       * src/editldap.c
+       * src/ldapctrl.c
+       * src/ldapctrl.h
+       * src/ldapupdate.c
+               Add capability to edit all inetOrgPerson
+               attributes on LDAP. Patch by Michael 
+               Rasmussen
+
 2007-07-09 [colin]     2.10.0cvs11
 
        * src/Makefile.am
index 51eb81d2b069f1d659c6ec8e4ac8345b45be3a72..aea83636ed9f1d2349a207429c208e201d5cd2d9 100644 (file)
--- a/PATCHSETS
+++ b/PATCHSETS
 ( cvs diff -u -r 1.5.10.5 -r 1.5.10.6 COPYING;  cvs diff -u -r 1.1.2.1 -r 1.1.2.2 manual/claws-mail-manual.xml;  cvs diff -u -r 1.1.2.13 -r 1.1.2.14 po/fi.po;  ) > 2.10.0cvs9.patchset
 ( cvs diff -u -r 1.30.2.33 -r 1.30.2.34 src/prefs_toolbar.c;  ) > 2.10.0cvs10.patchset
 ( cvs diff -u -r 1.155.2.66 -r 1.155.2.67 src/Makefile.am;  cvs diff -u -r 1.5.2.19 -r 1.5.2.20 src/exporthtml.c;  cvs diff -u -r 1.28.2.35 -r 1.28.2.36 src/mbox.c;  cvs diff -u -r 1.16.2.51 -r 1.16.2.52 src/msgcache.c;  cvs diff -u -r 1.1.2.44 -r 1.1.2.45 src/prefs_summaries.c;  cvs diff -u -r 1.3.2.48 -r 1.3.2.49 src/prefs_themes.c;  cvs diff -u -r 1.47.2.43 -r 1.47.2.44 src/procheader.c;  cvs diff -u -r 1.24.2.14 -r 1.24.2.15 src/common/Makefile.am;  cvs diff -u -r 1.6.2.11 -r 1.6.2.12 src/common/log.c;  cvs diff -u -r 1.1.2.7 -r 1.1.2.8 src/common/timing.h;  cvs diff -u -r 1.36.2.104 -r 1.36.2.105 src/common/utils.c;  cvs diff -u -r 1.20.2.46 -r 1.20.2.47 src/common/utils.h;  diff -u /dev/null src/common/w32_account.c;  cvs diff -u -r 1.1.2.1 -r 1.1.2.2 src/common/w32lib.h;  cvs diff -u -r 1.20.2.14 -r 1.20.2.15 src/gtk/Makefile.am;  cvs diff -u -r 1.2.2.18 -r 1.2.2.19 src/gtk/colorlabel.c;  cvs diff -u -r 1.1.2.8 -r 1.1.2.9 src/plugins/pgpcore/Makefile.am;  cvs diff -u -r 1.1.2.1 -r 1.1.2.2 src/plugins/pgpcore/claws.def;  cvs diff -u -r 1.1.2.26 -r 1.1.2.27 src/plugins/pgpcore/passphrase.c;  cvs diff -u -r 1.1.2.11 -r 1.1.2.12 src/plugins/pgpcore/pgp_viewer.c;  cvs diff -u -r 1.1.2.2 -r 1.1.2.3 src/plugins/pgpcore/plugin.def;  cvs diff -u -r 1.1.2.45 -r 1.1.2.46 src/plugins/pgpcore/sgpgme.c;  cvs diff -u -r 1.1.2.7 -r 1.1.2.8 src/plugins/pgpinline/Makefile.am;  cvs diff -u -r 1.1.2.1 -r 1.1.2.2 src/plugins/pgpinline/claws.def;  cvs diff -u -r 1.1.2.1 -r 1.1.2.2 src/plugins/pgpinline/mypgpcore.def;  cvs diff -u -r 1.1.2.2 -r 1.1.2.3 src/plugins/pgpinline/plugin.def;  cvs diff -u -r 1.1.2.9 -r 1.1.2.10 src/plugins/pgpmime/Makefile.am;  cvs diff -u -r 1.1.2.2 -r 1.1.2.3 src/plugins/pgpmime/claws.def;  cvs diff -u -r 1.1.2.1 -r 1.1.2.2 src/plugins/pgpmime/mypgpcore.def;  cvs diff -u -r 1.1.2.2 -r 1.1.2.3 src/plugins/pgpmime/plugin.def;  ) > 2.10.0cvs11.patchset
+( cvs diff -u -r 1.155.2.67 -r 1.155.2.68 src/Makefile.am;  cvs diff -u -r 1.13.2.11 -r 1.13.2.12 src/addritem.c;  cvs diff -u -r 1.12.2.9 -r 1.12.2.10 src/addritem.h;  cvs diff -u -r 1.14.2.24 -r 1.14.2.25 src/editaddress.c;  diff -u /dev/null src/editaddress_other_attributes_ldap.c;  diff -u /dev/null src/editaddress_other_attributes_ldap.h;  cvs diff -u -r 1.8.2.21 -r 1.8.2.22 src/editldap.c;  cvs diff -u -r 1.2.2.9 -r 1.2.2.10 src/ldapctrl.c;  cvs diff -u -r 1.1.4.10 -r 1.1.4.11 src/ldapctrl.h;  cvs diff -u -r 1.1.2.7 -r 1.1.2.8 src/ldapupdate.c;  ) > 2.10.0cvs12.patchset
index 6e6f1cb0abcadeb4f5557c4970c791ab9661a80f..2d529503b0d528f0e1ee73df2b95d476c95d2255 100644 (file)
@@ -11,7 +11,7 @@ MINOR_VERSION=10
 MICRO_VERSION=0
 INTERFACE_AGE=0
 BINARY_AGE=0
-EXTRA_VERSION=11
+EXTRA_VERSION=12
 EXTRA_RELEASE=
 EXTRA_GTK2_VERSION=
 
index 52b9e023b876f6ad5f3617f3451ac00d95aeba9c..f7ae184589b6d3ca3a8a3807d4896794ce2c87f9 100644 (file)
@@ -38,6 +38,7 @@ claws_mail_SOURCES = \
        customheader.c \
        displayheader.c \
        editaddress.c \
+       editaddress_other_attributes_ldap.c \
        editbook.c \
        editgroup.c \
        editjpilot.c \
@@ -182,6 +183,7 @@ claws_mailinclude_HEADERS = \
        customheader.h \
        displayheader.h \
        editaddress.h \
+       editaddress_other_attributes_ldap.h \
        editbook.h \
        editgroup.h \
        editjpilot.h \
index 8a1bbec90bc6c3c25b4e19be6375c76812046c11..81555cf1f47993a7e75d609c77e7e2d5843bca1d 100644 (file)
@@ -550,6 +550,47 @@ void addritem_person_add_attribute(
        person->listAttrib = g_list_append( person->listAttrib, attrib );
 }
 
+/**
+ * Remove user attribute from specified person.
+ * \param  person Person.
+ * \param  attrib Attribute to remove.
+ */
+void addritem_person_remove_attribute( ItemPerson *person, const gchar *attrib ) {
+       g_return_if_fail( person != NULL || attrib != NULL );
+       GList *attrib_list;
+
+       attrib_list = person->listAttrib;
+       while (attrib_list) {
+               UserAttribute *user_attrib = attrib_list->data;
+               if (strcmp(user_attrib->name, attrib) == 0) {
+                       person->listAttrib = g_list_remove(person->listAttrib, (gconstpointer) user_attrib);
+                       break;
+               }
+               attrib_list = g_list_next(attrib_list);
+       }
+}
+
+/**
+ * find out if a user attribute already exists in the specified person.
+ * \param  person Person.
+ * \param  attrib Attribute to remove.
+ * \return <i>TRUE</i> if attribute exists
+ */
+gboolean addritem_person_has_attribute(        ItemPerson *person, const gchar *attrib ) {
+       g_return_val_if_fail( person != NULL || attrib != NULL, FALSE );
+       GList *attrib_list = NULL;
+
+       attrib_list = person->listAttrib;
+       while (attrib_list) {
+               UserAttribute *user_attrib = attrib_list->data;
+               if (strcmp(user_attrib->name, attrib) == 0) {
+                       return TRUE;
+               }
+               attrib_list = g_list_next(attrib_list);
+       }
+       return FALSE;
+}
+
 /**
  * Create new address book group object.
  * \return Initialized group object.
index e5b8d231d75ceba502ca43c892f6db47a520bb32..2136e2e18b7ac89287ab7fd5b06c039bf6e7597f 100644 (file)
@@ -159,6 +159,8 @@ gboolean addritem_person_add_email          ( ItemPerson *person, ItemEMail *email );
 ItemEMail *addritem_person_remove_email                ( ItemPerson *person, ItemEMail *email );
 
 void addritem_person_add_attribute             ( ItemPerson *person, UserAttribute *attrib );
+void addritem_person_remove_attribute  ( ItemPerson *person, const gchar *attrib );
+gboolean addritem_person_has_attribute ( ItemPerson *person, const gchar *attrib );
 
 ItemFolder *addritem_create_item_folder        ( void );
 ItemFolder *addritem_copy_item_folder  ( ItemFolder *item );
index 2874f78c22812fdd0cfb37d7e5b9211d9f501d16..e5bac67e814cc802b082d5dd4428ce8fd2e54ffd 100644 (file)
 #include "gtkutils.h"
 #include "codeconv.h"
 #include "editaddress.h"
-
+#include "editaddress_other_attributes_ldap.h"
 #include "prefs_common.h"
 
-static struct _PersonEdit_dlg {
-       GtkWidget *container;
-       GtkWidget *notebook;
-       GtkWidget *ok_btn;
-       GtkWidget *cancel_btn;
-       GtkWidget *statusbar;   /* used when prefs_common.addressbook_use_editaddress_dialog is TRUE */
-       GtkWidget *title;       /* used when prefs_common.addressbook_use_editaddress_dialog is FALSE */
-       gint status_cid;
-
-       /* User data tab */
-       GtkWidget *entry_name;
-       GtkWidget *entry_first;
-       GtkWidget *entry_last;
-       GtkWidget *entry_nick;
-
-       /* EMail data tab */
-       GtkWidget *entry_email;
-       GtkWidget *entry_alias;
-       GtkWidget *entry_remarks;
-       GtkWidget *clist_email;
-       GtkWidget *email_up;
-       GtkWidget *email_down;
-       GtkWidget *email_del;
-       GtkWidget *email_mod;
-       GtkWidget *email_add;
-
-       /* Attribute data tab */
-       GtkWidget *entry_atname;
-       GtkWidget *entry_atvalue;
-       GtkWidget *clist_attrib;
-       GtkWidget *attrib_add;
-       GtkWidget *attrib_del;
-       GtkWidget *attrib_mod;
-
-       gint rowIndEMail;
-       gint rowIndAttrib;
-       gboolean editNew;
-       gboolean read_only;
-       gboolean ldap;
-} personeditdlg;
-
 /* transient data */
+static struct _PersonEdit_dlg personeditdlg;
 static AddressBookFile *current_abf = NULL;
 static ItemPerson *current_person = NULL;
 static ItemFolder *current_parent_folder = NULL;
@@ -226,8 +186,10 @@ static void edit_person_email_clear( gpointer data ) {
 }
 
 static void edit_person_attrib_clear( gpointer data ) {
-       gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atname), "" );
-       gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atvalue), "" );
+       if (!personeditdlg.ldap) {
+               gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atname), "" );
+               gtk_entry_set_text( GTK_ENTRY(personeditdlg.entry_atvalue), "" );
+       }
 }
 
 static void edit_person_switch_page( GtkNotebook *notebook, GtkNotebookPage *page,
@@ -1163,7 +1125,10 @@ static void addressbook_edit_person_create( GtkWidget *parent, gboolean *cancell
                addressbook_edit_person_widgetset_create( parent, cancelled );
        addressbook_edit_person_page_basic( PAGE_BASIC, _( "_User Data" ) );
        addressbook_edit_person_page_email( PAGE_EMAIL, _( "_Email Addresses" ) );
-       addressbook_edit_person_page_attrib( PAGE_ATTRIBUTES, _( "O_ther Attributes" ) );
+       if (personeditdlg.ldap)
+               addressbook_edit_person_page_attrib_ldap(&personeditdlg, PAGE_ATTRIBUTES, _("O_ther Attributes"));
+       else
+               addressbook_edit_person_page_attrib( PAGE_ATTRIBUTES, _( "O_ther Attributes" ) );
        gtk_widget_show_all( personeditdlg.container );
 }
 
@@ -1211,11 +1176,11 @@ static void update_sensitivity(void)
        gtk_widget_set_sensitive(personeditdlg.email_del,     !personeditdlg.read_only);
        gtk_widget_set_sensitive(personeditdlg.email_mod,     !personeditdlg.read_only);
        gtk_widget_set_sensitive(personeditdlg.email_add,     !personeditdlg.read_only);
-       gtk_widget_set_sensitive(personeditdlg.entry_atname,  !personeditdlg.read_only && !personeditdlg.ldap);
-       gtk_widget_set_sensitive(personeditdlg.entry_atvalue, !personeditdlg.read_only && !personeditdlg.ldap);
-       gtk_widget_set_sensitive(personeditdlg.attrib_add,    !personeditdlg.read_only && !personeditdlg.ldap);
-       gtk_widget_set_sensitive(personeditdlg.attrib_del,    !personeditdlg.read_only && !personeditdlg.ldap);
-       gtk_widget_set_sensitive(personeditdlg.attrib_mod,    !personeditdlg.read_only && !personeditdlg.ldap);
+       gtk_widget_set_sensitive(personeditdlg.entry_atname,  !personeditdlg.read_only);
+       gtk_widget_set_sensitive(personeditdlg.entry_atvalue, !personeditdlg.read_only);
+       gtk_widget_set_sensitive(personeditdlg.attrib_add,    !personeditdlg.read_only);
+       gtk_widget_set_sensitive(personeditdlg.attrib_del,    !personeditdlg.read_only);
+       gtk_widget_set_sensitive(personeditdlg.attrib_mod,    !personeditdlg.read_only);
 }
 
 static void addressbook_edit_person_flush_transient( void )
@@ -1388,6 +1353,7 @@ ItemPerson *addressbook_edit_person( AddressBookFile *abf, ItemFolder *parent_fo
                        gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_nick), person->nickName );
                edit_person_load_email( person );
                edit_person_load_attrib( person );
+               gtk_entry_set_text(GTK_ENTRY(personeditdlg.entry_atvalue), "");
        }
        else {
                personeditdlg.editNew = TRUE;
diff --git a/src/editaddress_other_attributes_ldap.c b/src/editaddress_other_attributes_ldap.c
new file mode 100644 (file)
index 0000000..fa645bd
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2007 Michael Rasmussen and the Claws Mail team
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#ifdef USE_LDAP
+
+#include "defs.h"
+
+#include "mgutils.h"
+#include "addressbook.h"
+#include "addressitem.h"
+#include "addritem.h"
+#include "addrbook.h"
+#include "manage_window.h"
+#include "gtkutils.h"
+#include "codeconv.h"
+#include "editaddress.h"
+#include "editaddress_other_attributes_ldap.h"
+#include "prefs_common.h"
+
+#define        ATTRIB_COL_NAME                 0
+#define        ATTRIB_COL_VALUE                1
+#define ATTRIB_N_COLS                  2
+#define EMAIL_N_COLS                   3
+#define ATTRIB_COL_WIDTH_NAME  120
+#define ATTRIB_COL_WIDTH_VALUE 180
+
+PersonEditDlg *personEditDlg;
+gboolean attrib_adding = FALSE, attrib_saving = FALSE;
+
+int get_attribute_index(const gchar *string_literal) {
+       int i = 0;
+       /*int count = sizeof(ATTRIBUTE) / sizeof(*ATTRIBUTE);*/
+       const gchar **attribute = ATTRIBUTE;
+
+       g_return_val_if_fail(string_literal != NULL, -1);
+       while (*attribute) {
+               debug_print("Comparing %s to %s\n", *attribute, string_literal);
+               if (strcmp(*attribute++, string_literal) == 0)
+                       return i;
+               i++;
+       }
+       return -1;
+}
+
+static void edit_person_status_show(gchar *msg) {
+       if (personEditDlg->statusbar != NULL) {
+               gtk_statusbar_pop(GTK_STATUSBAR(personEditDlg->statusbar), personEditDlg->status_cid);
+               if(msg) {
+                       gtk_statusbar_push(GTK_STATUSBAR(personEditDlg->statusbar), personEditDlg->status_cid, msg);
+               }
+       }
+}
+
+static void edit_person_attrib_clear(gpointer data) {
+       gtk_option_menu_set_history(GTK_OPTION_MENU(personEditDlg->entry_atname), 0);
+       gtk_entry_set_text(GTK_ENTRY(personEditDlg->entry_atvalue), "");
+}
+
+static gboolean list_find_attribute(const gchar *attr)
+{
+       GtkCList *clist = GTK_CLIST(personEditDlg->clist_attrib);
+       UserAttribute *attrib;
+       gint row = 0;
+       while((attrib = gtk_clist_get_row_data(clist, row))) {
+               if (!g_ascii_strcasecmp(attrib->name, attr)) {
+                       gtk_clist_select_row(clist, row, 0);
+                       return TRUE;
+               }
+               row++;
+       }
+       return FALSE;
+}
+
+/*
+* Comparison using cell contents (text in first column). Used for sort
+* address index widget.
+*/
+static gint edit_person_attrib_compare_func(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2) {
+       GtkCell *cell1 = ((GtkCListRow *)ptr1)->cell;
+       GtkCell *cell2 = ((GtkCListRow *)ptr2)->cell;
+       gchar *name1 = NULL, *name2 = NULL;
+
+       if (cell1) name1 = cell1->u.text;
+       if (cell2) name2 = cell2->u.text;
+       if (!name1) return (name2 != NULL);
+       if (!name2) return -1;
+       return g_utf8_collate(name1, name2);
+}
+
+static void edit_person_option_menu_changed(GtkOptionMenu *opt_menu, gpointer data) {
+       GtkCList *clist = GTK_CLIST(data);
+       gint row = personEditDlg->rowIndAttrib;
+       UserAttribute *attrib = gtk_clist_get_row_data(clist, row);
+       gint option = gtk_option_menu_get_history(opt_menu);
+
+       /* A corresponding attribute in contact does not match selected option */ 
+       if (strcmp(ATTRIBUTE[option], attrib->name) != 0) {
+               gtk_widget_set_sensitive(personEditDlg->attrib_add, TRUE);
+               gtk_widget_set_sensitive(personEditDlg->attrib_mod, TRUE);
+               gtk_widget_set_sensitive(personEditDlg->attrib_del, FALSE);
+               gtk_entry_set_text(GTK_ENTRY(personEditDlg->entry_atvalue), "");
+               gtk_widget_grab_focus(personEditDlg->entry_atvalue);
+               edit_person_status_show(NULL);
+       }
+}
+
+static void edit_person_attrib_list_selected(GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer data) {
+       UserAttribute *attrib = gtk_clist_get_row_data(clist, row);
+       if (attrib && !personEditDlg->read_only) {
+               int index = get_attribute_index(attrib->name);
+               /*fprintf(stderr, "Row: %d ->  %s = %s\n", index, attrib->name, attrib->value);*/
+               if (index == -1)
+                       index = 0;
+               gtk_option_menu_set_history(GTK_OPTION_MENU(personEditDlg->entry_atname), index);
+               gtk_entry_set_text( GTK_ENTRY(personEditDlg->entry_atvalue), attrib->value );
+               gtk_widget_set_sensitive(personEditDlg->attrib_del, TRUE);
+       }
+       else {
+               /*fprintf(stderr, "Row: %d -> empty attribute\n", row);*/
+               gtk_entry_set_text( GTK_ENTRY(personEditDlg->entry_atvalue), "");       
+               gtk_widget_set_sensitive(personEditDlg->attrib_del, FALSE);
+       }
+       personEditDlg->rowIndAttrib = row;
+       edit_person_status_show(NULL);
+}
+
+static void edit_person_attrib_delete(gpointer data) {
+       GtkCList *clist = GTK_CLIST(personEditDlg->clist_attrib);
+       gint row = personEditDlg->rowIndAttrib;
+       UserAttribute *attrib = gtk_clist_get_row_data(clist, row);
+       edit_person_attrib_clear(NULL);
+       if (attrib) {
+               /* Remove list entry */
+               gtk_clist_remove(clist, row);
+               addritem_free_attribute(attrib);
+               attrib = NULL;
+       }
+
+       /* Position hilite bar */
+       attrib = gtk_clist_get_row_data(clist, row);
+       if (!attrib) {
+               personEditDlg->rowIndAttrib = -1 + row;
+       } 
+       
+       if (!personEditDlg->read_only)
+               gtk_widget_set_sensitive(personEditDlg->attrib_del, gtk_clist_get_row_data(clist, 0) != NULL);
+       
+       edit_person_status_show(NULL);
+}
+
+static UserAttribute *edit_person_attrib_edit(gboolean *error, UserAttribute *attrib) {
+       UserAttribute *retVal = NULL;
+       gchar *sName, *sValue, *sName_, *sValue_;
+       gint index;
+
+       *error = TRUE;
+       index = gtk_option_menu_get_history(GTK_OPTION_MENU(personEditDlg->entry_atname));
+       sName_ = (gchar *) ATTRIBUTE[index];
+       sValue_ = gtk_editable_get_chars(GTK_EDITABLE(personEditDlg->entry_atvalue), 0, -1);
+       sName = mgu_email_check_empty(sName_);
+       sValue = mgu_email_check_empty(sValue_);
+       g_free(sValue_);
+
+       if (sName && sValue) {
+               if (attrib == NULL) {
+                       attrib = addritem_create_attribute();
+               }
+               addritem_attrib_set_name(attrib, sName);
+               addritem_attrib_set_value(attrib, sValue);
+               retVal = attrib;
+               *error = FALSE;
+       }
+       else {
+               edit_person_status_show(N_( "A Name and Value must be supplied." ));
+               gtk_widget_grab_focus(personEditDlg->entry_atvalue);            
+       }
+
+       g_free(sName);
+       g_free(sValue);
+
+       return retVal;
+}
+
+static void edit_person_attrib_modify(gpointer data) {
+       gboolean errFlg = FALSE;
+       GtkCList *clist = GTK_CLIST(personEditDlg->clist_attrib);
+       gint row = personEditDlg->rowIndAttrib;
+       UserAttribute *attrib = gtk_clist_get_row_data(clist, row);
+       if (attrib) {
+               edit_person_attrib_edit(&errFlg, attrib);
+               if (!errFlg) {
+                       gtk_clist_set_text(clist, row, ATTRIB_COL_NAME, attrib->name);
+                       gtk_clist_set_text(clist, row, ATTRIB_COL_VALUE, attrib->value);
+                       edit_person_attrib_clear(NULL);
+               }
+       }
+}
+
+static void edit_person_attrib_add(gpointer data) {
+       GtkCList *clist = GTK_CLIST(personEditDlg->clist_attrib);
+       gboolean errFlg = FALSE;
+       UserAttribute *attrib = NULL;
+       gint row = personEditDlg->rowIndAttrib;
+       if (gtk_clist_get_row_data(clist, row) == NULL) row = 0;
+
+       attrib = edit_person_attrib_edit(&errFlg, NULL);
+       if (!errFlg) {
+               gchar *text[EMAIL_N_COLS];
+               text[ATTRIB_COL_NAME] = attrib->name;
+               text[ATTRIB_COL_VALUE] = attrib->value;
+
+               row = gtk_clist_insert(clist, 1 + row, text);
+               gtk_clist_set_row_data(clist, row, attrib);
+               gtk_clist_select_row(clist, row, 0);
+               edit_person_attrib_clear(NULL);
+       }
+}
+
+static void edit_person_entry_att_changed (GtkWidget *entry, gpointer data)
+{
+       gboolean non_empty = gtk_clist_get_row_data(GTK_CLIST(personEditDlg->clist_attrib), 0) != NULL;
+       const gchar *sName;
+       int index;
+
+       if (personEditDlg->read_only)
+               return;
+
+       index = gtk_option_menu_get_history(GTK_OPTION_MENU(personEditDlg->entry_atname));
+       sName = ATTRIBUTE[index];
+       if (list_find_attribute(sName)) {
+               gtk_widget_set_sensitive(personEditDlg->attrib_add,FALSE);
+               gtk_widget_set_sensitive(personEditDlg->attrib_mod,non_empty);
+               attrib_adding = FALSE;
+               attrib_saving = non_empty;
+       } 
+       else {
+               gtk_widget_set_sensitive(personEditDlg->attrib_add,TRUE);
+               gtk_widget_set_sensitive(personEditDlg->attrib_mod,non_empty);
+               attrib_adding = TRUE;
+               attrib_saving = non_empty;
+       }
+}
+
+static gboolean edit_person_entry_att_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+       if (event && (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)) {
+               if (attrib_saving)
+                       edit_person_attrib_modify(NULL);
+               else if (attrib_adding)
+                       edit_person_attrib_add(NULL);
+       }
+       return FALSE;
+}
+
+void addressbook_edit_person_page_attrib_ldap(PersonEditDlg *dialog, gint pageNum, gchar *pageLbl) {
+       GtkWidget *option_menu;
+       GtkMenu *names_menu;
+
+       GtkWidget *vbox;
+       GtkWidget *hbox;
+       GtkWidget *vboxl;
+       GtkWidget *vboxb;
+       GtkWidget *vbuttonbox;
+       GtkWidget *buttonDel;
+       GtkWidget *buttonMod;
+       GtkWidget *buttonAdd;
+
+       GtkWidget *table;
+       GtkWidget *label;
+       GtkWidget *clist_swin;
+       GtkWidget *clist;
+       GtkWidget *entry_value;
+       gint top;
+
+       personEditDlg = dialog;
+
+       gchar *titles[ATTRIB_N_COLS];
+       gint i;
+
+       titles[ATTRIB_COL_NAME] = N_("Name");
+       titles[ATTRIB_COL_VALUE] = N_("Value");
+
+       vbox = gtk_vbox_new(FALSE, 8);
+       gtk_widget_show(vbox);
+       gtk_container_add(GTK_CONTAINER(personEditDlg->notebook), vbox);
+       gtk_container_set_border_width(GTK_CONTAINER(vbox), BORDER_WIDTH);
+
+       label = gtk_label_new_with_mnemonic(pageLbl);
+       gtk_widget_show(label);
+       gtk_notebook_set_tab_label(
+               GTK_NOTEBOOK(personEditDlg->notebook),
+               gtk_notebook_get_nth_page(GTK_NOTEBOOK(personEditDlg->notebook), pageNum), label);
+
+       /* Split into two areas */
+       hbox = gtk_hbox_new(FALSE, 0);
+       gtk_container_add(GTK_CONTAINER(vbox), hbox);
+
+       /* Attribute list */
+       vboxl = gtk_vbox_new(FALSE, 4);
+       gtk_container_add(GTK_CONTAINER(hbox), vboxl);
+       gtk_container_set_border_width(GTK_CONTAINER(vboxl), 4);
+
+       clist_swin = gtk_scrolled_window_new(NULL, NULL);
+       gtk_container_add(GTK_CONTAINER(vboxl), clist_swin);
+       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(clist_swin),
+                                      GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+       clist = gtk_clist_new_with_titles(ATTRIB_N_COLS, titles);
+       gtk_container_add(GTK_CONTAINER(clist_swin), clist);
+       gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_BROWSE);
+       gtk_clist_set_column_width(GTK_CLIST(clist), ATTRIB_COL_NAME, ATTRIB_COL_WIDTH_NAME);
+       gtk_clist_set_column_width(GTK_CLIST(clist), ATTRIB_COL_VALUE, ATTRIB_COL_WIDTH_VALUE);
+       gtk_clist_set_compare_func(GTK_CLIST(clist), edit_person_attrib_compare_func);
+       gtk_clist_set_auto_sort(GTK_CLIST(clist), TRUE);
+
+       for (i = 0; i < ATTRIB_N_COLS; i++)
+               GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button, GTK_CAN_FOCUS);
+
+       /* Data entry area */
+       table = gtk_table_new(4, 2, FALSE);
+       gtk_box_pack_start(GTK_BOX(vboxl), table, FALSE, FALSE, 0);
+       gtk_container_set_border_width(GTK_CONTAINER(table), 4);
+       gtk_table_set_row_spacings(GTK_TABLE(table), 4);
+       gtk_table_set_col_spacings(GTK_TABLE(table), 4);
+
+       /* First row */
+       top = 0;
+       label = gtk_label_new(N_("Name"));
+       gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+
+       names_menu = g_object_new(GTK_TYPE_MENU, NULL);
+       gchar **attribute = (gchar **) ATTRIBUTE;
+
+       while (*attribute) {
+               gtk_menu_shell_append(GTK_MENU_SHELL(names_menu),
+                       gtk_menu_item_new_with_label(*attribute++));
+       }
+       option_menu = GTK_WIDGET(g_object_new(GTK_TYPE_OPTION_MENU, "menu", names_menu, NULL));
+       gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), 0);
+
+       gtk_table_attach(GTK_TABLE(table), option_menu, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       /* Next row */
+       ++top;
+       label = gtk_label_new(N_("Value"));
+       gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+
+       entry_value = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), entry_value, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       /* Button box */
+       vboxb = gtk_vbox_new(FALSE, 4);
+       gtk_box_pack_start(GTK_BOX(hbox), vboxb, FALSE, FALSE, 2);
+
+       vbuttonbox = gtk_vbutton_box_new();
+       gtk_button_box_set_layout(GTK_BUTTON_BOX(vbuttonbox), GTK_BUTTONBOX_START);
+       gtk_box_set_spacing(GTK_BOX(vbuttonbox), 8);
+       gtk_container_set_border_width(GTK_CONTAINER(vbuttonbox), 4);
+       gtk_container_add(GTK_CONTAINER(vboxb), vbuttonbox);
+
+       /* Buttons */
+       buttonDel = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+       gtk_container_add(GTK_CONTAINER(vbuttonbox), buttonDel);
+
+       buttonMod = gtk_button_new_from_stock(GTK_STOCK_SAVE);
+       gtk_container_add(GTK_CONTAINER(vbuttonbox), buttonMod);
+
+       buttonAdd = gtk_button_new_from_stock(GTK_STOCK_ADD);
+       gtk_container_add(GTK_CONTAINER(vbuttonbox), buttonAdd);
+       
+       gtk_widget_set_sensitive(buttonDel,FALSE);
+       gtk_widget_set_sensitive(buttonMod,FALSE);
+       gtk_widget_set_sensitive(buttonAdd,FALSE);
+
+       gtk_widget_show_all(vbox);
+       
+       /* Event handlers */
+       g_signal_connect(G_OBJECT(clist), "select_row",
+                         G_CALLBACK( edit_person_attrib_list_selected), NULL);
+       g_signal_connect(G_OBJECT(buttonDel), "clicked",
+                         G_CALLBACK(edit_person_attrib_delete), NULL);
+       g_signal_connect(G_OBJECT(buttonMod), "clicked",
+                         G_CALLBACK(edit_person_attrib_modify), NULL);
+       g_signal_connect(G_OBJECT(buttonAdd), "clicked",
+                         G_CALLBACK(edit_person_attrib_add), NULL);
+       g_signal_connect(G_OBJECT(option_menu), "changed",
+                        G_CALLBACK(edit_person_entry_att_changed), NULL);
+       g_signal_connect(G_OBJECT(entry_value), "key_press_event",
+                        G_CALLBACK(edit_person_entry_att_pressed), NULL);
+       g_signal_connect(G_OBJECT(option_menu), "changed",
+                        G_CALLBACK(edit_person_option_menu_changed), clist);
+
+       personEditDlg->clist_attrib  = clist;
+       personEditDlg->entry_atname  = option_menu;
+       personEditDlg->entry_atvalue = entry_value;
+       personEditDlg->attrib_add = buttonAdd;
+       personEditDlg->attrib_del = buttonDel;
+       personEditDlg->attrib_mod = buttonMod;
+}
+
+#endif /* USE_LDAP */
+
+
diff --git a/src/editaddress_other_attributes_ldap.h b/src/editaddress_other_attributes_ldap.h
new file mode 100644 (file)
index 0000000..8d0becc
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2007 Michael Rasmussen and the Claws Mail team
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * Edit address item data.
+ */
+
+#ifndef __EDITADDRESS_OTHER_ATTRIBUTES_LDAP_H__
+#define __EDITADDRESS_OTHER_ATTRIBUTES_LDAP_H__
+
+#ifdef USE_LDAP
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtktable.h>
+
+typedef struct _PersonEdit_dlg PersonEditDlg;
+struct _PersonEdit_dlg {
+       GtkWidget *container;
+       GtkWidget *notebook;
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_btn;
+       GtkWidget *statusbar;   /* used when prefs_common.addressbook_use_editaddress_dialog is TRUE */
+       GtkWidget *title;       /* used when prefs_common.addressbook_use_editaddress_dialog is FALSE */
+       gint status_cid;
+
+       /* User data tab */
+       GtkWidget *entry_name;
+       GtkWidget *entry_first;
+       GtkWidget *entry_last;
+       GtkWidget *entry_nick;
+
+       /* EMail data tab */
+       GtkWidget *entry_email;
+       GtkWidget *entry_alias;
+       GtkWidget *entry_remarks;
+       GtkWidget *clist_email;
+       GtkWidget *email_up;
+       GtkWidget *email_down;
+       GtkWidget *email_del;
+       GtkWidget *email_mod;
+       GtkWidget *email_add;
+
+       /* Attribute data tab */
+       GtkWidget *entry_atname;
+       GtkWidget *entry_atvalue;
+       GtkWidget *clist_attrib;
+       GtkWidget *attrib_add;
+       GtkWidget *attrib_del;
+       GtkWidget *attrib_mod;
+
+       gint rowIndEMail;
+       gint rowIndAttrib;
+       gboolean editNew;
+       gboolean read_only;
+       gboolean ldap;
+};
+
+static const char *ATTRIBUTE[] = {
+       "telephoneNumber",
+       /*"description (Remarks)",*/
+       "title",
+       "telexNumber",
+       "facsimileTelephoneNumber",
+       "street",
+       "postOfficeBox",
+       "postalCode",
+       "postalAddress",
+       "st", /* state or province */
+       "departmentNumber",
+       "homePhone",
+       "homePostalAddress",
+       "initials",
+       "labeledURI",
+       "mobile",
+       "pager",
+       "roomNumber",
+       NULL
+};
+
+static const int ATTRIBUTE_SIZE = (sizeof(ATTRIBUTE) / sizeof(*ATTRIBUTE)) - 1;
+
+/* Function proto types */
+void addressbook_edit_person_page_attrib_ldap(PersonEditDlg *personEditDlg, gint pageNum, gchar *pageLbl);
+int get_attribute_index(const gchar *string_literal);
+
+#endif /* USE_LDAP */
+
+#endif /* __EDITADDRESS_OTHER_ATTRIBUTES_LDAP_H__ */
index dea34a11190eace53a34c3730b7230aefddb9ece..e12713054d56bcfce98cb8daa59d5a0147524be8 100644 (file)
@@ -311,8 +311,8 @@ static void edit_ldap_basedn_select( void ) {
        g_free( sPass );
 }
 
-static void edit_ldap_search_reset( void ) {
-       gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_criteria), LDAPCTL_DFL_ATTR_LIST );
+static void edit_ldap_search_reset() {
+       gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_criteria), ldapctl_get_default_criteria());
 }
 
 static void addressbook_edit_ldap_dialog_create( gboolean *cancelled ) {
@@ -591,6 +591,7 @@ static void addressbook_edit_ldap_page_search( gint pageNum, gchar *pageLbl ) {
        gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 
        entry_criteria = gtk_entry_new();
+       gtk_editable_set_editable(GTK_EDITABLE(entry_criteria), FALSE);
        gtk_table_attach(GTK_TABLE(table), entry_criteria, 1, 2, top, (top + 1),
                GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
 
@@ -878,7 +879,7 @@ static void editldap_parse_criteria( gchar *criteria, LdapControl *ctl ) {
 /**
  * Clear entry fields to reasonable defaults (for a new server entry).
  */
-static void edit_ldap_clear_fields( void ) {
+static void edit_ldap_clear_fields() {
        gtk_entry_set_text(
                GTK_ENTRY(ldapedit.entry_name), ADDRESSBOOK_GUESS_LDAP_NAME );
        gtk_entry_set_text(
@@ -893,7 +894,7 @@ static void edit_ldap_clear_fields( void ) {
        gtk_spin_button_set_value(
                GTK_SPIN_BUTTON( ldapedit.spinbtn_maxentry ), LDAPCTL_DFL_TIMEOUT );
        gtk_entry_set_text(
-               GTK_ENTRY(ldapedit.entry_criteria), LDAPCTL_DFL_ATTR_LIST );
+               GTK_ENTRY(ldapedit.entry_criteria), ldapctl_get_default_criteria());
        gtk_spin_button_set_value(
                GTK_SPIN_BUTTON(ldapedit.spinbtn_queryage), LDAPCTL_DFL_QUERY_AGE );
        gtk_toggle_button_set_active(
@@ -999,7 +1000,7 @@ AdapterDSource *addressbook_edit_ldap(
                        GTK_WINDOW(ldapedit.window), _("Edit LDAP Server"));
        }
        else {
-               edit_ldap_clear_fields();
+               edit_ldap_clear_fields(NULL);
                gtk_window_set_title(
                        GTK_WINDOW(ldapedit.window), _("Add New LDAP Server"));
        }
index ed4e1aff1b93d4116b019087c8703623ed324c67..d08a8fabdd403db06f079793dc58a25f520a74b8 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "ldapctrl.h"
 #include "mgutils.h"
+#include "editaddress_other_attributes_ldap.h"
 
 /**
  * Create new LDAP control block object.
@@ -553,14 +554,26 @@ gchar *ldapctl_format_criteria( LdapControl *ctl, const gchar *searchVal ) {
  */
 char **ldapctl_attribute_array( LdapControl *ctl ) {
        char **ptrArray;
-       GList *node;
+       GList *node, *def;
        gint cnt, i;
        g_return_val_if_fail( ctl != NULL, NULL );
 
+       def = ldapctl_get_default_criteria_list();
+       /* check if this servers config is updated to the new
+        * default list of search criteria. If not update the list */
+       if (! ldapctl_compare_list(ctl->listCriteria, def)) {
+               /* Deep copy search criteria */
+               ldapctl_criteria_list_clear(ctl);
+               while(def) {
+                       ctl->listCriteria = g_list_append(
+                               ctl->listCriteria, g_strdup(def->data));
+                       def = g_list_next(def);
+               }
+       }
+       node = ctl->listCriteria;
        cnt = g_list_length( ctl->listCriteria );
        ptrArray = g_new0( char *, 1 + cnt );
        i = 0;
-       node = ctl->listCriteria;
        while( node ) {
                ptrArray[ i++ ] = node->data;
                node = g_list_next( node );
@@ -627,6 +640,71 @@ void ldapctl_parse_ldap_search( LdapControl *ctl, gchar *criteria ) {
        }
 }
 
+/**
+ * Return the default LDAP search criteria string.
+ * \return Formatted string or <i>""</i>. Should be g_free() when done.
+ */
+gchar *ldapctl_get_default_criteria() {
+       gchar *retVal = LDAPCTL_DFL_ATTR_LIST;
+       const gchar **attrs = ATTRIBUTE; 
+
+       while (*attrs) {
+               retVal = g_strdup_printf("%s, %s", retVal, *attrs++);
+       }
+       return retVal;
+}
+
+/**
+ * Return the default LDAP search criteria list.
+ * \return GList or <i>NULL</i>.
+ */
+GList *ldapctl_get_default_criteria_list() {
+       gchar *criteria, *item;
+       gchar **c_list, **w_list;
+       GList *attr_list = NULL;
+
+       criteria = ldapctl_get_default_criteria();
+       c_list = g_strsplit(criteria, " ", 0);
+       g_free(criteria);
+       criteria = NULL;
+       w_list = c_list;
+       while ((criteria = *w_list++) != 0) {
+               /* copy string elimination <,> */
+               if (*w_list)
+                       item = g_strndup(criteria, strlen(criteria) - 1);
+               else
+                       item = g_strdup(criteria);
+               attr_list = g_list_append(attr_list, g_strdup(item));
+               g_free(item);
+       }
+       g_strfreev(c_list);
+       return attr_list;
+}
+
+/**
+ * Compare to GList for equality.
+ * \param l1 First GList
+ * \param l2 Second GList
+ * \Return TRUE or FALSE
+ */
+gboolean ldapctl_compare_list(GList *l1, GList *l2) {
+       gchar *first, *second;
+       if (! l1 && ! l2)
+               return TRUE;
+       if ((! l1 && l2) || (l1 && ! l2))
+               return FALSE;
+       while (l1 && l2) {
+               first = (gchar *) l1->data;
+               second = (gchar *) l2->data;
+               if ( ! (first && second) || strcmp(first, second) != 0) {
+                       return FALSE;
+               }
+               l1 = g_list_next(l1);
+               l2 = g_list_next(l2);
+       }
+       return TRUE;
+}
+
 #endif /* USE_LDAP */
 
 /*
index 2f515b6f4f19118773885f0c027cfc0f4655d8f3..0b21e4afe37d0c06122bbf2c0b137d52900a1e23 100644 (file)
@@ -47,7 +47,7 @@
 #define LDAPCTL_ATTR_PHONE     "telephoneNumber"
 #define LDAPCTL_ATTR_DISPLAYNAME "displayName"
 
-#define LDAPCTL_DFL_ATTR_LIST   "mail, cn, givenName, sn, displayName, telephoneNumber"
+#define LDAPCTL_DFL_ATTR_LIST   "mail, cn, givenName, sn, displayName"
 
 /*
  * Search matching options.
@@ -105,6 +105,9 @@ gchar *ldapctl_format_criteria      ( LdapControl *ctl, const gchar *searchVal );
 char **ldapctl_attribute_array ( LdapControl *ctl );
 void ldapctl_free_attribute_array( char **ptrArray );
 void ldapctl_parse_ldap_search ( LdapControl *ctl, gchar *criteria );
+gchar *ldapctl_get_default_criteria(void);
+GList *ldapctl_get_default_criteria_list();
+gboolean ldapctl_compare_list(GList *l1, GList *l2);
 
 #endif /* USE_LDAP */
 
index eca943eb2722759a76641ef22e1ce24a24f7e7d8..f3a85d4c8d4f94ad45fd5b79fd75b93cd2419a28 100644 (file)
@@ -56,6 +56,7 @@
 #include "ldaputil.h"
 #include "utils.h"
 #include "adbookbase.h"
+#include "editaddress_other_attributes_ldap.h"
 
 /**
  * Structure to hold user defined attributes
@@ -125,6 +126,23 @@ AttrKeyValue *attrkeyvalue_create() {
        return buf;
 }
 
+/**
+ * Free created AttrKeyValue structure
+ * \param akv AttrKeyValue structure to free
+ */
+void attrkeyvalue_free(AttrKeyValue *akv) {
+       if (akv->key) {
+               g_free(akv->key);
+               akv->key = NULL;
+       }
+       if (akv->value) {
+               g_free(akv->value);
+               akv->value = NULL;
+       }
+       g_free(akv);
+       akv = NULL;
+}
+
 /**
  * Retrieve E-Mail address object for update.
  * \param item   ItemEmail to update.
@@ -186,17 +204,15 @@ gboolean ldapsvr_retrieve_item_person(ItemPerson *person, GHashTable *array) {
                node = g_list_next(node);
        }
        g_hash_table_insert(array, "mail", attr);
-/* Not implemented in this release.
        node = person->listAttrib;
        attr = NULL;
        while (node) {
-               AttrKeyValue *newAttr = ldapsvr_retrieve_attribute(node->data)
+               AttrKeyValue *newAttr = ldapsvr_retrieve_attribute(node->data);
                if (newAttr)
                        attr = g_list_append(attr, newAttr);
                node = g_list_next(node);
        }
        g_hash_table_insert(array, "attribute", attr);
-*/
        return TRUE;
 }
 
@@ -259,11 +275,14 @@ void ldapsvr_print_contacts_hashtable(gpointer key, gpointer data, gpointer fd)
  * \param list List of GHashTable
  */
 void ldapsvr_free_hashtable(GList *list) {
-       while (list) {
-               g_hash_table_destroy(list->data);
-               list = g_list_next(list);
+       GList *tmp = list;
+       while (tmp) {
+               g_hash_table_destroy(tmp->data);
+               tmp->data = NULL;
+               tmp = g_list_next(tmp);
        }
        g_list_free(list);
+       list = NULL;
 }
 
 /**
@@ -370,6 +389,27 @@ Rdn *rdn_create() {
        return buf;
 }
 
+/**
+ * Free a created Rdn structure
+ * \param rdn Structure to free
+ */
+void rdn_free(Rdn *rdn) {
+       if (rdn->attribute) {
+               g_free(rdn->attribute);
+               rdn->attribute = NULL;
+       }
+       if (rdn->value) {
+               g_free(rdn->value);
+               rdn->value = NULL;
+       }
+       if (rdn->new_dn) {
+               g_free(rdn->new_dn);
+               rdn->new_dn = NULL;
+       }
+       g_free(rdn);
+       rdn = NULL;
+}
+
 /**
  * update Rdn structure
  *
@@ -435,6 +475,7 @@ Rdn *ldapsvr_modify_dn(GHashTable *hash, gchar *dn) {
                else {
                        /* We cannot remove dn */
                        g_free(compare);
+                       rdn_free(rdn);
                        return NULL;
                }
        }
@@ -447,9 +488,11 @@ Rdn *ldapsvr_modify_dn(GHashTable *hash, gchar *dn) {
                }
                else {
                        /* We cannot remove dn */
+                       rdn_free(rdn);
                        return NULL;
                }
        }
+       rdn_free(rdn);
        return NULL;
 }
 
@@ -513,8 +556,6 @@ void clean_up(LDAP *ld, LdapServer *server, GHashTable *contact) {
        }
        if (ld)
                ldapsvr_disconnect(ld);
-/*     ldapsvr_force_refresh(server);
-       ldapsvr_free_all_query(server);*/
 }
 
 /**
@@ -533,12 +574,16 @@ AttrKeyValue *get_cn(gchar *dn) {
        
        cn = attrkeyvalue_create();
        start = g_strstr_len(dn, strlen(dn), "cn");
-       if (start == NULL)
+       if (start == NULL) {
+               attrkeyvalue_free(cn);
                return NULL;
+       }
        end = g_strstr_len(start, strlen(start), ",");
        item = g_strndup(start, end - start);
-       if (item == NULL)
+       if (item == NULL) {
+               attrkeyvalue_free(cn);
                return NULL;
+       }
        key_value = g_strsplit(item, "=", 2);
        cn->key = g_strdup(key_value[0]);
        cn->value = g_strdup(key_value[1]);
@@ -547,6 +592,40 @@ AttrKeyValue *get_cn(gchar *dn) {
        return cn;
 }
 
+/**
+ * Get mail attribute from dn
+ *
+ * \param dn Distinguesh Name for current object
+ * \return AttrKeyValue, or <i>NULL</i> if none created
+ */
+AttrKeyValue *get_mail(gchar *dn) {
+       AttrKeyValue *mail;
+       gchar *start;
+       gchar *end;
+       gchar *item;
+       gchar **key_value;
+       g_return_val_if_fail(dn != NULL, NULL);
+       
+       mail = attrkeyvalue_create();
+       start = g_strstr_len(dn, strlen(dn), "mail");
+       if (start == NULL) {
+               attrkeyvalue_free(mail);
+               return NULL;
+       }
+       end = g_strstr_len(start, strlen(start), ",");
+       item = g_strndup(start, end - start);
+       if (item == NULL) {
+               attrkeyvalue_free(mail);
+               return NULL;
+       }
+       key_value = g_strsplit(item, "=", 2);
+       mail->key = g_strdup(key_value[0]);
+       mail->value = g_strdup(key_value[1]);
+       g_strfreev(key_value);
+       g_free(item);
+       return mail;
+}
+
 /**
  * Get ou or o attribute from dn
  *
@@ -565,13 +644,17 @@ AttrKeyValue *get_ou(gchar *dn) {
        start = g_strstr_len(dn, strlen(dn), ",o=");
        if (start == NULL)
                start = g_strstr_len(dn, strlen(dn), ",ou=");
-       if (start == NULL)
+       if (start == NULL) {
+               attrkeyvalue_free(ou);
                return NULL;
+       }
        start++;
        end = g_strstr_len(start, strlen(start), ",");
        item = g_strndup(start, end - start);
-       if (item == NULL)
+       if (item == NULL) {
+               attrkeyvalue_free(ou);
                return NULL;
+       }
        key_value = g_strsplit(item, "=", 2);
        ou->key = g_strdup(key_value[0]);
        ou->value = g_strdup(key_value[1]);
@@ -623,9 +706,280 @@ void ldapsvr_compare_attr(LDAP *ld, gchar *dn, gint cnt, LDAPMod *mods[]) {
        
        g_return_if_fail(ld != NULL || dn != NULL || cnt >= 0 || mods != NULL);
        for (i = 0; i < cnt; i++) {
-               rc = ldap_compare_s(ld, dn, mods[i]->mod_type, mods[i]->mod_vals.modv_strvals[0]);
+               gchar *value = g_strdup(mods[i]->mod_vals.modv_strvals[0]);
+               if (!value || strcmp(value, "") == 0)
+                       value = g_strdup("thisisonlyadummy");
+               rc = ldap_compare_s(ld, dn, mods[i]->mod_type, value);
                fprintf(stderr, "ldap_compare for (%s:%s)\" failed[0x%x]: %s\n",
-               mods[i]->mod_type, mods[i]->mod_vals.modv_strvals[0], rc, ldap_err2string(rc));
+               mods[i]->mod_type, value, rc, ldap_err2string(rc));
+               g_free(value);
+       }
+}
+
+/**
+ * compare attribute to LDAP in case of LDAP_INAPPROPRIATE_MATCHING
+ *
+ * \param ld AddressBook resource
+ * \param server Reference to server
+ * \param dn dn for the entry
+ * \param attr Attribute
+ * \param value New value
+ * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
+ */
+int ldapsvr_compare_manual_attr(LDAP *ld, LdapServer *server, gchar *dn, char *attr, char *value) {
+       LDAPMessage *res, *e = NULL;
+       BerElement *ber;
+       struct berval **vals;
+       int rc;
+       LdapControl *ctl;
+       gchar *filter;
+       gchar *attribute;
+       int retVal, i;
+       AttrKeyValue *mail;
+
+       g_return_val_if_fail(ld != NULL || server != NULL || attr != NULL, -1);
+       ctl = server->control;
+       mail = get_mail(dn);
+       if (! mail)
+               return -2;
+       filter = g_strdup_printf("(&(mail=%s)(%s=*))", mail->value, attr);
+       attrkeyvalue_free(mail);
+       if (ctl) {
+               rc = ldap_search_s(ld, ctl->baseDN, /*LDAP_SCOPE_SUBTREE*/LDAP_SCOPE_ONELEVEL, filter, NULL, 0, &res);
+               if (rc) {
+                       fprintf(stderr, "ldap_search for attr=%s\" failed[0x%x]: %s\n",attr, rc, ldap_err2string(rc));
+                       retVal = -2;
+               }
+               else {
+                       e = ldap_first_entry(ld, res);
+                       /* entry has this attribute */
+                       if (e) {
+                               attribute = ldap_first_attribute( ld, e, &ber );
+                               if (attribute) {
+                                       if (value) {
+                                               if( ( vals = ldap_get_values_len( ld, e, attr ) ) != NULL ) {
+                                                       for( i = 0; vals[i] != NULL; i++ ) {
+                                                               debug_print("Compare: %s=%s\n", attr, vals[i]->bv_val);
+                                                               /* attribute has same value */
+                                                               if (strcmp(vals[i]->bv_val, value) == 0)
+                                                                       retVal = -1;
+                                                               /* attribute has new value */
+                                                               else
+                                                                       retVal = LDAP_MOD_REPLACE;
+                                                       }
+                                               }
+                                               ldap_value_free_len(vals);
+                                       }
+                                       else
+                                               retVal = LDAP_MOD_DELETE;
+                               }
+                               if( ber != NULL ) {
+                                       ber_free( ber, 0 );
+                               }
+                               ldap_memfree(attribute);
+                       }
+                       /* entry does not have this attribute */
+                       else {
+                               /* Only add if this is a real attribute */
+                               if (value)
+                                       retVal = LDAP_MOD_ADD;
+                               /* This is dummy value used to avoid ldap_compare error */
+                               else
+                                       retVal = -1;
+                       }
+               }
+       }
+       else
+               retVal = -2;
+       g_free(filter);
+       return retVal;
+}
+
+/**
+ * Deside which kind of operation is required to handle
+ * updating the specified attribute
+ *
+ * \param ld AddressBook resource
+ * \param server Reference to server
+ * \param dn dn for the entry
+ * \param attr Attribute
+ * \param value New value
+ * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
+ */
+int ldapsvr_deside_operation(LDAP *ld, LdapServer *server, char *dn, char *attr, char *value) {
+       int rc;
+       gboolean dummy = FALSE;
+
+       g_return_val_if_fail(ld != NULL || server != NULL || dn != NULL || attr != NULL, -1);
+       if (value == NULL)
+               return -1;
+       /* value containing empty string cause invalid syntax. A bug in
+        * the LDAP library? Therefore we add a dummy value
+        */
+       if (strcmp(value,"") == 0) {
+               value = g_strdup("thisisonlyadummy");
+               dummy = TRUE;
+       }
+       rc = ldap_compare_s(ld, dn, attr, value);
+       debug_print("ldap_compare for (%s:%s)\" error_code[0x%x]: %s\n",
+               attr, value, rc, ldap_err2string(rc));
+       switch (rc) {
+               case LDAP_COMPARE_FALSE: 
+                       if (dummy)
+                               return LDAP_MOD_DELETE;
+                       else
+                               return LDAP_MOD_REPLACE;
+               case LDAP_COMPARE_TRUE: return -1;
+               case LDAP_NO_SUCH_ATTRIBUTE: return LDAP_MOD_ADD;
+               /* LDAP_INAPPROPRIATE_MATCHING needs extensive testing because I
+                * am not aware off the condition causing this return value!
+                */
+               case LDAP_INAPPROPRIATE_MATCHING:
+                       if (dummy)
+                               value = NULL;
+                       return ldapsvr_compare_manual_attr(ld, server, dn, attr, value);
+               case LDAP_UNDEFINED_TYPE: return -2;
+               case LDAP_INVALID_SYNTAX: return -2;
+               default: return -2;
+       }
+}
+
+/**
+ * Check if attribute is part of the current search criteria
+ *
+ * \param list Array containing attributes in the current search criteria
+ * \param attr Attribute to check
+ * \result <i>TRUE</i> if attribute is found in the current search criteria
+ */
+gboolean ldapsvr_check_search_attributes(char **list, char *attr) {
+       while (*list) {
+               if (strcmp(*list++, attr) == 0)
+                       return TRUE;
+       }
+       return FALSE;
+}
+
+/**
+ * Deside which other attributes needs updating
+ *
+ * \param ld LDAP resource
+ * \param server AddressBook resource
+ * \param dn dn for the entry
+ * \param contact GHashTable with information for the current contact
+ */
+void ldapsvr_handle_other_attributes(LDAP *ld, LdapServer *server, char *dn, GHashTable *contact) {
+       GList *node;
+       gboolean CHECKED_ATTRIBUTE[ATTRIBUTE_SIZE];
+       LDAPMod *mods[ATTRIBUTE_SIZE + 1];
+       LDAPMod modarr[ATTRIBUTE_SIZE];
+       gint cnt = 0;
+       char *attr[] = {NULL, NULL};
+       int mod_op, rc, i;
+
+       g_return_if_fail(server != NULL || dn != NULL || contact != NULL);
+       for (i = 0; i < ATTRIBUTE_SIZE; i++)
+               CHECKED_ATTRIBUTE[i] = FALSE;
+       node = g_hash_table_lookup(contact , "attribute");
+       while (node) {
+               AttrKeyValue *item = node->data;
+               if (item) {
+                       int index = get_attribute_index(item->key);
+                       if (index >= 0) {
+                               debug_print("Found other attribute: %s = %s\n", item->key, item->value);
+                               mod_op = ldapsvr_deside_operation(ld, server, dn, item->key, item->value);
+                               /* Only consider attributes which we no how to handle.
+                                * Set to TRUE in CHECKED_ATTRIBUTE array to indicate no further action
+                                */
+                               if (mod_op < 0) {
+                                       CHECKED_ATTRIBUTE[index] = TRUE;
+                                       node = g_list_next(node);
+                                       continue;
+                               }
+                               if (mod_op == LDAP_MOD_DELETE) {
+                                       /* Setting param to NULL instructs OpenLDAP to remove any
+                                       * value stored for this attribute and remove the attribute
+                                       * completely. Should multiple instances of an attribute be
+                                       * allowed in the future param is required to have the value
+                                       * store for the attribute which is going to be deleted
+                                       */
+                                       item->value = NULL;
+                               }
+                               if (mod_op == LDAP_MOD_REPLACE && strcmp(item->value, "") == 0) {
+                                       /* Having an empty string is considered a syntax error in
+                                       * ldap. E.g attributes with empty strings are not allowed
+                                       * in which case we treate this as a request for deleting
+                                       * the attribute.
+                                       */
+                                       mod_op = LDAP_MOD_DELETE;
+                                       item->value = NULL;
+                               }
+                               if (mod_op == LDAP_MOD_ADD && strcmp(item->value, "") == 0) {
+                                       /* Adding an empty string is considered a syntax error in
+                                       * ldap. E.g attributes with empty strings are not allowed
+                                       * in which case we silently refuse to add this entry
+                                       */
+                               }
+                               else {
+                                       SETMOD(mods[cnt], modarr[cnt], mod_op, g_strdup(item->key), attr, g_strdup(item->value));
+                                       cnt++;
+                                       CHECKED_ATTRIBUTE[index] = TRUE;
+                               }
+                       }
+               }
+               node = g_list_next(node);
+       }
+       char **attribs = ldapctl_attribute_array(server->control);
+       for (i = 0; i < ATTRIBUTE_SIZE; i++) {
+               /* Attributes which holds no information are to be removed */
+               if (CHECKED_ATTRIBUTE[i] == FALSE) {
+                       /* Only consider those attributes which is currently part of the search criteria.
+                        * If attributes are not part of the search criteria they would seem to hold
+                        * no information since their values will not be populated in the GUI
+                        */
+                       if (ldapsvr_check_search_attributes(attribs, (char *) ATTRIBUTE[i])) {
+                               mod_op = ldapsvr_deside_operation(ld, server, dn, (char *) ATTRIBUTE[i], "");
+                               if (mod_op == LDAP_MOD_DELETE) {
+                                       SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_DELETE, g_strdup((char *) ATTRIBUTE[i]), attr, NULL);
+                                       cnt++;
+                               }
+                       }
+               }
+       }
+       ldapctl_free_attribute_array(attribs);
+       mods[cnt] = NULL;
+       if (debug_get_mode())
+               ldapsvr_print_ldapmod(mods);
+       server->retVal = LDAPRC_SUCCESS;
+       rc = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
+       if (rc) {
+               switch (rc) {
+                       case LDAP_ALREADY_EXISTS: 
+                               server->retVal = LDAPRC_ALREADY_EXIST;
+                               break;
+                       default:
+                               fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",dn, rc, ldap_err2string(rc));
+                               if (rc == 0x8)
+                                       server->retVal = LDAPRC_STRONG_AUTH;
+                               else
+                                       server->retVal = LDAPRC_NAMING_VIOLATION;
+               }
+       }
+       else {
+               /* Only consider those attributes which is currently part of the search criteria.
+                * If attributes are not part of the search criteria they would seem to hold
+                * no information since their values will not be populated in the GUI
+                */
+               char **attribs = ldapctl_attribute_array(server->control);
+               for (i = 0; i < ATTRIBUTE_SIZE; i++) {
+                       if (ldapsvr_check_search_attributes(attribs, (char *) ATTRIBUTE[i])) {
+                               if (CHECKED_ATTRIBUTE[i] == FALSE) {
+                                       AddrItemObject *aio = addrcache_get_object(server->addressCache, g_hash_table_lookup(contact , "uid"));
+                                       ItemPerson *person = (ItemPerson *) aio;
+                                       addritem_person_remove_attribute(person, (const gchar *) ATTRIBUTE[i]);
+                               }
+                       }
+               }
+               ldapctl_free_attribute_array(attribs);
        }
 }
 
@@ -682,8 +1036,9 @@ void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
        cnt++;
        ou = get_ou(base_dn);
        if (ou != NULL) {
-               SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, ou->key, org, ou->value);
+               SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, g_strdup(ou->key), org, g_strdup(ou->value));
                cnt++;
+               attrkeyvalue_free(ou);
        }
        
        commonName = get_cn(base_dn);
@@ -699,11 +1054,12 @@ void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
                }
        }
        else {
-               SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, commonName->key, cn, commonName->value);
+               SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, g_strdup(commonName->key), cn, g_strdup(commonName->value));
                cnt++;
                param = g_hash_table_lookup(contact , "cn");
                SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "displayName", displayName, param);
                g_hash_table_insert(contact, "displayName", param);
+               attrkeyvalue_free(commonName);
        }
        cnt++;
        param = g_hash_table_lookup(contact , "givenName");
@@ -749,51 +1105,11 @@ void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
                                        server->retVal = LDAPRC_NAMING_VIOLATION;
                }
        }
+       ldapsvr_handle_other_attributes(ld, server, base_dn, contact);
        g_free(base_dn);
        clean_up(ld, server, contact);
 }
 
-/**
- * Deside which kind of operation is required to handle
- * updating the specified attribute
- *
- * \param ld AddressBook resource
- * \param dn dn for the entry
- * \param attr Attribute
- * \param value New value
- * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
- */
-int ldapsvr_deside_operation(LDAP *ld, char *dn, char *attr, char *value) {
-       int rc;
-       gboolean dummy = FALSE;
-
-       g_return_val_if_fail(ld != NULL || dn != NULL || attr != NULL, -1);
-       if (value == NULL)
-               return -1;
-       /* value containing empty string cause invalid syntax. A bug in
-        * the LDAP library? Therefore we add a dummy value
-        */
-       if (strcmp(value,"") == 0) {
-               value = g_strdup("thisisonlyadummy");
-               dummy = TRUE;
-       }
-       rc = ldap_compare_s(ld, dn, attr, value);
-       debug_print("ldap_compare for (%s:%s)\" error_code[0x%x]: %s\n",
-               attr, value, rc, ldap_err2string(rc));
-       switch (rc) {
-               case LDAP_COMPARE_FALSE: 
-                       if (dummy)
-                               return LDAP_MOD_DELETE;
-                       else
-                               return LDAP_MOD_REPLACE;
-               case LDAP_COMPARE_TRUE: return -1;
-               case LDAP_NO_SUCH_ATTRIBUTE: return LDAP_MOD_ADD;
-               case LDAP_UNDEFINED_TYPE: return -2;
-               case LDAP_INVALID_SYNTAX: return -2;
-               default: return -2;
-       }
-}
-
 /**
  * Update contact to LDAP
  *
@@ -860,7 +1176,7 @@ void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
                return;
        }
        param = g_hash_table_lookup(contact , "cn");
-       mod_op = ldapsvr_deside_operation(ld, dn, "displayName", param);
+       mod_op = ldapsvr_deside_operation(ld, server, dn, "displayName", param);
        if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("cn", NoRemove->attribute) != 0)) {
                if (mod_op == LDAP_MOD_DELETE) {
                        /* Setting param to NULL instructs OpenLDAP to remove any
@@ -893,7 +1209,7 @@ void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
                }
        }
        param = g_hash_table_lookup(contact , "givenName");
-       mod_op = ldapsvr_deside_operation(ld, dn, "givenName", param);
+       mod_op = ldapsvr_deside_operation(ld, server, dn, "givenName", param);
        if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("givenName", NoRemove->attribute) != 0)) {
                if (mod_op == LDAP_MOD_DELETE) {
                        /* Setting param to NULL instructs OpenLDAP to remove any
@@ -952,7 +1268,7 @@ void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
                 */
        }
        param = g_hash_table_lookup(contact , "sn");
-       mod_op = ldapsvr_deside_operation(ld, dn, "sn", param);
+       mod_op = ldapsvr_deside_operation(ld, server, dn, "sn", param);
        if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("sn", NoRemove->attribute) != 0)) {
                if (mod_op == LDAP_MOD_DELETE) {
                        /* Setting param to NULL instructs OpenLDAP to remove any
@@ -985,7 +1301,7 @@ void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
        }
        debug_print("newDN: %s\n", dn);
        if (NoRemove)
-               g_free(NoRemove);
+               rdn_free(NoRemove);
        server->retVal = LDAPRC_SUCCESS;
        if (cnt > 0) {
                int rc;
@@ -999,6 +1315,7 @@ void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
                if (mail)
                        g_free(mail);
        }
+       ldapsvr_handle_other_attributes(ld, server, dn, contact);
        /* If we do not make changes persistent at this point then changes
         * will be lost if the user makes new search on the same server since
         * changes are only present in Claws' internal cache. This issue has to