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
31 #include "addrcache.h"
38 LdifFile *ldif_create() {
40 ldifFile = g_new0( LdifFile, 1 );
41 ldifFile->path = NULL;
42 ldifFile->file = NULL;
43 ldifFile->bufptr = ldifFile->buffer;
44 ldifFile->hashFields = g_hash_table_new( g_str_hash, g_str_equal );
45 ldifFile->tempList = NULL;
46 ldifFile->dirtyFlag = TRUE;
47 ldifFile->accessFlag = FALSE;
48 ldifFile->retVal = MGU_SUCCESS;
49 ldifFile->cbProgress = NULL;
50 ldifFile->importCount = 0;
57 void ldif_set_file( LdifFile *ldifFile, const gchar *value ) {
58 g_return_if_fail( ldifFile != NULL );
60 if( ldifFile->path ) {
61 if( strcmp( ldifFile->path, value ) != 0 )
62 ldifFile->dirtyFlag = TRUE;
65 ldifFile->dirtyFlag = TRUE;
67 ldifFile->path = mgu_replace_string( ldifFile->path, value );
68 g_strstrip( ldifFile->path );
69 ldifFile->importCount = 0;
71 void ldif_set_accessed( LdifFile *ldifFile, const gboolean value ) {
72 g_return_if_fail( ldifFile != NULL );
73 ldifFile->accessFlag = value;
77 * Register a callback function. When called, the function will be passed
78 * the following arguments:
81 * Current position (long)
82 * This can be used for a progress indicator.
84 void ldif_set_callback( LdifFile *ldifFile, void *func ) {
85 ldifFile->cbProgress = func;
89 * Create field record object.
91 static Ldif_FieldRec *ldif_create_fieldrec( gchar *field ) {
92 Ldif_FieldRec *rec = g_new0( Ldif_FieldRec, 1 );
93 rec->tagName = g_strdup( field );
95 rec->reserved = FALSE;
96 rec->selected = FALSE;
101 * Free field record object.
103 static void ldif_free_fieldrec( Ldif_FieldRec *rec ) {
105 g_free( rec->tagName );
106 g_free( rec->userName );
108 rec->userName = NULL;
109 rec->reserved = FALSE;
110 rec->selected = FALSE;
116 * Free hash table entry visitor function.
118 static gint ldif_hash_free_vis( gpointer key, gpointer value, gpointer data ) {
119 ldif_free_fieldrec( ( Ldif_FieldRec * ) value );
126 * Free up object by releasing internal memory.
128 void ldif_free( LdifFile *ldifFile ) {
129 g_return_if_fail( ldifFile != NULL );
132 if( ldifFile->file ) fclose( ldifFile->file );
134 /* Free internal stuff */
135 g_free( ldifFile->path );
137 /* Free field list */
138 g_hash_table_foreach_remove( ldifFile->hashFields, ldif_hash_free_vis, NULL );
139 g_hash_table_destroy( ldifFile->hashFields );
140 ldifFile->hashFields = NULL;
143 ldifFile->file = NULL;
144 ldifFile->path = NULL;
145 ldifFile->retVal = MGU_SUCCESS;
146 ldifFile->tempList = NULL;
147 ldifFile->dirtyFlag = FALSE;
148 ldifFile->accessFlag = FALSE;
149 ldifFile->cbProgress = NULL;
151 /* Now release file object */
156 * Display field record.
158 void ldif_print_fieldrec( Ldif_FieldRec *rec, FILE *stream ) {
159 fprintf( stream, "\ttag:\t%s", rec->reserved ? "yes" : "no" );
160 fprintf( stream, "\t%s", rec->selected ? "yes" : "no" );
161 fprintf( stream, "\t:%s:\t:%s:\n", rec->userName, rec->tagName );
165 * Display field record.
168 static void ldif_print_file_vis( gpointer key, gpointer value, gpointer data ) {
169 Ldif_FieldRec *rec = value;
171 ldif_print_fieldrec( rec, stream );
175 * Display object to specified stream.
177 void ldif_print_file( LdifFile *ldifFile, FILE *stream ) {
178 g_return_if_fail( ldifFile != NULL );
179 fprintf( stream, "LDIF File:\n" );
180 fprintf( stream, "file spec: '%s'\n", ldifFile->path );
181 fprintf( stream, " ret val: %d\n", ldifFile->retVal );
182 fprintf( stream, " fields: {\n" );
183 g_hash_table_foreach( ldifFile->hashFields, ldif_print_file_vis, stream );
184 fprintf( stream, "} ---\n" );
188 * Open file for read.
189 * return: TRUE if file opened successfully.
191 static gint ldif_open_file( LdifFile* ldifFile ) {
192 /* printf( "Opening file\n" ); */
193 if( ldifFile->path ) {
194 ldifFile->file = fopen( ldifFile->path, "r" );
195 if( ! ldifFile->file ) {
196 /* printf( "can't open %s\n", ldifFile->path ); */
197 ldifFile->retVal = MGU_OPEN_FILE;
198 return ldifFile->retVal;
202 /* printf( "file not specified\n" ); */
203 ldifFile->retVal = MGU_NO_FILE;
204 return ldifFile->retVal;
207 /* Setup a buffer area */
208 ldifFile->buffer[0] = '\0';
209 ldifFile->bufptr = ldifFile->buffer;
210 ldifFile->retVal = MGU_SUCCESS;
211 return ldifFile->retVal;
217 static void ldif_close_file( LdifFile *ldifFile ) {
218 g_return_if_fail( ldifFile != NULL );
219 if( ldifFile->file ) fclose( ldifFile->file );
220 ldifFile->file = NULL;
224 * Read line of text from file.
225 * Return: ptr to buffer where line starts.
227 static gchar *ldif_get_line( LdifFile *ldifFile ) {
228 gchar buf[ LDIFBUFSIZE ];
232 if( feof( ldifFile->file ) ) return NULL;
237 ch = fgetc( ldifFile->file );
238 if( ch == '\0' || ch == EOF ) {
239 if( *buf == '\0' ) return NULL;
242 #if HAVE_DOSISH_SYSTEM
244 if( ch == '\r' ) continue;
246 if( ch == '\n' ) break;
251 /* Return a copy of buffer */
252 return g_strdup( buf );
256 * Parse tag name from line buffer.
257 * Enter: line Buffer.
258 * flag64 Base-64 encoder flag.
259 * Return: Buffer containing the tag name, or NULL if no delimiter char found.
260 * If a double delimiter (::) is found, flag64 is set.
262 static gchar *ldif_get_tagname( char* line, gboolean *flag64 ) {
269 /* Check for language tag */
270 if( *lptr == LDIF_LANG_TAG ) {
271 if( sptr == NULL ) sptr = lptr;
274 /* Check for delimiter */
275 if( *lptr == LDIF_SEP_TAG ) {
283 /* Base-64 encoding? */
284 if( * ++lptr == LDIF_SEP_TAG ) *flag64 = TRUE;
286 tag = g_strndup( line, len+1 );
296 * Parse tag value from line buffer.
297 * Enter: line Buffer.
298 * Return: Buffer containing the tag value. Empty string is returned if
299 * no delimiter char found.
301 static gchar *ldif_get_tagvalue( gchar* line ) {
307 for( lptr = line; *lptr; lptr++ ) {
308 if( *lptr == LDIF_SEP_TAG ) {
314 if( *start == LDIF_SEP_TAG ) start++;
316 value = g_strndup( start, len+1 );
320 /* Ensure that we get an empty string */
321 value = g_strndup( "", 1 );
328 * Dump linked lists of character strings (for debug).
330 static void ldif_dump_lists( GSList *listName, GSList *listAddr, GSList *listRem, GSList *listID, FILE *stream ) {
331 fprintf( stream, "dump name\n" );
332 fprintf( stream, "------------\n" );
333 mgu_print_list( listName, stdout );
334 fprintf( stream, "dump address\n" );
335 fprintf( stream, "------------\n" );
336 mgu_print_list( listAddr, stdout );
337 fprintf( stream, "dump remarks\n" );
338 fprintf( stdout, "------------\n" );
339 mgu_print_list( listRem, stdout );
340 fprintf( stream, "dump id\n" );
341 fprintf( stdout, "------------\n" );
342 mgu_print_list( listID, stdout );
346 * Parsed address data.
348 typedef struct _Ldif_ParsedRec_ Ldif_ParsedRec;
349 struct _Ldif_ParsedRec_ {
360 * User attribute data.
362 typedef struct _Ldif_UserAttr_ Ldif_UserAttr;
363 struct _Ldif_UserAttr_ {
369 * Build an address list entry and append to list of address items. Name is formatted
370 * as "<first-name> <last-name>".
372 static void ldif_build_items( LdifFile *ldifFile, Ldif_ParsedRec *rec, AddressCache *cache ) {
376 gchar *firstName = NULL, *lastName = NULL, *fullName = NULL, *nickName = NULL;
377 gint iLen = 0, iLenT = 0;
381 nodeAddress = rec->listAddress;
382 if( nodeAddress == NULL ) return;
384 /* Find longest first name in list */
385 nodeFirst = rec->listFName;
387 if( firstName == NULL ) {
388 firstName = nodeFirst->data;
389 iLen = strlen( firstName );
392 if( ( iLenT = strlen( nodeFirst->data ) ) > iLen ) {
393 firstName = nodeFirst->data;
397 nodeFirst = g_slist_next( nodeFirst );
401 if( rec->listLName ) {
402 lastName = rec->listLName->data;
407 fullName = g_strdup_printf( "%s %s", firstName, lastName );
410 fullName = g_strdup_printf( "%s", firstName );
415 fullName = g_strdup_printf( "%s", lastName );
419 g_strchug( fullName ); g_strchomp( fullName );
422 if( rec->listNName ) {
423 nickName = rec->listNName->data;
426 person = addritem_create_item_person();
427 addritem_person_set_common_name( person, fullName );
428 addritem_person_set_first_name( person, firstName );
429 addritem_person_set_last_name( person, lastName );
430 addritem_person_set_nick_name( person, nickName );
431 addrcache_id_person( cache, person );
432 addrcache_add_person( cache, person );
433 ++ldifFile->importCount;
435 /* Add address item */
436 while( nodeAddress ) {
437 email = addritem_create_item_email();
438 addritem_email_set_address( email, nodeAddress->data );
439 addrcache_id_email( cache, email );
440 addrcache_person_add_email( cache, person, email );
441 nodeAddress = g_slist_next( nodeAddress );
444 fullName = firstName = lastName = NULL;
446 /* Add user attributes */
447 nodeAttr = rec->userAttr;
449 Ldif_UserAttr *attr = nodeAttr->data;
450 UserAttribute *attrib = addritem_create_attribute();
451 addritem_attrib_set_name( attrib, attr->name );
452 addritem_attrib_set_value( attrib, attr->value );
453 addritem_person_add_attribute( person, attrib );
454 nodeAttr = g_slist_next( nodeAttr );
460 * Add selected field as user attribute.
462 static void ldif_add_user_attr( Ldif_ParsedRec *rec, gchar *tagName, gchar *tagValue, GHashTable *hashField ) {
463 Ldif_FieldRec *fld = NULL;
464 Ldif_UserAttr *attr = NULL;
467 fld = g_hash_table_lookup( hashField, tagName );
469 if( fld->reserved ) return;
470 if( ! fld->selected ) return;
473 if( fld->userName ) {
474 name = fld->userName;
476 attr = g_new0( Ldif_UserAttr, 1 );
477 attr->name = g_strdup( name );
478 attr->value = g_strdup( tagValue );
479 rec->userAttr = g_slist_append( rec->userAttr, attr );
484 * Add value to parsed data.
486 static void ldif_add_value( Ldif_ParsedRec *rec, gchar *tagName, gchar *tagValue, GHashTable *hashField ) {
489 nm = g_strdup( tagName );
492 val = g_strdup( tagValue );
495 val = g_strdup( "" );
498 if( g_strcasecmp( nm, LDIF_TAG_COMMONNAME ) == 0 ) {
499 rec->listCName = g_slist_append( rec->listCName, val );
501 else if( g_strcasecmp( nm, LDIF_TAG_FIRSTNAME ) == 0 ) {
502 rec->listFName = g_slist_append( rec->listFName, val );
504 else if( g_strcasecmp( nm, LDIF_TAG_LASTNAME ) == 0 ) {
505 rec->listLName = g_slist_append( rec->listLName, val );
507 else if( g_strcasecmp( nm, LDIF_TAG_NICKNAME ) == 0 ) {
508 rec->listNName = g_slist_append( rec->listNName, val );
510 else if( g_strcasecmp( nm, LDIF_TAG_EMAIL ) == 0 ) {
511 rec->listAddress = g_slist_append( rec->listAddress, val );
514 /* Add field as user attribute */
515 ldif_add_user_attr( rec, tagName, tagValue, hashField );
523 static void ldif_clear_rec( Ldif_ParsedRec *rec ) {
526 /* Free up user attributes */
527 list = rec->userAttr;
529 Ldif_UserAttr *attr = list->data;
530 g_free( attr->name );
531 g_free( attr->value );
533 list = g_slist_next( list );
535 g_slist_free( rec->userAttr );
537 g_slist_free( rec->listCName );
538 g_slist_free( rec->listFName );
539 g_slist_free( rec->listLName );
540 g_slist_free( rec->listNName );
541 g_slist_free( rec->listAddress );
542 g_slist_free( rec->listID );
544 rec->userAttr = NULL;
545 rec->listCName = NULL;
546 rec->listFName = NULL;
547 rec->listLName = NULL;
548 rec->listNName = NULL;
549 rec->listAddress = NULL;
557 static void ldif_print_record( Ldif_ParsedRec *rec, FILE *stream ) {
560 fprintf( stream, "LDIF Parsed Record:\n" );
561 fprintf( stream, "common name:" );
562 mgu_print_list( rec->listCName, stream );
563 if( ! rec->listCName ) fprintf( stream, "\n" );
564 fprintf( stream, "first name:" );
565 mgu_print_list( rec->listFName, stream );
566 if( ! rec->listFName ) fprintf( stream, "\n" );
567 fprintf( stream, "last name:" );
568 mgu_print_list( rec->listLName, stream );
569 if( ! rec->listLName ) fprintf( stream, "\n" );
570 fprintf( stream, "nick name:" );
571 mgu_print_list( rec->listNName, stream );
572 if( ! rec->listNName ) fprintf( stream, "\n" );
573 fprintf( stream, "address:" );
574 mgu_print_list( rec->listAddress, stream );
575 if( ! rec->listAddress ) fprintf( stream, "\n" );
576 fprintf( stream, "id:" );
577 mgu_print_list( rec->listID, stream );
578 if( ! rec->listID ) fprintf( stream, "\n" );
580 list = rec->userAttr;
582 Ldif_UserAttr *attr = list->data;
583 fprintf( stream, "n/v:\t%s:\t:%s:\n", attr->name, attr->value );
584 list = g_slist_next( list );
590 static void ldif_dump_b64( gchar *buf ) {
591 Base64Decoder *decoder = NULL;
595 printf( "base-64 : inbuf : %s\n", buf );
596 decoder = base64_decoder_new();
597 len = base64_decoder_decode( decoder, buf, outBuf );
599 printf( "base-64 : Bad BASE64 content\n" );
603 printf( "base-64 : %d : %s\n\n", len, outBuf );
605 base64_decoder_free( decoder );
610 * Read file data into address cache.
611 * Note that one LDIF record identifies one entity uniquely with the
612 * distinguished name (dn) tag. Each person can have multiple E-Mail
613 * addresses. Also, each person can have many common name (cn) tags.
615 static void ldif_read_file( LdifFile *ldifFile, AddressCache *cache ) {
616 gchar *tagName = NULL, *tagValue = NULL;
617 gchar *lastTag = NULL, *fullValue = NULL;
618 GSList *listValue = NULL;
619 gboolean flagEOF = FALSE, flagEOR = FALSE;
620 gboolean flag64 = FALSE, last64 = FALSE;
624 GHashTable *hashField;
626 hashField = ldifFile->hashFields;
627 rec = g_new0( Ldif_ParsedRec, 1 );
628 ldif_clear_rec( rec );
630 /* Find EOF for progress indicator */
631 fseek( ldifFile->file, 0L, SEEK_END );
632 posEnd = ftell( ldifFile->file );
633 fseek( ldifFile->file, 0L, SEEK_SET );
636 gchar *line = ldif_get_line( ldifFile );
638 posCur = ftell( ldifFile->file );
639 if( ldifFile->cbProgress ) {
640 /* Call progress indicator */
641 ( ldifFile->cbProgress ) ( ldifFile, & posEnd, & posCur );
646 flagEOF = flagEOR = TRUE;
648 else if( *line == '\0' ) {
653 /* EOR, Output address data */
656 fullValue = mgu_list_coalesce( listValue );
658 /* Base-64 encoded data */
661 ldif_dump_b64( fullValue );
665 ldif_add_value( rec, lastTag, fullValue, hashField );
666 /* ldif_print_record( rec, stdout ); */
667 ldif_build_items( ldifFile, rec, cache );
668 ldif_clear_rec( rec );
670 mgu_free_list( listValue );
679 /* Continuation line */
680 listValue = g_slist_append( listValue, g_strdup( line+1 ) );
682 else if( *line == '=' ) {
683 /* Base-64 encoded continuation field */
684 listValue = g_slist_append( listValue, g_strdup( line ) );
688 tagName = ldif_get_tagname( line, &flag64 );
690 tagValue = ldif_get_tagvalue( line );
694 fullValue = mgu_list_coalesce( listValue );
695 /* Base-64 encoded data */
698 ldif_dump_b64( fullValue );
702 ldif_add_value( rec, lastTag, fullValue, hashField );
704 mgu_free_list( listValue );
710 lastTag = g_strdup( tagName );
711 listValue = g_slist_append( listValue, g_strdup( tagValue ) );
723 ldif_clear_rec( rec );
726 mgu_free_list( listValue );
730 * Add list of field names to hash table.
732 static void ldif_hash_add_list( GHashTable *table, GSList *list ) {
735 /* mgu_print_list( list, stdout ); */
737 gchar *tag = node->data;
738 if( ! g_hash_table_lookup( table, tag ) ) {
739 Ldif_FieldRec *rec = NULL;
740 gchar *key = g_strdup( tag );
742 rec = ldif_create_fieldrec( tag );
743 if( g_strcasecmp( tag, LDIF_TAG_COMMONNAME ) == 0 ) {
744 rec->reserved = TRUE;
746 else if( g_strcasecmp( tag, LDIF_TAG_FIRSTNAME ) == 0 ) {
747 rec->reserved = TRUE;
749 else if( g_strcasecmp( tag, LDIF_TAG_LASTNAME ) == 0 ) {
750 rec->reserved = TRUE;
752 else if( g_strcasecmp( tag, LDIF_TAG_NICKNAME ) == 0 ) {
753 rec->reserved = TRUE;
755 else if( g_strcasecmp( tag, LDIF_TAG_EMAIL ) == 0 ) {
756 rec->reserved = TRUE;
758 g_hash_table_insert( table, key, rec );
760 node = g_slist_next( node );
765 * Sorted list comparison function.
767 static int ldif_field_compare( gconstpointer ptr1, gconstpointer ptr2 ) {
768 const Ldif_FieldRec *rec1 = ptr1;
769 const Ldif_FieldRec *rec2 = ptr2;
770 return g_strcasecmp( rec1->tagName, rec2->tagName );
774 * Append hash table entry to list - visitor function.
776 static void ldif_hash2list_vis( gpointer key, gpointer value, gpointer data ) {
777 LdifFile *ldf = data;
778 ldf->tempList = g_list_insert_sorted( ldf->tempList, value, ldif_field_compare );
782 * Read tag names for file data.
784 static void ldif_read_tag_list( LdifFile *ldifFile ) {
785 gchar *tagName = NULL;
786 GSList *listTags = NULL;
787 gboolean flagEOF = FALSE, flagEOR = FALSE, flagMail = FALSE;
788 gboolean flag64 = FALSE;
792 /* Clear hash table */
793 g_hash_table_foreach_remove( ldifFile->hashFields, ldif_hash_free_vis, NULL );
795 /* Find EOF for progress indicator */
796 fseek( ldifFile->file, 0L, SEEK_END );
797 posEnd = ftell( ldifFile->file );
798 fseek( ldifFile->file, 0L, SEEK_SET );
802 gchar *line = ldif_get_line( ldifFile );
804 posCur = ftell( ldifFile->file );
805 if( ldifFile->cbProgress ) {
806 /* Call progress indicator */
807 ( ldifFile->cbProgress ) ( ldifFile, & posEnd, & posCur );
812 flagEOF = flagEOR = TRUE;
814 else if( *line == '\0' ) {
819 /* EOR, Output address data */
820 /* Save field list to hash table */
822 ldif_hash_add_list( ldifFile->hashFields, listTags );
824 mgu_free_list( listTags );
831 /* Continuation line */
833 else if( *line == '=' ) {
834 /* Base-64 encoded continuation field */
838 tagName = ldif_get_tagname( line, &flag64 );
840 /* Add tag to list */
841 listTags = g_slist_append( listTags, tagName );
842 if( g_strcasecmp( tagName, LDIF_TAG_EMAIL ) == 0 ) {
852 mgu_free_list( listTags );
857 * ============================================================================
858 * Read file into list. Main entry point
859 * Enter: ldifFile LDIF control data.
860 * cache Address cache to load.
861 * Return: Status code.
862 * ============================================================================
864 gint ldif_import_data( LdifFile *ldifFile, AddressCache *cache ) {
865 g_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS );
866 ldifFile->retVal = MGU_SUCCESS;
867 addrcache_clear( cache );
868 cache->dataRead = FALSE;
869 ldif_open_file( ldifFile );
870 if( ldifFile->retVal == MGU_SUCCESS ) {
871 /* Read data into the cache */
872 ldif_read_file( ldifFile, cache );
873 ldif_close_file( ldifFile );
876 cache->modified = FALSE;
877 cache->dataRead = TRUE;
879 return ldifFile->retVal;
883 * ============================================================================
884 * Process entire file reading list of unique fields. List of fields may be
885 * accessed with the ldif_get_fieldlist() function.
886 * Enter: ldifFile LDIF control data.
887 * Return: Status code.
888 * ============================================================================
890 gint ldif_read_tags( LdifFile *ldifFile ) {
891 g_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS );
892 ldifFile->retVal = MGU_SUCCESS;
893 if( ldifFile->dirtyFlag ) {
894 ldif_open_file( ldifFile );
895 if( ldifFile->retVal == MGU_SUCCESS ) {
896 /* Read data into the cache */
897 ldif_read_tag_list( ldifFile );
898 ldif_close_file( ldifFile );
899 ldifFile->dirtyFlag = FALSE;
900 ldifFile->accessFlag = TRUE;
903 return ldifFile->retVal;
907 * Return list of fields for LDIF file.
908 * Enter: ldifFile LdifFile object.
909 * Return: Linked list of Ldif_FieldRec objects. This list may be g_free'd.
910 * Note that the objects in the list should not be freed since they refer to
911 * objects inside the internal cache. These objects will be freed when
912 * LDIF file object is freed.
914 GList *ldif_get_fieldlist( LdifFile *ldifFile ) {
917 g_return_val_if_fail( ldifFile != NULL, NULL );
918 if( ldifFile->hashFields ) {
919 ldifFile->tempList = NULL;
920 g_hash_table_foreach( ldifFile->hashFields, ldif_hash2list_vis, ldifFile );
921 list = ldifFile->tempList;
922 ldifFile->tempList = NULL;