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