src/account.[ch]
[claws.git] / src / jpilot.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001-2002 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 JPilot database files.
22  * JPilot is Copyright(c) by Judd Montgomery.
23  * Visit http://www.jpilot.org for more details.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #ifdef USE_JPILOT
31
32 #include <glib.h>
33 #include <time.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <netinet/in.h>
39
40 #ifdef HAVE_LIBPISOCK_PI_ARGS_H
41 #  include <libpisock/pi-args.h>
42 #  include <libpisock/pi-appinfo.h>
43 #  include <libpisock/pi-address.h>
44 #else
45 #  include <pi-args.h>
46 #  include <pi-appinfo.h>
47 #  include <pi-address.h>
48 #endif
49
50 #include "mgutils.h"
51 #include "addritem.h"
52 #include "addrcache.h"
53 #include "jpilot.h"
54 #include "adbookbase.h"
55
56 #define JPILOT_DBHOME_DIR   ".jpilot"
57 #define JPILOT_DBHOME_FILE  "AddressDB.pdb"
58 #define PILOT_LINK_LIB_NAME "libpisock.so"
59
60 #define IND_LABEL_LASTNAME  0   /* Index of last name in address data */
61 #define IND_LABEL_FIRSTNAME 1   /* Index of first name in address data */
62 #define IND_PHONE_EMAIL     4   /* Index of E-Mail address in phone labels */
63 #define OFFSET_PHONE_LABEL  3   /* Offset to phone data in address data */
64 #define IND_CUSTOM_LABEL    14  /* Offset to custom label names */
65 #define NUM_CUSTOM_LABEL    4   /* Number of custom labels */
66
67 /* Shamelessly copied from JPilot (libplugin.h) */
68 typedef struct {
69         unsigned char db_name[32];
70         unsigned char flags[2];
71         unsigned char version[2];
72         unsigned char creation_time[4];
73         unsigned char modification_time[4];
74         unsigned char backup_time[4];
75         unsigned char modification_number[4];
76         unsigned char app_info_offset[4];
77         unsigned char sort_info_offset[4];
78         unsigned char type[4];/*Database ID */
79         unsigned char creator_id[4];/*Application ID */
80         unsigned char unique_id_seed[4];
81         unsigned char next_record_list_id[4];
82         unsigned char number_of_records[2];
83 } RawDBHeader;
84
85 /* Shamelessly copied from JPilot (libplugin.h) */
86 typedef struct {
87         char db_name[32];
88         unsigned int flags;
89         unsigned int version;
90         time_t creation_time;
91         time_t modification_time;
92         time_t backup_time;
93         unsigned int modification_number;
94         unsigned int app_info_offset;
95         unsigned int sort_info_offset;
96         char type[5];/*Database ID */
97         char creator_id[5];/*Application ID */
98         char unique_id_seed[5];
99         unsigned int next_record_list_id;
100         unsigned int number_of_records;
101 } DBHeader;
102
103 /* Shamelessly copied from JPilot (libplugin.h) */
104 typedef struct {
105         unsigned char Offset[4];  /*4 bytes offset from BOF to record */
106         unsigned char attrib;
107         unsigned char unique_ID[3];
108 } record_header;
109
110 /* Shamelessly copied from JPilot (libplugin.h) */
111 typedef struct mem_rec_header_s {
112         unsigned int rec_num;
113         unsigned int offset;
114         unsigned int unique_id;
115         unsigned char attrib;
116         struct mem_rec_header_s *next;
117 } mem_rec_header;
118
119 /* Shamelessly copied from JPilot (libplugin.h) */
120 #define SPENT_PC_RECORD_BIT     256
121
122 typedef enum {
123         PALM_REC = 100L,
124         MODIFIED_PALM_REC = 101L,
125         DELETED_PALM_REC = 102L,
126         NEW_PC_REC = 103L,
127         DELETED_PC_REC = SPENT_PC_RECORD_BIT + 104L,
128         DELETED_DELETED_PALM_REC = SPENT_PC_RECORD_BIT + 105L
129 } PCRecType;
130
131 /* Shamelessly copied from JPilot (libplugin.h) */
132 typedef struct {
133         PCRecType rt;
134         unsigned int unique_id;
135         unsigned char attrib;
136         void *buf;
137         int size;
138 } buf_rec;
139
140 /* Shamelessly copied from JPilot (libplugin.h) */
141 typedef struct {
142         unsigned long header_len;
143         unsigned long header_version;
144         unsigned long rec_len;
145         unsigned long unique_id;
146         unsigned long rt; /* Record Type */
147         unsigned char attrib;
148 } PC3RecordHeader;
149
150 /*
151 * Create new pilot file object.
152 */
153 JPilotFile *jpilot_create() {
154         JPilotFile *pilotFile;
155         pilotFile = g_new0( JPilotFile, 1 );
156         pilotFile->type = ADBOOKTYPE_JPILOT;
157         pilotFile->addressCache = addrcache_create();
158         pilotFile->retVal = MGU_SUCCESS;
159
160         pilotFile->file = NULL;
161         pilotFile->path = NULL;
162         pilotFile->readMetadata = FALSE;
163         pilotFile->customLabels = NULL;
164         pilotFile->labelInd = NULL;
165         pilotFile->havePC3 = FALSE;
166         pilotFile->pc3ModifyTime = 0;
167         return pilotFile;
168 }
169
170 /*
171 * Create new pilot file object for specified file.
172 */
173 JPilotFile *jpilot_create_path( const gchar *path ) {
174         JPilotFile *pilotFile;
175         pilotFile = jpilot_create();
176         jpilot_set_file( pilotFile, path );
177         return pilotFile;
178 }
179
180 /*
181 * Properties...
182 */
183 void jpilot_set_name( JPilotFile* pilotFile, const gchar *value ) {
184         g_return_if_fail( pilotFile != NULL );
185         addrcache_set_name( pilotFile->addressCache, value );
186 }
187 void jpilot_set_file( JPilotFile* pilotFile, const gchar *value ) {
188         g_return_if_fail( pilotFile != NULL );
189         addrcache_refresh( pilotFile->addressCache );
190         pilotFile->readMetadata = FALSE;
191         pilotFile->path = mgu_replace_string( pilotFile->path, value );
192 }
193 void jpilot_set_accessed( JPilotFile *pilotFile, const gboolean value ) {
194         g_return_if_fail( pilotFile != NULL );
195         pilotFile->addressCache->accessFlag = value;
196 }
197
198 gint jpilot_get_status( JPilotFile *pilotFile ) {
199         g_return_val_if_fail( pilotFile != NULL, -1 );
200         return pilotFile->retVal;
201 }
202 ItemFolder *jpilot_get_root_folder( JPilotFile *pilotFile ) {
203         g_return_val_if_fail( pilotFile != NULL, NULL );
204         return addrcache_get_root_folder( pilotFile->addressCache );
205 }
206 gchar *jpilot_get_name( JPilotFile *pilotFile ) {
207         g_return_val_if_fail( pilotFile != NULL, NULL );
208         return addrcache_get_name( pilotFile->addressCache );
209 }
210
211 /*
212 * Test whether file was read.
213 * Return: TRUE if file was read.
214 */
215 gboolean jpilot_get_read_flag( JPilotFile *pilotFile ) {
216         g_return_val_if_fail( pilotFile != NULL, FALSE );
217         return pilotFile->addressCache->dataRead;
218 }
219
220 /*
221 * Free up custom label list.
222 */
223 void jpilot_clear_custom_labels( JPilotFile *pilotFile ) {
224         GList *node;
225
226         g_return_if_fail( pilotFile != NULL );
227
228         /* Release custom labels */
229         mgu_free_dlist( pilotFile->customLabels );
230         pilotFile->customLabels = NULL;
231
232         /* Release indexes */
233         node = pilotFile->labelInd;
234         while( node ) {
235                 node->data = NULL;
236                 node = g_list_next( node );
237         }
238         g_list_free( pilotFile->labelInd );
239         pilotFile->labelInd = NULL;
240
241         /* Force a fresh read */
242         addrcache_refresh( pilotFile->addressCache );
243 }
244
245 /*
246 * Append a custom label, representing an E-Mail address field to the
247 * custom label list.
248 */
249 void jpilot_add_custom_label( JPilotFile *pilotFile, const gchar *labelName ) {
250         g_return_if_fail( pilotFile != NULL );
251
252         if( labelName ) {
253                 gchar *labelCopy = g_strdup( labelName );
254                 g_strstrip( labelCopy );
255                 if( *labelCopy == '\0' ) {
256                         g_free( labelCopy );
257                 }
258                 else {
259                         pilotFile->customLabels = g_list_append( pilotFile->customLabels, labelCopy );
260                         /* Force a fresh read */
261                         addrcache_refresh( pilotFile->addressCache );
262                 }
263         }
264 }
265
266 /*
267 * Get list of custom labels.
268 * Return: List of labels. Must use g_free() when done.
269 */
270 GList *jpilot_get_custom_labels( JPilotFile *pilotFile ) {
271         GList *retVal = NULL;
272         GList *node;
273
274         g_return_val_if_fail( pilotFile != NULL, NULL );
275
276         node = pilotFile->customLabels;
277         while( node ) {
278                 retVal = g_list_append( retVal, g_strdup( node->data ) );
279                 node = g_list_next( node );
280         }
281         return retVal;
282 }
283
284 /*
285 * Return filespec of PC3 file corresponding to JPilot PDB file.
286 * Note: Filespec should be g_free() when done.
287 */
288 static gchar *jpilot_get_pc3_file( JPilotFile *pilotFile ) {
289         gchar *fileSpec, *r;
290         gint i, len, pos;
291
292         if( pilotFile == NULL ) return NULL;
293         if( pilotFile->path == NULL ) return NULL;
294
295         fileSpec = g_strdup( pilotFile->path );
296         len = strlen( fileSpec );
297         pos = -1;
298         r = NULL;
299         for( i = len; i > 0; i-- ) {
300                 if( *(fileSpec + i) == '.' ) {
301                         pos = i + 1;
302                         r = fileSpec + pos;
303                         break;
304                 }
305         }
306         if( r ) {
307                 if( len - pos == 3 ) {
308                         *r++ = 'p'; *r++ = 'c'; *r = '3';
309                         return fileSpec;
310                 }
311         }
312         g_free( fileSpec );
313         return NULL;
314 }
315
316 /*
317 * Save PC3 file time to cache.
318 * return: TRUE if time marked.
319 */
320 static gboolean jpilot_mark_files( JPilotFile *pilotFile ) {
321         gboolean retVal = FALSE;
322         struct stat filestat;
323         gchar *pcFile;
324
325         /* Mark PDB file cache */
326         retVal = addrcache_mark_file( pilotFile->addressCache, pilotFile->path );
327
328         /* Now mark PC3 file */
329         pilotFile->havePC3 = FALSE;
330         pilotFile->pc3ModifyTime = 0;
331         pcFile = jpilot_get_pc3_file( pilotFile );
332         if( pcFile == NULL ) return retVal;
333         if( 0 == lstat( pcFile, &filestat ) ) {
334                 pilotFile->havePC3 = TRUE;
335                 pilotFile->pc3ModifyTime = filestat.st_mtime;
336                 retVal = TRUE;
337         }
338         g_free( pcFile );
339         return retVal;
340 }
341
342 /*
343 * Check whether JPilot PDB or PC3 file has changed by comparing
344 * with cached data.
345 * return: TRUE if file has changed.
346 */
347 static gboolean jpilot_check_files( JPilotFile *pilotFile ) {
348         gboolean retVal = TRUE;
349         struct stat filestat;
350         gchar *pcFile;
351
352         /* Check main file */
353         if( addrcache_check_file( pilotFile->addressCache, pilotFile->path ) )
354                 return TRUE;
355
356         /* Test PC3 file */
357         if( ! pilotFile->havePC3 ) return FALSE;
358         pcFile = jpilot_get_pc3_file( pilotFile );
359         if( pcFile == NULL ) return FALSE;
360
361         if( 0 == lstat( pcFile, &filestat ) ) {
362                 if( filestat.st_mtime == pilotFile->pc3ModifyTime ) retVal = FALSE;
363         }
364         g_free( pcFile );
365         return retVal;
366 }
367
368 /*
369 * Test whether file was modified since last access.
370 * Return: TRUE if file was modified.
371 */
372 gboolean jpilot_get_modified( JPilotFile *pilotFile ) {
373         g_return_val_if_fail( pilotFile != NULL, FALSE );
374         pilotFile->addressCache->modified = jpilot_check_files( pilotFile );
375         return pilotFile->addressCache->modified;
376 }
377 void jpilot_set_modified( JPilotFile *pilotFile, const gboolean value ) {
378         g_return_if_fail( pilotFile != NULL );
379         pilotFile->addressCache->modified = value;
380 }
381 gboolean jpilot_get_accessed( JPilotFile *pilotFile ) {
382         g_return_val_if_fail( pilotFile != NULL, FALSE );
383         return pilotFile->addressCache->accessFlag;
384 }
385
386 /*
387 * Free up pilot file object by releasing internal memory.
388 */
389 void jpilot_free( JPilotFile *pilotFile ) {
390         g_return_if_fail( pilotFile != NULL );
391
392         /* Release custom labels */
393         jpilot_clear_custom_labels( pilotFile );
394
395         /* Clear cache */
396         addrcache_clear( pilotFile->addressCache );
397         addrcache_free( pilotFile->addressCache );
398
399         /* Free internal stuff */
400         g_free( pilotFile->path );
401
402         pilotFile->file = NULL;
403         pilotFile->path = NULL;
404         pilotFile->readMetadata = FALSE;
405         pilotFile->havePC3 = FALSE;
406         pilotFile->pc3ModifyTime = 0;
407
408         pilotFile->type = ADBOOKTYPE_NONE;
409         pilotFile->addressCache = NULL;
410         pilotFile->retVal = MGU_SUCCESS;
411
412         /* Now release file object */
413         g_free( pilotFile );
414 }
415
416 /*
417 * Refresh internal variables to force a file read.
418 */
419 void jpilot_force_refresh( JPilotFile *pilotFile ) {
420         addrcache_refresh( pilotFile->addressCache );
421 }
422
423 /*
424 * Print object to specified stream.
425 */
426 void jpilot_print_file( JPilotFile *pilotFile, FILE *stream ) {
427         GList *node;
428
429         g_return_if_fail( pilotFile != NULL );
430
431         fprintf( stream, "JPilotFile:\n" );
432         fprintf( stream, "file spec: '%s'\n", pilotFile->path );
433         fprintf( stream, " metadata: %s\n", pilotFile->readMetadata ? "yes" : "no" );
434         fprintf( stream, "  ret val: %d\n", pilotFile->retVal );
435
436         node = pilotFile->customLabels;
437         while( node ) {
438                 fprintf( stream, "  c label: %s\n", (gchar *)node->data );
439                 node = g_list_next( node );
440         }
441
442         node = pilotFile->labelInd;
443         while( node ) {
444                 fprintf( stream, " labelind: %d\n", GPOINTER_TO_INT(node->data) );
445                 node = g_list_next( node );
446         }
447
448         addrcache_print( pilotFile->addressCache, stream );
449         fprintf( stream, "  ret val: %d\n", pilotFile->retVal );
450         fprintf( stream, " have pc3: %s\n", pilotFile->havePC3 ? "yes" : "no" );
451         fprintf( stream, " pc3 time: %lu\n", pilotFile->pc3ModifyTime );
452         addritem_print_item_folder( pilotFile->addressCache->rootFolder, stream );
453 }
454
455 /*
456 * Print summary of object to specified stream.
457 */
458 void jpilot_print_short( JPilotFile *pilotFile, FILE *stream ) {
459         GList *node;
460         g_return_if_fail( pilotFile != NULL );
461         fprintf( stream, "JPilotFile:\n" );
462         fprintf( stream, "file spec: '%s'\n", pilotFile->path );
463         fprintf( stream, " metadata: %s\n", pilotFile->readMetadata ? "yes" : "no" );
464         fprintf( stream, "  ret val: %d\n", pilotFile->retVal );
465
466         node = pilotFile->customLabels;
467         while( node ) {
468                 fprintf( stream, "  c label: %s\n", (gchar *)node->data );
469                 node = g_list_next( node );
470         }
471
472         node = pilotFile->labelInd;
473         while( node ) {
474                 fprintf( stream, " labelind: %d\n", GPOINTER_TO_INT(node->data) );
475                 node = g_list_next( node );
476         }
477         addrcache_print( pilotFile->addressCache, stream );
478         fprintf( stream, " have pc3: %s\n", pilotFile->havePC3 ? "yes" : "no" );
479         fprintf( stream, " pc3 time: %lu\n", pilotFile->pc3ModifyTime );
480 }
481
482 /* Shamelessly copied from JPilot (libplugin.c) */
483 static unsigned int bytes_to_bin(unsigned char *bytes, unsigned int num_bytes) {
484 unsigned int i, n;
485         n=0;
486         for (i=0;i<num_bytes;i++) {
487                 n = n*256+bytes[i];
488         }
489         return n;
490 }
491
492 /* Shamelessly copied from JPilot (utils.c) */
493 /* These next 2 functions were copied from pi-file.c in the pilot-link app */
494 /* Exact value of "Jan 1, 1970 0:00:00 GMT" - "Jan 1, 1904 0:00:00 GMT" */
495 #define PILOT_TIME_DELTA (unsigned)(2082844800)
496
497 time_t pilot_time_to_unix_time ( unsigned long raw_time ) {
498    return (time_t)(raw_time - PILOT_TIME_DELTA);
499 }
500
501 /* Shamelessly copied from JPilot (libplugin.c) */
502 static int raw_header_to_header(RawDBHeader *rdbh, DBHeader *dbh) {
503         unsigned long temp;
504
505         strncpy(dbh->db_name, rdbh->db_name, 31);
506         dbh->db_name[31] = '\0';
507         dbh->flags = bytes_to_bin(rdbh->flags, 2);
508         dbh->version = bytes_to_bin(rdbh->version, 2);
509         temp = bytes_to_bin(rdbh->creation_time, 4);
510         dbh->creation_time = pilot_time_to_unix_time(temp);
511         temp = bytes_to_bin(rdbh->modification_time, 4);
512         dbh->modification_time = pilot_time_to_unix_time(temp);
513         temp = bytes_to_bin(rdbh->backup_time, 4);
514         dbh->backup_time = pilot_time_to_unix_time(temp);
515         dbh->modification_number = bytes_to_bin(rdbh->modification_number, 4);
516         dbh->app_info_offset = bytes_to_bin(rdbh->app_info_offset, 4);
517         dbh->sort_info_offset = bytes_to_bin(rdbh->sort_info_offset, 4);
518         strncpy(dbh->type, rdbh->type, 4);
519         dbh->type[4] = '\0';
520         strncpy(dbh->creator_id, rdbh->creator_id, 4);
521         dbh->creator_id[4] = '\0';
522         strncpy(dbh->unique_id_seed, rdbh->unique_id_seed, 4);
523         dbh->unique_id_seed[4] = '\0';
524         dbh->next_record_list_id = bytes_to_bin(rdbh->next_record_list_id, 4);
525         dbh->number_of_records = bytes_to_bin(rdbh->number_of_records, 2);
526         return 0;
527 }
528
529 /* Shamelessly copied from JPilot (libplugin.c) */
530 /* returns 1 if found */
531 /*         0 if eof */
532 static int find_next_offset( mem_rec_header *mem_rh, long fpos,
533         unsigned int *next_offset, unsigned char *attrib, unsigned int *unique_id )
534 {
535         mem_rec_header *temp_mem_rh;
536         unsigned char found = 0;
537         unsigned long found_at;
538
539         found_at=0xFFFFFF;
540         for (temp_mem_rh=mem_rh; temp_mem_rh; temp_mem_rh = temp_mem_rh->next) {
541                 if ((temp_mem_rh->offset > fpos) && (temp_mem_rh->offset < found_at)) {
542                         found_at = temp_mem_rh->offset;
543                         /* *attrib = temp_mem_rh->attrib; */
544                         /* *unique_id = temp_mem_rh->unique_id; */
545                 }
546                 if ((temp_mem_rh->offset == fpos)) {
547                         found = 1;
548                         *attrib = temp_mem_rh->attrib;
549                         *unique_id = temp_mem_rh->unique_id;
550                 }
551         }
552         *next_offset = found_at;
553         return found;
554 }
555
556 /* Shamelessly copied from JPilot (libplugin.c) */
557 static void free_mem_rec_header(mem_rec_header **mem_rh) {
558         mem_rec_header *h, *next_h;
559         for (h=*mem_rh; h; h=next_h) {
560                 next_h=h->next;
561                 free(h);
562         }
563         *mem_rh = NULL;
564 }
565
566 /* Shamelessly copied from JPilot (libplugin.c) */
567 static int jpilot_free_db_list( GList **br_list ) {
568         GList *temp_list, *first;
569         buf_rec *br;
570
571         /* Go to first entry in the list */
572         first=NULL;
573         for( temp_list = *br_list; temp_list; temp_list = temp_list->prev ) {
574                 first = temp_list;
575         }
576         for (temp_list = first; temp_list; temp_list = temp_list->next) {
577                 if (temp_list->data) {
578                         br=temp_list->data;
579                         if (br->buf) {
580                                 free(br->buf);
581                                 temp_list->data=NULL;
582                         }
583                         free(br);
584                 }
585         }
586         g_list_free(*br_list);
587         *br_list=NULL;
588         return 0;
589 }
590
591 /* Shamelessly copied from JPilot (libplugin.c) */
592 /* Read file size */
593 static int jpilot_get_info_size( FILE *in, int *size ) {
594         RawDBHeader rdbh;
595         DBHeader dbh;
596         unsigned int offset;
597         record_header rh;
598
599         fseek(in, 0, SEEK_SET);
600         fread(&rdbh, sizeof(RawDBHeader), 1, in);
601         if (feof(in)) {
602                 return MGU_EOF;
603         }
604
605         raw_header_to_header(&rdbh, &dbh);
606         if (dbh.app_info_offset==0) {
607                 *size=0;
608                 return MGU_SUCCESS;
609         }
610         if (dbh.sort_info_offset!=0) {
611                 *size = dbh.sort_info_offset - dbh.app_info_offset;
612                 return MGU_SUCCESS;
613         }
614         if (dbh.number_of_records==0) {
615                 fseek(in, 0, SEEK_END);
616                 *size=ftell(in) - dbh.app_info_offset;
617                 return MGU_SUCCESS;
618         }
619
620         fread(&rh, sizeof(record_header), 1, in);
621         offset = ((rh.Offset[0]*256+rh.Offset[1])*256+rh.Offset[2])*256+rh.Offset[3];
622         *size=offset - dbh.app_info_offset;
623
624         return MGU_SUCCESS;
625 }
626
627 /*
628  * Read address file into address list. Based on JPilot's
629  * libplugin.c (jp_get_app_info)
630  */
631 static gint jpilot_get_file_info( JPilotFile *pilotFile, unsigned char **buf, int *buf_size ) {
632         FILE *in;
633         int num;
634         unsigned int rec_size;
635         RawDBHeader rdbh;
636         DBHeader dbh;
637
638         if( ( !buf_size ) || ( ! buf ) ) {
639                 return MGU_BAD_ARGS;
640         }
641
642         *buf = NULL;
643         *buf_size=0;
644
645         if( pilotFile->path ) {
646                 in = fopen( pilotFile->path, "rb" );
647                 if( !in ) {
648                         return MGU_OPEN_FILE;
649                 }
650         }
651         else {
652                 return MGU_NO_FILE;
653         }
654
655         num = fread( &rdbh, sizeof( RawDBHeader ), 1, in );
656         if( num != 1 ) {
657                 if( ferror(in) ) {
658                         fclose(in);
659                         return MGU_ERROR_READ;
660                 }
661         }
662         if (feof(in)) {
663                 fclose(in);
664                 return MGU_EOF;
665         }
666
667         /* Convert header into something recognizable */
668         raw_header_to_header(&rdbh, &dbh);
669
670         num = jpilot_get_info_size(in, &rec_size);
671         if (num) {
672                 fclose(in);
673                 return MGU_ERROR_READ;
674         }
675
676         fseek(in, dbh.app_info_offset, SEEK_SET);
677         *buf = ( char * ) malloc(rec_size);
678         if (!(*buf)) {
679                 fclose(in);
680                 return MGU_OO_MEMORY;
681         }
682         num = fread(*buf, rec_size, 1, in);
683         if (num != 1) {
684                 if (ferror(in)) {
685                         fclose(in);
686                         free(*buf);
687                         return MGU_ERROR_READ;
688                 }
689         }
690         fclose(in);
691
692         *buf_size = rec_size;
693
694         return MGU_SUCCESS;
695 }
696
697 /* Shamelessly copied from JPilot (libplugin.c) */
698 static int unpack_header(PC3RecordHeader *header, unsigned char *packed_header) {
699         unsigned char *p;
700         unsigned long l;
701
702         p = packed_header;
703
704         memcpy(&l, p, sizeof(l));
705         header->header_len=ntohl(l);
706         p+=sizeof(l);
707
708         memcpy(&l, p, sizeof(l));
709         header->header_version=ntohl(l);
710         p+=sizeof(l);
711
712         memcpy(&l, p, sizeof(l));
713         header->rec_len=ntohl(l);
714         p+=sizeof(l);
715
716         memcpy(&l, p, sizeof(l));
717         header->unique_id=ntohl(l);
718         p+=sizeof(l);
719
720         memcpy(&l, p, sizeof(l));
721         header->rt=ntohl(l);
722         p+=sizeof(l);
723
724         memcpy(&(header->attrib), p, sizeof(unsigned char));
725         p+=sizeof(unsigned char);
726
727         return 0;
728 }
729
730 /* Shamelessly copied from JPilot (libplugin.c) */
731 static int read_header(FILE *pc_in, PC3RecordHeader *header) {
732         unsigned long l, len;
733         unsigned char packed_header[256];
734         int num;
735
736         num = fread(&l, sizeof(l), 1, pc_in);
737         if (feof(pc_in)) {
738                 return -1;
739         }
740         if (num!=1) {
741                 return num;
742         }
743         memcpy(packed_header, &l, sizeof(l));
744         len=ntohl(l);
745         if (len > 255) {
746                 return -1;
747         }
748         num = fread(packed_header+sizeof(l), len-sizeof(l), 1, pc_in);
749         if (feof(pc_in)) {
750                 return -1;
751         }
752         if (num!=1) {
753                 return num;
754         }
755         unpack_header(header, packed_header);
756         return 1;
757 }
758
759 /* Read next record from PC3 file. Based on JPilot's
760  * pc_read_next_rec (libplugin.c) */
761 static gint jpilot_read_next_pc( FILE *in, buf_rec *br ) {
762         PC3RecordHeader header;
763         int rec_len, num;
764         char *record;
765
766         if(feof(in)) {
767                 return MGU_EOF;
768         }
769         num = read_header(in, &header);
770         if (num < 1) {
771                 if (ferror(in)) {
772                         return MGU_ERROR_READ;
773                 }
774                 if (feof(in)) {
775                         return MGU_EOF;
776                 }
777         }
778         rec_len = header.rec_len;
779         record = malloc(rec_len);
780         if (!record) {
781                 return MGU_OO_MEMORY;
782         }
783         num = fread(record, rec_len, 1, in);
784         if (num != 1) {
785                 if (ferror(in)) {
786                         free(record);
787                         return MGU_ERROR_READ;
788                 }
789         }
790         br->rt = header.rt;
791         br->unique_id = header.unique_id;
792         br->attrib = header.attrib;
793         br->buf = record;
794         br->size = rec_len;
795
796         return MGU_SUCCESS;
797 }
798
799 /*
800  * Read address file into a linked list. Based on JPilot's
801  * jp_read_DB_files (from libplugin.c)
802  */
803 static gint jpilot_read_db_files( JPilotFile *pilotFile, GList **records ) {
804         FILE *in, *pc_in;
805         char *buf;
806         GList *temp_list;
807         int num_records, recs_returned, i, num, r;
808         unsigned int offset, prev_offset, next_offset, rec_size;
809         int out_of_order;
810         long fpos;  /*file position indicator */
811         unsigned char attrib;
812         unsigned int unique_id;
813         mem_rec_header *mem_rh, *temp_mem_rh, *last_mem_rh;
814         record_header rh;
815         RawDBHeader rdbh;
816         DBHeader dbh;
817         buf_rec *temp_br;
818         gchar *pcFile;
819
820         mem_rh = last_mem_rh = NULL;
821         *records = NULL;
822         recs_returned = 0;
823
824         if( pilotFile->path == NULL ) {
825                 return MGU_BAD_ARGS;
826         }
827
828         in = fopen( pilotFile->path, "rb" );
829         if (!in) {
830                 return MGU_OPEN_FILE;
831         }
832
833         /* Read the database header */
834         num = fread(&rdbh, sizeof(RawDBHeader), 1, in);
835         if (num != 1) {
836                 if (ferror(in)) {
837                         fclose(in);
838                         return MGU_ERROR_READ;
839                 }
840                 if (feof(in)) {
841                         fclose(in);
842                         return MGU_EOF;
843                 }
844         }
845         raw_header_to_header(&rdbh, &dbh);
846
847         /* Read each record entry header */
848         num_records = dbh.number_of_records;
849         out_of_order = 0;
850         prev_offset = 0;
851
852         for (i = 1; i < num_records + 1; i++) {
853                 num = fread(&rh, sizeof(record_header), 1, in);
854                 if (num != 1) {
855                         if (ferror(in)) {
856                                 break;
857                         }
858                         if (feof(in)) {
859                                 fclose(in);
860                                 return MGU_EOF;
861                         }
862                 }
863
864                 offset = ((rh.Offset[0]*256+rh.Offset[1])*256+rh.Offset[2])*256+rh.Offset[3];
865                 if (offset < prev_offset) {
866                         out_of_order = 1;
867                 }
868                 prev_offset = offset;
869                 temp_mem_rh = (mem_rec_header *)malloc(sizeof(mem_rec_header));
870                 if (!temp_mem_rh) {
871                         break;
872                 }
873                 temp_mem_rh->next = NULL;
874                 temp_mem_rh->rec_num = i;
875                 temp_mem_rh->offset = offset;
876                 temp_mem_rh->attrib = rh.attrib;
877                 temp_mem_rh->unique_id = (rh.unique_ID[0]*256+rh.unique_ID[1])*256+rh.unique_ID[2];
878                 if (mem_rh == NULL) {
879                         mem_rh = temp_mem_rh;
880                         last_mem_rh = temp_mem_rh;
881                 } else {
882                         last_mem_rh->next = temp_mem_rh;
883                         last_mem_rh = temp_mem_rh;
884                 }
885         }
886
887         temp_mem_rh = mem_rh;
888
889         if (num_records) {
890                 if (out_of_order) {
891                         find_next_offset(mem_rh, 0, &next_offset, &attrib, &unique_id);
892                 } else {
893                         if (mem_rh) {
894                                 next_offset = mem_rh->offset;
895                                 attrib = mem_rh->attrib;
896                                 unique_id = mem_rh->unique_id;
897                         }
898                 }
899                 fseek(in, next_offset, SEEK_SET);
900                 while(!feof(in)) {
901                         fpos = ftell(in);
902                         if (out_of_order) {
903                                 find_next_offset(mem_rh, fpos, &next_offset, &attrib, &unique_id);
904                         } else {
905                                 next_offset = 0xFFFFFF;
906                                 if (temp_mem_rh) {
907                                         attrib = temp_mem_rh->attrib;
908                                         unique_id = temp_mem_rh->unique_id;
909                                         if (temp_mem_rh->next) {
910                                                 temp_mem_rh = temp_mem_rh->next;
911                                                 next_offset = temp_mem_rh->offset;
912                                         }
913                                 }
914                         }
915                         rec_size = next_offset - fpos;
916                         buf = malloc(rec_size);
917                         if (!buf) break;
918                         num = fread(buf, rec_size, 1, in);
919                         if ((num != 1)) {
920                                 if (ferror(in)) {
921                                         free(buf);
922                                         break;
923                                 }
924                         }
925
926                         temp_br = malloc(sizeof(buf_rec));
927                         if (!temp_br) {
928                                 break;
929                         }
930                         temp_br->rt = PALM_REC;
931                         temp_br->unique_id = unique_id;
932                         temp_br->attrib = attrib;
933                         temp_br->buf = buf;
934                         temp_br->size = rec_size;
935
936                         *records = g_list_append(*records, temp_br);
937
938                         recs_returned++;
939                 }
940         }
941         fclose(in);
942         free_mem_rec_header(&mem_rh);
943
944         /* Read the PC3 file, if present */
945         pcFile = jpilot_get_pc3_file( pilotFile );
946         if( pcFile == NULL ) return MGU_SUCCESS;
947         pc_in = fopen( pcFile, "rb" );
948         g_free( pcFile );
949
950         if( pc_in == NULL ) {
951                 return MGU_SUCCESS;
952         }
953
954         while( ! feof( pc_in ) ) {
955                 temp_br = malloc(sizeof(buf_rec));
956                 if (!temp_br) {
957                         break;
958                 }
959                 r = jpilot_read_next_pc( pc_in, temp_br );
960                 if ( r != MGU_SUCCESS ) {
961                         free(temp_br);
962                         break;
963                 }
964                 if ((temp_br->rt!=DELETED_PC_REC)
965                         &&(temp_br->rt!=DELETED_PALM_REC)
966                         &&(temp_br->rt!=MODIFIED_PALM_REC)
967                         &&(temp_br->rt!=DELETED_DELETED_PALM_REC)) {
968                                 *records = g_list_append(*records, temp_br);
969                                 recs_returned++;
970                 }
971                 if ((temp_br->rt==DELETED_PALM_REC) || (temp_br->rt==MODIFIED_PALM_REC)) {
972                         temp_list=*records;
973                         if (*records) {
974                                 while(temp_list->next) {
975                                         temp_list=temp_list->next;
976                                 }
977                         }
978                         for (; temp_list; temp_list=temp_list->prev) {
979                                 if (((buf_rec *)temp_list->data)->unique_id == temp_br->unique_id) {
980                                         ((buf_rec *)temp_list->data)->rt = temp_br->rt;
981                                 }
982                         }
983                 }
984         }
985         fclose(pc_in);
986
987         return MGU_SUCCESS;
988 }
989
990 /*
991  * Parse buffer containing multiple e-mail addresses into a linked list of
992  * addresses. Separator characters are " ,;|" and control characters. Address
993  * is only extracted if it contains an "at" (@) character.
994  * Enter: buf Buffer
995  * Return: List of strings.
996  */
997 static GList *jpilot_parse_email( gchar *buf ) {
998         GList *list;
999         gchar *p, *st, *em;
1000         gchar lch;
1001         gint len;
1002         gboolean valid, done;
1003
1004         valid = done = FALSE;
1005         lch = ' ';
1006         list = NULL;
1007         p = st = buf;
1008         while( ! done ) {
1009                 if( *p == ' ' || *p == ',' || *p == ';' || *p == '|' || *p < 32 ) {
1010                         if( *p == '\0' ) {
1011                                 done = TRUE;
1012                         }
1013                         else {
1014                                 *p = ' ';
1015                         }
1016
1017                         if( *p == lch ) {
1018                                 st++;
1019                         }
1020                         else {
1021                                 len = p - st;
1022                                 if( len > 0 ) {
1023                                         if( valid ) {
1024                                                 em = g_strndup( st, len );
1025                                                 list = g_list_append( list, em );
1026                                         }
1027                                         st = p;
1028                                         ++st;
1029                                         valid = FALSE;
1030                                 }
1031                         }
1032                 }
1033                 if( *p == '@' ) valid = TRUE;
1034                 lch = *p;
1035                 ++p;
1036         }
1037
1038         return list;    
1039 }
1040
1041 #define FULLNAME_BUFSIZE        256
1042 #define EMAIL_BUFSIZE           256
1043
1044 /*
1045  * Process a single label entry field, parsing multiple e-mail address entries.
1046  * Enter: pilotFile  JPilot control data.
1047  *        labelEntry Label entry data.
1048  *        person     Person.
1049  */
1050 static void jpilot_parse_label( JPilotFile *pilotFile, gchar *labelEntry, ItemPerson *person ) {
1051         gchar buffer[ EMAIL_BUFSIZE ];
1052         ItemEMail *email;
1053         GList *list, *node;
1054
1055         if( labelEntry ) {
1056                 *buffer = '\0';
1057                 strcpy( buffer, labelEntry );
1058                 node = list = jpilot_parse_email( buffer );
1059                 while( node ) {
1060                         email = addritem_create_item_email();
1061                         addritem_email_set_address( email, node->data );
1062                         addrcache_id_email( pilotFile->addressCache, email );
1063                         addrcache_person_add_email( pilotFile->addressCache, person, email );
1064                         node = g_list_next( node );
1065                 }
1066                 mgu_free_dlist( list );
1067                 list = NULL;
1068         }
1069 }
1070         
1071 /*
1072  * Unpack address, building new data inside cache.
1073  */
1074 static void jpilot_load_address( JPilotFile *pilotFile, buf_rec *buf, ItemFolder *folderInd[] ) {
1075         struct Address addr;
1076         gchar **addrEnt;
1077         gint num, k;
1078         gint cat_id = 0;
1079         guint unique_id;
1080         guchar attrib;
1081         gchar fullName[ FULLNAME_BUFSIZE ];
1082         ItemPerson *person;
1083         gint *indPhoneLbl;
1084         gchar *labelEntry;
1085         GList *node;
1086         gchar* extID;
1087         struct AddressAppInfo *ai;
1088
1089         /* Retrieve address */
1090         num = unpack_Address( & addr, buf->buf, buf->size );
1091         if( num > 0 ) {
1092                 addrEnt = addr.entry;
1093                 attrib = buf->attrib;
1094                 unique_id = buf->unique_id;
1095                 cat_id = attrib & 0x0F;
1096
1097                 *fullName = '\0';
1098                 if( addrEnt[ IND_LABEL_FIRSTNAME ] ) {
1099                         strcat( fullName, addrEnt[ IND_LABEL_FIRSTNAME ] );
1100                 }
1101
1102                 if( addrEnt[ IND_LABEL_LASTNAME ] ) {
1103                         strcat( fullName, " " );
1104                         strcat( fullName, addrEnt[ IND_LABEL_LASTNAME ] );
1105                 }
1106                 g_strchug( fullName );
1107                 g_strchomp( fullName );
1108
1109                 person = addritem_create_item_person();
1110                 addritem_person_set_common_name( person, fullName );
1111                 addritem_person_set_first_name( person, addrEnt[ IND_LABEL_FIRSTNAME ] );
1112                 addritem_person_set_last_name( person, addrEnt[ IND_LABEL_LASTNAME ] );
1113                 addrcache_id_person( pilotFile->addressCache, person );
1114
1115                 extID = g_strdup_printf( "%d", unique_id );
1116                 addritem_person_set_external_id( person, extID );
1117                 g_free( extID );
1118                 extID = NULL;
1119
1120                 /* Pointer to address metadata. */
1121                 ai = & pilotFile->addrInfo;
1122
1123                 /* Add entry for each email address listed under phone labels. */
1124                 indPhoneLbl = addr.phoneLabel;
1125                 for( k = 0; k < JPILOT_NUM_ADDR_PHONE; k++ ) {
1126                         gint ind;
1127                         ind = indPhoneLbl[k];
1128                         /*
1129                         * fprintf( stdout, "%d : %d : %20s : %s\n", k, ind,
1130                         * ai->phoneLabels[ind], addrEnt[3+k] );
1131                         */
1132                         if( indPhoneLbl[k] == IND_PHONE_EMAIL ) {
1133                                 labelEntry = addrEnt[ OFFSET_PHONE_LABEL + k ];
1134                                 jpilot_parse_label( pilotFile, labelEntry, person );
1135                         }
1136                 }
1137
1138                 /* Add entry for each custom label */
1139                 node = pilotFile->labelInd;
1140                 while( node ) {
1141                         gint ind;
1142                         ind = GPOINTER_TO_INT( node->data );
1143                         if( ind > -1 ) {
1144                                 /*
1145                                 * fprintf( stdout, "%d : %20s : %s\n", ind, ai->labels[ind],
1146                                 * addrEnt[ind] );
1147                                 */
1148                                 labelEntry = addrEnt[ind];
1149                                 jpilot_parse_label( pilotFile, labelEntry, person );
1150                         }
1151
1152                         node = g_list_next( node );
1153                 }
1154
1155                 if( person->listEMail ) {
1156                         if( cat_id > -1 && cat_id < JPILOT_NUM_CATEG ) {
1157                                 /* Add to specified category */
1158                                 addrcache_folder_add_person
1159                                         ( pilotFile->addressCache, folderInd[cat_id], person );
1160                         } else {
1161                                 /* Add to root folder */
1162                                 addrcache_add_person( pilotFile->addressCache, person );
1163                         }
1164                 } else {
1165                         addritem_free_item_person( person );
1166                         person = NULL;
1167                 }
1168         }
1169 }
1170
1171 /*
1172  * Free up address list.
1173  */
1174 static void jpilot_free_addrlist( GList *records ) {
1175         GList *node;
1176         buf_rec *br;
1177
1178         node = records;
1179         while( node ) {
1180                 br = node->data;
1181                 free( br );
1182                 node->data = NULL;
1183                 node = g_list_next( node );
1184         }
1185
1186         /* Free up list */
1187         g_list_free( records );
1188 }
1189
1190 /*
1191  * Read address file into address cache.
1192  */
1193 static gint jpilot_read_file( JPilotFile *pilotFile ) {
1194         gint retVal, i;
1195         GList *records = NULL;
1196         GList *node;
1197         buf_rec *br;
1198         ItemFolder *folderInd[ JPILOT_NUM_CATEG ];
1199
1200         retVal = jpilot_read_db_files( pilotFile, &records );
1201         if( retVal != MGU_SUCCESS ) {
1202                 jpilot_free_addrlist( records );
1203                 return retVal;
1204         }
1205
1206         /* Build array of pointers to categories */
1207         i = 0;
1208         node = addrcache_get_list_folder( pilotFile->addressCache );
1209         while( node ) {
1210                 if( i < JPILOT_NUM_CATEG ) {
1211                         folderInd[i] = node->data;
1212                 }
1213                 node = g_list_next( node );
1214                 i++;
1215         }
1216
1217         /* Load all addresses, free up old stuff as we go */
1218         node = records;
1219         while( node ) {
1220                 br = node->data;
1221                 if( ( br->rt != DELETED_PC_REC ) &&
1222                     ( br->rt != DELETED_PALM_REC ) &&
1223                     ( br->rt != MODIFIED_PALM_REC ) &&
1224                     ( br->rt != DELETED_DELETED_PALM_REC ) ) {
1225                         jpilot_load_address( pilotFile, br, folderInd );
1226                 }
1227                 free( br );
1228                 node->data = NULL;
1229                 node = g_list_next( node );
1230         }
1231
1232         /* Free up list */
1233         g_list_free( records );
1234
1235         return retVal;
1236 }
1237
1238
1239 /*
1240 * Read metadata from file.
1241 */
1242 static gint jpilot_read_metadata( JPilotFile *pilotFile ) {
1243         gint retVal;
1244         unsigned int rec_size;
1245         unsigned char *buf;
1246         int num;
1247
1248         g_return_val_if_fail( pilotFile != NULL, -1 );
1249
1250         pilotFile->readMetadata = FALSE;
1251         addrcache_clear( pilotFile->addressCache );
1252
1253         /* Read file info */
1254         retVal = jpilot_get_file_info( pilotFile, &buf, &rec_size);
1255         if( retVal != MGU_SUCCESS ) {
1256                 pilotFile->retVal = retVal;
1257                 return pilotFile->retVal;
1258         }
1259
1260         num = unpack_AddressAppInfo( &pilotFile->addrInfo, buf, rec_size );
1261         if( buf ) {
1262                 free(buf);
1263         }
1264         if( num <= 0 ) {
1265                 pilotFile->retVal = MGU_ERROR_READ;
1266                 return pilotFile->retVal;
1267         }
1268
1269         pilotFile->readMetadata = TRUE;
1270         pilotFile->retVal = MGU_SUCCESS;
1271         return pilotFile->retVal;
1272 }
1273
1274 /*
1275 * Setup labels and indexes from metadata.
1276 * Return: TRUE is setup successfully.
1277 */
1278 static gboolean jpilot_setup_labels( JPilotFile *pilotFile ) {
1279         gboolean retVal = FALSE;
1280         struct AddressAppInfo *ai;
1281         GList *node;
1282
1283         g_return_val_if_fail( pilotFile != NULL, -1 );
1284
1285         /* Release indexes */
1286         node = pilotFile->labelInd;
1287         while( node ) {
1288                 node->data = NULL;
1289                 node = g_list_next( node );
1290         }
1291         pilotFile->labelInd = NULL;
1292
1293         if( pilotFile->readMetadata ) {
1294                 ai = & pilotFile->addrInfo;
1295                 node = pilotFile->customLabels;
1296                 while( node ) {
1297                         gchar *lbl = node->data;
1298                         gint ind = -1;
1299                         gint i;
1300                         for( i = 0; i < JPILOT_NUM_LABELS; i++ ) {
1301                                 gchar *labelName = ai->labels[i];
1302                                 if( g_strcasecmp( labelName, lbl ) == 0 ) {
1303                                         ind = i;
1304                                         break;
1305                                 }
1306                         }
1307                         pilotFile->labelInd = g_list_append( pilotFile->labelInd, GINT_TO_POINTER(ind) );
1308                         node = g_list_next( node );
1309                 }
1310                 retVal = TRUE;
1311         }
1312         return retVal;
1313 }
1314
1315 /*
1316 * Load list with character strings of label names.
1317 */
1318 GList *jpilot_load_label( JPilotFile *pilotFile, GList *labelList ) {
1319         int i;
1320
1321         g_return_val_if_fail( pilotFile != NULL, NULL );
1322
1323         if( pilotFile->readMetadata ) {
1324                 struct AddressAppInfo *ai = & pilotFile->addrInfo;
1325                 for( i = 0; i < JPILOT_NUM_LABELS; i++ ) {
1326                         gchar *labelName = ai->labels[i];
1327                         if( labelName ) {
1328                                 labelList = g_list_append( labelList, g_strdup( labelName ) );
1329                         }
1330                         else {
1331                                 labelList = g_list_append( labelList, g_strdup( "" ) );
1332                         }
1333                 }
1334         }
1335         return labelList;
1336 }
1337
1338 /*
1339 * Return category name for specified category ID.
1340 * Enter:  Category ID.
1341 * Return: Name, or empty string if not invalid ID. Name should be g_free() when done.
1342 */
1343 gchar *jpilot_get_category_name( JPilotFile *pilotFile, gint catID ) {
1344         gchar *catName = NULL;
1345
1346         g_return_val_if_fail( pilotFile != NULL, NULL );
1347
1348         if( pilotFile->readMetadata ) {
1349                 struct AddressAppInfo *ai = & pilotFile->addrInfo;
1350                 struct CategoryAppInfo *cat = & ai->category;
1351                 if( catID < 0 || catID > JPILOT_NUM_CATEG ) {
1352                 }
1353                 else {
1354                         catName = g_strdup( cat->name[catID] );
1355                 }
1356         }
1357         if( ! catName ) catName = g_strdup( "" );
1358         return catName;
1359 }
1360
1361 /*
1362 * Load list with character strings of phone label names.
1363 */
1364 GList *jpilot_load_phone_label( JPilotFile *pilotFile, GList *labelList ) {
1365         gint i;
1366
1367         g_return_val_if_fail( pilotFile != NULL, NULL );
1368
1369         if( pilotFile->readMetadata ) {
1370                 struct AddressAppInfo *ai = & pilotFile->addrInfo;
1371                 for( i = 0; i < JPILOT_NUM_PHONELABELS; i++ ) {
1372                         gchar   *labelName = ai->phoneLabels[i];
1373                         if( labelName ) {
1374                                 labelList = g_list_append( labelList, g_strdup( labelName ) );
1375                         }
1376                         else {
1377                                 labelList = g_list_append( labelList, g_strdup( "" ) );
1378                         }
1379                 }
1380         }
1381         return labelList;
1382 }
1383
1384 /*
1385 * Load list with character strings of label names. Only none blank names
1386 * are loaded.
1387 * Return: list of labels. Should by g_free()'d when done.
1388 */
1389 GList *jpilot_load_custom_label( JPilotFile *pilotFile, GList *labelList ) {
1390         gint i;
1391
1392         g_return_val_if_fail( pilotFile != NULL, NULL );
1393
1394         if( pilotFile->readMetadata ) {
1395                 struct AddressAppInfo *ai = & pilotFile->addrInfo;
1396                 for( i = 0; i < NUM_CUSTOM_LABEL; i++ ) {
1397                         gchar *labelName = ai->labels[i+IND_CUSTOM_LABEL];
1398                         if( labelName ) {
1399                                 g_strchomp( labelName );
1400                                 g_strchug( labelName );
1401                                 if( *labelName != '\0' ) {
1402                                         labelList = g_list_append( labelList, g_strdup( labelName ) );
1403                                 }
1404                         }
1405                 }
1406         }
1407         return labelList;
1408 }
1409
1410 /*
1411 * Load list with character strings of category names.
1412 */
1413 GList *jpilot_get_category_list( JPilotFile *pilotFile ) {
1414         GList *catList = NULL;
1415         gint i;
1416
1417         g_return_val_if_fail( pilotFile != NULL, NULL );
1418
1419         if( pilotFile->readMetadata ) {
1420                 struct AddressAppInfo *ai = & pilotFile->addrInfo;
1421                 struct CategoryAppInfo *cat = & ai->category;
1422                 for( i = 0; i < JPILOT_NUM_CATEG; i++ ) {
1423                         gchar *catName = cat->name[i];
1424                         if( catName ) {
1425                                 catList = g_list_append( catList, g_strdup( catName ) );
1426                         }
1427                         else {
1428                                 catList = g_list_append( catList, g_strdup( "" ) );
1429                         }
1430                 }
1431         }
1432         return catList;
1433 }
1434
1435 /*
1436 * Build folder for each category.
1437 */
1438 static void jpilot_build_category_list( JPilotFile *pilotFile ) {
1439         struct AddressAppInfo *ai = & pilotFile->addrInfo;
1440         struct CategoryAppInfo *cat = & ai->category;
1441         gint i;
1442
1443         for( i = 0; i < JPILOT_NUM_CATEG; i++ ) {
1444                 ItemFolder *folder = addritem_create_item_folder();
1445                 addritem_folder_set_name( folder, cat->name[i] );
1446                 addrcache_id_folder( pilotFile->addressCache, folder );
1447                 addrcache_add_folder( pilotFile->addressCache, folder );
1448         }
1449 }
1450
1451 /*
1452 * Remove empty folders (categories).
1453 */
1454 static void jpilot_remove_empty( JPilotFile *pilotFile ) {
1455         GList *listFolder;
1456         GList *remList;
1457         GList *node;
1458         gint i = 0;
1459
1460         listFolder = addrcache_get_list_folder( pilotFile->addressCache );
1461         node = listFolder;
1462         remList = NULL;
1463         while( node ) {
1464                 ItemFolder *folder = node->data;
1465                 if( ADDRITEM_NAME(folder) == NULL || *ADDRITEM_NAME(folder) == '\0' ) {
1466                         if( folder->listPerson ) {
1467                                 /* Give name to folder */
1468                                 gchar name[20];
1469                                 sprintf( name, "? %d", i );
1470                                 addritem_folder_set_name( folder, name );
1471                         }
1472                         else {
1473                                 /* Mark for removal */
1474                                 remList = g_list_append( remList, folder );
1475                         }
1476                 }
1477                 node = g_list_next( node );
1478                 i++;
1479         }
1480         node = remList;
1481         while( node ) {
1482                 ItemFolder *folder = node->data;
1483                 addrcache_remove_folder( pilotFile->addressCache, folder );
1484                 node = g_list_next( node );
1485         }
1486         g_list_free( remList );
1487 }
1488
1489 /*
1490 * ============================================================================================
1491 * Read file into list. Main entry point
1492 * Return: TRUE if file read successfully.
1493 * ============================================================================================
1494 */
1495 gint jpilot_read_data( JPilotFile *pilotFile ) {
1496         g_return_val_if_fail( pilotFile != NULL, -1 );
1497
1498         pilotFile->retVal = MGU_SUCCESS;
1499         pilotFile->addressCache->accessFlag = FALSE;
1500         if( jpilot_check_files( pilotFile ) ) {
1501                 addrcache_clear( pilotFile->addressCache );
1502                 jpilot_read_metadata( pilotFile );
1503                 if( pilotFile->retVal == MGU_SUCCESS ) {
1504                         jpilot_setup_labels( pilotFile );
1505                         jpilot_build_category_list( pilotFile );
1506                         pilotFile->retVal = jpilot_read_file( pilotFile );
1507                         if( pilotFile->retVal == MGU_SUCCESS ) {
1508                                 jpilot_remove_empty( pilotFile );
1509                                 jpilot_mark_files( pilotFile );
1510                                 pilotFile->addressCache->modified = FALSE;
1511                                 pilotFile->addressCache->dataRead = TRUE;
1512                         }
1513                 }
1514         }
1515         return pilotFile->retVal;
1516 }
1517
1518 /*
1519 * Return link list of persons.
1520 */
1521 GList *jpilot_get_list_person( JPilotFile *pilotFile ) {
1522         g_return_val_if_fail( pilotFile != NULL, NULL );
1523         return addrcache_get_list_person( pilotFile->addressCache );
1524 }
1525
1526 /*
1527 * Return link list of folders. This is always NULL since there are
1528 * no folders in GnomeCard.
1529 * Return: NULL.
1530 */
1531 GList *jpilot_get_list_folder( JPilotFile *pilotFile ) {
1532         g_return_val_if_fail( pilotFile != NULL, NULL );
1533         return addrcache_get_list_folder( pilotFile->addressCache );
1534 }
1535
1536 /*
1537 * Return link list of all persons. Note that the list contains references
1538 * to items. Do *NOT* attempt to use the addrbook_free_xxx() functions...
1539 * this will destroy the addressbook data!
1540 * Return: List of items, or NULL if none.
1541 */
1542 GList *jpilot_get_all_persons( JPilotFile *pilotFile ) {
1543         g_return_val_if_fail( pilotFile != NULL, NULL );
1544         return addrcache_get_all_persons( pilotFile->addressCache );
1545 }
1546
1547 /*
1548 * Check label list for specified label.
1549 */
1550 gint jpilot_check_label( struct AddressAppInfo *ai, gchar *lblCheck ) {
1551         gint i;
1552         gchar *lblName;
1553
1554         if( lblCheck == NULL ) return -1;
1555         if( strlen( lblCheck ) < 1 ) return -1;
1556         for( i = 0; i < JPILOT_NUM_LABELS; i++ ) {
1557                 lblName = ai->labels[i];
1558                 if( lblName ) {
1559                         if( strlen( lblName ) ) {
1560                                 if( g_strcasecmp( lblName, lblCheck ) == 0 ) return i;
1561                         }
1562                 }
1563         }
1564         return -2;
1565 }
1566
1567 /*
1568 * Validate that all parameters specified.
1569 * Return: TRUE if data is good.
1570 */
1571 gboolean jpilot_validate( JPilotFile *pilotFile ) {
1572         gboolean retVal;
1573         gchar *name;
1574
1575         g_return_val_if_fail( pilotFile != NULL, FALSE );
1576
1577         retVal = TRUE;
1578         if( pilotFile->path ) {
1579                 if( strlen( pilotFile->path ) < 1 ) retVal = FALSE;
1580         }
1581         else {
1582                 retVal = FALSE;
1583         }
1584         name = jpilot_get_name( pilotFile );
1585         if( name ) {
1586                 if( strlen( name ) < 1 ) retVal = FALSE;
1587         }
1588         else {
1589                 retVal = FALSE;
1590         }
1591         return retVal;
1592 }
1593
1594 #define WORK_BUFLEN 1024
1595
1596 /*
1597 * Attempt to find a valid JPilot file.
1598 * Return: Filename, or home directory if not found, or empty string if
1599 * no home. Filename should be g_free() when done.
1600 */
1601 gchar *jpilot_find_pilotdb( void ) {
1602         gchar *homedir;
1603         gchar str[ WORK_BUFLEN ];
1604         gint len;
1605         FILE *fp;
1606
1607         homedir = g_get_home_dir();
1608         if( ! homedir ) return g_strdup( "" );
1609
1610         strcpy( str, homedir );
1611         len = strlen( str );
1612         if( len > 0 ) {
1613                 if( str[ len-1 ] != G_DIR_SEPARATOR ) {
1614                         str[ len ] = G_DIR_SEPARATOR;
1615                         str[ ++len ] = '\0';
1616                 }
1617         }
1618         strcat( str, JPILOT_DBHOME_DIR );
1619         strcat( str, G_DIR_SEPARATOR_S );
1620         strcat( str, JPILOT_DBHOME_FILE );
1621
1622         /* Attempt to open */
1623         if( ( fp = fopen( str, "rb" ) ) != NULL ) {
1624                 fclose( fp );
1625         }
1626         else {
1627                 /* Truncate filename */
1628                 str[ len ] = '\0';
1629         }
1630         return g_strdup( str );
1631 }
1632
1633 /*
1634 * Attempt to read file, testing for valid JPilot format.
1635 * Return: TRUE if file appears to be valid format.
1636 */
1637 gint jpilot_test_read_file( const gchar *fileSpec ) {
1638         JPilotFile *pilotFile;
1639         gint retVal;
1640
1641         if( fileSpec ) {
1642                 pilotFile = jpilot_create_path( fileSpec );
1643                 retVal = jpilot_read_metadata( pilotFile );
1644                 jpilot_free( pilotFile );
1645                 pilotFile = NULL;
1646         }
1647         else {
1648                 retVal = MGU_NO_FILE;
1649         }
1650         return retVal;
1651 }
1652
1653 /*
1654 * Check whether label is in custom labels.
1655 * Return: TRUE if found.
1656 */
1657 gboolean jpilot_test_custom_label( JPilotFile *pilotFile, const gchar *labelName ) {
1658         gboolean retVal;
1659         GList *node;
1660
1661         g_return_val_if_fail( pilotFile != NULL, FALSE );
1662
1663         retVal = FALSE;
1664         if( labelName ) {
1665                 node = pilotFile->customLabels;
1666                 while( node ) {
1667                         if( g_strcasecmp( labelName, ( gchar * ) node->data ) == 0 ) {
1668                                 retVal = TRUE;
1669                                 break;
1670                         }
1671                         node = g_list_next( node );
1672                 }
1673         }
1674         return retVal;
1675 }
1676
1677 /*
1678 * Test whether pilot link library installed.
1679 * Return: TRUE if library available.
1680 */
1681 gboolean jpilot_test_pilot_lib( void ) {
1682         return TRUE;
1683 }
1684
1685 #endif  /* USE_JPILOT */
1686
1687 /*
1688 * End of Source.
1689 */