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