allows creation of mbox folder item from any files.
[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         gchar 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         gchar *value;
467
468         fld = g_hash_table_lookup( hashField, tagName );
469         if( fld ) {
470                 if( fld->reserved ) return;
471                 if( ! fld->selected ) return;
472
473                 name = fld->tagName;
474                 if( fld->userName ) {
475                         name = fld->userName;
476                 }
477                 attr = g_new0( Ldif_UserAttr, 1 );
478                 attr->name = g_strdup( name );
479                 attr->value = g_strdup( tagValue );
480                 rec->userAttr = g_slist_append( rec->userAttr, attr );
481         }
482 }
483
484 /*
485 * Add value to parsed data.
486 */
487 static void ldif_add_value( Ldif_ParsedRec *rec, gchar *tagName, gchar *tagValue, GHashTable *hashField ) {
488         gchar *nm, *val;
489
490         nm = g_strdup( tagName );
491         g_strdown( nm );
492         if( tagValue ) {
493                 val = g_strdup( tagValue );
494         }
495         else {
496                 val = g_strdup( "" );
497         }
498         g_strstrip( val );
499         if( g_strcasecmp( nm, LDIF_TAG_COMMONNAME ) == 0 ) {
500                 rec->listCName = g_slist_append( rec->listCName, val );
501         }
502         else if( g_strcasecmp( nm, LDIF_TAG_FIRSTNAME ) == 0 ) {
503                 rec->listFName = g_slist_append( rec->listFName, val );
504         }
505         else if( g_strcasecmp( nm, LDIF_TAG_LASTNAME ) == 0 ) {
506                 rec->listLName = g_slist_append( rec->listLName, val );
507         }
508         else if( g_strcasecmp( nm, LDIF_TAG_NICKNAME ) == 0 ) {
509                 rec->listNName = g_slist_append( rec->listNName, val );
510         }
511         else if( g_strcasecmp( nm, LDIF_TAG_EMAIL ) == 0 ) {
512                 rec->listAddress = g_slist_append( rec->listAddress, val );
513         }
514         else {
515                 /* Add field as user attribute */
516                 ldif_add_user_attr( rec, tagName, tagValue, hashField );
517         }
518         g_free( nm );
519 }
520
521 /*
522 * Clear parsed data.
523 */
524 static void ldif_clear_rec( Ldif_ParsedRec *rec ) {
525         GSList *list;
526
527         /* Free up user attributes */
528         list = rec->userAttr;
529         while( list ) {
530                 Ldif_UserAttr *attr = list->data;
531                 g_free( attr->name );
532                 g_free( attr->value );
533                 g_free( attr );
534                 list = g_slist_next( list );
535         }
536         g_slist_free( rec->userAttr );
537
538         g_slist_free( rec->listCName );
539         g_slist_free( rec->listFName );
540         g_slist_free( rec->listLName );
541         g_slist_free( rec->listNName );
542         g_slist_free( rec->listAddress );
543         g_slist_free( rec->listID );
544
545         rec->userAttr = NULL;
546         rec->listCName = NULL;
547         rec->listFName = NULL;
548         rec->listLName = NULL;
549         rec->listNName = NULL;
550         rec->listAddress = NULL;
551         rec->listID = NULL;
552 }
553
554 /*
555 * Print parsed data.
556 */
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
589 static void ldif_dump_b64( gchar *buf ) {
590         Base64Decoder *decoder = NULL;
591         gchar outBuf[8192];
592         gint len;
593
594         printf( "base-64 : inbuf : %s\n", buf );
595         decoder = base64_decoder_new();
596         len = base64_decoder_decode( decoder, buf, outBuf );
597         if (len < 0) {
598                 printf( "base-64 : Bad BASE64 content\n" );
599         }
600         else {
601                 outBuf[len] = '\0';
602                 printf( "base-64 : %d : %s\n\n", len, outBuf );
603         }
604         base64_decoder_free( decoder );
605         decoder = NULL;
606 }
607
608 /*
609 * Read file data into address cache.
610 * Note that one LDIF record identifies one entity uniquely with the
611 * distinguished name (dn) tag. Each person can have multiple E-Mail
612 * addresses. Also, each person can have many common name (cn) tags.
613 */
614 static void ldif_read_file( LdifFile *ldifFile, AddressCache *cache ) {
615         gchar *tagName = NULL, *tagValue = NULL;
616         gchar *lastTag = NULL, *fullValue = NULL;
617         GSList *listValue = NULL;
618         gboolean flagEOF = FALSE, flagEOR = FALSE;
619         gboolean flag64 = FALSE, last64 = FALSE;
620         Ldif_ParsedRec *rec;
621         long posEnd = 0L;
622         long posCur = 0L;
623         GHashTable *hashField;
624
625         hashField = ldifFile->hashFields;
626         rec = g_new0( Ldif_ParsedRec, 1 );
627         ldif_clear_rec( rec );
628
629         /* Find EOF for progress indicator */
630         fseek( ldifFile->file, 0L, SEEK_END );
631         posEnd = ftell( ldifFile->file );
632         fseek( ldifFile->file, 0L, SEEK_SET );
633
634         while( ! flagEOF ) {
635                 gchar *line =  ldif_get_line( ldifFile );
636
637                 posCur = ftell( ldifFile->file );
638                 if( ldifFile->cbProgress ) {
639                         /* Call progress indicator */
640                         ( ldifFile->cbProgress ) ( ldifFile, & posEnd, & posCur );
641                 }
642
643                 flag64 = FALSE;
644                 if( line == NULL ) {
645                         flagEOF = flagEOR = TRUE;
646                 }
647                 else if( *line == '\0' ) {
648                         flagEOR = TRUE;
649                 }
650
651                 if( flagEOR ) {
652                         /* EOR, Output address data */
653                         if( lastTag ) {
654                                 /* Save record */
655                                 fullValue = mgu_list_coalesce( listValue );
656
657                                 /* Base-64 encoded data */
658                                 /*
659                                 if( last64 ) {
660                                         ldif_dump_b64( fullValue );
661                                 }
662                                 */
663
664                                 ldif_add_value( rec, lastTag, fullValue, hashField );
665                                 /* ldif_print_record( rec, stdout ); */
666                                 ldif_build_items( ldifFile, rec, cache );
667                                 ldif_clear_rec( rec );
668                                 g_free( lastTag );
669                                 mgu_free_list( listValue );
670                                 lastTag = NULL;
671                                 listValue = NULL;
672                                 last64 = FALSE;
673                         }
674                 }
675                 if( line ) {
676                         flagEOR = FALSE;
677                         if( *line == ' ' ) {
678                                 /* Continuation line */
679                                 listValue = g_slist_append( listValue, g_strdup( line+1 ) );
680                         }
681                         else if( *line == '=' ) {
682                                 /* Base-64 encoded continuation field */
683                                 listValue = g_slist_append( listValue, g_strdup( line ) );
684                         }
685                         else {
686                                 /* Parse line */
687                                 tagName = ldif_get_tagname( line, &flag64 );
688                                 if( tagName ) {
689                                         tagValue = ldif_get_tagvalue( line );
690                                         if( tagValue ) {
691                                                 if( lastTag ) {
692                                                         /* Save data */
693                                                         fullValue = mgu_list_coalesce( listValue );
694                                                         /* Base-64 encoded data */
695                                                         /*
696                                                         if( last64 ) {
697                                                                 ldif_dump_b64( fullValue );
698                                                         }
699                                                         */
700
701                                                         ldif_add_value( rec, lastTag, fullValue, hashField );
702                                                         g_free( lastTag );
703                                                         mgu_free_list( listValue );
704                                                         lastTag = NULL;
705                                                         listValue = NULL;
706                                                         last64 = FALSE;
707                                                 }
708
709                                                 lastTag = g_strdup( tagName );
710                                                 listValue = g_slist_append( listValue, g_strdup( tagValue ) );
711                                                 g_free( tagValue );
712                                                 last64 = flag64;
713                                         }
714                                         g_free( tagName );
715                                 }
716                         }
717                 }
718                 g_free( line );
719         }
720
721         /* Release data */
722         ldif_clear_rec( rec );
723         g_free( rec );
724         g_free( lastTag );
725         mgu_free_list( listValue );
726 }
727
728 /*
729 * Add list of field names to hash table.
730 */
731 static void ldif_hash_add_list( GHashTable *table, GSList *list ) {
732         GSList *node = list;
733
734         /* mgu_print_list( list, stdout ); */
735         while( node ) {
736                 gchar *tag = node->data;
737                 if( ! g_hash_table_lookup( table, tag ) ) {
738                         Ldif_FieldRec *rec = NULL;
739                         gchar *key = g_strdup( tag );
740
741                         rec = ldif_create_fieldrec( tag );
742                         if( g_strcasecmp( tag, LDIF_TAG_COMMONNAME ) == 0 ) {
743                                 rec->reserved = TRUE;
744                         }
745                         else if( g_strcasecmp( tag, LDIF_TAG_FIRSTNAME ) == 0 ) {
746                                 rec->reserved = TRUE;
747                         }
748                         else if( g_strcasecmp( tag, LDIF_TAG_LASTNAME ) == 0 ) {
749                                 rec->reserved = TRUE;
750                         }
751                         else if( g_strcasecmp( tag, LDIF_TAG_NICKNAME ) == 0 ) {
752                                 rec->reserved = TRUE;
753                         }
754                         else if( g_strcasecmp( tag, LDIF_TAG_EMAIL ) == 0 ) {
755                                 rec->reserved = TRUE;
756                         }
757                         g_hash_table_insert( table, key, rec );
758                 }
759                 node = g_slist_next( node );
760         }
761 }
762
763 /*
764 * Sorted list comparison function.
765 */
766 static int ldif_field_compare( gconstpointer ptr1, gconstpointer ptr2 ) {
767         const Ldif_FieldRec *rec1 = ptr1;
768         const Ldif_FieldRec *rec2 = ptr2;
769         return strcasecmp(rec1->tagName, rec2->tagName );
770 }
771
772 /*
773 * Append hash table entry to list - visitor function.
774 */
775 static void ldif_hash2list_vis( gpointer key, gpointer value, gpointer data ) {
776         LdifFile *ldf = data;
777         ldf->tempList = g_list_insert_sorted( ldf->tempList, value, ldif_field_compare );
778 }
779
780 /*
781 * Read tag names for file data.
782 */
783 static void ldif_read_tag_list( LdifFile *ldifFile ) {
784         gchar *tagName = NULL;
785         GSList *listTags = NULL;
786         gboolean flagEOF = FALSE, flagEOR = FALSE, flagMail = FALSE;
787         gboolean flag64 = FALSE;
788         long posEnd = 0L;
789         long posCur = 0L;
790
791         /* Clear hash table */
792         g_hash_table_foreach_remove( ldifFile->hashFields, ldif_hash_free_vis, NULL );
793
794         /* Find EOF for progress indicator */
795         fseek( ldifFile->file, 0L, SEEK_END );
796         posEnd = ftell( ldifFile->file );
797         fseek( ldifFile->file, 0L, SEEK_SET );
798
799         /* Process file */
800         while( ! flagEOF ) {
801                 gchar *line = ldif_get_line( ldifFile );
802
803                 posCur = ftell( ldifFile->file );
804                 if( ldifFile->cbProgress ) {
805                         /* Call progress indicator */
806                         ( ldifFile->cbProgress ) ( ldifFile, & posEnd, & posCur );
807                 }
808
809                 flag64 = FALSE;
810                 if( line == NULL ) {
811                         flagEOF = flagEOR = TRUE;
812                 }
813                 else if( *line == '\0' ) {
814                         flagEOR = TRUE;
815                 }
816
817                 if( flagEOR ) {
818                         /* EOR, Output address data */
819                         /* Save field list to hash table */
820                         if( flagMail ) {
821                                 ldif_hash_add_list( ldifFile->hashFields, listTags );
822                         }
823                         mgu_free_list( listTags );
824                         listTags = NULL;
825                         flagMail = FALSE;
826                 }
827                 if( line ) {
828                         flagEOR = FALSE;
829                         if( *line == ' ' ) {
830                                 /* Continuation line */
831                         }
832                         else if( *line == '=' ) {
833                                 /* Base-64 encoded continuation field */
834                         }
835                         else {
836                                 /* Parse line */
837                                 tagName = ldif_get_tagname( line, &flag64 );
838                                 if( tagName ) {
839                                         /* Add tag to list */
840                                         listTags = g_slist_append( listTags, tagName );
841                                         if( g_strcasecmp( tagName, LDIF_TAG_EMAIL ) == 0 ) {
842                                                 flagMail = TRUE;
843                                         }
844                                 }
845                         }
846                 }
847                 g_free( line );
848         }
849
850         /* Release data */
851         mgu_free_list( listTags );
852         listTags = NULL;
853 }
854
855 /*
856 * ============================================================================
857 * Read file into list. Main entry point
858 * Enter:  ldifFile LDIF control data.
859 *         cache    Address cache to load.
860 * Return: Status code.
861 * ============================================================================
862 */
863 gint ldif_import_data( LdifFile *ldifFile, AddressCache *cache ) {
864         g_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS );
865         ldifFile->retVal = MGU_SUCCESS;
866         addrcache_clear( cache );
867         cache->dataRead = FALSE;
868         ldif_open_file( ldifFile );
869         if( ldifFile->retVal == MGU_SUCCESS ) {
870                 /* Read data into the cache */
871                 ldif_read_file( ldifFile, cache );
872                 ldif_close_file( ldifFile );
873
874                 /* Mark cache */
875                 cache->modified = FALSE;
876                 cache->dataRead = TRUE;
877         }
878         return ldifFile->retVal;
879 }
880
881 /*
882 * ============================================================================
883 * Process entire file reading list of unique fields. List of fields may be
884 * accessed with the ldif_get_fieldlist() function.
885 * Enter:  ldifFile LDIF control data.
886 * Return: Status code.
887 * ============================================================================
888 */
889 gint ldif_read_tags( LdifFile *ldifFile ) {
890         g_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS );
891         ldifFile->retVal = MGU_SUCCESS;
892         if( ldifFile->dirtyFlag ) {
893                 ldif_open_file( ldifFile );
894                 if( ldifFile->retVal == MGU_SUCCESS ) {
895                         /* Read data into the cache */
896                         ldif_read_tag_list( ldifFile );
897                         ldif_close_file( ldifFile );
898                         ldifFile->dirtyFlag = FALSE;
899                         ldifFile->accessFlag = TRUE;
900                 }
901         }
902         return ldifFile->retVal;
903 }
904
905 /*
906 * Return list of fields for LDIF file.
907 * Enter: ldifFile LdifFile object.
908 * Return: Linked list of Ldif_FieldRec objects. This list may be g_free'd.
909 * Note that the objects in the list should not be freed since they refer to
910 * objects inside the internal cache. These objects will be freed when
911 * LDIF file object is freed.
912 */
913 GList *ldif_get_fieldlist( LdifFile *ldifFile ) {
914         GList *list = NULL;
915
916         g_return_val_if_fail( ldifFile != NULL, NULL );
917         if( ldifFile->hashFields ) {
918                 ldifFile->tempList = NULL;
919                 g_hash_table_foreach( ldifFile->hashFields, ldif_hash2list_vis, ldifFile );
920                 list = ldifFile->tempList;
921                 ldifFile->tempList = NULL;
922         }
923         return list;
924 }
925
926 /*
927 * End of Source.
928 */
929