2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2001-2002 Match Grun
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.
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.
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.
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.
38 #include <netinet/in.h>
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>
46 # include <pi-appinfo.h>
47 # include <pi-address.h>
52 #include "addrcache.h"
54 #include "adbookbase.h"
56 #define JPILOT_DBHOME_DIR ".jpilot"
57 #define JPILOT_DBHOME_FILE "AddressDB.pdb"
58 #define PILOT_LINK_LIB_NAME "libpisock.so"
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 */
67 /* Shamelessly copied from JPilot (libplugin.h) */
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];
85 /* Shamelessly copied from JPilot (libplugin.h) */
91 time_t modification_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
;
103 /* Shamelessly copied from JPilot (libplugin.h) */
105 unsigned char Offset
[4]; /*4 bytes offset from BOF to record */
106 unsigned char attrib
;
107 unsigned char unique_ID
[3];
110 /* Shamelessly copied from JPilot (libplugin.h) */
111 typedef struct mem_rec_header_s
{
112 unsigned int rec_num
;
114 unsigned int unique_id
;
115 unsigned char attrib
;
116 struct mem_rec_header_s
*next
;
119 /* Shamelessly copied from JPilot (libplugin.h) */
120 #define SPENT_PC_RECORD_BIT 256
124 MODIFIED_PALM_REC
= 101L,
125 DELETED_PALM_REC
= 102L,
127 DELETED_PC_REC
= SPENT_PC_RECORD_BIT
+ 104L,
128 DELETED_DELETED_PALM_REC
= SPENT_PC_RECORD_BIT
+ 105L
131 /* Shamelessly copied from JPilot (libplugin.h) */
134 unsigned int unique_id
;
135 unsigned char attrib
;
140 /* Shamelessly copied from JPilot (libplugin.h) */
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
;
151 * Create new pilot file object.
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
;
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;
171 * Create new pilot file object for specified file.
173 JPilotFile
*jpilot_create_path( const gchar
*path
) {
174 JPilotFile
*pilotFile
;
175 pilotFile
= jpilot_create();
176 jpilot_set_file( pilotFile
, path
);
183 void jpilot_set_name( JPilotFile
* pilotFile
, const gchar
*value
) {
184 g_return_if_fail( pilotFile
!= NULL
);
185 addrcache_set_name( pilotFile
->addressCache
, value
);
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
);
193 void jpilot_set_accessed( JPilotFile
*pilotFile
, const gboolean value
) {
194 g_return_if_fail( pilotFile
!= NULL
);
195 pilotFile
->addressCache
->accessFlag
= value
;
198 gint
jpilot_get_status( JPilotFile
*pilotFile
) {
199 g_return_val_if_fail( pilotFile
!= NULL
, -1 );
200 return pilotFile
->retVal
;
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
);
206 gchar
*jpilot_get_name( JPilotFile
*pilotFile
) {
207 g_return_val_if_fail( pilotFile
!= NULL
, NULL
);
208 return addrcache_get_name( pilotFile
->addressCache
);
212 * Test whether file was read.
213 * Return: TRUE if file was read.
215 gboolean
jpilot_get_read_flag( JPilotFile
*pilotFile
) {
216 g_return_val_if_fail( pilotFile
!= NULL
, FALSE
);
217 return pilotFile
->addressCache
->dataRead
;
221 * Free up custom label list.
223 void jpilot_clear_custom_labels( JPilotFile
*pilotFile
) {
226 g_return_if_fail( pilotFile
!= NULL
);
228 /* Release custom labels */
229 mgu_free_dlist( pilotFile
->customLabels
);
230 pilotFile
->customLabels
= NULL
;
232 /* Release indexes */
233 node
= pilotFile
->labelInd
;
236 node
= g_list_next( node
);
238 g_list_free( pilotFile
->labelInd
);
239 pilotFile
->labelInd
= NULL
;
241 /* Force a fresh read */
242 addrcache_refresh( pilotFile
->addressCache
);
246 * Append a custom label, representing an E-Mail address field to the
249 void jpilot_add_custom_label( JPilotFile
*pilotFile
, const gchar
*labelName
) {
250 g_return_if_fail( pilotFile
!= NULL
);
253 gchar
*labelCopy
= g_strdup( labelName
);
254 g_strstrip( labelCopy
);
255 if( *labelCopy
== '\0' ) {
259 pilotFile
->customLabels
= g_list_append( pilotFile
->customLabels
, labelCopy
);
260 /* Force a fresh read */
261 addrcache_refresh( pilotFile
->addressCache
);
267 * Get list of custom labels.
268 * Return: List of labels. Must use g_free() when done.
270 GList
*jpilot_get_custom_labels( JPilotFile
*pilotFile
) {
271 GList
*retVal
= NULL
;
274 g_return_val_if_fail( pilotFile
!= NULL
, NULL
);
276 node
= pilotFile
->customLabels
;
278 retVal
= g_list_append( retVal
, g_strdup( node
->data
) );
279 node
= g_list_next( node
);
285 * Return filespec of PC3 file corresponding to JPilot PDB file.
286 * Note: Filespec should be g_free() when done.
288 static gchar
*jpilot_get_pc3_file( JPilotFile
*pilotFile
) {
292 if( pilotFile
== NULL
) return NULL
;
293 if( pilotFile
->path
== NULL
) return NULL
;
295 fileSpec
= g_strdup( pilotFile
->path
);
296 len
= strlen( fileSpec
);
299 for( i
= len
; i
> 0; i
-- ) {
300 if( *(fileSpec
+ i
) == '.' ) {
307 if( len
- pos
== 3 ) {
308 *r
++ = 'p'; *r
++ = 'c'; *r
= '3';
317 * Save PC3 file time to cache.
318 * return: TRUE if time marked.
320 static gboolean
jpilot_mark_files( JPilotFile
*pilotFile
) {
321 gboolean retVal
= FALSE
;
322 struct stat filestat
;
325 /* Mark PDB file cache */
326 retVal
= addrcache_mark_file( pilotFile
->addressCache
, pilotFile
->path
);
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
;
343 * Check whether JPilot PDB or PC3 file has changed by comparing
345 * return: TRUE if file has changed.
347 static gboolean
jpilot_check_files( JPilotFile
*pilotFile
) {
348 gboolean retVal
= TRUE
;
349 struct stat filestat
;
352 /* Check main file */
353 if( addrcache_check_file( pilotFile
->addressCache
, pilotFile
->path
) )
357 if( ! pilotFile
->havePC3
) return FALSE
;
358 pcFile
= jpilot_get_pc3_file( pilotFile
);
359 if( pcFile
== NULL
) return FALSE
;
361 if( 0 == lstat( pcFile
, &filestat
) ) {
362 if( filestat
.st_mtime
== pilotFile
->pc3ModifyTime
) retVal
= FALSE
;
369 * Test whether file was modified since last access.
370 * Return: TRUE if file was modified.
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
;
377 void jpilot_set_modified( JPilotFile
*pilotFile
, const gboolean value
) {
378 g_return_if_fail( pilotFile
!= NULL
);
379 pilotFile
->addressCache
->modified
= value
;
381 gboolean
jpilot_get_accessed( JPilotFile
*pilotFile
) {
382 g_return_val_if_fail( pilotFile
!= NULL
, FALSE
);
383 return pilotFile
->addressCache
->accessFlag
;
387 * Free up pilot file object by releasing internal memory.
389 void jpilot_free( JPilotFile
*pilotFile
) {
390 g_return_if_fail( pilotFile
!= NULL
);
392 /* Release custom labels */
393 jpilot_clear_custom_labels( pilotFile
);
396 addrcache_clear( pilotFile
->addressCache
);
397 addrcache_free( pilotFile
->addressCache
);
399 /* Free internal stuff */
400 g_free( pilotFile
->path
);
402 pilotFile
->file
= NULL
;
403 pilotFile
->path
= NULL
;
404 pilotFile
->readMetadata
= FALSE
;
405 pilotFile
->havePC3
= FALSE
;
406 pilotFile
->pc3ModifyTime
= 0;
408 pilotFile
->type
= ADBOOKTYPE_NONE
;
409 pilotFile
->addressCache
= NULL
;
410 pilotFile
->retVal
= MGU_SUCCESS
;
412 /* Now release file object */
417 * Refresh internal variables to force a file read.
419 void jpilot_force_refresh( JPilotFile
*pilotFile
) {
420 addrcache_refresh( pilotFile
->addressCache
);
424 * Print object to specified stream.
426 void jpilot_print_file( JPilotFile
*pilotFile
, FILE *stream
) {
429 g_return_if_fail( pilotFile
!= NULL
);
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
);
436 node
= pilotFile
->customLabels
;
438 fprintf( stream
, " c label: %s\n", (gchar
*)node
->data
);
439 node
= g_list_next( node
);
442 node
= pilotFile
->labelInd
;
444 fprintf( stream
, " labelind: %d\n", GPOINTER_TO_INT(node
->data
) );
445 node
= g_list_next( node
);
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
);
456 * Print summary of object to specified stream.
458 void jpilot_print_short( JPilotFile
*pilotFile
, FILE *stream
) {
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
);
466 node
= pilotFile
->customLabels
;
468 fprintf( stream
, " c label: %s\n", (gchar
*)node
->data
);
469 node
= g_list_next( node
);
472 node
= pilotFile
->labelInd
;
474 fprintf( stream
, " labelind: %d\n", GPOINTER_TO_INT(node
->data
) );
475 node
= g_list_next( node
);
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
);
482 /* Shamelessly copied from JPilot (libplugin.c) */
483 static unsigned int bytes_to_bin(unsigned char *bytes
, unsigned int num_bytes
) {
486 for (i
=0;i
<num_bytes
;i
++) {
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)
497 time_t pilot_time_to_unix_time ( unsigned long raw_time
) {
498 return (time_t)(raw_time
- PILOT_TIME_DELTA
);
501 /* Shamelessly copied from JPilot (libplugin.c) */
502 static int raw_header_to_header(RawDBHeader
*rdbh
, DBHeader
*dbh
) {
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);
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);
529 /* Shamelessly copied from JPilot (libplugin.c) */
530 /* returns 1 if found */
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
)
535 mem_rec_header
*temp_mem_rh
;
536 unsigned char found
= 0;
537 unsigned long found_at
;
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; */
546 if ((temp_mem_rh
->offset
== fpos
)) {
548 *attrib
= temp_mem_rh
->attrib
;
549 *unique_id
= temp_mem_rh
->unique_id
;
552 *next_offset
= found_at
;
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
) {
566 /* Shamelessly copied from JPilot (libplugin.c) */
567 static int jpilot_free_db_list( GList
**br_list
) {
568 GList
*temp_list
, *first
;
571 /* Go to first entry in the list */
573 for( temp_list
= *br_list
; temp_list
; temp_list
= temp_list
->prev
) {
576 for (temp_list
= first
; temp_list
; temp_list
= temp_list
->next
) {
577 if (temp_list
->data
) {
581 temp_list
->data
=NULL
;
586 g_list_free(*br_list
);
591 /* Shamelessly copied from JPilot (libplugin.c) */
593 static int jpilot_get_info_size( FILE *in
, int *size
) {
599 fseek(in
, 0, SEEK_SET
);
600 fread(&rdbh
, sizeof(RawDBHeader
), 1, in
);
605 raw_header_to_header(&rdbh
, &dbh
);
606 if (dbh
.app_info_offset
==0) {
610 if (dbh
.sort_info_offset
!=0) {
611 *size
= dbh
.sort_info_offset
- dbh
.app_info_offset
;
614 if (dbh
.number_of_records
==0) {
615 fseek(in
, 0, SEEK_END
);
616 *size
=ftell(in
) - dbh
.app_info_offset
;
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
;
628 * Read address file into address list. Based on JPilot's
629 * libplugin.c (jp_get_app_info)
631 static gint
jpilot_get_file_info( JPilotFile
*pilotFile
, unsigned char **buf
, int *buf_size
) {
634 unsigned int rec_size
;
638 if( ( !buf_size
) || ( ! buf
) ) {
645 if( pilotFile
->path
) {
646 in
= fopen( pilotFile
->path
, "rb" );
648 return MGU_OPEN_FILE
;
655 num
= fread( &rdbh
, sizeof( RawDBHeader
), 1, in
);
659 return MGU_ERROR_READ
;
667 /* Convert header into something recognizable */
668 raw_header_to_header(&rdbh
, &dbh
);
670 num
= jpilot_get_info_size(in
, &rec_size
);
673 return MGU_ERROR_READ
;
676 fseek(in
, dbh
.app_info_offset
, SEEK_SET
);
677 *buf
= ( char * ) malloc(rec_size
);
680 return MGU_OO_MEMORY
;
682 num
= fread(*buf
, rec_size
, 1, in
);
687 return MGU_ERROR_READ
;
692 *buf_size
= rec_size
;
697 /* Shamelessly copied from JPilot (libplugin.c) */
698 static int unpack_header(PC3RecordHeader
*header
, unsigned char *packed_header
) {
704 memcpy(&l
, p
, sizeof(l
));
705 header
->header_len
=ntohl(l
);
708 memcpy(&l
, p
, sizeof(l
));
709 header
->header_version
=ntohl(l
);
712 memcpy(&l
, p
, sizeof(l
));
713 header
->rec_len
=ntohl(l
);
716 memcpy(&l
, p
, sizeof(l
));
717 header
->unique_id
=ntohl(l
);
720 memcpy(&l
, p
, sizeof(l
));
724 memcpy(&(header
->attrib
), p
, sizeof(unsigned char));
725 p
+=sizeof(unsigned char);
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];
736 num
= fread(&l
, sizeof(l
), 1, pc_in
);
743 memcpy(packed_header
, &l
, sizeof(l
));
748 num
= fread(packed_header
+sizeof(l
), len
-sizeof(l
), 1, pc_in
);
755 unpack_header(header
, packed_header
);
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
;
769 num
= read_header(in
, &header
);
772 return MGU_ERROR_READ
;
778 rec_len
= header
.rec_len
;
779 record
= malloc(rec_len
);
781 return MGU_OO_MEMORY
;
783 num
= fread(record
, rec_len
, 1, in
);
787 return MGU_ERROR_READ
;
791 br
->unique_id
= header
.unique_id
;
792 br
->attrib
= header
.attrib
;
800 * Read address file into a linked list. Based on JPilot's
801 * jp_read_DB_files (from libplugin.c)
803 static gint
jpilot_read_db_files( JPilotFile
*pilotFile
, GList
**records
) {
807 int num_records
, recs_returned
, i
, num
, r
;
808 unsigned int offset
, prev_offset
, next_offset
, rec_size
;
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
;
820 mem_rh
= last_mem_rh
= NULL
;
824 if( pilotFile
->path
== NULL
) {
828 in
= fopen( pilotFile
->path
, "rb" );
830 return MGU_OPEN_FILE
;
833 /* Read the database header */
834 num
= fread(&rdbh
, sizeof(RawDBHeader
), 1, in
);
838 return MGU_ERROR_READ
;
845 raw_header_to_header(&rdbh
, &dbh
);
847 /* Read each record entry header */
848 num_records
= dbh
.number_of_records
;
852 for (i
= 1; i
< num_records
+ 1; i
++) {
853 num
= fread(&rh
, sizeof(record_header
), 1, in
);
864 offset
= ((rh
.Offset
[0]*256+rh
.Offset
[1])*256+rh
.Offset
[2])*256+rh
.Offset
[3];
865 if (offset
< prev_offset
) {
868 prev_offset
= offset
;
869 temp_mem_rh
= (mem_rec_header
*)malloc(sizeof(mem_rec_header
));
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
;
882 last_mem_rh
->next
= temp_mem_rh
;
883 last_mem_rh
= temp_mem_rh
;
887 temp_mem_rh
= mem_rh
;
891 find_next_offset(mem_rh
, 0, &next_offset
, &attrib
, &unique_id
);
894 next_offset
= mem_rh
->offset
;
895 attrib
= mem_rh
->attrib
;
896 unique_id
= mem_rh
->unique_id
;
899 fseek(in
, next_offset
, SEEK_SET
);
903 find_next_offset(mem_rh
, fpos
, &next_offset
, &attrib
, &unique_id
);
905 next_offset
= 0xFFFFFF;
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
;
915 rec_size
= next_offset
- fpos
;
916 buf
= malloc(rec_size
);
918 num
= fread(buf
, rec_size
, 1, in
);
926 temp_br
= malloc(sizeof(buf_rec
));
930 temp_br
->rt
= PALM_REC
;
931 temp_br
->unique_id
= unique_id
;
932 temp_br
->attrib
= attrib
;
934 temp_br
->size
= rec_size
;
936 *records
= g_list_append(*records
, temp_br
);
942 free_mem_rec_header(&mem_rh
);
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" );
950 if( pc_in
== NULL
) {
954 while( ! feof( pc_in
) ) {
955 temp_br
= malloc(sizeof(buf_rec
));
959 r
= jpilot_read_next_pc( pc_in
, temp_br
);
960 if ( r
!= MGU_SUCCESS
) {
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
);
971 if ((temp_br
->rt
==DELETED_PALM_REC
) || (temp_br
->rt
==MODIFIED_PALM_REC
)) {
974 while(temp_list
->next
) {
975 temp_list
=temp_list
->next
;
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
;
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.
995 * Return: List of strings.
997 static GList
*jpilot_parse_email( gchar
*buf
) {
1002 gboolean valid
, done
;
1004 valid
= done
= FALSE
;
1009 if( *p
== ' ' || *p
== ',' || *p
== ';' || *p
== '|' || *p
< 32 ) {
1024 em
= g_strndup( st
, len
);
1025 list
= g_list_append( list
, em
);
1033 if( *p
== '@' ) valid
= TRUE
;
1041 #define FULLNAME_BUFSIZE 256
1042 #define EMAIL_BUFSIZE 256
1045 * Process a single label entry field, parsing multiple e-mail address entries.
1046 * Enter: pilotFile JPilot control data.
1047 * labelEntry Label entry data.
1050 static void jpilot_parse_label( JPilotFile
*pilotFile
, gchar
*labelEntry
, ItemPerson
*person
) {
1051 gchar buffer
[ EMAIL_BUFSIZE
];
1057 strcpy( buffer
, labelEntry
);
1058 node
= list
= jpilot_parse_email( buffer
);
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
);
1066 mgu_free_dlist( list
);
1072 * Unpack address, building new data inside cache.
1074 static void jpilot_load_address( JPilotFile
*pilotFile
, buf_rec
*buf
, ItemFolder
*folderInd
[] ) {
1075 struct Address addr
;
1081 gchar fullName
[ FULLNAME_BUFSIZE
];
1087 struct AddressAppInfo
*ai
;
1089 /* Retrieve address */
1090 num
= unpack_Address( & addr
, buf
->buf
, buf
->size
);
1092 addrEnt
= addr
.entry
;
1093 attrib
= buf
->attrib
;
1094 unique_id
= buf
->unique_id
;
1095 cat_id
= attrib
& 0x0F;
1098 if( addrEnt
[ IND_LABEL_FIRSTNAME
] ) {
1099 strcat( fullName
, addrEnt
[ IND_LABEL_FIRSTNAME
] );
1102 if( addrEnt
[ IND_LABEL_LASTNAME
] ) {
1103 strcat( fullName
, " " );
1104 strcat( fullName
, addrEnt
[ IND_LABEL_LASTNAME
] );
1106 g_strchug( fullName
);
1107 g_strchomp( fullName
);
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
);
1115 extID
= g_strdup_printf( "%d", unique_id
);
1116 addritem_person_set_external_id( person
, extID
);
1120 /* Pointer to address metadata. */
1121 ai
= & pilotFile
->addrInfo
;
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
++ ) {
1127 ind
= indPhoneLbl
[k
];
1129 * fprintf( stdout, "%d : %d : %20s : %s\n", k, ind,
1130 * ai->phoneLabels[ind], addrEnt[3+k] );
1132 if( indPhoneLbl
[k
] == IND_PHONE_EMAIL
) {
1133 labelEntry
= addrEnt
[ OFFSET_PHONE_LABEL
+ k
];
1134 jpilot_parse_label( pilotFile
, labelEntry
, person
);
1138 /* Add entry for each custom label */
1139 node
= pilotFile
->labelInd
;
1142 ind
= GPOINTER_TO_INT( node
->data
);
1145 * fprintf( stdout, "%d : %20s : %s\n", ind, ai->labels[ind],
1148 labelEntry
= addrEnt
[ind
];
1149 jpilot_parse_label( pilotFile
, labelEntry
, person
);
1152 node
= g_list_next( node
);
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
);
1161 /* Add to root folder */
1162 addrcache_add_person( pilotFile
->addressCache
, person
);
1165 addritem_free_item_person( person
);
1172 * Free up address list.
1174 static void jpilot_free_addrlist( GList
*records
) {
1183 node
= g_list_next( node
);
1187 g_list_free( records
);
1191 * Read address file into address cache.
1193 static gint
jpilot_read_file( JPilotFile
*pilotFile
) {
1195 GList
*records
= NULL
;
1198 ItemFolder
*folderInd
[ JPILOT_NUM_CATEG
];
1200 retVal
= jpilot_read_db_files( pilotFile
, &records
);
1201 if( retVal
!= MGU_SUCCESS
) {
1202 jpilot_free_addrlist( records
);
1206 /* Build array of pointers to categories */
1208 node
= addrcache_get_list_folder( pilotFile
->addressCache
);
1210 if( i
< JPILOT_NUM_CATEG
) {
1211 folderInd
[i
] = node
->data
;
1213 node
= g_list_next( node
);
1217 /* Load all addresses, free up old stuff as we go */
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
);
1229 node
= g_list_next( node
);
1233 g_list_free( records
);
1240 * Read metadata from file.
1242 static gint
jpilot_read_metadata( JPilotFile
*pilotFile
) {
1244 unsigned int rec_size
;
1248 g_return_val_if_fail( pilotFile
!= NULL
, -1 );
1250 pilotFile
->readMetadata
= FALSE
;
1251 addrcache_clear( pilotFile
->addressCache
);
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
;
1260 num
= unpack_AddressAppInfo( &pilotFile
->addrInfo
, buf
, rec_size
);
1265 pilotFile
->retVal
= MGU_ERROR_READ
;
1266 return pilotFile
->retVal
;
1269 pilotFile
->readMetadata
= TRUE
;
1270 pilotFile
->retVal
= MGU_SUCCESS
;
1271 return pilotFile
->retVal
;
1275 * Setup labels and indexes from metadata.
1276 * Return: TRUE is setup successfully.
1278 static gboolean
jpilot_setup_labels( JPilotFile
*pilotFile
) {
1279 gboolean retVal
= FALSE
;
1280 struct AddressAppInfo
*ai
;
1283 g_return_val_if_fail( pilotFile
!= NULL
, -1 );
1285 /* Release indexes */
1286 node
= pilotFile
->labelInd
;
1289 node
= g_list_next( node
);
1291 pilotFile
->labelInd
= NULL
;
1293 if( pilotFile
->readMetadata
) {
1294 ai
= & pilotFile
->addrInfo
;
1295 node
= pilotFile
->customLabels
;
1297 gchar
*lbl
= node
->data
;
1300 for( i
= 0; i
< JPILOT_NUM_LABELS
; i
++ ) {
1301 gchar
*labelName
= ai
->labels
[i
];
1302 if( g_strcasecmp( labelName
, lbl
) == 0 ) {
1307 pilotFile
->labelInd
= g_list_append( pilotFile
->labelInd
, GINT_TO_POINTER(ind
) );
1308 node
= g_list_next( node
);
1316 * Load list with character strings of label names.
1318 GList
*jpilot_load_label( JPilotFile
*pilotFile
, GList
*labelList
) {
1321 g_return_val_if_fail( pilotFile
!= NULL
, NULL
);
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
];
1328 labelList
= g_list_append( labelList
, g_strdup( labelName
) );
1331 labelList
= g_list_append( labelList
, g_strdup( "" ) );
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.
1343 gchar
*jpilot_get_category_name( JPilotFile
*pilotFile
, gint catID
) {
1344 gchar
*catName
= NULL
;
1346 g_return_val_if_fail( pilotFile
!= NULL
, NULL
);
1348 if( pilotFile
->readMetadata
) {
1349 struct AddressAppInfo
*ai
= & pilotFile
->addrInfo
;
1350 struct CategoryAppInfo
*cat
= & ai
->category
;
1351 if( catID
< 0 || catID
> JPILOT_NUM_CATEG
) {
1354 catName
= g_strdup( cat
->name
[catID
] );
1357 if( ! catName
) catName
= g_strdup( "" );
1362 * Load list with character strings of phone label names.
1364 GList
*jpilot_load_phone_label( JPilotFile
*pilotFile
, GList
*labelList
) {
1367 g_return_val_if_fail( pilotFile
!= NULL
, NULL
);
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
];
1374 labelList
= g_list_append( labelList
, g_strdup( labelName
) );
1377 labelList
= g_list_append( labelList
, g_strdup( "" ) );
1385 * Load list with character strings of label names. Only none blank names
1387 * Return: list of labels. Should by g_free()'d when done.
1389 GList
*jpilot_load_custom_label( JPilotFile
*pilotFile
, GList
*labelList
) {
1392 g_return_val_if_fail( pilotFile
!= NULL
, NULL
);
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
];
1399 g_strchomp( labelName
);
1400 g_strchug( labelName
);
1401 if( *labelName
!= '\0' ) {
1402 labelList
= g_list_append( labelList
, g_strdup( labelName
) );
1411 * Load list with character strings of category names.
1413 GList
*jpilot_get_category_list( JPilotFile
*pilotFile
) {
1414 GList
*catList
= NULL
;
1417 g_return_val_if_fail( pilotFile
!= NULL
, NULL
);
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
];
1425 catList
= g_list_append( catList
, g_strdup( catName
) );
1428 catList
= g_list_append( catList
, g_strdup( "" ) );
1436 * Build folder for each category.
1438 static void jpilot_build_category_list( JPilotFile
*pilotFile
) {
1439 struct AddressAppInfo
*ai
= & pilotFile
->addrInfo
;
1440 struct CategoryAppInfo
*cat
= & ai
->category
;
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
);
1452 * Remove empty folders (categories).
1454 static void jpilot_remove_empty( JPilotFile
*pilotFile
) {
1460 listFolder
= addrcache_get_list_folder( pilotFile
->addressCache
);
1464 ItemFolder
*folder
= node
->data
;
1465 if( ADDRITEM_NAME(folder
) == NULL
|| *ADDRITEM_NAME(folder
) == '\0' ) {
1466 if( folder
->listPerson
) {
1467 /* Give name to folder */
1469 sprintf( name
, "? %d", i
);
1470 addritem_folder_set_name( folder
, name
);
1473 /* Mark for removal */
1474 remList
= g_list_append( remList
, folder
);
1477 node
= g_list_next( node
);
1482 ItemFolder
*folder
= node
->data
;
1483 addrcache_remove_folder( pilotFile
->addressCache
, folder
);
1484 node
= g_list_next( node
);
1486 g_list_free( remList
);
1490 * ============================================================================================
1491 * Read file into list. Main entry point
1492 * Return: TRUE if file read successfully.
1493 * ============================================================================================
1495 gint
jpilot_read_data( JPilotFile
*pilotFile
) {
1496 g_return_val_if_fail( pilotFile
!= NULL
, -1 );
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
;
1515 return pilotFile
->retVal
;
1519 * Return link list of persons.
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
);
1527 * Return link list of folders. This is always NULL since there are
1528 * no folders in GnomeCard.
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
);
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.
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
);
1548 * Check label list for specified label.
1550 gint
jpilot_check_label( struct AddressAppInfo
*ai
, gchar
*lblCheck
) {
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
];
1559 if( strlen( lblName
) ) {
1560 if( g_strcasecmp( lblName
, lblCheck
) == 0 ) return i
;
1568 * Validate that all parameters specified.
1569 * Return: TRUE if data is good.
1571 gboolean
jpilot_validate( JPilotFile
*pilotFile
) {
1575 g_return_val_if_fail( pilotFile
!= NULL
, FALSE
);
1578 if( pilotFile
->path
) {
1579 if( strlen( pilotFile
->path
) < 1 ) retVal
= FALSE
;
1584 name
= jpilot_get_name( pilotFile
);
1586 if( strlen( name
) < 1 ) retVal
= FALSE
;
1594 #define WORK_BUFLEN 1024
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.
1601 gchar
*jpilot_find_pilotdb( void ) {
1603 gchar str
[ WORK_BUFLEN
];
1607 homedir
= g_get_home_dir();
1608 if( ! homedir
) return g_strdup( "" );
1610 strcpy( str
, homedir
);
1611 len
= strlen( str
);
1613 if( str
[ len
-1 ] != G_DIR_SEPARATOR
) {
1614 str
[ len
] = G_DIR_SEPARATOR
;
1615 str
[ ++len
] = '\0';
1618 strcat( str
, JPILOT_DBHOME_DIR
);
1619 strcat( str
, G_DIR_SEPARATOR_S
);
1620 strcat( str
, JPILOT_DBHOME_FILE
);
1622 /* Attempt to open */
1623 if( ( fp
= fopen( str
, "rb" ) ) != NULL
) {
1627 /* Truncate filename */
1630 return g_strdup( str
);
1634 * Attempt to read file, testing for valid JPilot format.
1635 * Return: TRUE if file appears to be valid format.
1637 gint
jpilot_test_read_file( const gchar
*fileSpec
) {
1638 JPilotFile
*pilotFile
;
1642 pilotFile
= jpilot_create_path( fileSpec
);
1643 retVal
= jpilot_read_metadata( pilotFile
);
1644 jpilot_free( pilotFile
);
1648 retVal
= MGU_NO_FILE
;
1654 * Check whether label is in custom labels.
1655 * Return: TRUE if found.
1657 gboolean
jpilot_test_custom_label( JPilotFile
*pilotFile
, const gchar
*labelName
) {
1661 g_return_val_if_fail( pilotFile
!= NULL
, FALSE
);
1665 node
= pilotFile
->customLabels
;
1667 if( g_strcasecmp( labelName
, ( gchar
* ) node
->data
) == 0 ) {
1671 node
= g_list_next( node
);
1678 * Test whether pilot link library installed.
1679 * Return: TRUE if library available.
1681 gboolean
jpilot_test_pilot_lib( void ) {
1685 #endif /* USE_JPILOT */