Add export LDIF.
[claws.git] / src / ldif.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001-2003 Match Grun
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Functions necessary to access LDIF files (LDAP Data Interchange Format
22  * files).
23  */
24
25 #include <glib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28
29 #include "intl.h"
30 #include "mgutils.h"
31 #include "ldif.h"
32 #include "addritem.h"
33 #include "addrcache.h"
34
35 #include "base64.h"
36
37 #define LDIF_SEP_TAG    ':'
38 #define LDIF_LANG_TAG   ';'
39
40 /**
41  * Create new object.
42  * \return Initialized LDIF file object.
43  */
44 LdifFile *ldif_create() {
45         LdifFile *ldifFile;
46         ldifFile = g_new0( LdifFile, 1 );
47         ldifFile->path = NULL;
48         ldifFile->file = NULL;
49         ldifFile->bufptr = ldifFile->buffer;
50         ldifFile->hashFields = g_hash_table_new( g_str_hash, g_str_equal );
51         ldifFile->tempList = NULL;
52         ldifFile->dirtyFlag = TRUE;
53         ldifFile->accessFlag = FALSE;
54         ldifFile->retVal = MGU_SUCCESS;
55         ldifFile->cbProgress = NULL;
56         ldifFile->importCount = 0;
57         return ldifFile;
58 }
59
60 /**
61  * Specify full file specification of LDIF file.
62  * \param ldifFile LDIF import control object.
63  * \param value    Value of access flag.
64  */
65 void ldif_set_file( LdifFile *ldifFile, const gchar *value ) {
66         g_return_if_fail( ldifFile != NULL );
67
68         if( ldifFile->path ) {
69                 if( strcmp( ldifFile->path, value ) != 0 )
70                         ldifFile->dirtyFlag = TRUE;
71         }
72         else {
73                 ldifFile->dirtyFlag = TRUE;
74         }
75         ldifFile->path = mgu_replace_string( ldifFile->path, value );
76         g_strstrip( ldifFile->path );
77         ldifFile->importCount = 0;
78 }
79
80 /**
81  * Set the file access indicator.
82  * \param ldifFile LDIF import control object.
83  * \param value    File specification.
84  */
85 void ldif_set_accessed( LdifFile *ldifFile, const gboolean value ) {
86         g_return_if_fail( ldifFile != NULL );
87         ldifFile->accessFlag = value;
88 }
89
90 /**
91  * Register a progress indicator callback function.
92  *
93  * \param ldifFile LDIF import control object.
94  * \param func     Function to be called. When called, the function will be
95  *                 passed the following arguments:
96  *
97  * <ul>
98  * <li>LdifFile object,</li>
99  * <li>File size (long),</li>
100  * <li>Current position (long)</li>
101  * </ul>
102  */
103 void ldif_set_callback( LdifFile *ldifFile, void *func ) {
104         ldifFile->cbProgress = func;
105 }
106
107 /**
108  * Create field record object.
109  * \return Initialized LDIF field object.
110  */
111 static Ldif_FieldRec *ldif_create_fieldrec( const gchar *field ) {
112         Ldif_FieldRec *rec = g_new0( Ldif_FieldRec, 1 );
113         rec->tagName = g_strdup( field );
114         rec->userName = NULL;
115         rec->reserved = FALSE;
116         rec->selected = FALSE;
117         return rec;
118 }
119
120 /**
121  * Free field record object.
122  * \param rec LDIF field object.
123  */
124 static void ldif_free_fieldrec( Ldif_FieldRec *rec ) {
125         if( rec ) {
126                 g_free( rec->tagName );
127                 g_free( rec->userName );
128                 rec->tagName = NULL;
129                 rec->userName = NULL;
130                 rec->reserved = FALSE;
131                 rec->selected = FALSE;
132                 g_free( rec );
133         }
134 }
135
136 /**
137  * Set user name for field record.
138  * \param rec   LDIF field object.
139  * \param value User name to set. Note that reserved fields cannot be
140  *              named.
141  */
142 void ldif_field_set_name( Ldif_FieldRec *rec, const gchar *value ) {
143         g_return_if_fail( rec != NULL );
144
145         if( ! rec->reserved ) {
146                 rec->userName = mgu_replace_string( rec->userName, value );
147                 g_strstrip( rec->userName );
148         }
149 }
150
151 /**
152  * Specify selection for field record.
153  * \param rec   LDIF field object.
154  * \param value Set to <i>TRUE</i> to select field. Note that reserved
155  *              fields cannot be unselected.
156  */
157 void ldif_field_set_selected( Ldif_FieldRec *rec, const gboolean value ) {
158         g_return_if_fail( rec != NULL );
159
160         if( ! rec->reserved ) {
161                 rec->selected = value;
162         }
163 }
164
165 /**
166  * Toggle selection for field record. Note that reserved fields cannot be
167  * toggled.
168  * \param rec   LDIF field object.
169  */
170 void ldif_field_toggle( Ldif_FieldRec *rec ) {
171         g_return_if_fail( rec != NULL );
172
173         if( ! rec->reserved ) {
174                 rec->selected = !rec->selected;
175         }
176 }
177
178 /**
179  * Free hash table entry visitor function.
180  * \param  key   Key.
181  * \param  value Value (the LDIF field record).
182  * \param  data  User data.
183  * \return <code>-1</code>.
184 */
185 static gint ldif_hash_free_vis( gpointer key, gpointer value, gpointer data ) {
186         ldif_free_fieldrec( ( Ldif_FieldRec * ) value );
187         value = NULL;
188         key = NULL;
189         return -1;
190 }
191
192 /**
193  * Free up object by releasing internal memory.
194  * \param ldifFile LDIF import control object.
195  */
196 void ldif_free( LdifFile *ldifFile ) {
197         g_return_if_fail( ldifFile != NULL );
198
199         /* Close file */
200         if( ldifFile->file ) fclose( ldifFile->file );
201
202         /* Free internal stuff */
203         g_free( ldifFile->path );
204
205         /* Free field list */
206         g_hash_table_foreach_remove( ldifFile->hashFields, ldif_hash_free_vis, NULL );
207         g_hash_table_destroy( ldifFile->hashFields );
208         ldifFile->hashFields = NULL;
209
210         /* Clear pointers */
211         ldifFile->file = NULL;
212         ldifFile->path = NULL;
213         ldifFile->retVal = MGU_SUCCESS;
214         ldifFile->tempList = NULL;
215         ldifFile->dirtyFlag = FALSE;
216         ldifFile->accessFlag = FALSE;
217         ldifFile->cbProgress = NULL;
218
219         /* Now release file object */
220         g_free( ldifFile );
221 }
222
223 /**
224  * Display field record.
225  * \param rec    LDIF field object.
226  * \param stream File output stream.
227  */
228 void ldif_print_fieldrec( Ldif_FieldRec *rec, FILE *stream ) {
229         fprintf( stream, "\ttag:\t%s", rec->reserved ? "yes" : "no" );
230         fprintf( stream, "\t%s", rec->selected ? "yes" : "no" );
231         fprintf( stream, "\t:%s:\t:%s:\n", rec->userName, rec->tagName );
232 }
233
234 /**
235  * Display field record.
236  * \param key   Key.
237  * \param value Value (the LDIF field record).
238  * \param data  User data (file output stream).
239  * 
240  */
241 static void ldif_print_file_vis( gpointer key, gpointer value, gpointer data ) {
242         Ldif_FieldRec *rec = value;
243         FILE *stream = data;
244         ldif_print_fieldrec( rec, stream );
245 }
246
247 /**
248  * Display object to specified stream.
249  * \param ldifFile LDIF import control object.
250  * \param stream   File output stream.
251  */
252 void ldif_print_file( LdifFile *ldifFile, FILE *stream ) {
253         g_return_if_fail( ldifFile != NULL );
254         fprintf( stream, "LDIF File:\n" );
255         fprintf( stream, "file spec: '%s'\n", ldifFile->path );
256         fprintf( stream, "  ret val: %d\n",   ldifFile->retVal );
257         fprintf( stream, "   fields: {\n" );
258         g_hash_table_foreach( ldifFile->hashFields, ldif_print_file_vis, stream );
259         fprintf( stream, "} ---\n" );
260 }
261
262 /**
263  * Open file for read.
264  * \param  ldifFile LDIF import control object.
265  * \return <i>TRUE</i> if file opened successfully.
266  */
267 static gint ldif_open_file( LdifFile* ldifFile ) {
268         /* printf( "Opening file\n" ); */
269         if( ldifFile->path ) {
270                 ldifFile->file = fopen( ldifFile->path, "rb" );
271                 if( ! ldifFile->file ) {
272                         /* printf( "can't open %s\n", ldifFile->path ); */
273                         ldifFile->retVal = MGU_OPEN_FILE;
274                         return ldifFile->retVal;
275                 }
276         }
277         else {
278                 /* printf( "file not specified\n" ); */
279                 ldifFile->retVal = MGU_NO_FILE;
280                 return ldifFile->retVal;
281         }
282
283         /* Setup a buffer area */
284         ldifFile->buffer[0] = '\0';
285         ldifFile->bufptr = ldifFile->buffer;
286         ldifFile->retVal = MGU_SUCCESS;
287         return ldifFile->retVal;
288 }
289
290 /**
291  * Close file.
292  * \param  ldifFile LDIF import control object.
293  */
294 static void ldif_close_file( LdifFile *ldifFile ) {
295         g_return_if_fail( ldifFile != NULL );
296         if( ldifFile->file ) fclose( ldifFile->file );
297         ldifFile->file = NULL;
298 }
299
300 /**
301  * Read line of text from file.
302  * \param  ldifFile LDIF import control object.
303  * \return ptr to buffer where line starts.
304  */
305 static gchar *ldif_get_line( LdifFile *ldifFile ) {
306         gchar buf[ LDIFBUFSIZE ];
307         gint ch;
308         gchar *ptr;
309
310         if( feof( ldifFile->file ) ) return NULL;
311
312         ptr = buf;
313         while( TRUE ) {
314                 *ptr = '\0';
315                 ch = fgetc( ldifFile->file );
316                 if( ch == '\0' || ch == EOF ) {
317                         if( *buf == '\0' ) return NULL;
318                         break;
319                 }
320 #if HAVE_DOSISH_SYSTEM
321 #else
322                 if( ch == '\r' ) continue;
323 #endif
324                 if( ch == '\n' ) break;
325                 *ptr = ch;
326                 ptr++;
327         }
328
329         /* Return a copy of buffer */
330         return g_strdup( buf );
331 }
332
333 /**
334  * Parse tag name from line buffer.
335  * \param  line Buffer.
336  * \param  flag64 Base-64 encoder flag.
337  * \return Buffer containing the tag name, or NULL if no delimiter char found.
338  *         If a double delimiter (::) is found, flag64 is set.
339  */
340 static gchar *ldif_get_tagname( char* line, gboolean *flag64 ) {
341         gint len = 0;
342         gchar *tag = NULL;
343         gchar *lptr = line;
344         gchar *sptr = NULL;
345
346         while( *lptr++ ) {
347                 /* Check for language tag */
348                 if( *lptr == LDIF_LANG_TAG ) {
349                         if( sptr == NULL ) sptr = lptr;
350                 }
351
352                 /* Check for delimiter */
353                 if( *lptr == LDIF_SEP_TAG ) {
354                         if( sptr ) {
355                                 len = sptr - line;
356                         }
357                         else {
358                                 len = lptr - line;
359                         }
360
361                         /* Base-64 encoding? */
362                         if( * ++lptr == LDIF_SEP_TAG ) *flag64 = TRUE;
363
364                         tag = g_strndup( line, len+1 );
365                         tag[ len ] = '\0';
366                         g_strdown( tag );
367                         return tag;
368                 }
369         }
370         return tag;
371 }
372
373 /**
374  * Parse tag value from line buffer.
375  * \param  line Buffer.
376  * \return Buffer containing the tag value. Empty string is returned if
377  *         no delimiter char found.
378  */
379 static gchar *ldif_get_tagvalue( gchar* line ) {
380         gchar *value = NULL;
381         gchar *start = NULL;
382         gchar *lptr;
383         gint len = 0;
384
385         for( lptr = line; *lptr; lptr++ ) {
386                 if( *lptr == LDIF_SEP_TAG ) {
387                         if( ! start )
388                                 start = lptr + 1;
389                 }
390         }
391         if( start ) {
392                 if( *start == LDIF_SEP_TAG ) start++;
393                 len = lptr - start;
394                 value = g_strndup( start, len+1 );
395                 g_strstrip( value );
396         }
397         else {
398                 /* Ensure that we get an empty string */
399                 value = g_strndup( "", 1 );
400         }
401         value[ len ] = '\0';
402         return value;
403 }
404
405 /**
406  * Parsed address data record.
407  */
408 typedef struct _Ldif_ParsedRec_ Ldif_ParsedRec;
409 struct _Ldif_ParsedRec_ {
410         GSList *listCName;
411         GSList *listFName;
412         GSList *listLName;
413         GSList *listNName;
414         GSList *listAddress;
415         GSList *listID;
416         GSList *userAttr;
417 };
418
419 /**
420  * User attribute data record.
421  */
422 typedef struct _Ldif_UserAttr_ Ldif_UserAttr;
423 struct _Ldif_UserAttr_ {
424         gchar *name;
425         gchar *value;
426 };
427
428 /**
429  * Build an address list entry and append to list of address items in the
430  * address cache. Name is formatted as "<first-name> <last-name>".
431  * \param ldifFile LDIF import control object.
432  * \param rec      LDIF field object.
433  * \param cache    Address cache to be populated with data.
434  */
435 static void ldif_build_items(
436                 LdifFile *ldifFile, Ldif_ParsedRec *rec, AddressCache *cache )
437 {
438         GSList *nodeFirst;
439         GSList *nodeAddress;
440         GSList *nodeAttr;
441         gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
442         gchar *nickName = NULL;
443         gint iLen = 0, iLenT = 0;
444         ItemPerson *person;
445         ItemEMail *email;
446
447         nodeAddress = rec->listAddress;
448         if( nodeAddress == NULL ) return;
449
450         /* Find longest first name in list */
451         nodeFirst = rec->listFName;
452         while( nodeFirst ) {
453                 if( firstName == NULL ) {
454                         firstName = nodeFirst->data;
455                         iLen = strlen( firstName );
456                 }
457                 else {
458                         if( ( iLenT = strlen( nodeFirst->data ) ) > iLen ) {
459                                 firstName = nodeFirst->data;
460                                 iLen = iLenT;
461                         }
462                 }
463                 nodeFirst = g_slist_next( nodeFirst );
464         }
465
466         /* Format name */
467         if( rec->listLName ) {
468                 lastName = rec->listLName->data;
469         }
470
471         if( firstName ) {
472                 if( lastName ) {
473                         fullName = g_strdup_printf(
474                                 "%s %s", firstName, lastName );
475                 }
476                 else {
477                         fullName = g_strdup_printf( "%s", firstName );
478                 }
479         }
480         else {
481                 if( lastName ) {
482                         fullName = g_strdup_printf( "%s", lastName );
483                 }
484         }
485         if( fullName ) {
486                 g_strchug( fullName ); g_strchomp( fullName );
487         }
488
489         if( rec->listNName ) {
490                 nickName = rec->listNName->data;
491         }
492
493         person = addritem_create_item_person();
494         addritem_person_set_common_name( person, fullName );
495         addritem_person_set_first_name( person, firstName );
496         addritem_person_set_last_name( person, lastName );
497         addritem_person_set_nick_name( person, nickName );
498         addrcache_id_person( cache, person );
499         addrcache_add_person( cache, person );
500         ++ldifFile->importCount;
501
502         /* Add address item */
503         while( nodeAddress ) {
504                 email = addritem_create_item_email();
505                 addritem_email_set_address( email, nodeAddress->data );
506                 addrcache_id_email( cache, email );
507                 addrcache_person_add_email( cache, person, email );
508                 nodeAddress = g_slist_next( nodeAddress );
509         }
510         g_free( fullName );
511         fullName = firstName = lastName = NULL;
512
513         /* Add user attributes */
514         nodeAttr = rec->userAttr;
515         while( nodeAttr ) {
516                 Ldif_UserAttr *attr = nodeAttr->data;
517                 UserAttribute *attrib = addritem_create_attribute();
518                 addritem_attrib_set_name( attrib, attr->name );
519                 addritem_attrib_set_value( attrib, attr->value );
520                 addritem_person_add_attribute( person, attrib );
521                 nodeAttr = g_slist_next( nodeAttr );
522         }
523         nodeAttr = NULL;
524 }
525
526 /**
527  * Add selected field as user attribute.
528  * \param rec       LDIF field object.
529  * \param tagName   LDIF tag name.
530  * \param tagValue  Data value.
531  * \param hashField Hash table to populate.
532  */
533 static void ldif_add_user_attr(
534                 Ldif_ParsedRec *rec, gchar *tagName, gchar *tagValue,
535                 GHashTable *hashField )
536 {
537         Ldif_FieldRec *fld = NULL;
538         Ldif_UserAttr *attr = NULL;
539         gchar *name;
540
541         fld = g_hash_table_lookup( hashField, tagName );
542         if( fld ) {
543                 if( ! fld->selected ) return;
544
545                 name = fld->tagName;
546                 if( fld->userName ) {
547                         name = fld->userName;
548                 }
549                 attr = g_new0( Ldif_UserAttr, 1 );
550                 attr->name = g_strdup( name );
551                 attr->value = g_strdup( tagValue );
552                 rec->userAttr = g_slist_append( rec->userAttr, attr );
553         }
554 }
555
556 /**
557  * Add value to parsed data.
558  * \param rec       LDIF field object.
559  * \param tagName   LDIF tag name.
560  * \param tagValue  Data value.
561  * \param hashField Hash table to populate.
562  */
563 static void ldif_add_value(
564                Ldif_ParsedRec *rec, gchar *tagName, gchar *tagValue,
565                GHashTable *hashField )
566 {
567         gchar *nm, *val;
568
569         nm = g_strdup( tagName );
570         g_strdown( nm );
571         if( tagValue ) {
572                 val = g_strdup( tagValue );
573         }
574         else {
575                 val = g_strdup( "" );
576         }
577         g_strstrip( val );
578
579         if( g_strcasecmp( nm, LDIF_TAG_COMMONNAME ) == 0 ) {
580                 rec->listCName = g_slist_append( rec->listCName, val );
581         }
582         else if( g_strcasecmp( nm, LDIF_TAG_FIRSTNAME ) == 0 ) {
583                 rec->listFName = g_slist_append( rec->listFName, val );
584         }
585         else if( g_strcasecmp( nm, LDIF_TAG_LASTNAME ) == 0 ) {
586                 rec->listLName = g_slist_append( rec->listLName, val );
587         }
588         else if( g_strcasecmp( nm, LDIF_TAG_NICKNAME ) == 0 ) {
589                 rec->listNName = g_slist_append( rec->listNName, val );
590         }
591         else if( g_strcasecmp( nm, LDIF_TAG_EMAIL ) == 0 ) {
592                 rec->listAddress = g_slist_append( rec->listAddress, val );
593         }
594         else {
595                 /* Add field as user attribute */
596                 ldif_add_user_attr( rec, tagName, tagValue, hashField );
597         }
598         g_free( nm );
599 }
600
601 /**
602  * Clear parsed data record.
603  * \param rec LDIF field object.
604  */
605 static void ldif_clear_rec( Ldif_ParsedRec *rec ) {
606         GSList *list;
607
608         /* Free up user attributes */
609         list = rec->userAttr;
610         while( list ) {
611                 Ldif_UserAttr *attr = list->data;
612                 g_free( attr->name );
613                 g_free( attr->value );
614                 g_free( attr );
615                 list = g_slist_next( list );
616         }
617         g_slist_free( rec->userAttr );
618
619         g_slist_free( rec->listCName );
620         g_slist_free( rec->listFName );
621         g_slist_free( rec->listLName );
622         g_slist_free( rec->listNName );
623         g_slist_free( rec->listAddress );
624         g_slist_free( rec->listID );
625
626         rec->userAttr = NULL;
627         rec->listCName = NULL;
628         rec->listFName = NULL;
629         rec->listLName = NULL;
630         rec->listNName = NULL;
631         rec->listAddress = NULL;
632         rec->listID = NULL;
633 }
634
635 #if 0
636 /**
637  * Print parsed data.
638  * \param rec    LDIF field object.
639  * \param stream Output stream.
640  */
641 static void ldif_print_record( Ldif_ParsedRec *rec, FILE *stream ) {
642         GSList *list;
643
644         fprintf( stream, "LDIF Parsed Record:\n" );
645         fprintf( stream, "common name:" );
646         mgu_print_list( rec->listCName, stream );
647         if( ! rec->listCName ) fprintf( stream, "\n" );
648         fprintf( stream, "first name:" );
649         mgu_print_list( rec->listFName, stream );
650         if( ! rec->listFName ) fprintf( stream, "\n" );
651         fprintf( stream, "last name:" );
652         mgu_print_list( rec->listLName, stream );
653         if( ! rec->listLName ) fprintf( stream, "\n" );
654         fprintf( stream, "nick name:" );
655         mgu_print_list( rec->listNName, stream );
656         if( ! rec->listNName ) fprintf( stream, "\n" );
657         fprintf( stream, "address:" );
658         mgu_print_list( rec->listAddress, stream );
659         if( ! rec->listAddress ) fprintf( stream, "\n" );
660         fprintf( stream, "id:" );
661         mgu_print_list( rec->listID, stream );
662         if( ! rec->listID ) fprintf( stream, "\n" );
663
664         list = rec->userAttr;
665         while( list ) {
666                 Ldif_UserAttr *attr = list->data;
667                 fprintf( stream, "n/v:\t%s:\t:%s:\n", attr->name, attr->value );
668                 list = g_slist_next( list );
669         }
670         list = NULL;
671 }
672 #endif
673
674 /**
675  * Read file data into address cache.
676  * Note that one LDIF record identifies one entity uniquely with the
677  * distinguished name (dn) tag. Each person can have multiple E-Mail
678  * addresses. Also, each person can have many common name (cn) tags.
679  *
680  * \param  ldifFile LDIF import control object.
681  * \param  cache    Address cache to be populated with data.
682  */
683 static void ldif_read_file( LdifFile *ldifFile, AddressCache *cache ) {
684         gchar *tagName = NULL, *tagValue = NULL;
685         gchar *lastTag = NULL, *fullValue = NULL;
686         GSList *listValue = NULL;
687         gboolean flagEOF = FALSE, flagEOR = FALSE;
688         gboolean flag64 = FALSE, last64 = FALSE;
689         Ldif_ParsedRec *rec;
690         long posEnd = 0L;
691         long posCur = 0L;
692         GHashTable *hashField;
693
694         hashField = ldifFile->hashFields;
695         rec = g_new0( Ldif_ParsedRec, 1 );
696         ldif_clear_rec( rec );
697
698         /* Find EOF for progress indicator */
699         fseek( ldifFile->file, 0L, SEEK_END );
700         posEnd = ftell( ldifFile->file );
701         fseek( ldifFile->file, 0L, SEEK_SET );
702
703         while( ! flagEOF ) {
704                 gchar *line =  ldif_get_line( ldifFile );
705
706                 posCur = ftell( ldifFile->file );
707                 if( ldifFile->cbProgress ) {
708                         /* Call progress indicator */
709                         ( ldifFile->cbProgress ) ( ldifFile, & posEnd, & posCur );
710                 }
711
712                 flag64 = FALSE;
713                 if( line == NULL ) {
714                         flagEOF = flagEOR = TRUE;
715                 }
716                 else if( *line == '\0' ) {
717                         flagEOR = TRUE;
718                 }
719
720                 if( flagEOR ) {
721                         /* EOR, Output address data */
722                         if( lastTag ) {
723                                 /* Save record */
724                                 fullValue = mgu_list_coalesce( listValue );
725
726                                 /* Base-64 encoded data */
727                                 /*
728                                 if( last64 ) {
729                                         ldif_dump_b64( fullValue );
730                                 }
731                                 */
732
733                                 ldif_add_value( rec, lastTag, fullValue, hashField );
734                                 /* ldif_print_record( rec, stdout ); */
735                                 ldif_build_items( ldifFile, rec, cache );
736                                 ldif_clear_rec( rec );
737                                 g_free( lastTag );
738                                 mgu_free_list( listValue );
739                                 lastTag = NULL;
740                                 listValue = NULL;
741                                 last64 = FALSE;
742                         }
743                 }
744                 if( line ) {
745                         flagEOR = FALSE;
746                         if( *line == ' ' ) {
747                                 /* Continuation line */
748                                 listValue = g_slist_append(
749                                         listValue, g_strdup( line+1 ) );
750                         }
751                         else if( *line == '=' ) {
752                                 /* Base-64 encoded continuation field */
753                                 listValue = g_slist_append(
754                                         listValue, g_strdup( line ) );
755                         }
756                         else {
757                                 /* Parse line */
758                                 tagName = ldif_get_tagname( line, &flag64 );
759                                 if( tagName ) {
760                                         tagValue = ldif_get_tagvalue( line );
761                                         if( tagValue ) {
762                                                 if( lastTag ) {
763                                                         /* Save data */
764                                                         fullValue =
765                                                                 mgu_list_coalesce( listValue );
766                                                         /* Base-64 encoded data */
767                                                         /*
768                                                         if( last64 ) {
769                                                                 ldif_dump_b64( fullValue );
770                                                         }
771                                                         */
772
773                                                         ldif_add_value(
774                                                                 rec, lastTag, fullValue,
775                                                                 hashField );
776                                                         g_free( lastTag );
777                                                         mgu_free_list( listValue );
778                                                         lastTag = NULL;
779                                                         listValue = NULL;
780                                                         last64 = FALSE;
781                                                 }
782
783                                                 lastTag = g_strdup( tagName );
784                                                 listValue = g_slist_append(
785                                                         listValue,
786                                                         g_strdup( tagValue ) );
787                                                 g_free( tagValue );
788                                                 last64 = flag64;
789                                         }
790                                         g_free( tagName );
791                                 }
792                         }
793                 }
794                 g_free( line );
795         }
796
797         /* Release data */
798         ldif_clear_rec( rec );
799         g_free( rec );
800         g_free( lastTag );
801         mgu_free_list( listValue );
802 }
803
804 /**
805  * Add list of field names to hash table.
806  * \param table Hashtable.
807  * \param list  List of fields.
808  */
809 static void ldif_hash_add_list( GHashTable *table, GSList *list ) {
810         GSList *node = list;
811
812         /* mgu_print_list( list, stdout ); */
813         while( node ) {
814                 gchar *tag = node->data;
815                 if( ! g_hash_table_lookup( table, tag ) ) {
816                         Ldif_FieldRec *rec = NULL;
817                         gchar *key = g_strdup( tag );
818
819                         rec = ldif_create_fieldrec( tag );
820                         if( g_strcasecmp( tag, LDIF_TAG_DN ) == 0 ) {
821                                 rec->reserved = rec->selected = TRUE;
822                                 rec->userName = g_strdup( "dn" );
823                         }
824                         else if( g_strcasecmp( tag, LDIF_TAG_COMMONNAME ) == 0 ) {
825                                 rec->reserved = rec->selected = TRUE;
826                                 rec->userName = g_strdup( _( "Display Name" ) );
827                         }
828                         else if( g_strcasecmp( tag, LDIF_TAG_FIRSTNAME ) == 0 ) {
829                                 rec->reserved = rec->selected = TRUE;
830                                 rec->userName = g_strdup( _( "First Name" ) );
831                         }
832                         else if( g_strcasecmp( tag, LDIF_TAG_LASTNAME ) == 0 ) {
833                                 rec->reserved = rec->selected = TRUE;
834                                 rec->userName = g_strdup( _( "Last Name" ) );
835                         }
836                         else if( g_strcasecmp( tag, LDIF_TAG_NICKNAME ) == 0 ) {
837                                 rec->reserved = rec->selected = TRUE;
838                                 rec->userName = g_strdup( _( "Nick Name" ) );
839                         }
840                         else if( g_strcasecmp( tag, LDIF_TAG_EMAIL ) == 0 ) {
841                                 rec->reserved = rec->selected = TRUE;
842                                 rec->userName = g_strdup( _( "E-Mail Address" ) );
843                         }
844                         g_hash_table_insert( table, key, rec );
845                 }
846                 node = g_slist_next( node );
847         }
848 }
849
850 /**
851  * Sorted list comparison function.
852  * \param  ptr1 First field.
853  * \param  ptr2 Second field.
854  * \return <code>-1, 0, +1</code> if first record less than, equal,
855  *         greater than second.
856  */
857 static gint ldif_field_compare( gconstpointer ptr1, gconstpointer ptr2 ) {
858         const Ldif_FieldRec *rec1 = ptr1;
859         const Ldif_FieldRec *rec2 = ptr2;
860
861         if( rec1->reserved ) {
862                 if( ! rec2->reserved ) {
863                         return +1;
864                 }
865         }
866         else {
867                 if( rec2->reserved ) {
868                         return -1;
869                 }
870         }
871         return g_strcasecmp( rec1->tagName, rec2->tagName );
872 }
873
874 /*
875  * Append hash table entry to list - visitor function.
876  * \param key   Key.
877  * \param value Data value.
878  * \param data  User data (the LDIF import control object).
879  */
880 static void ldif_hash2list_vis( gpointer key, gpointer value, gpointer data ) {
881         LdifFile *ldf = data;
882         ldf->tempList =
883                 g_list_insert_sorted( ldf->tempList, value, ldif_field_compare );
884 }
885
886 /**
887  * Read tag names for file data.
888  * \param  ldifFile LDIF import control object.
889  */
890 static void ldif_read_tag_list( LdifFile *ldifFile ) {
891         gchar *tagName = NULL;
892         GSList *listTags = NULL;
893         gboolean flagEOF = FALSE, flagEOR = FALSE, flagMail = FALSE;
894         gboolean flag64 = FALSE;
895         long posEnd = 0L;
896         long posCur = 0L;
897
898         /* Clear hash table */
899         g_hash_table_foreach_remove(
900                 ldifFile->hashFields, ldif_hash_free_vis, NULL );
901
902         /* Find EOF for progress indicator */
903         fseek( ldifFile->file, 0L, SEEK_END );
904         posEnd = ftell( ldifFile->file );
905         fseek( ldifFile->file, 0L, SEEK_SET );
906
907         /* Process file */
908         while( ! flagEOF ) {
909                 gchar *line = ldif_get_line( ldifFile );
910
911                 posCur = ftell( ldifFile->file );
912                 if( ldifFile->cbProgress ) {
913                         /* Call progress indicator */
914                         ( ldifFile->cbProgress ) ( ldifFile, & posEnd, & posCur );
915                 }
916
917                 flag64 = FALSE;
918                 if( line == NULL ) {
919                         flagEOF = flagEOR = TRUE;
920                 }
921                 else if( *line == '\0' ) {
922                         flagEOR = TRUE;
923                 }
924
925                 if( flagEOR ) {
926                         /* EOR, Output address data */
927                         /* Save field list to hash table */
928                         if( flagMail ) {
929                                 ldif_hash_add_list(
930                                         ldifFile->hashFields, listTags );
931                         }
932                         mgu_free_list( listTags );
933                         listTags = NULL;
934                         flagMail = FALSE;
935                 }
936                 if( line ) {
937                         flagEOR = FALSE;
938                         if( *line == ' ' ) {
939                                 /* Continuation line */
940                         }
941                         else if( *line == '=' ) {
942                                 /* Base-64 encoded continuation field */
943                         }
944                         else {
945                                 /* Parse line */
946                                 tagName = ldif_get_tagname( line, &flag64 );
947                                 if( tagName ) {
948                                         /* Add tag to list */
949                                         listTags = g_slist_append( listTags, tagName );
950                                         if( g_strcasecmp(
951                                                 tagName, LDIF_TAG_EMAIL ) == 0 )
952                                         {
953                                                 flagMail = TRUE;
954                                         }
955                                 }
956                         }
957                 }
958                 g_free( line );
959         }
960
961         /* Release data */
962         mgu_free_list( listTags );
963         listTags = NULL;
964 }
965
966 /**
967  * Read file into list. Main entry point
968  * \param  ldifFile LDIF import control object.
969  * \param  cache    Address cache to load.
970  * \return Status code.
971  */
972 gint ldif_import_data( LdifFile *ldifFile, AddressCache *cache ) {
973         g_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS );
974         ldifFile->retVal = MGU_SUCCESS;
975         addrcache_clear( cache );
976         cache->dataRead = FALSE;
977         ldif_open_file( ldifFile );
978         if( ldifFile->retVal == MGU_SUCCESS ) {
979                 /* Read data into the cache */
980                 ldif_read_file( ldifFile, cache );
981                 ldif_close_file( ldifFile );
982
983                 /* Mark cache */
984                 cache->modified = FALSE;
985                 cache->dataRead = TRUE;
986         }
987         return ldifFile->retVal;
988 }
989
990 /**
991  * Process entire file reading list of unique fields. List of fields may be
992  * accessed with the <code>ldif_get_fieldlist()</code> function.
993  * \param  ldifFile LDIF import control object.
994  * \return Status code.
995  */
996 gint ldif_read_tags( LdifFile *ldifFile ) {
997         g_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS );
998         ldifFile->retVal = MGU_SUCCESS;
999         if( ldifFile->dirtyFlag ) {
1000                 ldif_open_file( ldifFile );
1001                 if( ldifFile->retVal == MGU_SUCCESS ) {
1002                         /* Read data into the cache */
1003                         ldif_read_tag_list( ldifFile );
1004                         ldif_close_file( ldifFile );
1005                         ldifFile->dirtyFlag = FALSE;
1006                         ldifFile->accessFlag = TRUE;
1007                 }
1008         }
1009         return ldifFile->retVal;
1010 }
1011
1012 /**
1013  * Return list of fields for LDIF file.
1014  * \param  ldifFile LDIF import control object.
1015  * \return Linked list of <code>Ldif_FieldRec</code> objects. This list may be
1016  *         <code>g_free()</code>. Note that the objects in the list should not
1017  *         be freed since they refer to objects inside the internal cache.
1018  *         These objects will be freed when LDIF file object is freed.
1019  */
1020 GList *ldif_get_fieldlist( LdifFile *ldifFile ) {
1021         GList *list = NULL;
1022
1023         g_return_val_if_fail( ldifFile != NULL, NULL );
1024         if( ldifFile->hashFields ) {
1025                 ldifFile->tempList = NULL;
1026                 g_hash_table_foreach( ldifFile->hashFields, ldif_hash2list_vis, ldifFile );
1027                 list = ldifFile->tempList;
1028                 ldifFile->tempList = NULL;
1029         }
1030         return list;
1031 }
1032
1033 /**
1034  * Output LDIF name-value pair to stream. Only non-empty names and values will
1035  * be output to file.
1036  * \param stream File output stream.
1037  * \param name   Name.
1038  * \param value  Data value.
1039  * \return <i>TRUE</i> if data output.
1040  */
1041 gboolean ldif_write_value( FILE *stream, const gchar *name, const gchar *value ) {
1042         if( name == NULL ) return FALSE;
1043         if( value == NULL ) return FALSE;
1044         if( strlen( name ) < 1 ) return FALSE;
1045         if( strlen( value ) < 1 ) return FALSE;
1046         fprintf( stream, "%s: ", name );
1047         fprintf( stream, "%s\n", value );
1048         return TRUE;
1049 }
1050
1051 /**
1052  * Output LDIF End of Record to stream.
1053  * \param stream File output stream.
1054  * \return <i>TRUE</i> if data output.
1055  */
1056 void ldif_write_eor( FILE *stream ) {
1057         /* Simple but caller should not need to know how to end record. */
1058         fprintf( stream, "\n" );
1059 }
1060
1061 /*
1062  * ============================================================================
1063  * End of Source.
1064  * ============================================================================
1065  */
1066