my stab at fixing the non MT safety code in the LDAP lookup
[claws.git] / src / ldif.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001 Match Grun
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Functions necessary to access LDIF files (LDAP Data Interchange Format files).
22  */
23
24 #include <sys/stat.h>
25 #include <glib.h>
26
27 #include "mgutils.h"
28 #include "ldif.h"
29 #include "addritem.h"
30 #include "addrcache.h"
31
32 /*
33 * Create new cardfile object.
34 */
35 LdifFile *ldif_create() {
36         LdifFile *ldifFile;
37         ldifFile = g_new0( LdifFile, 1 );
38         ldifFile->path = NULL;
39         ldifFile->file = NULL;
40         ldifFile->bufptr = ldifFile->buffer;
41         ldifFile->retVal = MGU_SUCCESS;
42         return ldifFile;
43 }
44
45 /*
46 * Properties...
47 */
48 void ldif_set_file( LdifFile* ldifFile, const gchar *value ) {
49         g_return_if_fail( ldifFile != NULL );
50         ldifFile->path = mgu_replace_string( ldifFile->path, value );
51         g_strstrip( ldifFile->path );
52 }
53
54 /*
55 * Free up cardfile object by releasing internal memory.
56 */
57 void ldif_free( LdifFile *ldifFile ) {
58         g_return_if_fail( ldifFile != NULL );
59
60         /* Close file */
61         if( ldifFile->file ) fclose( ldifFile->file );
62
63         /* Free internal stuff */
64         g_free( ldifFile->path );
65
66         /* Clear pointers */
67         ldifFile->file = NULL;
68         ldifFile->path = NULL;
69         ldifFile->retVal = MGU_SUCCESS;
70
71         /* Now release file object */
72         g_free( ldifFile );
73
74 }
75
76 /*
77 * Display object to specified stream.
78 */
79 void ldif_print_file( LdifFile *ldifFile, FILE *stream ) {
80         g_return_if_fail( ldifFile != NULL );
81         fprintf( stream, "LDIF File:\n" );
82         fprintf( stream, "file spec: '%s'\n", ldifFile->path );
83         fprintf( stream, "  ret val: %d\n",   ldifFile->retVal );
84 }
85
86 /*
87 * Open file for read.
88 * return: TRUE if file opened successfully.
89 */
90 static gint ldif_open_file( LdifFile* ldifFile ) {
91         /* printf( "Opening file\n" ); */
92         if( ldifFile->path ) {
93                 ldifFile->file = fopen( ldifFile->path, "r" );
94                 if( ! ldifFile->file ) {
95                         /* printf( "can't open %s\n", ldifFile->path ); */
96                         ldifFile->retVal = MGU_OPEN_FILE;
97                         return ldifFile->retVal;
98                 }
99         }
100         else {
101                 /* printf( "file not specified\n" ); */
102                 ldifFile->retVal = MGU_NO_FILE;
103                 return ldifFile->retVal;
104         }
105
106         /* Setup a buffer area */
107         ldifFile->buffer[0] = '\0';
108         ldifFile->bufptr = ldifFile->buffer;
109         ldifFile->retVal = MGU_SUCCESS;
110         return ldifFile->retVal;
111 }
112
113 /*
114 * Close file.
115 */
116 static void ldif_close_file( LdifFile *ldifFile ) {
117         g_return_if_fail( ldifFile != NULL );
118         if( ldifFile->file ) fclose( ldifFile->file );
119         ldifFile->file = NULL;
120 }
121
122 /*
123 * Read line of text from file.
124 * Return: ptr to buffer where line starts.
125 */
126 static gchar *ldif_get_line( LdifFile *ldifFile ) {
127         gchar buf[ LDIFBUFSIZE ];
128         gchar ch;
129         gchar *ptr;
130
131         if( feof( ldifFile->file ) ) return NULL;
132
133         ptr = buf;
134         while( TRUE ) {
135                 *ptr = '\0';
136                 ch = fgetc( ldifFile->file );
137                 if( ch == '\0' || ch == EOF ) {
138                         if( *buf == '\0' ) return NULL;
139                         break;
140                 }
141                 if( ch == '\n' ) break;
142                 *ptr = ch;
143                 ptr++;
144         }
145
146         /* Copy into private buffer */
147         return g_strdup( buf );
148 }
149
150 /*
151 * Parse tag name from line buffer.
152 * Return: Buffer containing the tag name, or NULL if no delimiter char found.
153 */
154 static gchar *ldif_get_tagname( char* line, gchar dlm ) {
155         gint len = 0;
156         gchar *tag = NULL;
157         gchar *lptr = line;
158         while( *lptr++ ) {
159                 if( *lptr == dlm ) {
160                         len = lptr - line;
161                         tag = g_strndup( line, len+1 );
162                         tag[ len ] = '\0';
163                         g_strdown( tag );
164                         return tag;
165                 }
166         }
167         return tag;
168 }
169
170 /*
171 * Parse tag value from line buffer.
172 * Return: Buffer containing the tag value. Empty string is returned if
173 * no delimiter char found.
174 */
175 static gchar *ldif_get_tagvalue( gchar* line, gchar dlm ) {
176         gchar *value = NULL;
177         gchar *start = NULL;
178         gchar *lptr;
179         gint len = 0;
180
181         for( lptr = line; *lptr; lptr++ ) {
182                 if( *lptr == dlm ) {
183                         if( ! start )
184                                 start = lptr + 1;
185                 }
186         }
187         if( start ) {
188                 len = lptr - start;
189                 value = g_strndup( start, len+1 );
190         }
191         else {
192                 /* Ensure that we get an empty string */
193                 value = g_strndup( "", 1 );
194         }
195         value[ len ] = '\0';
196         return value;
197 }
198
199 /*
200 * Dump linked lists of character strings (for debug).
201 */
202 static void ldif_dump_lists( GSList *listName, GSList *listAddr, GSList *listRem, GSList *listID, FILE *stream ) {
203         fprintf( stream, "dump name\n" );
204         fprintf( stream, "------------\n" );
205         mgu_print_list( listName, stdout );
206         fprintf( stream, "dump address\n" );
207         fprintf( stream, "------------\n" );
208         mgu_print_list( listAddr, stdout );
209         fprintf( stream, "dump remarks\n" );
210         fprintf( stdout, "------------\n" );
211         mgu_print_list( listRem, stdout );
212         fprintf( stream, "dump id\n" );
213         fprintf( stdout, "------------\n" );
214         mgu_print_list( listID, stdout );
215 }
216
217 typedef struct _Ldif_ParsedRec_ Ldif_ParsedRec;
218 struct _Ldif_ParsedRec_ {
219         GSList *listCName;
220         GSList *listFName;
221         GSList *listLName;
222         GSList *listNName;
223         GSList *listAddress;
224         GSList *listID;
225 };
226
227 /*
228 * Build an address list entry and append to list of address items. Name is formatted
229 * as "<first-name> <last-name>".
230 */
231 static void ldif_build_items( LdifFile *ldifFile, Ldif_ParsedRec *rec, AddressCache *cache ) {
232         GSList *nodeFirst;
233         GSList *nodeAddress;
234         gchar *firstName = NULL, *lastName = NULL, *fullName = NULL, *nickName = NULL;
235         gint iLen = 0, iLenT = 0;
236         ItemPerson *person;
237         ItemEMail *email;
238
239         nodeAddress = rec->listAddress;
240         if( nodeAddress == NULL ) return;
241
242         /* Find longest first name in list */
243         nodeFirst = rec->listFName;
244         while( nodeFirst ) {
245                 if( firstName == NULL ) {
246                         firstName = nodeFirst->data;
247                         iLen = strlen( firstName );
248                 }
249                 else {
250                         if( ( iLenT = strlen( nodeFirst->data ) ) > iLen ) {
251                                 firstName = nodeFirst->data;
252                                 iLen = iLenT;
253                         }
254                 }
255                 nodeFirst = g_slist_next( nodeFirst );
256         }
257
258         /* Format name */
259         if( rec->listLName ) {
260                 lastName = rec->listLName->data;
261         }
262
263         if( firstName ) {
264                 if( lastName ) {
265                         fullName = g_strdup_printf( "%s %s", firstName, lastName );
266                 }
267                 else {
268                         fullName = g_strdup_printf( "%s", firstName );
269                 }
270         }
271         else {
272                 if( lastName ) {
273                         fullName = g_strdup_printf( "%s", lastName );
274                 }
275         }
276         if( fullName ) {
277                 g_strchug( fullName ); g_strchomp( fullName );
278         }
279
280         if( rec->listNName ) {
281                 nickName = rec->listNName->data;
282         }
283
284         person = addritem_create_item_person();
285         addritem_person_set_common_name( person, fullName );
286         addritem_person_set_first_name( person, firstName );
287         addritem_person_set_last_name( person, lastName );
288         addritem_person_set_nick_name( person, nickName );
289         addrcache_id_person( cache, person );
290         addrcache_add_person( cache, person );
291
292         /* Add address item */
293         while( nodeAddress ) {
294                 email = addritem_create_item_email();
295                 addritem_email_set_address( email, nodeAddress->data );
296                 addrcache_id_email( cache, email );
297                 addrcache_person_add_email( cache, person, email );
298                 nodeAddress = g_slist_next( nodeAddress );
299         }
300         g_free( fullName );
301         fullName = firstName = lastName = NULL;
302
303 }
304
305 static void ldif_add_value( Ldif_ParsedRec *rec, gchar *tagName, gchar *tagValue ) {
306         gchar *nm, *val;
307
308         nm = g_strdup( tagName );
309         g_strdown( nm );
310         if( tagValue ) {
311                 val = g_strdup( tagValue );
312         }
313         else {
314                 val = g_strdup( "" );
315         }
316         g_strstrip( val );
317         if( strcmp( nm, LDIF_TAG_COMMONNAME ) == 0 ) {
318                 rec->listCName = g_slist_append( rec->listCName, val );
319         }
320         else if( strcmp( nm, LDIF_TAG_FIRSTNAME ) == 0 ) {
321                 rec->listFName = g_slist_append( rec->listFName, val );
322         }
323         else if( strcmp( nm, LDIF_TAG_LASTNAME ) == 0 ) {
324                 rec->listLName = g_slist_append( rec->listLName, val );
325         }
326         else if( strcmp( nm, LDIF_TAG_NICKNAME ) == 0 ) {
327                 rec->listNName = g_slist_append( rec->listNName, val );
328         }
329         else if( strcmp( nm, LDIF_TAG_EMAIL ) == 0 ) {
330                 rec->listAddress = g_slist_append( rec->listAddress, val );
331         }
332         g_free( nm );
333 }
334
335 static void ldif_clear_rec( Ldif_ParsedRec *rec ) {
336         g_slist_free( rec->listCName );
337         g_slist_free( rec->listFName );
338         g_slist_free( rec->listLName );
339         g_slist_free( rec->listNName );
340         g_slist_free( rec->listAddress );
341         g_slist_free( rec->listID );
342         rec->listCName = NULL;
343         rec->listFName = NULL;
344         rec->listLName = NULL;
345         rec->listNName = NULL;
346         rec->listAddress = NULL;
347         rec->listID = NULL;
348 }
349
350 static void ldif_print_record( Ldif_ParsedRec *rec, FILE *stream ) {
351         fprintf( stdout, "LDIF Parsed Record:\n" );
352         fprintf( stdout, "common name:" );
353         mgu_print_list( rec->listCName, stdout );
354         if( ! rec->listCName ) fprintf( stdout, "\n" );
355         fprintf( stdout, "first name:" );
356         mgu_print_list( rec->listFName, stdout );
357         if( ! rec->listFName ) fprintf( stdout, "\n" );
358         fprintf( stdout, "last name:" );
359         mgu_print_list( rec->listLName, stdout );
360         if( ! rec->listLName ) fprintf( stdout, "\n" );
361         fprintf( stdout, "nick name:" );
362         mgu_print_list( rec->listNName, stdout );
363         if( ! rec->listNName ) fprintf( stdout, "\n" );
364         fprintf( stdout, "address:" );
365         mgu_print_list( rec->listAddress, stdout );
366         if( ! rec->listAddress ) fprintf( stdout, "\n" );
367         fprintf( stdout, "id:" );
368         mgu_print_list( rec->listID, stdout );
369         if( ! rec->listID ) fprintf( stdout, "\n" );
370 }
371
372 /*
373 * Read file data into address cache.
374 * Note that one LDIF record identifies one entity uniquely with the
375 * distinguished name (dn) tag. Each person can have multiple E-Mail
376 * addresses. Also, each person can have many common name (cn) tags.
377 */
378 static void ldif_read_file( LdifFile *ldifFile, AddressCache *cache ) {
379         gchar *tagName = NULL, *tagValue = NULL;
380         gchar *lastTag = NULL, *fullValue = NULL;
381         GSList *listValue = NULL;
382         gboolean flagEOF = FALSE, flagEOR = FALSE;
383         Ldif_ParsedRec *rec;
384
385         rec = g_new0( Ldif_ParsedRec, 1 );
386         ldif_clear_rec( rec );
387
388         while( ! flagEOF ) {
389                 gchar *line =  ldif_get_line( ldifFile );
390                 if( line == NULL ) {
391                         flagEOF = flagEOR = TRUE;
392                 }
393                 else if( *line == '\0' ) {
394                         flagEOR = TRUE;
395                 }
396
397                 if( flagEOR ) {
398                         /* EOR, Output address data */
399                         if( lastTag ) {
400                                 /* Save record */
401                                 fullValue = mgu_list_coalesce( listValue );
402                                 ldif_add_value( rec, lastTag, fullValue );
403                                 /* ldif_print_record( rec, stdout ); */
404                                 ldif_build_items( ldifFile, rec, cache );
405                                 ldif_clear_rec( rec );
406                                 g_free( lastTag );
407                                 mgu_free_list( listValue );
408                                 lastTag = NULL;
409                                 listValue = NULL;
410                         }
411                 }
412                 if( line ) {
413                         flagEOR = FALSE;
414                         if( *line == ' ' ) {
415                                 /* Continuation line */
416                                 listValue = g_slist_append( listValue, g_strdup( line+1 ) );
417                         }
418                         else if( *line == '=' ) {
419                                 /* Base-64 binary encode field */
420                                 listValue = g_slist_append( listValue, g_strdup( line ) );
421                         }
422                         else {
423                                 /* Parse line */
424                                 tagName = ldif_get_tagname( line, LDIF_SEP_TAG );
425                                 if( tagName ) {
426                                         tagValue = ldif_get_tagvalue( line, LDIF_SEP_TAG );
427                                         if( tagValue ) {
428                                                 if( lastTag ) {
429                                                         /* Save data */
430                                                         fullValue = mgu_list_coalesce( listValue );
431                                                         ldif_add_value( rec, lastTag, fullValue );
432                                                         g_free( lastTag );
433                                                         mgu_free_list( listValue );
434                                                         lastTag = NULL;
435                                                         listValue = NULL;
436                                                 }
437
438                                                 lastTag = g_strdup( tagName );
439                                                 listValue = g_slist_append( listValue, g_strdup( tagValue ) );
440                                                 g_free( tagValue );
441                                         }
442                                         g_free( tagName );
443                                 }
444                         }
445                 }
446
447         }
448
449         /* Release data */
450         ldif_clear_rec( rec );
451         g_free( rec );
452         g_free( lastTag );
453         mgu_free_list( listValue );
454
455 }
456
457 // ============================================================================================
458 /*
459 * Read file into list. Main entry point
460 * Enter:  ldifFile LDIF control data.
461 *         cache    Address cache to load.
462 * Return: Status code.
463 */
464 // ============================================================================================
465 gint ldif_import_data( LdifFile *ldifFile, AddressCache *cache ) {
466         g_return_val_if_fail( ldifFile != NULL, MGU_BAD_ARGS );
467         ldifFile->retVal = MGU_SUCCESS;
468         addrcache_clear( cache );
469         cache->dataRead = FALSE;
470         ldif_open_file( ldifFile );
471         if( ldifFile->retVal == MGU_SUCCESS ) {
472                 /* Read data into the cache */
473                 ldif_read_file( ldifFile, cache );
474                 ldif_close_file( ldifFile );
475
476                 /* Mark cache */
477                 cache->modified = FALSE;
478                 cache->dataRead = TRUE;
479         }
480         return ldifFile->retVal;
481 }
482
483 /*
484 * End of Source.
485 */
486