Import Pine address book.
authorMatch Grun <match@dimensional.com>
Tue, 2 Apr 2002 06:57:31 +0000 (06:57 +0000)
committerMatch Grun <match@dimensional.com>
Tue, 2 Apr 2002 06:57:31 +0000 (06:57 +0000)
ChangeLog.claws
configure.in
po/POTFILES.in
src/Makefile.am
src/addressbook.c
src/importpine.c [new file with mode: 0644]
src/importpine.h [new file with mode: 0644]
src/pine.c [new file with mode: 0644]
src/pine.h [new file with mode: 0644]

index a17db1f..05f6803 100644 (file)
@@ -1,3 +1,10 @@
+2002-04-01 [match]     0.7.4claws60
+
+       * src/pine.[ch]
+       * src/importpine.[ch]
+       * src/addressbook.c
+               import Pine address book.
+
 2002-04-01 [match]     0.7.4claws60
 
        * src/addrharvest.[ch]
index 9be30a2..5b60771 100644 (file)
@@ -8,7 +8,7 @@ MINOR_VERSION=7
 MICRO_VERSION=4
 INTERFACE_AGE=0
 BINARY_AGE=0
-EXTRA_VERSION=claws60
+EXTRA_VERSION=claws61
 VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$EXTRA_VERSION
 
 dnl set $target
index f0be5ea..5f18fc4 100644 (file)
@@ -38,6 +38,7 @@ src/imap.c
 src/import.c
 src/importldif.c
 src/importmutt.c
+src/importpine.c
 src/inc.c
 src/inputdialog.c
 src/jpilot.c
@@ -59,6 +60,7 @@ src/mutt.c
 src/news.c
 src/nntp.c
 src/passphrase.c
+src/pine.c
 src/pop.c
 src/prefs.c
 src/prefs_account.c
index 6b0eedb..ce563ea 100644 (file)
@@ -54,6 +54,8 @@ sylpheed_SOURCES = \
        importldif.c importldif.h \
        mutt.c mutt.h \
        importmutt.c importmutt.h \
+       pine.c pine.h \
+       importpine.c importpine.h \
        jpilot.c jpilot.h \
        syldap.c syldap.h \
        editbook.c editbook.h \
index 5123a97..40a09eb 100644 (file)
@@ -78,6 +78,8 @@
 #include "importldif.h"
 #include "mutt.h"
 #include "importmutt.h"
+#include "pine.h"
+#include "importpine.h"
 
 #ifdef USE_JPILOT
 #include "jpilot.h"
@@ -332,6 +334,7 @@ static void addressbook_list_select_remove  ( AddrItemObject    *aio );
 
 static void addressbook_import_ldif_cb         ( void );
 static void addressbook_import_mutt_cb         ( void );
+static void addressbook_import_pine_cb         ( void );
 static void addressbook_clip_cut_cb            ( void );
 static void addressbook_clip_copy_cb           ( void );
 static void addressbook_clip_paste_cb          ( void );
@@ -373,6 +376,7 @@ static GtkItemFactoryEntry addressbook_entries[] =
        {N_("/_Tools/---"),             NULL,           NULL, 0, "<Separator>"},
        {N_("/_Tools/Import _LDIF file"), NULL,         addressbook_import_ldif_cb,     0, NULL},
        {N_("/_Tools/Import M_utt file"), NULL,         addressbook_import_mutt_cb,     0, NULL},
+       {N_("/_Tools/Import _Pine file"), NULL,         addressbook_import_pine_cb,     0, NULL},
        {N_("/_Help"),                  NULL,           NULL, 0, "<LastBranch>"},
        {N_("/_Help/_About"),           NULL,           about_show, 0, NULL}
 };
@@ -3924,6 +3928,44 @@ static void addressbook_import_mutt_cb() {
        }
 }
 
+/*
+* Import Pine file.
+*/
+static void addressbook_import_pine_cb() {
+       AddressDataSource *ds = NULL;
+       AdapterDSource *ads = NULL;
+       AddressBookFile *abf = NULL;
+       AdapterInterface *adapter;
+       GtkCTreeNode *newNode;
+
+       adapter = addrbookctl_find_interface( ADDR_IF_BOOK );
+       if( adapter ) {
+               if( adapter->treeNode ) {
+                       abf = addressbook_imp_pine( _addressIndex_ );
+                       if( abf ) {
+                               ds = addrindex_index_add_datasource(
+                                       _addressIndex_, ADDR_IF_BOOK, abf );
+                               ads = addressbook_create_ds_adapter(
+                                       ds, ADDR_BOOK, NULL );
+                               addressbook_ads_set_name(
+                                       ads, addrbook_get_name( abf ) );
+                               newNode = addressbook_add_object(
+                                       adapter->treeNode,
+                                       ADDRESS_OBJECT(ads) );
+                               if( newNode ) {
+                                       gtk_ctree_select(
+                                               GTK_CTREE(addrbook.ctree),
+                                               newNode );
+                                       addrbook.treeSelected = newNode;
+                               }
+
+                               /* Notify address completion */
+                               invalidate_address_completion();
+                       }
+               }
+       }
+}
+
 /*
 * Gather addresses.
 * Enter: folderItem Folder to import.
diff --git a/src/importpine.c b/src/importpine.c
new file mode 100644 (file)
index 0000000..c352036
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2002 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.
+ */
+
+/*
+ * Import Pine 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 "addrbook.h"
+#include "addressbook.h"
+#include "addressitem.h"
+#include "gtkutils.h"
+#include "prefs_common.h"
+#include "manage_window.h"
+#include "mgutils.h"
+#include "pine.h"
+
+#define IMPORTPINE_GUESS_NAME "Pine Import"
+
+static struct _ImpPine_Dlg {
+       GtkWidget *window;
+       GtkWidget *file_entry;
+       GtkWidget *name_entry;
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_btn;
+       GtkWidget *statusbar;
+       gint status_cid;
+} imppine_dlg;
+
+static struct _AddressFileSelection _imp_pine_file_selector_;
+static AddressBookFile *_importedBook_;
+static AddressIndex *_imp_addressIndex_;
+
+/*
+* Edit functions.
+*/
+void imp_pine_status_show( gchar *msg ) {
+       if( imppine_dlg.statusbar != NULL ) {
+               gtk_statusbar_pop( GTK_STATUSBAR(imppine_dlg.statusbar), imppine_dlg.status_cid );
+               if( msg ) {
+                       gtk_statusbar_push( GTK_STATUSBAR(imppine_dlg.statusbar), imppine_dlg.status_cid, msg );
+               }
+       }
+}
+
+static gboolean imp_pine_import_file( gchar *sName, gchar *sFile ) {
+       gboolean retVal = FALSE;
+       gchar *newFile;
+       AddressBookFile *abf = NULL;
+       PineFile *pdf = NULL;
+
+       if( _importedBook_ ) {
+               addrbook_free_book( _importedBook_ );
+       }
+
+       abf = addrbook_create_book();
+       addrbook_set_path( abf, _imp_addressIndex_->filePath );
+       addrbook_set_name( abf, sName );
+       newFile = addrbook_guess_next_file( abf );
+       addrbook_set_file( abf, newFile );
+       g_free( newFile );
+
+       /* Import data from file */
+       pdf = pine_create();
+       pine_set_file( pdf, sFile );
+       if( pine_import_data( pdf, abf->addressCache ) == MGU_SUCCESS ) {
+               addrbook_save_data( abf );
+               _importedBook_ = abf;
+               retVal = TRUE;
+       }
+       else {
+               addrbook_free_book( abf );
+       }
+
+       return retVal;
+}
+
+static void imp_pine_ok( GtkWidget *widget, gboolean *cancelled ) {
+       gchar *sName;
+       gchar *sFile;
+       gchar *sMsg = NULL;
+       gboolean errFlag = FALSE;
+
+       sFile = gtk_editable_get_chars( GTK_EDITABLE(imppine_dlg.file_entry), 0, -1 );
+       g_strchug( sFile ); g_strchomp( sFile );
+       gtk_entry_set_text( GTK_ENTRY(imppine_dlg.file_entry), sFile );
+
+       sName = gtk_editable_get_chars( GTK_EDITABLE(imppine_dlg.name_entry), 0, -1 );
+       g_strchug( sName ); g_strchomp( sName );
+       gtk_entry_set_text( GTK_ENTRY(imppine_dlg.name_entry), sName );
+
+       if( *sFile == '\0'|| strlen( sFile ) < 1 ) {
+               sMsg = _( "Please select a file." );
+               errFlag = TRUE;
+       }
+
+       if( *sName == '\0'|| strlen( sName ) < 1 ) {
+               if( ! errFlag ) sMsg = _( "Address book name must be supplied." );
+               errFlag = TRUE;
+       }
+
+       if( errFlag ) {
+               imp_pine_status_show( sMsg );
+       }
+       else {
+               /* Import the file */
+               if( imp_pine_import_file( sName, sFile ) ) {
+                       *cancelled = FALSE;
+                       gtk_main_quit();
+               }
+               else {
+                       imp_pine_status_show( _( "Error importing Pine file." ) );
+               }
+       }
+
+       g_free( sFile );
+       g_free( sName );
+
+}
+
+static void imp_pine_cancel( GtkWidget *widget, gboolean *cancelled ) {
+       *cancelled = TRUE;
+       gtk_main_quit();
+}
+
+static void imp_pine_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(imppine_dlg.file_entry), sFile );
+       gtk_widget_hide( afs->fileSelector );
+       gtk_grab_remove( afs->fileSelector );
+       gtk_widget_grab_focus( imppine_dlg.file_entry );
+       imp_pine_status_show( _( "Please select a file to import." ) );
+}
+
+static void imp_pine_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( imppine_dlg.file_entry );
+}
+
+static void imp_pine_file_select_create( AddressFileSelection *afs ) {
+       GtkWidget *fileSelector;
+
+       fileSelector = gtk_file_selection_new( _("Select Pine 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 (imp_pine_file_ok), ( gpointer ) afs );
+       gtk_signal_connect( GTK_OBJECT (GTK_FILE_SELECTION(fileSelector)->cancel_button),
+                             "clicked", GTK_SIGNAL_FUNC (imp_pine_file_cancel), ( gpointer ) afs );
+       afs->fileSelector = fileSelector;
+       afs->cancelled = TRUE;
+}
+
+static void imp_pine_file_select( void ) {
+       gchar *sFile;
+       if (! _imp_pine_file_selector_.fileSelector )
+               imp_pine_file_select_create( & _imp_pine_file_selector_ );
+
+       sFile = gtk_editable_get_chars( GTK_EDITABLE(imppine_dlg.file_entry), 0, -1 );
+       gtk_file_selection_set_filename( GTK_FILE_SELECTION( _imp_pine_file_selector_.fileSelector ), sFile );
+       g_free( sFile );
+       gtk_widget_show( _imp_pine_file_selector_.fileSelector );
+       gtk_grab_add( _imp_pine_file_selector_.fileSelector );
+}
+
+static gint imp_pine_delete_event( GtkWidget *widget, GdkEventAny *event, gboolean *cancelled ) {
+       *cancelled = TRUE;
+       gtk_main_quit();
+       return TRUE;
+}
+
+static void imp_pine_key_pressed( GtkWidget *widget, GdkEventKey *event, gboolean *cancelled ) {
+       if (event && event->keyval == GDK_Escape) {
+               *cancelled = TRUE;
+               gtk_main_quit();
+       }
+}
+
+static void imp_pine_create( gboolean *cancelled ) {
+       GtkWidget *window;
+       GtkWidget *vbox;
+       GtkWidget *table;
+       GtkWidget *label;
+       GtkWidget *file_entry;
+       GtkWidget *name_entry;
+       GtkWidget *hbbox;
+       GtkWidget *hsep;
+       GtkWidget *ok_btn;
+       GtkWidget *cancel_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), _("Import Pine file into Address Book") );
+       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(imp_pine_delete_event),
+                          cancelled);
+       gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
+                          GTK_SIGNAL_FUNC(imp_pine_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);
+
+       /* 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(imp_pine_ok), cancelled);
+       gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
+                          GTK_SIGNAL_FUNC(imp_pine_cancel), cancelled);
+       gtk_signal_connect(GTK_OBJECT(file_btn), "clicked",
+                          GTK_SIGNAL_FUNC(imp_pine_file_select), NULL);
+
+       gtk_widget_show_all(vbox);
+
+       imppine_dlg.window     = window;
+       imppine_dlg.file_entry = file_entry;
+       imppine_dlg.name_entry = name_entry;
+       imppine_dlg.ok_btn     = ok_btn;
+       imppine_dlg.cancel_btn = cancel_btn;
+       imppine_dlg.statusbar  = statusbar;
+       imppine_dlg.status_cid = gtk_statusbar_get_context_id(
+                      GTK_STATUSBAR(statusbar), "Import Pine Dialog" );
+}
+
+AddressBookFile *addressbook_imp_pine( AddressIndex *addrIndex ) {
+       static gboolean cancelled;
+       gchar *pineFile;
+
+       _importedBook_ = NULL;
+       _imp_addressIndex_ = addrIndex;
+
+       if( ! imppine_dlg.window )
+               imp_pine_create(&cancelled);
+       gtk_widget_grab_focus(imppine_dlg.ok_btn);
+       gtk_widget_grab_focus(imppine_dlg.file_entry);
+       gtk_widget_show(imppine_dlg.window);
+       manage_window_set_transient(GTK_WINDOW(imppine_dlg.window));
+
+       imp_pine_status_show( _( "Please select a file to import." ) );
+       pineFile = pine_find_file();
+       gtk_entry_set_text( GTK_ENTRY(imppine_dlg.name_entry), IMPORTPINE_GUESS_NAME );
+       gtk_entry_set_text( GTK_ENTRY(imppine_dlg.file_entry), pineFile );
+       g_free( pineFile );
+       pineFile = NULL;
+
+       gtk_main();
+       gtk_widget_hide(imppine_dlg.window);
+       _imp_addressIndex_ = NULL;
+
+       if (cancelled == TRUE) return NULL;
+       return _importedBook_;
+}
+
+/*
+* End of Source.
+*/
+
diff --git a/src/importpine.h b/src/importpine.h
new file mode 100644 (file)
index 0000000..8a28d79
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2002 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.
+ */
+
+/*
+ * Import Pine data.
+ */
+
+#ifndef __IMPORT_PINE_H__
+#define __IMPORT_PINE_H__
+
+/* Function prototypes */
+AddressBookFile *addressbook_imp_pine( AddressIndex *addrIndex );
+
+#endif /* __IMPORT_PINE_H__ */
+
+/*
+* End of Source.
+*/
+
diff --git a/src/pine.c b/src/pine.c
new file mode 100644 (file)
index 0000000..701f8e3
--- /dev/null
@@ -0,0 +1,695 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2002 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 Pine address book file.
+ */
+
+#include <sys/stat.h>
+#include <glib.h>
+#include <string.h>
+
+#include "utils.h"
+#include "mgutils.h"
+#include "pine.h"
+#include "addritem.h"
+#include "addrcache.h"
+
+#define PINE_HOME_FILE  ".addressbook"
+#define PINEBUFSIZE     2048
+#define CHAR_QUOTE      '\"'
+#define CHAR_APOS       '\''
+#define CHAR_COMMA      ','
+#define CHAR_AT         '@'
+
+/*
+* Create new object.
+*/
+PineFile *pine_create() {
+       PineFile *pineFile;
+       pineFile = g_new0( PineFile, 1 );
+       pineFile->path = NULL;
+       pineFile->file = NULL;
+       pineFile->retVal = MGU_SUCCESS;
+       pineFile->uniqTable = g_hash_table_new( g_str_hash, g_str_equal );
+       pineFile->cbProgress = NULL;
+       return pineFile;
+}
+
+/*
+* Properties...
+*/
+void pine_set_file( PineFile* pineFile, const gchar *value ) {
+       g_return_if_fail( pineFile != NULL );
+       pineFile->path = mgu_replace_string( pineFile->path, value );
+       g_strstrip( pineFile->path );
+}
+
+/*
+* Register a callback function. When called, the function will be passed
+* the following arguments:
+*      PineFile object,
+*      File size (long),
+*      Current position (long)
+* This can be used for a progress indicator.
+*/
+void pine_set_callback( PineFile *pineFile, void *func ) {
+       pineFile->cbProgress = func;
+}
+
+/*
+ * Free key in table.
+ */
+static gint pine_free_table_vis( gpointer key, gpointer value, gpointer data ) {
+       g_free( key );
+       key = NULL;
+       value = NULL;
+       return TRUE;
+}
+
+/*
+* Free up object by releasing internal memory.
+*/
+void pine_free( PineFile *pineFile ) {
+       g_return_if_fail( pineFile != NULL );
+
+       /* Close file */
+       if( pineFile->file ) fclose( pineFile->file );
+
+       /* Free internal stuff */
+       g_free( pineFile->path );
+
+       /* Free unique address table */
+       g_hash_table_freeze( pineFile->uniqTable );
+       g_hash_table_foreach_remove( pineFile->uniqTable, pine_free_table_vis, NULL );
+       g_hash_table_thaw( pineFile->uniqTable );
+       g_hash_table_destroy( pineFile->uniqTable );
+
+       /* Clear pointers */
+       pineFile->file = NULL;
+       pineFile->path = NULL;
+       pineFile->retVal = MGU_SUCCESS;
+       pineFile->uniqTable = NULL;
+       pineFile->cbProgress = NULL;
+
+       /* Now release file object */
+       g_free( pineFile );
+}
+
+/*
+ * Display object to specified stream.
+ * Enter: pineFile File object.
+ *        stream   File..
+ */
+void pine_print_file( PineFile *pineFile, FILE *stream ) {
+       g_return_if_fail( pineFile != NULL );
+       fprintf( stream, "Pine File:\n" );
+       fprintf( stream, "file spec: '%s'\n", pineFile->path );
+       fprintf( stream, "  ret val: %d\n",   pineFile->retVal );
+}
+
+/*
+ * Open file for read.
+ * Enter: pineFile File object.
+ * return: TRUE if file opened successfully.
+ */
+static gint pine_open_file( PineFile* pineFile ) {
+       if( pineFile->path ) {
+               pineFile->file = fopen( pineFile->path, "rb" );
+               if( ! pineFile->file ) {
+                       pineFile->retVal = MGU_OPEN_FILE;
+                       return pineFile->retVal;
+               }
+       }
+       else {
+               /* printf( "file not specified\n" ); */
+               pineFile->retVal = MGU_NO_FILE;
+               return pineFile->retVal;
+       }
+
+       /* Setup a buffer area */
+       pineFile->retVal = MGU_SUCCESS;
+       return pineFile->retVal;
+}
+
+/*
+ * Close file.
+ * Enter: pineFile File object.
+ */
+static void pine_close_file( PineFile *pineFile ) {
+       g_return_if_fail( pineFile != NULL );
+       if( pineFile->file ) fclose( pineFile->file );
+       pineFile->file = NULL;
+}
+
+/*
+ * Read line of text from file.
+ * Enter: pineFile File object.
+ * Return: Copy of buffer. Should be g_free'd when done.
+ */
+static gchar *pine_read_line( PineFile *pineFile ) {
+       gchar buf[ PINEBUFSIZE ];
+       gchar ch;
+       gchar *ptr;
+
+       if( feof( pineFile->file ) ) return NULL;
+
+       ptr = buf;
+       while( TRUE ) {
+               *ptr = '\0';
+               ch = fgetc( pineFile->file );
+               if( ch == '\0' || ch == EOF ) {
+                       if( *buf == '\0' ) return NULL;
+                       break;
+               }
+               if( ch == '\n' ) {
+                       break;
+               }
+               *ptr = ch;
+               ptr++;
+       }
+
+       /* Copy into private buffer */
+       return g_strdup( buf );
+}
+
+/*
+ * Parsed address data.
+ */
+typedef struct _Pine_ParsedRec_ Pine_ParsedRec;
+struct _Pine_ParsedRec_ {
+       gchar *nickName;
+       gchar *name;
+       gchar *address;
+       gchar *fcc;
+       gchar *comments;
+       gboolean isGroup;
+       GSList *listName;
+       GSList *listAddr;
+};
+
+/*
+ * Free data record.
+ * Enter: rec Data record.
+ */
+static pine_free_rec( Pine_ParsedRec *rec ) {
+       if( rec ) {
+               g_free( rec->nickName );
+               g_free( rec->name );
+               g_free( rec->address );
+               g_free( rec->fcc );
+               g_free( rec->comments );
+               mgu_clear_slist( rec->listName );
+               mgu_clear_slist( rec->listAddr );
+               g_slist_free( rec->listName );
+               g_slist_free( rec->listAddr );
+               rec->nickName = NULL;
+               rec->name = NULL;
+               rec->address = NULL;
+               rec->fcc = NULL;
+               rec->comments = NULL;
+               rec->isGroup = FALSE;
+               g_free( rec );
+       }
+}
+
+/*
+ * Print data record.
+ * Enter: rec    Data record.
+ *        stream File.
+ */
+static void pine_print_rec( Pine_ParsedRec *rec, FILE *stream ) {
+       fprintf( stream, "nick :%s:\n", rec->nickName );
+       fprintf( stream, "name :%s:\n", rec->name );
+       fprintf( stream, "addr :%s:\n", rec->address );
+       fprintf( stream, "fcc  :%s:\n", rec->fcc );
+       fprintf( stream, "comm :%s:\n", rec->comments );
+       fprintf( stream, "group:%s:\n", rec->isGroup ? "yes" : "no" );
+}
+
+/*
+ * Clean name.
+ */
+static void pine_clean_name( Pine_ParsedRec *rec ) {
+       gchar *p;
+
+       p = rec->name;
+       if( p == NULL ) return;
+       if( *p == '\0' ) return;
+
+       g_strstrip( rec->name );
+       if( *p == CHAR_APOS || *p == CHAR_QUOTE ) {
+               return;
+       }
+
+       /* If embedded comma present, surround match with quotes */
+       while( *p ) {
+               if( *p == CHAR_COMMA ) {
+                       p = g_strdup_printf( "\"%s\"", rec->name );
+                       g_free( rec->name );
+                       rec->name = p;
+                       return;
+               }
+               p++;
+       }
+}
+
+/*
+ * Parse pine address record.
+ * Enter:  buf Address record buffer.
+ * Return: Data record.
+ */
+static Pine_ParsedRec *pine_parse_record( gchar *buf ) {
+       Pine_ParsedRec *rec;
+       gchar *p, *f;
+       gint pos, len, i;
+       gchar *tmp[5];
+
+       for( i = 0; i < 5; i++ )
+               tmp[i] = NULL;
+
+       /* Extract tab separated values */
+       rec = NULL;
+       pos = 0;
+       p = f = buf;
+       while( *p ) {
+               if( *p == '\t' ) {
+                       len = p - f;
+                       if( len > 0 ) {
+                               tmp[ pos ] = g_strndup( f, len );
+                               f = p;
+                               f++;
+                       }
+                       pos++;
+               }
+               p++;
+       }
+
+       /* Extract last value */
+       len = p - f;
+       if( len > 0 ) {
+               tmp[ pos++ ] = g_strndup( f, len );
+       }
+
+       /* Populate record */
+       if( pos > 0 ) {
+               rec = g_new0( Pine_ParsedRec, 1 );
+               rec->isGroup = FALSE;
+               for( i = 0; i < pos; i++ ) {
+                       f = tmp[i];
+                       if( f ) {
+                               g_strstrip( f );
+                       }
+                       if( i == 0 ) rec->nickName = f;
+                       else if( i == 1 ) rec->name = f;
+                       else if( i == 2 ) rec->address = f;
+                       else if( i == 3 ) rec->fcc = f;
+                       else if( i == 4 ) rec->comments = f;
+                       tmp[i] = NULL;
+               }
+
+               if( rec->address != NULL ) {
+                       /* Strip leading/trailing parens */
+                       p = rec->address;
+                       if( *p == '(' ) {
+                               len = strlen( p ) - 1;
+                               *p = ' ';
+                               *(p + len) = ' ';
+                               rec->isGroup = TRUE;
+                       }
+               }
+       }
+
+       return rec;
+}
+
+/*
+ * Parse name from email address string.
+ * Enter: buf Start address of buffer to process (not modified).
+ *        atp Pointer to email at (@) character.
+ *        ap  Pointer to start of email address returned.
+ *        ep  Pointer to end of email address returned.
+ * Return: Parsed name or NULL if not present. This should be g_free'd
+ * when done.
+ */
+static gchar *pine_parse_name(
+               const gchar *buf, const gchar *atp, const gchar **ap,
+               const gchar **ep )
+{
+       gchar *name;
+       const gchar *pos;
+       const gchar *tmp;
+       const gchar *bp;
+       gint ilen;
+
+       name = NULL;
+       *ap = NULL;
+       *ep = NULL;
+
+       /* Find first non-separator char */
+       bp = buf;
+       while( TRUE ) {
+               if( strchr( ",; \n\r", *bp ) == NULL ) break;
+               bp++;
+       }
+
+       /* Search back for start of name */
+       tmp = atp;
+       pos = atp;
+       while( pos >= bp ) {
+               tmp = pos;
+               if( *pos == '<' ) {
+                       /* Found start of address/end of name part */
+                       ilen = -1 + ( size_t ) ( pos - bp );
+                       name = g_strndup( bp, ilen + 1 );
+                       *(name + ilen + 1) = '\0';
+
+                       /* Remove leading trailing quotes and spaces */
+                       mgu_str_ltc2space( name, '\"', '\"' );
+                       mgu_str_ltc2space( name, '\'', '\'' );
+                       mgu_str_ltc2space( name, '\"', '\"' );
+                       mgu_str_unescape( name );
+                       g_strstrip( name );
+                       break;
+               }
+               pos--;
+       }
+       *ap = tmp;
+
+       /* Search forward for end of address */
+       pos = atp + 1;
+       while( TRUE ) {
+               if( *pos == '>' ) {
+                       pos++;
+                       break;
+               }
+               if( strchr( ",; \'\n\r", *pos ) ) break;
+               pos++;
+       }
+       *ep = pos;
+
+       return name;
+}
+
+/*
+ * Parse address list.
+ * Enter: pineFile Pine control data.
+ *        cache    Address cache.
+ *        rec      Data record.
+ */
+static void pine_parse_address( PineFile *pineFile, AddressCache *cache, Pine_ParsedRec *rec ) {
+       const gchar *buf;
+       gchar addr[ PINEBUFSIZE ];
+       const gchar *bp;
+       const gchar *ep;
+       gchar *atCh;
+       gchar *name;
+       gint len;
+
+       buf = rec->address;
+       while( atCh = strchr( buf, CHAR_AT ) ) {
+               name = pine_parse_name( buf, atCh, &bp, &ep );
+               len = ( size_t ) ( ep - bp );
+               strncpy( addr, bp, len );
+               addr[ len ] = '\0';
+               extract_address( addr );
+
+               if( name == NULL ) name = g_strdup( "" );
+               rec->listName = g_slist_append( rec->listName, name );
+               rec->listAddr = g_slist_append( rec->listAddr, g_strdup( addr ) );
+
+               buf = ep;
+               if( atCh == ep ) {
+                       buf++;
+               }
+       }
+}
+
+/*
+ * Insert person and address into address cache.
+ * Enter: pineFile Pine control data.
+ *        cache    Address cache.
+ *        address  E-Mail address.
+ *        name     Name.
+ *        remarks  Remarks.
+ * Return: E-Mail object, either inserted or found in hash table.
+ */
+static ItemEMail *pine_insert_table(
+               PineFile *pineFile, AddressCache *cache, gchar *address,
+               gchar *name, gchar *remarks )
+{
+       ItemPerson *person;
+       ItemEMail *email;
+       gchar *key;
+
+       /* Test whether address already in hash table */
+       key = g_strdup( address );
+       g_strdown( key );
+       email = g_hash_table_lookup( pineFile->uniqTable, key );
+
+       if( email == NULL ) {
+               /* No - create person */
+               person = addritem_create_item_person();
+               addritem_person_set_common_name( person, name );
+               addrcache_id_person( cache, person );
+               addrcache_add_person( cache, person );
+
+               /* Add email for person */
+               email = addritem_create_item_email();
+               addritem_email_set_address( email, address );
+               addritem_email_set_remarks( email, remarks );
+               addrcache_id_email( cache, email );
+               addrcache_person_add_email( cache, person, email );
+
+               /* Insert entry */
+               g_hash_table_insert( pineFile->uniqTable, key, email );
+       }
+       else {
+               /* Yes - update person with longest name */
+               person = ( ItemPerson * ) ADDRITEM_PARENT(email);
+               if( strlen( name ) > strlen( ADDRITEM_NAME(person) ) ) {
+                       addritem_person_set_common_name( person, name );
+               }
+
+               /* Free up */
+               g_free( key );
+       }
+
+       return email;
+}
+
+/*
+ * Parse address line adn build address items.
+ * Enter: pineFile Pine control data.
+ *        cache    Address cache to load.
+ *        line     Address record.
+ */
+static void pine_build_items( PineFile *pineFile, AddressCache *cache, gchar *line ) {
+       Pine_ParsedRec *rec;
+       GSList *nodeAddr, *nodeName;
+       ItemGroup *group;
+       ItemEMail *email;
+
+       rec = pine_parse_record( line );
+       if( rec ) {
+               pine_clean_name( rec );
+               pine_parse_address( pineFile, cache, rec );
+               /* pine_print_rec( rec, stdout ); */
+               /* printf( "=========\n" ); */
+
+               if( rec->isGroup ) {
+                       /* Create group */
+                       group = addritem_create_item_group();
+                       addritem_group_set_name( group, rec->nickName );
+                       addrcache_id_group( cache, group );
+                       addrcache_add_group( cache, group );
+
+                       /* Add email to group */
+                       nodeName = rec->listName;
+                       nodeAddr = rec->listAddr;
+                       while( nodeAddr ) {
+                               email = pine_insert_table(
+                                               pineFile, cache, nodeAddr->data,
+                                               nodeName->data, "" );
+
+                               /* Add email to group */
+                               addritem_group_add_email( group, email );
+
+                               nodeAddr = g_slist_next( nodeAddr );
+                               nodeName = g_slist_next( nodeName );
+                       }
+               }
+               else {
+                       email = pine_insert_table(
+                                       pineFile, cache, rec->address,
+                                       rec->name, rec->comments );
+               }
+
+               pine_free_rec( rec );
+       }
+}
+
+/*
+ * Read file data into address cache.
+ * Enter: pineFile Pine control data.
+ *        cache    Address cache to load.
+ */
+static void pine_read_file( PineFile *pineFile, AddressCache *cache ) {
+       GSList *listValue = NULL;
+       gboolean flagEOF = FALSE, flagProc = FALSE, flagDone = FALSE;
+       gchar *line =  NULL, *lineValue = NULL;
+       long posEnd = 0L;
+       long posCur = 0L;
+
+       /* Find EOF for progress indicator */
+       fseek( pineFile->file, 0L, SEEK_END );
+       posEnd = ftell( pineFile->file );
+       fseek( pineFile->file, 0L, SEEK_SET );
+
+       flagProc = FALSE;
+       while( ! flagDone ) {
+               if( flagEOF ) {
+                       flagDone = TRUE;
+                       flagProc = TRUE;
+               }
+               else {
+                       line =  pine_read_line( pineFile );
+               }
+
+               posCur = ftell( pineFile->file );
+               if( pineFile->cbProgress ) {
+                       /* Call progress indicator */
+                       ( pineFile->cbProgress ) ( pineFile, & posEnd, & posCur );
+               }
+
+               /* Add line to list */
+               if( line == NULL ) {
+                       flagEOF = TRUE;
+               }
+               else {
+                       /* Check for continuation line (1 space only) */
+                       if( *line == ' ' ) {
+                               g_strchug( line );
+                               listValue = g_slist_append(
+                                               listValue, g_strdup( line ) );
+                               flagProc = FALSE;
+                       }
+                       else {
+                               flagProc = TRUE;
+                       }
+               }
+
+               if( flagProc ) {
+                       if( listValue != NULL ) {
+                               /* Process list */
+                               lineValue = mgu_list_coalesce( listValue );
+                               if( lineValue ) {
+                                       pine_build_items(
+                                               pineFile, cache, lineValue );
+                               }
+                               g_free( lineValue );
+                               lineValue = NULL;
+                               mgu_free_list( listValue );
+                               listValue = NULL;
+                       }
+                       if( line != NULL ) {
+                               /* Append to list */
+                               listValue = g_slist_append(
+                                               listValue, g_strdup( line ) );
+                       }
+               }
+
+               g_free( line );
+               line = NULL;
+       }
+
+       /* Release data */
+       mgu_free_list( listValue );
+       listValue = NULL;
+}
+
+/*
+ * ============================================================================================
+ * Read file into list. Main entry point
+ * Enter:  pineFile Pine control data.
+ *         cache    Address cache to load.
+ * Return: Status code.
+ * ============================================================================================
+ */
+gint pine_import_data( PineFile *pineFile, AddressCache *cache ) {
+       g_return_val_if_fail( pineFile != NULL, MGU_BAD_ARGS );
+       g_return_val_if_fail( cache != NULL, MGU_BAD_ARGS );
+
+       pineFile->retVal = MGU_SUCCESS;
+       addrcache_clear( cache );
+       cache->dataRead = FALSE;
+       pine_open_file( pineFile );
+       if( pineFile->retVal == MGU_SUCCESS ) {
+               /* Read data into the cache */
+               pine_read_file( pineFile, cache );
+               pine_close_file( pineFile );
+
+               /* Mark cache */
+               cache->modified = FALSE;
+               cache->dataRead = TRUE;
+       }
+       return pineFile->retVal;
+}
+
+#define WORK_BUFLEN 1024
+
+/*
+ * Attempt to find a Pine addressbook file.
+ * Return: Filename, or home directory if not found, or empty string if
+ * no home. Filename should be g_free() when done.
+ */
+gchar *pine_find_file( 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, PINE_HOME_FILE );
+
+       /* Attempt to open */
+       if( ( fp = fopen( str, "rb" ) ) != NULL ) {
+               fclose( fp );
+       }
+       else {
+               /* Truncate filename */
+               str[ len ] = '\0';
+       }
+       return g_strdup( str );
+}
+
+/*
+* End of Source.
+*/
+
diff --git a/src/pine.h b/src/pine.h
new file mode 100644 (file)
index 0000000..744621b
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2002 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 Pine addressbook files. These are
+ * used by the Pine E-Mail client.
+ */
+
+#ifndef __PINE_H__
+#define __PINE_H__
+
+#include <stdio.h>
+#include <glib.h>
+
+#include "addrcache.h"
+
+/*
+* Typical Pine simple entry:
+*
+*   nick-name \t friendly-name \t email-address \t fcc \t comments
+*
+* Or for a distribution list:
+*
+*   group-name \t friendly-name \t ( email, email, ... ) \t fcc \t comments
+*
+* The record is may span several lines if longer than about 80 characters.
+* Pine formats subsequent line with exactly 3 spaces to indicate that the
+* line is a continuation.
+*
+* An example:
+* 
+* axe \t Axel Rose \t axelrose@aol.com \t \t Guitar player
+*  \t Axel Rose \t axelrose@aol.com \t \t Guitarist
+* evilaxis \t \t (bgates@hotmail.com,shoebomber@empire.com) \t \t Axis of Evil
+*
+*/
+
+/* Pine file object */
+typedef struct _PineFile PineFile;
+struct _PineFile {
+       FILE  *file;
+       gchar *path;
+       gint  retVal;
+       GHashTable *uniqTable;
+       void  (*cbProgress)( void *, void *, void * );
+};
+
+/* Function prototypes */
+PineFile *pine_create  ( void );
+void pine_set_file     ( PineFile* pineFile, const gchar *value );
+void pine_free         ( PineFile *pineFile );
+gint pine_import_data  ( PineFile *pineFile, AddressCache *cache );
+gchar *pine_find_file  ( void );
+
+#endif /* __PINE_H__ */
+