#include "palm_sync.h"
#include "vcard.h"
#include "xml.h"

static GMutex *piMutex = NULL;
gboolean dbCreated = FALSE;
gboolean tryConnecting = TRUE;
int dialogShowing = 0;
GtkWidget *dialogConnecting = NULL;
extern void async_add_pairlist_log(sync_pair *pair, char* logstring, sync_log_type type);


/***********************************************************************
 *
 * Function:    piMutex_create
 *
 * Summary:   initialize the global mutex to protect the libpisock
 *
 ***********************************************************************/
void piMutex_create()
{
	g_assert (piMutex == NULL);
	piMutex = g_mutex_new ();
}

/***********************************************************************
 *
 * Function:    palm_debug
 *
 * Summary:   used to write Debug messages where leve = 0 is the highest
 *			(error) and 4 is the lowest (full debug)
 *
 ***********************************************************************/
void palm_debug(palm_connection *conn, int level, char *message, ...)
{
	va_list arglist;
	char buffer[4096];
	char *type = (conn->type==CONNECTION_TYPE_LOCAL?"local":"remote");
	int debuglevel = conn->debuglevel;

	if (level > debuglevel) { return; }
	va_start(arglist, message);
	vsprintf(buffer, message, arglist);

	switch (level) {
		case 0:
			//Error
			printf("[%s] ERROR: %s\n", type, buffer);
			if (conn->handle) {
				async_add_pairlist_log(conn->handle, buffer, SYNC_LOG_ERROR);
			}
			break;
		case 1:
			//Warning
			printf("[%s] WARNING: %s\n", type, buffer);
			break;
		case 2:
			//Information
			printf("[%s] INFORMATION: %s\n", type, buffer);
			break;
		case 3:
			//debug
			printf("[%s] DEBUG: %s\n", type, buffer);
			break;
		case 4:
			//fulldebug
			printf("[%s] FULL DEBUG: %s\n", type, buffer);
			break;
	}
	va_end(arglist);
}

gboolean pingfunc(gpointer data)
{
	palm_connection *conn = data;

	if (!conn->socket)
		return FALSE;

	if (g_mutex_trylock(piMutex) == FALSE) {
		palm_debug(conn, 3, "Ping: Mutex locked!");
		return TRUE;
	}

	if (pi_tickle(conn->socket) < 0) {
		palm_debug(conn, 1, "Ping: Error");
	} else {
		palm_debug(conn, 3, "Ping");
	}

	g_mutex_unlock(piMutex);
	return TRUE;
}

void cancelButton(void *data)
{
	printf("Cancel\n");
	if (data)
		gtk_widget_destroy((GtkWidget *)data);
	tryConnecting = FALSE;
}

gboolean showDialogConnecting(void *data)
{
	dialogConnecting = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CANCEL, "Please connect device and press the HotSync button or cancel the operation.");
	gtk_signal_connect (GTK_OBJECT (dialogConnecting), "response", G_CALLBACK (cancelButton), dialogConnecting);
	gtk_widget_show(dialogConnecting);
	return FALSE;
}

int connectDevice(palm_connection *conn, gboolean block, gboolean popup)
{
	struct pi_sockaddr addr;
	int listen_sd, pf;
	char rate_buf[128];
	long timeout;

	if (conn->conntype != PILOT_DEVICE_NETWORK) {
		g_snprintf (rate_buf,128,"PILOTRATE=%i", conn->speed);
		palm_debug(conn, 2, "setting PILOTRATE=%i", conn->speed);
		putenv (rate_buf);
	}

	switch (conn->conntype) {
		case PILOT_DEVICE_SERIAL:
			pf = PI_PF_PADP;
			break;
		case PILOT_DEVICE_USB_VISOR:
			pf = PI_PF_NET;
			break;
		case PILOT_DEVICE_IRDA:
			pf = PI_PF_PADP;
			break;
		case PILOT_DEVICE_NETWORK:
			pf = PI_PF_NET;
			break;
		default:
			pf = PI_PF_DLP;
			break;
	}

	if (!(listen_sd = pi_socket (PI_AF_PILOT, PI_SOCK_STREAM, pf))) {
		palm_debug(conn, 0, "pi_socket: %s", strerror (errno));
		return -1;
	}

	addr.pi_family = PI_AF_PILOT;

	if (conn->conntype == PILOT_DEVICE_NETWORK) {
		conn->sockaddr = "net:any";
	}

	strcpy (addr.pi_device, conn->sockaddr);

	if (pi_bind (listen_sd, (struct sockaddr*)&addr, sizeof (addr)) == -1) {
		palm_debug(conn, 0, "Unable to bind to pilot");
		pi_close(listen_sd);
		return -2;
	}

	if (pi_listen (listen_sd, 1) != 0) {
		palm_debug(conn, 0, "pi_listen: %s", strerror (errno));
		pi_close(listen_sd);
		return -3;
	}

	sync_set_pair_status(conn->handle, "Press \"Hotsync\" now.");

	tryConnecting = TRUE;
	if (popup) {
		g_idle_add(showDialogConnecting, NULL);
	}

	if (!block) {
		timeout = 0;
		while (tryConnecting) {
			conn->socket = pi_accept_to(listen_sd, 0, 0, 100);
			timeout += 100;
			if (timeout > conn->timeout * 1000 && conn->timeout > 0) {
				palm_debug(conn, 1, "pi_accept_to: timeout");
				palm_debug(conn, 1, "pi_accept_to: timeout was %i secs", conn->timeout);
				pi_close(listen_sd);
				if (popup && dialogConnecting)
					gtk_widget_destroy(dialogConnecting);
				return -4;
			}
			if (conn->socket == -1) {
				while (gtk_events_pending ())
					gtk_main_iteration();
				continue;
			}
			if (conn->socket < -1) {
				palm_debug(conn, 0, "Unable to accept() listen socket");
				pi_close(listen_sd);
				if (popup && dialogConnecting)
					gtk_widget_destroy(dialogConnecting);
				return -5;
			}
			if (popup && dialogConnecting)
				gtk_widget_destroy(dialogConnecting);
			break;
		}
	} else {
		conn->socket = pi_accept_to(listen_sd, 0, 0, conn->timeout * 1000);
		if (conn->socket == -1) {
			palm_debug(conn, 1, "pi_accept_to: %s", strerror (errno));
			palm_debug(conn, 1, "pi_accept_to: timeout was %i secs", conn->timeout);
			pi_close(listen_sd);
			return -6;
		}
	}

	pi_close(listen_sd);
	if (!tryConnecting)
		return -7;
	return 0;
}

gboolean showDialogMismatch(void *data)
{
	GtkWidget *dialog = NULL;
	dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, (char *)data);
	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
		dialogShowing = 1;
	} else {
		dialogShowing = 2;
	}
	gtk_widget_destroy (dialog);
	return FALSE;

}


palm_connection *sync_connect(sync_pair *handle, connection_type type, sync_object_type object_types)
{
	palm_connection *conn;
	struct PilotUser User;
	struct SysInfo sys_info;
	gchar *txt;

	conn = g_malloc0(sizeof(palm_connection));
	g_assert(conn);
	conn->handle = handle;
	conn->commondata.object_types = object_types;
	conn->type = type;
	palm_debug(conn, 3, "start: sync_connect");

	sprintf(conn->statefile, "%s/%ssettings", sync_get_datapath(handle), (type==CONNECTION_TYPE_LOCAL?"local":"remote"));

	//Load the settings
	load_palm_settings(conn);

	//now connect with the palm
	if (connectDevice(conn, FALSE, conn->popup)) {
		sync_set_requestfailed(conn->handle);
		return NULL;
	}

	// Do the following so that MultiSync will work with password enabled
	// Palm devices (OS 4 and higher).
	if (dlp_ReadSysInfo(conn->socket, &sys_info) < 0) {
		palm_debug(conn, 0, "Unable to read SysInfo");
		goto failed;
	}

	//check the user
	if (dlp_ReadUserInfo(conn->socket, &User) >= 0) {
		if (User.userID == 0)
			strcpy(User.username, "");
		palm_debug(conn, 2, "User: %s, %i\n", User.username, User.userID);
		if (strcmp(User.username, conn->username) || User.userID != conn->id) {
			//Id or username mismatch
			switch (conn->mismatch) {
				case 0:
					break;
				case 1:
					//ask
					dialogShowing = 0;
					txt = g_strdup_printf("The username \"%s\" or the ID %i on the device  did not match the username or ID from this syncpair.\nPress \"Cancel\" to abort the synchronization.\n\"OK\" to sync anyway", User.username, (int)User.userID);
					g_idle_add(showDialogMismatch, txt);
					while (!dialogShowing) {
						usleep(100000);
					}
					if (dialogShowing == 2) {
						palm_debug(conn, 0, "Sync aborted because of User mismatch");
						goto failed;
					}
					break;
				case 2:
					palm_debug(conn, 0, "Sync aborted because of User mismatch");
					goto failed;
					break;
			}
		}
	} else {
		palm_debug(conn, 0, "Unable to read UserInfo");
		goto failed;
	}

	//init the mutex
	piMutex_create();
	//Add the ping function every 5 secs
	g_timeout_add(5000, pingfunc, conn);

	sync_set_requestdone(handle);
	srand(time(NULL));
	palm_debug(conn, 3, "end: sync_connect");
	return(conn);

	failed:
		if(conn->socket) {
			dlp_EndOfSync(conn->socket, 0);
			pi_close(conn->socket);
		}
		conn->socket = 0;
		sync_set_requestfailed(conn->handle);
		return NULL;
}

/***********************************************************************
 *
 * Function:    add_changed
 *
 * Summary:   calls the convert functions and finally returns the resulting changed_object
 *
 ***********************************************************************/
changed_object *add_changed(palm_connection *conn, palm_entry *data, int change_type)
{
	GString *vcard = NULL;
	changed_object *change = NULL;

	change = g_malloc0(sizeof(changed_object));
	change->uid = strdup(data->uid);
	change->change_type = change_type;
	change->object_type = data->type;
	change->comp = NULL;
	change->removepriority = NULL;

	if (change_type != SYNC_OBJ_HARDDELETED && change_type != SYNC_OBJ_SOFTDELETED) {
		//Convert attributes to VCARD
		switch (data->type) {
			case SYNC_OBJECT_TYPE_PHONEBOOK:
				vcard = address2vcard(conn, data->address, data->category);
				break;
			case SYNC_OBJECT_TYPE_CALENDAR:
				vcard = calendar2vevent(conn, data->appointment);
				break;
			case SYNC_OBJECT_TYPE_TODO:
				vcard = todo2vcal(conn, data->todo, data->category);
				break;
			default:
				break;
		}
		change->comp = strdup(vcard->str);
		g_string_free(vcard, TRUE);
	}
	return change;
}

/***********************************************************************
 *
 * Function:    unpackEntry
 *
 * Summary:   unpacks the buffer returned based on the sync_obj_type
 *
 ***********************************************************************/
void unpackEntry(palm_entry *entry, char *buffer, int l, sync_object_type type)
{
	entry->type = type;

	//Phonebook
	if (type == SYNC_OBJECT_TYPE_PHONEBOOK) {
		memset(&entry->address, 0, sizeof(&entry->address));
		unpack_Address(&entry->address, buffer, l);
	}

	//Datebook
	if (type == SYNC_OBJECT_TYPE_CALENDAR) {
		memset(&entry->appointment, 0, sizeof(&entry->appointment));
		unpack_Appointment(&entry->appointment, buffer, l);
	}

	//Todos
	if (type == SYNC_OBJECT_TYPE_TODO) {
		memset(&entry->todo, 0, sizeof(&entry->todo));
		unpack_ToDo(&entry->todo, buffer, l);
	}
}

/***********************************************************************
*
* Function:    CloseDB
*
* Summary:   closes the currently open DB and unsets the conn variables
*
 ***********************************************************************/
  void CloseDB(palm_connection *conn)
 {
	dlp_CloseDB(conn->socket, conn->database);
	strcpy(conn->databasename, "");
	conn->database = 0;
}

/***********************************************************************
*
* Function:    openDB
*
* Summary:   tries to open a given DB. if it is already open it does nothing.
*			if another db is opened it closes this one first.
*
* Returns:	0 on success
*			-1 on unable to find DB
*			-2 on unable to open DB
*
 ***********************************************************************/
 int openDB(palm_connection *conn, char *name)
 {
	struct DBInfo dbInfo;
	memset(&dbInfo, 0, sizeof(struct DBInfo));

	if (conn->database) {
		if (strcmp(conn->databasename, name) != 0) {
			//We have another DB open. close it first
			palm_debug(conn, 2, "OpenDB called, closing %s first", conn->databasename);
			CloseDB(conn);
		} else {
			//It was already open
			return 0;
		}
	}

 	//Search it
	if (dlp_FindDBInfo(conn->socket, 0, 0, name, 0, 0, &dbInfo) < 0) {
		palm_debug(conn, 1, "Unable to locate %s. Assuming it has been reset", name);
		return -1;
	}

	//open it
	if (dlp_OpenDB(conn->socket, 0, dlpOpenReadWrite, name, &conn->database) < 0) {
		palm_debug(conn, 0, "Unable to open %s", name);
		conn->database = 0;
		return -2;
	}

	palm_debug(conn, 2, "Successfully opened %s", name);
	strcpy(conn->databasename, name);

	return 0;
}


#define CATCOUNT 16
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif

/***********************************************************************
 *
 * Function:    get_category_name_from_id
 *
 * Summary:   gets the installed categories as a GList
 *
 ***********************************************************************/
gchar *get_category_name_from_id(palm_connection *conn, int id)
{	
	int size;
	unsigned char buf[65536];
	struct CategoryAppInfo cai;
	int r;

	if (id == 0)
		return NULL;
	
	/* buffer size passed in cannot be any larger than 0xffff */
	size = dlp_ReadAppBlock(conn->socket, conn->database, 0, buf, min(sizeof(buf), 0xFFFF));
	if (size<=0) {
		palm_debug(conn, 0, "Error reading appinfo block\n");
		return NULL;
	}

	r = unpack_CategoryAppInfo(&cai, buf, size);
	if ((r <= 0) || (size <= 0)) {
		palm_debug(conn, 0, "unpack_AddressAppInfo failed %s %d\n", __FILE__, __LINE__);
		return NULL;
	}
   
	return g_strdup(cai.name[id]);
}

/***********************************************************************
 *
 * Function:    get_category_id_from_name
 *
 * Summary:   gets the installed categories as a GList
 *
 ***********************************************************************/
int get_category_id_from_name(palm_connection *conn, char *name)
{	
	int size;
	unsigned char buf[65536];
	struct CategoryAppInfo cai;
	int r, i;

	if (!name)
		return 0;
	
	/* buffer size passed in cannot be any larger than 0xffff */
	size = dlp_ReadAppBlock(conn->socket, conn->database, 0, buf, min(sizeof(buf), 0xFFFF));
	if (size<=0) {
		palm_debug(conn, 0, "Error reading appinfo block\n");
		return 0;
	}

	r = unpack_CategoryAppInfo(&cai, buf, size);
	if ((r <= 0) || (size <= 0)) {
		palm_debug(conn, 0, "unpack_AddressAppInfo failed %s %d\n", __FILE__, __LINE__);
		return 0;
	}
   
	for (i = 0; i < CATCOUNT; i++) {
		if (cai.name[i][0] != '\0') {
			palm_debug(conn, 3, "remote: cat %d [%s] ID %d renamed %d\n", i, cai.name[i], cai.ID[i], cai.renamed[i]);
			if (!strcmp(cai.name[i], name)) {
				return i;
			}
		}
	}
	return 0;
}

/***********************************************************************
 *
 * Function:    get_changes
 *
 * Summary:   gets called by Multisync. returns a GList with all the changes requested
 *
 ***********************************************************************/
void get_changes(palm_connection *conn, sync_object_type newdbs)
{
	int l, n, ret, i;
	unsigned char buffer[65536];
	GList *changes = NULL;
	change_info *chinfo = NULL;
	recordid_t id=0;
	int index, size, attr, category;
	palm_entry entry;
	struct  PilotUser User;
	char *database;
	sync_object_type type;

	chinfo = g_malloc0(sizeof(change_info));

	//Lock our Mutex
	g_mutex_lock(piMutex);

	dlp_OpenConduit(conn->socket);

	//Loop over everything for every type
	for (i = 0; i < 3; i++) {
		//Addressbook
		if (i == 0 && (conn->commondata.object_types & SYNC_OBJECT_TYPE_PHONEBOOK)) {
			//Open the Address Book
			database = "AddressDB";
			type = SYNC_OBJECT_TYPE_PHONEBOOK;
		} else if (i == 1 && (conn->commondata.object_types & SYNC_OBJECT_TYPE_CALENDAR)) {
			//DatebookDB
			database = "DatebookDB";
			type = SYNC_OBJECT_TYPE_CALENDAR;
		} else if (i == 2 && (conn->commondata.object_types & SYNC_OBJECT_TYPE_TODO)) {
			//TodoDB
			database = "ToDoDB";
			type = SYNC_OBJECT_TYPE_TODO;
		} else {
			continue;
		}

		//Open the database
		ret = openDB(conn, database);
		if (ret == -1) {
			//database does not exist. we definetly need a resync for that
			chinfo->newdbs |= type;
		}
		if (ret != 0) {
			continue;
		}
		l = dlp_ReadAppBlock(conn->socket, conn->database, 0, buffer, 0xffff);


		//Now we choose if we want everything or just the modified stuff
		if (newdbs & type) {
			for (n = 0; dlp_ReadRecordByIndex(conn->socket, conn->database, n, buffer, &id, &size, &attr, &category) >= 0; n++) {
				//we have a record
				unpackEntry(&entry, buffer, l, type);
				entry.category = get_category_name_from_id(conn, category);
				snprintf(entry.uid, 1024, "uid-%s-%ld", database, id);
				palm_debug(conn, 2, "NEWDBS: Found a record on palm: %s", entry.uid);
				changes = g_list_append(changes, add_changed(conn, &entry, SYNC_OBJ_ADDED));
			}
		} else {
			while(1) {
				ret = dlp_ReadNextModifiedRec(conn->socket, conn->database, buffer, &id, &index, &size, &attr, &category);
				if (ret < 0 ) {
					break;
				}

				if ((attr &  dlpRecAttrDeleted) || (attr & dlpRecAttrArchived)) {
					if ((attr & dlpRecAttrArchived)) {
						palm_debug(conn, 2, "Archieved\n");
					}
					//we have a deleted record
					snprintf(entry.uid, 1024, "uid-%s-%ld", database, id);
					palm_debug(conn, 2, "Found a deleted record on palm: %s", entry.uid);
					entry.type = type;
					changes = g_list_append(changes, add_changed(conn, &entry, SYNC_OBJ_HARDDELETED));
					continue;
				}

				if (attr & dlpRecAttrDirty) {
					//We have a modified record
					unpackEntry(&entry, buffer, l, type);
					entry.category = get_category_name_from_id(conn, category);
					snprintf(entry.uid, 1024, "uid-%s-%ld", database, id);
					palm_debug(conn, 2, "Found a modified record on palm: %s", entry.uid);
					changes = g_list_append(changes, add_changed(conn, &entry, SYNC_OBJ_MODIFIED));
				}
			}
		}
	}

	palm_debug(conn, 2, "Done searching for changes");

	chinfo->changes = changes;

	if (dlp_ReadUserInfo(conn->socket, &User) >= 0) {
		if (User.lastSyncPC == 0) {
			//Device has been reseted
			palm_debug(conn, 3, "Detected that the Device has been reset");
			chinfo->newdbs = SYNC_OBJECT_TYPE_ANY;
		}
	}

	sync_set_requestdata(chinfo, conn->handle);
	palm_debug(conn, 2, "Found %i changes", g_list_length(changes));

	//Unlock our Mutex
	g_mutex_unlock(piMutex);
}

/***********************************************************************
 *
 * Function:    detectDB
 *
 * Summary:   tries to detect the type of vcard
 *
 ***********************************************************************/
char *detectDB(sync_object_type objtype)
{
	switch (objtype) {
		case SYNC_OBJECT_TYPE_PHONEBOOK:
			return "AddressDB";
		case SYNC_OBJECT_TYPE_TODO:
			return "ToDoDB";
		case SYNC_OBJECT_TYPE_CALENDAR:
			return "DatebookDB";
		default:
			return NULL;
	}
}

/*
char *detectDB(palm_connection *conn, char *comp)
{
	VObjectO *vcard;

	vcard = Parse_MIMEO(comp, strlen(comp));
	printf("Just parsed a vcard to %s\n", comp);
	if (isAPropertyOfO(vcard,  VCTodoPropO)) {
		deleteVObjectO(vcard);
		return "ToDoDB";
	} else if (isAPropertyOfO(vcard,  VCEventPropO)) {
		deleteVObjectO(vcard);
		return "DatebookDB";
	} else {
		deleteVObjectO(vcard);
		return "AddressDB";
	}
}
*/

void palm_print_binary(unsigned char *data, int len) {
  int t;
  for (t = 0; t < len; t++) {
    if (data[t] >= ' ' && data[t] <= 'z')
      printf("%c", data[t]);
    else
      printf(" %02x ", data[t]);
  }
  printf("\n");
}


 
/***********************************************************************
 *
 * Function:    syncobj_modify
 *
 * Summary:   opens the database and tries to modify the entry in the given db
 *
 ***********************************************************************/
void syncobj_modify(palm_connection *conn, char *comp, char *uid, sync_object_type objtype, char *uidret, int *uidretlen)
{
	int l, ret;
	unsigned char buffer[65536];
	unsigned char orig_buffer[65536];
	palm_entry entry;
	palm_entry orig_entry;
	recordid_t id=0;
	int size;
	int attr, category;
	int category_new;
	char database[1024];
	int dbhandle;
	entry.catID = 0;
	
	//Lock our Mutex
	g_mutex_lock(piMutex);

	palm_debug(conn, 2, "start: syncobj_modify");
	palm_debug(conn, 3, "uid: %s\nCOMP: \n", uid);
	palm_print_binary(comp, strlen(comp) + 1);

	if (!comp) {
		sync_set_requestfailed(conn->handle);
		//Unlock our Mutex
		g_mutex_unlock(piMutex);
		return;
	}

	//detect the db needed
	strcpy(database, detectDB(objtype));
	palm_debug(conn, 2, "Detected vcard to belong to %s", database);

	//Convert the vcard to our palm entry
	if (!strcmp(database, "AddressDB")) {
		if (!vcard2address(conn, &entry, comp))
			goto failed;
		l = pack_Address(&entry.address, buffer, sizeof(buffer));
	} else if (!strcmp(database, "DatebookDB")) {
		if(!vevent2calendar(conn, &entry, comp))
			goto failed;
		l = pack_Appointment(&entry.appointment, buffer, sizeof(buffer));
	} else {
		if(!vcal2todo(conn, &entry, comp))
			goto failed;
		l = pack_ToDo(&(entry.todo), buffer, sizeof(buffer));
	}

	//open the DB
	ret = openDB(conn, database);
	if (ret == -2) {
		sync_set_requestfailed(conn->handle);
		palm_debug(conn, 1, "Unable to modify entry %s: Unable to open DB %s", uid, database);
		goto failed;
	}
	if (ret == -1 && (dbCreated == FALSE) && !strcmp(database, "DatebookDB")) {
		//Unlock our Mutex so the palm does not die will the messagebox is open
		dbCreated = TRUE;
		if (dlp_CreateDB(conn->socket, 1684108389, 1145132097, 0, 8, 0, "DatebookDB", &dbhandle) < 0) {
			dlp_AddSyncLogEntry(conn->socket, "Unable to create Calendar.\n");
			palm_debug(conn, 0, "Unable to create Calendar");
			goto failed;
		}
		conn->database = dbhandle;
		dlp_AddSyncLogEntry(conn->socket, "Created Calendar.\n");
		palm_debug(conn, 2, "Created Calendar.");
	}

	if (uid) {
		int ret = 0;
		//Modify a entry
		palm_debug(conn, 2, "Modifying: %s", uid);
		sscanf(uid, "uid-%[^-]-%ld", database, &id);
		if (dlp_ReadRecordById(conn->socket, conn->database, id, orig_buffer, NULL, &size, &attr, &category) < 0) {
			palm_debug(conn, 1, "Unable to find entry i want to modify");
			goto failed;
		}
		
		//Now retrieve the info about "show in list"
		if (!strcmp(database, "AddressDB")) {	
			unpackEntry(&orig_entry, orig_buffer, l, objtype);
			palm_debug(conn, 2, "Using orig \"show in list\": %i", orig_entry.address.showPhone);
			if ((orig_entry.address.showPhone) > 4)
				orig_entry.address.showPhone = 0;
			entry.address.showPhone = orig_entry.address.showPhone;
			//Repack it
			l = pack_Address(&entry.address, buffer, sizeof(buffer));
		}
		
		ret = dlp_WriteRecord(conn->socket, conn->database, attr, id, entry.catID, buffer, l, 0);
		if (ret < 0) {
			palm_debug(conn, 0, "Unable to modify entry %s", uid);
			goto failed;
		}
		*uidretlen = 0;
	} else {
		//Add a new entry
		palm_debug(conn, 2, "Adding new entry");
		id = 0;
		if (dlp_WriteRecord(conn->socket, conn->database, 0, 0, entry.catID, buffer, l, &id) < 0) {
			palm_debug(conn, 0, "Unable to add new entry");
			goto failed;
		}
		//Make the new uid
		sprintf(uidret, "uid-%s-%ld", database, id);
		*uidretlen = strlen(uidret);
	}

	//Unlock our Mutex
	g_mutex_unlock(piMutex);
	sync_set_requestdone(conn->handle);
	return;

	failed:
		sync_set_requestfailed(conn->handle);
		//Unlock our Mutex
		g_mutex_unlock(piMutex);
		return;
}

/***********************************************************************
 *
 * Function:    syncobj_delete
 *
 * Summary:   opens the database and deletes the given entry
 *
 ***********************************************************************/
void syncobj_delete(palm_connection *conn, char *uid, sync_object_type objtype, int softdelete)
{
	recordid_t id;
	char database[1024];

	//Lock our Mutex
	g_mutex_lock(piMutex);

	sscanf(uid, "uid-%[^-]-%ld", database, &id);

	//Open the database
	if (openDB(conn, database) < 0) {
		sync_set_requestfailed(conn->handle);
		palm_debug(conn, 1, "Unable to delete entry %s: Unable to open DB %s", uid, database);
		//Unlock our Mutex
		g_mutex_unlock(piMutex);
		return;
	}

	if (dlp_DeleteRecord(conn->socket, conn->database, 0,  id) < 0) {
		sync_set_requestfailed(conn->handle);
		palm_debug(conn, 2, "Unable to delete entry %s", uid);
	} else {
		sync_set_requestdone(conn->handle);
		palm_debug(conn, 2, "Done deleting %s", uid);
	}

	//Unlock our Mutex
	g_mutex_unlock(piMutex);
}

/***********************************************************************
 *
 * Function:    sync_done
 *
 * Summary:		gets called by Multisync. opens each DB, reset the sync flags
 *				and write some information to the Palm
 *
 ***********************************************************************/
void sync_done(palm_connection *conn, gboolean success)
{
        struct  PilotUser User;
	int i = 0, ret;
	char *database = NULL;

	//Lock our Mutex
	g_mutex_lock(piMutex);

	if (success == TRUE) {
		//reset change counters
		for (i = 0; i < 3; i++) {
			switch (i) {
				case 0:
					database = "AddressDB";
					break;
				case 1:
					database = "DatebookDB";
					break;
				case 2:
					database = "ToDoDB";
					break;
			}

			if (openDB(conn, database) == 0) {
				palm_debug(conn, 2, "Reseting Sync Flags for %s", database);
				dlp_ResetSyncFlags(conn->socket, conn->database);
				dlp_CleanUpDatabase(conn->socket, conn->database);
				CloseDB(conn);
			}
		}

		//Set the log and sync entries on the palm
		dlp_AddSyncLogEntry(conn->socket, "Sync Successfull\n");
		dlp_AddSyncLogEntry(conn->socket, "Thank you for using\n");
		dlp_AddSyncLogEntry(conn->socket, "Multisync");

		ret = dlp_ReadUserInfo(conn->socket, &User);
		if (ret >= 0) {
			if (User.userID == 0)
				strcpy(User.username, "");
			User.lastSyncPC = 1;
			User.lastSyncDate = time(NULL);
			User.successfulSyncDate = time(NULL);
			if (dlp_WriteUserInfo(conn->socket, &User) < 0) {
				palm_debug(conn, 0, "Unable to write UserInfo");
			} else {
				palm_debug(conn, 2, "Done writing new UserInfo");
			}
		} else {
			palm_debug(conn, 0, "Unable to read UserInfo: %i, %s", ret, dlp_strerror(ret));
		}
	}

	dbCreated = FALSE;

	dlp_EndOfSync(conn->socket, 0);
	sync_set_requestdone(conn->handle);
	palm_debug(conn, 2, "Done syncing");

	//Unlock our Mutex
	g_mutex_unlock(piMutex);
}

/***********************************************************************
 *
 * Function:    sync_disconnect
 *
 * Summary:		gets called by Multisync. Closes the connection with the Palm
 *
 ***********************************************************************/
void sync_disconnect(palm_connection *conn)
{
	//Lock our Mutex
	g_mutex_lock(piMutex);

	if(conn->socket) {
		pi_close(conn->socket);
	}
	conn->socket = 0;
	sync_set_requestdone(conn->handle);

	//Unlock our Mutex
	g_mutex_unlock(piMutex);
	//Free it
	g_mutex_free(piMutex);
	piMutex = NULL;
}

char* short_name()
{
	return("palm-sync");
}

char *long_name()
{
	return("Palm-Sync");
}

sync_object_type object_types()
{
	return(SYNC_OBJECT_TYPE_ANY);
}

void plugin_init(void)
{

}

char *plugin_info()
{
	return("Allows synchronization of devices with PalmOS");
}

gboolean always_connected()
{
	return(FALSE);
}

int plugin_API_version(void)
{
	return(3);
}
