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