Statistics
| Revision:

root / src / ldif.c @ 2366

History | View | Annotate | Download (23.9 kB)

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