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