3.9.0 unleashedcd claws
[claws.git] / src / ldif.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001-2012 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         
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                         return tag;
320                 }
321         }
322         return tag;
323 }
324
325 /**
326  * Parse tag value from line buffer.
327  * \param  line Buffer.
328  * \return Buffer containing the tag value. Empty string is returned if
329  *         no delimiter char found.
330  */
331 static gchar *ldif_get_tagvalue( gchar* line ) {
332         gchar *value = NULL;
333         gchar *start = NULL;
334         gchar *lptr;
335         gint len = 0;
336
337         for( lptr = line; *lptr; lptr++ ) {
338                 if( *lptr == LDIF_SEP_TAG ) {
339                         if( ! start )
340                                 start = lptr + 1;
341                 }
342         }
343         if( start ) {
344                 if( *start == LDIF_SEP_TAG ) start++;
345                 len = lptr - start;
346                 value = g_strndup( start, len+1 );
347                 g_strstrip( value );
348         }
349         else {
350                 /* Ensure that we get an empty string */
351                 value = g_strndup( "", 1 );
352         }
353         value[ len ] = '\0';
354         return value;
355 }
356
357 /**
358  * Parsed address data record.
359  */
360 typedef struct _Ldif_ParsedRec_ Ldif_ParsedRec;
361 struct _Ldif_ParsedRec_ {
362         GSList *listCName;
363         GSList *listFName;
364         GSList *listLName;
365         GSList *listNName;
366         GSList *listAddress;
367         GSList *listID;
368         GSList *userAttr;
369 };
370
371 /**
372  * User attribute data record.
373  */
374 typedef struct _Ldif_UserAttr_ Ldif_UserAttr;
375 struct _Ldif_UserAttr_ {
376         gchar *name;
377         gchar *value;
378 };
379
380 /**
381  * Build an address list entry and append to list of address items in the
382  * address cache. Name is formatted as "<first-name> <last-name>".
383  * \param ldifFile LDIF import control object.
384  * \param rec      LDIF field object.
385  * \param cache    Address cache to be populated with data.
386  */
387 static void ldif_build_items(
388                 LdifFile *ldifFile, Ldif_ParsedRec *rec, AddressCache *cache )
389 {
390         GSList *nodeFirst;
391         GSList *nodeAddress;
392         GSList *nodeAttr;
393         gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
394         gchar *nickName = NULL;
395         gint iLen = 0, iLenT = 0;
396         ItemPerson *person;
397         ItemEMail *email;
398
399         nodeAddress = rec->listAddress;
400 //      if( nodeAddress == NULL ) return;
401
402         /* Find longest first name in list */
403         nodeFirst = rec->listFName;
404         while( nodeFirst ) {
405                 if( firstName == NULL ) {
406                         firstName = nodeFirst->data;
407                         iLen = strlen( firstName );
408                 }
409                 else {
410                         if( ( iLenT = strlen( nodeFirst->data ) ) > iLen ) {
411                                 firstName = nodeFirst->data;
412                                 iLen = iLenT;
413                         }
414                 }
415                 nodeFirst = g_slist_next( nodeFirst );
416         }
417
418         /* Format name */
419         if( rec->listLName ) {
420                 lastName = rec->listLName->data;
421         }
422
423         if( firstName ) {
424                 if( lastName ) {
425                         fullName = g_strdup_printf(
426                                 "%s %s", firstName, lastName );
427                 }
428                 else {
429                         fullName = g_strdup_printf( "%s", firstName );
430                 }
431         }
432         else {
433                 if( lastName ) {
434                         fullName = g_strdup_printf( "%s", lastName );
435                 }
436         }
437         
438         if (!fullName || strlen(fullName) == 0) {
439                 g_free(fullName);
440                 fullName = NULL;
441                 if (rec->listCName)
442                         fullName = g_strdup(rec->listCName->data);
443         }
444         
445         if( fullName ) {
446                 g_strchug( fullName ); g_strchomp( fullName );
447         }
448
449         if( rec->listNName ) {
450                 nickName = rec->listNName->data;
451         }
452
453         person = addritem_create_item_person();
454         addritem_person_set_common_name( person, fullName );
455         addritem_person_set_first_name( person, firstName );
456         addritem_person_set_last_name( person, lastName );
457         addritem_person_set_nick_name( person, nickName );
458         addrcache_id_person( cache, person );
459         addrcache_add_person( cache, person );
460         ++ldifFile->importCount;
461
462         /* Add address item */
463         while( nodeAddress ) {
464                 email = addritem_create_item_email();
465                 addritem_email_set_address( email, nodeAddress->data );
466                 addrcache_id_email( cache, email );
467                 addrcache_person_add_email( cache, person, email );
468                 nodeAddress = g_slist_next( nodeAddress );
469         }
470         g_free( fullName );
471         fullName = firstName = lastName = NULL;
472
473         /* Add user attributes */
474         nodeAttr = rec->userAttr;
475         while( nodeAttr ) {
476                 Ldif_UserAttr *attr = nodeAttr->data;
477                 UserAttribute *attrib = addritem_create_attribute();
478                 addritem_attrib_set_name( attrib, attr->name );
479                 addritem_attrib_set_value( attrib, attr->value );
480                 addritem_person_add_attribute( person, attrib );
481                 nodeAttr = g_slist_next( nodeAttr );
482         }
483         nodeAttr = NULL;
484 }
485
486 /**
487  * Add selected field as user attribute.
488  * \param rec       LDIF field object.
489  * \param tagName   LDIF tag name.
490  * \param tagValue  Data value.
491  * \param hashField Hash table to populate.
492  */
493 static void ldif_add_user_attr(
494                 Ldif_ParsedRec *rec, gchar *tagName, gchar *tagValue,
495                 GHashTable *hashField )
496 {
497         Ldif_FieldRec *fld = NULL;
498         Ldif_UserAttr *attr = NULL;
499         gchar *name;
500
501         fld = g_hash_table_lookup( hashField, tagName );
502         if( fld ) {
503                 if( ! fld->selected ) return;
504
505                 name = fld->tagName;
506                 if( fld->userName ) {
507                         name = fld->userName;
508                 }
509                 attr = g_new0( Ldif_UserAttr, 1 );
510                 attr->name = g_strdup( name );
511                 attr->value = g_strdup( tagValue );
512                 rec->userAttr = g_slist_append( rec->userAttr, attr );
513         }
514 }
515
516 /**
517  * Add value to parsed data.
518  * \param rec       LDIF field object.
519  * \param tagName   LDIF tag name.
520  * \param tagValue  Data value.
521  * \param hashField Hash table to populate.
522  */
523 static void ldif_add_value(
524                Ldif_ParsedRec *rec, gchar *tagName, gchar *tagValue,
525                GHashTable *hashField )
526 {
527         gchar *nm, *val;
528
529         nm = g_utf8_strdown( tagName, -1 );
530         if( tagValue ) {
531                 val = g_strdup( tagValue );
532         }
533         else {
534                 val = g_strdup( "" );
535         }
536         g_strstrip( val );
537
538         if( g_utf8_collate( nm, g_utf8_strdown( LDIF_TAG_COMMONNAME, -1 ) ) == 0 ) {
539                 rec->listCName = g_slist_append( rec->listCName, val );
540         }
541         else if( g_utf8_collate( nm, g_utf8_strdown( LDIF_TAG_FIRSTNAME, -1 ) ) == 0 ) {
542                 rec->listFName = g_slist_append( rec->listFName, val );
543         }
544         else if( g_utf8_collate( nm, g_utf8_strdown( LDIF_TAG_LASTNAME, -1 ) ) == 0 ) {
545                 rec->listLName = g_slist_append( rec->listLName, val );
546         }
547         else if( g_utf8_collate( nm, g_utf8_strdown( LDIF_TAG_NICKNAME, -1 ) ) == 0 ) {
548                 rec->listNName = g_slist_append( rec->listNName, val );
549         }
550         else if( g_utf8_collate( nm, g_utf8_strdown( LDIF_TAG_EMAIL, -1 ) ) == 0 ) {
551                 rec->listAddress = g_slist_append( rec->listAddress, val );
552         }
553         else {
554                 /* Add field as user attribute */
555                 ldif_add_user_attr( rec, tagName, tagValue, hashField );
556         }
557         g_free( nm );
558 }
559
560 /**
561  * Clear parsed data record.
562  * \param rec LDIF field object.
563  */
564 static void ldif_clear_rec( Ldif_ParsedRec *rec ) {
565         GSList *list;
566
567         /* Free up user attributes */
568         list = rec->userAttr;
569         while( list ) {
570                 Ldif_UserAttr *attr = list->data;
571                 g_free( attr->name );
572                 g_free( attr->value );
573                 g_free( attr );
574                 list = g_slist_next( list );
575         }
576         g_slist_free( rec->userAttr );
577
578         g_slist_free( rec->listCName );
579         g_slist_free( rec->listFName );
580         g_slist_free( rec->listLName );
581         g_slist_free( rec->listNName );
582         g_slist_free( rec->listAddress );
583         g_slist_free( rec->listID );
584
585         rec->userAttr = NULL;
586         rec->listCName = NULL;
587         rec->listFName = NULL;
588         rec->listLName = NULL;
589         rec->listNName = NULL;
590         rec->listAddress = NULL;
591         rec->listID = NULL;
592 }
593
594 /**
595  * Read file data into address cache.
596  * Note that one LDIF record identifies one entity uniquely with the
597  * distinguished name (dn) tag. Each person can have multiple E-Mail
598  * addresses. Also, each person can have many common name (cn) tags.
599  *
600  * \param  ldifFile LDIF import control object.
601  * \param  cache    Address cache to be populated with data.
602  */
603 static void ldif_read_file( LdifFile *ldifFile, AddressCache *cache ) {
604         gchar *tagName = NULL, *tagValue = NULL;
605         gchar *lastTag = NULL, *fullValue = NULL;
606         GSList *listValue = NULL;
607         gboolean flagEOF = FALSE, flagEOR = FALSE;
608         gboolean flag64 = FALSE, last64 = FALSE;
609         Ldif_ParsedRec *rec;
610         long posEnd = 0L;
611         long posCur = 0L;
612         GHashTable *hashField;
613
614         hashField = ldifFile->hashFields;
615         rec = g_new0( Ldif_ParsedRec, 1 );
616         ldif_clear_rec( rec );
617
618         /* Find EOF for progress indicator */
619         fseek( ldifFile->file, 0L, SEEK_END );
620         posEnd = ftell( ldifFile->file );
621         fseek( ldifFile->file, 0L, SEEK_SET );
622
623         while( ! flagEOF ) {
624                 gchar *line =  ldif_get_line( ldifFile );
625
626                 posCur = ftell( ldifFile->file );
627                 if( ldifFile->cbProgress ) {
628                         /* Call progress indicator */
629                         ( ldifFile->cbProgress ) ( ldifFile, & posEnd, & posCur );
630                 }
631
632                 flag64 = FALSE;
633                 if( line == NULL ) {
634                         flagEOF = flagEOR = TRUE;
635                 }
636                 else if( *line == '\0' ) {
637                         flagEOR = TRUE;
638                 }
639
640                 if( flagEOR ) {
641                         /* EOR, Output address data */
642                         if( lastTag ) {
643                                 /* Save record */
644                                 fullValue = mgu_list_coalesce( listValue );
645                                 if (fullValue && last64) {
646                                         gchar *out = g_malloc(strlen(fullValue));
647                                         int len = 0;
648                                         if ((len = base64_decode(out, fullValue,
649                                                         strlen(fullValue))) >= 0) {
650                                                 g_free(fullValue);
651                                                 fullValue = out;
652                                                 fullValue[len] = '\0';
653                                         } else
654                                                 g_free(out);
655                                 }
656                                 /* Base-64 encoded data */
657                                 /*
658                                 if( last64 ) {
659                                         ldif_dump_b64( fullValue );
660                                 }
661                                 */
662
663                                 ldif_add_value( rec, lastTag, fullValue, hashField );
664                                 /* ldif_print_record( rec, stdout ); */
665                                 ldif_build_items( ldifFile, rec, cache );
666                                 ldif_clear_rec( rec );
667                                 g_free( lastTag );
668                                 mgu_free_list( listValue );
669                                 lastTag = NULL;
670                                 listValue = NULL;
671                                 last64 = FALSE;
672                         }
673                 }
674                 if( line ) {
675                         flagEOR = FALSE;
676                         if( *line == ' ' ) {
677                                 /* Continuation line */
678                                 listValue = g_slist_append(
679                                         listValue, g_strdup( line+1 ) );
680                         }
681                         else if( *line == '=' ) {
682                                 /* Base-64 encoded continuation field */
683                                 listValue = g_slist_append(
684                                         listValue, g_strdup( line ) );
685                         }
686                         else {
687                                 /* Parse line */
688                                 tagName = ldif_get_tagname( line, &flag64 );
689                                 if( tagName ) {
690                                         tagValue = ldif_get_tagvalue( line );
691                                         if( tagValue ) {
692                                                 if( lastTag ) {
693                                                         /* Save data */
694                                                         fullValue =
695                                                                 mgu_list_coalesce( listValue );
696                                                         if (fullValue && last64) {
697                                                                 gchar *out = g_malloc(strlen(fullValue));
698                                                                 int len = 0;
699                                                                 if ((len = base64_decode(out, fullValue,
700                                                                                 strlen(fullValue))) >= 0) {
701                                                                         g_free(fullValue);
702                                                                         fullValue = out;
703                                                                         fullValue[len] = '\0';
704                                                                 } else
705                                                                         g_free(out);
706                                                         }
707                                                         /* Base-64 encoded data */
708                                                         /*
709                                                         if( last64 ) {
710                                                                 ldif_dump_b64( fullValue );
711                                                         }
712                                                         */
713
714                                                         ldif_add_value(
715                                                                 rec, lastTag, fullValue,
716                                                                 hashField );
717                                                         g_free( lastTag );
718                                                         mgu_free_list( listValue );
719                                                         lastTag = NULL;
720                                                         listValue = NULL;
721                                                 }
722
723                                                 lastTag = g_strdup( tagName );
724                                                 listValue = g_slist_append(
725                                                         listValue,
726                                                         g_strdup( tagValue ) );
727                                                 g_free( tagValue );
728                                                 last64 = flag64;
729                                         }
730                                         g_free( tagName );
731                                 }
732                         }
733                 }
734                 g_free( line );
735         }
736
737         /* Release data */
738         ldif_clear_rec( rec );
739         g_free( rec );
740         g_free( lastTag );
741         mgu_free_list( listValue );
742 }
743
744 /**
745  * Add list of field names to hash table.
746  * \param table Hashtable.
747  * \param list  List of fields.
748  */
749 static void ldif_hash_add_list( GHashTable *table, GSList *list ) {
750         GSList *node = list;
751
752         /* mgu_print_list( list, stdout ); */
753         while( node ) {
754                 gchar *tag = node->data;
755                 if( ! g_hash_table_lookup( table, tag ) ) {
756                         Ldif_FieldRec *rec = NULL;
757                         gchar *key = g_utf8_strdown( tag, -1 );
758
759                         rec = ldif_create_fieldrec( tag );
760                         if( g_utf8_collate( key, LDIF_TAG_DN ) == 0 ) {
761                                 rec->reserved = rec->selected = TRUE;
762                                 rec->userName = g_strdup( "dn" );
763                         }
764                         else if( g_utf8_collate( key, g_utf8_strdown( LDIF_TAG_COMMONNAME, -1 ) ) == 0 ) {
765                                 rec->reserved = rec->selected = TRUE;
766                                 rec->userName = g_strdup( _( "Display Name" ) );
767                         }
768                         else if( g_utf8_collate( key, g_utf8_strdown( LDIF_TAG_FIRSTNAME, -1 ) ) == 0 ) {
769                                 rec->reserved = rec->selected = TRUE;
770                                 rec->userName = g_strdup( _( "First Name" ) );
771                         }
772                         else if( g_utf8_collate( key, g_utf8_strdown( LDIF_TAG_LASTNAME, -1 ) ) == 0 ) {
773                                 rec->reserved = rec->selected = TRUE;
774                                 rec->userName = g_strdup( _( "Last Name" ) );
775                         }
776                         else if( g_utf8_collate( key, g_utf8_strdown( LDIF_TAG_NICKNAME, -1 ) ) == 0 ) {
777                                 rec->reserved = rec->selected = TRUE;
778                                 rec->userName = g_strdup( _( "Nick Name" ) );
779                         }
780                         else if( g_utf8_collate( key, g_utf8_strdown( LDIF_TAG_EMAIL, -1 ) ) == 0 ) {
781                                 rec->reserved = rec->selected = TRUE;
782                                 rec->userName = g_strdup( _( "Email Address" ) );
783                         }
784                         g_hash_table_insert( table, key, rec );
785                 }
786                 node = g_slist_next( node );
787         }
788 }
789
790 /**
791  * Sorted list comparison function.
792  * \param  ptr1 First field.
793  * \param  ptr2 Second field.
794  * \return <code>-1, 0, +1</code> if first record less than, equal,
795  *         greater than second.
796  */
797 static gint ldif_field_compare( gconstpointer ptr1, gconstpointer ptr2 ) {
798         const Ldif_FieldRec *rec1 = ptr1;
799         const Ldif_FieldRec *rec2 = ptr2;
800
801         if( rec1->reserved ) {
802                 if( ! rec2->reserved ) {
803                         return +1;
804                 }
805         }
806         else {
807                 if( rec2->reserved ) {
808                         return -1;
809                 }
810         }
811         return g_utf8_collate( rec1->tagName, rec2->tagName );
812 }
813
814 /*
815  * Append hash table entry to list - visitor function.
816  * \param key   Key.
817  * \param value Data value.
818  * \param data  User data (the LDIF import control object).
819  */
820 static void ldif_hash2list_vis( gpointer key, gpointer value, gpointer data ) {
821         LdifFile *ldf = data;
822         ldf->tempList =
823                 g_list_insert_sorted( ldf->tempList, value, ldif_field_compare );
824 }
825
826 /**
827  * Read tag names for file data.
828  * \param  ldifFile LDIF import control object.
829  */
830 static void ldif_read_tag_list( LdifFile *ldifFile ) {
831         gchar *tagName = NULL;
832         GSList *listTags = NULL;
833         gboolean flagEOF = FALSE, flagEOR = FALSE, flagMail = FALSE;
834         gboolean flag64 = FALSE;
835         long posEnd = 0L;
836         long posCur = 0L;
837
838         /* Clear hash table */
839         g_hash_table_foreach_remove(
840                 ldifFile->hashFields, ldif_hash_free_vis, NULL );
841
842         /* Find EOF for progress indicator */
843         fseek( ldifFile->file, 0L, SEEK_END );
844         posEnd = ftell( ldifFile->file );
845         fseek( ldifFile->file, 0L, SEEK_SET );
846
847         if (posEnd == 0) {
848                 ldifFile->retVal = MGU_EOF;
849                 return;
850         }
851                 
852         /* Process file */
853         while( ! flagEOF ) {
854                 gchar *line = ldif_get_line( ldifFile );
855                 posCur = ftell( ldifFile->file );
856                 if( ldifFile->cbProgress ) {
857                         /* Call progress indicator */
858                         ( ldifFile->cbProgress ) ( ldifFile, & posEnd, & posCur );
859                 }
860
861                 flag64 = FALSE;
862                 if( line == NULL ) {
863                         flagEOF = flagEOR = TRUE;
864                 }
865                 else if( *line == '\0' ) {
866                         flagEOR = TRUE;
867                 }
868
869                 if( flagEOR ) {
870                         /* EOR, Output address data */
871                         /* Save field list to hash table */
872                         if( flagMail ) {
873                                 ldif_hash_add_list(
874                                         ldifFile->hashFields, listTags );
875                         }
876                         mgu_free_list( listTags );
877                         listTags = NULL;
878                         flagMail = FALSE;
879                 }
880                 if( line ) {
881                         flagEOR = FALSE;
882                         if( *line == ' ' ) {
883                                 /* Continuation line */
884                         }
885                         else if( *line == '=' ) {
886                                 /* Base-64 encoded continuation field */
887                         }
888                         else {
889                                 /* Parse line */
890                                 tagName = ldif_get_tagname( line, &flag64 );
891                                 if( tagName ) {
892                                         /* Add tag to list */
893                                         listTags = g_slist_append( listTags, tagName );
894
895                                         if( g_utf8_collate(
896                                                 tagName, LDIF_TAG_EMAIL ) == 0 )
897                                         {
898                                                 flagMail = TRUE;
899                                         }
900                                 } else {
901                                         g_strstrip(line);
902                                         if (*line != '\0') {
903                                                 debug_print("ldif: bad format: '%s'\n", line);
904                                                 ldifFile->retVal = MGU_BAD_FORMAT;
905                                         }
906                                 }
907                         }
908                 }
909                 g_free( line );
910         }
911
912         /* Release data */
913         mgu_free_list( listTags );
914         listTags = NULL;
915 }
916
917 /**
918  * Read file into list. Main entry point
919  * \param  ldifFile LDIF import control object.
920  * \param  cache    Address cache to load.
921  * \return Status code.
922  */
923 gint ldif_import_data( LdifFile *ldifFile, AddressCache *cache ) {
924         cm_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS );
925         ldifFile->retVal = MGU_SUCCESS;
926         addrcache_clear( cache );
927         cache->dataRead = FALSE;
928         ldif_open_file( ldifFile );
929         if( ldifFile->retVal == MGU_SUCCESS ) {
930                 /* Read data into the cache */
931                 ldif_read_file( ldifFile, cache );
932                 ldif_close_file( ldifFile );
933
934                 /* Mark cache */
935                 cache->modified = FALSE;
936                 cache->dataRead = TRUE;
937         }
938         return ldifFile->retVal;
939 }
940
941 /**
942  * Process entire file reading list of unique fields. List of fields may be
943  * accessed with the <code>ldif_get_fieldlist()</code> function.
944  * \param  ldifFile LDIF import control object.
945  * \return Status code.
946  */
947 gint ldif_read_tags( LdifFile *ldifFile ) {
948         cm_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS );
949         ldifFile->retVal = MGU_SUCCESS;
950         if( ldifFile->dirtyFlag ) {
951                 ldif_open_file( ldifFile );
952                 if( ldifFile->retVal == MGU_SUCCESS ) {
953                         /* Read data into the cache */
954                         ldif_read_tag_list( ldifFile );
955                         ldif_close_file( ldifFile );
956                         ldifFile->dirtyFlag = FALSE;
957                         ldifFile->accessFlag = TRUE;
958                 }
959         }
960         return ldifFile->retVal;
961 }
962
963 /**
964  * Return list of fields for LDIF file.
965  * \param  ldifFile LDIF import control object.
966  * \return Linked list of <code>Ldif_FieldRec</code> objects. This list may be
967  *         <code>g_free()</code>. Note that the objects in the list should not
968  *         be freed since they refer to objects inside the internal cache.
969  *         These objects will be freed when LDIF file object is freed.
970  */
971 GList *ldif_get_fieldlist( LdifFile *ldifFile ) {
972         GList *list = NULL;
973
974         cm_return_val_if_fail( ldifFile != NULL, NULL );
975         if( ldifFile->hashFields ) {
976                 ldifFile->tempList = NULL;
977                 g_hash_table_foreach( ldifFile->hashFields, ldif_hash2list_vis, ldifFile );
978                 list = ldifFile->tempList;
979                 ldifFile->tempList = NULL;
980         }
981         return list;
982 }
983
984 /**
985  * Output LDIF name-value pair to stream. Only non-empty names and values will
986  * be output to file.
987  * \param stream File output stream.
988  * \param name   Name.
989  * \param value  Data value.
990  * \return <i>TRUE</i> if data output.
991  */
992 gboolean ldif_write_value( FILE *stream, const gchar *name, const gchar *value ) {
993         if( name == NULL ) return FALSE;
994         if( value == NULL ) return FALSE;
995         if( strlen( name ) < 1 ) return FALSE;
996         if( strlen( value ) < 1 ) return FALSE;
997         fprintf( stream, "%s: ", name );
998         fprintf( stream, "%s\n", value );
999         return TRUE;
1000 }
1001
1002 /**
1003  * Output LDIF End of Record to stream.
1004  * \param stream File output stream.
1005  * \return <i>TRUE</i> if data output.
1006  */
1007 void ldif_write_eor( FILE *stream ) {
1008         /* Simple but caller should not need to know how to end record. */
1009         fprintf( stream, "\n" );
1010 }
1011
1012 /*
1013  * ============================================================================
1014  * End of Source.
1015  * ============================================================================
1016  */
1017