sync with sylpheed 0.6.0cvs4
[claws.git] / src / vcard.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 VCard files. VCard files are used
22  * by GnomeCard for addressbook, and Netscape for sending business
23  * card information. Refer to RFC2426 for more information.
24  */
25
26 #include <sys/stat.h>
27 #include <glib.h>
28
29 #include "mgutils.h"
30 #include "vcard.h"
31 #include "addritem.h"
32 #include "addrcache.h"
33
34 #define GNOMECARD_DIR     ".gnome"
35 #define GNOMECARD_FILE    "GnomeCard"
36 #define GNOMECARD_SECTION "[file]"
37 #define GNOMECARD_PARAM   "open"
38
39 #define VCARD_TEST_LINES  200
40
41 /*
42 * Create new cardfile object.
43 */
44 VCardFile *vcard_create() {
45         VCardFile *cardFile;
46         cardFile = g_new0( VCardFile, 1 );
47         cardFile->name = NULL;
48         cardFile->path = NULL;
49         cardFile->file = NULL;
50         cardFile->bufptr = cardFile->buffer;
51         cardFile->addressCache = addrcache_create();
52         cardFile->retVal = MGU_SUCCESS;
53         cardFile->accessFlag = FALSE;
54         return cardFile;
55 }
56
57 /*
58 * Properties...
59 */
60 void vcard_set_name( VCardFile* cardFile, const gchar *value ) {
61         g_return_if_fail( cardFile != NULL );
62         cardFile->name = mgu_replace_string( cardFile->name, value );
63         g_strstrip( cardFile->name );
64 }
65 void vcard_set_file( VCardFile* cardFile, const gchar *value ) {
66         g_return_if_fail( cardFile != NULL );
67         addrcache_refresh( cardFile->addressCache );
68         cardFile->path = mgu_replace_string( cardFile->path, value );
69         g_strstrip( cardFile->path );
70 }
71 void vcard_set_accessed( VCardFile *cardFile, const gboolean value ) {
72         g_return_if_fail( cardFile != NULL );
73         cardFile->accessFlag = value;
74 }
75
76 /*
77 * Test whether file was modified since last access.
78 * Return: TRUE if file was modified.
79 */
80 gboolean vcard_get_modified( VCardFile *vcardFile ) {
81         g_return_if_fail( vcardFile != NULL );
82         return addrcache_check_file( vcardFile->addressCache, vcardFile->path );
83 }
84 gboolean vcard_get_accessed( VCardFile *vcardFile ) {
85         g_return_if_fail( vcardFile != NULL );
86         return addrcache_check_file( vcardFile->addressCache, vcardFile->path );
87 }
88
89 /*
90 * Test whether file was read.
91 * Return: TRUE if file was read.
92 */
93 gboolean vcard_get_read_flag( VCardFile *vcardFile ) {
94         g_return_if_fail( vcardFile != NULL );
95         return vcardFile->addressCache->dataRead;
96 }
97
98 /*
99 * Return status code from last file operation.
100 * Return: Status code.
101 */
102 gint vcard_get_status( VCardFile *cardFile ) {
103         g_return_if_fail( cardFile != NULL );
104         return cardFile->retVal;
105 }
106
107 ItemFolder *vcard_get_root_folder( VCardFile *cardFile ) {
108         g_return_if_fail( cardFile != NULL );
109         return addrcache_get_root_folder( cardFile->addressCache );
110 }
111 gchar *vcard_get_name( VCardFile *cardFile ) {
112         g_return_if_fail( cardFile != NULL );
113         return cardFile->name;
114 }
115
116 /*
117 * Refresh internal variables to force a file read.
118 */
119 void vcard_force_refresh( VCardFile *cardFile ) {
120         addrcache_refresh( cardFile->addressCache );
121 }
122
123 /*
124 * Create new cardfile object for specified file.
125 */
126 VCardFile *vcard_create_path( const gchar *path ) {
127         VCardFile *cardFile;
128         cardFile = vcard_create();
129         vcard_set_file(cardFile, path );
130         return cardFile;
131 }
132
133 /*
134 * Free up cardfile object by releasing internal memory.
135 */
136 void vcard_free( VCardFile *cardFile ) {
137         g_return_if_fail( cardFile != NULL );
138
139         /* Close file */
140         if( cardFile->file ) fclose( cardFile->file );
141
142         /* Free internal stuff */
143         g_free( cardFile->name );
144         g_free( cardFile->path );
145
146         /* Clear cache */
147         addrcache_clear( cardFile->addressCache );
148         addrcache_free( cardFile->addressCache );
149
150         // Clear pointers
151         cardFile->file = NULL;
152         cardFile->name = NULL;
153         cardFile->path = NULL;
154         cardFile->addressCache = NULL;
155         cardFile->retVal = MGU_SUCCESS;
156         cardFile->accessFlag = FALSE;
157
158         /* Now release file object */
159         g_free( cardFile );
160
161 }
162
163 /*
164 * Display object to specified stream.
165 */
166 void vcard_print_file( VCardFile *cardFile, FILE *stream ) {
167         GSList *node;
168         g_return_if_fail( cardFile != NULL );
169         fprintf( stream, "VCardFile:\n" );
170         fprintf( stream, "     name: '%s'\n", cardFile->name );
171         fprintf( stream, "file spec: '%s'\n", cardFile->path );
172         fprintf( stream, "  ret val: %d\n",   cardFile->retVal );
173         addrcache_print( cardFile->addressCache, stream );
174         addritem_print_item_folder( cardFile->addressCache->rootFolder, stream );
175 }
176
177 /*
178 * Open file for read.
179 * return: TRUE if file opened successfully.
180 */
181 static gint vcard_open_file( VCardFile* cardFile ) {
182         g_return_if_fail( cardFile != NULL );
183
184         // fprintf( stdout, "Opening file\n" );
185         cardFile->addressCache->dataRead = FALSE;
186         if( cardFile->path ) {
187                 cardFile->file = fopen( cardFile->path, "r" );
188                 if( ! cardFile->file ) {
189                         // fprintf( stderr, "can't open %s\n", cardFile->path );
190                         cardFile->retVal = MGU_OPEN_FILE;
191                         return cardFile->retVal;
192                 }
193         }
194         else {
195                 // fprintf( stderr, "file not specified\n" );
196                 cardFile->retVal = MGU_NO_FILE;
197                 return cardFile->retVal;
198         }
199
200         /* Setup a buffer area */
201         cardFile->buffer[0] = '\0';
202         cardFile->bufptr = cardFile->buffer;
203         cardFile->retVal = MGU_SUCCESS;
204         return cardFile->retVal;
205 }
206
207 /*
208 * Close file.
209 */
210 static void vcard_close_file( VCardFile *cardFile ) {
211         g_return_if_fail( cardFile != NULL );
212         if( cardFile->file ) fclose( cardFile->file );
213         cardFile->file = NULL;
214 }
215
216 /*
217 * Read line of text from file.
218 * Return: ptr to buffer where line starts.
219 */
220 static gchar *vcard_read_line( VCardFile *cardFile ) {
221         while( *cardFile->bufptr == '\n' || *cardFile->bufptr == '\0' ) {
222                 if( fgets( cardFile->buffer, VCARDBUFSIZE, cardFile->file ) == NULL )
223                         return NULL;
224                 g_strstrip( cardFile->buffer );
225                 cardFile->bufptr = cardFile->buffer;
226         }
227         return cardFile->bufptr;
228 }
229
230 /*
231 * Read line of text from file.
232 * Return: ptr to buffer where line starts.
233 */
234 static gchar *vcard_get_line( VCardFile *cardFile ) {
235         gchar buf[ VCARDBUFSIZE ];
236         gchar *start, *end;
237         gint len;
238
239         if (vcard_read_line( cardFile ) == NULL ) {
240                 buf[0] = '\0';
241                 return;
242         }
243
244         /* Copy into private buffer */
245         start = cardFile->bufptr;
246         len = strlen( start );
247         end = start + len;
248         strncpy( buf, start, len );
249         buf[ len ] = '\0';
250         g_strstrip(buf);
251         cardFile->bufptr = end + 1;
252
253         /* Return a copy of buffer */   
254         return g_strdup( buf );
255 }
256
257 /*
258 * Free linked lists of character strings.
259 */
260 static void vcard_free_lists( GSList *listName, GSList *listAddr, GSList *listRem, GSList* listID ) {
261         mgu_free_list( listName );
262         mgu_free_list( listAddr );
263         mgu_free_list( listRem );
264         mgu_free_list( listID );
265 }
266
267 /*
268 * Read quoted-printable text, which may span several lines into one long string.
269 * Param: cardFile - object.
270 * Param: tagvalue - will be placed into the linked list.
271 */
272 static gchar *vcard_read_qp( VCardFile *cardFile, char *tagvalue ) {
273         GSList *listQP = NULL;
274         gint len = 0;
275         gchar *line = tagvalue;
276         while( line ) {
277                 listQP = g_slist_append( listQP, line );
278                 len = strlen( line ) - 1;
279                 if( len > 0 ) {
280                         if( line[ len ] != '=' ) break;
281                         line[ len ] = '\0';
282                 }
283                 line = vcard_get_line( cardFile );
284         }
285
286         // Coalesce linked list into one long buffer.
287         line = mgu_list_coalesce( listQP );
288
289         // Clean up
290         mgu_free_list( listQP );
291         listQP = NULL;
292         return line;
293 }
294
295 /*
296 * Parse tag name from line buffer.
297 * Return: Buffer containing the tag name, or NULL if no delimiter char found.
298 */
299 static gchar *vcard_get_tagname( char* line, gchar dlm ) {
300         gint len = 0;
301         gchar *tag = NULL;
302         gchar *lptr = line;
303         while( *lptr++ ) {
304                 if( *lptr == dlm ) {
305                         len = lptr - line;
306                         tag = g_strndup( line, len+1 );
307                         tag[ len ] = '\0';
308                         g_strdown( tag );
309                         return tag;
310                 }
311         }
312         return tag;
313 }
314
315 /*
316 * Parse tag value from line buffer.
317 * Return: Buffer containing the tag value. Empty string is returned if
318 * no delimiter char found.
319 */
320 static gchar *vcard_get_tagvalue( gchar* line, gchar dlm ) {
321         gchar *value = NULL;
322         gchar *start = NULL;
323         gchar *lptr;
324         gint len = 0;
325
326         for( lptr = line; *lptr; lptr++ ) {
327                 if( *lptr == dlm ) {
328                         if( ! start )
329                                 start = lptr + 1;
330                 }
331         }
332         if( start ) {
333                 len = lptr - start;
334                 value = g_strndup( start, len+1 );
335         }
336         else {
337                 // Ensure that we get an empty string
338                 value = g_strndup( "", 1 );
339         }
340         value[ len ] = '\0';
341         return value;
342 }
343
344 /*
345 * Dump linked lists of character strings (for debug).
346 */
347 static void vcard_dump_lists( GSList *listName, GSList *listAddr, GSList *listRem, GSList *listID, FILE *stream ) {
348         fprintf( stream, "dump name\n" );
349         fprintf( stream, "------------\n" );
350         mgu_print_list( listName, stdout );
351         fprintf( stream, "dump address\n" );
352         fprintf( stream, "------------\n" );
353         mgu_print_list( listAddr, stdout );
354         fprintf( stream, "dump remarks\n" );
355         fprintf( stdout, "------------\n" );
356         mgu_print_list( listRem, stdout );
357         fprintf( stream, "dump id\n" );
358         fprintf( stdout, "------------\n" );
359         mgu_print_list( listID, stdout );
360 }
361
362 /*
363 * Build an address list entry and append to list of address items.
364 */
365 static void vcard_build_items( VCardFile *cardFile, GSList *listName, GSList *listAddr, GSList *listRem,
366                                 GSList *listID )
367 {
368         GSList *nodeName = listName;
369         GSList *nodeID = listID;
370         gchar *str;
371         while( nodeName ) {
372                 GSList *nodeAddress = listAddr;
373                 GSList *nodeRemarks = listRem;
374                 ItemPerson *person = addritem_create_item_person();
375                 addritem_person_set_common_name( person, nodeName->data );
376                 while( nodeAddress ) {
377                         str = nodeAddress->data;
378                         if( *str != '\0' ) {
379                                 ItemEMail *email = addritem_create_item_email();
380                                 addritem_email_set_address( email, str );
381                                 str = nodeRemarks->data;
382                                 if( nodeRemarks ) {
383                                         if( str ) {
384                                                 if( g_strcasecmp( str, "internet" ) != 0 ) {
385                                                         if( *str != '\0' ) addritem_email_set_remarks( email, str );
386                                                 }
387                                         }
388                                 }
389                                 addrcache_id_email( cardFile->addressCache, email );
390                                 addrcache_person_add_email( cardFile->addressCache, person, email );
391                         }
392                         nodeAddress = g_slist_next( nodeAddress );
393                         nodeRemarks = g_slist_next( nodeRemarks );
394                 }
395                 if( person->listEMail ) {
396                         addrcache_id_person( cardFile->addressCache, person );
397                         addrcache_add_person( cardFile->addressCache, person );
398                 }
399                 else {
400                         addritem_free_item_person( person );
401                 }
402                 if( nodeID ) {
403                         str = nodeID->data;
404                         addritem_person_set_external_id( person, str );
405                 }
406                 nodeName = g_slist_next( nodeName );
407                 nodeID = g_slist_next( nodeID );
408         }
409 }
410
411 // Unescape characters in quoted-printable string.
412 static void vcard_unescape_qp( gchar *value ) {
413         gchar *ptr, *src, *dest;
414         int d, v;
415         char ch;
416         gboolean gotch;
417         ptr = value;
418         while( *ptr ) {
419                 gotch = FALSE;
420                 if( *ptr == '=' ) {
421                         v = 0;
422                         ch = *(ptr + 1);
423                         if( ch ) {
424                                 if( ch > '0' && ch < '8' ) v = ch - '0';
425                         }
426                         d = -1;
427                         ch = *(ptr + 2);
428                         if( ch ) {
429                                 if( ch > '\x60' ) ch -= '\x20';
430                                 if( ch > '0' && ch < ' ' ) d = ch - '0';
431                                 d = ch - '0';
432                                 if( d > 9 ) d -= 7;
433                                 if( d > -1 && d < 16 ) {
434                                         v = ( 16 * v ) + d;
435                                         gotch = TRUE;
436                                 }
437                         }
438                 }
439                 if( gotch ) {
440                         // Replace = with char and move down in buffer
441                         *ptr = v;
442                         src = ptr + 3;
443                         dest = ptr + 1;
444                         while( *src ) {
445                                 *dest++ = *src++;
446                         }
447                         *dest = '\0';
448                 }
449                 ptr++;
450         }
451 }
452
453 /*
454 * Read file data into root folder.
455 * Note that one VCard can have multiple E-Mail addresses (MAIL tags);
456 * these are broken out into separate address items. An address item
457 * is generated for the person identified by FN tag and each EMAIL tag.
458 * If a sub-type is included in the EMAIL entry, this will be used as
459 * the Remarks member. Also note that it is possible for one VCard
460 * entry to have multiple FN tags; this might not make sense. However,
461 * it will generate duplicate address entries for each person listed.
462 */
463 static void vcard_read_file( VCardFile *cardFile ) {
464         gchar *tagtemp = NULL, *tagname = NULL, *tagvalue = NULL, *tagtype = NULL, *tagrest = NULL;
465         GSList *listName = NULL, *listAddress = NULL, *listRemarks = NULL, *listID = NULL;
466         GSList *listQP = NULL;
467
468         for( ;; ) {
469                 gchar *line =  vcard_get_line( cardFile );
470                 if( line == NULL ) break;
471
472                 // fprintf( stdout, "%s\n", line );
473
474                 /* Parse line */
475                 tagtemp = vcard_get_tagname( line, VCARD_SEP_TAG );
476                 if( tagtemp ) {
477                         // fprintf( stdout, "\ttemp:  %s\n", tagtemp );
478                         tagvalue = vcard_get_tagvalue( line, VCARD_SEP_TAG );
479                         tagname = vcard_get_tagname( tagtemp, VCARD_SEP_TYPE );
480                         tagtype = vcard_get_tagvalue( tagtemp, VCARD_SEP_TYPE );
481                         if( tagname == NULL ) {
482                                 tagname = tagtemp;
483                                 tagtemp = NULL;
484                         }
485
486                         // fprintf( stdout, "\tname:  %s\n", tagname );
487                         // fprintf( stdout, "\ttype:  %s\n", tagtype );
488                         // fprintf( stdout, "\tvalue: %s\n", tagvalue );
489
490                         if( tagvalue ) {
491                                 if( g_strcasecmp( tagtype, VCARD_TYPE_QP ) == 0 ) {
492                                         // Quoted-Printable: could span multiple lines
493                                         tagvalue = vcard_read_qp( cardFile, tagvalue );
494                                         vcard_unescape_qp( tagvalue );
495                                         // fprintf( stdout, "QUOTED-PRINTABLE !!! final\n>%s<\n", tagvalue );
496                                 }
497
498                                 if( g_strcasecmp( tagname, VCARD_TAG_START ) == 0 &&
499                                         g_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) {
500                                         // fprintf( stdout, "start card\n" );
501                                         vcard_free_lists( listName, listAddress, listRemarks, listID );
502                                         listName = listAddress = listRemarks = listID = NULL;
503                                 }
504                                 if( g_strcasecmp( tagname, VCARD_TAG_FULLNAME ) == 0 ) {
505                                         // fprintf( stdout, "- full name: %s\n", tagvalue );
506                                         listName = g_slist_append( listName, g_strdup( tagvalue ) );
507                                 }
508                                 if( g_strcasecmp( tagname, VCARD_TAG_EMAIL ) == 0 ) {
509                                         // fprintf( stdout, "- address: %s\n", tagvalue );
510                                         listAddress = g_slist_append( listAddress, g_strdup( tagvalue ) );
511                                         listRemarks = g_slist_append( listRemarks, g_strdup( tagtype ) );
512                                 }
513                                 if( g_strcasecmp( tagname, VCARD_TAG_UID ) == 0 ) {
514                                         // fprintf( stdout, "- id: %s\n", tagvalue );
515                                         listID = g_slist_append( listID, g_strdup( tagvalue ) );
516                                 }
517                                 if( g_strcasecmp( tagname, VCARD_TAG_END ) == 0 &&
518                                         g_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) {
519                                         // VCard is complete
520                                         // fprintf( stdout, "end card\n--\n" );
521                                         // vcard_dump_lists( listName, listAddress, listRemarks, listID, stdout );
522                                         vcard_build_items( cardFile, listName, listAddress, listRemarks, listID );
523                                         vcard_free_lists( listName, listAddress, listRemarks, listID );
524                                         listName = listAddress = listRemarks = listID = NULL;
525                                 }
526                                 g_free( tagvalue );
527                         }
528                         g_free( tagname );
529                         g_free( tagtype );
530                 }
531         }
532
533         // Free lists
534         vcard_free_lists( listName, listAddress, listRemarks, listID );
535         listName = listAddress = listRemarks = listID = NULL;
536 }
537
538 // ============================================================================================
539 /*
540 * Read file into list. Main entry point
541 * Return: TRUE if file read successfully.
542 */
543 // ============================================================================================
544 gint vcard_read_data( VCardFile *cardFile ) {
545         g_return_if_fail( cardFile != NULL );
546         cardFile->retVal = MGU_SUCCESS;
547         cardFile->accessFlag = FALSE;
548         if( addrcache_check_file( cardFile->addressCache, cardFile->path ) ) {
549                 addrcache_clear( cardFile->addressCache );
550                 vcard_open_file( cardFile );
551                 if( cardFile->retVal == MGU_SUCCESS ) {
552                         // Read data into the list
553                         vcard_read_file( cardFile );
554                         vcard_close_file( cardFile );
555
556                         // Mark cache
557                         addrcache_mark_file( cardFile->addressCache, cardFile->path );
558                         cardFile->addressCache->modified = FALSE;
559                         cardFile->addressCache->dataRead = TRUE;
560                 }
561         }
562         return cardFile->retVal;
563 }
564
565 /*
566 * Return link list of persons.
567 */
568 GList *vcard_get_list_person( VCardFile *cardFile ) {
569         g_return_if_fail( cardFile != NULL );
570         return addrcache_get_list_person( cardFile->addressCache );
571 }
572
573 /*
574 * Return link list of folders. This is always NULL since there are
575 * no folders in GnomeCard.
576 * Return: NULL.
577 */
578 GList *vcard_get_list_folder( VCardFile *cardFile ) {
579         g_return_if_fail( cardFile != NULL );
580         return NULL;
581 }
582
583 /*
584 * Return link list of all persons. Note that the list contains references
585 * to items. Do *NOT* attempt to use the addrbook_free_xxx() functions...
586 * this will destroy the addressbook data!
587 * Return: List of items, or NULL if none.
588 */
589 GList *vcard_get_all_persons( VCardFile *cardFile ) {
590         g_return_if_fail( cardFile != NULL );
591         return addrcache_get_all_persons( cardFile->addressCache );
592 }
593
594 /*
595 * Validate that all parameters specified.
596 * Return: TRUE if data is good.
597 */
598 gboolean vcard_validate( const VCardFile *cardFile ) {
599         gboolean retVal;
600         g_return_if_fail( cardFile != NULL );
601
602         retVal = TRUE;
603         if( cardFile->path ) {
604                 if( strlen( cardFile->path ) < 1 ) retVal = FALSE;
605         }
606         else {
607                 retVal = FALSE;
608         }
609         if( cardFile->name ) {
610                 if( strlen( cardFile->name ) < 1 ) retVal = FALSE;
611         }
612         else {
613                 retVal = FALSE;
614         }
615         return retVal;
616 }
617
618 #define WORK_BUFLEN 1024
619
620 /*
621 * Attempt to find a valid GnomeCard file.
622 * Return: Filename, or home directory if not found. Filename should
623 *       be g_free() when done.
624 */
625 gchar *vcard_find_gnomecard( void ) {
626         gchar *homedir;
627         gchar buf[ WORK_BUFLEN ];
628         gchar str[ WORK_BUFLEN ];
629         gchar *fileSpec;
630         gint len, lenlbl, i;
631         FILE *fp;
632
633         homedir = g_get_home_dir();
634         if( ! homedir ) return NULL;
635
636         strcpy( str, homedir );
637         len = strlen( str );
638         if( len > 0 ) {
639                 if( str[ len-1 ] != G_DIR_SEPARATOR ) {
640                         str[ len ] = G_DIR_SEPARATOR;
641                         str[ ++len ] = '\0';
642                 }
643         }
644         strcat( str, GNOMECARD_DIR );
645         strcat( str, G_DIR_SEPARATOR_S );
646         strcat( str, GNOMECARD_FILE );
647
648         fileSpec = NULL;
649         if( ( fp = fopen( str, "r" ) ) != NULL ) {
650                 // Read configuration file
651                 lenlbl = strlen( GNOMECARD_SECTION );
652                 while( fgets( buf, sizeof( buf ), fp ) != NULL ) {
653                         if( 0 == g_strncasecmp( buf, GNOMECARD_SECTION, lenlbl ) ) {
654                                 break;
655                         }
656                 }
657
658                 while( fgets( buf, sizeof( buf ), fp ) != NULL ) {
659                         g_strchomp( buf );
660                         if( buf[0] == '[' ) break;
661                         for( i = 0; i < lenlbl; i++ ) {
662                                 if( buf[i] == '=' ) {
663                                         if( 0 == g_strncasecmp( buf, GNOMECARD_PARAM, i ) ) {
664                                                 fileSpec = g_strdup( buf + i + 1 );
665                                                 g_strstrip( fileSpec );
666                                         }
667                                 }
668                         }
669                 }
670                 fclose( fp );
671         }
672
673         if( fileSpec == NULL ) {
674                 // Use the home directory
675                 str[ len ] = '\0';
676                 fileSpec = g_strdup( str );
677         }
678
679         return fileSpec;
680 }
681
682 /*
683 * Attempt to read file, testing for valid VCard format.
684 * Return: TRUE if file appears to be valid format.
685 */
686 gint vcard_test_read_file( const gchar *fileSpec ) {
687         gboolean haveStart;
688         gchar *tagtemp = NULL, *tagname = NULL, *tagvalue = NULL, *tagtype = NULL, *tagrest = NULL, *line;
689         VCardFile *cardFile;
690         gint retVal, lines;
691
692         if( ! fileSpec ) return MGU_NO_FILE;
693
694         cardFile = vcard_create_path( fileSpec );
695         cardFile->retVal = MGU_SUCCESS;
696         vcard_open_file( cardFile );
697         if( cardFile->retVal == MGU_SUCCESS ) {
698                 cardFile->retVal = MGU_BAD_FORMAT;
699                 haveStart = FALSE;
700                 lines = VCARD_TEST_LINES;
701                 while( lines > 0 ) {
702                         lines--;
703                         if( ( line =  vcard_get_line( cardFile ) ) == NULL ) break;
704
705                         /* Parse line */
706                         tagtemp = vcard_get_tagname( line, VCARD_SEP_TAG );
707                         if( tagtemp ) {
708                                 tagvalue = vcard_get_tagvalue( line, VCARD_SEP_TAG );
709                                 tagname = vcard_get_tagname( tagtemp, VCARD_SEP_TYPE );
710                                 tagtype = vcard_get_tagvalue( tagtemp, VCARD_SEP_TYPE );
711                                 if( tagname == NULL ) {
712                                         tagname = tagtemp;
713                                         tagtemp = NULL;
714                                 }
715
716                                 if( tagvalue ) {
717                                         if( g_strcasecmp( tagtype, VCARD_TYPE_QP ) == 0 ) {
718                                                 // Quoted-Printable: could span multiple lines
719                                                 tagvalue = vcard_read_qp( cardFile, tagvalue );
720                                                 vcard_unescape_qp( tagvalue );
721                                         }
722                                         if( g_strcasecmp( tagname, VCARD_TAG_START ) == 0 &&
723                                                 g_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) {
724                                                 haveStart = TRUE;
725                                         }
726                                         if( g_strcasecmp( tagname, VCARD_TAG_END ) == 0 &&
727                                                 g_strcasecmp( tagvalue, VCARD_NAME ) == 0 ) {
728                                                 // VCard is complete
729                                                 if( haveStart ) cardFile->retVal = MGU_SUCCESS;
730                                         }
731                                         g_free( tagvalue );
732                                 }
733                                 g_free( tagname );
734                                 g_free( tagtype );
735                         }
736                 }
737                 vcard_close_file( cardFile );
738         }
739         retVal = cardFile->retVal;
740         vcard_free( cardFile );
741         cardFile = NULL;
742         return retVal;
743 }
744
745 /*
746 * End of Source.
747 */
748