2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2001-2015 Match Grun and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
20 * Functions necessary to access JPilot database files.
21 * JPilot is Copyright(c) by Judd Montgomery.
22 * Visit http://www.jpilot.org for more details.
27 #include "claws-features.h"
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>
44 # include <libpisock/pi-version.h>
47 # include <pi-appinfo.h>
48 # include <pi-address.h>
49 # include <pi-version.h>
54 #include "addrcache.h"
57 #include "adbookbase.h"
60 #define JPILOT_DBHOME_DIR ".jpilot"
61 #define JPILOT_DBHOME_FILE "AddressDB.pdb"
62 #define PILOT_LINK_LIB_NAME "libpisock.so"
64 #define IND_LABEL_LASTNAME 0 /* Index of last name in address data */
65 #define IND_LABEL_FIRSTNAME 1 /* Index of first name in address data */
66 #define IND_PHONE_EMAIL 4 /* Index of E-Mail address in phone labels */
67 #define OFFSET_PHONE_LABEL 3 /* Offset to phone data in address data */
68 #define IND_CUSTOM_LABEL 14 /* Offset to custom label names */
69 #define NUM_CUSTOM_LABEL 4 /* Number of custom labels */
71 /* Shamelessly copied from JPilot (libplugin.h) */
73 unsigned char db_name
[32];
74 unsigned char flags
[2];
75 unsigned char version
[2];
76 unsigned char creation_time
[4];
77 unsigned char modification_time
[4];
78 unsigned char backup_time
[4];
79 unsigned char modification_number
[4];
80 unsigned char app_info_offset
[4];
81 unsigned char sort_info_offset
[4];
82 unsigned char type
[4];/*Database ID */
83 unsigned char creator_id
[4];/*Application ID */
84 unsigned char unique_id_seed
[4];
85 unsigned char next_record_list_id
[4];
86 unsigned char number_of_records
[2];
89 /* Shamelessly copied from JPilot (libplugin.h) */
95 time_t modification_time
;
97 unsigned int modification_number
;
98 unsigned int app_info_offset
;
99 unsigned int sort_info_offset
;
100 char type
[5];/*Database ID */
101 char creator_id
[5];/*Application ID */
102 char unique_id_seed
[5];
103 unsigned int next_record_list_id
;
104 unsigned int number_of_records
;
107 /* Shamelessly copied from JPilot (libplugin.h) */
109 unsigned char Offset
[4]; /*4 bytes offset from BOF to record */
110 unsigned char attrib
;
111 unsigned char unique_ID
[3];
114 /* Shamelessly copied from JPilot (libplugin.h) */
115 typedef struct mem_rec_header_s
{
116 unsigned int rec_num
;
118 unsigned int unique_id
;
119 unsigned char attrib
;
120 struct mem_rec_header_s
*next
;
123 /* Shamelessly copied from JPilot (libplugin.h) */
124 #define SPENT_PC_RECORD_BIT 256
128 MODIFIED_PALM_REC
= 101L,
129 DELETED_PALM_REC
= 102L,
131 DELETED_PC_REC
= SPENT_PC_RECORD_BIT
+ 104L,
132 DELETED_DELETED_PALM_REC
= SPENT_PC_RECORD_BIT
+ 105L
135 /* Shamelessly copied from JPilot (libplugin.h) */
138 unsigned int unique_id
;
139 unsigned char attrib
;
144 /* Shamelessly copied from JPilot (libplugin.h) */
146 unsigned long header_len
;
147 unsigned long header_version
;
148 unsigned long rec_len
;
149 unsigned long unique_id
;
150 unsigned long rt
; /* Record Type */
151 unsigned char attrib
;
159 gboolean convert_charcode
= TRUE
;
161 static const gchar
*jpilot_get_charset(void)
163 static const gchar
*charset
= NULL
;
166 charset
= g_getenv("PILOT_CHARSET");
175 * Create new pilot file object.
176 * \return Initialized JPilot file object.
178 JPilotFile
*jpilot_create() {
179 JPilotFile
*pilotFile
;
180 pilotFile
= g_new0( JPilotFile
, 1 );
181 pilotFile
->type
= ADBOOKTYPE_JPILOT
;
182 pilotFile
->addressCache
= addrcache_create();
183 pilotFile
->retVal
= MGU_SUCCESS
;
185 pilotFile
->file
= NULL
;
186 pilotFile
->path
= NULL
;
187 pilotFile
->readMetadata
= FALSE
;
188 pilotFile
->customLabels
= NULL
;
189 pilotFile
->labelInd
= NULL
;
190 pilotFile
->havePC3
= FALSE
;
191 pilotFile
->pc3ModifyTime
= 0;
196 * Create new pilot file object for specified file.
197 * \param path Path to JPilot address book.
198 * \return Initialized JPilot file object.
200 JPilotFile
*jpilot_create_path( const gchar
*path
) {
201 JPilotFile
*pilotFile
;
202 pilotFile
= jpilot_create();
203 jpilot_set_file( pilotFile
, path
);
210 void jpilot_set_name( JPilotFile
* pilotFile
, const gchar
*value
) {
211 cm_return_if_fail( pilotFile
!= NULL
);
212 addrcache_set_name( pilotFile
->addressCache
, value
);
214 void jpilot_set_file( JPilotFile
* pilotFile
, const gchar
*value
) {
215 cm_return_if_fail( pilotFile
!= NULL
);
216 addrcache_refresh( pilotFile
->addressCache
);
217 pilotFile
->readMetadata
= FALSE
;
218 pilotFile
->path
= mgu_replace_string( pilotFile
->path
, value
);
220 void jpilot_set_accessed( JPilotFile
*pilotFile
, const gboolean value
) {
221 cm_return_if_fail( pilotFile
!= NULL
);
222 pilotFile
->addressCache
->accessFlag
= value
;
225 gint
jpilot_get_status( JPilotFile
*pilotFile
) {
226 cm_return_val_if_fail( pilotFile
!= NULL
, -1 );
227 return pilotFile
->retVal
;
229 ItemFolder
*jpilot_get_root_folder( JPilotFile
*pilotFile
) {
230 cm_return_val_if_fail( pilotFile
!= NULL
, NULL
);
231 return addrcache_get_root_folder( pilotFile
->addressCache
);
233 gchar
*jpilot_get_name( JPilotFile
*pilotFile
) {
234 cm_return_val_if_fail( pilotFile
!= NULL
, NULL
);
235 return addrcache_get_name( pilotFile
->addressCache
);
239 * Test whether file was read.
240 * \param pilotFile JPilot control data.
241 * \return <i>TRUE</i> if file was read.
243 gboolean
jpilot_get_read_flag( JPilotFile
*pilotFile
) {
244 cm_return_val_if_fail( pilotFile
!= NULL
, FALSE
);
245 return pilotFile
->addressCache
->dataRead
;
249 * Free up custom label list.
250 * \param pilotFile JPilot control data.
252 void jpilot_clear_custom_labels( JPilotFile
*pilotFile
) {
255 cm_return_if_fail( pilotFile
!= NULL
);
257 /* Release custom labels */
258 mgu_free_dlist( pilotFile
->customLabels
);
259 pilotFile
->customLabels
= NULL
;
261 /* Release indexes */
262 node
= pilotFile
->labelInd
;
265 node
= g_list_next( node
);
267 g_list_free( pilotFile
->labelInd
);
268 pilotFile
->labelInd
= NULL
;
270 /* Force a fresh read */
271 addrcache_refresh( pilotFile
->addressCache
);
275 * Append a custom label, representing an E-Mail address field to the
277 * \param pilotFile JPilot control data.
279 void jpilot_add_custom_label( JPilotFile
*pilotFile
, const gchar
*labelName
) {
280 cm_return_if_fail( pilotFile
!= NULL
);
283 gchar
*labelCopy
= g_strdup( labelName
);
284 g_strstrip( labelCopy
);
285 if( *labelCopy
== '\0' ) {
289 pilotFile
->customLabels
= g_list_append( pilotFile
->customLabels
, labelCopy
);
290 /* Force a fresh read */
291 addrcache_refresh( pilotFile
->addressCache
);
297 * Get list of custom labels.
298 * \param pilotFile JPilot control data.
299 * \return List of labels. Must use g_free() when done.
301 GList
*jpilot_get_custom_labels( JPilotFile
*pilotFile
) {
302 GList
*retVal
= NULL
;
305 cm_return_val_if_fail( pilotFile
!= NULL
, NULL
);
307 node
= pilotFile
->customLabels
;
309 retVal
= g_list_append( retVal
, g_strdup( node
->data
) );
310 node
= g_list_next( node
);
316 * Return filespec of PC3 file corresponding to JPilot PDB file.
317 * \param pilotFile JPilot control data.
318 * \return File specification; should be g_free() when done.
320 static gchar
*jpilot_get_pc3_file( JPilotFile
*pilotFile
) {
324 if( pilotFile
== NULL
) return NULL
;
325 if( pilotFile
->path
== NULL
) return NULL
;
327 fileSpec
= g_strdup( pilotFile
->path
);
328 len
= strlen( fileSpec
);
331 for( i
= len
; i
> 0; i
-- ) {
332 if( *(fileSpec
+ i
) == '.' ) {
339 if( len
- pos
== 3 ) {
340 *r
++ = 'p'; *r
++ = 'c'; *r
= '3';
349 * Save PC3 file time to cache.
350 * \param pilotFile JPilot control data.
351 * \return <i>TRUE</i> if time marked.
353 static gboolean
jpilot_mark_files( JPilotFile
*pilotFile
) {
354 gboolean retVal
= FALSE
;
358 /* Mark PDB file cache */
359 retVal
= addrcache_mark_file( pilotFile
->addressCache
, pilotFile
->path
);
361 /* Now mark PC3 file */
362 pilotFile
->havePC3
= FALSE
;
363 pilotFile
->pc3ModifyTime
= 0;
364 pcFile
= jpilot_get_pc3_file( pilotFile
);
365 if( pcFile
== NULL
) return retVal
;
366 if( 0 == g_stat( pcFile
, &filestat
) ) {
367 pilotFile
->havePC3
= TRUE
;
368 pilotFile
->pc3ModifyTime
= filestat
.st_mtime
;
376 * Check whether JPilot PDB or PC3 file has changed by comparing
378 * \param pilotFile JPilot control data.
379 * \return <i>TRUE</i> if file has changed.
381 static gboolean
jpilot_check_files( JPilotFile
*pilotFile
) {
382 gboolean retVal
= TRUE
;
386 /* Check main file */
387 if( addrcache_check_file( pilotFile
->addressCache
, pilotFile
->path
) )
391 if( ! pilotFile
->havePC3
) return FALSE
;
392 pcFile
= jpilot_get_pc3_file( pilotFile
);
393 if( pcFile
== NULL
) return FALSE
;
395 if( 0 == g_stat( pcFile
, &filestat
) ) {
396 if( filestat
.st_mtime
== pilotFile
->pc3ModifyTime
) retVal
= FALSE
;
403 * Test whether file was modified since last access.
404 * Return: TRUE if file was modified.
406 gboolean
jpilot_get_modified( JPilotFile
*pilotFile
) {
407 cm_return_val_if_fail( pilotFile
!= NULL
, FALSE
);
408 pilotFile
->addressCache
->modified
= jpilot_check_files( pilotFile
);
409 return pilotFile
->addressCache
->modified
;
411 gboolean
jpilot_get_accessed( JPilotFile
*pilotFile
) {
412 cm_return_val_if_fail( pilotFile
!= NULL
, FALSE
);
413 return pilotFile
->addressCache
->accessFlag
;
417 * Free up pilot file object by releasing internal memory.
418 * \param pilotFile JPilot control data.
420 void jpilot_free( JPilotFile
*pilotFile
) {
421 cm_return_if_fail( pilotFile
!= NULL
);
423 /* Release custom labels */
424 jpilot_clear_custom_labels( pilotFile
);
427 addrcache_clear( pilotFile
->addressCache
);
428 addrcache_free( pilotFile
->addressCache
);
430 /* Free internal stuff */
431 g_free( pilotFile
->path
);
433 pilotFile
->file
= NULL
;
434 pilotFile
->path
= NULL
;
435 pilotFile
->readMetadata
= FALSE
;
436 pilotFile
->havePC3
= FALSE
;
437 pilotFile
->pc3ModifyTime
= 0;
439 pilotFile
->type
= ADBOOKTYPE_NONE
;
440 pilotFile
->addressCache
= NULL
;
441 pilotFile
->retVal
= MGU_SUCCESS
;
443 /* Now release file object */
447 /* Shamelessly copied from JPilot (libplugin.c) */
448 static unsigned int bytes_to_bin(unsigned char *bytes
, unsigned int num_bytes
) {
451 for (i
=0;i
<num_bytes
;i
++) {
457 /* Shamelessly copied from JPilot (utils.c) */
458 /* These next 2 functions were copied from pi-file.c in the pilot-link app */
459 /* Exact value of "Jan 1, 1970 0:00:00 GMT" - "Jan 1, 1904 0:00:00 GMT" */
460 #define PILOT_TIME_DELTA (unsigned)(2082844800)
462 static time_t pilot_time_to_unix_time ( unsigned long raw_time
) {
463 return (time_t)(raw_time
- PILOT_TIME_DELTA
);
466 /* Shamelessly copied from JPilot (libplugin.c) */
467 static int raw_header_to_header(RawDBHeader
*rdbh
, DBHeader
*dbh
) {
470 strncpy(dbh
->db_name
, rdbh
->db_name
, 31);
471 dbh
->db_name
[31] = '\0';
472 dbh
->flags
= bytes_to_bin(rdbh
->flags
, 2);
473 dbh
->version
= bytes_to_bin(rdbh
->version
, 2);
474 temp
= bytes_to_bin(rdbh
->creation_time
, 4);
475 dbh
->creation_time
= pilot_time_to_unix_time(temp
);
476 temp
= bytes_to_bin(rdbh
->modification_time
, 4);
477 dbh
->modification_time
= pilot_time_to_unix_time(temp
);
478 temp
= bytes_to_bin(rdbh
->backup_time
, 4);
479 dbh
->backup_time
= pilot_time_to_unix_time(temp
);
480 dbh
->modification_number
= bytes_to_bin(rdbh
->modification_number
, 4);
481 dbh
->app_info_offset
= bytes_to_bin(rdbh
->app_info_offset
, 4);
482 dbh
->sort_info_offset
= bytes_to_bin(rdbh
->sort_info_offset
, 4);
483 strncpy(dbh
->type
, rdbh
->type
, 4);
485 strncpy(dbh
->creator_id
, rdbh
->creator_id
, 4);
486 dbh
->creator_id
[4] = '\0';
487 strncpy(dbh
->unique_id_seed
, rdbh
->unique_id_seed
, 4);
488 dbh
->unique_id_seed
[4] = '\0';
489 dbh
->next_record_list_id
= bytes_to_bin(rdbh
->next_record_list_id
, 4);
490 dbh
->number_of_records
= bytes_to_bin(rdbh
->number_of_records
, 2);
494 /* Shamelessly copied from JPilot (libplugin.c) */
495 /* returns 1 if found */
497 static int find_next_offset( mem_rec_header
*mem_rh
, long fpos
,
498 unsigned int *next_offset
, unsigned char *attrib
, unsigned int *unique_id
)
500 mem_rec_header
*temp_mem_rh
;
501 unsigned char found
= 0;
502 unsigned long found_at
;
505 for (temp_mem_rh
=mem_rh
; temp_mem_rh
; temp_mem_rh
= temp_mem_rh
->next
) {
506 if ((temp_mem_rh
->offset
> fpos
) && (temp_mem_rh
->offset
< found_at
)) {
507 found_at
= temp_mem_rh
->offset
;
508 /* *attrib = temp_mem_rh->attrib; */
509 /* *unique_id = temp_mem_rh->unique_id; */
511 if ((temp_mem_rh
->offset
== fpos
)) {
513 *attrib
= temp_mem_rh
->attrib
;
514 *unique_id
= temp_mem_rh
->unique_id
;
517 *next_offset
= found_at
;
521 /* Shamelessly copied from JPilot (libplugin.c) */
522 static void free_mem_rec_header(mem_rec_header
**mem_rh
) {
523 mem_rec_header
*h
, *next_h
;
524 for (h
=*mem_rh
; h
; h
=next_h
) {
531 /* Shamelessly copied from JPilot (libplugin.c) */
533 static int jpilot_get_info_size( FILE *in
, int *size
) {
540 fseek(in
, 0, SEEK_SET
);
541 r
= fread(&rdbh
, sizeof(RawDBHeader
), 1, in
);
543 return MGU_ERROR_READ
;
548 raw_header_to_header(&rdbh
, &dbh
);
549 if (dbh
.app_info_offset
==0) {
553 if (dbh
.sort_info_offset
!=0) {
554 *size
= dbh
.sort_info_offset
- dbh
.app_info_offset
;
557 if (dbh
.number_of_records
==0) {
558 fseek(in
, 0, SEEK_END
);
559 *size
=ftell(in
) - dbh
.app_info_offset
;
563 r
= fread(&rh
, sizeof(record_header
), 1, in
);
565 return MGU_ERROR_READ
;
567 offset
= ((rh
.Offset
[0]*256+rh
.Offset
[1])*256+rh
.Offset
[2])*256+rh
.Offset
[3];
568 *size
=offset
- dbh
.app_info_offset
;
574 * Read address file into address list. Based on JPilot's
575 * libplugin.c (jp_get_app_info)
577 static gint
jpilot_get_file_info( JPilotFile
*pilotFile
, unsigned char **buf
, int *buf_size
) {
580 unsigned int rec_size
;
584 if( ( !buf_size
) || ( ! buf
) ) {
591 if( pilotFile
->path
) {
592 in
= g_fopen( pilotFile
->path
, "rb" );
594 return MGU_OPEN_FILE
;
601 num
= fread( &rdbh
, sizeof( RawDBHeader
), 1, in
);
605 return MGU_ERROR_READ
;
613 /* Convert header into something recognizable */
614 raw_header_to_header(&rdbh
, &dbh
);
616 num
= jpilot_get_info_size(in
, &rec_size
);
619 return MGU_ERROR_READ
;
622 if (fseek(in
, dbh
.app_info_offset
, SEEK_SET
) < 0) {
624 return MGU_ERROR_READ
;
626 *buf
= ( char * ) malloc(rec_size
);
629 return MGU_OO_MEMORY
;
631 num
= fread(*buf
, rec_size
, 1, in
);
636 return MGU_ERROR_READ
;
641 *buf_size
= rec_size
;
646 /* Shamelessly copied from JPilot (libplugin.c) */
647 static int unpack_header(PC3RecordHeader
*header
, unsigned char *packed_header
) {
653 memcpy(&l
, p
, sizeof(l
));
654 header
->header_len
=ntohl(l
);
657 memcpy(&l
, p
, sizeof(l
));
658 header
->header_version
=ntohl(l
);
661 memcpy(&l
, p
, sizeof(l
));
662 header
->rec_len
=ntohl(l
);
665 memcpy(&l
, p
, sizeof(l
));
666 header
->unique_id
=ntohl(l
);
669 memcpy(&l
, p
, sizeof(l
));
673 memcpy(&(header
->attrib
), p
, sizeof(unsigned char));
674 p
+=sizeof(unsigned char);
679 /* Shamelessly copied from JPilot (libplugin.c) */
680 static int read_header(FILE *pc_in
, PC3RecordHeader
*header
) {
683 unsigned char packed_header
[256];
686 memset(header
, 0, sizeof(PC3RecordHeader
));
688 num
= fread(&l
, sizeof(l
), 1, pc_in
);
695 memcpy(packed_header
, &l
, sizeof(l
));
697 if (len
> 255 || len
< sizeof(l
)) {
700 num
= fread(packed_header
+sizeof(l
), len
-sizeof(l
), 1, pc_in
);
707 unpack_header(header
, packed_header
);
712 * Read next record from PC3 file. Based on JPilot function
713 * <code>pc_read_next_rec()</code> (libplugin.c)
715 * \param in File handle.
716 * \param br Record buffer.
717 * \return Status/error code. <code>MGU_SUCCESS</code> if data read
720 static gint
jpilot_read_next_pc( FILE *in
, buf_rec
*br
) {
721 PC3RecordHeader header
;
728 num
= read_header( in
, &header
);
731 return MGU_ERROR_READ
;
732 else if( feof( in
) )
737 rec_len
= header
.rec_len
;
738 record
= malloc( rec_len
);
740 return MGU_OO_MEMORY
;
742 num
= fread( record
, rec_len
, 1, in
);
746 return MGU_ERROR_READ
;
750 br
->unique_id
= header
.unique_id
;
751 br
->attrib
= header
.attrib
;
759 * Read address file into a linked list. Based on JPilot function
760 * <code>jp_read_DB_files()</code> (from libplugin.c)
762 * \param pilotFile JPilot control data.
763 * \param records Pointer to linked list of records read.
764 * \return Status/error code. <code>MGU_SUCCESS</code> if data read
767 static gint
jpilot_read_db_files( JPilotFile
*pilotFile
, GList
**records
) {
771 int num_records
, recs_returned
, i
, num
, r
;
772 unsigned int offset
, prev_offset
, next_offset
= 0, rec_size
;
774 long fpos
; /*file position indicator */
775 unsigned char attrib
= '\0';
776 unsigned int unique_id
= 0;
777 mem_rec_header
*mem_rh
, *temp_mem_rh
, *last_mem_rh
;
784 mem_rh
= last_mem_rh
= NULL
;
788 if( pilotFile
->path
== NULL
) {
792 in
= g_fopen( pilotFile
->path
, "rb" );
794 return MGU_OPEN_FILE
;
797 /* Read the database header */
798 num
= fread( &rdbh
, sizeof( RawDBHeader
), 1, in
);
802 return MGU_ERROR_READ
;
809 raw_header_to_header( &rdbh
, &dbh
);
811 /* Read each record entry header */
812 num_records
= dbh
.number_of_records
;
816 for( i
= 1; i
< num_records
+ 1; i
++ ) {
817 num
= fread( &rh
, sizeof( record_header
), 1, in
);
825 free_mem_rec_header( &mem_rh
);
831 ( ( rh
.Offset
[0] * 256 + rh
.Offset
[1] ) * 256
832 + rh
.Offset
[2] ) * 256
834 if( offset
< prev_offset
) {
837 prev_offset
= offset
;
838 temp_mem_rh
= ( mem_rec_header
* ) malloc( sizeof( mem_rec_header
) );
839 if( ! temp_mem_rh
) {
842 temp_mem_rh
->next
= NULL
;
843 temp_mem_rh
->rec_num
= i
;
844 temp_mem_rh
->offset
= offset
;
845 temp_mem_rh
->attrib
= rh
.attrib
;
846 temp_mem_rh
->unique_id
=
847 ( rh
.unique_ID
[0] * 256 + rh
.unique_ID
[1] ) * 256
849 if( mem_rh
== NULL
) {
850 mem_rh
= temp_mem_rh
;
851 last_mem_rh
= temp_mem_rh
;
854 last_mem_rh
->next
= temp_mem_rh
;
855 last_mem_rh
= temp_mem_rh
;
859 temp_mem_rh
= mem_rh
;
864 mem_rh
, 0, &next_offset
, &attrib
, &unique_id
);
868 next_offset
= mem_rh
->offset
;
869 attrib
= mem_rh
->attrib
;
870 unique_id
= mem_rh
->unique_id
;
873 if (fseek( in
, next_offset
, SEEK_SET
) < 0) {
874 free_mem_rec_header( &mem_rh
);
876 return MGU_ERROR_READ
;
878 while( ! feof( in
) ) {
882 mem_rh
, fpos
, &next_offset
, &attrib
,
885 next_offset
= 0xFFFFFF;
887 attrib
= temp_mem_rh
->attrib
;
888 unique_id
= temp_mem_rh
->unique_id
;
889 if ( temp_mem_rh
->next
) {
890 temp_mem_rh
= temp_mem_rh
->next
;
891 next_offset
= temp_mem_rh
->offset
;
895 rec_size
= next_offset
- fpos
;
896 buf
= malloc( rec_size
);
898 num
= fread( buf
, rec_size
, 1, in
);
906 temp_br
= malloc( sizeof( buf_rec
) );
911 temp_br
->rt
= PALM_REC
;
912 temp_br
->unique_id
= unique_id
;
913 temp_br
->attrib
= attrib
;
915 temp_br
->size
= rec_size
;
917 *records
= g_list_append( *records
, temp_br
);
923 free_mem_rec_header( &mem_rh
);
925 /* Read the PC3 file, if present */
926 pcFile
= jpilot_get_pc3_file( pilotFile
);
927 if( pcFile
== NULL
) return MGU_SUCCESS
;
928 pc_in
= g_fopen( pcFile
, "rb");
931 if( pc_in
== NULL
) {
935 while( ! feof( pc_in
) ) {
938 temp_br
= malloc( sizeof( buf_rec
) );
942 r
= jpilot_read_next_pc( pc_in
, temp_br
);
943 if( r
!= MGU_SUCCESS
) {
944 if( (r
!= MGU_EOF
) && (r
!= MGU_ERROR_READ
) ) {
945 free( temp_br
->buf
);
952 if( ( temp_br
->rt
!= DELETED_PC_REC
)
953 && ( temp_br
->rt
!= DELETED_PALM_REC
)
954 && ( temp_br
->rt
!= MODIFIED_PALM_REC
)
955 && ( temp_br
->rt
!= DELETED_DELETED_PALM_REC
) )
957 *records
= g_list_append( *records
, temp_br
);
962 if( ( temp_br
->rt
== DELETED_PALM_REC
)
963 || ( temp_br
->rt
== MODIFIED_PALM_REC
) )
965 temp_list
= *records
;
967 while( temp_list
->next
) {
968 temp_list
=temp_list
->next
;
971 for( ; temp_list
; temp_list
=temp_list
->prev
) {
972 if( ( ( buf_rec
* )temp_list
->data
)->unique_id
==
973 temp_br
->unique_id
) {
974 ( ( buf_rec
* )temp_list
->data
)->rt
=
981 free( temp_br
->buf
);
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 * \param buf Buffer to process.
996 * \return List of strings.
998 static GList
*jpilot_parse_email( gchar
*buf
) {
1003 gboolean valid
, done
;
1005 valid
= done
= FALSE
;
1010 if( *p
== ' ' || *p
== ',' || *p
== ';' || *p
== '|' || *p
< 32 ) {
1025 em
= g_strndup( st
, len
);
1026 list
= g_list_append( list
, em
);
1034 if( *p
== '@' ) valid
= TRUE
;
1042 #define FULLNAME_BUFSIZE 256
1043 #define EMAIL_BUFSIZE 256
1046 * Process a single label entry field, parsing multiple e-mail address entries.
1048 * \param pilotFile JPilot control data.
1049 * \param labelEntry Label entry data.
1050 * \param person Person.
1052 static void jpilot_parse_label( JPilotFile
*pilotFile
, gchar
*labelEntry
, ItemPerson
*person
) {
1053 gchar buffer
[ EMAIL_BUFSIZE
];
1059 g_strlcpy( buffer
, labelEntry
, sizeof(buffer
) );
1060 node
= list
= jpilot_parse_email( buffer
);
1062 email
= addritem_create_item_email();
1063 addritem_email_set_address( email
, node
->data
);
1064 if (convert_charcode
) {
1065 gchar
*convertBuff
= NULL
;
1066 convertBuff
= conv_codeset_strdup( labelEntry
,
1067 jpilot_get_charset(),
1070 addritem_email_set_remarks( email
, convertBuff
);
1071 g_free( convertBuff
);
1074 addritem_email_set_remarks(email
, buffer
);
1077 addrcache_id_email( pilotFile
->addressCache
, email
);
1078 addrcache_person_add_email( pilotFile
->addressCache
, person
, email
);
1079 node
= g_list_next( node
);
1081 mgu_free_dlist( list
);
1087 * Unpack address, building new data inside cache.
1088 * \param pilotFile JPilot control data.
1089 * \param buf Record buffer.
1090 * \param folderInd Array of (category) folders to load.
1092 static void jpilot_load_address(
1093 JPilotFile
*pilotFile
, buf_rec
*buf
, ItemFolder
*folderInd
[] )
1095 struct Address addr
;
1101 gchar fullName
[ FULLNAME_BUFSIZE
];
1107 gchar
**firstName
= NULL
;
1108 gchar
**lastName
= NULL
;
1109 #if (PILOT_LINK_MAJOR > 11)
1110 pi_buffer_t
*RecordBuffer
;
1111 RecordBuffer
= pi_buffer_new(buf
->size
);
1113 memcpy(RecordBuffer
->data
, buf
->buf
, buf
->size
);
1114 RecordBuffer
->used
= buf
->size
;
1115 if (unpack_Address(&addr
, RecordBuffer
, address_v1
) == -1) {
1116 pi_buffer_free(RecordBuffer
);
1119 pi_buffer_free(RecordBuffer
);
1123 num
= unpack_Address(&addr
, buf
->buf
, buf
->size
);
1127 #endif /* PILOT_LINK_0_12 */
1129 addrEnt
= addr
.entry
;
1130 attrib
= buf
->attrib
;
1131 unique_id
= buf
->unique_id
;
1132 cat_id
= attrib
& 0x0F;
1135 if( addrEnt
[ IND_LABEL_FIRSTNAME
] ) {
1136 firstName
= g_strsplit( addrEnt
[ IND_LABEL_FIRSTNAME
], "\01", 2 );
1139 if( addrEnt
[ IND_LABEL_LASTNAME
] ) {
1140 lastName
= g_strsplit( addrEnt
[ IND_LABEL_LASTNAME
], "\01", 2 );
1143 if( name_order
== FAMILY_LAST
) {
1144 g_snprintf( fullName
, FULLNAME_BUFSIZE
, "%s %s",
1145 firstName
? firstName
[0] : "",
1146 lastName
? lastName
[0] : "" );
1149 g_snprintf( fullName
, FULLNAME_BUFSIZE
, "%s %s",
1150 lastName
? lastName
[0] : "",
1151 firstName
? firstName
[0] : "" );
1155 g_strfreev( firstName
);
1158 g_strfreev( lastName
);
1161 g_strstrip( fullName
);
1163 if( convert_charcode
) {
1164 gchar
*nameConv
= NULL
;
1165 nameConv
= conv_codeset_strdup( fullName
,
1166 jpilot_get_charset(),
1169 strncpy2( fullName
, nameConv
, FULLNAME_BUFSIZE
);
1173 person
= addritem_create_item_person();
1174 addritem_person_set_common_name( person
, fullName
);
1175 addritem_person_set_first_name( person
, addrEnt
[ IND_LABEL_FIRSTNAME
] );
1176 addritem_person_set_last_name( person
, addrEnt
[ IND_LABEL_LASTNAME
] );
1177 addrcache_id_person( pilotFile
->addressCache
, person
);
1179 extID
= g_strdup_printf( "%d", unique_id
);
1180 addritem_person_set_external_id( person
, extID
);
1184 /* Add entry for each email address listed under phone labels. */
1185 indPhoneLbl
= addr
.phoneLabel
;
1186 for( k
= 0; k
< JPILOT_NUM_ADDR_PHONE
; k
++ ) {
1187 if( indPhoneLbl
[k
] == IND_PHONE_EMAIL
) {
1188 labelEntry
= addrEnt
[ OFFSET_PHONE_LABEL
+ k
];
1189 jpilot_parse_label( pilotFile
, labelEntry
, person
);
1193 /* Add entry for each custom label */
1194 node
= pilotFile
->labelInd
;
1198 ind
= GPOINTER_TO_INT( node
->data
);
1201 * g_print( "%d : %20s : %s\n", ind, ai->labels[ind],
1204 labelEntry
= addrEnt
[ind
];
1205 jpilot_parse_label( pilotFile
, labelEntry
, person
);
1208 node
= g_list_next( node
);
1211 if( person
->listEMail
) {
1212 /* Add to specified category */
1213 addrcache_folder_add_person(
1214 pilotFile
->addressCache
,
1215 folderInd
[cat_id
], person
);
1218 addritem_free_item_person( person
);
1221 /* Free up pointer allocated inside address */
1222 free_Address( & addr
);
1226 * Free up address list.
1227 * \param records List of records to free.
1229 static void jpilot_free_addrlist( GList
*records
) {
1239 node
= g_list_next( node
);
1243 g_list_free( records
);
1247 * Read metadata from file.
1248 * \param pilotFile JPilot control data.
1249 * \return Status/error code. <code>MGU_SUCCESS</code> if data read
1252 static gint
jpilot_read_metadata( JPilotFile
*pilotFile
) {
1254 unsigned int rec_size
;
1258 cm_return_val_if_fail( pilotFile
!= NULL
, -1 );
1260 pilotFile
->readMetadata
= FALSE
;
1261 addrcache_clear( pilotFile
->addressCache
);
1263 /* Read file info */
1264 retVal
= jpilot_get_file_info( pilotFile
, &buf
, &rec_size
);
1265 if( retVal
!= MGU_SUCCESS
) {
1266 pilotFile
->retVal
= retVal
;
1267 return pilotFile
->retVal
;
1270 num
= unpack_AddressAppInfo( &pilotFile
->addrInfo
, buf
, rec_size
);
1275 pilotFile
->retVal
= MGU_ERROR_READ
;
1276 return pilotFile
->retVal
;
1279 pilotFile
->readMetadata
= TRUE
;
1280 pilotFile
->retVal
= MGU_SUCCESS
;
1281 return pilotFile
->retVal
;
1285 * Setup labels and indexes from metadata.
1286 * \param pilotFile JPilot control data.
1287 * \return <i>TRUE</i> is setup successfully.
1289 static gboolean
jpilot_setup_labels( JPilotFile
*pilotFile
) {
1290 gboolean retVal
= FALSE
;
1291 struct AddressAppInfo
*ai
;
1294 cm_return_val_if_fail( pilotFile
!= NULL
, -1 );
1296 /* Release indexes */
1297 node
= pilotFile
->labelInd
;
1300 node
= g_list_next( node
);
1302 pilotFile
->labelInd
= NULL
;
1304 if( pilotFile
->readMetadata
) {
1305 ai
= & pilotFile
->addrInfo
;
1306 node
= pilotFile
->customLabels
;
1308 gchar
*lbl
= node
->data
;
1311 for( i
= 0; i
< JPILOT_NUM_LABELS
; i
++ ) {
1312 gchar
*labelName
= ai
->labels
[i
];
1314 if( convert_charcode
) {
1315 gchar
*convertBuff
= NULL
;
1316 convertBuff
= conv_codeset_strdup( labelName
,
1317 jpilot_get_charset(),
1320 labelName
= convertBuff
;
1324 if( g_utf8_collate( labelName
, lbl
) == 0 ) {
1329 pilotFile
->labelInd
= g_list_append(
1330 pilotFile
->labelInd
, GINT_TO_POINTER(ind
) );
1331 node
= g_list_next( node
);
1339 * Load list with character strings of custom label names. Only none blank
1341 * \param pilotFile JPilot control data.
1342 * \param labelList List of label names to load.
1343 * \return List of label names loaded. Should be freed when done.
1345 GList
*jpilot_load_custom_label( JPilotFile
*pilotFile
, GList
*labelList
) {
1348 cm_return_val_if_fail( pilotFile
!= NULL
, NULL
);
1350 if( pilotFile
->readMetadata
) {
1351 struct AddressAppInfo
*ai
= & pilotFile
->addrInfo
;
1352 for( i
= 0; i
< NUM_CUSTOM_LABEL
; i
++ ) {
1353 gchar
*labelName
= ai
->labels
[i
+IND_CUSTOM_LABEL
];
1355 g_strstrip( labelName
);
1356 if( *labelName
!= '\0' ) {
1357 if( convert_charcode
) {
1358 gchar
*convertBuff
= NULL
;
1359 convertBuff
= conv_codeset_strdup( labelName
,
1360 jpilot_get_charset(),
1363 labelName
= convertBuff
;
1367 labelName
= g_strdup( labelName
);
1369 labelList
= g_list_append( labelList
, labelName
);
1378 * Build folder in address book for each category.
1379 * \param pilotFile JPilot control data.
1381 static void jpilot_build_category_list( JPilotFile
*pilotFile
) {
1382 struct AddressAppInfo
*ai
= & pilotFile
->addrInfo
;
1383 struct CategoryAppInfo
*cat
= & ai
->category
;
1386 for( i
= 0; i
< JPILOT_NUM_CATEG
; i
++ ) {
1387 ItemFolder
*folder
= addritem_create_item_folder();
1389 if( convert_charcode
) {
1390 gchar
*convertBuff
= NULL
;
1391 convertBuff
= conv_codeset_strdup( cat
->name
[i
],
1392 jpilot_get_charset(),
1395 addritem_folder_set_name( folder
, convertBuff
);
1396 g_free( convertBuff
);
1398 addritem_folder_set_name( folder
, cat
->name
[i
] );
1402 addritem_folder_set_name( folder
, cat
->name
[i
] );
1405 addrcache_id_folder( pilotFile
->addressCache
, folder
);
1406 addrcache_add_folder( pilotFile
->addressCache
, folder
);
1411 * Remove empty (category) folders.
1412 * \param pilotFile JPilot control data.
1414 static void jpilot_remove_empty( JPilotFile
*pilotFile
) {
1420 listFolder
= addrcache_get_list_folder( pilotFile
->addressCache
);
1424 ItemFolder
*folder
= node
->data
;
1425 if( ADDRITEM_NAME(folder
) == NULL
|| *ADDRITEM_NAME(folder
) == '\0' ) {
1426 if( folder
->listPerson
) {
1427 /* Give name to folder */
1429 sprintf( name
, "? %d", i
);
1430 addritem_folder_set_name( folder
, name
);
1433 /* Mark for removal */
1434 remList
= g_list_append( remList
, folder
);
1437 node
= g_list_next( node
);
1442 ItemFolder
*folder
= node
->data
;
1443 addrcache_remove_folder( pilotFile
->addressCache
, folder
);
1444 node
= g_list_next( node
);
1446 g_list_free( remList
);
1450 * Read address file into address cache.
1451 * \param pilotFile JPilot control data.
1452 * \return Error/status code. <code>MGU_SUCCESS</code> if data read
1455 static gint
jpilot_read_file( JPilotFile
*pilotFile
) {
1457 GList
*records
= NULL
;
1460 ItemFolder
*folderInd
[ JPILOT_NUM_CATEG
];
1462 /* Read list of records from JPilot files */
1463 retVal
= jpilot_read_db_files( pilotFile
, &records
);
1464 if( retVal
!= MGU_SUCCESS
) {
1465 jpilot_free_addrlist( records
);
1469 /* Setup labels and category folders */
1470 jpilot_setup_labels( pilotFile
);
1471 jpilot_build_category_list( pilotFile
);
1473 /* Build array of pointers to categories */
1475 node
= addrcache_get_list_folder( pilotFile
->addressCache
);
1477 if( i
< JPILOT_NUM_CATEG
) {
1478 folderInd
[i
] = node
->data
;
1480 node
= g_list_next( node
);
1484 /* Load all addresses, free up old stuff as we go */
1488 if( ( br
->rt
!= DELETED_PC_REC
) &&
1489 ( br
->rt
!= DELETED_PALM_REC
) &&
1490 ( br
->rt
!= MODIFIED_PALM_REC
) &&
1491 ( br
->rt
!= DELETED_DELETED_PALM_REC
) ) {
1492 jpilot_load_address( pilotFile
, br
, folderInd
);
1497 node
= g_list_next( node
);
1501 g_list_free( records
);
1503 /* Remove empty category folders */
1504 jpilot_remove_empty( pilotFile
);
1505 jpilot_mark_files( pilotFile
);
1511 * Read file into list. Main entry point
1512 * \param pilotFile JPilot control data.
1513 * \return Error/status code. <code>MGU_SUCCESS</code> if data read
1516 gint
jpilot_read_data( JPilotFile
*pilotFile
) {
1517 const gchar
*cur_locale
;
1519 name_order
= FAMILY_LAST
;
1521 cur_locale
= conv_get_current_locale();
1523 if( g_ascii_strncasecmp( cur_locale
, "hu", 2 ) == 0 ||
1524 g_ascii_strncasecmp( cur_locale
, "ja", 2 ) == 0 ||
1525 g_ascii_strncasecmp( cur_locale
, "ko", 2 ) == 0 ||
1526 g_ascii_strncasecmp( cur_locale
, "vi", 2 ) == 0 ||
1527 g_ascii_strncasecmp( cur_locale
, "zh", 2 ) == 0 ) {
1528 name_order
= FAMILY_FIRST
;
1531 cm_return_val_if_fail( pilotFile
!= NULL
, -1 );
1533 pilotFile
->retVal
= MGU_SUCCESS
;
1534 pilotFile
->addressCache
->accessFlag
= FALSE
;
1535 if( jpilot_check_files( pilotFile
) ) {
1536 addrcache_clear( pilotFile
->addressCache
);
1537 jpilot_read_metadata( pilotFile
);
1538 if( pilotFile
->retVal
== MGU_SUCCESS
) {
1539 pilotFile
->retVal
= jpilot_read_file( pilotFile
);
1540 if( pilotFile
->retVal
== MGU_SUCCESS
) {
1541 pilotFile
->addressCache
->modified
= FALSE
;
1542 pilotFile
->addressCache
->dataRead
= TRUE
;
1546 return pilotFile
->retVal
;
1550 * Return linked list of persons. This is a list of references to ItemPerson
1551 * objects. Do <b>NOT</b> attempt to use the <code>addrbook_free_xxx()</code>
1552 * functions... this will destroy the addressbook data!
1554 * \param pilotFile JPilot control data.
1555 * \return List of persons.
1557 GList
*jpilot_get_list_person( JPilotFile
*pilotFile
) {
1558 cm_return_val_if_fail( pilotFile
!= NULL
, NULL
);
1559 return addrcache_get_list_person( pilotFile
->addressCache
);
1563 * Return linked list of folders. This is a list of references to non-empty
1564 * category folders. Do <b>NOT</b> attempt to use the
1565 * <code>addrbook_free_xxx()</code> functions... this will destroy the
1568 * \param pilotFile JPilot control data.
1569 * \return List of ItemFolder objects. This should not be freed.
1571 GList
*jpilot_get_list_folder( JPilotFile
*pilotFile
) {
1572 cm_return_val_if_fail( pilotFile
!= NULL
, NULL
);
1573 return addrcache_get_list_folder( pilotFile
->addressCache
);
1577 * Return linked list of all persons. Note that the list contains references
1578 * to items. Do <b>NOT</b> attempt to use the <code>addrbook_free_xxx()</code>
1579 * functions... this will destroy the addressbook data!
1581 * \param pilotFile JPilot control data.
1582 * \return List of items, or NULL if none.
1584 GList
*jpilot_get_all_persons( JPilotFile
*pilotFile
) {
1585 cm_return_val_if_fail( pilotFile
!= NULL
, NULL
);
1586 return addrcache_get_all_persons( pilotFile
->addressCache
);
1589 #define WORK_BUFLEN 1024
1592 * Attempt to find a valid JPilot file.
1593 * \param pilotFile JPilot control data.
1594 * \return Filename, or home directory if not found, or empty string if
1595 * no home. Filename should be <code>g_free()</code> when done.
1597 gchar
*jpilot_find_pilotdb( void ) {
1598 const gchar
*homedir
;
1599 gchar str
[ WORK_BUFLEN
+ 1 ];
1603 homedir
= get_home_dir();
1604 if( ! homedir
) return g_strdup( "" );
1606 g_strlcpy( str
, homedir
, sizeof(str
));
1607 len
= strlen( str
);
1609 if( str
[ len
-1 ] != G_DIR_SEPARATOR
) {
1610 str
[ len
] = G_DIR_SEPARATOR
;
1611 str
[ ++len
] = '\0';
1614 strncat( str
, JPILOT_DBHOME_DIR
, WORK_BUFLEN
- strlen(str
) );
1615 strncat( str
, G_DIR_SEPARATOR_S
, WORK_BUFLEN
- strlen(str
) );
1616 strncat( str
, JPILOT_DBHOME_FILE
, WORK_BUFLEN
- strlen(str
) );
1618 /* Attempt to open */
1619 if( ( fp
= g_fopen( str
, "rb" ) ) != NULL
) {
1623 /* Truncate filename */
1626 return g_strdup( str
);
1630 * Check whether label is in list of custom labels.
1631 * \param pilotFile JPilot control data.
1632 * \param labelName to test.
1633 * \return <i>TRUE</i> if found.
1635 gboolean
jpilot_test_custom_label( JPilotFile
*pilotFile
, const gchar
*labelName
) {
1639 cm_return_val_if_fail( pilotFile
!= NULL
, FALSE
);
1643 node
= pilotFile
->customLabels
;
1645 if( g_utf8_collate( labelName
, ( gchar
* ) node
->data
) == 0 ) {
1649 node
= g_list_next( node
);
1656 * Test whether pilot link library installed.
1657 * \return <i>TRUE</i> if library available.
1659 gboolean
jpilot_test_pilot_lib( void ) {
1663 #endif /* USE_JPILOT */