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