/* 
   MultiSync Nokia (Gnokii) Plugin 
   Copyright (C) 2004 Balint Reczey <balint_reczey@yahoo.com>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2 as
   published by the Free Software Foundation;

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
   SOFTWARE IS DISCLAIMED.

   based on backup_plugin for Multisync: 
   Copyright (C) 2002-2003 Bo Lincoln <lincoln@lysator.liu.se>
*/

/*
 *  
 */
#define _GNU_SOURCE		// for string.h

#include <stdlib.h>
#include <stdio.h>
#include <glib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <assert.h>
#include <unistd.h>
#include <gmodule.h>
#include <gtk/gtkmain.h>
#include <gtk/gtksignal.h>
#include <gnokii.h>
#include <multisync.h>

#include "gnokii_sync.h"
#include "vcard.h"
#include "entrydb.h"

#define DEBUG

/**
 * Compares two gnokii_sync_object type objects by object_type, mem_type and
 * location in that order of precedence
 */

gint
compare_gnokii_sync_object_id(gnokii_sync_object * a,
			      gnokii_sync_object * b)
{
	if (a->object_type == b->object_type) {
		if (a->mem_type == b->mem_type) {
			if (a->location == b->location)
				return 0;
			return (a->location < b->location) ? -1 : 1;
		} else {
			return (a->mem_type < b->mem_type) ? -1 : 1;
		}
	} else {
		return (a->object_type < b->object_type) ? -1 : 1;
	}
}


/**
 * Deletes the given list entry from the list, frees data structure of the entry
 */
GList *delete_gnokii_sync_obj_from_entries(GList ** entries,
					   GList * entry_listobj)
{

	GList *next;
	assert(entry_listobj);
	assert(*entries);
	if (entry_listobj->data
	    && ((gnokii_sync_object *) (entry_listobj->data))->vobj) {
		free(((gnokii_sync_object *) (entry_listobj->data))->vobj);
		g_free(entry_listobj->data);
	}
	next = g_list_next(entry_listobj);
	*entries = g_list_remove_link(*entries, entry_listobj);

	return next;
}

/** 
 * XML parser's startElement fuction
 * Handles <entries> and <entry ...> elements
 */


/**
 * Frees entry list
 */
void gnokii_free_entries(gnokii_connection * conn)
{
	while (conn->entries)
		delete_gnokii_sync_obj_from_entries(&
						    (conn->
						     entries),
						    conn->entries);
}

/**
 * Loads config file, inits connnection to phone 
 *
 */

gboolean gnokii_do_connect(gpointer data)
{
	gnokii_connection *conn = data;
	gnokii_load_entries(conn);
	char *bindir;
	gn_error error;
	char *aux = NULL;
	if (gn_cfg_read(&bindir) < 0) {
		sync_set_requestfailederror
		    ("Cannot read Gnokii config file", conn->sync_pair);
		return FALSE;
	}
	if (!gn_cfg_phone_load("", &(conn->gnokii_state))) {
		sync_set_requestfailederror
		    ("Cannot connect to phone", conn->sync_pair);
		return FALSE;
	};
	// similar to businit() in gnokii.c
	gn_data_clear(&(conn->gnokii_data));
	aux = gn_cfg_get(gn_cfg_info, "global", "use_locking");
	/* Defaults to 'no' */
	if (aux && !strcmp(aux, "yes")) {
		conn->lockfile =
		    gn_device_lock(conn->gnokii_state.config.port_device);
		if (conn->lockfile == NULL) {
			sync_set_requestfailederror
			    ("Cannot get lock for connection device.",
			     conn->sync_pair);;
		}
	}


	error = gn_gsm_initialise(&(conn->gnokii_state));
	if (error != GN_ERR_NONE) {
		char *err_msg;
		err_msg =
		    g_strdup_printf
		    ("Telephone interface init failed: %s\n",
		     gn_error_print(error));
		// release lock
		gn_sm_functions(GN_OP_Terminate, NULL,
				&(conn->gnokii_state));
		if (conn->lockfile) {
			gn_device_unlock(conn->lockfile);
			free(conn->lockfile);
		}


		sync_set_requestfailederror(err_msg, conn->sync_pair);
		g_free(err_msg);
	}



	sync_set_requestdone(conn->sync_pair);
	return FALSE;
}

/**
 * Creates connection structure, schedules connetion initalization
 */
gnokii_connection *sync_connect(sync_pair *
				handle,
				connection_type
				type, sync_object_type object_types)
{
	gnokii_connection *conn;
	conn = g_malloc0(sizeof(gnokii_connection));
	conn->sync_pair = handle;
	conn->conntype = type;
	conn->entries = NULL;
	g_idle_add(gnokii_do_connect, conn);
	return (conn);
}


void gnokii_free_connection(gnokii_connection * conn)
{
	if (conn) {
		// gnokii_save_entries(conn);
		gnokii_free_entries(conn);
		gn_sm_functions(GN_OP_Terminate, NULL,
				&(conn->gnokii_state));
		if (conn->lockfile)
			gn_device_unlock(conn->lockfile);
		g_free(conn);
	}
}


void sync_disconnect(gnokii_connection * conn)
{
	sync_pair *sync_pair = conn->sync_pair;
	gnokii_free_connection(conn);
	sync_set_requestdone(sync_pair);
}

typedef struct {
	gnokii_connection *conn;
	sync_object_type newdbs;
} gnokii_get_changes_arg;

// entry->vobj should not be allocated
void update_phone2entry(gnokii_connection * conn, sync_object_type objtype,
			gnokii_sync_object * entry, gn_memory_type memtype,
			int location)
{

	gn_error error;

	switch (objtype) {
	case SYNC_OBJECT_TYPE_CALENDAR:
		//FIXME
		break;
	case SYNC_OBJECT_TYPE_PHONEBOOK:
		{
			gn_phonebook_entry aux;
			break;
		}
	case SYNC_OBJECT_TYPE_TODO:
		//FIXME
		break;
	default:
		break;
	}
}
gboolean gnokii_do_get_changes(gpointer data)
{
	gnokii_get_changes_arg *arg = data;
	gnokii_connection *conn = arg->conn;
	int t;
	int quit_loop = FALSE, existing = FALSE;
	gn_phonebook_entry ph_entry;
	gn_error error;
	GList *changes = NULL;
	GList *curr = NULL;
	change_info *chinfo;
	sync_object_type newdbs = arg->newdbs;
	chinfo = g_malloc(sizeof(change_info));
	g_free(arg);
	conn->entries = g_list_sort(conn->entries, (GCompareFunc)
				    compare_gnokii_sync_object_id);
	curr = g_list_first(conn->entries);
	conn->gnokii_data.phonebook_entry = &ph_entry;
	// sync calendars
	while (curr != NULL
	       && ((gnokii_sync_object *) (curr->data))->
	       object_type == SYNC_OBJECT_TYPE_CALENDAR)
		curr = g_list_next(curr);
	// sync phonebook
	//FIXME 
	/*
	   if ((conn->commondata.object_types & SYNC_OBJECT_TYPE_PHONEBOOK)) {
	   // phonebook changes not requested
	   while (curr != NULL
	   && ((gnokii_sync_object *) (curr->data))->
	   object_type == SYNC_OBJECT_TYPE_PHONEBOOK);
	   } else {
	 */
	// TODO currently reads ME memory only
	ph_entry.memory_type = gn_str2memory_type("ME");
	quit_loop = FALSE;
	for (t = 1; !quit_loop; t++) {
		existing = FALSE;
		if (curr != NULL
		    &&
		    ((gnokii_sync_object *) (curr->data))->
		    object_type ==
		    SYNC_OBJECT_TYPE_PHONEBOOK
		    &&
		    ((gnokii_sync_object *) (curr->data))->location == t)
			existing = TRUE;
		else {
			if (curr != NULL
			    &&
			    ((gnokii_sync_object *) (curr->
						     data))->
			    object_type ==
			    SYNC_OBJECT_TYPE_PHONEBOOK
			    &&
			    ((gnokii_sync_object *) (curr->
						     data))->
			    location < t) {
				curr = g_list_next(curr);
				if (curr != NULL
				    &&
				    ((gnokii_sync_object *) (curr->
							     data))->
				    location == t)
					existing = TRUE;
			}

		}
		ph_entry.location = t;
		error =
		    gn_sm_functions(GN_OP_ReadPhonebook,
				    &(conn->gnokii_data),
				    &(conn->gnokii_state));
		switch (error) {
		case GN_ERR_NONE:{
				// found a valid entry in phone
				if (existing) {
					char *tmp_str, *location_str;
					location_str =
					    g_strdup_printf
					    ("ME%d", ph_entry.location);
					gn_phonebook2vcard_str
					    (&tmp_str,
					     &ph_entry, location_str);
					g_free(location_str);
					assert(((gnokii_sync_object
						 *) (curr->data))->
					       vobj != NULL);
					if (strcmp
					    (((gnokii_sync_object
					       *) (curr->data))->
					     vobj, tmp_str)) {
						// modified in phone 
#ifdef DEBUG
						printf
						    ("modified entry:\n %s ---- \n %s",
						     ((gnokii_sync_object
						       *) (curr->data))->
						     vobj, tmp_str);
#endif
						g_free(((gnokii_sync_object
							 *) (curr->data))->
						       vobj);
						((gnokii_sync_object
						  *) (curr->data))->vobj =
				 tmp_str;
						((gnokii_sync_object
						  *) (curr->data))->
				 changed = SYNC_OBJ_MODIFIED;
					}
				} else {
#ifdef DEBUG
					printf("New entry in phone\n");
#endif
					// new entry in phone
					char *tmp_str, *location_str;
					gnokii_sync_object *entry;
					entry =
					    g_malloc(sizeof
						     (gnokii_sync_object));
					entry->mem_type =
					    ph_entry.memory_type;
					entry->location = t;
					entry->
					    object_type =
					    SYNC_OBJECT_TYPE_PHONEBOOK;
					entry->changed = SYNC_OBJ_ADDED;
					// get vcard from ph_entry
					location_str =
					    g_strdup_printf("ME%d",
							    //FIXME !!! internal memory supported only
							    ph_entry.
							    location);
					gn_phonebook2vcard_str
					    (&tmp_str, &ph_entry,
					     location_str);
					g_free(location_str);
					entry->vobj = tmp_str;
					conn->entries =
					    g_list_prepend(conn->
							   entries, entry);
					// if (curr == NULL) curr = conn->entries;
				}
				break;
			}
		case GN_ERR_EMPTYLOCATION:{
				if (existing) {
					//deleted in phone
					((gnokii_sync_object
					  *) (curr->data))->
			 changed = SYNC_OBJ_HARDDELETED;
				}
				break;
			}
		case GN_ERR_INVALIDLOCATION:{
				quit_loop = TRUE;
				break;
			}

		default:
			quit_loop = TRUE;
			fprintf(stderr, "Error: %s\n",
				gn_error_print(error));
			break;
		}

	}
	/*
	   }
	 */

	// sync todo

	while (curr != NULL
	       && ((gnokii_sync_object *) (curr->data))->
	       object_type == SYNC_OBJECT_TYPE_TODO)
		curr = g_list_next(curr);
	// parse entries list for changed objects
	curr = conn->entries;
	while (curr) {

#ifdef DEBUG

		printf("changed: %d\n",
		       ((gnokii_sync_object *) (curr->data))->changed);
#endif
		if (((gnokii_sync_object *) (curr->data))->
		    changed == SYNC_OBJ_MODIFIED
		    || ((gnokii_sync_object *) (curr->data))->
		    changed == SYNC_OBJ_ADDED
		    || ((gnokii_sync_object *) (curr->data))->
		    changed == SYNC_OBJ_HARDDELETED
		    || ((gnokii_sync_object *) (curr->data))->
		    object_type | newdbs) {
			// should be returned as changed object
			changed_object *chobj;
			chobj = g_malloc(sizeof(changed_object));
			chobj->comp =
			    g_strdup(((gnokii_sync_object
				       *) (curr->data))->vobj);
			chobj->uid =
			    g_strdup_printf("mem=%d-loc=%d",
					    ((gnokii_sync_object
					      *) (curr->data))->
					    mem_type,
					    ((gnokii_sync_object
					      *) (curr->data))->location);
			chobj->removepriority = NULL;
			chobj->change_type =
			    ((gnokii_sync_object *) (curr->data))->changed;
			chobj->object_type =
			    ((gnokii_sync_object *) (curr->data))->
			    object_type;
			changes = g_list_prepend(changes, chobj);
		}
		curr = g_list_next(curr);
	}

	chinfo->changes = changes;
	chinfo->newdbs = 0;
//          SYNC_OBJECT_TYPE_UNKNOWN | SYNC_OBJECT_TYPE_CALENDAR |
//          SYNC_OBJECT_TYPE_TODO;
	sync_set_requestdata(chinfo, conn->sync_pair);
	return (FALSE);
}

void get_changes(gnokii_connection * conn, sync_object_type newdbs)
{
	gnokii_get_changes_arg *arg;
	arg = g_malloc0(sizeof(gnokii_get_changes_arg));
	arg->conn = conn;
	arg->newdbs = newdbs;
	g_idle_add(gnokii_do_get_changes, arg);
}


void sync_done(gnokii_connection * conn, gboolean success)
{

	if (success) {

		GList *entry;
		entry = g_list_first(conn->entries);
		while (entry) {
			assert((gnokii_sync_object *) (entry->data));
			switch (((gnokii_sync_object *) (entry->
							 data))->changed) {
			case SYNC_OBJ_ADDED:
			case SYNC_OBJ_MODIFIED:
				{
					((gnokii_sync_object
					  *) (entry->data))->changed = 0;
					entry = g_list_next(entry);
					break;
				}

			case SYNC_OBJ_HARDDELETED:
				{
					entry =
					    delete_gnokii_sync_obj_from_entries
					    (&(conn->entries), entry);
					break;
				}
			default:
				entry = g_list_next(entry);;
			}
		}


		gnokii_save_entries(conn);
	}
	sync_set_requestdone(conn->sync_pair);
}

/*
void backup_hard_delete(backup_connection * conn, backup_object * entry)
{
	char *fname;
	if (entry) {
		conn->entries = g_list_remove(conn->entries, entry);
		fname =
		    g_strdup_printf("%s/%s", conn->backupdir, entry->uid);
		unlink(fname);
		g_free(fname);
		if (entry->uid)
			g_free(entry->uid);
		g_free(entry);
	}
}
*/
/**
 * Finds free location for an object from the given object type in the given memory.
 */


int find_free_mem(gnokii_connection * conn, sync_object_type objtype,
		  gn_memory_type memtype)
{
	int i = -2;
	gn_error error;

	switch (objtype) {
	case SYNC_OBJECT_TYPE_CALENDAR:
		{
			gn_calnote_list cal_list;
			gn_calnote cal;
			for (i = 1;; i++) {
				memset(&(cal), 0, sizeof(gn_calnote));
				memset(&(cal_list), 0,
				       sizeof(gn_calnote_list));
				cal.location = i;
				gn_data_clear(&(conn->gnokii_data));
				conn->gnokii_data.calnote = &cal;
				conn->gnokii_data.calnote_list = &cal_list;
				error =
				    gn_sm_functions
				    (GN_OP_GetCalendarNote,
				     &(conn->gnokii_data),
				     &(conn->gnokii_state));
				if (error != GN_ERR_NONE
				    && error != GN_ERR_EMPTYLOCATION) {
					return -1;
					break;
				}
				if (error == GN_ERR_EMPTYLOCATION) {
					break;
				}
			}
			break;
		}

	case SYNC_OBJECT_TYPE_PHONEBOOK:
		{
			gn_phonebook_entry aux;
			conn->gnokii_data.phonebook_entry = &aux;
			for (i = 1;; i++) {
				memset(&aux, 0,
				       sizeof(gn_phonebook_entry));
				conn->gnokii_data.
				    phonebook_entry->location = i;
				conn->gnokii_data.
				    phonebook_entry->memory_type = memtype;
				error =
				    gn_sm_functions
				    (GN_OP_ReadPhonebook,
				     &(conn->gnokii_data),
				     &(conn->gnokii_state));
				if (error != GN_ERR_NONE
				    && error != GN_ERR_EMPTYLOCATION) {
					return -1;
					break;
				}
				if (error == GN_ERR_EMPTYLOCATION) {
					break;
				}
			}
			break;
		}
	case SYNC_OBJECT_TYPE_TODO:
		{
			gn_todo_list todo_list;
			gn_todo todo;
			for (i = 1;; i++) {
				memset(&(todo), 0, sizeof(gn_calnote));
				memset(&(todo_list), 0,
				       sizeof(gn_calnote_list));
				todo.location = i;
				gn_data_clear(&(conn->gnokii_data));
				conn->gnokii_data.todo = &todo;
				conn->gnokii_data.todo_list = &todo_list;
				error =
				    gn_sm_functions
				    (GN_OP_GetToDo,
				     &(conn->gnokii_data),
				     &(conn->gnokii_state));
				if (error != GN_ERR_NONE
				    && error != GN_ERR_EMPTYLOCATION) {
					return -1;
					break;
				}
				if (error == GN_ERR_EMPTYLOCATION) {
					break;
				}
			}
			break;
		}
	default:
		return -2;
	}
	return i;
}

void gnokii_modify_or_delete(gnokii_connection *
			     conn, char *comp,
			     char *uid,
			     sync_object_type
			     objtype, char *uidret,
			     int *uidretlen, gboolean softdelete)
{

	assert(conn);
	gnokii_sync_object *entry = NULL;
	GList *list_obj = NULL;
	// No uid and no data given. Can't do anything 
	if (!uid && !comp) {
		sync_set_requestfailed(conn->sync_pair);
		return;
	}


	if (uid) {
		int mem_type;
		entry = g_malloc0(sizeof(gnokii_sync_object));
		sscanf(uid, "mem=%d-loc=%d", &mem_type,
		       &(entry->location));
		entry->mem_type = mem_type;
		entry->object_type = objtype;
		if (!
		    (list_obj =
		     g_list_find_custom(conn->entries, entry,
					(GCompareFunc)
					compare_gnokii_sync_object_id))) {
			// object not found
			// Non-existing UID given
			sync_set_requestfailed(conn->sync_pair);
			return;
		}

		if (comp) {
			// modify
			switch (objtype) {
			case SYNC_OBJECT_TYPE_CALENDAR:
				//FIXME
				break;
			case SYNC_OBJECT_TYPE_PHONEBOOK:
				{
					char *location_str;
					gn_phonebook_entry ph_entry;
					gn_error error;
					memset(&ph_entry, 0,
					       sizeof(gn_phonebook_entry));
					ph_entry.memory_type =
					    entry->mem_type;
					if (ph_entry.memory_type ==
					    GN_MT_XX) {
						fprintf(stderr,
							"Unknown memory type!\n");
						sync_set_requestfailed
						    (conn->sync_pair);
						return;
					}


					ph_entry.location =
					    entry->location;
					gn_vcard_str2phonebook
					    (comp, &ph_entry);
					ph_entry.empty = 0;
					conn->gnokii_data.
					    phonebook_entry = &ph_entry;
					gn_phonebook_entry_sanitize
					    (&ph_entry);
					error =
					    gn_sm_functions
					    (GN_OP_WritePhonebook,
					     &(conn->gnokii_data),
					     &(conn->gnokii_state));
					switch (error) {
					case GN_ERR_NONE:
						// update in entry list

						free(((gnokii_sync_object
						       *) (list_obj->
							   data))->vobj);
						location_str =
						    g_strdup_printf("ME%d",
								    ph_entry.
								    location);
						gn_phonebook2vcard_str
						    (&(((gnokii_sync_object
							 *) (list_obj->
							     data))->vobj),
						     &ph_entry,
						     location_str);
						g_free(location_str);
						sync_set_requestdone
						    (conn->sync_pair);
						break;
					default:
						fprintf(stderr,
							"Phonebook entry modification FAILED (%s): memory type: %d, loc: %d\n",
							gn_error_print
							(error),
							ph_entry.
							memory_type,
							ph_entry.location);
						sync_set_requestfailed
						    (conn->sync_pair);
						break;
					}

					//FIXME
					break;
				}
			case SYNC_OBJECT_TYPE_TODO:
				//FIXME
				break;
			default:{
					// invalid sync_object_type  
					sync_set_requestfailed
					    (conn->sync_pair);
					break;
				}
			}

		} else {
			//delete
			switch (objtype) {
			case SYNC_OBJECT_TYPE_CALENDAR:
				//FIXME
				break;
			case SYNC_OBJECT_TYPE_PHONEBOOK:
				{
					gn_phonebook_entry ph_entry;
					gn_error error;
					ph_entry.memory_type =
					    entry->mem_type;
					if (ph_entry.memory_type ==
					    GN_MT_XX) {
						fprintf(stderr,
							"Unknown memory type!\n");
						sync_set_requestfailed
						    (conn->sync_pair);
						return;
					}


					ph_entry.location =
					    entry->location;
					ph_entry.empty = !(0);
					conn->gnokii_data.
					    phonebook_entry = &ph_entry;
					error =
					    gn_sm_functions
					    (GN_OP_DeletePhonebook,
					     &(conn->gnokii_data),
					     &(conn->gnokii_state));
					switch (error) {
					case GN_ERR_NONE:
						// remove from linked entry list
						delete_gnokii_sync_obj_from_entries
						    (&
						     (conn->
						      entries), list_obj);
						g_free(list_obj);
						sync_set_requestdone
						    (conn->sync_pair);
						break;
					default:
						fprintf(stderr,
							"Phonebook entry removal FAILED (%s): memory type: %d, loc: %d\n",
							gn_error_print
							(error),
							ph_entry.
							memory_type,
							ph_entry.location);
						break;
					}

					//FIXME
					break;
				}
			case SYNC_OBJECT_TYPE_TODO:
				//FIXME
				break;
			default:{
					// invalid sync_object_type  
					sync_set_requestfailed
					    (conn->sync_pair);
					break;
				}
			}
		}

	} else {
		// add
		printf(" add : %s\n", comp);
		entry = g_malloc0(sizeof(gnokii_sync_object));
		entry->object_type = objtype;
		switch (objtype) {
		case SYNC_OBJECT_TYPE_CALENDAR:
			//FIXME
			break;
		case SYNC_OBJECT_TYPE_PHONEBOOK:
			{
				char *tmp_str = NULL;
				char *location_str = NULL;
				gn_phonebook_entry ph_entry;
				gn_error error;
				memset(&ph_entry, 0,
				       sizeof(gn_phonebook_entry));
				// find location for added entry
				//TODO currently uses only phone memory (ME)
				ph_entry.memory_type = GN_MT_ME;
				ph_entry.location =
				    find_free_mem(conn, objtype,
						  ph_entry.location);

				switch (ph_entry.location) {
				case -1:{
						sync_set_requestmsg
						    (SYNC_MSG_DATABASEFULLERROR,
						     conn->sync_pair);

						return;
						break;
					}
				case -2:
					{
						sync_set_requestfailed
						    (conn->sync_pair);
						return;
						break;
					}
				}

				entry->location = ph_entry.location;
				entry->mem_type = ph_entry.memory_type;
				ph_entry.empty = FALSE;
				gn_vcard_str2phonebook(comp, &ph_entry);
				conn->gnokii_data.phonebook_entry =
				    &ph_entry;
				gn_phonebook_entry_sanitize(&ph_entry);
				error =
				    gn_sm_functions
				    (GN_OP_WritePhonebook,
				     &(conn->gnokii_data),
				     &(conn->gnokii_state));
				switch (error) {
				case GN_ERR_NONE:{
						// update in entry list

						list_obj =
						    conn->entries =
						    g_list_prepend
						    (conn->entries, entry);
						free(((gnokii_sync_object
						       *) (list_obj->
							   data))->vobj);
						location_str =
						    g_strdup_printf("ME%d",
								    ph_entry.
								    location);
						gn_phonebook2vcard_str
						    (&(((gnokii_sync_object
							 *) (list_obj->
							     data))->vobj),
						     &ph_entry,
						     location_str);
						g_free(location_str);
						((gnokii_sync_object
						  *) (list_obj->data))->
				 changed = 0;
						tmp_str =
						    g_strdup_printf
						    ("mem=%d-loc=%d",
						     ph_entry.memory_type,
						     ph_entry.location);
						strncpy(uidret, tmp_str,
							*uidretlen);
						*uidretlen =
						    strlen(uidret);
						g_free(tmp_str);
						sync_set_requestdone
						    (conn->sync_pair);
						break;
					}
				default:{
						fprintf(stderr,
							"Phonebook entry add FAILED (%s): memory type: %d, loc: %d\n",
							gn_error_print
							(error),
							ph_entry.
							memory_type,
							ph_entry.location);
						sync_set_requestfailed
						    (conn->sync_pair);
						break;
					}
				}

				//FIXME
				break;
			}
		case SYNC_OBJECT_TYPE_TODO:
			//FIXME
			break;
		default:{
				// invalid sync_object_type  
				sync_set_requestfailed(conn->sync_pair);
				break;
			}

		}

	}
	gnokii_save_entries(conn);
	// sync_set_requestdone(conn->sync_pair);
}

void syncobj_modify(gnokii_connection * conn,
		    char *comp,
		    char *uid,
		    sync_object_type objtype, char *uidret, int *uidretlen)
{
	gnokii_modify_or_delete(conn, comp, uid, objtype,
				uidret, uidretlen, FALSE);
}

void syncobj_delete(gnokii_connection * conn,
		    char *uid, sync_object_type objtype, int softdelete)
{
	gnokii_modify_or_delete(conn, NULL, uid, objtype,
				NULL, NULL, softdelete);
}

// Return TRUE if this client does not have to be polled 
// (i.e. can be constantly connected)
gboolean always_connected()
{
	return (TRUE);
}

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

char *long_name()
{
	return ("Mobile Phone (Gnokii)");
}

// Return the types of objects that this client handle
sync_object_type object_types()
{
	return (SYNC_OBJECT_TYPE_PHONEBOOK);
}


void plugin_init(void)
{
}

char *plugin_info(void)
{
	return ("Connects to mobile phones through Gnokii.");
}

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