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