Included address book patch for VCard, JPilot, LDAP.
authorMatch Grun <match@dimensional.com>
Mon, 2 Jul 2001 22:29:47 +0000 (22:29 +0000)
committerMatch Grun <match@dimensional.com>
Mon, 2 Jul 2001 22:29:47 +0000 (22:29 +0000)
17 files changed:
src/addressitem.h [new file with mode: 0644]
src/editjpilot.c [new file with mode: 0644]
src/editjpilot.h [new file with mode: 0644]
src/editldap.c [new file with mode: 0644]
src/editldap.h [new file with mode: 0644]
src/editldap_basedn.c [new file with mode: 0644]
src/editldap_basedn.h [new file with mode: 0644]
src/editvcard.c [new file with mode: 0644]
src/editvcard.h [new file with mode: 0644]
src/jpilot.c [new file with mode: 0644]
src/jpilot.h [new file with mode: 0644]
src/mgutils.c [new file with mode: 0644]
src/mgutils.h [new file with mode: 0644]
src/syldap.c [new file with mode: 0644]
src/syldap.h [new file with mode: 0644]
src/vcard.c [new file with mode: 0644]
src/vcard.h [new file with mode: 0644]

diff --git a/src/addressitem.h b/src/addressitem.h
new file mode 100644 (file)
index 0000000..0886a9d
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999,2000 Hiroyuki Yamamoto
+ *
+ * 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.
+ */
+
+/*
+ * Address item data.
+ */
+
+#ifndef __ADDRESSITEM_H__
+#define __ADDRESSITEM_H__
+
+#define ADDRESS_OBJECT(obj)            ((AddressObject *)obj)
+#define ADDRESS_OBJECT_TYPE(obj)       (ADDRESS_OBJECT(obj)->type)
+#define ADDRESS_ITEM(obj)              ((AddressItem *)obj)
+
+#define ADDRESS_ITEM_CAT_UNKNOWN -1
+
+typedef struct _AddressObject  AddressObject;
+typedef struct _AddressItem    AddressItem;
+
+typedef enum
+{
+       ADDR_ITEM,
+       ADDR_GROUP,
+       ADDR_FOLDER,
+       ADDR_VCARD,
+       ADDR_JPILOT,
+       ADDR_CATEGORY,
+       ADDR_LDAP
+} AddressObjectType;
+
+struct _AddressObject
+{
+       AddressObjectType type;
+};
+
+struct _AddressItem
+{
+       AddressObject obj;
+
+       gchar *name;
+       gchar *address;
+       gchar *remarks;
+       gchar *externalID;
+       gint  categoryID;
+};
+
+#endif /* __ADDRESSITEM_H__ */
diff --git a/src/editjpilot.c b/src/editjpilot.c
new file mode 100644 (file)
index 0000000..d98a4cc
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Edit JPilot address book data.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#ifdef USE_JPILOT
+
+#include "defs.h"
+
+#include <glib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtkhbbox.h>
+#include <gtk/gtkbutton.h>
+
+#include "intl.h"
+#include "addressbook.h"
+#include "prefs_common.h"
+#include "addressitem.h"
+#include "jpilot.h"
+
+#define ADDRESSBOOK_GUESS_JPILOT "MyJPilot"
+#define JPILOT_NUM_CUSTOM_LABEL        4
+
+static struct _JPilotEdit {
+       GtkWidget *window;
+       GtkWidget *name_entry;
+       GtkWidget *file_entry;
+       GtkWidget *custom_check[JPILOT_NUM_CUSTOM_LABEL];
+       GtkWidget *custom_label[JPILOT_NUM_CUSTOM_LABEL];
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_btn;
+       GtkWidget *statusbar;
+       gint status_cid;
+} jpilotedit;
+
+static struct _AddressFileSelection jpilot_file_selector;
+
+/*
+* Edit functions.
+*/
+void edit_jpilot_status_show( gchar *msg ) {
+       if( jpilotedit.statusbar != NULL ) {
+               gtk_statusbar_pop( GTK_STATUSBAR(jpilotedit.statusbar), jpilotedit.status_cid );
+               if( msg ) {
+                       gtk_statusbar_push( GTK_STATUSBAR(jpilotedit.statusbar), jpilotedit.status_cid, msg );
+               }
+       }
+}
+
+static gint edit_jpilot_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled ) {
+       *cancelled = TRUE;
+       gtk_main_quit();
+       return TRUE;
+}
+
+static void edit_jpilot_key_pressed( GtkWidget *widget, GdkEventKey *event, gboolean *cancelled ) {
+       if (event && event->keyval == GDK_Escape) {
+               *cancelled = TRUE;
+               gtk_main_quit();
+       }
+}
+
+static void edit_jpilot_ok( GtkWidget *widget, gboolean *cancelled ) {
+       *cancelled = FALSE;
+       gtk_main_quit();
+}
+
+static void edit_jpilot_cancel( GtkWidget *widget, gboolean *cancelled ) {
+       *cancelled = TRUE;
+       gtk_main_quit();
+}
+
+static void edit_jpilot_fill_check_box( JPilotFile *jpf ) {
+       gint i;
+       GList *node, *customLbl = NULL;
+       gchar *labelName;
+       gboolean done, checked;
+       for( i = 0; i < JPILOT_NUM_CUSTOM_LABEL; i++ ) {
+               gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( jpilotedit.custom_check[i] ), FALSE );
+               gtk_label_set_text( GTK_LABEL( jpilotedit.custom_label[i] ), "" );
+       }
+
+       done = FALSE;
+       i = 0;
+       customLbl = jpilot_load_custom_label( jpf, customLbl );
+       node = customLbl;
+       while( ! done ) {
+               if( node ) {
+                       labelName = node->data;
+                       gtk_label_set_text( GTK_LABEL( jpilotedit.custom_label[i] ), labelName );
+                       checked = jpilot_test_custom_label( jpf, labelName );
+                       gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( jpilotedit.custom_check[i] ), checked );
+                       i++;
+                       if( i >= JPILOT_NUM_CUSTOM_LABEL ) done = TRUE;
+                       node = g_list_next( node );
+               }
+               else {
+                       done = TRUE;
+               }
+       }
+       mgu_free_dlist( customLbl );
+       customLbl = NULL;
+}
+
+static void edit_jpilot_fill_check_box_new() {
+       gint i;
+       for( i = 0; i < JPILOT_NUM_CUSTOM_LABEL; i++ ) {
+               gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( jpilotedit.custom_check[i] ), FALSE );
+               gtk_label_set_text( GTK_LABEL( jpilotedit.custom_label[i] ), "" );
+       }
+}
+
+static void edit_jpilot_read_check_box( JPilotFile *pilotFile ) {
+       gint i;
+       gchar *labelName;
+       jpilot_clear_custom_labels( pilotFile );
+       for( i = 0; i < JPILOT_NUM_CUSTOM_LABEL; i++ ) {
+               if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(jpilotedit.custom_check[i]) ) ) {
+                       labelName = g_strdup( GTK_LABEL(jpilotedit.custom_label[i])->label );
+                       jpilot_add_custom_label( pilotFile, labelName );
+               }
+       }
+}
+
+static void edit_jpilot_file_check( void ) {
+       gint t;
+       gchar *sFile;
+       gchar *sMsg;
+       gboolean flg;
+
+       flg = FALSE;
+       sFile = gtk_editable_get_chars( GTK_EDITABLE(jpilotedit.file_entry), 0, -1 );
+       if( sFile ) {
+               g_strchomp( sFile ); g_strchug( sFile );
+               if( *sFile != '\0' ) {
+                       // Attempt to read file
+                       JPilotFile *jpf = jpilot_create_path( sFile );
+                       t = jpilot_read_data( jpf );
+                       if( t == MGU_SUCCESS ) {
+                               // Set check boxes
+                               edit_jpilot_fill_check_box( jpf );
+                               flg = TRUE;
+                       }
+                       jpilot_free( jpf );
+               }
+       }
+       if( ! flg ) {
+               // Clear all check boxes
+               edit_jpilot_fill_check_box_new();
+       }
+       g_free( sFile );
+
+       // Display appropriate message
+       if( t == MGU_SUCCESS ) {
+               sMsg = "";
+       }
+       else if( t == MGU_BAD_FORMAT || t == MGU_OO_MEMORY ) {
+               sMsg = _("File does not appear to be JPilot format.");
+       }
+       else {
+               sMsg = _("Could not read file.");
+       }
+       edit_jpilot_status_show( sMsg );
+}
+
+static void edit_jpilot_file_ok( GtkWidget *widget, gpointer data ) {
+       gchar *sFile;
+       AddressFileSelection *afs;
+       GtkWidget *fileSel;
+
+       afs = ( AddressFileSelection * ) data;
+       fileSel = afs->fileSelector;
+       sFile = gtk_file_selection_get_filename( GTK_FILE_SELECTION(fileSel) );
+
+       afs->cancelled = FALSE;
+       gtk_entry_set_text( GTK_ENTRY(jpilotedit.file_entry), sFile );
+       gtk_widget_hide( afs->fileSelector );
+       gtk_grab_remove( afs->fileSelector );
+       edit_jpilot_file_check();
+       gtk_widget_grab_focus( jpilotedit.file_entry );
+}
+
+static void edit_jpilot_file_cancel( GtkWidget *widget, gpointer data ) {
+       AddressFileSelection *afs = ( AddressFileSelection * ) data;
+       afs->cancelled = TRUE;
+       gtk_widget_hide( afs->fileSelector );
+       gtk_grab_remove( afs->fileSelector );
+       gtk_widget_grab_focus( jpilotedit.file_entry );
+}
+
+static void edit_jpilot_file_select_create( AddressFileSelection *afs ) {
+       GtkWidget *fileSelector;
+
+       fileSelector = gtk_file_selection_new( _("Select JPilot File") );
+       gtk_file_selection_hide_fileop_buttons( GTK_FILE_SELECTION(fileSelector) );
+       gtk_signal_connect( GTK_OBJECT (GTK_FILE_SELECTION(fileSelector)->ok_button),
+                             "clicked", GTK_SIGNAL_FUNC (edit_jpilot_file_ok), ( gpointer ) afs );
+       gtk_signal_connect( GTK_OBJECT (GTK_FILE_SELECTION(fileSelector)->cancel_button),
+                             "clicked", GTK_SIGNAL_FUNC (edit_jpilot_file_cancel), ( gpointer ) afs );
+       afs->fileSelector = fileSelector;
+       afs->cancelled = TRUE;
+}
+
+static void edit_jpilot_file_select( void ) {
+       gchar *sFile;
+
+       if (! jpilot_file_selector.fileSelector )
+               edit_jpilot_file_select_create( & jpilot_file_selector );
+
+       sFile = gtk_editable_get_chars( GTK_EDITABLE(jpilotedit.file_entry), 0, -1 );
+       gtk_file_selection_set_filename( GTK_FILE_SELECTION( jpilot_file_selector.fileSelector ), sFile );
+       g_free( sFile );
+       gtk_widget_show( jpilot_file_selector.fileSelector );
+       gtk_grab_add( jpilot_file_selector.fileSelector );
+}
+
+static void addressbook_edit_jpilot_create( gboolean *cancelled ) {
+       GtkWidget *window;
+       GtkWidget *vbox;
+       GtkWidget *table;
+       GtkWidget *label;
+       GtkWidget *name_entry;
+       GtkWidget *file_entry;
+       GtkWidget *vbox_custom;
+       GtkWidget *frame_custom;
+       GtkWidget *custom_check[JPILOT_NUM_CUSTOM_LABEL];
+       GtkWidget *custom_label[JPILOT_NUM_CUSTOM_LABEL];
+       GtkWidget *hlbox;
+       GtkWidget *hbbox;
+       GtkWidget *hsep;
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_btn;
+       GtkWidget *check_btn;
+       GtkWidget *file_btn;
+       GtkWidget *hsbox;
+       GtkWidget *statusbar;
+       gint top, i;
+
+       window = gtk_window_new(GTK_WINDOW_DIALOG);
+       gtk_widget_set_usize(window, 450, -1);
+       gtk_container_set_border_width(GTK_CONTAINER(window), 0);
+       gtk_window_set_title(GTK_WINDOW(window), _("Edit JPilot Entry"));
+       gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+       gtk_window_set_modal(GTK_WINDOW(window), TRUE); 
+       gtk_signal_connect(GTK_OBJECT(window), "delete_event",
+                          GTK_SIGNAL_FUNC(edit_jpilot_delete_event),
+                          cancelled);
+       gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
+                          GTK_SIGNAL_FUNC(edit_jpilot_key_pressed),
+                          cancelled);
+
+       vbox = gtk_vbox_new(FALSE, 8);
+       gtk_container_add(GTK_CONTAINER(window), vbox);
+       gtk_container_set_border_width( GTK_CONTAINER(vbox), 0 );
+
+       table = gtk_table_new(2 + JPILOT_NUM_CUSTOM_LABEL, 3, FALSE);
+       gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+       gtk_container_set_border_width( GTK_CONTAINER(table), 8 );
+       gtk_table_set_row_spacings(GTK_TABLE(table), 8);
+       gtk_table_set_col_spacings(GTK_TABLE(table), 8);
+
+       // First row
+       top = 0;
+       label = gtk_label_new(_("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);
+
+       name_entry = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), name_entry, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       check_btn = gtk_button_new_with_label( _(" Check File "));
+       gtk_table_attach(GTK_TABLE(table), check_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0);
+
+       // Second row
+       top = 1;
+       label = gtk_label_new(_("File"));
+       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);
+
+       file_entry = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), file_entry, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       file_btn = gtk_button_new_with_label( _(" ... "));
+       gtk_table_attach(GTK_TABLE(table), file_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0);
+
+       // Third row
+       top = 2;
+       frame_custom = gtk_frame_new(_("Additional e-Mail address item(s)"));
+       gtk_table_attach(GTK_TABLE(table), frame_custom, 1, 2, top, (top + JPILOT_NUM_CUSTOM_LABEL), GTK_FILL, 0, 0, 0);
+
+       // Now do custom labels.
+       vbox_custom = gtk_vbox_new (FALSE, 8);
+       for( i = 0; i < JPILOT_NUM_CUSTOM_LABEL; i++ ) {
+               hlbox = gtk_hbox_new( FALSE, 0 );
+               custom_check[i] = gtk_check_button_new();
+               custom_label[i] = gtk_label_new( "" );
+               gtk_box_pack_start( GTK_BOX(hlbox), custom_check[i], FALSE, FALSE, 0 );
+               gtk_box_pack_start( GTK_BOX(hlbox), custom_label[i], TRUE, TRUE, 0 );
+               gtk_box_pack_start( GTK_BOX(vbox_custom), hlbox, TRUE, TRUE, 0 );
+               gtk_misc_set_alignment(GTK_MISC(custom_label[i]), 0, 0.5);
+               top++;
+       }
+       gtk_container_add (GTK_CONTAINER (frame_custom), vbox_custom);
+       gtk_container_set_border_width( GTK_CONTAINER(vbox_custom), 8 );
+
+       // Status line
+       hsbox = gtk_hbox_new(FALSE, 0);
+       gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
+       statusbar = gtk_statusbar_new();
+       gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
+
+       // Button panel
+       gtkut_button_set_create(&hbbox, &ok_btn, _("OK"),
+                               &cancel_btn, _("Cancel"), NULL, NULL);
+       gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
+       gtk_container_set_border_width( GTK_CONTAINER(hbbox), 0 );
+       gtk_widget_grab_default(ok_btn);
+
+       hsep = gtk_hseparator_new();
+       gtk_box_pack_end(GTK_BOX(vbox), hsep, FALSE, FALSE, 0);
+
+       gtk_signal_connect(GTK_OBJECT(ok_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_jpilot_ok), cancelled);
+       gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_jpilot_cancel), cancelled);
+       gtk_signal_connect(GTK_OBJECT(file_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_jpilot_file_select), NULL);
+       gtk_signal_connect(GTK_OBJECT(check_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_jpilot_file_check), NULL);
+
+       gtk_widget_show_all(vbox);
+
+       jpilotedit.window     = window;
+       jpilotedit.name_entry = name_entry;
+       jpilotedit.file_entry = file_entry;
+       jpilotedit.ok_btn     = ok_btn;
+       jpilotedit.cancel_btn = cancel_btn;
+       jpilotedit.statusbar  = statusbar;
+       jpilotedit.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Edit JPilot Dialog" );
+       for( i = 0; i < JPILOT_NUM_CUSTOM_LABEL; i++ ) {
+               jpilotedit.custom_check[i] = custom_check[i];
+               jpilotedit.custom_label[i] = custom_label[i];
+       }
+}
+
+AddressJPilot *addressbook_edit_jpilot( AddressJPilot *jpilot ) {
+       static gboolean cancelled;
+       gchar *sName;
+       gchar *sFile;
+       JPilotFile *jpf;
+       gboolean fin;
+
+       if (!jpilotedit.window)
+               addressbook_edit_jpilot_create(&cancelled);
+       gtk_widget_grab_focus(jpilotedit.ok_btn);
+       gtk_widget_grab_focus(jpilotedit.name_entry);
+       gtk_widget_show(jpilotedit.window);
+       manage_window_set_transient(GTK_WINDOW(jpilotedit.window));
+
+       edit_jpilot_status_show( "" );
+       if( jpilot ) {
+               jpf = jpilot->pilotFile;
+               if (jpf->name)
+                       gtk_entry_set_text(GTK_ENTRY(jpilotedit.name_entry), jpf->name);
+               if (jpf->path)
+                       gtk_entry_set_text(GTK_ENTRY(jpilotedit.file_entry), jpf->path);
+               gtk_window_set_title( GTK_WINDOW(jpilotedit.window), _("Edit JPilot Entry"));
+               edit_jpilot_fill_check_box( jpf );
+       }
+       else {
+               gchar *guessFile = jpilot_find_pilotdb();
+               gtk_entry_set_text(GTK_ENTRY(jpilotedit.name_entry), ADDRESSBOOK_GUESS_JPILOT );
+               gtk_entry_set_text(GTK_ENTRY(jpilotedit.file_entry), guessFile );
+               gtk_window_set_title( GTK_WINDOW(jpilotedit.window), _("Add New JPilot Entry"));
+               edit_jpilot_fill_check_box_new();
+               // Attempt to load labels
+               if( *guessFile != '\0' ) {
+                       edit_jpilot_file_check();
+               }
+       }
+
+       gtk_main();
+       gtk_widget_hide(jpilotedit.window);
+       if (cancelled == TRUE) return NULL;
+
+       fin = FALSE;
+       sName = gtk_editable_get_chars( GTK_EDITABLE(jpilotedit.name_entry), 0, -1 );
+       sFile = gtk_editable_get_chars( GTK_EDITABLE(jpilotedit.file_entry), 0, -1 );
+       if( *sName == '\0' ) fin = TRUE;
+       if( *sFile == '\0' ) fin = TRUE;
+
+       if( ! fin ) {
+               if( ! jpilot ) {
+                       jpilot = g_new0(AddressJPilot, 1);
+                       ADDRESS_OBJECT_TYPE(jpilot) = ADDR_JPILOT;
+                       jpf = jpilot_create();
+                       jpilot->pilotFile = jpf;
+               }
+               g_free( jpilot->name );
+               jpilot->name = g_strdup( sName );
+               jpilot_set_name( jpf, sName );
+               jpilot_set_file( jpf, sFile );
+               edit_jpilot_read_check_box( jpf );
+       }
+       g_free( sName );
+       g_free( sFile );
+
+       return jpilot;
+}
+
+#endif /* USE_JPILOT */
+
+/*
+* End of Source.
+*/
+
diff --git a/src/editjpilot.h b/src/editjpilot.h
new file mode 100644 (file)
index 0000000..795d8c9
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Edit JPilot address book data.
+ */
+
+#ifndef __EDITJPILOT_H__
+#define __EDITJPILOT_H__
+
+#ifdef USE_JPILOT
+
+// Function prototypes
+AddressJPilot *addressbook_edit_jpilot( AddressJPilot *jpilot );
+
+#endif /* USE_JPILOT */
+
+#endif /* __EDITJPILOT_H__ */
+
+/*
+* End of Source.
+*/
+
diff --git a/src/editldap.c b/src/editldap.c
new file mode 100644 (file)
index 0000000..010a45d
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Edit VCard address book data.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#ifdef USE_LDAP
+
+#include "defs.h"
+
+#include <glib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtkbutton.h>
+
+#include "intl.h"
+#include "addressbook.h"
+#include "prefs_common.h"
+#include "menu.h"
+#include "addressitem.h"
+#include "syldap.h"
+#include "editldap_basedn.h"
+
+#define ADDRESSBOOK_GUESS_LDAP_NAME    "MyServer"
+#define ADDRESSBOOK_GUESS_LDAP_SERVER  "localhost"
+
+#define LDAPEDIT_TABLE_ROWS    6
+#define LDAPEDIT_TABLE_COLS    3
+
+static struct _LDAPEdit {
+       GtkWidget *window;
+       GtkWidget *notebook;
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_btn;
+       GtkWidget *statusbar;
+       gint status_cid;
+       GtkWidget *entry_name;
+       GtkWidget *entry_server;
+       GtkWidget *spinbtn_port;
+       GtkWidget *entry_baseDN;
+       GtkWidget *spinbtn_timeout;
+       GtkWidget *entry_bindDN;
+       GtkWidget *entry_bindPW;
+       GtkWidget *entry_criteria;
+       GtkWidget *spinbtn_maxentry;
+} ldapedit;
+
+/*
+* Edit functions.
+*/
+void edit_ldap_status_show( gchar *msg ) {
+       if( ldapedit.statusbar != NULL ) {
+               gtk_statusbar_pop( GTK_STATUSBAR(ldapedit.statusbar), ldapedit.status_cid );
+               if( msg ) {
+                       gtk_statusbar_push( GTK_STATUSBAR(ldapedit.statusbar), ldapedit.status_cid, msg );
+               }
+       }
+}
+
+static void edit_ldap_ok( GtkWidget *widget, gboolean *cancelled ) {
+       *cancelled = FALSE;
+       gtk_main_quit();
+}
+
+static void edit_ldap_cancel( GtkWidget *widget, gboolean *cancelled ) {
+       *cancelled = TRUE;
+       gtk_main_quit();
+}
+
+static gint edit_ldap_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled ) {
+       *cancelled = TRUE;
+       gtk_main_quit();
+       return TRUE;
+}
+
+static void edit_ldap_key_pressed( GtkWidget *widget, GdkEventKey *event, gboolean *cancelled ) {
+       if (event && event->keyval == GDK_Escape) {
+               *cancelled = TRUE;
+               gtk_main_quit();
+       }
+}
+
+static void edit_ldap_switch_page( GtkWidget *widget ) {
+       edit_ldap_status_show( "" );
+}
+
+static void edit_ldap_server_check( void ) {
+       gchar *sHost, *sBind, *sPass;
+       gint iPort, iTime;
+       gchar *sMsg;
+       gchar *sBaseDN = NULL;
+       gint iBaseDN = 0;
+       gboolean flg;
+
+       edit_ldap_status_show( "" );
+       flg = FALSE;
+       sHost = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_server), 0, -1 );
+       sBind = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_bindDN), 0, -1 );
+       sPass = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_bindPW), 0, -1 );
+       iPort = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_port ) );
+       iTime = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_timeout ) );
+       g_strchomp( sHost ); g_strchug( sHost );
+       g_strchomp( sBind ); g_strchug( sBind );
+       g_strchomp( sPass ); g_strchug( sPass );
+       if( *sHost != '\0' ) {
+               // Test connection to server
+               if( syldap_test_connect_s( sHost, iPort ) ) {
+                       // Attempt to read base DN
+                       GList *baseDN = syldap_read_basedn_s( sHost, iPort, sBind, sPass, iTime );
+                       if( baseDN ) {
+                               GList *node = baseDN;
+                               while( node ) {
+                                       ++iBaseDN;
+                                       if( ! sBaseDN ) {
+                                               sBaseDN = g_strdup( node->data );
+                                       }
+                                       node = g_list_next( node );
+                               }
+                               mgu_free_dlist( baseDN );
+                               baseDN = node = NULL;
+                       }
+                       flg = TRUE;
+               }
+       }
+       g_free( sHost );
+       g_free( sBind );
+       g_free( sPass );
+
+       if( sBaseDN ) {
+               // Load search DN
+               gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_baseDN), sBaseDN);
+               g_free( sBaseDN );
+       }
+
+       // Display appropriate message
+       if( flg ) {
+               sMsg = _( "Connected successfully to server" );
+       }
+       else {
+               sMsg = _( "Could not connect to server" );
+       }
+       edit_ldap_status_show( sMsg );
+}
+
+static void edit_ldap_basedn_select( void ) {
+       gchar *sHost, *sBind, *sPass, *sBase;
+       gint iPort, iTime;
+       gchar *selectDN;
+
+       sHost = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_server), 0, -1 );
+       sBase = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_baseDN), 0, -1 );
+       sBind = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_bindDN), 0, -1 );
+       sPass = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_bindPW), 0, -1 );
+       iPort = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_port ) );
+       iTime = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_timeout ) );
+       g_strchomp( sHost ); g_strchug( sHost );
+       g_strchomp( sBind ); g_strchug( sBind );
+       g_strchomp( sPass ); g_strchug( sPass );
+       selectDN = edit_ldap_basedn_selection( sHost, iPort, sBase, iTime, sBind, sPass );
+       if( selectDN ) {
+               gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_baseDN), selectDN);
+               g_free( selectDN );
+               selectDN = NULL;
+       }
+       g_free( sHost );
+       g_free( sBase );
+       g_free( sBind );
+       g_free( sPass );
+}
+
+static void edit_ldap_search_reset( void ) {
+       gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_criteria), SYLDAP_DFL_CRITERIA );
+}
+
+static void addressbook_edit_ldap_dialog_create( gboolean *cancelled ) {
+       GtkWidget *window;
+       GtkWidget *vbox;
+       GtkWidget *notebook;
+       GtkWidget *hbbox;
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_btn;
+       GtkWidget *hsbox;
+       GtkWidget *statusbar;
+
+       window = gtk_window_new(GTK_WINDOW_DIALOG);
+       gtk_widget_set_usize(window, 450, -1);
+       gtk_container_set_border_width(GTK_CONTAINER(window), 0);
+       gtk_window_set_title(GTK_WINDOW(window), _("Edit LDAP Server"));
+       gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+       gtk_window_set_modal(GTK_WINDOW(window), TRUE); 
+       gtk_signal_connect(GTK_OBJECT(window), "delete_event",
+                          GTK_SIGNAL_FUNC(edit_ldap_delete_event),
+                          cancelled);
+       gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
+                          GTK_SIGNAL_FUNC(edit_ldap_key_pressed),
+                          cancelled);
+
+       vbox = gtk_vbox_new( FALSE, 6 );
+       gtk_container_set_border_width(GTK_CONTAINER(vbox), BORDER_WIDTH);
+       gtk_widget_show( vbox );
+       gtk_container_add( GTK_CONTAINER( window ), vbox );
+
+       // Notebook
+       notebook = gtk_notebook_new();
+       gtk_widget_show( notebook );
+       gtk_box_pack_start( GTK_BOX( vbox ), notebook, TRUE, TRUE, 0 );
+       gtk_container_set_border_width( GTK_CONTAINER( notebook ), 6 );
+
+       // Status line
+       hsbox = gtk_hbox_new(FALSE, 0);
+       gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
+       statusbar = gtk_statusbar_new();
+       gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
+
+       // Button panel
+       gtkut_button_set_create(&hbbox, &ok_btn, _("OK"),
+                               &cancel_btn, _("Cancel"), NULL, NULL);
+       gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
+       gtk_widget_grab_default(ok_btn);
+
+       gtk_signal_connect(GTK_OBJECT(ok_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_ldap_ok), cancelled);
+       gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_ldap_cancel), cancelled);
+       gtk_signal_connect(GTK_OBJECT(notebook), "switch_page",
+                          GTK_SIGNAL_FUNC(edit_ldap_switch_page), NULL );
+
+       gtk_widget_show_all(vbox);
+
+       ldapedit.window     = window;
+       ldapedit.notebook   = notebook;
+       ldapedit.ok_btn     = ok_btn;
+       ldapedit.cancel_btn = cancel_btn;
+       ldapedit.statusbar  = statusbar;
+       ldapedit.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Edit LDAP Server Dialog" );
+}
+
+void addressbook_edit_ldap_page_basic( gint pageNum, gchar *pageLbl ) {
+       GtkWidget *vbox;
+       GtkWidget *table;
+       GtkWidget *label;
+       GtkWidget *entry_name;
+       GtkWidget *entry_server;
+       GtkWidget *hbox_spin;
+       GtkObject *spinbtn_port_adj;
+       GtkWidget *spinbtn_port;
+       GtkWidget *entry_baseDN;
+       GtkWidget *check_btn;
+       GtkWidget *lookdn_btn;
+       gint top;
+
+       vbox = gtk_vbox_new( FALSE, 8 );
+       gtk_widget_show( vbox );
+       gtk_container_add( GTK_CONTAINER( ldapedit.notebook ), vbox );
+       gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH );
+
+       label = gtk_label_new( pageLbl );
+       gtk_widget_show( label );
+       gtk_notebook_set_tab_label(
+               GTK_NOTEBOOK( ldapedit.notebook ),
+               gtk_notebook_get_nth_page( GTK_NOTEBOOK( ldapedit.notebook ), pageNum ), label );
+
+       table = gtk_table_new( LDAPEDIT_TABLE_ROWS, LDAPEDIT_TABLE_COLS, FALSE);
+       gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+       gtk_container_set_border_width( GTK_CONTAINER(table), 8 );
+       gtk_table_set_row_spacings(GTK_TABLE(table), 8);
+       gtk_table_set_col_spacings(GTK_TABLE(table), 8);
+
+       // First row
+       top = 0;
+       label = gtk_label_new(_("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);
+
+       entry_name = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), entry_name, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       // Next row
+       ++top;
+       label = gtk_label_new(_("Hostname"));
+       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_server = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), entry_server, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       // Next row
+       ++top;
+       label = gtk_label_new(_("Port"));
+       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);
+
+       hbox_spin = gtk_hbox_new (FALSE, 8);
+       spinbtn_port_adj = gtk_adjustment_new (389, 1, 65535, 100, 1000, 1000);
+       spinbtn_port = gtk_spin_button_new(GTK_ADJUSTMENT (spinbtn_port_adj), 1, 0);
+       gtk_box_pack_start (GTK_BOX (hbox_spin), spinbtn_port, FALSE, FALSE, 0);
+       gtk_widget_set_usize (spinbtn_port, 64, -1);
+       gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_port), TRUE);
+       gtk_table_attach(GTK_TABLE(table), hbox_spin, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       check_btn = gtk_button_new_with_label( _(" Check Server "));
+       gtk_table_attach(GTK_TABLE(table), check_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0);
+
+       // Next row
+       ++top;
+       label = gtk_label_new(_("Search Base"));
+       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_baseDN = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), entry_baseDN, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       lookdn_btn = gtk_button_new_with_label( _(" ... "));
+       gtk_table_attach(GTK_TABLE(table), lookdn_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0);
+
+       gtk_signal_connect(GTK_OBJECT(check_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_ldap_server_check), NULL);
+       gtk_signal_connect(GTK_OBJECT(lookdn_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_ldap_basedn_select), NULL);
+
+       gtk_widget_show_all(vbox);
+
+       ldapedit.entry_name   = entry_name;
+       ldapedit.entry_server = entry_server;
+       ldapedit.spinbtn_port = spinbtn_port;
+       ldapedit.entry_baseDN = entry_baseDN;
+}
+
+void addressbook_edit_ldap_page_extended( gint pageNum, gchar *pageLbl ) {
+       GtkWidget *vbox;
+       GtkWidget *table;
+       GtkWidget *label;
+       GtkWidget *entry_bindDN;
+       GtkWidget *entry_bindPW;
+       GtkWidget *entry_criteria;
+       GtkWidget *hbox_spin;
+       GtkObject *spinbtn_timeout_adj;
+       GtkWidget *spinbtn_timeout;
+       GtkObject *spinbtn_maxentry_adj;
+       GtkWidget *spinbtn_maxentry;
+       GtkWidget *reset_btn;
+       gint top;
+
+       vbox = gtk_vbox_new( FALSE, 8 );
+       gtk_widget_show( vbox );
+       gtk_container_add( GTK_CONTAINER( ldapedit.notebook ), vbox );
+       gtk_container_set_border_width( GTK_CONTAINER (vbox), BORDER_WIDTH );
+
+       label = gtk_label_new( pageLbl );
+       gtk_widget_show( label );
+       gtk_notebook_set_tab_label(
+               GTK_NOTEBOOK( ldapedit.notebook ),
+               gtk_notebook_get_nth_page( GTK_NOTEBOOK( ldapedit.notebook ), pageNum ), label );
+
+       table = gtk_table_new( LDAPEDIT_TABLE_ROWS, LDAPEDIT_TABLE_COLS, FALSE);
+       gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+       gtk_container_set_border_width( GTK_CONTAINER(table), 8 );
+       gtk_table_set_row_spacings(GTK_TABLE(table), 8);
+       gtk_table_set_col_spacings(GTK_TABLE(table), 8);
+
+       // First row
+       top = 0;
+       label = gtk_label_new(_("Search Criteria"));
+       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_criteria = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), entry_criteria, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       reset_btn = gtk_button_new_with_label( _(" Reset "));
+       gtk_table_attach(GTK_TABLE(table), reset_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0);
+
+       // Next row
+       ++top;
+       label = gtk_label_new(_("Bind DN"));
+       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_bindDN = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), entry_bindDN, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       // Next row
+       ++top;
+       label = gtk_label_new(_("Bind Password"));
+       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_bindPW = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), entry_bindPW, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       // Next row
+       ++top;
+       label = gtk_label_new(_("Timeout (secs)"));
+       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);
+
+       hbox_spin = gtk_hbox_new (FALSE, 8);
+       spinbtn_timeout_adj = gtk_adjustment_new (0, 0, 300, 1, 10, 10);
+       spinbtn_timeout = gtk_spin_button_new(GTK_ADJUSTMENT (spinbtn_timeout_adj), 1, 0);
+       gtk_box_pack_start (GTK_BOX (hbox_spin), spinbtn_timeout, FALSE, FALSE, 0);
+       gtk_widget_set_usize (spinbtn_timeout, 64, -1);
+       gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_timeout), TRUE);
+       gtk_table_attach(GTK_TABLE(table), hbox_spin, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       // Next row
+       ++top;
+       label = gtk_label_new(_("Maximum Entries"));
+       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);
+
+       hbox_spin = gtk_hbox_new (FALSE, 8);
+       spinbtn_maxentry_adj = gtk_adjustment_new (0, 0, 500, 1, 10, 10);
+       spinbtn_maxentry = gtk_spin_button_new(GTK_ADJUSTMENT (spinbtn_maxentry_adj), 1, 0);
+       gtk_box_pack_start (GTK_BOX (hbox_spin), spinbtn_maxentry, FALSE, FALSE, 0);
+       gtk_widget_set_usize (spinbtn_maxentry, 64, -1);
+       gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbtn_maxentry), TRUE);
+       gtk_table_attach(GTK_TABLE(table), hbox_spin, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       gtk_signal_connect(GTK_OBJECT(reset_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_ldap_search_reset), NULL);
+
+       gtk_widget_show_all(vbox);
+
+       ldapedit.entry_criteria   = entry_criteria;
+       ldapedit.entry_bindDN     = entry_bindDN;
+       ldapedit.entry_bindPW     = entry_bindPW;
+       ldapedit.spinbtn_timeout  = spinbtn_timeout;
+       ldapedit.spinbtn_maxentry = spinbtn_maxentry;
+}
+
+static void addressbook_edit_ldap_create( gboolean *cancelled ) {
+       gint page = 0;
+       addressbook_edit_ldap_dialog_create( cancelled );
+       addressbook_edit_ldap_page_basic( page++, _( "Basic" ) );
+       addressbook_edit_ldap_page_extended( page++, _( "Extended" ) );
+       gtk_widget_show_all( ldapedit.window );
+}
+
+void edit_ldap_set_optmenu( GtkOptionMenu *optmenu, const gint value ) {
+       GList *cur;
+       GtkWidget *menu;
+       GtkWidget *menuitem;
+       gint menuVal;
+       gint n = 0;
+
+       g_return_if_fail(menu != NULL);
+
+       menu = gtk_option_menu_get_menu(optmenu);
+       for( cur = GTK_MENU_SHELL(menu)->children; cur != NULL; cur = cur->next ) {
+               menuitem = GTK_WIDGET(cur->data);
+               menuVal = GPOINTER_TO_INT(gtk_object_get_user_data(GTK_OBJECT(menuitem)));
+               if( menuVal == value ) {
+                       gtk_option_menu_set_history(optmenu, n);
+                       return;
+               }
+               n++;
+       }
+       gtk_option_menu_set_history(optmenu, 0);
+}
+
+gint edit_ldap_get_optmenu( GtkOptionMenu *optmenu ) {
+       GtkWidget *menu;
+       GtkWidget *menuitem;
+       g_return_if_fail(menu != NULL);
+       menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu));
+       menuitem = gtk_menu_get_active(GTK_MENU(menu));
+       return GPOINTER_TO_INT(gtk_object_get_user_data(GTK_OBJECT(menuitem)));
+}
+
+AddressLDAP *addressbook_edit_ldap( AddressLDAP *ldapi ) {
+       static gboolean cancelled;
+       gchar *sName, *sHost, *sBase, *sBind, *sPass, *sCrit;
+       gint iPort, iMaxE, iTime, iMail, iName;
+       SyldapServer *server;
+       gboolean fin;
+
+       if (!ldapedit.window)
+               addressbook_edit_ldap_create(&cancelled);
+       gtk_notebook_set_page( GTK_NOTEBOOK(ldapedit.notebook), 0 );
+       gtk_widget_grab_focus(ldapedit.ok_btn);
+       gtk_widget_grab_focus(ldapedit.entry_name);
+       gtk_widget_show(ldapedit.window);
+       manage_window_set_transient(GTK_WINDOW(ldapedit.window));
+
+       edit_ldap_status_show( "" );
+       if( ldapi ) {
+               server = ldapi->ldapServer;
+               if (server->name)
+                       gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_name), server->name);
+               if (server->hostName)
+                       gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_server), server->hostName);
+               gtk_spin_button_set_value( GTK_SPIN_BUTTON( ldapedit.spinbtn_port ), server->port );
+               gtk_spin_button_set_value( GTK_SPIN_BUTTON( ldapedit.spinbtn_timeout ), server->timeOut );
+               if (server->baseDN)
+                       gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_baseDN), server->baseDN);
+               if (server->searchCriteria)
+                       gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_criteria), server->searchCriteria);
+               if (server->bindDN)
+                       gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_bindDN), server->bindDN);
+               if (server->bindPass)
+                       gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_bindPW), server->bindPass);
+               gtk_spin_button_set_value( GTK_SPIN_BUTTON( ldapedit.spinbtn_maxentry ), server->maxEntries );
+               gtk_window_set_title( GTK_WINDOW(ldapedit.window), _("Edit LDAP Server"));
+       }
+       else {
+               gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_name), ADDRESSBOOK_GUESS_LDAP_NAME );
+               gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_server), ADDRESSBOOK_GUESS_LDAP_SERVER );
+               gtk_spin_button_set_value( GTK_SPIN_BUTTON( ldapedit.spinbtn_port ), SYLDAP_DFL_PORT );
+               gtk_spin_button_set_value( GTK_SPIN_BUTTON( ldapedit.spinbtn_timeout ), SYLDAP_DFL_TIMEOUT );
+               gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_baseDN), "");
+               gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_criteria), SYLDAP_DFL_CRITERIA );
+               gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_bindDN), "");
+               gtk_entry_set_text(GTK_ENTRY(ldapedit.entry_bindPW), "");
+               gtk_spin_button_set_value( GTK_SPIN_BUTTON( ldapedit.spinbtn_maxentry ), SYLDAP_MAX_ENTRIES );
+               gtk_window_set_title( GTK_WINDOW(ldapedit.window), _("Add New LDAP Server"));
+       }
+
+       gtk_main();
+       gtk_widget_hide(ldapedit.window);
+       if (cancelled == TRUE) return NULL;
+
+       fin = FALSE;
+       sName = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_name), 0, -1 );
+       sHost = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_server), 0, -1 );
+       iPort = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_port ) );
+       iTime = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_timeout ) );
+       sBase = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_baseDN), 0, -1 );
+       sCrit = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_criteria), 0, -1 );
+       sBind = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_bindDN), 0, -1 );
+       sPass = gtk_editable_get_chars( GTK_EDITABLE(ldapedit.entry_bindPW), 0, -1 );
+       iMaxE = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON( ldapedit.spinbtn_maxentry ) );
+
+       if( *sName == '\0' ) fin = TRUE;
+       if( *sHost == '\0' ) fin = TRUE;
+       if( *sBase == '\0' ) fin = TRUE;
+
+       if( ! fin ) {
+               if( ! ldapi ) {
+                       ldapi = g_new0(AddressLDAP, 1);
+                       ADDRESS_OBJECT_TYPE(ldapi) = ADDR_LDAP;
+                       server = syldap_create();
+                       ldapi->ldapServer = server;
+               }
+               g_free( ldapi->name );
+               ldapi->name = g_strdup( sName );
+               syldap_set_name( server, sName );
+               syldap_set_host( server, sHost );
+               syldap_set_port( server, iPort );
+               syldap_set_base_dn( server, sBase );
+               syldap_set_bind_dn( server, sBind );
+               syldap_set_bind_password( server, sPass );
+               syldap_set_search_criteria( server, sCrit );
+               syldap_set_max_entries( server, iMaxE );
+               syldap_set_timeout( server, iTime );
+       }
+       g_free( sName );
+       g_free( sHost );
+       g_free( sBase );
+       g_free( sBind );
+       g_free( sPass );
+       g_free( sCrit );
+
+       return ldapi;
+}
+
+#endif /* USE_LDAP */
+
+/*
+* End of Source.
+*/
+
diff --git a/src/editldap.h b/src/editldap.h
new file mode 100644 (file)
index 0000000..68fafb5
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Edit VCard address book data.
+ */
+
+#ifndef __EDITLDAP_H__
+#define __EDITLDAP_H__
+
+#ifdef USE_LDAP
+
+// Function prototypes
+AddressLDAP *addressbook_edit_ldap( AddressLDAP *ldapi );
+
+#endif /* USE_LDAP */
+
+#endif /* __EDITLDAP_H__ */
+
+/*
+* End of Source.
+*/
+
diff --git a/src/editldap_basedn.c b/src/editldap_basedn.c
new file mode 100644 (file)
index 0000000..aabb27f
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * LDAP Base DN selection dialog.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#ifdef USE_LDAP
+
+#include "defs.h"
+
+#include <glib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtkhbbox.h>
+#include <gtk/gtkbutton.h>
+
+#include "intl.h"
+#include "prefs_common.h"
+#include "syldap.h"
+
+static struct _LDAPEdit_basedn {
+       GtkWidget *window;
+       GtkWidget *host_label;
+       GtkWidget *port_label;
+       GtkWidget *basedn_entry;
+       GtkWidget *basedn_list;
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_btn;
+       GtkWidget *statusbar;
+       gint status_cid;
+} ldapedit_basedn;
+
+static gboolean ldapedit_basedn_cancelled;
+static gboolean ldapedit_basedn_bad_server;
+
+/*
+* Edit functions.
+*/
+void edit_ldap_bdn_status_show( gchar *msg ) {
+       if( ldapedit_basedn.statusbar != NULL ) {
+               gtk_statusbar_pop( GTK_STATUSBAR(ldapedit_basedn.statusbar), ldapedit_basedn.status_cid );
+               if( msg ) {
+                       gtk_statusbar_push( GTK_STATUSBAR(ldapedit_basedn.statusbar), ldapedit_basedn.status_cid, msg );
+               }
+       }
+}
+
+static gint edit_ldap_bdn_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled ) {
+       ldapedit_basedn_cancelled = TRUE;
+       gtk_main_quit();
+       return TRUE;
+}
+
+static void edit_ldap_bdn_key_pressed( GtkWidget *widget, GdkEventKey *event, gboolean *cancelled ) {
+       if (event && event->keyval == GDK_Escape) {
+               ldapedit_basedn_cancelled = TRUE;
+               gtk_main_quit();
+       }
+}
+
+static void edit_ldap_bdn_ok( GtkWidget *widget, gboolean *cancelled ) {
+       ldapedit_basedn_cancelled = FALSE;
+       gtk_main_quit();
+}
+
+static void edit_ldap_bdn_cancel( GtkWidget *widget, gboolean *cancelled ) {
+       ldapedit_basedn_cancelled = TRUE;
+       gtk_main_quit();
+}
+
+static void edit_ldap_bdn_list_select( GtkCList *clist, gint row, gint column, GdkEvent *event, gpointer data ) {
+       gchar **text;
+       if( gtk_clist_get_text( clist, row, 0, text ) ) {
+               if( *text ) {
+                       gtk_entry_set_text(GTK_ENTRY(ldapedit_basedn.basedn_entry), *text );
+               }
+       }
+}
+
+static void edit_ldap_bdn_list_button( GtkCList *clist, GdkEventButton *event, gpointer data ) {
+       if( ! event ) return;
+       if( event->button == 1 ) {
+               if( event->type == GDK_2BUTTON_PRESS ) {
+                       ldapedit_basedn_cancelled = FALSE;
+                       gtk_main_quit();
+               }
+       }
+}
+
+static void edit_ldap_bdn_create() {
+       GtkWidget *window;
+       GtkWidget *vbox;
+       GtkWidget *table;
+       GtkWidget *label;
+       GtkWidget *host_label;
+       GtkWidget *port_label;
+       GtkWidget *basedn_label;
+       GtkWidget *basedn_list;
+       GtkWidget *vlbox;
+       GtkWidget *lwindow;
+       GtkWidget *basedn_entry;
+       GtkWidget *hlbox;
+       GtkWidget *hbbox;
+       GtkWidget *hsep;
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_btn;
+       GtkWidget *check_btn;
+       GtkWidget *file_btn;
+       GtkWidget *hsbox;
+       GtkWidget *statusbar;
+       gint top;
+
+       window = gtk_window_new(GTK_WINDOW_DIALOG);
+       gtk_widget_set_usize(window, 300, 270);
+       gtk_container_set_border_width(GTK_CONTAINER(window), 0);
+       gtk_window_set_title(GTK_WINDOW(window), _("Edit LDAP - Select Search Base"));
+       gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+       gtk_window_set_modal(GTK_WINDOW(window), TRUE); 
+       gtk_signal_connect(GTK_OBJECT(window), "delete_event",
+                          GTK_SIGNAL_FUNC(edit_ldap_bdn_delete_event), NULL );
+       gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
+                          GTK_SIGNAL_FUNC(edit_ldap_bdn_key_pressed), NULL );
+
+       vbox = gtk_vbox_new(FALSE, 8);
+       gtk_container_add(GTK_CONTAINER(window), vbox);
+       gtk_container_set_border_width( GTK_CONTAINER(vbox), 0 );
+
+       table = gtk_table_new(3, 2, FALSE);
+       gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+       gtk_container_set_border_width( GTK_CONTAINER(table), 8 );
+       gtk_table_set_row_spacings(GTK_TABLE(table), 8);
+       gtk_table_set_col_spacings(GTK_TABLE(table), 8);
+
+       // First row
+       top = 0;
+       label = gtk_label_new(_("Hostname"));
+       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);
+
+       host_label = gtk_label_new("");
+       gtk_table_attach(GTK_TABLE(table), host_label, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(host_label), 0, 0.5);
+
+       // Second row
+       top = 1;
+       label = gtk_label_new(_("Port"));
+       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);
+
+       port_label = gtk_label_new("");
+       gtk_table_attach(GTK_TABLE(table), port_label, 1, 2, top, (top + 1), GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(port_label), 0, 0.5);
+
+       // Third row
+       top = 2;
+       label = gtk_label_new(_("Search Base"));
+       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);
+
+       basedn_entry = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), basedn_entry, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       // Basedn list
+       vlbox = gtk_vbox_new(FALSE, 8);
+       gtk_box_pack_start(GTK_BOX(vbox), vlbox, TRUE, TRUE, 0);
+       gtk_container_set_border_width( GTK_CONTAINER(vlbox), 8 );
+
+       lwindow = gtk_scrolled_window_new(NULL, NULL);
+       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(lwindow),
+                                      GTK_POLICY_AUTOMATIC,
+                                      GTK_POLICY_ALWAYS);
+       gtk_box_pack_start(GTK_BOX(vlbox), lwindow, TRUE, TRUE, 0);
+
+       basedn_list = gtk_clist_new(1);
+       gtk_container_add(GTK_CONTAINER(lwindow), basedn_list);
+       gtk_clist_column_titles_show( GTK_CLIST(basedn_list) );
+       gtk_clist_set_column_title( GTK_CLIST(basedn_list), 0, _( "Available Search Base(s)" ) );
+       gtk_clist_set_selection_mode(GTK_CLIST(basedn_list), GTK_SELECTION_BROWSE);
+
+       // Status line
+       hsbox = gtk_hbox_new(FALSE, 0);
+       gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
+       statusbar = gtk_statusbar_new();
+       gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
+
+       // Button panel
+       gtkut_button_set_create(&hbbox, &ok_btn, _("OK"),
+                               &cancel_btn, _("Cancel"), NULL, NULL);
+       gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
+       gtk_container_set_border_width( GTK_CONTAINER(hbbox), 0 );
+       gtk_widget_grab_default(ok_btn);
+
+       hsep = gtk_hseparator_new();
+       gtk_box_pack_end(GTK_BOX(vbox), hsep, FALSE, FALSE, 0);
+
+       gtk_signal_connect(GTK_OBJECT(ok_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_ldap_bdn_ok), NULL);
+       gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_ldap_bdn_cancel), NULL);
+       gtk_signal_connect(GTK_OBJECT(basedn_list), "select_row",
+                          GTK_SIGNAL_FUNC(edit_ldap_bdn_list_select), NULL);
+       gtk_signal_connect(GTK_OBJECT(basedn_list), "button_press_event",
+                          GTK_SIGNAL_FUNC(edit_ldap_bdn_list_button), NULL);
+
+       gtk_widget_show_all(vbox);
+
+       ldapedit_basedn.window     = window;
+       ldapedit_basedn.host_label = host_label;
+       ldapedit_basedn.port_label = port_label;
+       ldapedit_basedn.basedn_entry = basedn_entry;
+       ldapedit_basedn.basedn_list  = basedn_list;
+       ldapedit_basedn.ok_btn     = ok_btn;
+       ldapedit_basedn.cancel_btn = cancel_btn;
+       ldapedit_basedn.statusbar  = statusbar;
+       ldapedit_basedn.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Edit LDAP Select Base DN" );
+}
+
+void edit_ldap_bdn_load_data( const gchar *hostName, const gint iPort, const gint tov, const gchar* bindDN,
+              const gchar *bindPW ) {
+       gchar *sHost;
+       gchar *sMsg = NULL;
+       gchar sPort[20];
+       gboolean flgConn;
+       gboolean flgDN;
+
+       edit_ldap_status_show( "" );
+       gtk_clist_clear(GTK_CLIST(ldapedit_basedn.basedn_list));
+       ldapedit_basedn_bad_server = TRUE;
+       flgConn = flgDN = FALSE;
+       sHost = g_strdup( hostName );
+       sprintf( sPort, "%d", iPort );
+       gtk_label_set_text(GTK_LABEL(ldapedit_basedn.host_label), hostName);
+       gtk_label_set_text(GTK_LABEL(ldapedit_basedn.port_label), sPort);
+       if( *sHost != '\0' ) {
+               // Test connection to server
+               if( syldap_test_connect_s( sHost, iPort ) ) {
+                       // Attempt to read base DN
+                       GList *baseDN = syldap_read_basedn_s( sHost, iPort, bindDN, bindPW, tov );
+                       if( baseDN ) {
+                               GList *node = baseDN;
+                               gchar *sBase[2];
+                               sBase[1] = NULL;
+                               while( node ) {
+                                       sBase[0] = g_strdup( node->data );
+                                       gtk_clist_append(GTK_CLIST(ldapedit_basedn.basedn_list), sBase);
+                                       node = g_list_next( node );
+                                       flgDN = TRUE;
+                               }
+                               mgu_free_dlist( baseDN );
+                               baseDN = node = NULL;
+                               sBase[0] = NULL;
+                       }
+                       ldapedit_basedn_bad_server = FALSE;
+                       flgConn = TRUE;
+               }
+       }
+       g_free( sHost );
+
+       // Display appropriate message
+       if( flgConn ) {
+               if( ! flgDN ) {
+                       sMsg = _( "Could not read Search Base(s) from server - please set manually" );
+               }
+       }
+       else {
+               sMsg = _( "Could not connect to server" );
+       }
+       edit_ldap_bdn_status_show( sMsg );
+}
+
+gchar *edit_ldap_basedn_selection( const gchar *hostName, const gint port, gchar *baseDN, const gint tov,
+              const gchar* bindDN, const gchar *bindPW ) {
+       gchar *retVal = NULL;
+
+       ldapedit_basedn_cancelled = FALSE;
+       if( ! ldapedit_basedn.window ) edit_ldap_bdn_create();
+       gtk_widget_grab_focus(ldapedit_basedn.ok_btn);
+       gtk_widget_show(ldapedit_basedn.window);
+       manage_window_set_transient(GTK_WINDOW(ldapedit_basedn.window));
+
+       edit_ldap_bdn_status_show( "" );
+       edit_ldap_bdn_load_data( hostName, port, tov, bindDN, bindPW );
+       gtk_widget_show(ldapedit_basedn.window);
+
+//     sprintf( sPort, "%d", port );
+//     gtk_label_set_text(GTK_LABEL(ldapedit_basedn.host_label), hostName);
+//     gtk_label_set_text(GTK_LABEL(ldapedit_basedn.port_label), sPort);
+       gtk_entry_set_text(GTK_ENTRY(ldapedit_basedn.basedn_entry), baseDN);
+
+       gtk_main();
+       gtk_widget_hide(ldapedit_basedn.window);
+       if( ldapedit_basedn_cancelled ) return NULL;
+//     if( cancelled == TRUE ) return NULL;
+       if( ldapedit_basedn_bad_server ) return NULL;
+
+       retVal = g_strdup( gtk_editable_get_chars( GTK_EDITABLE(ldapedit_basedn.basedn_entry), 0, -1 ) );
+       g_strchomp( retVal ); g_strchug( retVal );
+       if( *retVal == '\0' ) {
+               g_free( retVal );
+               retVal = NULL;
+       }
+       return retVal;
+}
+
+#endif /* USE_LDAP */
+
+/*
+* End of Source.
+*/
+
diff --git a/src/editldap_basedn.h b/src/editldap_basedn.h
new file mode 100644 (file)
index 0000000..d53620f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Edit VCard address book data.
+ */
+
+#ifndef __EDITLDAP_BASEDN_H__
+#define __EDITLDAP_BASEDN_H__
+
+#ifdef USE_LDAP
+
+// Function prototypes
+gchar *edit_ldap_basedn_selection( const gchar *hostName, const gint port, gchar *baseDN, const gint tov,
+              const gchar* bindDN, const gchar *bindPW );
+
+#endif /* USE_LDAP */
+
+#endif /* __EDITLDAP_BASEDN_H__ */
+
+/*
+* End of Source.
+*/
+
diff --git a/src/editvcard.c b/src/editvcard.c
new file mode 100644 (file)
index 0000000..4420b84
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Edit VCard address book data.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "defs.h"
+
+#include <glib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtkbutton.h>
+
+#include "intl.h"
+#include "addressbook.h"
+#include "prefs_common.h"
+#include "addressitem.h"
+#include "vcard.h"
+
+#define ADDRESSBOOK_GUESS_VCARD  "MyGnomeCard"
+
+static struct _VCardEdit {
+       GtkWidget *window;
+       GtkWidget *name_entry;
+       GtkWidget *file_entry;
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_btn;
+       GtkWidget *statusbar;
+       gint status_cid;
+} vcardedit;
+
+static struct _AddressFileSelection vcard_file_selector;
+
+/*
+* Edit functions.
+*/
+void edit_vcard_status_show( gchar *msg ) {
+       if( vcardedit.statusbar != NULL ) {
+               gtk_statusbar_pop( GTK_STATUSBAR(vcardedit.statusbar), vcardedit.status_cid );
+               if( msg ) {
+                       gtk_statusbar_push( GTK_STATUSBAR(vcardedit.statusbar), vcardedit.status_cid, msg );
+               }
+       }
+}
+
+static void edit_vcard_ok( GtkWidget *widget, gboolean *cancelled ) {
+       *cancelled = FALSE;
+       gtk_main_quit();
+}
+
+static void edit_vcard_cancel( GtkWidget *widget, gboolean *cancelled ) {
+       *cancelled = TRUE;
+       gtk_main_quit();
+}
+
+static void edit_vcard_file_check( void ) {
+       gint t;
+       gchar *sFile;
+       gchar *sMsg;
+
+       sFile = gtk_editable_get_chars( GTK_EDITABLE(vcardedit.file_entry), 0, -1 );
+       t = vcard_test_read_file( sFile );
+       g_free( sFile );
+       if( t == MGU_SUCCESS ) {
+               sMsg = "";
+       }
+       else if( t == MGU_BAD_FORMAT ) {
+               sMsg = _("File does not appear to be VCard format.");
+       }
+       else {
+               sMsg = _("Could not read file.");
+       }
+       edit_vcard_status_show( sMsg );
+}
+
+static void edit_vcard_file_ok( GtkWidget *widget, gpointer data ) {
+       gchar *sFile;
+       AddressFileSelection *afs;
+       GtkWidget *fileSel;
+
+       afs = ( AddressFileSelection * ) data;
+       fileSel = afs->fileSelector;
+       sFile = gtk_file_selection_get_filename( GTK_FILE_SELECTION(fileSel) );
+
+       afs->cancelled = FALSE;
+       gtk_entry_set_text( GTK_ENTRY(vcardedit.file_entry), sFile );
+       gtk_widget_hide( afs->fileSelector );
+       gtk_grab_remove( afs->fileSelector );
+       edit_vcard_file_check();
+       gtk_widget_grab_focus( vcardedit.file_entry );
+}
+
+static void edit_vcard_file_cancel( GtkWidget *widget, gpointer data ) {
+       AddressFileSelection *afs = ( AddressFileSelection * ) data;
+       afs->cancelled = TRUE;
+       gtk_widget_hide( afs->fileSelector );
+       gtk_grab_remove( afs->fileSelector );
+       gtk_widget_grab_focus( vcardedit.file_entry );
+}
+
+static void edit_vcard_file_select_create( AddressFileSelection *afs ) {
+       GtkWidget *fileSelector;
+
+       fileSelector = gtk_file_selection_new( _("Select VCard File") );
+       gtk_file_selection_hide_fileop_buttons( GTK_FILE_SELECTION(fileSelector) );
+       gtk_signal_connect( GTK_OBJECT (GTK_FILE_SELECTION(fileSelector)->ok_button),
+                             "clicked", GTK_SIGNAL_FUNC (edit_vcard_file_ok), ( gpointer ) afs );
+       gtk_signal_connect( GTK_OBJECT (GTK_FILE_SELECTION(fileSelector)->cancel_button),
+                             "clicked", GTK_SIGNAL_FUNC (edit_vcard_file_cancel), ( gpointer ) afs );
+       afs->fileSelector = fileSelector;
+       afs->cancelled = TRUE;
+}
+
+static void edit_vcard_file_select( void ) {
+       gchar *sFile;
+
+       if (! vcard_file_selector.fileSelector )
+               edit_vcard_file_select_create( & vcard_file_selector );
+
+       sFile = gtk_editable_get_chars( GTK_EDITABLE(vcardedit.file_entry), 0, -1 );
+       gtk_file_selection_set_filename( GTK_FILE_SELECTION( vcard_file_selector.fileSelector ), sFile );
+       g_free( sFile );
+       gtk_widget_show( vcard_file_selector.fileSelector );
+       gtk_grab_add( vcard_file_selector.fileSelector );
+}
+
+static gint edit_vcard_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled ) {
+       *cancelled = TRUE;
+       gtk_main_quit();
+       return TRUE;
+}
+
+static void edit_vcard_key_pressed( GtkWidget *widget, GdkEventKey *event, gboolean *cancelled ) {
+       if (event && event->keyval == GDK_Escape) {
+               *cancelled = TRUE;
+               gtk_main_quit();
+       }
+}
+
+static void addressbook_edit_vcard_create( gboolean *cancelled ) {
+       GtkWidget *window;
+       GtkWidget *vbox;
+       GtkWidget *table;
+       GtkWidget *label;
+       GtkWidget *name_entry;
+       GtkWidget *file_entry;
+       GtkWidget *hbbox;
+       GtkWidget *hsep;
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_btn;
+       GtkWidget *check_btn;
+       GtkWidget *file_btn;
+       GtkWidget *statusbar;
+       GtkWidget *hsbox;
+       gint top;
+
+       window = gtk_window_new(GTK_WINDOW_DIALOG);
+       gtk_widget_set_usize(window, 450, -1);
+       gtk_container_set_border_width( GTK_CONTAINER(window), 0 );
+       gtk_window_set_title(GTK_WINDOW(window), _("Edit VCard Entry"));
+       gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+       gtk_window_set_modal(GTK_WINDOW(window), TRUE); 
+       gtk_signal_connect(GTK_OBJECT(window), "delete_event",
+                          GTK_SIGNAL_FUNC(edit_vcard_delete_event),
+                          cancelled);
+       gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
+                          GTK_SIGNAL_FUNC(edit_vcard_key_pressed),
+                          cancelled);
+
+       vbox = gtk_vbox_new(FALSE, 8);
+       gtk_container_add(GTK_CONTAINER(window), vbox);
+       gtk_container_set_border_width( GTK_CONTAINER(vbox), 0 );
+
+       table = gtk_table_new(2, 3, FALSE);
+       gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+       gtk_container_set_border_width( GTK_CONTAINER(table), 8 );
+       gtk_table_set_row_spacings(GTK_TABLE(table), 8);
+       gtk_table_set_col_spacings(GTK_TABLE(table), 8 );
+
+       // First row
+       top = 0;
+       label = gtk_label_new(_("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);
+
+       name_entry = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), name_entry, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       check_btn = gtk_button_new_with_label( _(" Check File "));
+       gtk_table_attach(GTK_TABLE(table), check_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0);
+
+       // Second row
+       top = 1;
+       label = gtk_label_new(_("File"));
+       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);
+
+       file_entry = gtk_entry_new();
+       gtk_table_attach(GTK_TABLE(table), file_entry, 1, 2, top, (top + 1), GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
+
+       file_btn = gtk_button_new_with_label( _(" ... "));
+       gtk_table_attach(GTK_TABLE(table), file_btn, 2, 3, top, (top + 1), GTK_FILL, 0, 3, 0);
+
+       // Status line
+       hsbox = gtk_hbox_new(FALSE, 0);
+       gtk_box_pack_end(GTK_BOX(vbox), hsbox, FALSE, FALSE, BORDER_WIDTH);
+       statusbar = gtk_statusbar_new();
+       gtk_box_pack_start(GTK_BOX(hsbox), statusbar, TRUE, TRUE, BORDER_WIDTH);
+
+       // Button panel
+       gtkut_button_set_create(&hbbox, &ok_btn, _("OK"),
+                               &cancel_btn, _("Cancel"), NULL, NULL);
+       gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
+       gtk_container_set_border_width( GTK_CONTAINER(hbbox), 0 );
+       gtk_widget_grab_default(ok_btn);
+
+       hsep = gtk_hseparator_new();
+       gtk_box_pack_end(GTK_BOX(vbox), hsep, FALSE, FALSE, 0);
+
+       gtk_signal_connect(GTK_OBJECT(ok_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_vcard_ok), cancelled);
+       gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_vcard_cancel), cancelled);
+       gtk_signal_connect(GTK_OBJECT(file_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_vcard_file_select), NULL);
+       gtk_signal_connect(GTK_OBJECT(check_btn), "clicked",
+                          GTK_SIGNAL_FUNC(edit_vcard_file_check), NULL);
+
+       gtk_widget_show_all(vbox);
+
+       vcardedit.window     = window;
+       vcardedit.name_entry = name_entry;
+       vcardedit.file_entry = file_entry;
+       vcardedit.ok_btn     = ok_btn;
+       vcardedit.cancel_btn = cancel_btn;
+       vcardedit.statusbar  = statusbar;
+       vcardedit.status_cid = gtk_statusbar_get_context_id( GTK_STATUSBAR(statusbar), "Edit VCard Dialog" );
+}
+
+AddressVCard *addressbook_edit_vcard( AddressVCard *vcard ) {
+       static gboolean cancelled;
+       gchar *sName;
+       gchar *sFile;
+       VCardFile *vcf;
+       gboolean fin;
+
+       if (!vcardedit.window)
+               addressbook_edit_vcard_create(&cancelled);
+       gtk_widget_grab_focus(vcardedit.ok_btn);
+       gtk_widget_grab_focus(vcardedit.name_entry);
+       gtk_widget_show(vcardedit.window);
+       manage_window_set_transient(GTK_WINDOW(vcardedit.window));
+
+       edit_vcard_status_show( "" );
+       if( vcard ) {
+               vcf = vcard->cardFile;
+               if (vcf->name)
+                       gtk_entry_set_text(GTK_ENTRY(vcardedit.name_entry), vcf->name);
+               if (vcf->path)
+                       gtk_entry_set_text(GTK_ENTRY(vcardedit.file_entry), vcf->path);
+               gtk_window_set_title( GTK_WINDOW(vcardedit.window), _("Edit VCard Entry"));
+       }
+       else {
+               gtk_entry_set_text(GTK_ENTRY(vcardedit.name_entry), ADDRESSBOOK_GUESS_VCARD );
+               gtk_entry_set_text(GTK_ENTRY(vcardedit.file_entry), vcard_find_gnomecard() );
+               gtk_window_set_title( GTK_WINDOW(vcardedit.window), _("Add New VCard Entry"));
+       }
+
+       gtk_main();
+       gtk_widget_hide(vcardedit.window);
+       if (cancelled == TRUE) return NULL;
+
+       fin = FALSE;
+       sName = gtk_editable_get_chars( GTK_EDITABLE(vcardedit.name_entry), 0, -1 );
+       sFile = gtk_editable_get_chars( GTK_EDITABLE(vcardedit.file_entry), 0, -1 );
+       if( *sName == '\0' ) fin = TRUE;
+       if( *sFile == '\0' ) fin = TRUE;
+
+       if( ! fin ) {
+               if( ! vcard ) {
+                       vcard = g_new0(AddressVCard, 1);
+                       ADDRESS_OBJECT_TYPE(vcard) = ADDR_VCARD;
+                       vcf = vcard_create();
+                       vcard->cardFile = vcf;
+               }
+               g_free( vcard->name );
+               vcard->name = g_strdup( sName );
+               vcard_set_name( vcf, sName );
+               vcard_set_file( vcf, sFile );
+       }
+       g_free( sName );
+       g_free( sFile );
+
+       return vcard;
+}
+
+/*
+* End of Source.
+*/
+
diff --git a/src/editvcard.h b/src/editvcard.h
new file mode 100644 (file)
index 0000000..26b9471
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Edit VCard address book data.
+ */
+
+#ifndef __EDITVCARD_H__
+#define __EDITVCARD_H__
+
+// Function prototypes
+AddressVCard *addressbook_edit_vcard( AddressVCard *vcard );
+
+#endif /* __EDITVCARD_H__ */
+
+/*
+* End of Source.
+*/
+
diff --git a/src/jpilot.c b/src/jpilot.c
new file mode 100644 (file)
index 0000000..27d0896
--- /dev/null
@@ -0,0 +1,1264 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Functions necessary to access JPilot database files.
+ * JPilot is Copyright(c) by Judd Montgomery.
+ * Visit http://www.jpilot.org for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#ifdef USE_JPILOT
+
+#include <time.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+
+#include <pi-args.h>
+#include <pi-appinfo.h>
+#include <pi-address.h>
+
+#include "mgutils.h"
+#include "jpilot.h"
+
+#define JPILOT_DBHOME_DIR   ".jpilot"
+#define JPILOT_DBHOME_FILE  "AddressDB.pdb"
+#define PILOT_LINK_LIB_NAME "libpisock.so"
+
+#define IND_LABEL_LASTNAME  0  // Index of last name in address data
+#define IND_LABEL_FIRSTNAME 1  // Index of first name in address data
+#define IND_PHONE_EMAIL     4  // Index of E-Mail address in phone labels
+#define OFFSET_PHONE_LABEL  3  // Offset to phone data in address data
+#define IND_CUSTOM_LABEL    14 // Offset to custom label names
+#define NUM_CUSTOM_LABEL    4  // Number of custom labels
+
+typedef struct _JPilotCategory JPilotCategory;
+struct _JPilotCategory {
+       AddressItem *category;
+       GList *addressList;
+       gint count;
+};
+
+/*
+* Specify name to be used.
+*/
+void jpilot_set_name( JPilotFile* pilotFile, const gchar *name ) {
+       if( pilotFile->name ) g_free( pilotFile->name );
+       if( name ) pilotFile->name = g_strdup( name );
+}
+
+/*
+* Specify file to be used.
+*/
+void jpilot_set_file( JPilotFile* pilotFile, const gchar *path ) {
+       g_return_if_fail( pilotFile != NULL );
+       mgu_refresh_cache( pilotFile->addressCache );
+       pilotFile->readMetadata = FALSE;
+
+       /* Copy file path */
+       if( pilotFile->path ) g_free( pilotFile->path );
+       if( path ) pilotFile->path = g_strdup( path );
+}
+
+/*
+* Create new pilot file object.
+*/
+JPilotFile *jpilot_create() {
+       JPilotFile *pilotFile;
+       pilotFile = g_new( JPilotFile, 1 );
+       pilotFile->name = NULL;
+       pilotFile->path = NULL;
+       pilotFile->file = NULL;
+       pilotFile->addressCache = mgu_create_cache();
+       pilotFile->readMetadata = FALSE;
+       pilotFile->customLabels = NULL;
+       pilotFile->labelInd = NULL;
+       pilotFile->retVal = MGU_SUCCESS;
+       pilotFile->categoryList = NULL;
+       pilotFile->catAddrList = NULL;
+       return pilotFile;
+}
+
+/*
+* Create new pilot file object for specified file.
+*/
+JPilotFile *jpilot_create_path( const gchar *path ) {
+       JPilotFile *pilotFile;
+       pilotFile = jpilot_create();
+       jpilot_set_file( pilotFile, path );
+       return pilotFile;
+}
+
+/*
+* Test whether file was modified since last access.
+* Return: TRUE if file was modified.
+*/
+gboolean jpilot_get_modified( JPilotFile *pilotFile ) {
+       g_return_if_fail( pilotFile != NULL );
+       return mgu_check_file( pilotFile->addressCache, pilotFile->path );
+}
+
+/*
+* Free up custom label list.
+*/
+void jpilot_clear_custom_labels( JPilotFile *pilotFile ) {
+       GSList *node;
+       g_return_if_fail( pilotFile != NULL );
+
+       // Release custom labels
+       mgu_free_list( pilotFile->customLabels );
+       pilotFile->customLabels = NULL;
+
+       // Release indexes
+       node = pilotFile->labelInd;
+       while( node ) {
+               node->data = NULL;
+               node = g_slist_next( node );
+       }
+       g_slist_free( pilotFile->labelInd );
+       pilotFile->labelInd = NULL;
+
+       // Force a fresh read
+       mgu_refresh_cache( pilotFile->addressCache );
+}
+
+/*
+* Append a custom label, representing an E-Mail address field to the
+* custom label list.
+*/
+void jpilot_add_custom_label( JPilotFile *pilotFile, const gchar *labelName ) {
+       g_return_if_fail( pilotFile != NULL );
+
+       if( labelName ) {
+               gchar *labelCopy = g_strdup( labelName );
+               g_strstrip( labelCopy );
+               if( *labelCopy == '\0' ) {
+                       g_free( labelCopy );
+               }
+               else {
+                       pilotFile->customLabels = g_slist_append( pilotFile->customLabels, labelCopy );
+                       // Force a fresh read
+                       mgu_refresh_cache( pilotFile->addressCache );
+               }
+       }
+}
+
+/*
+* Get list of custom labels.
+* Return: List of labels. Must use g_free() when done.
+*/
+GList *jpilot_get_custom_labels( JPilotFile *pilotFile ) {
+       GList *retVal = NULL;
+       GSList *node;
+       g_return_if_fail( pilotFile != NULL );
+       node = pilotFile->customLabels;
+       while( node ) {
+               retVal = g_list_append( retVal, g_strdup( node->data ) );
+               node = g_slist_next( node );
+       }
+       return retVal;
+}
+
+/*
+* Free up pilot file object by releasing internal memory.
+*/
+void jpilot_free( JPilotFile *pilotFile ) {
+       g_return_if_fail( pilotFile != NULL );
+
+       /* Free internal stuff */
+       g_free( pilotFile->path );
+
+       // Release custom labels
+       jpilot_clear_custom_labels( pilotFile );
+
+       /* Clear cache */
+       mgu_clear_cache( pilotFile->addressCache );
+       mgu_free_cache( pilotFile->addressCache );
+       mgu_free_dlist( pilotFile->categoryList );
+       pilotFile->addressCache = NULL;
+       pilotFile->readMetadata = FALSE;
+       pilotFile->categoryList = NULL;
+       pilotFile->catAddrList = NULL;
+
+       /* Now release file object */
+       g_free( pilotFile );
+}
+
+/*
+* Refresh internal variables to force a file read.
+*/
+void jpilot_force_refresh( JPilotFile *pilotFile ) {
+       mgu_refresh_cache( pilotFile->addressCache );
+}
+
+/*
+* Print category address list for specified category.
+*/
+void jpilot_print_category( JPilotCategory *jpcat, FILE *stream ) {
+       fprintf( stream, "category: %s : count: %d\n", jpcat->category->name, jpcat->count );
+       if( jpcat->addressList ) {
+               mgu_print_address_list( jpcat->addressList, stream );
+       }
+       fprintf( stream, "=========================================\n" );
+}
+
+/*
+* Print address list for all categories.
+*/
+void jpilot_print_category_list( GList *catAddrList, FILE *stream ) {
+       GList *node;
+       JPilotCategory *jpcat;
+       if( catAddrList == NULL ) return;
+       
+       fprintf( stream, "Address list by category\n" );
+       node = catAddrList;
+       while( node ) {
+               jpcat = node->data;
+               jpilot_print_category( jpcat, stream );
+               node = g_list_next( node );
+       }
+}
+
+/*
+* Print object to specified stream.
+*/
+void jpilot_print_file( JPilotFile *pilotFile, FILE *stream ) {
+       GSList *node;
+       g_return_if_fail( pilotFile != NULL );
+       fprintf( stream, "JPilotFile:\n" );
+       fprintf( stream, "file spec: '%s'\n", pilotFile->path );
+       fprintf( stream, " metadata: %s\n", pilotFile->readMetadata ? "yes" : "no" );
+       fprintf( stream, "  ret val: %d\n", pilotFile->retVal );
+
+       node = pilotFile->customLabels;
+       while( node ) {
+               fprintf( stream, "  c label: %s\n", node->data );
+               node = g_slist_next( node );
+       }
+
+       node = pilotFile->labelInd;
+       while( node ) {
+               fprintf( stream, " labelind: %d\n", GPOINTER_TO_INT(node->data) );
+               node = g_slist_next( node );
+       }
+
+       mgu_print_cache( pilotFile->addressCache, stream );
+       jpilot_print_category_list( pilotFile->catAddrList, stream );
+}
+
+// Shamelessly copied from JPilot (libplugin.c)
+static unsigned int bytes_to_bin(unsigned char *bytes, unsigned int num_bytes) {
+   unsigned int i, n;
+   n=0;
+   for (i=0;i<num_bytes;i++) {
+      n = n*256+bytes[i];
+   }
+   return n;
+}
+
+// Shamelessly copied from JPilot (utils.c)
+/*These next 2 functions were copied from pi-file.c in the pilot-link app */
+/* Exact value of "Jan 1, 1970 0:00:00 GMT" - "Jan 1, 1904 0:00:00 GMT" */
+#define PILOT_TIME_DELTA (unsigned)(2082844800)
+
+time_t pilot_time_to_unix_time ( unsigned long raw_time ) {
+   return (time_t)(raw_time - PILOT_TIME_DELTA);
+}
+
+// Shamelessly copied from JPilot (libplugin.c)
+static int raw_header_to_header(RawDBHeader *rdbh, DBHeader *dbh) {
+   unsigned long temp;
+   strncpy(dbh->db_name, rdbh->db_name, 31);
+   dbh->db_name[31] = '\0';
+   dbh->flags = bytes_to_bin(rdbh->flags, 2);
+   dbh->version = bytes_to_bin(rdbh->version, 2);
+   temp = bytes_to_bin(rdbh->creation_time, 4);
+   dbh->creation_time = pilot_time_to_unix_time(temp);
+   temp = bytes_to_bin(rdbh->modification_time, 4);
+   dbh->modification_time = pilot_time_to_unix_time(temp);
+   temp = bytes_to_bin(rdbh->backup_time, 4);
+   dbh->backup_time = pilot_time_to_unix_time(temp);
+   dbh->modification_number = bytes_to_bin(rdbh->modification_number, 4);
+   dbh->app_info_offset = bytes_to_bin(rdbh->app_info_offset, 4);
+   dbh->sort_info_offset = bytes_to_bin(rdbh->sort_info_offset, 4);
+   strncpy(dbh->type, rdbh->type, 4);
+   dbh->type[4] = '\0';
+   strncpy(dbh->creator_id, rdbh->creator_id, 4);
+   dbh->creator_id[4] = '\0';
+   strncpy(dbh->unique_id_seed, rdbh->unique_id_seed, 4);
+   dbh->unique_id_seed[4] = '\0';
+   dbh->next_record_list_id = bytes_to_bin(rdbh->next_record_list_id, 4);
+   dbh->number_of_records = bytes_to_bin(rdbh->number_of_records, 2);
+   return 0;
+}
+
+// Shamelessly copied from JPilot (libplugin.c)
+/*returns 1 if found */
+/*        0 if eof */
+static int find_next_offset( mem_rec_header *mem_rh, long fpos,
+       unsigned int *next_offset, unsigned char *attrib, unsigned int *unique_id )
+{
+       mem_rec_header *temp_mem_rh;
+       unsigned char found = 0;
+       unsigned long found_at;
+
+       found_at=0xFFFFFF;
+       for (temp_mem_rh=mem_rh; temp_mem_rh; temp_mem_rh = temp_mem_rh->next) {
+               if ((temp_mem_rh->offset > fpos) && (temp_mem_rh->offset < found_at)) {
+                       found_at = temp_mem_rh->offset;
+                       /* *attrib = temp_mem_rh->attrib; */
+                       /* *unique_id = temp_mem_rh->unique_id; */
+               }
+               if ((temp_mem_rh->offset == fpos)) {
+                       found = 1;
+                       *attrib = temp_mem_rh->attrib;
+                       *unique_id = temp_mem_rh->unique_id;
+               }
+       }
+       *next_offset = found_at;
+       return found;
+}
+
+// Shamelessly copied from JPilot (libplugin.c)
+static void free_mem_rec_header(mem_rec_header **mem_rh) {
+       mem_rec_header *h, *next_h;
+       for (h=*mem_rh; h; h=next_h) {
+               next_h=h->next;
+               free(h);
+       }
+       *mem_rh = NULL;
+}
+
+// Shamelessly copied from JPilot (libplugin.c)
+int jpilot_free_db_list( GList **br_list ) {
+       GList *temp_list, *first;
+       buf_rec *br;
+
+       /* Go to first entry in the list */
+       first=NULL;
+       for( temp_list = *br_list; temp_list; temp_list = temp_list->prev ) {
+               first = temp_list;
+       }
+       for (temp_list = first; temp_list; temp_list = temp_list->next) {
+               if (temp_list->data) {
+                       br=temp_list->data;
+                       if (br->buf) {
+                               free(br->buf);
+                               temp_list->data=NULL;
+                       }
+                       free(br);
+               }
+       }
+       g_list_free(*br_list);
+       *br_list=NULL;
+       return 0;
+}
+
+// Shamelessly copied from JPilot (libplugin.c)
+// Read file size.
+int jpilot_get_info_size( FILE *in, int *size ) {
+       RawDBHeader rdbh;
+       DBHeader dbh;
+       unsigned int offset;
+       record_header rh;
+
+       fseek(in, 0, SEEK_SET);
+       fread(&rdbh, sizeof(RawDBHeader), 1, in);
+       if (feof(in)) {
+               // fprintf( stderr, "error reading file in 'jpilot_get_info_size'\n" );
+               return MGU_EOF;
+       }
+
+       raw_header_to_header(&rdbh, &dbh);
+       if (dbh.app_info_offset==0) {
+               *size=0;
+               return MGU_SUCCESS;
+       }
+       if (dbh.sort_info_offset!=0) {
+               *size = dbh.sort_info_offset - dbh.app_info_offset;
+               return MGU_SUCCESS;
+       }
+       if (dbh.number_of_records==0) {
+               fseek(in, 0, SEEK_END);
+               *size=ftell(in) - dbh.app_info_offset;
+               return MGU_SUCCESS;
+       }
+
+       fread(&rh, sizeof(record_header), 1, in);
+       offset = ((rh.Offset[0]*256+rh.Offset[1])*256+rh.Offset[2])*256+rh.Offset[3];
+       *size=offset - dbh.app_info_offset;
+
+       return MGU_SUCCESS;
+}
+
+// Read address file into address list. Based on JPilot's
+// libplugin.c (jp_get_app_info)
+gint jpilot_get_file_info( JPilotFile *pilotFile, unsigned char **buf, int *buf_size ) {
+       FILE *in;
+       int num;
+       unsigned int rec_size;
+       RawDBHeader rdbh;
+       DBHeader dbh;
+
+       if( ( !buf_size ) || ( ! buf ) ) {
+               return MGU_BAD_ARGS;
+       }
+
+       *buf = NULL;
+       *buf_size=0;
+
+       if( pilotFile->path ) {
+               in = fopen( pilotFile->path, "r" );
+               if( !in ) {
+                       // fprintf( stderr, "can't open %s\n", pilotFile->path );
+                       return MGU_OPEN_FILE;
+               }
+       }
+       else {
+               // fprintf( stderr, "file not specified\n" );
+               return MGU_NO_FILE;
+       }
+
+       num = fread( &rdbh, sizeof( RawDBHeader ), 1, in );
+       if( num != 1 ) {
+               if( ferror(in) ) {
+                       // fprintf( stderr, "error reading %s\n", pilotFile->path );
+                       fclose(in);
+                       return MGU_ERROR_READ;
+               }
+       }
+       if (feof(in)) {
+               fclose(in);
+               return MGU_EOF;
+       }
+
+       // Convert header into something recognizable
+       raw_header_to_header(&rdbh, &dbh);
+
+       num = jpilot_get_info_size(in, &rec_size);
+       if (num) {
+               fclose(in);
+               return MGU_ERROR_READ;
+       }
+
+       fseek(in, dbh.app_info_offset, SEEK_SET);
+       *buf = ( char * ) malloc(rec_size);
+       if (!(*buf)) {
+               // fprintf( stderr, "jpilot_get_file_info(): Out of memory\n" );
+               fclose(in);
+               return MGU_OO_MEMORY;
+       }
+       num = fread(*buf, rec_size, 1, in);
+       if (num != 1) {
+               if (ferror(in)) {
+                       fclose(in);
+                       free(*buf);
+                       // fprintf( stderr, "Error reading %s\n", pilotFile->path );
+                       return MGU_ERROR_READ;
+               }
+       }
+       fclose(in);
+
+       *buf_size = rec_size;
+
+       return MGU_SUCCESS;
+}
+
+#define        FULLNAME_BUFSIZE   256
+#define        EMAIL_BUFSIZE      256
+// Read address file into address cache. Based on JPilot's
+// jp_read_DB_files (from libplugin.c)
+gint jpilot_read_cache( JPilotFile *pilotFile ) {
+       FILE *in;
+       char *buf;
+       int num_records, recs_returned, i, num, r;
+       unsigned int offset, prev_offset, next_offset, rec_size;
+       int out_of_order;
+       long fpos;  /*file position indicator */
+       unsigned char attrib;
+       unsigned int unique_id;
+       gint cat_id;
+       mem_rec_header *mem_rh, *temp_mem_rh, *last_mem_rh;
+       record_header rh;
+       RawDBHeader rdbh;
+       DBHeader dbh;
+       gint retVal;
+       struct Address addr;
+       struct AddressAppInfo *ai;
+       char **addrEnt;
+       int inum, k;
+       gchar fullName[ FULLNAME_BUFSIZE ];
+       gchar bufEMail[ EMAIL_BUFSIZE ];
+       gchar* extID;
+       AddressItem *addrItem = NULL;
+       int *indPhoneLbl;
+       char *labelEntry;
+       GSList *node;
+
+       retVal = MGU_SUCCESS;
+       mem_rh = last_mem_rh = NULL;
+       recs_returned = 0;
+
+       // Pointer to address metadata.
+       ai = & pilotFile->addrInfo;
+
+       // Open file for read
+       if( pilotFile->path ) {
+               in = fopen( pilotFile->path, "r" );
+               if( !in ) {
+                       // fprintf( stderr, "can't open %s\n", pilotFile->path );
+                       return MGU_OPEN_FILE;
+               }
+       }
+       else {
+               // fprintf( stderr, "file not specified\n" );
+               return MGU_NO_FILE;
+       }
+
+       /* Read the database header */
+       num = fread(&rdbh, sizeof(RawDBHeader), 1, in);
+       if (num != 1) {
+               if (ferror(in)) {
+                       // fprintf( stderr, "error reading '%s'\n", pilotFile->path );
+                       fclose(in);
+                       return MGU_ERROR_READ;
+               }
+               if (feof(in)) {
+                       return MGU_EOF;
+               }
+       }
+
+       raw_header_to_header(&rdbh, &dbh);
+
+       /*Read each record entry header */
+       num_records = dbh.number_of_records;
+       out_of_order = 0;
+       prev_offset = 0;
+
+       for (i=1; i<num_records+1; i++) {
+               num = fread( &rh, sizeof( record_header ), 1, in );
+               if (num != 1) {
+                       if (ferror(in)) {
+                               // fprintf( stderr, "error reading '%s'\n", pilotFile->path );
+                               retVal = MGU_ERROR_READ;
+                               break;
+                       }
+                       if (feof(in)) {
+                               return MGU_EOF;
+                       }
+               }
+
+               offset = ((rh.Offset[0]*256+rh.Offset[1])*256+rh.Offset[2])*256+rh.Offset[3];
+               if (offset < prev_offset) {
+                       out_of_order = 1;
+               }
+               prev_offset = offset;
+
+               temp_mem_rh = (mem_rec_header *)malloc(sizeof(mem_rec_header));
+               if (!temp_mem_rh) {
+                       // fprintf( stderr, "jpilot_read_db_file(): Out of memory 1\n" );
+                       retVal = MGU_OO_MEMORY;
+                       break;
+               }
+
+               temp_mem_rh->next = NULL;
+               temp_mem_rh->rec_num = i;
+               temp_mem_rh->offset = offset;
+               temp_mem_rh->attrib = rh.attrib;
+               temp_mem_rh->unique_id = (rh.unique_ID[0]*256+rh.unique_ID[1])*256+rh.unique_ID[2];
+
+               if (mem_rh == NULL) {
+                       mem_rh = temp_mem_rh;
+                       last_mem_rh = temp_mem_rh;
+               }
+               else {
+                       last_mem_rh->next = temp_mem_rh;
+                       last_mem_rh = temp_mem_rh;
+               }
+
+       }       // for( ;; )
+
+       temp_mem_rh = mem_rh;
+
+       if (num_records) {
+               if (out_of_order) {
+                       find_next_offset(mem_rh, 0, &next_offset, &attrib, &unique_id);
+               }
+               else {
+                       if (mem_rh) {
+                               next_offset = mem_rh->offset;
+                               attrib = mem_rh->attrib;
+                               unique_id = mem_rh->unique_id;
+                       }
+               }
+               fseek(in, next_offset, SEEK_SET);
+
+               // Now go load all records              
+               while(!feof(in)) {
+                       struct CategoryAppInfo *cat = & ai->category;
+                       fpos = ftell(in);
+                       if (out_of_order) {
+                               find_next_offset(mem_rh, fpos, &next_offset, &attrib, &unique_id);
+                       }
+                       else {
+                               next_offset = 0xFFFFFF;
+                               if (temp_mem_rh) {
+                                       attrib = temp_mem_rh->attrib;
+                                       unique_id = temp_mem_rh->unique_id;
+                                       cat_id = attrib & 0x0F;
+                                       if (temp_mem_rh->next) {
+                                               temp_mem_rh = temp_mem_rh->next;
+                                               next_offset = temp_mem_rh->offset;
+                                       }
+                               }
+                       }
+                       rec_size = next_offset - fpos;
+
+                       buf = ( char * ) malloc(rec_size);
+                       if (!buf) break;
+                       num = fread(buf, rec_size, 1, in);
+                       if ((num != 1)) {
+                               if (ferror(in)) {
+                                       // fprintf( stderr, "Error reading %s 5\n", pilotFile );
+                                       free(buf);
+                                       retVal = MGU_ERROR_READ;
+                                       break;
+                               }
+                       }
+
+                       // Retrieve address
+                       inum = unpack_Address( & addr, buf, rec_size );
+                       if( inum > 0 ) {
+                               addrEnt = addr.entry;
+
+                               *fullName = *bufEMail = '\0';
+                               if( addrEnt[ IND_LABEL_FIRSTNAME ] ) {
+                                       strcat( fullName, addrEnt[ IND_LABEL_FIRSTNAME ] );
+                               }
+
+                               if( addrEnt[ IND_LABEL_LASTNAME ] ) {
+                                       strcat( fullName, " " );
+                                       strcat( fullName, addrEnt[ IND_LABEL_LASTNAME ] );
+                               }
+                               g_strchug( fullName );
+                               g_strchomp( fullName );
+                               extID = g_strdup_printf( "%d", unique_id );
+
+                               // Add entry for each email address listed under phone labels.
+                               indPhoneLbl = addr.phoneLabel;
+                               for( k = 0; k < JPILOT_NUM_ADDR_PHONE; k++ ) {
+                                       int ind;
+                                       ind = indPhoneLbl[k];
+                                       // fprintf( stdout, "%d : %d : %20s : %s\n", k, ind, ai->phoneLabels[ind], addrEnt[3+k] );
+                                       if( indPhoneLbl[k] == IND_PHONE_EMAIL ) {
+                                               labelEntry = addrEnt[ OFFSET_PHONE_LABEL + k ];
+                                               if( labelEntry ) {
+                                                       strcpy( bufEMail, labelEntry );
+                                                       g_strchug( bufEMail );
+                                                       g_strchomp( bufEMail );
+
+                                                       addrItem = mgu_create_address();
+                                                       addrItem->name = g_strdup( fullName );
+                                                       addrItem->address = g_strdup( bufEMail );
+                                                       addrItem->remarks = g_strdup( "" );
+                                                       addrItem->externalID = g_strdup( extID );
+                                                       addrItem->categoryID = cat_id;
+                                                       mgu_add_cache( pilotFile->addressCache, addrItem );
+                                               }
+                                       }
+                               }
+
+                               // Add entry for each custom label
+                               node = pilotFile->labelInd;
+                               while( node ) {
+                                       gint ind;
+                                       ind = GPOINTER_TO_INT( node->data );
+                                       if( ind > -1 ) {
+                                               // fprintf( stdout, "%d : %20s : %s\n", ind, ai->labels[ind], addrEnt[ind] );
+                                               labelEntry = addrEnt[ind];
+                                               if( labelEntry ) {
+                                                       strcpy( bufEMail, labelEntry );
+                                                       g_strchug( bufEMail );
+                                                       g_strchomp( bufEMail );
+
+                                                       addrItem = mgu_create_address();
+                                                       addrItem->name = g_strdup( fullName );
+                                                       addrItem->address = g_strdup( bufEMail );
+                                                       addrItem->remarks = g_strdup( ai->labels[ind] );
+                                                       addrItem->externalID = g_strdup( extID );
+                                                       addrItem->categoryID = cat_id;
+                                                       mgu_add_cache( pilotFile->addressCache, addrItem );
+                                               }
+
+                                       }
+
+                                       node = g_slist_next( node );
+                               }
+
+                               g_free( extID );
+                               extID = NULL;
+                       }
+                       recs_returned++;
+               }
+       }
+       fclose(in);
+       free_mem_rec_header(&mem_rh);
+       return retVal;
+}
+
+/*
+* Read metadata from file.
+*/
+gint jpilot_read_metadata( JPilotFile *pilotFile ) {
+       gint retVal;
+       unsigned int rec_size;
+       unsigned char *buf;
+       int num;
+
+       g_return_if_fail( pilotFile != NULL );
+
+       pilotFile->readMetadata = FALSE;
+
+       // Read file info
+       retVal = jpilot_get_file_info( pilotFile, &buf, &rec_size);
+       if( retVal != MGU_SUCCESS ) {
+               pilotFile->retVal = retVal;
+               return pilotFile->retVal;
+       }
+
+       num = unpack_AddressAppInfo( &pilotFile->addrInfo, buf, rec_size );
+       if( buf ) {
+               free(buf);
+       }
+       if( num <= 0 ) {
+               // fprintf( stderr, "error reading '%s'\n", pilotFile->path );
+               pilotFile->retVal = MGU_ERROR_READ;
+               return pilotFile->retVal;
+       }
+
+       pilotFile->readMetadata = TRUE;
+       pilotFile->retVal = MGU_SUCCESS;
+       return pilotFile->retVal;
+}
+
+/*
+* Setup labels and indexes from metadata.
+* Return: TRUE is setup successfully.
+*/
+gboolean jpilot_setup_labels( JPilotFile *pilotFile ) {
+       gboolean retVal = FALSE;
+       struct AddressAppInfo *ai;
+       GSList *node;
+
+       g_return_if_fail( pilotFile != NULL );
+
+       // Release indexes
+       node = pilotFile->labelInd;
+       while( node ) {
+               node->data = NULL;
+               node = g_slist_next( node );
+       }
+       pilotFile->labelInd = NULL;
+
+       if( pilotFile->readMetadata ) {
+               ai = & pilotFile->addrInfo;
+               node = pilotFile->customLabels;
+               while( node ) {
+                       gchar *lbl, *labelName;
+                       int i;
+                       gint ind;
+                       ind = -1;
+                       lbl = node->data;
+                       for( i = 0; i < JPILOT_NUM_LABELS; i++ ) {
+                               labelName = ai->labels[i];
+                               if( g_strcasecmp( labelName, lbl ) == 0 ) {
+                                       ind = i;
+                                       break;
+                               }
+                       }
+                       pilotFile->labelInd = g_slist_append( pilotFile->labelInd, GINT_TO_POINTER(ind) );
+                       node = g_slist_next( node );
+               }
+               retVal = TRUE;
+       }
+       return retVal;
+}
+
+/*
+* Load list with character strings of label names.
+*/
+GSList *jpilot_load_label( JPilotFile *pilotFile, GSList *labelList ) {
+       int i;
+       g_return_if_fail( pilotFile != NULL );
+       if( pilotFile->readMetadata ) {
+               struct AddressAppInfo *ai = & pilotFile->addrInfo;
+               for( i = 0; i < JPILOT_NUM_LABELS; i++ ) {
+                       gchar *labelName = ai->labels[i];
+                       if( labelName ) {
+                               labelList = g_slist_append( labelList, g_strdup( labelName ) );
+                       }
+                       else {
+                               labelList = g_slist_append( labelList, g_strdup( "" ) );
+                       }
+               }
+       }
+       return labelList;
+}
+
+/*
+* Return category name for specified category ID.
+* Enter:  Category ID.
+* Return: Name, or empty string if not invalid ID. Name should be g_free() when done.
+*/
+gchar *jpilot_get_category_name( JPilotFile *pilotFile, gint catID ) {
+       gchar *catName = NULL;
+       g_return_if_fail( pilotFile != NULL );
+       if( pilotFile->readMetadata ) {
+               struct AddressAppInfo *ai = & pilotFile->addrInfo;
+               struct CategoryAppInfo *cat = & ai->category;
+               if( catID < 0 || catID > JPILOT_NUM_CATEG ) {
+               }
+               else {
+                       catName = g_strdup( cat->name[catID] );
+               }
+       }
+       if( ! catName ) catName = g_strdup( "" );
+       return catName;
+}
+
+/*
+* Load list with character strings of phone label names.
+*/
+GSList *jpilot_load_phone_label( JPilotFile *pilotFile, GSList *labelList ) {
+       int i;
+       g_return_if_fail( pilotFile != NULL );
+       if( pilotFile->readMetadata ) {
+               struct AddressAppInfo *ai = & pilotFile->addrInfo;
+               for( i = 0; i < JPILOT_NUM_PHONELABELS; i++ ) {
+                       gchar   *labelName = ai->phoneLabels[i];
+                       if( labelName ) {
+                               labelList = g_slist_append( labelList, g_strdup( labelName ) );
+                       }
+                       else {
+                               labelList = g_slist_append( labelList, g_strdup( "" ) );
+                       }
+               }
+       }
+       return labelList;
+}
+
+/*
+* Load list with character strings of label names. Only none blank names
+* are loaded.
+*/
+GList *jpilot_load_custom_label( JPilotFile *pilotFile, GList *labelList ) {
+       int i;
+       g_return_if_fail( pilotFile != NULL );
+
+       if( pilotFile->readMetadata ) {
+               struct AddressAppInfo *ai = & pilotFile->addrInfo;
+               for( i = 0; i < NUM_CUSTOM_LABEL; i++ ) {
+                       gchar *labelName = ai->labels[i+IND_CUSTOM_LABEL];
+                       if( labelName ) {
+                               g_strchomp( labelName );
+                               g_strchug( labelName );
+                               if( *labelName != '\0' ) {
+                                       labelList = g_list_append( labelList, g_strdup( labelName ) );
+                               }
+                       }
+               }
+       }
+       return labelList;
+}
+
+/*
+* Load list with character strings of category names.
+*/
+GSList *jpilot_get_category_list( JPilotFile *pilotFile ) {
+       GSList *catList = NULL;
+       int i;
+       g_return_if_fail( pilotFile != NULL );
+       if( pilotFile->readMetadata ) {
+               struct AddressAppInfo *ai = & pilotFile->addrInfo;
+               struct CategoryAppInfo *cat = & ai->category;
+               for( i = 0; i < JPILOT_NUM_CATEG; i++ ) {
+                       gchar *catName = cat->name[i];
+                       if( catName ) {
+                               catList = g_slist_append( catList, g_strdup( catName ) );
+                       }
+                       else {
+                               catList = g_slist_append( catList, g_strdup( "" ) );
+                       }
+               }
+       }
+       return catList;
+}
+
+/*
+* Free category address list.
+*/
+void jpilot_free_address_list( JPilotFile *pilotFile ) {
+       GList *addrList;
+       g_return_if_fail( pilotFile != NULL );
+
+       addrList = pilotFile->catAddrList;
+       while( addrList ) {
+               JPilotCategory *jpcat = addrList->data;
+               mgu_free_address( jpcat->category );
+               mgu_free_address_list( jpcat->addressList );
+               jpcat->category = NULL;
+               jpcat->addressList = NULL;
+               jpcat->count = 0;
+               g_free( jpcat );
+               jpcat = NULL;
+               addrList->data = NULL;
+               addrList = g_list_next( addrList );
+       }
+       pilotFile->catAddrList = NULL;
+}
+
+/*
+* Move data from address list and group into category address list.
+*/
+void jpilot_load_category_items( JPilotFile *pilotFile ) {
+       JPilotCategory *jpcat[ 1 + JPILOT_NUM_CATEG ];
+       struct AddressAppInfo *ai = & pilotFile->addrInfo;
+       struct CategoryAppInfo *cat = & ai->category;
+       AddressItem *itemCat;
+       GList *addrList;
+       int i;
+
+       // Create array for data by category
+       for( i = 0; i < 1 + JPILOT_NUM_CATEG; i++ ) {
+               itemCat = mgu_create_address_item( ADDR_CATEGORY );
+               itemCat->categoryID = i;
+               jpcat[i] = g_new( JPilotCategory, 1 );
+               jpcat[i]->category = itemCat;
+               jpcat[i]->addressList = NULL;
+               jpcat[i]->count = 0;
+               if( i < JPILOT_NUM_CATEG ) {
+                       gchar *catName = cat->name[i];
+                       if( catName && strlen( catName ) > 0 ) {
+                               itemCat->name = g_strdup( catName );
+                       }
+               }
+       }
+
+       // Process address list moving data to category
+       addrList = pilotFile->addressCache->addressList;
+       while( addrList ) {
+               GList *addrLink;
+               AddressItem *item;
+               item = addrList->data;
+               i = item->categoryID;
+               if( i < 0 || i > JPILOT_NUM_CATEG ) i = JPILOT_NUM_CATEG; // Position at end of array
+
+               // Add item to category list
+               addrLink = jpcat[i]->addressList;
+               addrLink = g_list_append( addrLink, item );
+               jpcat[i]->addressList = addrLink;
+               jpcat[i]->count++;
+
+               // Clear from cache list
+               addrList->data = NULL;
+               addrList = g_list_next( addrList );
+       }
+
+       // Remove entries from address cache
+       mgu_clear_cache_null( pilotFile->addressCache );
+
+/*
+       printf( "dump jpcArray[]...\n" );
+       for( i = 0; i < 1 + JPILOT_NUM_CATEG; i++ ) {
+               jpilot_print_category( jpcat[i], stdout );
+       }
+*/
+
+       // Free up existing category address list
+       jpilot_free_address_list( pilotFile );
+
+       // Move categories from array to category address list
+       addrList = NULL;
+       for( i = 0; i < 1 + JPILOT_NUM_CATEG; i++ ) {
+               if( jpcat[i]->count > 0 ) {
+                       itemCat = jpcat[i]->category;
+                       if( ! itemCat->name ) {
+                               // Create a category name
+                               itemCat->name = g_strdup_printf( "? %d", itemCat->categoryID );
+                       }
+               }
+               addrList = g_list_append( addrList, jpcat[i] );
+               jpcat[i] = NULL;
+       }
+       pilotFile->catAddrList = addrList;
+
+       // jpilot_print_category_list( pilotFile->catAddrList, stdout );
+
+}
+
+/*
+* Build linked list of address items for each category.
+*/
+GList *jpilot_build_category_list( JPilotFile *pilotFile ) {
+       GList *catList = NULL;
+       GList *node;
+       node = pilotFile->catAddrList;
+       while( node ) {
+               JPilotCategory *jpcat = node->data;
+               AddressItem *catItem = jpcat->category;
+
+               catItem = jpcat->category;
+               if( jpcat->count > 0 || catItem->name ) {
+                       AddressItem *itemNew = mgu_copy_address_item( catItem );
+                       if( itemNew ) {
+                               catList = g_list_append( catList, itemNew );
+                       }
+               }
+               node = g_list_next( node );
+       }
+       mgu_free_address_list( pilotFile->categoryList );
+       pilotFile->categoryList = catList;
+       // mgu_print_address_list( catList, stdout );
+       return catList;
+}
+
+// ============================================================================================
+/*
+* Read file into list. Main entry point
+* Return: TRUE if file read successfully.
+*/
+// ============================================================================================
+gint jpilot_read_data( JPilotFile *pilotFile ) {
+       g_return_if_fail( pilotFile != NULL );
+       pilotFile->retVal = MGU_SUCCESS;
+       if( mgu_check_file( pilotFile->addressCache, pilotFile->path ) ) {
+               mgu_clear_cache( pilotFile->addressCache );
+               jpilot_read_metadata( pilotFile );
+               if( pilotFile->retVal == MGU_SUCCESS ) {
+                       jpilot_setup_labels( pilotFile );
+                       pilotFile->retVal = jpilot_read_cache( pilotFile );
+                       if( pilotFile->retVal == MGU_SUCCESS ) {
+                               mgu_mark_cache( pilotFile->addressCache, pilotFile->path );
+                               pilotFile->addressCache->modified = FALSE;
+                               pilotFile->addressCache->dataRead = TRUE;
+                       }
+                       jpilot_load_category_items( pilotFile );
+                       jpilot_build_category_list( pilotFile );
+               }
+       }
+       return pilotFile->retVal;
+}
+
+/*
+* Return linked list of address items for loaded category names.
+*/
+GList *jpilot_get_category_items( JPilotFile *pilotFile ) {
+       g_return_if_fail( pilotFile != NULL );
+       return pilotFile->categoryList;
+}
+
+/*
+* Return address list for specified category.
+*/
+GList *jpilot_get_address_list_cat( JPilotFile *pilotFile, const gint catID ) {
+       GList *addrList = NULL, *node;
+       g_return_if_fail( pilotFile != NULL );
+
+       node = pilotFile->catAddrList;
+       while( node ) {
+               JPilotCategory *jpcat = node->data;
+               AddressItem *catItem = jpcat->category;
+               if( catItem->categoryID == catID ) {
+                       addrList = jpcat->addressList;
+                       break;
+               }
+               node = g_list_next( node );
+       }
+       return addrList;
+}
+
+/*
+* Return linked list of address items.
+*/
+GList *jpilot_get_address_list( JPilotFile *pilotFile ) {
+       g_return_if_fail( pilotFile != NULL );
+       return pilotFile->addressCache->addressList;
+}
+
+/*
+* Check label list for specified label.
+*/
+gint jpilot_check_label( struct AddressAppInfo *ai, gchar *lblCheck ) {
+       int i;
+       gchar   *lblName;
+       if( lblCheck == NULL ) return -1;
+       if( strlen( lblCheck ) < 1 ) return -1;
+       for( i = 0; i < JPILOT_NUM_LABELS; i++ ) {
+               lblName = ai->labels[i];
+               if( lblName ) {
+                       if( strlen( lblName ) ) {
+                               if( g_strcasecmp( lblName, lblCheck ) == 0 ) return i;
+                       }
+               }
+       }
+       return -2;
+}
+
+/*
+* Validate that all parameters specified.
+* Return: TRUE if data is good.
+*/
+gboolean jpilot_validate( const JPilotFile *pilotFile ) {
+       gboolean retVal;
+       g_return_if_fail( pilotFile != NULL );
+
+       retVal = TRUE;
+       if( pilotFile->path ) {
+               if( strlen( pilotFile->path ) < 1 ) retVal = FALSE;
+       }
+       else {
+               retVal = FALSE;
+       }
+       if( pilotFile->name ) {
+               if( strlen( pilotFile->name ) < 1 ) retVal = FALSE;
+       }
+       else {
+               retVal = FALSE;
+       }
+       return retVal;
+}
+
+#define WORK_BUFLEN 1024
+
+/*
+* Attempt to find a valid JPilot file.
+* Return: Filename, or home directory if not found, or empty string if
+* no home. Filename should be g_free() when done.
+*/
+gchar *jpilot_find_pilotdb( void ) {
+       gchar *homedir;
+       gchar str[ WORK_BUFLEN ];
+       gint len;
+       FILE *fp;
+
+       homedir = g_get_home_dir();
+       if( ! homedir ) return g_strdup( "" );
+
+       strcpy( str, homedir );
+       len = strlen( str );
+       if( len > 0 ) {
+               if( str[ len-1 ] != G_DIR_SEPARATOR ) {
+                       str[ len ] = G_DIR_SEPARATOR;
+                       str[ ++len ] = '\0';
+               }
+       }
+       strcat( str, JPILOT_DBHOME_DIR );
+       strcat( str, G_DIR_SEPARATOR_S );
+       strcat( str, JPILOT_DBHOME_FILE );
+
+       // Attempt to open\e
+       if( ( fp = fopen( str, "r" ) ) != NULL ) {
+               fclose( fp );
+       }
+       else {
+               // Truncate filename
+               str[ len ] = '\0';
+       }
+       return g_strdup( str );
+}
+
+/*
+* Attempt to read file, testing for valid JPilot format.
+* Return: TRUE if file appears to be valid format.
+*/
+gint jpilot_test_read_file( const gchar *fileSpec ) {
+       JPilotFile *pilotFile;
+       gint retVal;
+       if( fileSpec ) {
+               pilotFile = jpilot_create_path( fileSpec );
+               retVal = jpilot_read_metadata( pilotFile );
+               jpilot_free( pilotFile );
+               pilotFile = NULL;
+       }
+       else {
+               retVal = MGU_NO_FILE;
+       }
+       return retVal;
+}
+
+/*
+* Check whether label is in custom labels.
+* Return: TRUE if found.
+*/
+gboolean jpilot_test_custom_label( JPilotFile *pilotFile, const gchar *labelName ) {
+       gboolean retVal;
+       GSList *node;
+       g_return_if_fail( pilotFile != NULL );
+
+       retVal = FALSE;
+       if( labelName ) {
+               node = pilotFile->customLabels;
+               while( node ) {
+                       if( g_strcasecmp( labelName, node->data ) == 0 ) {
+                               retVal = TRUE;
+                               break;
+                       }
+                       node = g_slist_next( node );
+               }
+       }
+       return retVal;
+}
+
+/*
+* Test whether pilot link library installed.
+* Return: TRUE if library available.
+*/
+gboolean jpilot_test_pilot_lib() {
+       void *handle, *fun;
+
+       handle = dlopen( PILOT_LINK_LIB_NAME, RTLD_LAZY );
+       if( ! handle ) {
+               return FALSE;
+       }
+
+       // Test for symbols we need
+       fun = dlsym( handle, "unpack_Address" );
+       if( ! fun ) {
+               dlclose( handle );
+               return FALSE;
+       }
+
+       fun = dlsym( handle, "unpack_AddressAppInfo" );
+       if( ! fun ) {
+               dlclose( handle );
+               return FALSE;
+       }
+       dlclose( handle );
+       return TRUE;
+}
+
+#endif /* USE_JPILOT */
+
+/*
+* End of Source.
+*/
diff --git a/src/jpilot.h b/src/jpilot.h
new file mode 100644 (file)
index 0000000..dd28795
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Definitions for accessing JPilot database files.
+ * JPilot is Copyright(c) by Judd Montgomery.
+ * Visit http://www.jpilot.org for more details.
+ */
+
+#ifndef __JPILOT_H__
+#define __JPILOT_H__
+
+#ifdef USE_JPILOT
+
+#include <pi-address.h>
+
+#include <time.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include "mgutils.h"
+
+typedef struct _JPilotFile JPilotFile;
+
+struct _JPilotFile {
+       gchar        *name;
+       FILE         *file;
+       gchar        *path;
+       AddressCache *addressCache;
+       struct AddressAppInfo addrInfo;
+       gboolean     readMetadata;
+       GSList       *customLabels;
+       GSList       *labelInd;
+       gint         retVal;
+       GList        *categoryList;
+       GList        *catAddrList;
+};
+
+// Limits
+#define JPILOT_NUM_LABELS      22      // Number of labels
+#define JPILOT_NUM_PHONELABELS  8      // Number of phone number labels
+#define JPILOT_NUM_CATEG       16      // Number of categories
+#define JPILOT_LEN_LABEL       15      // Max length of label
+#define JPILOT_LEN_CATEG       15      // Max length of category
+#define JPILOT_NUM_ADDR_PHONE   5      // Number of phone entries a person can have
+
+// Shamelessly copied from JPilot (libplugin.h)
+typedef struct {
+       unsigned char db_name[32];
+       unsigned char flags[2];
+       unsigned char version[2];
+       unsigned char creation_time[4];
+       unsigned char modification_time[4];
+       unsigned char backup_time[4];
+       unsigned char modification_number[4];
+       unsigned char app_info_offset[4];
+       unsigned char sort_info_offset[4];
+       unsigned char type[4];/*Database ID */
+       unsigned char creator_id[4];/*Application ID */
+       unsigned char unique_id_seed[4];
+       unsigned char next_record_list_id[4];
+       unsigned char number_of_records[2];
+} RawDBHeader;
+
+// Shamelessly copied from JPilot (libplugin.h)
+typedef struct {
+       char db_name[32];
+       unsigned int flags;
+       unsigned int version;
+       time_t creation_time;
+       time_t modification_time;
+       time_t backup_time;
+       unsigned int modification_number;
+       unsigned int app_info_offset;
+       unsigned int sort_info_offset;
+       char type[5];/*Database ID */
+       char creator_id[5];/*Application ID */
+       char unique_id_seed[5];
+       unsigned int next_record_list_id;
+       unsigned int number_of_records;
+} DBHeader;
+
+// Shamelessly copied from JPilot (libplugin.h)
+typedef struct {
+       unsigned char Offset[4];  /*4 bytes offset from BOF to record */
+       unsigned char attrib;
+       unsigned char unique_ID[3];
+} record_header;
+
+// Shamelessly copied from JPilot (libplugin.h)
+typedef struct mem_rec_header_s {
+       unsigned int rec_num;
+       unsigned int offset;
+       unsigned int unique_id;
+       unsigned char attrib;
+       struct mem_rec_header_s *next;
+} mem_rec_header;
+
+// Shamelessly copied from JPilot (libplugin.h)
+#define SPENT_PC_RECORD_BIT    256
+
+typedef enum {
+       PALM_REC = 100L,
+       MODIFIED_PALM_REC = 101L,
+       DELETED_PALM_REC = 102L,
+       NEW_PC_REC = 103L,
+       DELETED_PC_REC = SPENT_PC_RECORD_BIT + 104L,
+       DELETED_DELETED_PALM_REC = SPENT_PC_RECORD_BIT + 105L
+} PCRecType;
+
+// Shamelessly copied from JPilot (libplugin.h)
+typedef struct {
+       PCRecType rt;
+       unsigned int unique_id;
+       unsigned char attrib;
+       void *buf;
+       int size;
+} buf_rec;
+
+/* Function prototypes */
+JPilotFile *jpilot_create();
+JPilotFile *jpilot_create_path( const gchar *path );
+void jpilot_free( JPilotFile *pilotFile );
+void jpilot_force_refresh( JPilotFile *pilotFile );
+gboolean jpilot_get_modified( JPilotFile *pilotFile );
+void jpilot_print_file( JPilotFile *jpilotFile, FILE *stream );
+void jpilot_print_list( GSList *list, FILE *stream );
+gint jpilot_read_file( JPilotFile *pilotFile );
+
+GList *jpilot_get_address_list( JPilotFile *pilotFile );
+GSList *jpilot_load_label( JPilotFile *pilotFile, GSList *labelList );
+GSList *jpilot_get_category_list( JPilotFile *pilotFile );
+gchar *jpilot_get_category_name( JPilotFile *pilotFile, gint catID );
+GSList *jpilot_load_phone_label( JPilotFile *pilotFile, GSList *labelList );
+GList *jpilot_load_custom_label( JPilotFile *pilotFile, GList *labelList );
+
+GList *jpilot_get_category_items( JPilotFile *pilotFile );
+GList *jpilot_get_address_list_cat( JPilotFile *pilotFile, const gint catID );
+
+void jpilot_set_file( JPilotFile* pilotFile, const gchar *path );
+gboolean jpilot_validate( const JPilotFile *pilotFile );
+gchar *jpilot_find_pilotdb( void );
+gint jpilot_test_read_data( const gchar *fileSpec );
+
+void jpilot_clear_custom_labels( JPilotFile *pilotFile );
+void jpilot_add_custom_label( JPilotFile *pilotFile, const gchar *labelName );
+GList *jpilot_get_custom_labels( JPilotFile *pilotFile );
+gboolean jpilot_test_custom_label( JPilotFile *pilotFile, const gchar *labelName );
+gboolean jpilot_setup_labels( JPilotFile *pilotFile );
+gboolean jpilot_test_pilot_lib();
+
+#endif /* USE_JPILOT */
+
+#endif /* __JPILOT_H__ */
+
diff --git a/src/mgutils.c b/src/mgutils.c
new file mode 100644 (file)
index 0000000..68595dd
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * General functions for create common address book entries.
+ */
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include "addressitem.h"
+#include "mgutils.h"
+
+/*
+* Create new address item.
+*/
+AddressItem *mgu_create_address_item( AddressObjectType type ) {
+       AddressItem *item;
+       item = g_new( AddressItem, 1 );
+       ADDRESS_OBJECT(item)->type = type;
+       item->name = NULL;
+       item->address = NULL;
+       item->remarks = NULL;
+       item->externalID = NULL;
+       item->categoryID = ADDRESS_ITEM_CAT_UNKNOWN;
+       return item;
+}
+
+/*
+* Create new address item.
+*/
+AddressItem *mgu_create_address( void ) {
+       AddressItem *item;
+       item = g_new( AddressItem, 1 );
+       ADDRESS_OBJECT(item)->type = ADDR_ITEM;
+       item->name = NULL;
+       item->address = NULL;
+       item->remarks = NULL;
+       item->externalID = NULL;
+       item->categoryID = ADDRESS_ITEM_CAT_UNKNOWN;
+       return item;
+}
+
+/*
+* Create copy of specified address item.
+*/
+AddressItem *mgu_copy_address_item( AddressItem *item ) {
+       AddressItem *itemNew = NULL;
+       if( item ) {
+               itemNew = mgu_create_address_item( ADDRESS_OBJECT(item)->type );
+               itemNew->name = g_strdup( item->name );
+               itemNew->address = g_strdup( item->address );
+               itemNew->remarks = g_strdup( item->remarks );
+               itemNew->externalID = g_strdup( item->externalID );
+               itemNew->categoryID = item->categoryID;
+       }
+       return itemNew;
+}
+
+/*
+* Free address item.
+*/
+void mgu_free_address( AddressItem *item ) {
+       g_return_if_fail( item != NULL );
+
+       /* Free internal stuff */
+       g_free( item->name );
+       g_free( item->address );
+       g_free( item->remarks );
+       g_free( item->externalID );
+       item->name = NULL;
+       item->address = NULL;
+       item->remarks = NULL;
+       item->externalID = NULL;
+       item->categoryID = ADDRESS_ITEM_CAT_UNKNOWN;
+
+       /* Now release item */
+       g_free( item );
+}
+
+/*
+* Refresh internal variables to force a reload.
+*/
+void mgu_refresh_cache( AddressCache *cache ) {
+       cache->dataRead = FALSE;
+       cache->modified = TRUE;
+       cache->modifyTime = 0;
+}
+
+/*
+* Free up address list.
+*/
+void mgu_free_address_list( GList *addrList ) {
+       AddressItem *item;
+       GList *node;
+
+       /* Free data in the list */
+       node = addrList;
+       while( node ) {
+               item = node->data;
+               mgu_free_address( item );
+               node->data = NULL;
+               node = g_list_next( node );
+       }
+
+       /* Now release linked list object */
+       g_list_free( addrList );
+}
+
+/*
+* Clear the cache.
+*/
+void mgu_clear_cache( AddressCache *cache ) {
+       AddressItem *item;
+       GList *node;
+       g_return_if_fail( cache != NULL );
+
+       /* Free data in the list */
+       mgu_free_address_list( cache->addressList );
+       cache->addressList = NULL;
+       mgu_refresh_cache( cache );
+}
+
+/*
+* Clear the cache by setting pointers to NULL and free list.
+* Note that individual items are not free'd.
+*/
+void mgu_clear_cache_null( AddressCache *cache ) {
+       GList *node;
+       g_return_if_fail( cache != NULL );
+
+       /* Free data in the list */
+       node = cache->addressList;
+       while( node ) {
+               node->data = NULL;
+               node = g_list_next( node );
+       }
+
+       /* Now release linked list object */
+       g_list_free( cache->addressList );
+       cache->addressList = NULL;
+}
+
+/*
+* Create new cache.
+*/
+AddressCache *mgu_create_cache( void ) {
+       AddressCache *cache;
+       cache = g_new( AddressCache, 1 );
+       cache->addressList = NULL;
+       cache->dataRead = FALSE;
+       cache->modified = FALSE;
+       cache->modifyTime = 0;
+       return cache;
+}
+
+/*
+* Create new address item.
+*/
+void mgu_free_cache( AddressCache *cache ) {
+       mgu_clear_cache( cache );
+       cache->addressList = NULL;
+}
+
+/*
+* Print address item.
+*/
+void mgu_print_address( AddressItem *item, FILE *stream ) {
+       g_return_if_fail( item != NULL );
+       fprintf( stream, "addr item:\n" );
+       fprintf( stream, "\tname: '%s'\n", item->name );
+       fprintf( stream, "\taddr: '%s'\n", item->address );
+       fprintf( stream, "\trems: '%s'\n", item->remarks );
+       fprintf( stream, "\tid  : '%s'\n", item->externalID );
+       fprintf( stream, "\tcatg: '%d'\n", item->categoryID );
+       fprintf( stream, "---\n" );
+}
+
+/*
+* Print linked list containing address item(s).
+*/
+void mgu_print_address_list( GList *addrList, FILE *stream ) {
+       GList *node;
+       g_return_if_fail( addrList != NULL );
+
+       /* Now process the list */
+       node = addrList;
+       while( node ) {
+               gpointer *gptr = node->data;
+               AddressItem *item = ( AddressItem * ) gptr;
+               mgu_print_address( item, stream );              
+               node = g_list_next( node );
+       }
+}
+
+/*
+* Print address cache.
+*/
+void mgu_print_cache( AddressCache *cache, FILE *stream ) {
+       GList *node;
+       g_return_if_fail( cache != NULL );
+       fprintf( stream, "AddressCache:\n" );
+       fprintf( stream, "modified : %s\n", cache->modified ? "yes" : "no" );
+       fprintf( stream, "data read: %s\n", cache->dataRead ? "yes" : "no" );
+
+       /* Now process the list */
+       node = cache->addressList;
+       while( node ) {
+               gpointer *gptr;
+               AddressItem *item;
+               gptr = node->data;
+               item = ( AddressItem * ) gptr;
+               mgu_print_address( item, stream );              
+               node = g_list_next( node );
+       }
+}
+
+/*
+* Dump linked list of character strings (for debug).
+*/
+void mgu_print_list( GSList *list, FILE *stream ) {
+       GSList *node = list;
+       while( node ) {
+               fprintf( stream, "\t- >%s<\n", node->data );
+               node = g_slist_next( node );
+       }
+}
+
+/*
+* Dump linked list of character strings (for debug).
+*/
+void mgu_print_dlist( GList *list, FILE *stream ) {
+       GList *node = list;
+       while( node ) {
+               fprintf( stream, "\t- >%s<\n", node->data );
+               node = g_list_next( node );
+       }
+}
+
+/*
+* Check whether file has changed by comparing with cache.
+* return:      TRUE if file has changed.
+*/
+gboolean mgu_check_file( AddressCache *cache, gchar *path ) {
+       gboolean retVal;
+       struct stat filestat;
+       retVal = TRUE;
+       if( path ) {
+               if( 0 == lstat( path, &filestat ) ) {
+                       if( filestat.st_mtime == cache->modifyTime ) retVal = FALSE;
+               }
+       }
+       return retVal;
+}
+
+/*
+* Save file time to cache.
+* return:      TRUE if time marked.
+*/
+gboolean mgu_mark_cache( AddressCache *cache, gchar *path ) {
+       gboolean retVal = FALSE;
+       struct stat filestat;
+       if( path ) {
+               if( 0 == lstat( path, &filestat ) ) {
+                       cache->modifyTime = filestat.st_mtime;
+                       retVal = TRUE;
+               }
+       }
+       return retVal;
+}
+
+/*
+* Free linked list of character strings.
+*/
+void mgu_free_list( GSList *list ) {
+       GSList *node = list;
+       while( node ) {
+               g_free( node->data );
+               node->data = NULL;
+               node = g_slist_next( node );
+       }
+       g_slist_free( list );
+}
+
+/*
+* Free linked list of character strings.
+*/
+void mgu_free_dlist( GList *list ) {
+       GList *node = list;
+       while( node ) {
+               g_free( node->data );
+               node->data = NULL;
+               node = g_list_next( node );
+       }
+       g_list_free( list );
+}
+
+/*
+* Coalesce linked list of characaters into one long string.
+*/
+gchar *mgu_list_coalesce( GSList *list ) {
+       gchar *str = NULL;
+       gchar *buf = NULL;
+       gchar *start = NULL;
+       GSList *node = NULL;
+       gint len;
+
+       if( ! list ) return NULL;
+
+       // Calculate maximum length of text
+       len = 0;
+       node = list;
+       while( node ) {
+               str = node->data;
+               len += 1 + strlen( str );
+               node = g_slist_next( node );
+       }
+
+       // Create new buffer.
+       buf = g_new( gchar, len+1 );
+       start = buf;
+       node = list;
+       while( node ) {
+               str = node->data;
+               len = strlen( str );
+               strcpy( start, str );
+               start += len;
+               node = g_slist_next( node );
+       }
+       return buf;
+}
+
+/*
+* Add address item to cache.
+*/
+void mgu_add_cache( AddressCache *cache, AddressItem *addrItem ) {
+       cache->addressList = g_list_append( cache->addressList, addrItem );
+       cache->modified = TRUE;
+}
+
+struct mgu_error_entry {
+       gint    e_code;
+       gchar   *e_reason;
+};
+
+static const struct mgu_error_entry mgu_error_list[] = {
+       { MGU_SUCCESS,          "Success" },
+       { MGU_BAD_ARGS,         "Bad arguments" },
+       { MGU_NO_FILE,          "File not specified" },
+       { MGU_OPEN_FILE,        "Error opening file" },
+       { MGU_ERROR_READ,       "Error reading file" },
+       { MGU_EOF,              "End of file encountered" },
+       { MGU_OO_MEMORY,        "Error allocating memory" },
+       { MGU_BAD_FORMAT,       "Bad file format" },
+       { MGU_LDAP_CONNECT,     "Error connecting to LDAP server" },
+       { MGU_LDAP_INIT,        "Error initializing LDAP" },
+       { MGU_LDAP_BIND,        "Error binding to LDAP server" },
+       { MGU_LDAP_SEARCH,      "Error searching LDAP database" },
+       { MGU_LDAP_TIMEOUT,     "Timeout performing LDAP operation" },
+       { MGU_LDAP_CRITERIA,    "Error in LDAP search criteria" },
+       { MGU_LDAP_CRITERIA,    "Error in LDAP search criteria" },
+       { MGU_LDAP_NOENTRIES,   "No LDAP entries found for search criteria" },
+       { -999,                 NULL }
+};
+
+static const struct mgu_error_entry *mgu_error_find( gint err ) {
+       gint i;
+       for ( i = 0; mgu_error_list[i].e_code != -999; i++ ) {
+               if ( err == mgu_error_list[i].e_code )
+                       return & mgu_error_list[i];
+       }
+       return NULL;
+}
+
+/*
+* Return error message for specified error code.
+*/
+gchar *mgu_error2string( gint err ) {
+       const struct mgu_error_entry *e;
+       e = mgu_error_find( err );
+       return ( e != NULL ) ? e->e_reason : "Unknown error";
+}
+
+/*
+* End of Source.
+*/
diff --git a/src/mgutils.h b/src/mgutils.h
new file mode 100644 (file)
index 0000000..04ea7c1
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * General definitions for common address book entries.
+ */
+
+#ifndef __MGUTILS_H__
+#define __MGUTILS_H__
+
+#include <time.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include "addressitem.h"
+
+// Error codes
+#define MGU_SUCCESS        0
+#define MGU_BAD_ARGS       -1
+#define MGU_NO_FILE        -2
+#define MGU_OPEN_FILE      -3
+#define MGU_ERROR_READ     -4
+#define MGU_EOF            -5
+#define MGU_OO_MEMORY      -6
+#define MGU_BAD_FORMAT     -7
+#define MGU_LDAP_CONNECT   -8
+#define MGU_LDAP_INIT      -9
+#define MGU_LDAP_BIND      -10
+#define MGU_LDAP_SEARCH    -11
+#define MGU_LDAP_TIMEOUT   -12
+#define MGU_LDAP_CRITERIA  -13
+#define MGU_LDAP_NOENTRIES -14
+
+// Address cache
+typedef struct _AddressCache AddressCache;
+struct _AddressCache {
+       GList    *addressList;
+       gboolean dataRead;
+       gboolean modified;
+       time_t   modifyTime;
+};
+
+// Function prototypes
+AddressItem *mgu_create_address_item( AddressObjectType type );
+AddressItem *mgu_create_address( void );
+AddressItem *mgu_copy_address_item( AddressItem *item );
+void mgu_free_address( AddressItem *item );
+void mgu_free_address_list( GList *addrList );
+void mgu_refresh_cache( AddressCache *cache );
+void mgu_clear_cache( AddressCache *cache );
+void mgu_clear_cache_null( AddressCache *cache );
+AddressCache *mgu_create_cache( void );
+void mgu_free_cache( AddressCache *cache );
+void mgu_print_address( AddressItem *item, FILE *stream );
+void mgu_print_address_list( GList *addrList, FILE *stream );
+void mgu_print_cache( AddressCache *cache, FILE *stream );
+void mgu_print_list( GSList *list, FILE *stream );
+void mgu_print_dlist( GList *list, FILE *stream );
+gboolean mgu_check_file( AddressCache *cache, gchar *path );
+gboolean mgu_mark_cache( AddressCache *cache, gchar *path );
+void mgu_free_list( GSList *list );
+void mgu_free_dlist( GList *list );
+gchar *mgu_list_coalesce( GSList *list );
+void mgu_add_cache( AddressCache *cache, AddressItem *addrItem );
+gchar *mgu_error2string( gint err );
+
+#endif /* __MGUTILS_H__ */
diff --git a/src/syldap.c b/src/syldap.c
new file mode 100644 (file)
index 0000000..188f8c4
--- /dev/null
@@ -0,0 +1,1056 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Functions necessary to access LDAP servers.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#ifdef USE_LDAP
+
+#include <sys/time.h>
+#include <glib.h>
+#include <ldap.h>
+#include <lber.h>
+#include <pthread.h>
+#include <dlfcn.h>
+
+#include "mgutils.h"
+#include "syldap.h"
+
+/*
+* Specify name to be used.
+*/
+void syldap_set_name( SyldapServer* ldapServer, const gchar *value ) {
+       if( ldapServer->name ) g_free( ldapServer->name );
+       if( value ) ldapServer->name = g_strdup( value );
+       g_strstrip( ldapServer->name );
+}
+
+/*
+* Specify hostname to be used.
+*/
+void syldap_set_host( SyldapServer* ldapServer, const gchar *value ) {
+       mgu_refresh_cache( ldapServer->addressCache );
+
+       if( ldapServer->hostName ) g_free( ldapServer->hostName );
+       if( value ) ldapServer->hostName = g_strdup( value );
+       g_strstrip( ldapServer->hostName );
+}
+
+/*
+* Specify port to be used.
+*/
+void syldap_set_port( SyldapServer* ldapServer, const gint value ) {
+       mgu_refresh_cache( ldapServer->addressCache );
+
+       if( value > 0 ) {
+               ldapServer->port = value;
+       }
+       else {
+               ldapServer->port = SYLDAP_DFL_PORT;
+       }
+}
+
+/*
+* Specify base DN to be used.
+*/
+void syldap_set_base_dn( SyldapServer* ldapServer, const gchar *value ) {
+       mgu_refresh_cache( ldapServer->addressCache );
+
+       if( ldapServer->baseDN ) g_free( ldapServer->baseDN );
+       if( value ) ldapServer->baseDN = g_strdup( value );
+       g_strstrip( ldapServer->baseDN );
+}
+
+/*
+* Specify bind DN to be used.
+*/
+void syldap_set_bind_dn( SyldapServer* ldapServer, const gchar *value ) {
+       mgu_refresh_cache( ldapServer->addressCache );
+
+       if( ldapServer->bindDN ) g_free( ldapServer->bindDN );
+       if( value ) ldapServer->bindDN = g_strdup( value );
+       g_strstrip( ldapServer->bindDN );
+}
+
+/*
+* Specify bind password to be used.
+*/
+void syldap_set_bind_password( SyldapServer* ldapServer, const gchar *value ) {
+       mgu_refresh_cache( ldapServer->addressCache );
+
+       if( ldapServer->bindPass ) g_free( ldapServer->bindPass );
+       if( value ) ldapServer->bindPass = g_strdup( value );
+       g_strstrip( ldapServer->bindPass );
+}
+
+/*
+* Specify search criteria to be used.
+*/
+void syldap_set_search_criteria( SyldapServer* ldapServer, const gchar *value ) {
+       mgu_refresh_cache( ldapServer->addressCache );
+
+       if( ldapServer->searchCriteria ) g_free( ldapServer->searchCriteria );
+       if( value ) ldapServer->searchCriteria = g_strdup( value );
+       g_strstrip( ldapServer->searchCriteria );
+       ldapServer->newSearch = TRUE;
+}
+
+/*
+* Specify search value to be searched for.
+*/
+void syldap_set_search_value( SyldapServer* ldapServer, const gchar *value ) {
+       mgu_refresh_cache( ldapServer->addressCache );
+
+       if( ldapServer->searchValue ) g_free( ldapServer->searchValue );
+       if( value ) ldapServer->searchValue = g_strdup( value );
+       g_strstrip( ldapServer->searchValue );
+       ldapServer->newSearch = TRUE;
+}
+
+/*
+* Specify maximum number of entries to retrieve.
+*/
+void syldap_set_max_entries( SyldapServer* ldapServer, const gint value ) {
+       mgu_refresh_cache( ldapServer->addressCache );
+       if( value > 0 ) {
+               ldapServer->maxEntries = value;
+       }
+       else {
+               ldapServer->maxEntries = SYLDAP_MAX_ENTRIES;
+       }
+}
+
+/*
+* Specify timeout value for LDAP operation (in seconds).
+*/
+void syldap_set_timeout( SyldapServer* ldapServer, const gint value ) {
+       mgu_refresh_cache( ldapServer->addressCache );
+       if( value > 0 ) {
+               ldapServer->timeOut = value;
+       }
+       else {
+               ldapServer->timeOut = SYLDAP_DFL_TIMEOUT;
+       }
+}
+
+/*
+* Register a callback function. When called, the function will be passed
+* this object as an argument.
+*/
+void syldap_set_callback( SyldapServer *ldapServer, void *func ) {
+       ldapServer->callBack = func;
+}
+
+/*
+* Create new LDAP server interface object.
+*/
+SyldapServer *syldap_create() {
+       SyldapServer *ldapServer;
+       ldapServer = g_new( SyldapServer, 1 );
+       ldapServer->name = NULL;
+       ldapServer->hostName = NULL;
+       ldapServer->port = SYLDAP_DFL_PORT;
+       ldapServer->baseDN = NULL;
+       ldapServer->bindDN = NULL;
+       ldapServer->bindPass = NULL;
+       ldapServer->searchCriteria = NULL;
+       ldapServer->searchValue = NULL;
+       ldapServer->entriesRead = 0;
+       ldapServer->maxEntries = SYLDAP_MAX_ENTRIES;
+       ldapServer->timeOut = SYLDAP_DFL_TIMEOUT;
+       ldapServer->newSearch = TRUE;
+       ldapServer->addressCache = mgu_create_cache();
+       ldapServer->thread = NULL;
+       ldapServer->busyFlag = FALSE;
+       ldapServer->retVal = MGU_SUCCESS;
+       ldapServer->callBack = NULL;
+       return ldapServer;
+}
+
+/*
+* Refresh internal variables to force a file read.
+*/
+void syldap_force_refresh( SyldapServer *ldapServer ) {
+       mgu_refresh_cache( ldapServer->addressCache );
+       ldapServer->newSearch = TRUE;
+}
+
+/*
+* Free up LDAP server interface object by releasing internal memory.
+*/
+void syldap_free( SyldapServer *ldapServer ) {
+       g_return_if_fail( ldapServer != NULL );
+
+       ldapServer->callBack = NULL;
+       // fprintf( stdout, "freeing... SyldapServer\n" );
+
+       /* Free internal stuff */
+       g_free( ldapServer->name );
+       g_free( ldapServer->hostName );
+       g_free( ldapServer->baseDN );
+       g_free( ldapServer->bindDN );
+       g_free( ldapServer->bindPass );
+       g_free( ldapServer->searchCriteria );
+       g_free( ldapServer->searchValue );
+
+       ldapServer->port = 0;
+       ldapServer->entriesRead = 0;
+       ldapServer->maxEntries = 0;
+       ldapServer->newSearch = FALSE;
+
+       /* Clear cache */
+       mgu_clear_cache( ldapServer->addressCache );
+       mgu_free_cache( ldapServer->addressCache );
+
+       // Clear pointers
+       ldapServer->name = NULL;
+       ldapServer->hostName = NULL;
+       ldapServer->baseDN = NULL;
+       ldapServer->bindDN = NULL;
+       ldapServer->bindPass = NULL;
+       ldapServer->searchCriteria = NULL;
+       ldapServer->searchValue = NULL;
+       ldapServer->addressCache = NULL;
+       ldapServer->thread = NULL;
+       ldapServer->busyFlag = FALSE;
+       ldapServer->retVal = MGU_SUCCESS;
+
+       /* Now release file object */
+       g_free( ldapServer );
+
+       // fprintf( stdout, "freeing... SyldapServer done\n" );
+
+}
+
+/*
+* Display object to specified stream.
+*/
+void syldap_print_data( SyldapServer *ldapServer, FILE *stream ) {
+       GSList *node;
+       g_return_if_fail( ldapServer != NULL );
+       fprintf( stream, "SyldapServer:\n" );
+       fprintf( stream, "     name: '%s'\n", ldapServer->name );
+       fprintf( stream, "host name: '%s'\n", ldapServer->hostName );
+       fprintf( stream, "     port: %d\n",   ldapServer->port );
+       fprintf( stream, "  base dn: '%s'\n", ldapServer->baseDN );
+       fprintf( stream, "  bind dn: '%s'\n", ldapServer->bindDN );
+       fprintf( stream, "bind pass: '%s'\n", ldapServer->bindPass );
+       fprintf( stream, " criteria: '%s'\n", ldapServer->searchCriteria );
+       fprintf( stream, "searchval: '%s'\n", ldapServer->searchValue );
+       fprintf( stream, "max entry: %d\n",   ldapServer->maxEntries );
+       fprintf( stream, " num read: %d\n",   ldapServer->entriesRead );
+       fprintf( stream, "  ret val: %d\n",   ldapServer->retVal );
+       mgu_print_cache( ldapServer->addressCache, stream );
+}
+
+/*
+* Display object to specified stream.
+*/
+void syldap_print_short( SyldapServer *ldapServer, FILE *stream ) {
+       GSList *node;
+       g_return_if_fail( ldapServer != NULL );
+       fprintf( stream, "SyldapServer:\n" );
+       fprintf( stream, "     name: '%s'\n", ldapServer->name );
+       fprintf( stream, "host name: '%s'\n", ldapServer->hostName );
+       fprintf( stream, "     port: %d\n",   ldapServer->port );
+       fprintf( stream, "  base dn: '%s'\n", ldapServer->baseDN );
+       fprintf( stream, "  bind dn: '%s'\n", ldapServer->bindDN );
+       fprintf( stream, "bind pass: '%s'\n", ldapServer->bindPass );
+       fprintf( stream, " criteria: '%s'\n", ldapServer->searchCriteria );
+       fprintf( stream, "searchval: '%s'\n", ldapServer->searchValue );
+       fprintf( stream, "max entry: %d\n",   ldapServer->maxEntries );
+       fprintf( stream, " num read: %d\n",   ldapServer->entriesRead );
+       fprintf( stream, "  ret val: %d\n",   ldapServer->retVal );
+}
+
+/*
+* Build an address list entry and append to list of address items. Name is formatted
+* as it appears in the common name (cn) attribute.
+*/
+void syldap_build_items_cn( SyldapServer *ldapServer, GSList *listName, GSList *listAddr ) {
+       AddressItem *addrItem = NULL;
+       GSList *nodeName = listName;
+       while( nodeName ) {
+               GSList *nodeAddress = listAddr;
+               while( nodeAddress ) {
+                       addrItem = mgu_create_address();
+                       addrItem->name = g_strdup( nodeName->data );
+                       addrItem->address = g_strdup( nodeAddress->data );
+                       addrItem->remarks = g_strdup( "" );
+                       mgu_add_cache( ldapServer->addressCache, addrItem );
+                       nodeAddress = g_slist_next( nodeAddress );
+                       ldapServer->entriesRead++;
+               }
+               nodeName = g_slist_next( nodeName );
+       }
+       addrItem = NULL;
+}
+
+/*
+* Build an address list entry and append to list of address items. Name is formatted
+* as "<first-name> <last-name>".
+*/
+void syldap_build_items_fl( SyldapServer *ldapServer, GSList *listAddr, GSList *listFirst, GSList *listLast  ) {
+       AddressItem *addrItem = NULL;
+       GSList *nodeFirst = listFirst;
+       GSList *nodeAddress = listAddr;
+       gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
+       gint iLen = 0, iLenT = 0;
+
+       // Find longest first name in list
+       while( nodeFirst ) {
+               if( firstName == NULL ) {
+                       firstName = nodeFirst->data;
+                       iLen = strlen( firstName );
+               }
+               else {
+                       if( ( iLenT = strlen( nodeFirst->data ) ) > iLen ) {
+                               firstName = nodeFirst->data;
+                               iLen = iLenT;
+                       }
+               }
+               nodeFirst = g_slist_next( nodeFirst );
+       }
+
+       // Format name
+       if( listLast ) {
+               lastName = listLast->data;
+       }
+
+       if( firstName ) {
+               if( lastName ) {
+                       fullName = g_strdup_printf( "%s %s", firstName, lastName );
+               }
+               else {
+                       fullName = g_strdup_printf( "%s", firstName );
+               }
+       }
+       else {
+               if( lastName ) {
+                       fullName = g_strdup_printf( "%s", lastName );
+               }
+       }
+       if( fullName ) {
+               g_strchug( fullName ); g_strchomp( fullName );
+       }
+
+       // Add address item
+       while( nodeAddress ) {
+               addrItem = mgu_create_address();
+               if( fullName ) {
+                       addrItem->name = g_strdup( fullName );
+               }
+               else {
+                       addrItem->name = g_strdup( "" );
+               }
+               addrItem->address = g_strdup( nodeAddress->data );
+               addrItem->remarks = g_strdup( "" );
+               mgu_add_cache( ldapServer->addressCache, addrItem );
+
+               nodeAddress = g_slist_next( nodeAddress );
+               ldapServer->entriesRead++;
+       }
+       g_free( fullName );
+       fullName = firstName = lastName = NULL;
+       addrItem = NULL;
+}
+
+/*
+* Add all attribute values to a list.
+*/
+GSList *syldap_add_list_values( LDAP *ld, LDAPMessage *entry, char *attr ) {
+       GSList *list = NULL;
+       gint i;
+       char **vals;
+       if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
+               for( i = 0; vals[i] != NULL; i++ ) {
+                       // printf( "lv\t%s: %s\n", attr, vals[i] );
+                       list = g_slist_append( list, g_strdup( vals[i] ) );
+               }
+       }
+       ldap_value_free( vals );
+       return list;
+}
+
+/*
+* Add a single attribute value to a list.
+*/
+GSList *syldap_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
+       GSList *list = NULL;
+       char **vals;
+       if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
+               if( vals[0] != NULL ) {
+                       // printf( "sv\t%s: %s\n", attr, vals[0] );
+                       list = g_slist_append( list, g_strdup( vals[0] ) );
+               }
+       }
+       ldap_value_free( vals );
+       return list;
+}
+
+/*
+* Free linked lists of character strings.
+*/
+void syldap_free_lists( GSList *listName, GSList *listAddr, GSList *listID, GSList *listDN, GSList *listFirst, GSList *listLast ) {
+       mgu_free_list( listName );
+       mgu_free_list( listAddr );
+       mgu_free_list( listID );
+       mgu_free_list( listDN );
+       mgu_free_list( listFirst );
+       mgu_free_list( listLast );
+}
+
+/*
+* Check parameters that are required for a search. This should
+* be called before performing a search.
+* Return: TRUE if search criteria appear OK.
+*/
+gboolean syldap_check_search( SyldapServer *ldapServer ) {
+       g_return_if_fail( ldapServer != NULL );
+       ldapServer->retVal = MGU_LDAP_CRITERIA;
+
+       // Test search criteria
+       if( ldapServer->searchCriteria == NULL ) {
+               return FALSE;
+       }
+       if( strlen( ldapServer->searchCriteria ) < 1 ) {
+               return FALSE;
+       }
+
+       if( ldapServer->searchValue == NULL ) {
+               return FALSE;
+       }
+       if( strlen( ldapServer->searchValue ) < 1 ) {
+               return FALSE;
+       }
+
+       ldapServer->retVal = MGU_SUCCESS;
+       return TRUE;
+}
+
+/*
+* Perform the LDAP search, reading LDAP entries into cache.
+* Note that one LDAP entry can have multiple values for many of its
+* attributes. If these attributes are E-Mail addresses; these are
+* broken out into separate address items. For any other attribute,
+* only the first occurrence is read.
+*/
+gint syldap_search( SyldapServer *ldapServer ) {
+       LDAP *ld;
+       LDAPMessage *result, *e;
+       char *attribs[10];
+       char *attribute;
+       gchar *criteria;
+       BerElement *ber;
+       int rc, cnt;
+       GSList *listName = NULL, *listAddress = NULL, *listID = NULL;
+       GSList *listFirst = NULL, *listLast = NULL, *listDN = NULL;
+       struct timeval timeout;
+       gboolean entriesFound = FALSE;
+
+       g_return_if_fail( ldapServer != NULL );
+
+       ldapServer->retVal = MGU_SUCCESS;
+       if( ! syldap_check_search( ldapServer ) ) {
+               return ldapServer->retVal;
+       }
+
+       // Set timeout
+       timeout.tv_sec = ldapServer->timeOut;
+       timeout.tv_usec = 0L;
+
+       ldapServer->entriesRead = 0;
+       if( ( ld = ldap_init( ldapServer->hostName, ldapServer->port ) ) == NULL ) {
+               ldapServer->retVal = MGU_LDAP_INIT;
+               return ldapServer->retVal;
+       }
+
+       // printf( "connected to LDAP host %s on port %d\n", ldapServer->hostName, ldapServer->port );
+
+       // Bind to the server, if required
+       if( ldapServer->bindDN ) {
+               if( * ldapServer->bindDN != '\0' ) {
+                       // printf( "binding...\n" );
+                       rc = ldap_simple_bind_s( ld, ldapServer->bindDN, ldapServer->bindPass );
+                       // printf( "rc=%d\n", rc );
+                       if( rc != LDAP_SUCCESS ) {
+                               // printf( "LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string( rc ) );
+                               ldap_unbind( ld );
+                               ldapServer->retVal = MGU_LDAP_BIND;
+                               return ldapServer->retVal;
+                       }
+               }
+       }
+
+       // Define all attributes we are interested in.
+       attribs[0] = SYLDAP_ATTR_DN;
+       attribs[1] = SYLDAP_ATTR_COMMONNAME;
+       attribs[2] = SYLDAP_ATTR_GIVENNAME;
+       attribs[3] = SYLDAP_ATTR_SURNAME;
+       attribs[4] = SYLDAP_ATTR_EMAIL;
+       attribs[5] = SYLDAP_ATTR_UID;
+       attribs[6] = NULL;
+
+       // Create LDAP search string and apply search criteria
+       criteria = g_strdup_printf( ldapServer->searchCriteria, ldapServer->searchValue );
+       rc = ldap_search_ext_s( ld, ldapServer->baseDN, LDAP_SCOPE_SUBTREE, criteria, attribs, 0, NULL, NULL,
+                      &timeout, 0, &result );
+       g_free( criteria );
+       criteria = NULL;
+       if( rc == LDAP_TIMEOUT ) {
+               ldap_unbind( ld );
+               ldapServer->retVal = MGU_LDAP_TIMEOUT;
+               return ldapServer->retVal;
+       }
+       if( rc != LDAP_SUCCESS ) {
+               // printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
+               ldap_unbind( ld );
+               ldapServer->retVal = MGU_LDAP_SEARCH;
+               return ldapServer->retVal;
+       }
+
+       // printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
+
+       // Clear the cache if we have new entries, otherwise leave untouched.
+       if( ldap_count_entries( ld, result ) > 0 ) {
+               mgu_clear_cache( ldapServer->addressCache );
+       }
+
+       // Process results
+       ldapServer->entriesRead = 0;
+       for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
+               entriesFound = TRUE;
+               if( ldapServer->entriesRead >= ldapServer->maxEntries ) break;          
+               // printf( "DN: %s\n", ldap_get_dn( ld, e ) );
+
+               // Process all attributes
+               for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
+                              attribute = ldap_next_attribute( ld, e, ber ) ) {
+                       if( strcasecmp( attribute, SYLDAP_ATTR_COMMONNAME ) == 0 ) {
+                               listName = syldap_add_list_values( ld, e, attribute );
+                       }
+                       if( strcasecmp( attribute, SYLDAP_ATTR_EMAIL ) == 0 ) {
+                               listAddress = syldap_add_list_values( ld, e, attribute );
+                       }
+                       if( strcasecmp( attribute, SYLDAP_ATTR_UID ) == 0 ) {
+                               listID = syldap_add_single_value( ld, e, attribute );
+                       }
+                       if( strcasecmp( attribute, SYLDAP_ATTR_GIVENNAME ) == 0 ) {
+                               listFirst = syldap_add_list_values( ld, e, attribute );
+                       }
+                       if( strcasecmp( attribute, SYLDAP_ATTR_SURNAME ) == 0 ) {
+                               listLast = syldap_add_single_value( ld, e, attribute );
+                       }
+                       if( strcasecmp( attribute, SYLDAP_ATTR_DN ) == 0 ) {
+                               listDN = syldap_add_single_value( ld, e, attribute );
+                       }
+               }
+
+               // Free memory used to store attribute
+               ldap_memfree( attribute );
+
+               // Format and add items to cache
+               syldap_build_items_fl( ldapServer, listAddress, listFirst, listLast );
+
+               // Free up
+               syldap_free_lists( listName, listAddress, listID, listDN, listFirst, listLast );
+               listName = listAddress = listID = listFirst = listLast = listDN = NULL;
+
+               if( ber != NULL ) {
+                       ber_free( ber, 0 );
+               }
+       }
+
+       syldap_free_lists( listName, listAddress, listID, listDN, listFirst, listLast );
+       listName = listAddress = listID = listFirst = listLast = listDN = NULL;
+       
+       // Free up and disconnect
+       ldap_msgfree( result );
+       ldap_unbind( ld );
+       ldapServer->newSearch = FALSE;
+       if( entriesFound ) {
+               ldapServer->retVal = MGU_SUCCESS;
+       }
+       else {
+               ldapServer->retVal = MGU_LDAP_NOENTRIES;
+       }
+       return ldapServer->retVal;
+}
+
+// ============================================================================================
+/*
+* Read data into list. Main entry point
+* Return: TRUE if file read successfully.
+*/
+// ============================================================================================
+gint syldap_read_data( SyldapServer *ldapServer ) {
+       g_return_if_fail( ldapServer != NULL );
+
+       pthread_detach( pthread_self() );
+       if( ldapServer->newSearch ) {
+               // Read data into the list
+               syldap_search( ldapServer );
+
+               // Mark cache
+               ldapServer->addressCache->modified = FALSE;
+               ldapServer->addressCache->dataRead = TRUE;
+       }
+
+       // Callback
+       ldapServer->busyFlag = FALSE;
+       if( ldapServer->callBack ) {
+               sched_yield();
+               ( ldapServer->callBack )( ldapServer );
+       }
+       ldapServer->thread = NULL;
+       pthread_exit( NULL );
+       return ldapServer->retVal;
+}
+
+// ============================================================================================
+/*
+* Cancel read with thread.
+*/
+// ============================================================================================
+void syldap_cancel_read( SyldapServer *ldapServer ) {
+       g_return_if_fail( ldapServer != NULL );
+       if( ldapServer->thread ) {
+printf( "thread cancelled\n" );
+               pthread_cancel( *ldapServer->thread );
+       }
+       ldapServer->thread = NULL;
+       ldapServer->busyFlag = FALSE;
+}
+
+// ============================================================================================
+/*
+* Read data into list using a background thread.
+* Return: TRUE if file read successfully. Callback function will be
+* notified when search is complete.
+*/
+// ============================================================================================
+gint syldap_read_data_th( SyldapServer *ldapServer ) {
+       pthread_t thread;
+       g_return_if_fail( ldapServer != NULL );
+
+       ldapServer->busyFlag = FALSE;
+       syldap_check_search( ldapServer );
+       if( ldapServer->retVal == MGU_SUCCESS ) {
+               ldapServer->busyFlag = TRUE;
+               ldapServer->thread = &thread;
+               pthread_create( ldapServer->thread, NULL, (void *) &syldap_read_data, (void *) ldapServer );
+       }
+       return ldapServer->retVal;
+}
+
+/*
+* Return link list of address items.
+* Return: TRUE if file read successfully.
+*/
+GList *syldap_get_address_list( const SyldapServer *ldapServer ) {
+       g_return_if_fail( ldapServer != NULL );
+       return ldapServer->addressCache->addressList;
+}
+
+#define SYLDAP_TEST_FILTER   "(objectclass=*)"
+#define SYLDAP_SEARCHBASE_V2 "cn=config"
+#define SYLDAP_SEARCHBASE_V3 ""
+#define SYLDAP_V2_TEST_ATTR  "database"
+#define SYLDAP_V3_TEST_ATTR  "namingcontexts"
+
+/*
+* Attempt to discover the base DN for the server.
+* Enter:
+*      host    Host name
+*      port    Port number
+*      bindDN  Bind DN (optional).
+*      bindPW  Bind PW (optional).
+*      tov     Timeout value (seconds), or 0 for none, default 30 secs.
+* Return: List of Base DN's, or NULL if could not read. Base DN should
+* be g_free() when done.
+*/
+GList *syldap_read_basedn_s( const gchar *host, const gint port, const gchar *bindDN, const gchar *bindPW, const gint tov ) {
+       GList *baseDN = NULL;
+       LDAP *ld;
+       int rc, i;
+       LDAPMessage *result, *e;
+       char *attribs[10];
+       BerElement *ber;
+       char *attribute;
+       char **vals;
+       struct timeval timeout;
+
+       if( host == NULL ) return baseDN;
+       if( port < 1 ) return baseDN;
+
+       // Set timeout
+       timeout.tv_usec = 0L;
+       if( tov > 0 ) {
+               timeout.tv_sec = tov;
+       }
+       else {
+               timeout.tv_sec = 30L;
+       }
+
+       // Connect to server.
+       if( ( ld = ldap_init( host, port ) ) == NULL ) {
+               return baseDN;
+       }
+
+       // Bind to the server, if required
+       if( bindDN ) {
+               if( *bindDN != '\0' ) {
+                       rc = ldap_simple_bind_s( ld, bindDN, bindPW );
+                       if( rc != LDAP_SUCCESS ) {
+                               // printf( "LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string( rc ) );
+                               ldap_unbind( ld );
+                               return baseDN;
+                       }
+               }
+       }
+
+       // Test for LDAP version 3
+       attribs[0] = SYLDAP_V3_TEST_ATTR;
+       attribs[1] = NULL;
+       rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V3, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs,
+                      0, NULL, NULL, &timeout, 0, &result );
+       if( rc == LDAP_SUCCESS ) {
+               // Process entries
+               for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
+                       // printf( "DN: %s\n", ldap_get_dn( ld, e ) );
+
+                       // Process attributes
+                       for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
+                                       attribute = ldap_next_attribute( ld, e, ber ) ) {
+                               if( strcasecmp( attribute, SYLDAP_V3_TEST_ATTR ) == 0 ) {
+                                       if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) {
+                                               for( i = 0; vals[i] != NULL; i++ ) {
+                                                       // printf( "\t%s: %s\n", attribute, vals[i] );
+                                                       baseDN = g_list_append( baseDN, g_strdup( vals[i] ) );
+                                               }
+                                       }
+                                       ldap_value_free( vals );
+                               }
+                       }
+                       ldap_memfree( attribute );
+                       if( ber != NULL ) {
+                               ber_free( ber, 0 );
+                       }
+               }
+               ldap_msgfree( result );
+       }
+       else {
+       }
+
+       if( baseDN == NULL ) {
+               // Test for LDAP version 2
+               attribs[0] = NULL;
+               rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V2, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs,
+                              0, NULL, NULL, &timeout, 0, &result );
+               if( rc == LDAP_SUCCESS ) {
+                       // Process entries
+                       for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
+                               // if( baseDN ) break;                  
+                               // printf( "DN: %s\n", ldap_get_dn( ld, e ) );
+
+                               // Process attributes
+                               for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
+                                              attribute = ldap_next_attribute( ld, e, ber ) ) {
+                                       // if( baseDN ) break;                  
+                                       if( strcasecmp( attribute, SYLDAP_V2_TEST_ATTR ) == 0 ) {
+                                               if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) {
+                                                       for( i = 0; vals[i] != NULL; i++ ) {
+                                                               char *ch;
+                                                               // Strip the 'ldb:' from the front of the value
+                                                               ch = ( char * ) strchr( vals[i], ':' );
+                                                               if( ch ) {
+                                                                       gchar *bn = g_strdup( ++ch );
+                                                                       g_strchomp( bn );
+                                                                       g_strchug( bn );
+                                                                       baseDN = g_list_append( baseDN, g_strdup( bn ) );
+                                                               }
+                                                       }
+                                               }
+                                               ldap_value_free( vals );
+                                       }
+                               }
+                               ldap_memfree( attribute );
+                               if( ber != NULL ) {
+                                       ber_free( ber, 0 );
+                               }
+                       }
+                       ldap_msgfree( result );
+               }
+       }
+       ldap_unbind( ld );
+       return baseDN;
+}
+
+/*
+* Attempt to discover the base DN for the server.
+* Enter:  ldapServer Server to test.
+* Return: List of Base DN's, or NULL if could not read. Base DN should
+* be g_free() when done. Return code set in ldapServer.
+*/
+GList *syldap_read_basedn( SyldapServer *ldapServer ) {
+       GList *baseDN = NULL;
+       LDAP *ld;
+       int rc, i;
+       LDAPMessage *result, *e;
+       char *attribs[10];
+       BerElement *ber;
+       char *attribute;
+       char **vals;
+       struct timeval timeout;
+
+       ldapServer->retVal = MGU_BAD_ARGS;
+       if( ldapServer == NULL ) return baseDN;
+       if( ldapServer->hostName == NULL ) return baseDN;
+       if( ldapServer->port < 1 ) return baseDN;
+
+       // Set timeout
+       timeout.tv_usec = 0L;
+       if( ldapServer->timeOut > 0 ) {
+               timeout.tv_sec = ldapServer->timeOut;
+       }
+       else {
+               timeout.tv_sec = 30L;
+       }
+
+       // Connect to server.
+       if( ( ld = ldap_init( ldapServer->hostName, ldapServer->port ) ) == NULL ) {
+               ldapServer->retVal = MGU_LDAP_INIT;
+               return baseDN;
+       }
+
+       // Bind to the server, if required
+       if( ldapServer->bindDN ) {
+               if( *ldapServer->bindDN != '\0' ) {
+                       rc = ldap_simple_bind_s( ld, ldapServer->bindDN, ldapServer->bindPass );
+                       if( rc != LDAP_SUCCESS ) {
+                               //printf( "LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string( rc ) );
+                               ldap_unbind( ld );
+                               ldapServer->retVal = MGU_LDAP_BIND;
+                               return baseDN;
+                       }
+               }
+       }
+
+       ldapServer->retVal = MGU_LDAP_SEARCH;
+
+       // Test for LDAP version 3
+       attribs[0] = SYLDAP_V3_TEST_ATTR;
+       attribs[1] = NULL;
+       rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V3, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs,
+                      0, NULL, NULL, &timeout, 0, &result );
+       if( rc == LDAP_SUCCESS ) {
+               // Process entries
+               for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
+                       // printf( "DN: %s\n", ldap_get_dn( ld, e ) );
+
+                       // Process attributes
+                       for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
+                                       attribute = ldap_next_attribute( ld, e, ber ) ) {
+                               if( strcasecmp( attribute, SYLDAP_V3_TEST_ATTR ) == 0 ) {
+                                       if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) {
+                                               for( i = 0; vals[i] != NULL; i++ ) {
+                                                       // printf( "\t%s: %s\n", attribute, vals[i] );
+                                                       baseDN = g_list_append( baseDN, g_strdup( vals[i] ) );
+                                               }
+                                       }
+                                       ldap_value_free( vals );
+                               }
+                       }
+                       ldap_memfree( attribute );
+                       if( ber != NULL ) {
+                               ber_free( ber, 0 );
+                       }
+               }
+               ldap_msgfree( result );
+               ldapServer->retVal = MGU_SUCCESS;
+       }
+       else if( rc == LDAP_TIMEOUT ) {
+               ldapServer->retVal = MGU_LDAP_TIMEOUT;
+       }
+
+       if( baseDN == NULL ) {
+               // Test for LDAP version 2
+               attribs[0] = NULL;
+               rc = ldap_search_ext_s( ld, SYLDAP_SEARCHBASE_V2, LDAP_SCOPE_BASE, SYLDAP_TEST_FILTER, attribs,
+                              0, NULL, NULL, &timeout, 0, &result );
+               if( rc == LDAP_SUCCESS ) {
+                       // Process entries
+                       for( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
+                               // if( baseDN ) break;                  
+                               // printf( "DN: %s\n", ldap_get_dn( ld, e ) );
+
+                               // Process attributes
+                               for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
+                                              attribute = ldap_next_attribute( ld, e, ber ) ) {
+                                       // if( baseDN ) break;                  
+                                       if( strcasecmp( attribute, SYLDAP_V2_TEST_ATTR ) == 0 ) {
+                                               if( ( vals = ldap_get_values( ld, e, attribute ) ) != NULL ) {
+                                                       for( i = 0; vals[i] != NULL; i++ ) {
+                                                               char *ch;
+                                                               // Strip the 'ldb:' from the front of the value
+                                                               ch = ( char * ) strchr( vals[i], ':' );
+                                                               if( ch ) {
+                                                                       gchar *bn = g_strdup( ++ch );
+                                                                       g_strchomp( bn );
+                                                                       g_strchug( bn );
+                                                                       baseDN = g_list_append( baseDN, g_strdup( bn ) );
+                                                               }
+                                                       }
+                                               }
+                                               ldap_value_free( vals );
+                                       }
+                               }
+                               ldap_memfree( attribute );
+                               if( ber != NULL ) {
+                                       ber_free( ber, 0 );
+                               }
+                       }
+                       ldap_msgfree( result );
+                       ldapServer->retVal = MGU_SUCCESS;
+               }
+               else if( rc == LDAP_TIMEOUT ) {
+                       ldapServer->retVal = MGU_LDAP_TIMEOUT;
+               }
+       }
+       ldap_unbind( ld );
+
+       return baseDN;
+}
+
+/*
+* Attempt to connect to the server.
+* Enter:
+*      host    Host name
+*      port    Port number
+* Return: TRUE if connected successfully.
+*/
+gboolean syldap_test_connect_s( const gchar *host, const gint port ) {
+       gboolean retVal = FALSE;
+       LDAP *ld;
+       if( host == NULL ) return retVal;
+       if( port < 1 ) return retVal;
+       if( ( ld = ldap_open( host, port ) ) != NULL ) {
+               retVal = TRUE;
+       }
+       if( ld != NULL ) {
+               ldap_unbind( ld );
+       }
+       return retVal;
+}
+
+/*
+* Attempt to connect to the server.
+* Enter:  ldapServer Server to test.
+* Return: TRUE if connected successfully. Return code set in ldapServer.
+*/
+gboolean syldap_test_connect( SyldapServer *ldapServer ) {
+       gboolean retVal = FALSE;
+       LDAP *ld;
+       ldapServer->retVal = MGU_BAD_ARGS;
+       if( ldapServer == NULL ) return retVal;
+       if( ldapServer->hostName == NULL ) return retVal;
+       if( ldapServer->port < 1 ) return retVal;
+       ldapServer->retVal = MGU_LDAP_INIT;
+       if( ( ld = ldap_open( ldapServer->hostName, ldapServer->port ) ) != NULL ) {
+               ldapServer->retVal = MGU_SUCCESS;
+               retVal = TRUE;
+       }
+       if( ld != NULL ) {
+               ldap_unbind( ld );
+       }
+       return retVal;
+}
+
+#define LDAP_LINK_LIB_NAME_1 "libldap.so"
+#define LDAP_LINK_LIB_NAME_2 "liblber.so"
+#define LDAP_LINK_LIB_NAME_3 "libresolv.so"
+#define LDAP_LINK_LIB_NAME_4 "libpthread.so"
+
+/*
+* Test whether LDAP libraries installed.
+* Return: TRUE if library available.
+*/
+gboolean syldap_test_ldap_lib() {
+       void *handle, *fun;
+
+       // Get library
+       handle = dlopen( LDAP_LINK_LIB_NAME_1, RTLD_LAZY );
+       if( ! handle ) {
+               return FALSE;
+       }
+
+       // Test for symbols we need
+       fun = dlsym( handle, "ldap_init" );
+       if( ! fun ) {
+               dlclose( handle );
+               return FALSE;
+       }
+       dlclose( handle ); handle = NULL; fun = NULL;
+
+       handle = dlopen( LDAP_LINK_LIB_NAME_2, RTLD_LAZY );
+       if( ! handle ) {
+               return FALSE;
+       }
+       fun = dlsym( handle, "ber_init" );
+       if( ! fun ) {
+               dlclose( handle );
+               return FALSE;
+       }
+       dlclose( handle ); handle = NULL; fun = NULL;
+
+       handle = dlopen( LDAP_LINK_LIB_NAME_3, RTLD_LAZY );
+       if( ! handle ) {
+               return FALSE;
+       }
+       fun = dlsym( handle, "res_query" );
+       if( ! fun ) {
+               dlclose( handle );
+               return FALSE;
+       }
+       dlclose( handle ); handle = NULL; fun = NULL;
+
+       handle = dlopen( LDAP_LINK_LIB_NAME_4, RTLD_LAZY );
+       if( ! handle ) {
+               return FALSE;
+       }
+       fun = dlsym( handle, "pthread_create" );
+       if( ! fun ) {
+               dlclose( handle );
+               return FALSE;
+       }
+       dlclose( handle ); handle = NULL; fun = NULL;
+
+       return TRUE;
+}
+
+#endif /* USE_LDAP */
+
+/*
+* End of Source.
+*/
diff --git a/src/syldap.h b/src/syldap.h
new file mode 100644 (file)
index 0000000..c9151d4
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Definitions necessary to access LDAP servers.
+ */
+
+#ifndef __SYLDAP_H__
+#define __SYLDAP_H__
+
+#ifdef USE_LDAP
+
+#include <glib.h>
+#include <pthread.h>
+
+#include "mgutils.h"
+
+#define SYLDAP_DFL_PORT        389
+#define SYLDAP_MAX_ENTRIES     20
+#define SYLDAP_DFL_TIMEOUT     30
+#define SYLDAP_DFL_CRITERIA    "(&(mail=*)(cn=%s*))"
+       
+#define SYLDAP_ATTR_DN         "dn"
+#define SYLDAP_ATTR_COMMONNAME "cn"
+#define SYLDAP_ATTR_GIVENNAME  "givenName"
+#define SYLDAP_ATTR_SURNAME    "sn"
+#define SYLDAP_ATTR_EMAIL      "mail"
+#define SYLDAP_ATTR_UID        "uid"
+
+// VCard object
+typedef struct _SyldapServer SyldapServer;
+struct _SyldapServer {
+       gchar *name;
+       gchar *hostName;
+       gint  port;
+       gchar *baseDN;
+       gchar *bindDN;
+       gchar *bindPass;
+       gchar *searchCriteria;
+       gchar *searchValue;
+       gint  entriesRead;
+       gint  maxEntries;
+       gint  timeOut;
+       gboolean newSearch;
+       AddressCache *addressCache;
+       gint  retVal;
+       pthread_t *thread;
+       gboolean busyFlag;
+       void (*callBack)( void * );
+};
+
+/* Function prototypes */
+void syldap_set_name( SyldapServer* ldapServer, const gchar *value );
+void syldap_set_host( SyldapServer* ldapServer, const gchar *value );
+void syldap_set_port( SyldapServer* ldapServer, const gint value );
+void syldap_set_base_dn( SyldapServer* ldapServer, const gchar *value );
+void syldap_set_bind_dn( SyldapServer* ldapServer, const gchar *value );
+void syldap_set_bind_password( SyldapServer* ldapServer, const gchar *value );
+void syldap_set_search_criteria( SyldapServer* ldapServer, const gchar *value );
+void syldap_set_search_value( SyldapServer* ldapServer, const gchar *value );
+void syldap_set_max_entries( SyldapServer* ldapServer, const gint value );
+void syldap_set_timeout( SyldapServer* ldapServer, const gint value );
+void syldap_set_callback( SyldapServer *ldapServer, void *func );
+void syldap_force_refresh( SyldapServer *ldapServer );
+SyldapServer *syldap_create();
+void syldap_free( SyldapServer *ldapServer );
+void syldap_print_data( SyldapServer *ldapServer, FILE *stream );
+gboolean syldap_check_search( SyldapServer *ldapServer );
+gint syldap_read_data( SyldapServer *ldapServer );
+gint syldap_read_data_th( SyldapServer *ldapServer );
+void syldap_cancel_read( SyldapServer *ldapServer );
+GList *syldap_get_address_list( const SyldapServer *ldapServer );
+GList *syldap_read_basedn_s( const gchar *host, const gint port, const gchar *bindDN, const gchar *bindPW, const gint tov );
+GList *syldap_read_basedn( SyldapServer *ldapServer );
+gboolean syldap_test_connect_s( const gchar *host, const gint port );
+gboolean syldap_test_connect( SyldapServer *ldapServer );
+gboolean syldap_test_ldap_lib();
+
+#endif /* USE_LDAP */
+
+#endif /* __SYLDAP_H__ */
+
diff --git a/src/vcard.c b/src/vcard.c
new file mode 100644 (file)
index 0000000..d9cf523
--- /dev/null
@@ -0,0 +1,691 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Functions necessary to access VCard files. VCard files are used
+ * by GnomeCard for addressbook, and Netscape for sending business
+ * card information. Refer to RFC2426 for more information.
+ */
+
+#include <sys/stat.h>
+#include <glib.h>
+
+#include "mgutils.h"
+#include "vcard.h"
+
+#define GNOMECARD_DIR     ".gnome"
+#define GNOMECARD_FILE    "GnomeCard"
+#define GNOMECARD_SECTION "[file]"
+#define GNOMECARD_PARAM   "open"
+
+#define VCARD_TEST_LINES  200
+
+/*
+* Specify name to be used.
+*/
+void vcard_set_name( VCardFile* cardFile, const gchar *name ) {
+       /* Copy file name */
+       if( cardFile->name ) g_free( cardFile->name );
+       if( name ) cardFile->name = g_strdup( name );
+       g_strstrip( cardFile->name );
+}
+
+/*
+* Specify file to be used.
+*/
+void vcard_set_file( VCardFile* cardFile, const gchar *path ) {
+       mgu_refresh_cache( cardFile->addressCache );
+
+       /* Copy file path */
+       if( cardFile->path ) g_free( cardFile->path );
+       if( path ) cardFile->path = g_strdup( path );
+       g_strstrip( cardFile->path );
+}
+
+/*
+* Create new cardfile object.
+*/
+VCardFile *vcard_create() {
+       VCardFile *cardFile;
+       cardFile = g_new( VCardFile, 1 );
+       cardFile->name = NULL;
+       cardFile->path = NULL;
+       cardFile->file = NULL;
+       cardFile->bufptr = cardFile->buffer;
+       cardFile->addressCache = mgu_create_cache();
+       cardFile->retVal = MGU_SUCCESS;
+       return cardFile;
+}
+
+/*
+* Refresh internal variables to force a file read.
+*/
+void vcard_force_refresh( VCardFile *cardFile ) {
+       mgu_refresh_cache( cardFile->addressCache );
+}
+
+/*
+* Create new cardfile object for specified file.
+*/
+VCardFile *vcard_create_path( const gchar *path ) {
+       VCardFile *cardFile;
+       cardFile = vcard_create();
+       vcard_set_file(cardFile, path );
+       return cardFile;
+}
+
+/*
+* Free up cardfile object by releasing internal memory.
+*/
+void vcard_free( VCardFile *cardFile ) {
+       g_return_if_fail( cardFile != NULL );
+
+       // fprintf( stdout, "freeing... VCardFile\n" );
+
+       /* Close file */
+       if( cardFile->file ) fclose( cardFile->file );
+
+       /* Free internal stuff */
+       g_free( cardFile->name );
+       g_free( cardFile->path );
+
+       /* Clear cache */
+       mgu_clear_cache( cardFile->addressCache );
+       mgu_free_cache( cardFile->addressCache );
+
+       // Clear pointers
+       cardFile->file = NULL;
+       cardFile->name = NULL;
+       cardFile->path = NULL;
+       cardFile->addressCache = NULL;
+       cardFile->retVal = MGU_SUCCESS;
+
+       /* Now release file object */
+       g_free( cardFile );
+
+       // fprintf( stdout, "freeing... VCardFile done\n" );
+
+}
+
+/*
+* Display object to specified stream.
+*/
+void vcard_print_file( VCardFile *cardFile, FILE *stream ) {
+       GSList *node;
+       g_return_if_fail( cardFile != NULL );
+       fprintf( stream, "VCardFile:\n" );
+       fprintf( stream, "     name: '%s'\n", cardFile->name );
+       fprintf( stream, "file spec: '%s'\n", cardFile->path );
+       fprintf( stream, "  ret val: %d\n",   cardFile->retVal );
+       mgu_print_cache( cardFile->addressCache, stream );
+}
+
+/*
+* Open file for read.
+* return: TRUE if file opened successfully.
+*/
+gint vcard_open_file( VCardFile* cardFile ) {
+       g_return_if_fail( cardFile != NULL );
+
+       // fprintf( stdout, "Opening file\n" );
+       cardFile->addressCache->dataRead = FALSE;
+       if( cardFile->path ) {
+               cardFile->file = fopen( cardFile->path, "r" );
+               if( ! cardFile->file ) {
+                       // fprintf( stderr, "can't open %s\n", cardFile->path );
+                       cardFile->retVal = MGU_OPEN_FILE;
+                       return cardFile->retVal;
+               }
+       }
+       else {
+               // fprintf( stderr, "file not specified\n" );
+               cardFile->retVal = MGU_NO_FILE;
+               return cardFile->retVal;
+       }
+
+       /* Setup a buffer area */
+       cardFile->buffer[0] = '\0';
+       cardFile->bufptr = cardFile->buffer;
+       cardFile->retVal = MGU_SUCCESS;
+       return cardFile->retVal;
+}
+
+/*
+* Close file.
+*/
+void vcard_close_file( VCardFile *cardFile ) {
+       g_return_if_fail( cardFile != NULL );
+       if( cardFile->file ) fclose( cardFile->file );
+       cardFile->file = NULL;
+}
+
+/*
+* Read line of text from file.
+* Return: ptr to buffer where line starts.
+*/
+gchar *vcard_read_line( VCardFile *cardFile ) {
+       while( *cardFile->bufptr == '\n' || *cardFile->bufptr == '\0' ) {
+               if( fgets( cardFile->buffer, VCARDBUFSIZE, cardFile->file ) == NULL )
+                       return NULL;
+               g_strstrip( cardFile->buffer );
+               cardFile->bufptr = cardFile->buffer;
+       }
+       return cardFile->bufptr;
+}
+
+/*
+* Read line of text from file.
+* Return: ptr to buffer where line starts.
+*/
+gchar *vcard_get_line( VCardFile *cardFile ) {
+       gchar buf[ VCARDBUFSIZE ];
+       gchar *start, *end;
+       gint len;
+
+       if (vcard_read_line( cardFile ) == NULL ) {
+               buf[0] = '\0';
+               return;
+       }
+
+       /* Copy into private buffer */
+       start = cardFile->bufptr;
+       len = strlen( start );
+       end = start + len;
+       strncpy( buf, start, len );
+       buf[ len ] = '\0';
+       g_strstrip(buf);
+       cardFile->bufptr = end + 1;
+
+       /* Return a copy of buffer */   
+       return g_strdup( buf );
+}
+
+/*
+* Free linked lists of character strings.
+*/
+void vcard_free_lists( GSList *listName, GSList *listAddr, GSList *listRem, GSList* listID ) {
+       mgu_free_list( listName );
+       mgu_free_list( listAddr );
+       mgu_free_list( listRem );
+       mgu_free_list( listID );
+}
+
+/*
+* Read quoted-printable text, which may span several lines into one long string.
+* Param: cardFile - object.
+* Param: tagvalue - will be placed into the linked list.
+*/
+gchar *vcard_read_qp( VCardFile *cardFile, char *tagvalue ) {
+       GSList *listQP = NULL;
+       gint len = 0;
+       gchar *line = tagvalue;
+       while( line ) {
+               listQP = g_slist_append( listQP, line );
+               len = strlen( line ) - 1;
+               if( len > 0 ) {
+                       if( line[ len ] != '=' ) break;
+                       line[ len ] = '\0';
+               }
+               line = vcard_get_line( cardFile );
+       }
+
+       // Coalesce linked list into one long buffer.
+       line = mgu_list_coalesce( listQP );
+
+       // Clean up
+       mgu_free_list( listQP );
+       listQP = NULL;
+       return line;
+}
+
+/*
+* Parse tag name from line buffer.
+* Return: Buffer containing the tag name, or NULL if no delimiter char found.
+*/
+gchar *vcard_get_tagname( char* line, gchar dlm ) {
+       gint len = 0;
+       gchar *tag = NULL;
+       gchar *lptr = line;
+       while( *lptr++ ) {
+               if( *lptr == dlm ) {
+                       len = lptr - line;
+                       tag = g_strndup( line, len+1 );
+                       tag[ len ] = '\0';
+                       g_strdown( tag );
+                       return tag;
+               }
+       }
+       return tag;
+}
+
+/*
+* Parse tag value from line buffer.
+* Return: Buffer containing the tag value. Empty string is returned if
+* no delimiter char found.
+*/
+gchar *vcard_get_tagvalue( gchar* line, gchar dlm ) {
+       gchar *value = NULL;
+       gchar *start = NULL;
+       gchar *lptr;
+       gint len = 0;
+
+       for( lptr = line; *lptr; lptr++ ) {
+               if( *lptr == dlm ) {
+                       if( ! start )
+                               start = lptr + 1;
+               }
+       }
+       if( start ) {
+               len = lptr - start;
+               value = g_strndup( start, len+1 );
+       }
+       else {
+               // Ensure that we get an empty string
+               value = g_strndup( "", 1 );
+       }
+       value[ len ] = '\0';
+       return value;
+}
+
+/*
+* Dump linked lists of character strings (for debug).
+*/
+void vcard_dump_lists( GSList *listName, GSList *listAddr, GSList *listRem, GSList *listID, FILE *stream ) {
+       fprintf( stream, "dump name\n" );
+       fprintf( stream, "------------\n" );
+       mgu_print_list( listName, stdout );
+       fprintf( stream, "dump address\n" );
+       fprintf( stream, "------------\n" );
+       mgu_print_list( listAddr, stdout );
+       fprintf( stream, "dump remarks\n" );
+       fprintf( stdout, "------------\n" );
+       mgu_print_list( listRem, stdout );
+       fprintf( stream, "dump id\n" );
+       fprintf( stdout, "------------\n" );
+       mgu_print_list( listID, stdout );
+}
+
+/*
+* Build an address list entry and append to list of address items.
+*/
+void vcard_build_items( VCardFile *cardFile, GSList *listName, GSList *listAddr, GSList *listRem, GSList *listID ) {
+       AddressItem *addrItem = NULL;
+       GSList *nodeName = listName;
+       GSList *nodeID = listID;
+       while( nodeName ) {
+               GSList *nodeAddress = listAddr;
+               GSList *nodeRemarks = listRem;
+               while( nodeAddress ) {
+                       addrItem = mgu_create_address();
+                       addrItem->name = g_strdup( nodeName->data );
+                       addrItem->address = g_strdup( nodeAddress->data );
+                       if( nodeRemarks ) {
+                               if( nodeRemarks->data ) {
+                                       if( g_strcasecmp( nodeRemarks->data, "internet" ) == 0 ) {
+                                               // Trivially exclude this one (appears for most records)
+                                               addrItem->remarks = g_strdup( "" );
+                                       }
+                                       else {
+                                               addrItem->remarks = g_strdup( nodeRemarks->data );
+                                       }
+                               }
+                               else {
+                                       addrItem->remarks = g_strdup( "" );
+                               }
+                       }
+                       else {
+                                       addrItem->remarks = g_strdup( "" );
+                       }
+/*
+                       if( nodeID ) {
+                               if( nodeID->data ) {
+                                       addrItem->externalID = g_strdup( nodeID->data );
+                               }
+                               else {
+                                       addrItem->externalID = g_strdup( "" );
+                               }
+                       }
+                       else {
+                               addrItem->externalID = g_strdup( "" );
+                       }
+*/
+                       mgu_add_cache( cardFile->addressCache, addrItem );
+
+                       nodeAddress = g_slist_next( nodeAddress );
+                       nodeRemarks = g_slist_next( nodeRemarks );
+               }
+               nodeName = g_slist_next( nodeName );
+               nodeID = g_slist_next( nodeID );
+       }
+       addrItem = NULL;
+}
+
+// Unescape characters in quoted-printable string.
+void vcard_unescape_qp( gchar *value ) {
+       gchar *ptr, *src, *dest;
+       int d, v;
+       char ch;
+       gboolean gotch;
+       ptr = value;
+       while( *ptr ) {
+               gotch = FALSE;
+               if( *ptr == '=' ) {
+                       v = 0;
+                       ch = *(ptr + 1);
+                       if( ch ) {
+                               if( ch > '0' && ch < '8' ) v = ch - '0';
+                       }
+                       d = -1;
+                       ch = *(ptr + 2);
+                       if( ch ) {
+                               if( ch > '\x60' ) ch -= '\x20';
+                               if( ch > '0' && ch < ' ' ) d = ch - '0';
+                               d = ch - '0';
+                               if( d > 9 ) d -= 7;
+                               if( d > -1 && d < 16 ) {
+                                       v = ( 16 * v ) + d;
+                                       gotch = TRUE;
+                               }
+                       }
+               }
+               if( gotch ) {
+                       // Replace = with char and move down in buffer
+                       *ptr = v;
+                       src = ptr + 3;
+                       dest = ptr + 1;
+                       while( *src ) {
+                               *dest++ = *src++;
+                       }
+                       *dest = '\0';
+               }
+               ptr++;
+       }
+}
+
+/*
+* Read file into cache.
+* Note that one VCard can have multiple E-Mail addresses (MAIL tags);
+* these are broken out into separate address items. An address item
+* is generated for the person identified by FN tag and each EMAIL tag.
+* If a sub-type is included in the EMAIL entry, this will be used as
+* the Remarks member. Also note that it is possible for one VCard
+* entry to have multiple FN tags; this might not make sense. However,
+* it will generate duplicate address entries for each person listed.
+*/
+void vcard_read_cache( VCardFile *cardFile ) {
+       gchar *tagtemp = NULL, *tagname = NULL, *tagvalue = NULL, *tagtype = NULL, *tagrest = NULL;
+       GSList *listName = NULL, *listAddress = NULL, *listRemarks = NULL, *listID = NULL;
+       GSList *listQP = NULL;
+
+       for( ;; ) {
+               gchar *line =  vcard_get_line( cardFile );
+               if( line == NULL ) break;
+
+               // fprintf( stdout, "%s\n", line );
+
+               /* Parse line */
+               tagtemp = vcard_get_tagname( line, VCARD_SEP_TAG );
+               if( tagtemp ) {
+                       // fprintf( stdout, "\ttemp:  %s\n", tagtemp );
+                       tagvalue = vcard_get_tagvalue( line, VCARD_SEP_TAG );
+                       tagname = vcard_get_tagname( tagtemp, VCARD_SEP_TYPE );
+                       tagtype = vcard_get_tagvalue( tagtemp, VCARD_SEP_TYPE );
+                       if( tagname == NULL ) {
+                               tagname = tagtemp;
+                               tagtemp = NULL;
+                       }
+
+                       // fprintf( stdout, "\tname:  %s\n", tagname );
+                       // fprintf( stdout, "\ttype:  %s\n", tagtype );
+                       // fprintf( stdout, "\tvalue: %s\n", tagvalue );
+
+                       if( tagvalue ) {
+                               if( g_strcasecmp( tagtype, VCARD_TYPE_QP ) == 0 ) {
+                                       // Quoted-Printable: could span multiple lines
+                                       tagvalue = vcard_read_qp( cardFile, tagvalue );
+                                       vcard_unescape_qp( tagvalue );
+                                       // fprintf( stdout, "QUOTED-PRINTABLE !!! final\n>%s<\n", tagvalue );
+                               }
+
+                               if( g_strcasecmp( tagname, VCARD_TAG_START ) == 0 &&  g_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) {
+                                       // fprintf( stdout, "start card\n" );
+                                       vcard_free_lists( listName, listAddress, listRemarks, listID );
+                                       listName = listAddress = listRemarks = listID = NULL;
+                               }
+                               if( g_strcasecmp( tagname, VCARD_TAG_FULLNAME ) == 0 ) {
+                                       // fprintf( stdout, "- full name: %s\n", tagvalue );
+                                       listName = g_slist_append( listName, g_strdup( tagvalue ) );
+                               }
+                               if( g_strcasecmp( tagname, VCARD_TAG_EMAIL ) == 0 ) {
+                                       // fprintf( stdout, "- address: %s\n", tagvalue );
+                                       listAddress = g_slist_append( listAddress, g_strdup( tagvalue ) );
+                                       listRemarks = g_slist_append( listRemarks, g_strdup( tagtype ) );
+                               }
+                               if( g_strcasecmp( tagname, VCARD_TAG_UID ) == 0 ) {
+                                       // fprintf( stdout, "- id: %s\n", tagvalue );
+                                       listID = g_slist_append( listID, g_strdup( tagvalue ) );
+                               }
+                               if( g_strcasecmp( tagname, VCARD_TAG_END ) == 0 && g_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) {
+                                       // VCard is complete
+                                       // fprintf( stdout, "end card\n--\n" );
+                                       // vcard_dump_lists( listName, listAddress, listRemarks, listID, stdout );
+                                       vcard_build_items( cardFile, listName, listAddress, listRemarks, listID );
+                                       vcard_free_lists( listName, listAddress, listRemarks, listID );
+                                       listName = listAddress = listRemarks = listID = NULL;
+                               }
+                               g_free( tagvalue );
+                       }
+                       g_free( tagname );
+                       g_free( tagtype );
+               }
+       }
+
+       // Free lists
+       vcard_free_lists( listName, listAddress, listRemarks, listID );
+       listName = listAddress = listRemarks = listID = NULL;
+}
+
+// ============================================================================================
+/*
+* Read file into list. Main entry point
+* Return: TRUE if file read successfully.
+*/
+// ============================================================================================
+gint vcard_read_data( VCardFile *cardFile ) {
+       g_return_if_fail( cardFile != NULL );
+       cardFile->retVal = MGU_SUCCESS;
+       if( mgu_check_file( cardFile->addressCache, cardFile->path ) ) {
+               mgu_clear_cache( cardFile->addressCache );
+               vcard_open_file( cardFile );
+               if( cardFile->retVal == MGU_SUCCESS ) {
+                       // Read data into the list
+                       vcard_read_cache( cardFile );
+                       vcard_close_file( cardFile );
+
+                       // Mark cache
+                       mgu_mark_cache( cardFile->addressCache, cardFile->path );
+                       cardFile->addressCache->modified = FALSE;
+                       cardFile->addressCache->dataRead = TRUE;
+               }
+       }
+       return cardFile->retVal;
+}
+
+/*
+* Return link list of address items.
+* Return: TRUE if file read successfully.
+*/
+GList *vcard_get_address_list( VCardFile *cardFile ) {
+       g_return_if_fail( cardFile != NULL );
+       return cardFile->addressCache->addressList;
+}
+
+/*
+* Validate that all parameters specified.
+* Return: TRUE if data is good.
+*/
+gboolean vcard_validate( const VCardFile *cardFile ) {
+       gboolean retVal;
+       g_return_if_fail( cardFile != NULL );
+
+       retVal = TRUE;
+       if( cardFile->path ) {
+               if( strlen( cardFile->path ) < 1 ) retVal = FALSE;
+       }
+       else {
+               retVal = FALSE;
+       }
+       if( cardFile->name ) {
+               if( strlen( cardFile->name ) < 1 ) retVal = FALSE;
+       }
+       else {
+               retVal = FALSE;
+       }
+       return retVal;
+}
+
+#define WORK_BUFLEN 1024
+
+/*
+* Attempt to find a valid GnomeCard file.
+* Return: Filename, or home directory if not found. Filename should
+*      be g_free() when done.
+*/
+gchar *vcard_find_gnomecard( void ) {
+       gchar *homedir;
+       gchar buf[ WORK_BUFLEN ];
+       gchar str[ WORK_BUFLEN ];
+       gchar *fileSpec;
+       gint len, lenlbl, i;
+       FILE *fp;
+
+       homedir = g_get_home_dir();
+       if( ! homedir ) return NULL;
+
+       strcpy( str, homedir );
+       len = strlen( str );
+       if( len > 0 ) {
+               if( str[ len-1 ] != G_DIR_SEPARATOR ) {
+                       str[ len ] = G_DIR_SEPARATOR;
+                       str[ ++len ] = '\0';
+               }
+       }
+       strcat( str, GNOMECARD_DIR );
+       strcat( str, G_DIR_SEPARATOR_S );
+       strcat( str, GNOMECARD_FILE );
+
+       fileSpec = NULL;
+       if( ( fp = fopen( str, "r" ) ) != NULL ) {
+               // Read configuration file
+               lenlbl = strlen( GNOMECARD_SECTION );
+               while( fgets( buf, sizeof( buf ), fp ) != NULL ) {
+                       if( 0 == g_strncasecmp( buf, GNOMECARD_SECTION, lenlbl ) ) {
+                               break;
+                       }
+               }
+
+               while( fgets( buf, sizeof( buf ), fp ) != NULL ) {
+                       g_strchomp( buf );
+                       if( buf[0] == '[' ) break;
+                       for( i = 0; i < lenlbl; i++ ) {
+                               if( buf[i] == '=' ) {
+                                       if( 0 == g_strncasecmp( buf, GNOMECARD_PARAM, i ) ) {
+                                               fileSpec = g_strdup( buf + i + 1 );
+                                               g_strstrip( fileSpec );
+                                       }
+                               }
+                       }
+               }
+               fclose( fp );
+       }
+
+       if( fileSpec == NULL ) {
+               // Use the home directory
+               str[ len ] = '\0';
+               fileSpec = g_strdup( str );
+       }
+
+       return fileSpec;
+}
+
+/*
+* Attempt to read file, testing for valid VCard format.
+* Return: TRUE if file appears to be valid format.
+*/
+gint vcard_test_read_file( const gchar *fileSpec ) {
+       gboolean haveStart;
+       gchar *tagtemp = NULL, *tagname = NULL, *tagvalue = NULL, *tagtype = NULL, *tagrest = NULL, *line;
+       VCardFile *cardFile;
+       gint retVal, lines;
+
+       if( ! fileSpec ) return MGU_NO_FILE;
+
+       cardFile = vcard_create_path( fileSpec );
+       cardFile->retVal = MGU_SUCCESS;
+       vcard_open_file( cardFile );
+       if( cardFile->retVal == MGU_SUCCESS ) {
+               cardFile->retVal = MGU_BAD_FORMAT;
+               haveStart = FALSE;
+               lines = VCARD_TEST_LINES;
+               while( lines > 0 ) {
+                       lines--;
+                       if( ( line =  vcard_get_line( cardFile ) ) == NULL ) break;
+
+                       /* Parse line */
+                       tagtemp = vcard_get_tagname( line, VCARD_SEP_TAG );
+                       if( tagtemp ) {
+                               tagvalue = vcard_get_tagvalue( line, VCARD_SEP_TAG );
+                               tagname = vcard_get_tagname( tagtemp, VCARD_SEP_TYPE );
+                               tagtype = vcard_get_tagvalue( tagtemp, VCARD_SEP_TYPE );
+                               if( tagname == NULL ) {
+                                       tagname = tagtemp;
+                                       tagtemp = NULL;
+                               }
+
+                               if( tagvalue ) {
+                                       if( g_strcasecmp( tagtype, VCARD_TYPE_QP ) == 0 ) {
+                                               // Quoted-Printable: could span multiple lines
+                                               tagvalue = vcard_read_qp( cardFile, tagvalue );
+                                               vcard_unescape_qp( tagvalue );
+                                       }
+
+                                       if( g_strcasecmp( tagname, VCARD_TAG_START ) == 0 &&  g_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) {
+                                               haveStart = TRUE;
+                                       }
+                                       if( g_strcasecmp( tagname, VCARD_TAG_END ) == 0 && g_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) {
+                                               // VCard is complete
+                                               if( haveStart ) cardFile->retVal = MGU_SUCCESS;
+                                       }
+                                       g_free( tagvalue );
+                               }
+                               g_free( tagname );
+                               g_free( tagtype );
+                       }
+               }
+               vcard_close_file( cardFile );
+       }
+       retVal = cardFile->retVal;
+       vcard_free( cardFile );
+       cardFile = NULL;
+       return retVal;
+}
+
+/*
+* End of Source.
+*/
diff --git a/src/vcard.h b/src/vcard.h
new file mode 100644 (file)
index 0000000..dd5e420
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2001 Match Grun
+ *
+ * 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.
+ */
+
+/*
+ * Definitions necessary to access VCard files. VCard files are used
+ * by GnomeCard for addressbook, and Netscape for sending business
+ * card information. Refer to RFC2426 for more information.
+ */
+
+#ifndef __VCARD_H__
+#define __VCARD_H__
+
+#include <time.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include "mgutils.h"
+
+#define VCARDBUFSIZE       1024
+
+#define        VCARD_TAG_START    "begin"
+#define        VCARD_TAG_END      "end"
+#define        VCARD_NAME         "vcard"
+
+#define        VCARD_TAG_FULLNAME "fn"
+#define VCARD_TAG_NAME     "n"
+#define        VCARD_TAG_EMAIL    "email"
+#define VCARD_TAG_UID      "uid"
+
+#define VCARD_TYPE_QP      "quoted-printable"
+
+#define        VCARD_SEP_TAG   ':'
+#define        VCARD_SEP_TYPE  ';'
+
+/*
+// Typical VCard entry:
+//
+// BEGIN:VCARD
+// FN:Axle Rose
+// N:Rose;Axle;D;Ms;Jnr
+// REV:2001-04-22T03:52:05
+// ADR;HOME:;;777 Lexington Avenue;Denver;CO;80299;USA
+// ADR;POSTAL:P O Box 777;;;Denver;CO;80298;Usa
+// TEL;HOME:303-555-1234
+// EMAIL;AOL:axlerose@aol.com
+// EMAIL;INTERNET:axlerose@netscape.net
+// TITLE:Janitor
+// ORG:The Company
+// URL:http://www.axlerose.com
+// END:VCARD
+*/
+
+// VCard object
+typedef struct _VCardFile VCardFile;
+struct _VCardFile {
+       gchar        *name;
+       FILE         *file;
+       gchar        *path;
+       AddressCache *addressCache;
+       gchar        buffer[ VCARDBUFSIZE ];
+       gchar        *bufptr;
+       gint         retVal;
+};
+
+/* Function prototypes */
+VCardFile *vcard_create();
+VCardFile *vcard_create_path( const gchar *path );
+void vcard_force_refresh( VCardFile *cardFile );
+void vcard_free( VCardFile *cardFile );
+gint vcard_read_data( VCardFile *cardFile );
+GList *vcard_get_address_list( VCardFile *cardFile );
+gboolean vcard_validate( const VCardFile *cardFile );
+gchar *vcard_find_gnomecard( void );
+gint vcard_test_read_file( const gchar *fileSpec );
+
+#endif /* __VCARD_H__ */