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