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