You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
587 lines
17 KiB
587 lines
17 KiB
/* $Id: contact.c,v 1.20 2009/02/23 11:59:16 nicholas Exp $ */
|
|
|
|
/*******************************************************************************
|
|
* contact.c: Translate Palm contact data formats
|
|
* Derived from a module of J-Pilot http://jpilot.org (jp-contact.c 1.10)
|
|
*
|
|
* Rewrite Copyright 2006, 2007 Judd Montgomery
|
|
* Rewrite Copyright 2004, 2005 Joseph Carter
|
|
* Copyright 2003, 2004 Judd Montgomery
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
******************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "pi-macros.h"
|
|
#include "pi-contact.h"
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* Function: free_Contact
|
|
*
|
|
* Summary: Free the members of a contact structure
|
|
*
|
|
* Parameters: None
|
|
*
|
|
* Returns: Nothing
|
|
*
|
|
***********************************************************************/
|
|
void free_Contact(struct Contact *c)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_CONTACT_ENTRIES; i++)
|
|
if (c->entry[i])
|
|
free(c->entry[i]);
|
|
for (i = 0; i < MAX_CONTACT_BLOBS; i++) {
|
|
if (c->blob[i]) {
|
|
if (c->blob[i]->data)
|
|
free(c->blob[i]->data);
|
|
|
|
free(c->blob[i]);
|
|
}
|
|
}
|
|
|
|
if (c->picture) free(c->picture);
|
|
}
|
|
|
|
#define hi(x) (((x) >> 4) & 0x0f)
|
|
#define lo(x) ((x) & 0x0f)
|
|
#define pair(x,y) (((x) << 4) | (y))
|
|
|
|
/***********************************************************************
|
|
*
|
|
* Function: unpack_Contact
|
|
*
|
|
* Summary: Fill in the contact structure based on the raw record
|
|
* data
|
|
*
|
|
* Parameters: None
|
|
*
|
|
* Returns: -1 on error,
|
|
* The length of the data used from the buffer on success
|
|
*
|
|
***********************************************************************/
|
|
int unpack_Contact(struct Contact *c, pi_buffer_t *buf, contactsType type)
|
|
{
|
|
unsigned long contents1;
|
|
unsigned long contents2;
|
|
unsigned char *Pbuf, *record;
|
|
int i, field_num, len;
|
|
unsigned int packed_date;
|
|
unsigned int blob_count;
|
|
|
|
if (buf == NULL || buf->data == NULL || buf->used < 17)
|
|
return -1;
|
|
|
|
if (type != contacts_v10 && type != contacts_v11) {
|
|
/* Don't support anything else yet */
|
|
return -1;
|
|
}
|
|
|
|
record = Pbuf = buf->data;
|
|
len = buf->used;
|
|
|
|
for (i=0; i<MAX_CONTACT_BLOBS; i++) {
|
|
c->blob[i]=NULL;
|
|
}
|
|
c->picture=NULL;
|
|
|
|
c->showPhone = hi(get_byte(Pbuf));
|
|
c->phoneLabel[6] = lo(get_byte(Pbuf));
|
|
c->phoneLabel[5] = hi(get_byte(Pbuf + 1));
|
|
c->phoneLabel[4] = lo(get_byte(Pbuf + 1));
|
|
c->phoneLabel[3] = hi(get_byte(Pbuf + 2));
|
|
c->phoneLabel[2] = lo(get_byte(Pbuf + 2));
|
|
c->phoneLabel[1] = hi(get_byte(Pbuf + 3));
|
|
c->phoneLabel[0] = lo(get_byte(Pbuf + 3));
|
|
|
|
c->addressLabel[2] = lo(get_byte(Pbuf + 4));
|
|
c->addressLabel[1] = hi(get_byte(Pbuf + 5));
|
|
c->addressLabel[0] = lo(get_byte(Pbuf + 5));
|
|
|
|
c->IMLabel[1] = hi(get_byte(Pbuf + 7));
|
|
c->IMLabel[0] = lo(get_byte(Pbuf + 7));
|
|
|
|
contents1 = get_long(record + 8);
|
|
contents2 = get_long(record + 12);
|
|
|
|
/* c->companyOffset = get_byte(record + 16); */
|
|
|
|
Pbuf += 17;
|
|
len -= 17;
|
|
|
|
field_num=0;
|
|
|
|
for (i = 0; i < 28; i++, field_num++) {
|
|
if (contents1 & (1 << i)) {
|
|
if (len < 1)
|
|
return 0;
|
|
c->entry[field_num] = strdup((char *) Pbuf);
|
|
Pbuf += strlen((char *) Pbuf) + 1;
|
|
len -= strlen(c->entry[field_num]) + 1;
|
|
} else {
|
|
c->entry[field_num] = 0;
|
|
}
|
|
}
|
|
for (i = 0; i < 11; i++, field_num++) {
|
|
if (contents2 & (1 << i)) {
|
|
if (len < 1)
|
|
return 0;
|
|
c->entry[field_num] = strdup((char *) Pbuf);
|
|
Pbuf += strlen((char *) Pbuf) + 1;
|
|
len -= strlen(c->entry[field_num]) + 1;
|
|
} else {
|
|
c->entry[field_num] = 0;
|
|
}
|
|
}
|
|
|
|
/* I think one of these is a birthday flag and one is an alarm flag.
|
|
* Since both are always set there is no way to know which is which.
|
|
* It could be something like a flag for advanceUnits also. */
|
|
if ((contents2 & 0x0800) || (contents2 & 0x1000)) {
|
|
c->birthdayFlag = 1;
|
|
if (len < 1)
|
|
return 0;
|
|
packed_date = get_short(Pbuf);
|
|
c->birthday.tm_year = ((packed_date & 0xFE00) >> 9) + 4;
|
|
c->birthday.tm_mon = ((packed_date & 0x01E0) >> 5) - 1;
|
|
c->birthday.tm_mday = (packed_date & 0x001F);
|
|
c->birthday.tm_hour = 0;
|
|
c->birthday.tm_min = 0;
|
|
c->birthday.tm_sec = 0;
|
|
c->birthday.tm_isdst= -1;
|
|
mktime(&c->birthday);
|
|
/* 2 bytes and a zero padding byte */
|
|
len -= 3;
|
|
Pbuf += 3;
|
|
c->advanceUnits = get_byte(Pbuf);
|
|
len--;
|
|
Pbuf++;
|
|
} else {
|
|
c->birthdayFlag = 0;
|
|
}
|
|
|
|
if (contents2 & 0x2000) {
|
|
c->reminder = 1;
|
|
if (len < 1)
|
|
return 0;
|
|
c->advance = get_byte(Pbuf);
|
|
len -= 1;
|
|
Pbuf += 1;
|
|
} else {
|
|
c->reminder = 0;
|
|
c->advance = 0;
|
|
}
|
|
|
|
/* A blob of size zero would take 6 bytes */
|
|
blob_count=0;
|
|
while (len >= 6) {
|
|
if (blob_count >= MAX_CONTACT_BLOBS) {
|
|
/* Too many blobs were found. */
|
|
return (Pbuf - record);
|
|
}
|
|
c->blob[blob_count] = malloc(sizeof(struct ContactBlob));
|
|
strncpy(c->blob[blob_count]->type, (char *)Pbuf, 4);
|
|
c->blob[blob_count]->length = get_short(Pbuf+4);
|
|
c->blob[blob_count]->data = malloc(c->blob[blob_count]->length);
|
|
if (c->blob[blob_count]->data) {
|
|
memcpy(c->blob[blob_count]->data, Pbuf+6, c->blob[blob_count]->length);
|
|
}
|
|
if (! strncmp(c->blob[blob_count]->type, BLOB_TYPE_PICTURE_ID, 4)) {
|
|
if (!(c->picture)) {
|
|
c->picture = malloc(sizeof(struct ContactPicture));
|
|
}
|
|
c->picture->dirty = get_short(c->blob[blob_count]->data);
|
|
c->picture->length = c->blob[blob_count]->length - 2;
|
|
c->picture->data = c->blob[blob_count]->data + 2;
|
|
}
|
|
|
|
Pbuf += 6;
|
|
len -= 6;
|
|
Pbuf += c->blob[blob_count]->length;
|
|
len -= c->blob[blob_count]->length;
|
|
blob_count++;
|
|
}
|
|
|
|
return (Pbuf - record);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* Function: pack_Contact
|
|
*
|
|
* Summary: Fill in the raw contact record data based on the
|
|
* contact structure
|
|
*
|
|
* Parameters: None
|
|
*
|
|
* Returns: -1 on error
|
|
* The length of the buffer used on success
|
|
*
|
|
***********************************************************************/
|
|
int pack_Contact(struct Contact *c, pi_buffer_t *buf, contactsType type)
|
|
{
|
|
int l, destlen = 17;
|
|
|
|
unsigned char *Pbuf, *record;
|
|
unsigned long contents1, contents2;
|
|
int i;
|
|
unsigned int field_i;
|
|
unsigned long phoneflag;
|
|
unsigned long typesflag;
|
|
unsigned short packed_date;
|
|
int companyOffset = 0;
|
|
|
|
if (c == NULL || buf == NULL)
|
|
return -1;
|
|
|
|
if (type != contacts_v10 && type != contacts_v11) {
|
|
/* Don't support anything else yet */
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < NUM_CONTACT_ENTRIES; i++) {
|
|
if (c->entry[i]) {
|
|
destlen += (strlen(c->entry[i]) + 1);
|
|
}
|
|
}
|
|
if (c->birthdayFlag) {
|
|
destlen += 3;
|
|
if (c->reminder) {
|
|
destlen += 2;
|
|
} else {
|
|
destlen += 1;
|
|
}
|
|
}
|
|
|
|
/* Check for blobs */
|
|
for (i=0; i<MAX_CONTACT_BLOBS; i++) {
|
|
if (c->blob[i]) {
|
|
destlen += c->blob[i]->length + 6;
|
|
}
|
|
}
|
|
|
|
pi_buffer_expect(buf, destlen);
|
|
|
|
record = buf->data;
|
|
|
|
Pbuf = record + 17;
|
|
phoneflag = 0;
|
|
typesflag = 0;
|
|
contents1 = contents2 = 0;
|
|
|
|
field_i = 0;
|
|
for (i = 0; i < 28; i++, field_i++) {
|
|
if (c->entry[field_i] && strlen(c->entry[field_i])) {
|
|
contents1 |= (1 << i);
|
|
l = strlen(c->entry[field_i]) + 1;
|
|
memcpy(Pbuf, c->entry[field_i], l);
|
|
Pbuf += l;
|
|
}
|
|
}
|
|
for (i = 0; i < 11; i++, field_i++) {
|
|
if (c->entry[field_i] && strlen(c->entry[field_i])) {
|
|
contents2 |= (1 << i);
|
|
l = strlen(c->entry[field_i]) + 1;
|
|
memcpy(Pbuf, c->entry[field_i], l);
|
|
Pbuf += l;
|
|
}
|
|
}
|
|
|
|
phoneflag = (((unsigned long) c->phoneLabel[0]) & 0xF) << 0;
|
|
phoneflag |= (((unsigned long) c->phoneLabel[1]) & 0xF) << 4;
|
|
phoneflag |= (((unsigned long) c->phoneLabel[2]) & 0xF) << 8;
|
|
phoneflag |= (((unsigned long) c->phoneLabel[3]) & 0xF) << 12;
|
|
phoneflag |= (((unsigned long) c->phoneLabel[4]) & 0xF) << 16;
|
|
phoneflag |= (((unsigned long) c->phoneLabel[5]) & 0xF) << 20;
|
|
phoneflag |= (((unsigned long) c->phoneLabel[6]) & 0xF) << 24;
|
|
phoneflag |= (((unsigned long) c->showPhone) & 0xF) << 28;
|
|
|
|
typesflag = (((unsigned long) c->IMLabel[0]) & 0xF) << 0;
|
|
typesflag |= (((unsigned long) c->IMLabel[1]) & 0xF) << 4;
|
|
typesflag |= (((unsigned long) c->addressLabel[0]) & 0xF) << 16;
|
|
typesflag |= (((unsigned long) c->addressLabel[1]) & 0xF) << 20;
|
|
typesflag |= (((unsigned long) c->addressLabel[2]) & 0xF) << 24;
|
|
|
|
if (c->birthdayFlag) {
|
|
contents2 |= 0x1800;
|
|
packed_date = (((c->birthday.tm_year - 4) << 9) & 0xFE00) |
|
|
(((c->birthday.tm_mon+1) << 5) & 0x01E0) |
|
|
(c->birthday.tm_mday & 0x001F);
|
|
set_short(Pbuf, packed_date);
|
|
Pbuf += 2;
|
|
set_byte(Pbuf, 0); /* padding byte in birthday date */
|
|
Pbuf += 1;
|
|
if (c->reminder) {
|
|
contents2 |= 0x2000;
|
|
set_byte(Pbuf, c->advanceUnits);
|
|
Pbuf += 1;
|
|
set_byte(Pbuf, c->advance);
|
|
Pbuf += 1;
|
|
} else {
|
|
set_byte(Pbuf, 0);
|
|
Pbuf += 1;
|
|
}
|
|
}
|
|
|
|
set_long(record, phoneflag);
|
|
set_long(record + 4, typesflag);
|
|
set_long(record + 8, contents1);
|
|
set_long(record + 12, contents2);
|
|
/* companyOffset is the offset from itself to the company field,
|
|
* or zero if no company field. Its not useful to us at all. */
|
|
if (c->entry[2]) {
|
|
companyOffset++;
|
|
if (c->entry[0]) companyOffset += strlen(c->entry[0]) + 1;
|
|
if (c->entry[1]) companyOffset += strlen(c->entry[1]) + 1;
|
|
}
|
|
set_byte(record + 16, companyOffset);
|
|
|
|
/* Pack blobs */
|
|
for (i=0; i<MAX_CONTACT_BLOBS; i++) {
|
|
if (c->blob[i]) {
|
|
memcpy(Pbuf, c->blob[i]->type, 4);
|
|
Pbuf += 4;
|
|
set_short(Pbuf, c->blob[i]->length);
|
|
Pbuf += 2;
|
|
memcpy(Pbuf, c->blob[i]->data, c->blob[i]->length);
|
|
Pbuf += c->blob[i]->length;
|
|
}
|
|
}
|
|
buf->used = Pbuf - record;
|
|
|
|
return (buf->used);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* Function: Contact_add_blob
|
|
*
|
|
* Summary: Add a blob record to a Contact Record
|
|
*
|
|
* Parameters: None
|
|
*
|
|
* Returns: 0 on success
|
|
* 1 on other error
|
|
*
|
|
***********************************************************************/
|
|
int Contact_add_blob(struct Contact *c, struct ContactBlob *blob)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<MAX_CONTACT_BLOBS; i++) {
|
|
if (c->blob[i]) {
|
|
continue;
|
|
}
|
|
|
|
c->blob[i] = malloc(sizeof(struct ContactBlob));
|
|
if (!c->blob[i]) return EXIT_FAILURE;
|
|
|
|
c->blob[i]->data = malloc(blob->length);
|
|
strncpy(c->blob[i]->type, blob->type, 4);
|
|
c->blob[i]->length = blob->length;
|
|
strncpy((char *)c->blob[i]->data, (char *)blob->data, blob->length);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* Function: Contact_add_picture
|
|
*
|
|
* Summary: Add a picture blob record to a Contact Record
|
|
* This will add a blob, but not touch the picture structure
|
|
* of the contact record
|
|
*
|
|
* Parameters: None
|
|
*
|
|
* Returns: 0 on success
|
|
* 1 on other error
|
|
*
|
|
***********************************************************************/
|
|
int Contact_add_picture(struct Contact *c, struct ContactPicture *p)
|
|
{
|
|
int i;
|
|
|
|
if ((!p) || (p->length<1) || (!p->data)) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
for (i=0; i<MAX_CONTACT_BLOBS; i++) {
|
|
if (c->blob[i]) {
|
|
continue;
|
|
}
|
|
|
|
c->blob[i] = malloc(sizeof(struct ContactBlob));
|
|
if (!c->blob[i]) return EXIT_FAILURE;
|
|
|
|
c->blob[i]->data = malloc(p->length + 2);
|
|
strncpy(c->blob[i]->type, BLOB_TYPE_PICTURE_ID, 4);
|
|
c->blob[i]->length = p->length + 2;
|
|
set_short(c->blob[i]->data, p->dirty);
|
|
memcpy(c->blob[i]->data + 2, p->data, p->length);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* Function: unpack_ContactAppInfo
|
|
*
|
|
* Summary: Fill in the app info structure based on the raw app
|
|
* info data
|
|
*
|
|
* Parameters: None
|
|
*
|
|
* Returns: -1 on error
|
|
* The length of data used from the buffer on success
|
|
*
|
|
***********************************************************************/
|
|
int unpack_ContactAppInfo(struct ContactAppInfo *ai, pi_buffer_t *buf)
|
|
{
|
|
int i, j, destlen;
|
|
unsigned char *start, *Pbuf;
|
|
int len;
|
|
|
|
start = Pbuf = buf->data;
|
|
len = buf->used;
|
|
if (len == 1092) {
|
|
ai->type = contacts_v10;
|
|
ai->num_labels = NUM_CONTACT_V10_LABELS;
|
|
ai->numCustoms = 9; /* not sure - but pi-contact.h <= 1.11 had 9 all the time */
|
|
} else if (len == 1156) {
|
|
ai->type = contacts_v11;
|
|
ai->num_labels = NUM_CONTACT_V11_LABELS;
|
|
ai->numCustoms = 9; /* not sure - but pi-contact.h <= 1.11 had 9 all the time */
|
|
} else {
|
|
fprintf(stderr, "contact.c: unpack_ContactAppInfo: ContactAppInfo size of %d incorrect\n", len);
|
|
return -1;
|
|
}
|
|
|
|
/* 278 app info, 26 unknown, labels, county, sortBy */
|
|
destlen = 278 + 26 + (16 * ai->num_labels) + 2 + 2;
|
|
if (buf->used < destlen)
|
|
return -1;
|
|
|
|
i = unpack_CategoryAppInfo(&ai->category, start, len);
|
|
if (!i)
|
|
return i;
|
|
Pbuf += i;
|
|
|
|
memcpy(ai->internal, Pbuf, 26);
|
|
Pbuf += 26;
|
|
memcpy(ai->labels, Pbuf, 16 * ai->num_labels);
|
|
Pbuf += 16 * ai->num_labels;
|
|
ai->country = get_byte(Pbuf);
|
|
Pbuf += 2;
|
|
ai->sortByCompany = get_byte(Pbuf);
|
|
Pbuf += 2;
|
|
|
|
/* These are the fields that go in drop down menus */
|
|
for (i = 4, j = 0; i < 11; i++, j++) {
|
|
strcpy(ai->phoneLabels[j], ai->labels[i]);
|
|
}
|
|
strcpy(ai->phoneLabels[j], ai->labels[40]);
|
|
|
|
for (i = 0; i < ai->numCustoms; i++) {
|
|
strcpy(ai->customLabels[i], ai->labels[14 + i]);
|
|
}
|
|
|
|
strcpy(ai->addrLabels[0], ai->labels[23]);
|
|
strcpy(ai->addrLabels[1], ai->labels[28]);
|
|
strcpy(ai->addrLabels[2], ai->labels[33]);
|
|
|
|
strcpy(ai->IMLabels[0], ai->labels[41]);
|
|
strcpy(ai->IMLabels[1], ai->labels[42]);
|
|
strcpy(ai->IMLabels[2], ai->labels[43]);
|
|
strcpy(ai->IMLabels[3], ai->labels[44]);
|
|
strcpy(ai->IMLabels[4], ai->labels[45]);
|
|
|
|
return (Pbuf - start);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* Function: free_ContactAppInfo
|
|
*
|
|
* Summary: Unallocate dynamically sized parts of ContactAppInfo
|
|
* (However, there are now none - provided for source compatibility)
|
|
*
|
|
* Parameters: struct ContactAppInfo *ai
|
|
*
|
|
* Returns: None
|
|
*
|
|
***********************************************************************/
|
|
void free_ContactAppInfo (struct ContactAppInfo *ai)
|
|
{
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* Function: pack_ContactAppInfo
|
|
*
|
|
* Summary: Fill in the raw app info record data based on the
|
|
* ContactAppInfo structure
|
|
*
|
|
* Parameters: None
|
|
*
|
|
* Returns: -1 on error
|
|
* The length of the data used from the buffer on success.
|
|
*
|
|
***********************************************************************/
|
|
int pack_ContactAppInfo(struct ContactAppInfo *ai, pi_buffer_t *buf)
|
|
{
|
|
int destlen;
|
|
|
|
if (buf == NULL || buf->data == NULL)
|
|
return -1;
|
|
|
|
/* 278 app info, 26 unknown, labels, country, sortBy */
|
|
destlen = 278 + 26 + (16 * ai->num_labels) + 2 + 2;
|
|
|
|
pi_buffer_expect(buf, destlen);
|
|
|
|
buf->used = pack_CategoryAppInfo(&ai->category, buf->data, buf->allocated);
|
|
if (buf->used != 278)
|
|
return -1;
|
|
|
|
pi_buffer_append(buf, ai->internal, 26);
|
|
|
|
pi_buffer_append(buf, ai->labels, 16 * ai->num_labels);
|
|
|
|
set_byte(buf->data + buf->used++, ai->country);
|
|
/* Unknown field */
|
|
set_byte(buf->data + buf->used++, 0x00);
|
|
|
|
set_byte(buf->data + buf->used++, ai->sortByCompany);
|
|
/* Unknown field */
|
|
set_byte(buf->data + buf->used++, 0x00);
|
|
|
|
return (buf->used);
|
|
}
|
|
|