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