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