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