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