2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2001 Match Grun
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 * Functions necessary to access LDIF files (LDAP Data Interchange Format
32 #include "addrcache.h"
39 LdifFile *ldif_create() {
41 ldifFile = g_new0( LdifFile, 1 );
42 ldifFile->path = NULL;
43 ldifFile->file = NULL;
44 ldifFile->bufptr = ldifFile->buffer;
45 ldifFile->hashFields = g_hash_table_new( g_str_hash, g_str_equal );
46 ldifFile->tempList = NULL;
47 ldifFile->dirtyFlag = TRUE;
48 ldifFile->accessFlag = FALSE;
49 ldifFile->retVal = MGU_SUCCESS;
50 ldifFile->cbProgress = NULL;
51 ldifFile->importCount = 0;
58 void ldif_set_file( LdifFile *ldifFile, const gchar *value ) {
59 g_return_if_fail( ldifFile != NULL );
61 if( ldifFile->path ) {
62 if( strcmp( ldifFile->path, value ) != 0 )
63 ldifFile->dirtyFlag = TRUE;
66 ldifFile->dirtyFlag = TRUE;
68 ldifFile->path = mgu_replace_string( ldifFile->path, value );
69 g_strstrip( ldifFile->path );
70 ldifFile->importCount = 0;
72 void ldif_set_accessed( LdifFile *ldifFile, const gboolean value ) {
73 g_return_if_fail( ldifFile != NULL );
74 ldifFile->accessFlag = value;
78 * Register a callback function. When called, the function will be passed
79 * the following arguments:
82 * Current position (long)
83 * This can be used for a progress indicator.
85 void ldif_set_callback( LdifFile *ldifFile, void *func ) {
86 ldifFile->cbProgress = func;
90 * Create field record object.
92 static Ldif_FieldRec *ldif_create_fieldrec( gchar *field ) {
93 Ldif_FieldRec *rec = g_new0( Ldif_FieldRec, 1 );
94 rec->tagName = g_strdup( field );
96 rec->reserved = FALSE;
97 rec->selected = FALSE;
102 * Free field record object.
104 static void ldif_free_fieldrec( Ldif_FieldRec *rec ) {
106 g_free( rec->tagName );
107 g_free( rec->userName );
109 rec->userName = NULL;
110 rec->reserved = FALSE;
111 rec->selected = FALSE;
117 * Free hash table entry visitor function.
119 static gint ldif_hash_free_vis( gpointer key, gpointer value, gpointer data ) {
120 ldif_free_fieldrec( ( Ldif_FieldRec * ) value );
127 * Free up object by releasing internal memory.
129 void ldif_free( LdifFile *ldifFile ) {
130 g_return_if_fail( ldifFile != NULL );
133 if( ldifFile->file ) fclose( ldifFile->file );
135 /* Free internal stuff */
136 g_free( ldifFile->path );
138 /* Free field list */
139 g_hash_table_foreach_remove( ldifFile->hashFields, ldif_hash_free_vis, NULL );
140 g_hash_table_destroy( ldifFile->hashFields );
141 ldifFile->hashFields = NULL;
144 ldifFile->file = NULL;
145 ldifFile->path = NULL;
146 ldifFile->retVal = MGU_SUCCESS;
147 ldifFile->tempList = NULL;
148 ldifFile->dirtyFlag = FALSE;
149 ldifFile->accessFlag = FALSE;
150 ldifFile->cbProgress = NULL;
152 /* Now release file object */
157 * Display field record.
159 void ldif_print_fieldrec( Ldif_FieldRec *rec, FILE *stream ) {
160 fprintf( stream, "\ttag:\t%s", rec->reserved ? "yes" : "no" );
161 fprintf( stream, "\t%s", rec->selected ? "yes" : "no" );
162 fprintf( stream, "\t:%s:\t:%s:\n", rec->userName, rec->tagName );
166 * Display field record.
169 static void ldif_print_file_vis( gpointer key, gpointer value, gpointer data ) {
170 Ldif_FieldRec *rec = value;
172 ldif_print_fieldrec( rec, stream );
176 * Display object to specified stream.
178 void ldif_print_file( LdifFile *ldifFile, FILE *stream ) {
179 g_return_if_fail( ldifFile != NULL );
180 fprintf( stream, "LDIF File:\n" );
181 fprintf( stream, "file spec: '%s'\n", ldifFile->path );
182 fprintf( stream, " ret val: %d\n", ldifFile->retVal );
183 fprintf( stream, " fields: {\n" );
184 g_hash_table_foreach( ldifFile->hashFields, ldif_print_file_vis, stream );
185 fprintf( stream, "} ---\n" );
189 * Open file for read.
190 * return: TRUE if file opened successfully.
192 static gint ldif_open_file( LdifFile* ldifFile ) {
193 /* printf( "Opening file\n" ); */
194 if( ldifFile->path ) {
195 ldifFile->file = fopen( ldifFile->path, "rb" );
196 if( ! ldifFile->file ) {
197 /* printf( "can't open %s\n", ldifFile->path ); */
198 ldifFile->retVal = MGU_OPEN_FILE;
199 return ldifFile->retVal;
203 /* printf( "file not specified\n" ); */
204 ldifFile->retVal = MGU_NO_FILE;
205 return ldifFile->retVal;
208 /* Setup a buffer area */
209 ldifFile->buffer[0] = '\0';
210 ldifFile->bufptr = ldifFile->buffer;
211 ldifFile->retVal = MGU_SUCCESS;
212 return ldifFile->retVal;
218 static void ldif_close_file( LdifFile *ldifFile ) {
219 g_return_if_fail( ldifFile != NULL );
220 if( ldifFile->file ) fclose( ldifFile->file );
221 ldifFile->file = NULL;
225 * Read line of text from file.
226 * Return: ptr to buffer where line starts.
228 static gchar *ldif_get_line( LdifFile *ldifFile ) {
229 gchar buf[ LDIFBUFSIZE ];
233 if( feof( ldifFile->file ) ) return NULL;
238 ch = fgetc( ldifFile->file );
239 if( ch == '\0' || ch == EOF ) {
240 if( *buf == '\0' ) return NULL;
243 #if HAVE_DOSISH_SYSTEM
245 if( ch == '\r' ) continue;
247 if( ch == '\n' ) break;
252 /* Return a copy of buffer */
253 return g_strdup( buf );
257 * Parse tag name from line buffer.
258 * Enter: line Buffer.
259 * flag64 Base-64 encoder flag.
260 * Return: Buffer containing the tag name, or NULL if no delimiter char found.
261 * If a double delimiter (::) is found, flag64 is set.
263 static gchar *ldif_get_tagname( char* line, gboolean *flag64 ) {
270 /* Check for language tag */
271 if( *lptr == LDIF_LANG_TAG ) {
272 if( sptr == NULL ) sptr = lptr;
275 /* Check for delimiter */
276 if( *lptr == LDIF_SEP_TAG ) {
284 /* Base-64 encoding? */
285 if( * ++lptr == LDIF_SEP_TAG ) *flag64 = TRUE;
287 tag = g_strndup( line, len+1 );
297 * Parse tag value from line buffer.
298 * Enter: line Buffer.
299 * Return: Buffer containing the tag value. Empty string is returned if
300 * no delimiter char found.
302 static gchar *ldif_get_tagvalue( gchar* line ) {
308 for( lptr = line; *lptr; lptr++ ) {
309 if( *lptr == LDIF_SEP_TAG ) {
315 if( *start == LDIF_SEP_TAG ) start++;
317 value = g_strndup( start, len+1 );
321 /* Ensure that we get an empty string */
322 value = g_strndup( "", 1 );
329 * Parsed address data.
331 typedef struct _Ldif_ParsedRec_ Ldif_ParsedRec;
332 struct _Ldif_ParsedRec_ {
343 * User attribute data.
345 typedef struct _Ldif_UserAttr_ Ldif_UserAttr;
346 struct _Ldif_UserAttr_ {
352 * Build an address list entry and append to list of address items. Name is formatted
353 * as "<first-name> <last-name>".
355 static void ldif_build_items( LdifFile *ldifFile, Ldif_ParsedRec *rec, AddressCache *cache ) {
359 gchar *firstName = NULL, *lastName = NULL, *fullName = NULL, *nickName = NULL;
360 gint iLen = 0, iLenT = 0;
364 nodeAddress = rec->listAddress;
365 if( nodeAddress == NULL ) return;
367 /* Find longest first name in list */
368 nodeFirst = rec->listFName;
370 if( firstName == NULL ) {
371 firstName = nodeFirst->data;
372 iLen = strlen( firstName );
375 if( ( iLenT = strlen( nodeFirst->data ) ) > iLen ) {
376 firstName = nodeFirst->data;
380 nodeFirst = g_slist_next( nodeFirst );
384 if( rec->listLName ) {
385 lastName = rec->listLName->data;
390 fullName = g_strdup_printf( "%s %s", firstName, lastName );
393 fullName = g_strdup_printf( "%s", firstName );
398 fullName = g_strdup_printf( "%s", lastName );
402 g_strchug( fullName ); g_strchomp( fullName );
405 if( rec->listNName ) {
406 nickName = rec->listNName->data;
409 person = addritem_create_item_person();
410 addritem_person_set_common_name( person, fullName );
411 addritem_person_set_first_name( person, firstName );
412 addritem_person_set_last_name( person, lastName );
413 addritem_person_set_nick_name( person, nickName );
414 addrcache_id_person( cache, person );
415 addrcache_add_person( cache, person );
416 ++ldifFile->importCount;
418 /* Add address item */
419 while( nodeAddress ) {
420 email = addritem_create_item_email();
421 addritem_email_set_address( email, nodeAddress->data );
422 addrcache_id_email( cache, email );
423 addrcache_person_add_email( cache, person, email );
424 nodeAddress = g_slist_next( nodeAddress );
427 fullName = firstName = lastName = NULL;
429 /* Add user attributes */
430 nodeAttr = rec->userAttr;
432 Ldif_UserAttr *attr = nodeAttr->data;
433 UserAttribute *attrib = addritem_create_attribute();
434 addritem_attrib_set_name( attrib, attr->name );
435 addritem_attrib_set_value( attrib, attr->value );
436 addritem_person_add_attribute( person, attrib );
437 nodeAttr = g_slist_next( nodeAttr );
443 * Add selected field as user attribute.
445 static void ldif_add_user_attr( Ldif_ParsedRec *rec, gchar *tagName, gchar *tagValue, GHashTable *hashField ) {
446 Ldif_FieldRec *fld = NULL;
447 Ldif_UserAttr *attr = NULL;
450 fld = g_hash_table_lookup( hashField, tagName );
452 if( fld->reserved ) return;
453 if( ! fld->selected ) return;
456 if( fld->userName ) {
457 name = fld->userName;
459 attr = g_new0( Ldif_UserAttr, 1 );
460 attr->name = g_strdup( name );
461 attr->value = g_strdup( tagValue );
462 rec->userAttr = g_slist_append( rec->userAttr, attr );
467 * Add value to parsed data.
469 static void ldif_add_value( Ldif_ParsedRec *rec, gchar *tagName, gchar *tagValue, GHashTable *hashField ) {
472 nm = g_strdup( tagName );
475 val = g_strdup( tagValue );
478 val = g_strdup( "" );
481 if( g_strcasecmp( nm, LDIF_TAG_COMMONNAME ) == 0 ) {
482 rec->listCName = g_slist_append( rec->listCName, val );
484 else if( g_strcasecmp( nm, LDIF_TAG_FIRSTNAME ) == 0 ) {
485 rec->listFName = g_slist_append( rec->listFName, val );
487 else if( g_strcasecmp( nm, LDIF_TAG_LASTNAME ) == 0 ) {
488 rec->listLName = g_slist_append( rec->listLName, val );
490 else if( g_strcasecmp( nm, LDIF_TAG_NICKNAME ) == 0 ) {
491 rec->listNName = g_slist_append( rec->listNName, val );
493 else if( g_strcasecmp( nm, LDIF_TAG_EMAIL ) == 0 ) {
494 rec->listAddress = g_slist_append( rec->listAddress, val );
497 /* Add field as user attribute */
498 ldif_add_user_attr( rec, tagName, tagValue, hashField );
506 static void ldif_clear_rec( Ldif_ParsedRec *rec ) {
509 /* Free up user attributes */
510 list = rec->userAttr;
512 Ldif_UserAttr *attr = list->data;
513 g_free( attr->name );
514 g_free( attr->value );
516 list = g_slist_next( list );
518 g_slist_free( rec->userAttr );
520 g_slist_free( rec->listCName );
521 g_slist_free( rec->listFName );
522 g_slist_free( rec->listLName );
523 g_slist_free( rec->listNName );
524 g_slist_free( rec->listAddress );
525 g_slist_free( rec->listID );
527 rec->userAttr = NULL;
528 rec->listCName = NULL;
529 rec->listFName = NULL;
530 rec->listLName = NULL;
531 rec->listNName = NULL;
532 rec->listAddress = NULL;
540 static void ldif_print_record( Ldif_ParsedRec *rec, FILE *stream ) {
543 fprintf( stream, "LDIF Parsed Record:\n" );
544 fprintf( stream, "common name:" );
545 mgu_print_list( rec->listCName, stream );
546 if( ! rec->listCName ) fprintf( stream, "\n" );
547 fprintf( stream, "first name:" );
548 mgu_print_list( rec->listFName, stream );
549 if( ! rec->listFName ) fprintf( stream, "\n" );
550 fprintf( stream, "last name:" );
551 mgu_print_list( rec->listLName, stream );
552 if( ! rec->listLName ) fprintf( stream, "\n" );
553 fprintf( stream, "nick name:" );
554 mgu_print_list( rec->listNName, stream );
555 if( ! rec->listNName ) fprintf( stream, "\n" );
556 fprintf( stream, "address:" );
557 mgu_print_list( rec->listAddress, stream );
558 if( ! rec->listAddress ) fprintf( stream, "\n" );
559 fprintf( stream, "id:" );
560 mgu_print_list( rec->listID, stream );
561 if( ! rec->listID ) fprintf( stream, "\n" );
563 list = rec->userAttr;
565 Ldif_UserAttr *attr = list->data;
566 fprintf( stream, "n/v:\t%s:\t:%s:\n", attr->name, attr->value );
567 list = g_slist_next( list );
574 * Read file data into address cache.
575 * Note that one LDIF record identifies one entity uniquely with the
576 * distinguished name (dn) tag. Each person can have multiple E-Mail
577 * addresses. Also, each person can have many common name (cn) tags.
579 static void ldif_read_file( LdifFile *ldifFile, AddressCache *cache ) {
580 gchar *tagName = NULL, *tagValue = NULL;
581 gchar *lastTag = NULL, *fullValue = NULL;
582 GSList *listValue = NULL;
583 gboolean flagEOF = FALSE, flagEOR = FALSE;
584 gboolean flag64 = FALSE, last64 = FALSE;
588 GHashTable *hashField;
590 hashField = ldifFile->hashFields;
591 rec = g_new0( Ldif_ParsedRec, 1 );
592 ldif_clear_rec( rec );
594 /* Find EOF for progress indicator */
595 fseek( ldifFile->file, 0L, SEEK_END );
596 posEnd = ftell( ldifFile->file );
597 fseek( ldifFile->file, 0L, SEEK_SET );
600 gchar *line = ldif_get_line( ldifFile );
602 posCur = ftell( ldifFile->file );
603 if( ldifFile->cbProgress ) {
604 /* Call progress indicator */
605 ( ldifFile->cbProgress ) ( ldifFile, & posEnd, & posCur );
610 flagEOF = flagEOR = TRUE;
612 else if( *line == '\0' ) {
617 /* EOR, Output address data */
620 fullValue = mgu_list_coalesce( listValue );
622 /* Base-64 encoded data */
625 ldif_dump_b64( fullValue );
629 ldif_add_value( rec, lastTag, fullValue, hashField );
630 /* ldif_print_record( rec, stdout ); */
631 ldif_build_items( ldifFile, rec, cache );
632 ldif_clear_rec( rec );
634 mgu_free_list( listValue );
643 /* Continuation line */
644 listValue = g_slist_append( listValue, g_strdup( line+1 ) );
646 else if( *line == '=' ) {
647 /* Base-64 encoded continuation field */
648 listValue = g_slist_append( listValue, g_strdup( line ) );
652 tagName = ldif_get_tagname( line, &flag64 );
654 tagValue = ldif_get_tagvalue( line );
658 fullValue = mgu_list_coalesce( listValue );
659 /* Base-64 encoded data */
662 ldif_dump_b64( fullValue );
666 ldif_add_value( rec, lastTag, fullValue, hashField );
668 mgu_free_list( listValue );
674 lastTag = g_strdup( tagName );
675 listValue = g_slist_append( listValue, g_strdup( tagValue ) );
687 ldif_clear_rec( rec );
690 mgu_free_list( listValue );
694 * Add list of field names to hash table.
696 static void ldif_hash_add_list( GHashTable *table, GSList *list ) {
699 /* mgu_print_list( list, stdout ); */
701 gchar *tag = node->data;
702 if( ! g_hash_table_lookup( table, tag ) ) {
703 Ldif_FieldRec *rec = NULL;
704 gchar *key = g_strdup( tag );
706 rec = ldif_create_fieldrec( tag );
707 if( g_strcasecmp( tag, LDIF_TAG_COMMONNAME ) == 0 ) {
708 rec->reserved = TRUE;
710 else if( g_strcasecmp( tag, LDIF_TAG_FIRSTNAME ) == 0 ) {
711 rec->reserved = TRUE;
713 else if( g_strcasecmp( tag, LDIF_TAG_LASTNAME ) == 0 ) {
714 rec->reserved = TRUE;
716 else if( g_strcasecmp( tag, LDIF_TAG_NICKNAME ) == 0 ) {
717 rec->reserved = TRUE;
719 else if( g_strcasecmp( tag, LDIF_TAG_EMAIL ) == 0 ) {
720 rec->reserved = TRUE;
722 g_hash_table_insert( table, key, rec );
724 node = g_slist_next( node );
729 * Sorted list comparison function.
731 static int ldif_field_compare( gconstpointer ptr1, gconstpointer ptr2 ) {
732 const Ldif_FieldRec *rec1 = ptr1;
733 const Ldif_FieldRec *rec2 = ptr2;
734 return g_strcasecmp( rec1->tagName, rec2->tagName );
738 * Append hash table entry to list - visitor function.
740 static void ldif_hash2list_vis( gpointer key, gpointer value, gpointer data ) {
741 LdifFile *ldf = data;
742 ldf->tempList = g_list_insert_sorted( ldf->tempList, value, ldif_field_compare );
746 * Read tag names for file data.
748 static void ldif_read_tag_list( LdifFile *ldifFile ) {
749 gchar *tagName = NULL;
750 GSList *listTags = NULL;
751 gboolean flagEOF = FALSE, flagEOR = FALSE, flagMail = FALSE;
752 gboolean flag64 = FALSE;
756 /* Clear hash table */
757 g_hash_table_foreach_remove( ldifFile->hashFields, ldif_hash_free_vis, NULL );
759 /* Find EOF for progress indicator */
760 fseek( ldifFile->file, 0L, SEEK_END );
761 posEnd = ftell( ldifFile->file );
762 fseek( ldifFile->file, 0L, SEEK_SET );
766 gchar *line = ldif_get_line( ldifFile );
768 posCur = ftell( ldifFile->file );
769 if( ldifFile->cbProgress ) {
770 /* Call progress indicator */
771 ( ldifFile->cbProgress ) ( ldifFile, & posEnd, & posCur );
776 flagEOF = flagEOR = TRUE;
778 else if( *line == '\0' ) {
783 /* EOR, Output address data */
784 /* Save field list to hash table */
786 ldif_hash_add_list( ldifFile->hashFields, listTags );
788 mgu_free_list( listTags );
795 /* Continuation line */
797 else if( *line == '=' ) {
798 /* Base-64 encoded continuation field */
802 tagName = ldif_get_tagname( line, &flag64 );
804 /* Add tag to list */
805 listTags = g_slist_append( listTags, tagName );
806 if( g_strcasecmp( tagName, LDIF_TAG_EMAIL ) == 0 ) {
816 mgu_free_list( listTags );
821 * ============================================================================
822 * Read file into list. Main entry point
823 * Enter: ldifFile LDIF control data.
824 * cache Address cache to load.
825 * Return: Status code.
826 * ============================================================================
828 gint ldif_import_data( LdifFile *ldifFile, AddressCache *cache ) {
829 g_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS );
830 ldifFile->retVal = MGU_SUCCESS;
831 addrcache_clear( cache );
832 cache->dataRead = FALSE;
833 ldif_open_file( ldifFile );
834 if( ldifFile->retVal == MGU_SUCCESS ) {
835 /* Read data into the cache */
836 ldif_read_file( ldifFile, cache );
837 ldif_close_file( ldifFile );
840 cache->modified = FALSE;
841 cache->dataRead = TRUE;
843 return ldifFile->retVal;
847 * ============================================================================
848 * Process entire file reading list of unique fields. List of fields may be
849 * accessed with the ldif_get_fieldlist() function.
850 * Enter: ldifFile LDIF control data.
851 * Return: Status code.
852 * ============================================================================
854 gint ldif_read_tags( LdifFile *ldifFile ) {
855 g_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS );
856 ldifFile->retVal = MGU_SUCCESS;
857 if( ldifFile->dirtyFlag ) {
858 ldif_open_file( ldifFile );
859 if( ldifFile->retVal == MGU_SUCCESS ) {
860 /* Read data into the cache */
861 ldif_read_tag_list( ldifFile );
862 ldif_close_file( ldifFile );
863 ldifFile->dirtyFlag = FALSE;
864 ldifFile->accessFlag = TRUE;
867 return ldifFile->retVal;
871 * Return list of fields for LDIF file.
872 * Enter: ldifFile LdifFile object.
873 * Return: Linked list of Ldif_FieldRec objects. This list may be g_free'd.
874 * Note that the objects in the list should not be freed since they refer to
875 * objects inside the internal cache. These objects will be freed when
876 * LDIF file object is freed.
878 GList *ldif_get_fieldlist( LdifFile *ldifFile ) {
881 g_return_val_if_fail( ldifFile != NULL, NULL );
882 if( ldifFile->hashFields ) {
883 ldifFile->tempList = NULL;
884 g_hash_table_foreach( ldifFile->hashFields, ldif_hash2list_vis, ldifFile );
885 list = ldifFile->tempList;
886 ldifFile->tempList = NULL;