/* (c) 2003 by Marcin Wiacek */
/* (c) 2006-2007 by Michal Cihar */

/**
 * \file obexgen.c
 * @author Michal Čihař
 * @author Marcin Wiacek
 */
/**
 * @addtogroup Phone
 * @{
 */
/**
 * \defgroup OBEXPhone OBEX phones communication
 * Generic OBEX access to phones, made addording to OBEX specification
 * version 1.3 and IrMC specification version 1.1 as available from IrDA
 * <http://www.irda.org>.
 *
 * @author Michal Cihar
 * @author Marcin Wiacek
 * @{
 */

#include <string.h>
#include <time.h>

#include <gammu-config.h>

#include "../../gsmcomon.h"
#include "../../misc/coding/coding.h"
#include "../../gsmphones.h"
#include "../../gsmstate.h"
#include "../../service/gsmmisc.h"
#include "../../protocol/obex/obex.h"
#include "obexfunc.h"
#include "mobex.h"
#include "../../../libgammu/misc/string.h"

#ifdef GSM_ENABLE_OBEXGEN

/* Forward definitions */
GSM_Error OBEXGEN_GetModel(GSM_StateMachine *s);

/**
 * How many read attempts will happen.
 */
#define OBEX_TIMEOUT 10

/**
 * Handles various error codes in OBEX protocol.
 */
static GSM_Error OBEXGEN_HandleError(GSM_Protocol_Message *msg, GSM_StateMachine *s)
{
	switch (msg->Type & 0x7f) {
		/* HTTP based codes */
		case 0x40:
		case 0x45:
		case 0x47:
		case 0x48:
		case 0x4d:
		case 0x4e:
		case 0x4f:
			smprintf(s, "Bad request (0x%02x)\n", msg->Type);
			return ERR_BUG;
		case 0x41:
		case 0x42:
		case 0x43:
		case 0x46: /* Not acceptable */
		case 0x49: /* Conflict */
			smprintf(s, "Security error (0x%02x)\n", msg->Type);
			return ERR_PERMISSION;
		case 0x44:
		case 0x4a:
			smprintf(s, "File not found (0x%02x)\n", msg->Type);
			return ERR_FILENOTEXIST;
		case 0x50: /* Internal server error */
		case 0x51: /* Not implemented */
		case 0x53: /* Service unavailable */
			smprintf(s, "Internal phone error (0x%02x)\n", msg->Type);
			return ERR_PHONE_INTERNAL;
		/* OBEX specials */
		case 0x60:
			smprintf(s, "Database full\n");
			return ERR_FULL;
		case 0x61:
			smprintf(s, "Database locked\n");
			return ERR_PERMISSION;
		case 0x4c:
			smprintf(s, "Precondition failed\n");
			return ERR_NOTSUPPORTED;
	}
	smprintf(s, "Unknown OBEX error (0x%02x)\n", msg->Type);
	return ERR_UNKNOWN;
}

/**
 * Adds connection ID block to the request.
 */
void OBEXGEN_AddConnectionID(GSM_StateMachine *s, char *Buffer, int *Pos)
{
	GSM_Phone_OBEXGENData *Priv = &s->Phone.Data.Priv.OBEXGEN;

	Buffer[(*Pos)++] = 0xCB;
	Buffer[(*Pos)++] = Priv->connection_id[0];
	Buffer[(*Pos)++] = Priv->connection_id[1];
	Buffer[(*Pos)++] = Priv->connection_id[2];
	Buffer[(*Pos)++] = Priv->connection_id[3];
}

/**
 * \defgroup OBEXinit OBEX initialisation and terminating
 * \ingroup OBEXPhone
 * @{
 */

static GSM_Error OBEXGEN_ReplyConnect(GSM_Protocol_Message *msg, GSM_StateMachine *s)
{
	size_t i;
	GSM_Phone_OBEXGENData *Priv = &s->Phone.Data.Priv.OBEXGEN;

	switch (msg->Type) {
	case 0xA0:
		smprintf(s,"Connected/disconnected OK\n");
		/* Sample: 10 |00 |20 |00 |CB |00 |00 |00 |10 |4AJ|00 |08 |4DM|4FO|42B|45E|58X */
		/* Byte 0 - OBEX version */
		/* Byte 1 - flags */
		/* Bytes 2,3 - maximum size of packet */
		if (msg->Length >= 4) {
			s->Phone.Data.Priv.OBEXGEN.FrameSize = msg->Buffer[2]*256 + msg->Buffer[3];
			smprintf(s,"Maximum size of frame is %i 0x%x\n",s->Phone.Data.Priv.OBEXGEN.FrameSize,s->Phone.Data.Priv.OBEXGEN.FrameSize);
		}
		/* Remaining bytes - optional headers */
		for (i = 4; i < msg->Length;) {
			switch (msg->Buffer[i]) {
				case 0xCB:
					/* Connection ID */
					memcpy(Priv->connection_id, msg->Buffer + i + 1, 4);
					i += 5;
					break;
				case 0x4A:
					/* Peer */
					/* We just skip it */
					i += (msg->Buffer[i + 1] << 8) + msg->Buffer[i + 2];
					break;
				default:
					smprintf(s, "Unknown OBEX header: 0x%02X, skipping rest\n", msg->Buffer[i]);
					i = msg->Length;
					break;
			}
		}
		return ERR_NONE;
	case 0xC0:
		smprintf(s, "Wrong request sent to phone!\n");
		return ERR_BUG;
	case 0xC1:
	case 0xC3:
		smprintf(s, "Connection not allowed!\n");
		return ERR_SECURITYERROR;
	}
	/* Generic error codes */
	if ((msg->Type & 0x7f) >= 0x40) {
		return OBEXGEN_HandleError(msg, s);
	}
	return ERR_UNKNOWNRESPONSE;
}

/**
 * Disconnects from OBEX service
 */
GSM_Error OBEXGEN_Disconnect(GSM_StateMachine *s)
{
	GSM_Error error;
	smprintf(s, "Disconnecting\n");
	error = GSM_WaitFor (s, NULL, 0, 0x81, OBEX_TIMEOUT, ID_Initialise);
	if (error == ERR_BUG) return ERR_NONE;
	return error;
}

/**
 * Connects to OBEX using selected service
 */
GSM_Error OBEXGEN_Connect(GSM_StateMachine *s, OBEX_Service service)
{
	GSM_Error	error = ERR_NONE;
	int		Current=4;
	unsigned char 	req2[200];
	unsigned char 	req[200] = {
		0x10,			/* Version 1.0 			*/
		0x00,			/* no flags 			*/
		0x04,0x00};		/* max size of packet (changed below for m-obex) */

	/* Are we requsted for initial service? */
	if (service == 0) {
		/* If not set, stay as we are configured now */
		if (s->Phone.Data.Priv.OBEXGEN.InitialService == 0) {
			return ERR_NONE;
		}
		service = s->Phone.Data.Priv.OBEXGEN.InitialService;
	}

	/* Don't we already have correct service? */
	if (service == s->Phone.Data.Priv.OBEXGEN.Service) return ERR_NONE;

	/* Disconnect from current service, if we were connected */
	if (s->Phone.Data.Priv.OBEXGEN.Service != 0) {
		error = OBEXGEN_Disconnect(s);
		if (error != ERR_NONE) return error;
	}

	switch (service) {
	case OBEX_None:
		smprintf(s, "No service requested\n");
		break;
	case OBEX_IRMC:
		smprintf(s, "IrMC service requested\n");
		/* IrMC Service UUID */
		req2[0] = 'I'; req2[1] = 'R'; req2[2] = 'M';
		req2[3] = 'C'; req2[4] = '-'; req2[5] = 'S';
		req2[6] = 'Y'; req2[7] = 'N'; req2[8] = 'C';

		/* Target block */
		OBEXAddBlock(req, &Current, 0x46, req2, 9);
		break;
	case OBEX_BrowsingFolders:
		smprintf(s, "Folder Browsing service requested\n");
		/* Folder Browsing Service UUID */
		req2[0] = 0xF9; req2[1] = 0xEC; req2[2] = 0x7B;
		req2[3] = 0xC4; req2[4] = 0x95; req2[5] = 0x3C;
		req2[6] = 0x11; req2[7] = 0xD2; req2[8] = 0x98;
		req2[9] = 0x4E; req2[10]= 0x52; req2[11]= 0x54;
		req2[12]= 0x00; req2[13]= 0xDC; req2[14]= 0x9E;
		req2[15]= 0x09;

		/* Target block */
		OBEXAddBlock(req, &Current, 0x46, req2, 16);
		break;
	case OBEX_m_OBEX:
		/* Bigger frame size for m-OBEX */
		req[2] = 0x20;
		/* IrMC Service UUID */
		req2[0] = 'M'; req2[1] = 'O'; req2[2] = 'B';
		req2[3] = 'E'; req2[4] = 'X';

		req[0] = 0x11; /* Phones seem to require OBEX 1.1 */

		/* Target block */
		OBEXAddBlock(req, &Current, 0x46, req2, 5);
		break;
	}

	/* Remember current service */
	s->Phone.Data.Priv.OBEXGEN.Service = service;

	smprintf(s, "Connecting\n");
	return GSM_WaitFor (s, req, Current, 0x80, OBEX_TIMEOUT, ID_Initialise);
}

/**
 * Clears any flags in IrMC capabilities structure.
 */
void IRMC_InitCapabilities(IRMC_Capability *Cap)
{
	Cap->IEL = -1;
	Cap->HD = FALSE;
}

/**
 * Initializes OBEX internal variables. To be used by other who need
 * OBEX protocol, but don't need it's init.
 */
GSM_Error OBEXGEN_InitialiseVars(GSM_StateMachine *s)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	Priv->Service = 0;
	Priv->InitialService = 0;
	Priv->PbLUID = NULL;
	Priv->PbLUIDCount = 0;
	Priv->PbIndex = NULL;
	Priv->PbIndexCount = 0;
	Priv->PbData = NULL;
	Priv->PbCount = -1;
	Priv->CalLUID = NULL;
	Priv->CalLUIDCount = 0;
	Priv->CalIndex = NULL;
	Priv->CalIndexCount = 0;
	Priv->CalData = NULL;
	Priv->TodoLUID = NULL;
	Priv->TodoLUIDCount = 0;
	Priv->TodoIndex = NULL;
	Priv->TodoIndexCount = 0;
	Priv->CalCount = -1;
	Priv->TodoCount = -1;
	Priv->CalOffsets = NULL;
	Priv->TodoOffsets = NULL;
	Priv->UpdateCalLUID = FALSE;
	Priv->UpdatePbLUID = FALSE;
	Priv->UpdateTodoLUID = FALSE;
	Priv->UpdateNoteLUID = FALSE;
	Priv->OBEXCapability = NULL;
	Priv->OBEXDevinfo = NULL;
	Priv->NoteLUID = NULL;
	Priv->NoteLUIDCount = 0;
	Priv->NoteIndex = NULL;
	Priv->NoteIndexCount = 0;
	Priv->NoteData = NULL;
	Priv->NoteCount = -1;
	Priv->NoteOffsets = NULL;
	Priv->m_obex_appdata = NULL;
	Priv->m_obex_appdata_len = 0;
        Priv->m_obex_calendar_nextid = 0;
        Priv->m_obex_calendar_nexterror = 0;
        Priv->m_obex_calendar_buffer = NULL;
        Priv->m_obex_calendar_buffer_pos = 0;
        Priv->m_obex_calendar_buffer_size = 0;
        Priv->m_obex_contacts_nextid = 0;
        Priv->m_obex_contacts_nexterror = 0;
        Priv->m_obex_contacts_buffer = NULL;
        Priv->m_obex_contacts_buffer_pos = 0;
        Priv->m_obex_contacts_buffer_size = 0;

	/* Default connection ID */
	Priv->connection_id[0] = 0;
	Priv->connection_id[1] = 0;
	Priv->connection_id[2] = 0;
	Priv->connection_id[3] = 1;

	IRMC_InitCapabilities(&(Priv->NoteCap));
	IRMC_InitCapabilities(&(Priv->PbCap));
	IRMC_InitCapabilities(&(Priv->CalCap));

	return ERR_NONE;
}


/**
 * Initializes OBEX connection in desired mode as defined by config.
 */
GSM_Error OBEXGEN_Initialise(GSM_StateMachine *s)
{
	GSM_Error	error = ERR_NONE;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	gboolean service_forced = FALSE;

	/* Init variables */
	error = OBEXGEN_InitialiseVars(s);
	if (error != ERR_NONE) return error;


	s->Phone.Data.VerNum 		= 0;
	s->Phone.Data.Version[0] 	= 0;
	s->Phone.Data.Manufacturer[0] 	= 0;

	/* Detect connection for desired type */
	Priv->InitialService = OBEX_BrowsingFolders;
	smprintf(s, "Connected using model %s\n", s->CurrentConfig->Model);
	if (strcmp(s->CurrentConfig->Model, "obex") == 0) {
		Priv->InitialService = OBEX_BrowsingFolders;
		service_forced = TRUE;
	} else if (strcmp(s->CurrentConfig->Model, "obexfs") == 0) {
		Priv->InitialService = OBEX_BrowsingFolders;
		service_forced = TRUE;
	} else if (strcmp(s->CurrentConfig->Model, "obexirmc") == 0) {
		Priv->InitialService = OBEX_IRMC;
		service_forced = TRUE;
	} else if (strcmp(s->CurrentConfig->Model, "seobex") == 0) {
		Priv->InitialService = OBEX_IRMC;
		service_forced = TRUE;
	} else if (strcmp(s->CurrentConfig->Model, "mobex") == 0) {
		Priv->InitialService = OBEX_m_OBEX;
		service_forced = TRUE;
	} else if (strcmp(s->CurrentConfig->Model, "obexnone") == 0) {
		Priv->InitialService = OBEX_None;
		service_forced = TRUE;
	}

	/* Grab OBEX capability */
	if (!service_forced || Priv->InitialService == OBEX_BrowsingFolders) {
		error = OBEXGEN_Connect(s, OBEX_BrowsingFolders);
		if (error == ERR_NONE) {
			error = OBEXGEN_GetTextFile(s, "", &(Priv->OBEXCapability));
		}
	}

	/* Grab IrMC devinfo */
	if (!service_forced || Priv->InitialService == OBEX_IRMC) {
		error = OBEXGEN_Connect(s, OBEX_IRMC);
		if (error == ERR_NONE) {
			error = OBEXGEN_GetTextFile(s, "", &(Priv->OBEXDevinfo));
		}
	}

	/* Initialise connection */
	error = OBEXGEN_Connect(s, 0);
	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

/**
 * Frees internal OBEX variables.
 *
 * \todo This should be done on terminate, but not on termination from
 * Sony-Ericsson.
 */
void OBEXGEN_FreeVars(GSM_StateMachine *s)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	int i=0;

	for (i = 1; i <= Priv->PbLUIDCount; i++) {
		free(Priv->PbLUID[i]);
		Priv->PbLUID[i]=NULL;
	}
	free(Priv->PbLUID);
	Priv->PbLUID=NULL;
	free(Priv->PbData);
	Priv->PbData=NULL;

	for (i = 1; i <= Priv->NoteLUIDCount; i++) {
		free(Priv->NoteLUID[i]);
		Priv->NoteLUID[i]=NULL;
	}
	free(Priv->NoteLUID);
	Priv->NoteLUID=NULL;
	free(Priv->NoteData);
	Priv->NoteData=NULL;

	for (i = 1; i <= Priv->CalLUIDCount; i++) {
		free(Priv->CalLUID[i]);
		Priv->CalLUID[i]=NULL;
	}
	free(Priv->CalLUID);
	Priv->CalLUID=NULL;
	free(Priv->CalData);
	Priv->CalData=NULL;

	for (i = 1; i <= Priv->TodoLUIDCount; i++) {
		free(Priv->TodoLUID[i]);
		Priv->TodoLUID[i]=NULL;
	}
	free(Priv->TodoLUID);
	Priv->TodoLUID=NULL;
	free(Priv->PbIndex);
	Priv->PbIndex=NULL;
	free(Priv->NoteIndex);
	Priv->NoteIndex=NULL;
	free(Priv->CalIndex);
	Priv->CalIndex=NULL;
	free(Priv->TodoIndex);
	Priv->TodoIndex=NULL;
	free(Priv->PbOffsets);
	Priv->PbOffsets=NULL;
	free(Priv->NoteOffsets);
	Priv->NoteOffsets=NULL;
	free(Priv->CalOffsets);
	Priv->CalOffsets=NULL;
	free(Priv->TodoOffsets);
	Priv->TodoOffsets=NULL;
	free(Priv->OBEXCapability);
	Priv->OBEXCapability=NULL;
	free(Priv->OBEXDevinfo);
	Priv->OBEXDevinfo=NULL;

        free(Priv->m_obex_calendar_buffer);
        Priv->m_obex_calendar_buffer = NULL;
        free(Priv->m_obex_calendar_buffer);
        Priv->m_obex_contacts_buffer = NULL;
}

/**
 * Terminates OBEX connection.
 */
GSM_Error OBEXGEN_Terminate(GSM_StateMachine *s)
{
	return OBEXGEN_Disconnect(s);
}

/*@}*/

/**
 * \defgroup OBEXfiles OBEX transfer implementation
 * \ingroup OBEXPhone
 * @{
 */

/**
 * Merges filename from path and file
 */
void OBEXGEN_CreateFileName(unsigned char *Dest, unsigned char *Path, unsigned char *Name)
{
	size_t len;

	/* Folder name */
	CopyUnicodeString(Dest, Path);
	len = UnicodeLength(Dest);
	/* Append slash */
	if (len > 0) {
		Dest[2*len + 0] = 0;
		Dest[2*len + 1] = '/';
		len++;
	}
	/* And add filename */
	CopyUnicodeString(Dest + 2*len, Name);
}

/**
 * Grabs path part from complete path
 */
static void OBEXGEN_FindNextDir(unsigned char *Path, size_t *Pos, unsigned char *Return)
{
	int Retlen = 0;

	while(1) {
		if (Path[(*Pos)*2] == 0 && Path[(*Pos)*2+1] == 0) break;
		if (Path[(*Pos)*2] == 0 && Path[(*Pos)*2+1] == '/') {
			(*Pos)++;
			break;
		}
		Return[Retlen*2]     = Path[(*Pos)*2];
		Return[Retlen*2+1]   = Path[(*Pos)*2+1];
		(*Pos)++;
		Retlen++;
	}
	Return[Retlen*2]     = 0;
	Return[Retlen*2+1]   = 0;
}

/**
 * Reply handler for changing path
 */
static GSM_Error OBEXGEN_ReplyChangePath(GSM_Protocol_Message *msg, GSM_StateMachine *s)
{
	/* Non standard Sharp GX reply */
	if (msg->Type == 0x80) {
		return ERR_FILENOTEXIST;
	}

	/* Generic error codes */
	if ((msg->Type & 0x7f) >= 0x40) {
		return OBEXGEN_HandleError(msg, s);
	}
	switch (msg->Type) {
	case 0xA0:
		smprintf(s,"Path set OK\n");
		return ERR_NONE;
	case 0xA1:
		smprintf(s,"Folder created\n");
		return ERR_NONE;
	}
	return ERR_UNKNOWNRESPONSE;
}

/**
 * Changes current path on OBEX device.
 */
static GSM_Error OBEXGEN_ChangePath(GSM_StateMachine *s, char *Name, unsigned char Flag)
{
	unsigned char 	req[400];
	int		Current = 2;

	/* Flags */
	req[0] = Flag;
	req[1] = 0x00;

	/* Name block */
	if (Name != NULL && UnicodeLength(Name) != 0) {
		OBEXAddBlock(req, &Current, 0x01, Name, UnicodeLength(Name)*2+2);
	} else {
		OBEXAddBlock(req, &Current, 0x01, NULL, 0);
	}

	OBEXGEN_AddConnectionID(s, req, &Current);

	return GSM_WaitFor (s, req, Current, 0x85, OBEX_TIMEOUT, ID_SetPath);
}

/**
 * Changes current path on OBEX device to match GSM_File one.
 *
 * @param s State machine
 * @param File File which path we want to set
 * @param DirOnly Whether to descend only do directory name of path or full path (/foo or /foo/bar.png)
 * @param Buffer Optional buffer for storing last path part. Not used if NULL.
 */
static GSM_Error OBEXGEN_ChangeToFilePath(GSM_StateMachine *s, char *File, gboolean DirOnly, unsigned char *Buffer)
{
	GSM_Error		error;
	size_t			Pos;
	unsigned char		*req, req2[200];

	if (Buffer == NULL) {
		req = req2;
	} else {
		req = Buffer;
	}

	smprintf(s,"Changing to root\n");
	error = OBEXGEN_ChangePath(s, NULL, 2);
	if (error != ERR_NONE) return error;

	Pos = 0;
	do {
		OBEXGEN_FindNextDir(File, &Pos, req);
		if (DirOnly && Pos == UnicodeLength(File)) break;
		smprintf(s,"Changing path down to %s (%ld, %ld)\n",
			DecodeUnicodeString(req),
			(long)Pos,
			(long)UnicodeLength(File));
		error=OBEXGEN_ChangePath(s, req, 2);
		if (error != ERR_NONE) return error;
		if (Pos == UnicodeLength(File)) break;
	} while (1);

	return ERR_NONE;
}

/**
 * Reply handler for most file write operations.
 */
static GSM_Error OBEXGEN_ReplyAddFilePart(GSM_Protocol_Message *msg, GSM_StateMachine *s)
{
	size_t Pos=0, pos2, len2;
	char *NewLUID = NULL;
	char *timestamp = NULL;
	char *CC = NULL;
	gboolean UpdatePbLUID, UpdateCalLUID, UpdateTodoLUID;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	UpdatePbLUID = Priv->UpdatePbLUID;
	Priv->UpdatePbLUID = FALSE;
	UpdateCalLUID = Priv->UpdateCalLUID;
	Priv->UpdateCalLUID = FALSE;
	UpdateTodoLUID = Priv->UpdateTodoLUID;
	Priv->UpdateTodoLUID = FALSE;

	if ((msg->Type & 0x7f) >= 0x40) {
		return OBEXGEN_HandleError(msg, s);
	}

	switch (msg->Type) {
	case 0x90:
		smprintf(s,"Last part of file added OK\n");
		return ERR_NONE;
	case 0xA0:
		smprintf(s,"Part of file added OK\n");
		while(1) {
			if (Pos >= msg->Length) break;
			switch (msg->Buffer[Pos]) {
			case 0x4C:
				smprintf(s, "Application data received:");
				len2 =  msg->Buffer[Pos+1] * 256 + msg->Buffer[Pos+2];
				pos2 = 0;
				while(1) {
					if (pos2 >= len2) break;
					switch (msg->Buffer[Pos + 3 + pos2]) {
						case 0x00:
							if (Priv->Service == OBEX_m_OBEX) {
								Priv->m_obex_error = msg->Buffer[Pos + 3 + pos2 + 1];
								smprintf(s, " m-obex error=\"%d\"", Priv->m_obex_error);
								/* This field has fixed size */
								pos2 += 2;
								continue;
							}
							break;
						case 0x01:
							NewLUID = (char *)malloc(msg->Buffer[Pos + 3 + pos2 + 1]+1);
							memcpy(NewLUID,msg->Buffer + Pos + 3 + pos2 + 2, msg->Buffer[Pos + 3 + pos2 + 1]);
							NewLUID[msg->Buffer[Pos + 3 + pos2 + 1]]=0;
							smprintf(s, " LUID=\"%s\"", NewLUID);
							break;
						case 0x02:
							CC = (char *)malloc(msg->Buffer[Pos + 3 + pos2 + 1]+1);
							memcpy(CC,msg->Buffer + Pos + 3 + pos2 + 2, msg->Buffer[Pos + 3 + pos2 + 1]);
							CC[msg->Buffer[Pos + 3 + pos2 + 1]]=0;
							smprintf(s, " CC=\"%s\"", CC);
							break;
						case 0x03:
							timestamp = (char *)malloc(msg->Buffer[Pos + 3 + pos2 + 1]+1);
							memcpy(timestamp,msg->Buffer + Pos + 3 + pos2 + 2, msg->Buffer[Pos + 3 + pos2 + 1]);
							timestamp[msg->Buffer[Pos + 3 + pos2 + 1]] = 0;
							smprintf(s, " Timestamp=\"%s\"", timestamp);
							break;
					}
					pos2 += 2 + msg->Buffer[Pos + 3 + pos2 + 1];
				}
				smprintf(s, "\n");
				if (timestamp != NULL) {
					free(timestamp);
					timestamp=NULL;
				}
				if (CC != NULL) {
					free(CC);
					CC=NULL;
				}
				if (NewLUID != NULL) {
					if (UpdatePbLUID) {
						Priv->PbLUIDCount++;
						Priv->PbLUID = (char **)realloc(Priv->PbLUID, (Priv->PbLUIDCount + 1) * sizeof(char *));
						if (Priv->PbLUID == NULL) {
							return ERR_MOREMEMORY;
						}
						Priv->PbLUID[Priv->PbLUIDCount] = NewLUID;
					} else if (UpdateTodoLUID) {
						Priv->TodoLUIDCount++;
						Priv->TodoLUID = (char **)realloc(Priv->TodoLUID, (Priv->TodoLUIDCount + 1) * sizeof(char *));
						if (Priv->TodoLUID == NULL) {
							return ERR_MOREMEMORY;
						}
						Priv->TodoLUID[Priv->TodoLUIDCount] = NewLUID;
					} else if (UpdateCalLUID) {
						Priv->CalLUIDCount++;
						Priv->CalLUID = (char **)realloc(Priv->CalLUID, (Priv->CalLUIDCount + 1) * sizeof(char *));
						if (Priv->CalLUID == NULL) {
							return ERR_MOREMEMORY;
						}
						Priv->CalLUID[Priv->CalLUIDCount] = NewLUID;
					} else {
						free(NewLUID);
						NewLUID=NULL;
					}
				}
				Pos += len2;
				break;
			case 0xc3:
				/* Length */
				/**
				 * @todo Lenght is ignored now
				 */
				Pos += 5;
				break;
			case 0x49:
				/* ID of newly created m-obex entry */
				Priv->m_obex_newid = msg->Buffer[Pos+3]*256 + msg->Buffer[Pos+4];
				Pos += 5;
				break;
			case 0xcb:
				/* Skip Connection ID (we ignore this for now) */
				Pos += 5;
				break;
			default:
				Pos+=msg->Buffer[Pos+1]*256+msg->Buffer[Pos+2];
				break;
			}
		}
		return ERR_NONE;
	}
	return ERR_UNKNOWNRESPONSE;
}

GSM_Error OBEXGEN_PrivAddFilePart(GSM_StateMachine *s, GSM_File *File, size_t *Pos, int *Handle UNUSED, gboolean HardDelete)
{
	GSM_Error		error;
	size_t			j;
	int		Current = 0;
	unsigned char 		req[2000];
	unsigned char		hard_delete_header[2] = {'\x12', '\x0'};
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	s->Phone.Data.File = File;

	if (Priv->Service == OBEX_BrowsingFolders || Priv->Service == OBEX_m_OBEX) {
		OBEXGEN_AddConnectionID(s, req, &Current);
	}

	/* Are we sending first request or continuation? */
	if (*Pos == 0) {
		if (!strcmp(DecodeUnicodeString(File->ID_FullName),"")) {
			error = OBEXGEN_Connect(s,OBEX_None);
			if (error != ERR_NONE) return error;
		} else {
			if (Priv->Service == OBEX_BrowsingFolders) {
				error = OBEXGEN_ChangeToFilePath(s, File->ID_FullName, FALSE, NULL);
				if (error != ERR_NONE) return error;
			}
		}

		/* Name block */
		if (Priv->Service == OBEX_m_OBEX) {
			OBEXAddBlock(req, &Current, 0x42, DecodeUnicodeString(File->ID_FullName), UnicodeLength(File->ID_FullName) + 1);
		} else {
			OBEXAddBlock(req, &Current, 0x01, File->Name, UnicodeLength(File->Name)*2+2);
		}

		/* Include m-obex application data */
		if (Priv->Service == OBEX_m_OBEX && Priv->m_obex_appdata != NULL && Priv->m_obex_appdata_len != 0) {
			OBEXAddBlock(req, &Current, 0x4C, Priv->m_obex_appdata, Priv->m_obex_appdata_len);
		}

		/* Adding empty file is special on mobex */
		if (Priv->Service == OBEX_m_OBEX && File->Buffer == NULL) {
			error = GSM_WaitFor (s, req, Current, 0x82, OBEX_TIMEOUT * 10, ID_AddFile);
			if (error == ERR_NONE) {
				return ERR_EMPTY;
			}
			return error;
		}

		/* File size block */
		req[Current++] = 0xC3; /* ID */
		req[Current++] = (File->Used >> 24) & 0xff;
		req[Current++] = (File->Used >> 16) & 0xff;
		req[Current++] = (File->Used >> 8) & 0xff;
		req[Current++] = File->Used & 0xff;

		/* Application data block for hard delete */
		if (HardDelete) {
			OBEXAddBlock(req, &Current, 0x4c, hard_delete_header, 2);
		}
	}

	j = Priv->FrameSize - Current - 20;
	if (j > 1000) j = 1000;

	if (File->Used - *Pos < j) {
		j = File->Used - *Pos;
		/* End of file body block */
		OBEXAddBlock(req, &Current, 0x49, File->Buffer+(*Pos), j);
		smprintf(s, "Adding last file part %ld %ld\n", (long)*Pos, (long)j);
		*Pos = *Pos + j;
		error = GSM_WaitFor (s, req, (long)Current, 0x82, OBEX_TIMEOUT * 10, ID_AddFile);
		if (error != ERR_NONE) return error;
		return ERR_EMPTY;
	} else {
		/* File body block */
		OBEXAddBlock(req, &Current, 0x48, File->Buffer+(*Pos), j);
		smprintf(s, "Adding file part %ld %ld\n", (long)*Pos, (long)j);
		*Pos = *Pos + j;
		error=GSM_WaitFor (s, req, (long)Current, 0x02, OBEX_TIMEOUT * 10, ID_AddFile);
	}
	return error;
}

GSM_Error OBEXGEN_AddFilePart(GSM_StateMachine *s, GSM_File *File, size_t *Pos, int *Handle)
{
	GSM_Error		error;

	/* Go to default service */
	error = OBEXGEN_Connect(s, 0);
	if (error != ERR_NONE) return error;

	/* Add file */
	smprintf(s,"Adding file\n");
	error = OBEXGEN_PrivAddFilePart(s, File, Pos, Handle, FALSE);

	/* Calculate path of added file if we're done */
	if (error == ERR_EMPTY) {
		OBEXGEN_CreateFileName(File->ID_FullName, File->ID_FullName, File->Name);
	}

	return error;
}

GSM_Error OBEXGEN_SendFilePart(GSM_StateMachine *s, GSM_File *File, size_t *Pos, int *Handle)
{
	GSM_Error		error;

	/* No service for this */
	error = OBEXGEN_Connect(s, OBEX_None);
	if (error != ERR_NONE) return error;

	/* Send file */
	smprintf(s,"Sending file\n");
	File->ID_FullName[0] = 0;
	File->ID_FullName[1] = 0;
	error = OBEXGEN_PrivAddFilePart(s, File, Pos, Handle, FALSE);
	if (error != ERR_NONE) return error;

	/* Calculate path of added file */
	OBEXGEN_CreateFileName(File->ID_FullName, File->ID_FullName, File->Name);
	return ERR_NONE;
}

/**
 * Reply handler for file reading operations.
 */
static GSM_Error OBEXGEN_ReplyGetFilePart(GSM_Protocol_Message *msg, GSM_StateMachine *s)
{
	size_t old,Pos=0,len2,pos2;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* Non standard Sharp GX reply */
	if (msg->Type == 0x80) {
		return ERR_FILENOTEXIST;
	}

	/* Generic error codes */
	if ((msg->Type & 0x7f) >= 0x40) {
		return OBEXGEN_HandleError(msg, s);
	}

	switch (msg->Type) {
	case 0xA0:
		smprintf(s,"Last file part received\n");
		s->Phone.Data.Priv.OBEXGEN.FileLastPart = TRUE;
		if (msg->Length == 0) return ERR_NONE;
		/* Fallthrough */
	case 0x90:
		while(1) {
			if (Pos >= msg->Length) break;
			switch (msg->Buffer[Pos]) {
			case 0x48:
			case 0x49:
				smprintf(s,"File part received\n");
				old = s->Phone.Data.File->Used;
				s->Phone.Data.File->Used += msg->Buffer[Pos+1]*256+msg->Buffer[Pos+2]-3;
				smprintf(s,"Length of file part: %i\n",
						msg->Buffer[Pos+1]*256+msg->Buffer[Pos+2]-3);
				s->Phone.Data.File->Buffer = (unsigned char *)realloc(s->Phone.Data.File->Buffer,s->Phone.Data.File->Used);
				memcpy(s->Phone.Data.File->Buffer+old,msg->Buffer+Pos+3,s->Phone.Data.File->Used-old);
				return ERR_NONE;
			case 0xc3:
				/* Length */
				/**
				 * @todo Length is ignored now
				 */
				Pos += 5;
				break;
			case 0xcb:
				/* Skip Connection ID (we ignore this for now) */
				Pos += 5;
				break;
			case 0x4C:
				smprintf(s, "Application data received:");
				len2 =  msg->Buffer[Pos+1] * 256 + msg->Buffer[Pos+2];
				pos2 = 0;
				while(1) {
					if (pos2 >= len2) break;
					switch (msg->Buffer[Pos + 3 + pos2]) {
						case 0x00:
							if (Priv->Service == OBEX_m_OBEX) {
								Priv->m_obex_error = msg->Buffer[Pos + 3 + pos2 + 1];
								smprintf(s, " m-obex error=\"%d\"", Priv->m_obex_error);
								/* This field has fixed size */
								pos2 += 2;
								continue;
							}
							break;
					}
					pos2 += 2 + msg->Buffer[Pos + 3 + pos2 + 1];
				}
				smprintf(s, "\n");
				Pos += len2;
				break;
			default:
				Pos+=msg->Buffer[Pos+1]*256+msg->Buffer[Pos+2];
				break;
			}
		}
		return ERR_UNKNOWNRESPONSE;
	}
	return ERR_UNKNOWNRESPONSE;
}

static GSM_Error OBEXGEN_PrivGetFilePart(GSM_StateMachine *s, GSM_File *File, gboolean FolderList)
{
	int 		Current = 0;
	GSM_Error		error;
	unsigned char 		req[2000], req2[200];
	int			retries;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	s->Phone.Data.File 	= File;
	File->ReadOnly 		= FALSE;
	File->Protected 	= FALSE;
	File->Hidden		= FALSE;
	File->System		= FALSE;
	File->ModifiedEmpty	= TRUE;

	if (Priv->Service == OBEX_BrowsingFolders || Priv->Service == OBEX_m_OBEX) {
		OBEXGEN_AddConnectionID(s, req, &Current);
	}

	if (File->Used == 0x00) {
		if (FolderList) {
			/* Type block */
			strcpy(req2,"x-obex/folder-listing");
			OBEXAddBlock(req, &Current, 0x42, req2, strlen(req2)+1);

			/* Name block should be empty, we're already in this folder */
			OBEXAddBlock(req, &Current, 0x01, NULL, 0);
		} else {
			File->Folder = FALSE;

			if (File->ID_FullName[0] == 0x00 && File->ID_FullName[1] == 0x00) {
				if (Priv->Service == OBEX_BrowsingFolders) {
					/* No file name? Grab OBEX capabilities in browse mode */
					smprintf(s, "No filename requested, grabbing OBEX capabilities as obex-capability.xml\n");
					EncodeUnicode(File->Name, "obex-capability.xml", 19);
					strcpy(req2,"x-obex/capability");

					/* Type block */
					OBEXAddBlock(req, &Current, 0x42, req2, strlen(req2));
				} else if (Priv->Service == OBEX_IRMC) {
					/* No file name? Grab devinfo in IrMC mode */
					smprintf(s, "No filename requested, grabbing device information as devinfo.txt\n");
					EncodeUnicode(File->Name, "devinfo.txt", 19);
					EncodeUnicode(req2,"telecom/devinfo.txt",19);

					/* Name block */
					OBEXAddBlock(req, &Current, 0x01, req2, UnicodeLength(req2)*2+2);
				} else {
					return ERR_NOTSUPPORTED;
				}
			} else if (Priv->Service == OBEX_m_OBEX) {
				OBEXAddBlock(req, &Current, 0x42, DecodeUnicodeString(File->ID_FullName), UnicodeLength(File->ID_FullName) + 1);
			} else {
				if (Priv->Service == OBEX_BrowsingFolders) {
					error = OBEXGEN_ChangeToFilePath(s, File->ID_FullName, TRUE, req2);
					if (error != ERR_NONE) return error;
				} else {
					CopyUnicodeString(req2,File->ID_FullName);
				}
				CopyUnicodeString(File->Name,req2);

				s->Phone.Data.File = File;

				Current = 0;
				/* Name block */
				OBEXAddBlock(req, &Current, 0x01, req2, UnicodeLength(req2)*2+2);
			}
		}
	}

	Priv->FileLastPart = FALSE;

	/* Include m-obex application data */
	if (Priv->Service == OBEX_m_OBEX && Priv->m_obex_appdata != NULL && Priv->m_obex_appdata_len != 0) {
		OBEXAddBlock(req, &Current, 0x4C, Priv->m_obex_appdata, Priv->m_obex_appdata_len);
	}

	smprintf(s, "Getting first file part\n");
	retries = 0;
	while (retries < 5) {
		if (retries > 0) {
			smprintf(s, "Retry %d\n", retries);
		}
		error=GSM_WaitFor (s, req, Current, 0x83, OBEX_TIMEOUT, ID_GetFile);
		if (error != ERR_PERMISSION) break;
		usleep(200000);
		retries++;
	}
	if (error != ERR_NONE) return error;

	while (!Priv->FileLastPart) {
		Current = 0;
		if (Priv->Service == OBEX_BrowsingFolders || Priv->Service == OBEX_m_OBEX) {
			OBEXGEN_AddConnectionID(s, req, &Current);
		}
		/* Include m-obex application data */
		if (Priv->Service == OBEX_m_OBEX && Priv->m_obex_appdata != NULL && Priv->m_obex_appdata_len != 0) {
			OBEXAddBlock(req, &Current, 0x4C, Priv->m_obex_appdata, Priv->m_obex_appdata_len);
		}
		smprintf(s, "Getting next file part\n");
		/* We retry for ERR_PERMISSION, because it can be caused by database locked error */
		retries = 0;
		while (retries < 5) {
			if (retries > 0) {
				smprintf(s, "Retry %d\n", retries);
			}
			error = GSM_WaitFor (s, req, Current, 0x83, OBEX_TIMEOUT, ID_GetFile);
			if (error != ERR_PERMISSION) break;
			usleep(200000);
			retries++;
		}
		if (error != ERR_NONE) return error;
	}
	return ERR_EMPTY;
}

GSM_Error OBEXGEN_GetFilePart(GSM_StateMachine *s, GSM_File *File, int *Handle, size_t *Size)
{
	GSM_Error		error;

	/* Go to default service */
	error = OBEXGEN_Connect(s, 0);
	if (error != ERR_NONE) return error;

	(*Handle) = 0;
	error = OBEXGEN_PrivGetFilePart(s, File, FALSE);
	(*Size) = File->Used;
	return error;
}


/**
 * List OBEX folder.
 */
/**
 * @todo We assume XML reply is in UTF-8, but this doesn't have to be true.
 */
GSM_Error OBEXGEN_GetNextFileFolder(GSM_StateMachine *s, GSM_File *File, gboolean start)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_Error		error;
	unsigned char		Line[500],Line2[500],*name,*size;
	int			i,j,num,pos2;
	size_t			Pos;

	/* Go to default service */
	error = OBEXGEN_Connect(s, 0);
	if (error != ERR_NONE) return error;

	/* We can browse files only when using browse service */
	if (Priv->Service != OBEX_BrowsingFolders) {
		return ERR_NOTSUPPORTED;
	}

	if (start) {
		Priv->Files[0].Folder		= TRUE;
		Priv->Files[0].Level		= 1;
		Priv->Files[0].Name[0]		= 0;
		Priv->Files[0].Name[1]		= 0;
		Priv->Files[0].ID_FullName[0]	= 0;
		Priv->Files[0].ID_FullName[1]	= 0;

		Priv->FilesLocationsUsed 	= 1;
		Priv->FilesLocationsCurrent 	= 0;
	}

	while (1) {
		if (Priv->FilesLocationsCurrent == Priv->FilesLocationsUsed) {
			smprintf(s, "Last file\n");
			return ERR_EMPTY;
		}

		CopyUnicodeString(File->ID_FullName,Priv->Files[Priv->FilesLocationsCurrent].ID_FullName);
		File->Level	= Priv->Files[Priv->FilesLocationsCurrent].Level;
		File->Folder	= Priv->Files[Priv->FilesLocationsCurrent].Folder;
		CopyUnicodeString(File->Name,Priv->Files[Priv->FilesLocationsCurrent].Name);
		Priv->FilesLocationsCurrent++;

		if (File->Folder) {
			error = OBEXGEN_ChangeToFilePath(s, File->ID_FullName, FALSE, NULL);
			if (error != ERR_NONE) return error;

			File->Buffer		= NULL;
			File->Used		= 0;
			File->ModifiedEmpty	= TRUE;

			error = OBEXGEN_PrivGetFilePart(s, File, TRUE);
			if (error != ERR_NONE && error != ERR_EMPTY) return error;

			num = 0;
			Pos = 0;
			/* Calculate number of files */
			while (1) {
				error = MyGetLine(File->Buffer, &Pos, Line, File->Used, sizeof(Line), FALSE);
				if (error != ERR_NONE) return error;
				if (strlen(Line) == 0) break;
				name = strstr(Line,"folder name=\"");
				if (name != NULL) {
					name += 13;
					j = 0;
					while(1) {
						if (name[j] == '"') break;
						j++;
					}
					name[j] = 0;

					if (strcmp(name,".")) num++;
				}
				name = strstr(Line,"file name=\"");
				if (name != NULL) num++;
			}
			/* Shift current files to the end of list */
			if (num != 0) {
				i = Priv->FilesLocationsUsed-1;
				while (1) {
					if (i==Priv->FilesLocationsCurrent-1) break;
					memcpy(&Priv->Files[i+num],&Priv->Files[i],sizeof(GSM_File));
					i--;
				}
			}

			/* Actually parse file listing */
			Pos 	= 0;
			pos2 	= 0;
			while (1) {
				error = MyGetLine(File->Buffer, &Pos, Line, File->Used, sizeof(Line), FALSE);
				if (error != ERR_NONE) return error;
				if (strlen(Line) == 0) break;
				strcpy(Line2,Line);
				name = strstr(Line2,"folder name=\"");
				if (name != NULL) {
					name += 13;
					j = 0;
					while(1) {
						if (name[j] == '"') break;
						j++;
					}
					name[j] = 0;
					if (strcmp(name,".")) {
						smprintf(s, "copying folder %s to %i parent %i\n",name,Priv->FilesLocationsCurrent+pos2,Priv->FilesLocationsCurrent);
						/* Convert filename from UTF-8 */
						DecodeXMLUTF8(Priv->Files[Priv->FilesLocationsCurrent+pos2].Name, name, strlen(name));
						/* Create file name from parts */
						OBEXGEN_CreateFileName(
							Priv->Files[Priv->FilesLocationsCurrent+pos2].ID_FullName,
							File->ID_FullName,
							Priv->Files[Priv->FilesLocationsCurrent+pos2].Name
							);
						Priv->Files[Priv->FilesLocationsCurrent+pos2].Level  = File->Level+1;
						Priv->Files[Priv->FilesLocationsCurrent+pos2].Folder = TRUE;
						Priv->FilesLocationsUsed++;
						pos2++;
					}
				}
				strcpy(Line2,Line);
				name = strstr(Line2,"file name=\"");
				if (name != NULL) {
					name += 11;
					j = 0;
					while(1) {
						if (name[j] == '"') break;
						j++;
					}
					name[j] = 0;
					smprintf(s, "copying file %s to %i\n",name,Priv->FilesLocationsCurrent+pos2);
					/* Convert filename from UTF-8 */
					DecodeXMLUTF8(Priv->Files[Priv->FilesLocationsCurrent+pos2].Name, name, strlen(name));
					/* Create file name from parts */
					OBEXGEN_CreateFileName(
						Priv->Files[Priv->FilesLocationsCurrent+pos2].ID_FullName,
						File->ID_FullName,
						Priv->Files[Priv->FilesLocationsCurrent+pos2].Name
						);

					Priv->Files[Priv->FilesLocationsCurrent+pos2].Level	= File->Level+1;
					Priv->Files[Priv->FilesLocationsCurrent+pos2].Folder 	= FALSE;
					Priv->Files[Priv->FilesLocationsCurrent+pos2].Used 	= 0;
					strcpy(Line2,Line);
					size = strstr(Line2,"size=\"");
					if (size != NULL) Priv->Files[Priv->FilesLocationsCurrent+pos2].Used = atoi(size+6);

					Priv->Files[Priv->FilesLocationsCurrent+pos2].ModifiedEmpty = TRUE;
					strcpy(Line2,Line);
					size = strstr(Line2,"modified=\"");
					if (size != NULL) {
						Priv->Files[Priv->FilesLocationsCurrent+pos2].ModifiedEmpty = FALSE;
						ReadVCALDateTime(size+10, &Priv->Files[Priv->FilesLocationsCurrent+pos2].Modified);
					}
					Priv->FilesLocationsUsed++;
					pos2++;
				}
			}

			free(File->Buffer);
			File->Buffer = NULL;
		} else {
			File->Used 	    	= Priv->Files[Priv->FilesLocationsCurrent-1].Used;
			File->ModifiedEmpty 	= Priv->Files[Priv->FilesLocationsCurrent-1].ModifiedEmpty;
			if (!File->ModifiedEmpty) {
				memcpy(&File->Modified,&Priv->Files[Priv->FilesLocationsCurrent-1].Modified,sizeof(GSM_DateTime));
			}
			File->ReadOnly 		= FALSE;
			File->Protected 	= FALSE;
			File->Hidden		= FALSE;
			File->System		= FALSE;

		}
		return ERR_NONE;
	}
}

GSM_Error OBEXGEN_DeleteFile(GSM_StateMachine *s, unsigned char *ID)
{
	GSM_Error		error;
	int		Current = 0;
	unsigned char		req[200],req2[200];

	/* Go to default service */
	error = OBEXGEN_Connect(s, 0);
	if (error != ERR_NONE) return error;

	if (s->Phone.Data.Priv.OBEXGEN.Service != OBEX_BrowsingFolders) {
		return ERR_NOTSUPPORTED;
	}

	/* Go to file directory */
	error = OBEXGEN_ChangeToFilePath(s, ID, TRUE, req2);
	if (error != ERR_NONE) return error;

	/* Name block */
	OBEXAddBlock(req, &Current, 0x01, req2, UnicodeLength(req2)*2+2);

	/* connection ID block */
	OBEXGEN_AddConnectionID(s, req, &Current);

	return GSM_WaitFor (s, req, Current, 0x82, OBEX_TIMEOUT, ID_AddFile);
}

GSM_Error OBEXGEN_AddFolder(GSM_StateMachine *s, GSM_File *File)
{
	GSM_Error		error;

	/* Go to default service */
	error = OBEXGEN_Connect(s, 0);
	if (error != ERR_NONE) return error;

	if (s->Phone.Data.Priv.OBEXGEN.Service != OBEX_BrowsingFolders) {
		return ERR_NOTSUPPORTED;
	}

	/* Go to file directory */
	error = OBEXGEN_ChangeToFilePath(s, File->ID_FullName, FALSE, NULL);
	if (error != ERR_NONE) return error;

	/* Add folder */
	smprintf(s,"Adding directory\n");
	error = OBEXGEN_ChangePath(s, File->Name, 0);
	if (error != ERR_NONE) return error;

	/* Calculate path of added folder */
	OBEXGEN_CreateFileName(File->ID_FullName, File->ID_FullName, File->Name);
	return ERR_NONE;
}

/*@}*/

/**
 * \defgroup OBEXhelper OBEX helper functions which are used in IrMC code
 * \ingroup OBEXPhone
 * @{
 */

/**
 * Grabs complete single file
 */
GSM_Error OBEXGEN_GetFile(GSM_StateMachine *s, const char *FileName, unsigned char ** Buffer, size_t *len)
{
	GSM_Error error = ERR_NONE;
	GSM_File File;

	/* Clear structure */
	memset(&File, 0, sizeof(GSM_File));

	/* Encode file name to unicode */
	EncodeUnicode(File.ID_FullName, FileName, strlen(FileName));

	/* Grab complete file */
	while (error == ERR_NONE) {
		error = OBEXGEN_PrivGetFilePart(s, &File, FALSE);
	}

	/* We should get ERR_EMPTY at the end of file */
	if (error != ERR_EMPTY) {
		if (File.Buffer != NULL) {
			free(File.Buffer);
			File.Buffer=NULL;
		}
		return error;
	}

	/* Return data we got */
	*Buffer = File.Buffer;
	*len = File.Used;
	return ERR_NONE;
}

/**
 * Grabs complete single binary file
 */
GSM_Error OBEXGEN_GetBinaryFile(GSM_StateMachine *s, const char *FileName, unsigned char ** Buffer, size_t *len)
{
	GSM_Error error = ERR_NONE;

	/* Grab complete file */
	error = OBEXGEN_GetFile(s, FileName, (unsigned char **)Buffer, len);
	if (error != ERR_NONE) return error;

	/* Return data we got */
	smprintf(s, "Got %ld bytes of data\n", (long int)*len);
	*Buffer = (unsigned char *)realloc(*Buffer, *len + 1);
	if (*Buffer == NULL) {
		return ERR_MOREMEMORY;
	}
	(*Buffer)[*len] = 0;
	return ERR_NONE;
}

/**
 * Grabs complete single text file
 */
GSM_Error OBEXGEN_GetTextFile(GSM_StateMachine *s, const char *FileName, char ** Buffer)
{
	size_t len;

	return OBEXGEN_GetBinaryFile(s, FileName, (unsigned char **)Buffer, &len);
}

/**
 * Sets single file on filesystem, file can be created or updated.
 */
GSM_Error OBEXGEN_SetFile(GSM_StateMachine *s, const char *FileName, const unsigned char *Buffer, size_t Length, gboolean HardDelete)
{
	GSM_Error	error = ERR_NONE;
	GSM_File 	File;
	size_t		Pos = 0;
	int Handle;

	/* Fill file structure */
	EncodeUnicode(File.ID_FullName, FileName, strlen(FileName));
	EncodeUnicode(File.Name, FileName, strlen(FileName));
	File.Used 	= Length;
	File.Buffer 	= (unsigned char *)Buffer;

	/* Send file */
	while (error == ERR_NONE) {
		error = OBEXGEN_PrivAddFilePart(s, &File, &Pos, &Handle, HardDelete);
	}
	if (error != ERR_EMPTY) return error;

	return ERR_NONE;
}

/*@}*/

/**
 * \defgroup IrMChelper Generic IrMC helper functions
 * \ingroup OBEXPhone
 * @{
 */

/**
 * Parses selected information from IrMC info.log. Information parsed:
 *  * IEL (Information Exchange Level)
 *  * Number of free records
 *  * Number of used records
 */
GSM_Error OBEXGEN_ParseInfoLog(GSM_StateMachine *s, const char *data, int *free_out, int *used_out, IRMC_Capability *Cap)
{
	char *pos;
	int IEL;
	int maximum_records = -1;
	int used_records = -1;
	int free_records = -1;
	char free_text[] = "Free-Records:";
	char used_text[] = "Total-Records:";
	char maximum_text[] = "Maximum-Records:";
	char IEL_text[] = "IEL:";
	char HD_text[] = "HD:";

	smprintf(s, "OBEX info data:\n---\n%s\n---\n", data);

	pos = strstr(data, IEL_text);
	if (pos == NULL) {
		smprintf(s, "Could not grab Information Exchange Level, phone does not support it\n");
		return ERR_NOTSUPPORTED;
	}
	pos += strlen(IEL_text);
	/* This might be hex */
	if (pos[0] != 0 && pos[0] == '0' && pos[1] != 0 && pos[1] == 'x') {
		/* Hex means IEL flag we use */
		IEL = strtol(pos + 2, (char **)NULL, 16);
	} else {
		/* Decimal means directly IEL level, convert it to flags */
		IEL = atoi(pos);
		/* Adjust index to flags we use further */
		switch (IEL) {
			case 3:
				IEL = 0x4;
				break;
			case 4:
				IEL = 0x8;
				/* In fact this can be also 0x10, but we can't tell */
				break;
		}
	}
	switch (IEL) {
		case 0x1:
			smprintf(s, "Information Exchange Level 1 supported\n");
			break;
		case 0x2:
			smprintf(s, "Information Exchange Level 1 and 2 supported\n");
			break;
		case 0x4:
			smprintf(s, "Information Exchange Level 1, 2 and 3 supported\n");
			break;
		case 0x8:
			smprintf(s, "Information Exchange Level 1, 2 and 4 supported\n");
			break;
		case 0x10:
			smprintf(s, "Information Exchange Level 1, 2, 3 and 4 supported\n");
			break;
		default:
			smprintf(s, "Could not parse Information Exchange Level (0x%x)\n", IEL);
			return ERR_INVALIDDATA;
	}

	if (Cap != NULL) {
		Cap->IEL = IEL;
	}

	pos = strstr(data, HD_text);
	if (pos == NULL) {
		smprintf(s, "Could not grab HD support\n");
	} else {
		pos += strlen(HD_text);
		if (strncasecmp("YES", pos, 3) == 0) {
			smprintf(s, "HD is supported\n");
			if (Cap != NULL) {
				Cap->HD = TRUE;
			}
		} else if (strncasecmp("NO", pos, 2) == 0) {
			smprintf(s, "HD is not supported\n");
		} else {
			smprintf(s, "WARNING: Could not parse HD support\n");
		}
	}

	pos = strstr(data, free_text);
	if (pos == NULL) {
		smprintf(s, "Could not grab number of free records\n");
	} else {
		pos += strlen(free_text);
		free_records = atoi(pos);
		smprintf(s, "Number of free records: %d\n", free_records);
	}

	pos = strstr(data, used_text);
	if (pos == NULL) {
		smprintf(s, "Could not grab number of used records\n");
	} else {
		pos += strlen(used_text);
		used_records = atoi(pos);
		smprintf(s, "Number of used records: %d\n", used_records);
	}

	pos = strstr(data, maximum_text);
	if (pos == NULL) {
		smprintf(s, "Could not grab number of maximum records\n");
	} else {
		pos += strlen(maximum_text);
		maximum_records = atoi(pos);
		smprintf(s, "Number of maximum records: %d\n", maximum_records);
	}

	if (free_out != NULL) {
		if (free_records != -1) {
			*free_out = free_records;
		} else if (maximum_records != -1 && used_records != -1) {
			*free_out = maximum_records - used_records;
		} else {
			*free_out = 0;
			smprintf(s, "Could not grab number of free records\n");
			return ERR_INVALIDDATA;
		}
	}

	if (used_out != NULL) {
		if (used_records != -1) {
			*used_out = used_records;
		} else if (maximum_records != -1 && free_records != -1) {
			*used_out = maximum_records - free_records;
		} else {
			*used_out = 0;
			smprintf(s, "Could not grab number of used records\n");
			return ERR_INVALIDDATA;
		}
	}

	return ERR_NONE;
}

/**
 * Grabs information from defined OBEX IrMC information log into variables.
 */
GSM_Error OBEXGEN_GetInformation(GSM_StateMachine *s, const char *path, int *free_records, int *used_records, IRMC_Capability *Cap)
{
	GSM_Error 	error;
	char		*data;

	/* IEL by default - support adding */
	Cap->IEL = 1;

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* Grab log info file */
	error = OBEXGEN_GetTextFile(s, path, &data);

	/* Level 0 or 1 phones do not have to expose information */
	if (error == ERR_BUG || error == ERR_FILENOTEXIST || error == ERR_PERMISSION) {
		/* Some phones do not follow IrMC specs and do not provide info.log for level 2 */
		if (GSM_IsPhoneFeatureAvailable(s->Phone.Data.ModelInfo, F_IRMC_LEVEL_2)) {
			Cap->IEL = 2;
		}
		if (free_records == NULL) {
			/* We were asked only for IEL, so don't bail out */
			return ERR_NONE;
		} else {
			/* No support for status if no info.log */
			return ERR_NOTSUPPORTED;
		}
	} else if (error != ERR_NONE) {
		/* Something wrong has happened */
		return error;
	}

	/* Parse it */
	error = OBEXGEN_ParseInfoLog(s, data, free_records, used_records, Cap);

	free(data);
	data=NULL;
	return error;
}

/**
 * Finds first empty location in index list and appends it.
 */
int OBEXGEN_GetFirstFreeLocation(int **IndexStorage, int *IndexCount) {
	int i;
	int max = -1;

	/* Find maximum used location */
	for (i = 0; i < *IndexCount; i++) {
		if (*IndexStorage[i] > max) {
			max = (*IndexStorage)[i];
		}
	}

	/* Next behind maximum is empty */
	max++;

	/* Update internal list */
	(*IndexCount)++;
	*IndexStorage = (int *)realloc(*IndexStorage, (*IndexCount + 1) * sizeof(int));
	(*IndexStorage)[*IndexCount] = max;

	return max;
}

/**
 * Initialises LUID database, which is used for LUID - Location mapping.
 */
GSM_Error OBEXGEN_InitLUID(GSM_StateMachine *s, const char *Name,
		const gboolean Recalculate,
		const char *Header,
		char **Data, int **Offsets, int *Count,
		char ***LUIDStorage, int *LUIDCount,
		int **IndexStorage, int *IndexCount)
{
	GSM_Error 	error;
	char		*pos;
	int		LUIDSize = 0;
	int		IndexSize = 0;
	int		Size = 0;
	size_t		linepos = 0;
	int		prevpos;
	char		line[2000];
	size_t		len;
	size_t		hlen;
	int		level = 0;

	/* Free data if previously allocated */
	if (!Recalculate) {
		if (*Data != NULL) {
			free(*Data);
			*Data=NULL;
		}
	}
	/**
	 * @todo should free all data here, but this execution path is not supported now
	 */

	/* Grab file with listing */
	if (!Recalculate || *Data == NULL) {
		/* We need IrMC service for this */
		error = OBEXGEN_Connect(s, OBEX_IRMC);
		if (error != ERR_NONE) return error;

		error = OBEXGEN_GetTextFile(s, Name, Data);
		if (error != ERR_NONE) return error;
	}

	*Count = 0;
	*Offsets = NULL;
	*LUIDCount = 0;
	*LUIDStorage = NULL;
	*IndexCount = 0;
	*IndexStorage = NULL;
	len = strlen(*Data);
	hlen = strlen(Header);

	while (1) {
		/* Remember line start position */
		prevpos = linepos;
		error = MyGetLine(*Data, &linepos, line, len, sizeof(line), TRUE);
		if (error != ERR_NONE) return error;
		if (strlen(line) == 0) break;
		switch (level) {
			case 0:
				if (strncmp(line, Header, hlen) == 0) {
					level = 1;
					(*Count)++;
					/* Do we need to reallocate? */
					if (*Count >= Size) {
						Size += 20;
						*Offsets = (int *)realloc(*Offsets, Size * sizeof(int));
						if (*Offsets == NULL) {
							return ERR_MOREMEMORY;
						}
					}
					/* Store start of item */
					(*Offsets)[*Count] = prevpos;
				} else if (strncmp(line, "BEGIN:VCALENDAR", 15) == 0) {
					/* We need to skip vCalendar header */
				} else if (strncmp(line, "BEGIN:", 6) == 0) {
					/* Skip other event types */
					level = 2;
				}
				break;
			case 1:
				if (strncmp(line, "END:", 4) == 0) {
					level = 0;
				} else if (strncmp(line, "X-IRMC-LUID:", 12) == 0) {
					pos = line + 12; /* Length of X-IRMC-LUID: */
					(*LUIDCount)++;
					/* Do we need to reallocate? */
					if (*LUIDCount >= LUIDSize) {
						LUIDSize += 20;
						*LUIDStorage = (char **)realloc(*LUIDStorage, LUIDSize * sizeof(char *));
						if (*LUIDStorage == NULL) {
							return ERR_MOREMEMORY;
						}
					}
					/* Copy LUID text */
					(*LUIDStorage)[*LUIDCount] = strdup(pos);
#if 0
					smprintf(s, "Added LUID %s at position %d\n", (*LUIDStorage)[*LUIDCount], *LUIDCount);
#endif
				} else if (strncmp(line, "X-INDEX:", 8) == 0) {
					pos = line + 8; /* Length of X-INDEX: */
					(*IndexCount)++;
					/* Do we need to reallocate? */
					if (*IndexCount >= IndexSize) {
						IndexSize += 20;
						*IndexStorage = (int *)realloc(*IndexStorage, IndexSize * sizeof(int));
						if (*IndexStorage == NULL) {
							return ERR_MOREMEMORY;
						}
					}
					/* Copy Index text */
					(*IndexStorage)[*IndexCount] = atoi(pos);
#if 0
					smprintf(s, "Added Index %d at position %d\n", (*IndexStorage)[*IndexCount], *IndexCount);
#endif
				}
				break;
			case 2:
				if (strncmp(line, "END:", 4) == 0) level = 0;
				break;
		}
	}

	smprintf(s, "Data parsed, found %d entries, %d indexes and %d LUIDs\n", *Count, *IndexCount, *LUIDCount);

	return ERR_NONE;
}

/*@}*/

/**
 * \defgroup IrMCphonebook IrMC phonebook support
 * \ingroup OBEXPhone
 * @{
 */

/**
 * Parses pb/info.log (phonebook IrMC information log).
 */
GSM_Error OBEXGEN_GetPbInformation(GSM_StateMachine *s, int *free_records, int *used)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	return OBEXGEN_GetInformation(s, "telecom/pb/info.log", free_records, used, &(Priv->PbCap));

}

/**
 * Grabs phonebook memory status
 */
GSM_Error OBEXGEN_GetMemoryStatus(GSM_StateMachine *s, GSM_MemoryStatus *Status)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	if (Status->MemoryType != MEM_ME && Status->MemoryType != MEM_SM) return ERR_NOTSUPPORTED;

	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_GetStatus(s, "m-obex/contacts/count", Status->MemoryType, &(Status->MemoryFree), &(Status->MemoryUsed));
	}

	if (Status->MemoryType != MEM_ME) return ERR_NOTSUPPORTED;

	return OBEXGEN_GetPbInformation(s, &(Status->MemoryFree), &(Status->MemoryUsed));

}

/**
 * Initializes phonebook LUID database.
 */
GSM_Error OBEXGEN_InitPbLUID(GSM_StateMachine *s)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* We might do validation here using telecom/pb/luid/cc.log fir IEL 4, but not on each request */
	if (Priv->PbData != NULL) return ERR_NONE;

	return OBEXGEN_InitLUID(s, "telecom/pb.vcf", FALSE, "BEGIN:VCARD",
			&(Priv->PbData), &(Priv->PbOffsets), &(Priv->PbCount),
			&(Priv->PbLUID), &(Priv->PbLUIDCount),
			&(Priv->PbIndex), &(Priv->PbIndexCount));
}

/**
 * Read memory by reading static index.
 */
GSM_Error OBEXGEN_GetMemoryIndex(GSM_StateMachine *s, GSM_MemoryEntry *Entry)
{
	GSM_Error 	error;
	char		*data=NULL;
	char		*path=NULL;
	size_t		pos = 0;

	error = OBEXGEN_InitPbLUID(s);
	if (error != ERR_NONE) return error;

	/* Calculate path */
	path = (char *)malloc(20 + 22); /* Length of string below + length of number */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/pb/%d.vcf", Entry->Location);
	smprintf(s, "Getting vCard %s\n", path);

	/* Grab vCard */
	error = OBEXGEN_GetTextFile(s, path, &data);
	free(path);
	path=NULL;

	if (error == ERR_FILENOTEXIST) return ERR_EMPTY;
	if (error != ERR_NONE) return error;

	/* Decode it */
	error = GSM_DecodeVCARD(&(s->di), data, &pos, Entry, SonyEricsson_VCard21_Phone);
	free(data);
	data=NULL;

	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

/**
 * Reads memory by reading from LUID location.
 */
GSM_Error OBEXGEN_GetMemoryLUID(GSM_StateMachine *s, GSM_MemoryEntry *Entry)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	char		*data=NULL;
	char		*path=NULL;
	size_t		pos = 0;

	error = OBEXGEN_InitPbLUID(s);
	if (error != ERR_NONE) return error;

	/* Check bounds */
	if (Entry->Location > Priv->PbLUIDCount) return ERR_EMPTY; /* Maybe invalid location? */
	if (Priv->PbLUID[Entry->Location] == NULL) return ERR_EMPTY;

	/* Calculate path */
	path = (char *)malloc(strlen(Priv->PbLUID[Entry->Location]) + 22); /* Length of string below */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/pb/luid/%s.vcf", Priv->PbLUID[Entry->Location]);
	smprintf(s, "Getting vCard %s\n", path);

	/* Grab vCard */
	error = OBEXGEN_GetTextFile(s, path, &data);
	free(path);
	path=NULL;
	if (error != ERR_NONE) return error;

	/* Decode it */
	error = GSM_DecodeVCARD(&(s->di), data, &pos, Entry, SonyEricsson_VCard21_Phone);
	free(data);
	data=NULL;
	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

/**
 * Reads memory by reading from full data.
 */
GSM_Error OBEXGEN_GetMemoryFull(GSM_StateMachine *s, GSM_MemoryEntry *Entry)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	size_t		pos = 0;

	/* Read phonebook data */
	error = OBEXGEN_InitPbLUID(s);
	if (error != ERR_NONE) return error;

	/* Check bounds */
	if (Entry->Location > Priv->PbCount) return ERR_EMPTY; /* Maybe invalid location? */

	/* Decode vCard */
	error = GSM_DecodeVCARD(&(s->di), Priv->PbData + Priv->PbOffsets[Entry->Location], &pos, Entry, SonyEricsson_VCard21_Phone);
	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

GSM_Error OBEXGEN_GetMemory(GSM_StateMachine *s, GSM_MemoryEntry *Entry)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	if (Entry->MemoryType != MEM_ME && Entry->MemoryType != MEM_SM) return ERR_NOTSUPPORTED;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_GetMemory(s, Entry);
	}

	if (Entry->MemoryType != MEM_ME) return ERR_NOTSUPPORTED;

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->PbCap.IEL == -1) {
		error = OBEXGEN_GetPbInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->PbCap.IEL == 0x8 || Priv->PbCap.IEL == 0x10) {
		return OBEXGEN_GetMemoryLUID(s, Entry);
	} else if (Priv->PbCap.IEL == 0x4) {
		return OBEXGEN_GetMemoryIndex(s, Entry);
	} else if (Priv->PbCap.IEL == 0x2) {
		return OBEXGEN_GetMemoryFull(s, Entry);
	} else {
		smprintf(s, "Can not read phonebook from IEL 1 phone\n");
		return ERR_NOTSUPPORTED;
	}
}

GSM_Error OBEXGEN_GetNextMemory(GSM_StateMachine *s, GSM_MemoryEntry *Entry, gboolean start)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_Error 	error = ERR_EMPTY;;


	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_GetNextMemory(s, Entry, start);
	}

	/* Get  location */
	if (start) {
		Entry->Location = 1;
		Priv->ReadPhonebook = 0;
	} else {
		Entry->Location++;
	}

	/* Do real getting */
	while (error == ERR_EMPTY) {

		/* Have we read them all? */
		/* Needs to be inside loop as we get count after
		 * first invocation of get function */
		if (Priv->ReadPhonebook == Priv->PbCount) {
			return ERR_EMPTY;
		}

		error = OBEXGEN_GetMemory(s, Entry);
		if (error == ERR_NONE) {
			Priv->ReadPhonebook++;
		} else if (error == ERR_EMPTY) {
			Entry->Location++;
		}
	}
	return error;
}

GSM_Error OBEXGEN_AddMemory(GSM_StateMachine *s, GSM_MemoryEntry *Entry)
{
	unsigned char 		req[5000];
	char			path[100];
	size_t			size=0;
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	if (Entry->MemoryType != MEM_ME && (Entry->MemoryType != MEM_SM || Priv->Service != OBEX_m_OBEX)) return ERR_NOTSUPPORTED;

	/* Encode vCard */
	error = GSM_EncodeVCARD(&(s->di), req, sizeof(req), &size, Entry, TRUE, SonyEricsson_VCard21);
	if (error != ERR_NONE) return error;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_CreateEntry(s, "m-obex/contacts/create", Entry->MemoryType, &(Entry->Location), req);
	}

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->PbCap.IEL == -1) {
		error = OBEXGEN_GetPbInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->PbCap.IEL == 0x8 || Priv->PbCap.IEL == 0x10) {
		/* We need to grab LUID list now in order to keep position later */
		error = OBEXGEN_InitPbLUID(s);
		if (error != ERR_NONE) return error;

		smprintf(s,"Adding phonebook entry %ld:\n%s\n", (long)size, req);
		Priv->UpdatePbLUID = TRUE;
		error = OBEXGEN_SetFile(s, "telecom/pb/luid/.vcf", req, size, FALSE);
		Entry->Location = Priv->PbLUIDCount;
		if (error == ERR_NONE) Priv->PbCount++;
		return error;
	} else if (Priv->PbCap.IEL == 0x4) {
		/* We need to grab LUID/Index list now in order to keep position later */
		error = OBEXGEN_InitPbLUID(s);
		if (error != ERR_NONE) return error;

		Entry->Location = OBEXGEN_GetFirstFreeLocation(&Priv->PbIndex, &Priv->PbIndexCount);
		smprintf(s,"Adding phonebook entry %ld at location %d:\n%s\n", (long)size, Entry->Location, req);
		sprintf(path, "telecom/pb/%d.vcf", Entry->Location);
		error = OBEXGEN_SetFile(s, path, req, size, FALSE);
		if (error == ERR_NONE) Priv->PbCount++;
		return error;
	} else {
		/* I don't know add command for other levels, just plain send vCard */
		Entry->Location = 0;
		smprintf(s,"Sending phonebook entry\n");
		return OBEXGEN_SetFile(s, "gammu.vcf", req, size, FALSE);
	}
}

GSM_Error OBEXGEN_SetMemoryLUID(GSM_StateMachine *s, GSM_MemoryEntry *Entry, const char *Data, int Size)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	char		*path=NULL;

	error = OBEXGEN_InitPbLUID(s);
	if (error != ERR_NONE) return error;

	/* Check bounds */
	if (Entry->Location > Priv->PbLUIDCount ||
			Priv->PbLUID[Entry->Location] == NULL) {
		/**
		 * \todo We should keep location here!
		 */
		return OBEXGEN_AddMemory(s, Entry);
	}

	/* Calculate path */
	path = (char *)malloc(strlen(Priv->PbLUID[Entry->Location]) + 22); /* Length of string below */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/pb/luid/%s.vcf", Priv->PbLUID[Entry->Location]);
	smprintf(s, "Seting vCard %s [%d]\n", path, Entry->Location);

	/* Forget entry if we're deleting */
	if (Size == 0) {
		free(Priv->PbLUID[Entry->Location]);
		Priv->PbLUID[Entry->Location] = NULL;
		Priv->PbCount--;
	}

	/* Store vCard */
	error = OBEXGEN_SetFile(s, path, Data, Size, Size == 0 ? Priv->PbCap.HD : FALSE);;
	free(path);
	return error;
}

GSM_Error OBEXGEN_SetMemoryIndex(GSM_StateMachine *s, GSM_MemoryEntry *Entry, const char *Data, int Size)
{
	char		*path=NULL;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_Error		error;

	/* Forget entry if we're deleting */
	if (Size == 0) {
		Priv->PbCount--;
	}

	/* Calculate path */
	path = (char *)malloc(20 + 22); /* Length of string below + length of number */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/pb/%d.vcf", Entry->Location);
	smprintf(s, "Seting vCard %s\n", path);

	/* Store vCard */
	error = OBEXGEN_SetFile(s, path, Data, Size, FALSE);;
	free(path);
	return error;
}

GSM_Error OBEXGEN_SetMemory(GSM_StateMachine *s, GSM_MemoryEntry *Entry)
{
	unsigned char 		req[5000];
	size_t			size=0;
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	if (Entry->MemoryType != MEM_ME && (Entry->MemoryType != MEM_SM || Priv->Service != OBEX_m_OBEX)) return ERR_NOTSUPPORTED;

	/* Encode vCard */
	error = GSM_EncodeVCARD(&(s->di), req, sizeof(req), &size, Entry, TRUE, SonyEricsson_VCard21);
	if (error != ERR_NONE) return error;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_UpdateEntry(s, "m-obex/contacts/write", Entry->Location, Entry->MemoryType, req);
	}

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->PbCap.IEL == -1) {
		error = OBEXGEN_GetPbInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->PbCap.IEL == 0x8 || Priv->PbCap.IEL == 0x10) {
		return OBEXGEN_SetMemoryLUID(s, Entry, req, size);
	} else if (Priv->PbCap.IEL == 0x4) {
		return OBEXGEN_SetMemoryIndex(s, Entry, req, size);
	} else if (Priv->PbCap.IEL == 0x2) {
		/* Work on full phonebook */
		return ERR_NOTIMPLEMENTED;
	} else {
		return ERR_NOTSUPPORTED;
	}
}

GSM_Error OBEXGEN_DeleteMemory(GSM_StateMachine *s, GSM_MemoryEntry *Entry)
{
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	if (Entry->MemoryType != MEM_ME && Entry->MemoryType != MEM_SM) return ERR_NOTSUPPORTED;

	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_UpdateEntry(s, "m-obex/contacts/delete", Entry->Location, Entry->MemoryType, NULL);
	}

	if (Entry->MemoryType != MEM_ME) return ERR_NOTSUPPORTED;

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->PbCap.IEL == -1) {
		error = OBEXGEN_GetPbInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->PbCap.IEL == 0x8 || Priv->PbCap.IEL == 0x10) {
		return OBEXGEN_SetMemoryLUID(s, Entry, "", 0);
	} else if (Priv->PbCap.IEL == 0x4) {
		return OBEXGEN_SetMemoryIndex(s, Entry, "", 0);
	} else if (Priv->PbCap.IEL == 0x2) {
		/* Work on full phonebook */
		return ERR_NOTIMPLEMENTED;
	} else {
		return ERR_NOTSUPPORTED;
	}
}

GSM_Error OBEXGEN_DeleteAllMemory(GSM_StateMachine *s, GSM_MemoryType MemoryType)
{
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_MemoryEntry		entry;

	/* Should not happen */
	if (MemoryType != MEM_ME) return ERR_NOTSUPPORTED;

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need count of entries */
	error = OBEXGEN_InitPbLUID(s);
	if (error != ERR_NONE) return error;

	/* Delete all entries */
	entry.Location = 1;
	entry.MemoryType = MEM_ME;
	while (Priv->PbCount > 0) {
		error = OBEXGEN_DeleteMemory(s, &entry);
		if (error != ERR_NONE && error != ERR_EMPTY) return error;
		entry.Location++;
	}
	return error;
}

/*@}*/

/**
 * \defgroup IrMCcaltodo IrMC common calendar and todo functions
 * \ingroup OBEXPhone
 * @{
 */

/**
 * Parses cal/info.log (calendar IrMC information log).
 */
GSM_Error OBEXGEN_GetCalInformation(GSM_StateMachine *s, int *free_records, int *used)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	return OBEXGEN_GetInformation(s, "telecom/cal/info.log", free_records, used, &(Priv->CalCap));

}

/**
 * Initializes calendar LUID database.
 */
GSM_Error OBEXGEN_InitCalLUID(GSM_StateMachine *s)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_Error 	error;

	/* We might do validation here using telecom/cal/luid/cc.log fir IEL 4, but not on each request */
	if (Priv->CalData != NULL) return ERR_NONE;

	error = OBEXGEN_InitLUID(s, "telecom/cal.vcs", FALSE, "BEGIN:VEVENT",
			&(Priv->CalData), &(Priv->CalOffsets), &(Priv->CalCount),
			&(Priv->CalLUID), &(Priv->CalLUIDCount),
			&(Priv->CalIndex), &(Priv->CalIndexCount));
	if (error != ERR_NONE) return error;
	return OBEXGEN_InitLUID(s, "telecom/cal.vcs", TRUE, "BEGIN:VTODO",
			&(Priv->CalData), &(Priv->TodoOffsets), &(Priv->TodoCount),
			&(Priv->TodoLUID), &(Priv->TodoLUIDCount),
			&(Priv->TodoIndex), &(Priv->TodoIndexCount));
}

/*@}*/

/**
 * \defgroup IrMCcalendar IrMC calendar support
 * \ingroup OBEXPhone
 * @{
 */

/**
 * Grabs calendar memory status
 */
GSM_Error OBEXGEN_GetCalendarStatus(GSM_StateMachine *s, GSM_CalendarStatus *Status)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_Error 	error;

	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_GetStatus(s, "m-obex/calendar/count", MEM_ME, &(Status->Free), &(Status->Used));
	}

	error = OBEXGEN_InitCalLUID(s);
	if (error != ERR_NONE) return error;

	Status->Used = Priv->CalCount;

	return OBEXGEN_GetCalInformation(s, &(Status->Free), NULL);

}

/**
 * Read memory by reading static index.
 */
GSM_Error OBEXGEN_GetCalendarIndex(GSM_StateMachine *s, GSM_CalendarEntry *Entry)
{
	GSM_Error 	error;
	char		*data=NULL;
	char		*path=NULL;
	size_t		pos = 0;
	GSM_ToDoEntry	ToDo;

	error = OBEXGEN_InitCalLUID(s);
	if (error != ERR_NONE) return error;

	/* Calculate path */
	path = (char *)malloc(20 + 22); /* Length of string below + length of number */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/cal/%d.vcs", Entry->Location);
	smprintf(s, "Getting vCalendar %s\n", path);

	/* Grab vCalendar */
	error = OBEXGEN_GetTextFile(s, path, &data);
	free(path);
	path=NULL;
	if (error == ERR_FILENOTEXIST) return ERR_EMPTY;
	if (error != ERR_NONE) return error;

	/* Decode it */
	error = GSM_DecodeVCALENDAR_VTODO(&(s->di), data, &pos, Entry, &ToDo, SonyEricsson_VCalendar, SonyEricsson_VToDo);
	free(data);
	data=NULL;
	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

/**
 * Reads memory by reading from LUID location.
 */
GSM_Error OBEXGEN_GetCalendarLUID(GSM_StateMachine *s, GSM_CalendarEntry *Entry)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	char		*data=NULL;
	char		*path=NULL;
	size_t		pos = 0;
	GSM_ToDoEntry	ToDo;

	error = OBEXGEN_InitCalLUID(s);
	if (error != ERR_NONE) return error;

	/* Check bounds */
	if (Entry->Location > Priv->CalLUIDCount) return ERR_EMPTY; /* Maybe invalid location? */
	if (Priv->CalLUID[Entry->Location] == NULL) return ERR_EMPTY;

	/* Calculate path */
	path = (char *)malloc(strlen(Priv->CalLUID[Entry->Location]) + 22); /* Length of string below */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/cal/luid/%s.vcs", Priv->CalLUID[Entry->Location]);
	smprintf(s, "Getting vCalendar %s\n", path);

	/* Grab vCalendar */
	error = OBEXGEN_GetTextFile(s, path, &data);
	free(path);
	path=NULL;
	if (error != ERR_NONE) return error;

	/* Decode it */
	error = GSM_DecodeVCALENDAR_VTODO(&(s->di), data, &pos, Entry, &ToDo, SonyEricsson_VCalendar, SonyEricsson_VToDo);
	free(data);
	data=NULL;
	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

/**
 * Reads memory by reading from full data.
 */
GSM_Error OBEXGEN_GetCalendarFull(GSM_StateMachine *s, GSM_CalendarEntry *Entry)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	size_t		pos = 0;
	GSM_ToDoEntry	ToDo;

	/* Read calendar data */
	error = OBEXGEN_InitCalLUID(s);
	if (error != ERR_NONE) return error;

	/* Check bounds */
	if (Entry->Location > Priv->CalCount) return ERR_EMPTY; /* Maybe invalid location? */

	/* Decode vCalendar */
	error = GSM_DecodeVCALENDAR_VTODO(&(s->di), Priv->CalData + Priv->CalOffsets[Entry->Location], &pos, Entry, &ToDo, SonyEricsson_VCalendar, SonyEricsson_VToDo);
	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

GSM_Error OBEXGEN_GetCalendar(GSM_StateMachine *s, GSM_CalendarEntry *Entry)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_GetCalendar(s, Entry);
	}

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->CalCap.IEL == -1) {
		error = OBEXGEN_GetCalInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->CalCap.IEL == 0x8 || Priv->CalCap.IEL == 0x10) {
		return OBEXGEN_GetCalendarLUID(s, Entry);
	} else if (Priv->CalCap.IEL == 0x4) {
		return OBEXGEN_GetCalendarIndex(s, Entry);
	} else if (Priv->CalCap.IEL == 0x2) {
		return OBEXGEN_GetCalendarFull(s, Entry);
	} else {
		smprintf(s, "Can not read calendar from IEL 1 phone\n");
		return ERR_NOTSUPPORTED;
	}
}

GSM_Error OBEXGEN_GetNextCalendar(GSM_StateMachine *s, GSM_CalendarEntry *Entry, gboolean start)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_Error 	error = ERR_EMPTY;;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_GetNextCalendar(s, Entry, start);
	}

	/* Get  location */
	if (start) {
		Entry->Location = 1;
		Priv->ReadCalendar = 0;
	} else {
		Entry->Location++;
	}

	/* Do real getting */
	while (error == ERR_EMPTY) {

		/* Have we read them all? */
		/* Needs to be inside loop as we get count after
		 * first invocation of get function */
		if (Priv->ReadCalendar == Priv->CalCount) {
			return ERR_EMPTY;
		}

		error = OBEXGEN_GetCalendar(s, Entry);
		if (error == ERR_NONE) {
			Priv->ReadCalendar++;
		} else if (error == ERR_EMPTY) {
			Entry->Location++;
		}
	}
	return error;
}

GSM_Error OBEXGEN_AddCalendar(GSM_StateMachine *s, GSM_CalendarEntry *Entry)
{
	unsigned char 		req[5000];
	char			path[100];
	size_t			size=0;
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* Encode vCalendar */
	error = GSM_EncodeVCALENDAR(req, sizeof(req), &size, Entry, TRUE, SonyEricsson_VCalendar);
	if (error != ERR_NONE) return error;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_CreateEntry(s, "m-obex/calendar/create", MEM_ME, &(Entry->Location), req);
	}

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->CalCap.IEL == -1) {
		error = OBEXGEN_GetCalInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->CalCap.IEL == 0x8 || Priv->CalCap.IEL == 0x10) {
		/* We need to grab LUID list now in order to keep position later */
		error = OBEXGEN_InitCalLUID(s);
		if (error != ERR_NONE) return error;

		smprintf(s,"Adding calendar entry %ld:\n%s\n", (long)size, req);
		Priv->UpdateCalLUID = TRUE;
		error = OBEXGEN_SetFile(s, "telecom/cal/luid/.vcs", req, size, FALSE);
		Entry->Location = Priv->CalLUIDCount;
		if (error == ERR_NONE) Priv->CalCount++;
		return error;
	} else if (Priv->CalCap.IEL == 0x4) {
		/* We need to grab LUID/Index list now in order to keep position later */
		error = OBEXGEN_InitCalLUID(s);
		if (error != ERR_NONE) return error;

		Entry->Location = OBEXGEN_GetFirstFreeLocation(&Priv->CalIndex, &Priv->CalIndexCount);
		smprintf(s,"Adding calendar entry %ld at location %d:\n%s\n", (long)size, Entry->Location, req);
		sprintf(path, "telecom/cal/%d.vcf", Entry->Location);
		error = OBEXGEN_SetFile(s, path, req, size, FALSE);
		if (error == ERR_NONE) Priv->CalCount++;
		return error;
	} else {
		/* I don't know add command for other levels, just plain send vCalendar */
		Entry->Location = 0;
		smprintf(s,"Sending calendar entry\n");
		return OBEXGEN_SetFile(s, "gammu.vcs", req, size, FALSE);
	}
}

GSM_Error OBEXGEN_SetCalendarLUID(GSM_StateMachine *s, GSM_CalendarEntry *Entry, const char *Data, int Size)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	char		*path=NULL;

	error = OBEXGEN_InitCalLUID(s);
	if (error != ERR_NONE) return error;

	/* Check bounds */
	if (Entry->Location > Priv->CalLUIDCount
			|| Priv->CalLUID[Entry->Location] == NULL) {
		/**
		 * \todo We should keep location here!
		 */
		return OBEXGEN_AddCalendar(s, Entry);
	}

	/* Calculate path */
	path = (char *)malloc(strlen(Priv->CalLUID[Entry->Location]) + 22); /* Length of string below */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/cal/luid/%s.vcs", Priv->CalLUID[Entry->Location]);
	smprintf(s, "Seting vCalendar %s\n", path);

	/* Forget entry if we're deleting */
	if (Size == 0) {
		free(Priv->CalLUID[Entry->Location]);
		Priv->CalLUID[Entry->Location] = NULL;
		Priv->CalCount--;
	}

	/* Store vCalendar */
	error = OBEXGEN_SetFile(s, path, Data, Size, Size == 0 ? Priv->CalCap.HD : FALSE);;
	free(path);
	return error;
}

GSM_Error OBEXGEN_SetCalendarIndex(GSM_StateMachine *s, GSM_CalendarEntry *Entry, const char *Data, int Size)
{
	char		*path=NULL;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_Error		error;

	/* Forget entry if we're deleting */
	if (Size == 0) {
		Priv->CalCount--;
	}

	/* Calculate path */
	path = (char *)malloc(20 + 22); /* Length of string below + length of number */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/cal/%d.vcs", Entry->Location);
	smprintf(s, "Seting vCalendar %s\n", path);

	/* Store vCalendar */
	error = OBEXGEN_SetFile(s, path, Data, Size, FALSE);;
	free(path);
	return error;
}

GSM_Error OBEXGEN_SetCalendar(GSM_StateMachine *s, GSM_CalendarEntry *Entry)
{
	unsigned char 		req[5000];
	size_t			size=0;
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* Encode vCalendar */
	error = GSM_EncodeVCALENDAR(req, sizeof(req), &size, Entry, TRUE, SonyEricsson_VCalendar);
	if (error != ERR_NONE) return error;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_UpdateEntry(s, "m-obex/calendar/write", Entry->Location, MEM_ME, req);
	}

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->CalCap.IEL == -1) {
		error = OBEXGEN_GetCalInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->CalCap.IEL == 0x8 || Priv->CalCap.IEL == 0x10) {
		return OBEXGEN_SetCalendarLUID(s, Entry, req, size);
	} else if (Priv->CalCap.IEL == 0x4) {
		return OBEXGEN_SetCalendarIndex(s, Entry, req, size);
	} else if (Priv->CalCap.IEL == 0x2) {
		/* Work on full calendar */
		return ERR_NOTIMPLEMENTED;
	} else {
		return ERR_NOTSUPPORTED;
	}
}

GSM_Error OBEXGEN_DeleteCalendar(GSM_StateMachine *s, GSM_CalendarEntry *Entry)
{
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_UpdateEntry(s, "m-obex/calendar/delete", Entry->Location, 1, NULL);
	}

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->CalCap.IEL == -1) {
		error = OBEXGEN_GetCalInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->CalCap.IEL == 0x8 || Priv->CalCap.IEL == 0x10) {
		return OBEXGEN_SetCalendarLUID(s, Entry, "", 0);
	} else if (Priv->CalCap.IEL == 0x4) {
		return OBEXGEN_SetCalendarIndex(s, Entry, "", 0);
	} else if (Priv->CalCap.IEL == 0x2) {
		/* Work on full calendar */
		return ERR_NOTIMPLEMENTED;
	} else {
		return ERR_NOTSUPPORTED;
	}
}

GSM_Error OBEXGEN_DeleteAllCalendar(GSM_StateMachine *s)
{
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_CalendarEntry		entry;

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need count of entries */
	error = OBEXGEN_InitCalLUID(s);
	if (error != ERR_NONE) return error;

	/* Delete all entries */
	entry.Location = 1;
	while (Priv->CalCount > 0) {
		error = OBEXGEN_DeleteCalendar(s, &entry);
		if (error != ERR_NONE && error != ERR_EMPTY) return error;
		entry.Location++;
	}
	return error;
}

/*@}*/

/**
 * \defgroup IrMCtodo IrMC todo support
 * \ingroup OBEXPhone
 * @{
 */

/**
 * Grabs todo memory status
 */
GSM_Error OBEXGEN_GetTodoStatus(GSM_StateMachine *s, GSM_ToDoStatus *Status)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_Error 	error;

	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_GetStatus(s, "m-obex/calendar/count", 0xff, &(Status->Free), &(Status->Used));
	}

	error = OBEXGEN_InitCalLUID(s);
	if (error != ERR_NONE) return error;

	Status->Used = Priv->TodoCount;

	return OBEXGEN_GetCalInformation(s, &(Status->Free), NULL);

}

/**
 * Read memory by reading static index.
 */
GSM_Error OBEXGEN_GetTodoIndex(GSM_StateMachine *s, GSM_ToDoEntry *Entry)
{
	GSM_Error 	error;
	char		*data=NULL;
	char		*path=NULL;
	size_t		pos = 0;
	GSM_CalendarEntry	Cal;

	/* Todoculate path */
	path = (char *)malloc(20 + 22); /* Length of string below + length of number */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/cal/%d.vcs", Entry->Location);
	smprintf(s, "Getting vTodo %s\n", path);

	/* Grab vTodo */
	error = OBEXGEN_GetTextFile(s, path, &data);
	free(path);
	path=NULL;
	if (error == ERR_FILENOTEXIST) return ERR_EMPTY;
	if (error != ERR_NONE) return error;

	/* Decode it */
	error = GSM_DecodeVCALENDAR_VTODO(&(s->di), data, &pos, &Cal, Entry, SonyEricsson_VCalendar, SonyEricsson_VToDo);
	free(data);
	data=NULL;
	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

/**
 * Reads memory by reading from LUID location.
 */
GSM_Error OBEXGEN_GetTodoLUID(GSM_StateMachine *s, GSM_ToDoEntry *Entry)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	char		*data=NULL;
	char		*path=NULL;
	size_t		pos = 0;
	GSM_CalendarEntry	Cal;

	error = OBEXGEN_InitCalLUID(s);
	if (error != ERR_NONE) return error;

	/* Check bounds */
	if (Entry->Location > Priv->TodoLUIDCount) return ERR_EMPTY; /* Maybe invalid location? */
	if (Priv->TodoLUID[Entry->Location] == NULL) return ERR_EMPTY;

	/* Todoculate path */
	path = (char *)malloc(strlen(Priv->TodoLUID[Entry->Location]) + 22); /* Length of string below */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/cal/luid/%s.vcs", Priv->TodoLUID[Entry->Location]);
	smprintf(s, "Getting vTodo %s\n", path);

	/* Grab vTodo */
	error = OBEXGEN_GetTextFile(s, path, &data);
	free(path);
	path=NULL;
	if (error != ERR_NONE) return error;

	/* Decode it */
	error = GSM_DecodeVCALENDAR_VTODO(&(s->di), data, &pos, &Cal, Entry, SonyEricsson_VCalendar, SonyEricsson_VToDo);
	free(data);
	data=NULL;
	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

/**
 * Reads memory by reading from full data.
 */
GSM_Error OBEXGEN_GetTodoFull(GSM_StateMachine *s, GSM_ToDoEntry *Entry)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	size_t		pos = 0;
	GSM_CalendarEntry	Cal;

	/* Read todo data */
	error = OBEXGEN_InitCalLUID(s);
	if (error != ERR_NONE) return error;

	/* Check bounds */
	if (Entry->Location > Priv->TodoCount) return ERR_EMPTY; /* Maybe invalid location? */

	/* Decode vTodo */
	error = GSM_DecodeVCALENDAR_VTODO(&(s->di), Priv->CalData + Priv->TodoOffsets[Entry->Location], &pos, &Cal, Entry, SonyEricsson_VCalendar, SonyEricsson_VToDo);
	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

GSM_Error OBEXGEN_GetTodo(GSM_StateMachine *s, GSM_ToDoEntry *Entry)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_GetTodo(s, Entry);
	}

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->CalCap.IEL == -1) {
		error = OBEXGEN_GetCalInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->CalCap.IEL == 0x8 || Priv->CalCap.IEL == 0x10) {
		return OBEXGEN_GetTodoLUID(s, Entry);
	} else if (Priv->CalCap.IEL == 0x4) {
		return OBEXGEN_GetTodoIndex(s, Entry);
	} else if (Priv->CalCap.IEL == 0x2) {
		return OBEXGEN_GetTodoFull(s, Entry);
	} else {
		smprintf(s, "Can not read todo from IEL 1 phone\n");
		return ERR_NOTSUPPORTED;
	}
}

GSM_Error OBEXGEN_GetNextTodo(GSM_StateMachine *s, GSM_ToDoEntry *Entry, gboolean start)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_Error 	error = ERR_EMPTY;;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_GetNextTodo(s, Entry, start);
	}

	/* Get  location */
	if (start) {
		Entry->Location = 1;
		Priv->ReadTodo = 0;
	} else {
		Entry->Location++;
	}

	smprintf (s, "stat: %d, %d\n", Priv->ReadTodo, Priv->TodoCount);

	/* Do real getting */
	while (error == ERR_EMPTY) {

		/* Have we read them all? */
		/* Needs to be inside loop as we get count after
		 * first invocation of get function */
		if (Priv->ReadTodo >= Priv->TodoCount) {
			return ERR_EMPTY;
		}

		error = OBEXGEN_GetTodo(s, Entry);
		smprintf (s, "attempted location: %d, %d\n", Entry->Location, error);
		if (error == ERR_NONE) {
			Priv->ReadTodo++;
		} else if (error == ERR_EMPTY) {
			Entry->Location++;
		}
	}
	return error;
}

GSM_Error OBEXGEN_AddTodo(GSM_StateMachine *s, GSM_ToDoEntry *Entry)
{
	unsigned char 		req[5000];
	char			path[100];
	size_t			size=0;
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* Encode vTodo */
	error = GSM_EncodeVTODO(req, sizeof(req), &size, Entry, TRUE, SonyEricsson_VToDo);
	if (error != ERR_NONE) return error;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_CreateEntry(s, "m-obex/calendar/create", 7 /* 0xff */, &(Entry->Location), req);
	}

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->CalCap.IEL == -1) {
		error = OBEXGEN_GetCalInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->CalCap.IEL == 0x8 || Priv->CalCap.IEL == 0x10) {
		/* We need to grab LUID list now in order to keep position later */
		error = OBEXGEN_InitCalLUID(s);
		if (error != ERR_NONE) return error;

		smprintf(s,"Adding todo entry %ld:\n%s\n", (long)size, req);
		Priv->UpdateTodoLUID = TRUE;
		error = OBEXGEN_SetFile(s, "telecom/cal/luid/.vcs", req, size, FALSE);
		Entry->Location = Priv->TodoLUIDCount;
		if (error == ERR_NONE) Priv->TodoCount++;
		return error;
	} else if (Priv->CalCap.IEL == 0x4) {
		/* We need to grab LUID/Index list now in order to keep position later */
		error = OBEXGEN_InitCalLUID(s);
		if (error != ERR_NONE) return error;

		Entry->Location = OBEXGEN_GetFirstFreeLocation(&Priv->TodoIndex, &Priv->TodoIndexCount);
		smprintf(s,"Adding todo entry %ld at location %d:\n%s\n", (long)size, Entry->Location, req);
		sprintf(path, "telecom/cal/%d.vcf", Entry->Location);
		error = OBEXGEN_SetFile(s, path, req, size, FALSE);
		if (error == ERR_NONE) Priv->TodoCount++;
		return error;
	} else {
		/* I don't know add command for other levels, just plain send vTodo */
		Entry->Location = 0;
		smprintf(s,"Sending todo entry\n");
		return OBEXGEN_SetFile(s, "gammu.vcs", req, size, FALSE);
	}
}

GSM_Error OBEXGEN_SetTodoLUID(GSM_StateMachine *s, GSM_ToDoEntry *Entry, const char *Data, int Size)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	char		*path=NULL;

	error = OBEXGEN_InitCalLUID(s);
	if (error != ERR_NONE) return error;

	/* Check bounds */
	if (Entry->Location > Priv->TodoLUIDCount ||
			NULL == Priv->TodoLUID || Priv->TodoLUID[Entry->Location] == NULL) {
		/**
		 * \todo We should keep location here!
		 */
		return OBEXGEN_AddTodo(s, Entry);
	}

	/* Calculate path */
	path = (char *)malloc(strlen(Priv->TodoLUID[Entry->Location]) + 22); /* Length of string below */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/cal/luid/%s.vcs", Priv->TodoLUID[Entry->Location]);
	smprintf(s, "Seting vTodo %s\n", path);

	/* Forget entry if we're deleting */
	if (Size == 0) {
		free(Priv->TodoLUID[Entry->Location]);
		Priv->TodoLUID[Entry->Location] = NULL;
		Priv->TodoCount--;
	}

	/* Store vTodo */
	error = OBEXGEN_SetFile(s, path, Data, Size, Size == 0 ? Priv->CalCap.HD : FALSE);;
	free(path);
	return error;
}

GSM_Error OBEXGEN_SetTodoIndex(GSM_StateMachine *s, GSM_ToDoEntry *Entry, const char *Data, int Size)
{
	char		*path=NULL;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_Error		error;

	/* Forget entry if we're deleting */
	if (Size == 0) {
		Priv->TodoCount--;
	}

	/* Todoculate path */
	path = (char *)malloc(20 + 22); /* Length of string below + length of number */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/cal/%d.vcs", Entry->Location);
	smprintf(s, "Seting vTodo %s\n", path);

	/* Store vTodo */
	error = OBEXGEN_SetFile(s, path, Data, Size, FALSE);;
	free(path);
	return error;
}

GSM_Error OBEXGEN_SetTodo(GSM_StateMachine *s, GSM_ToDoEntry *Entry)
{
	unsigned char 		req[5000];
	size_t			size=0;
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* Encode vTodo */
	error = GSM_EncodeVTODO(req, sizeof(req), &size, Entry, TRUE, SonyEricsson_VToDo);
	if (error != ERR_NONE) return error;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_UpdateEntry(s, "m-obex/calendar/write",
					 Entry->Location, 7, req);
	}

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->CalCap.IEL == -1) {
		error = OBEXGEN_GetCalInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->CalCap.IEL == 0x8 || Priv->CalCap.IEL == 0x10) {
		return OBEXGEN_SetTodoLUID(s, Entry, req, size);
	} else if (Priv->CalCap.IEL == 0x4) {
		return OBEXGEN_SetTodoIndex(s, Entry, req, size);
	} else if (Priv->CalCap.IEL == 0x2) {
		/* Work on full todo */
		return ERR_NOTIMPLEMENTED;
	} else {
		return ERR_NOTSUPPORTED;
	}
}

GSM_Error OBEXGEN_DeleteTodo(GSM_StateMachine *s, GSM_ToDoEntry *Entry)
{
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* Handle m-obex case */
	if (Priv->Service == OBEX_m_OBEX) {
		return MOBEX_UpdateEntry(s, "m-obex/calendar/delete", Entry->Location, 7 /* 0xff */, NULL);
	}

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->CalCap.IEL == -1) {
		error = OBEXGEN_GetCalInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->CalCap.IEL == 0x8 || Priv->CalCap.IEL == 0x10) {
		return OBEXGEN_SetTodoLUID(s, Entry, "", 0);
	} else if (Priv->CalCap.IEL == 0x4) {
		return OBEXGEN_SetTodoIndex(s, Entry, "", 0);
	} else if (Priv->CalCap.IEL == 0x2) {
		/* Work on full todo */
		return ERR_NOTIMPLEMENTED;
	} else {
		return ERR_NOTSUPPORTED;
	}
}

GSM_Error OBEXGEN_DeleteAllTodo(GSM_StateMachine *s)
{
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_ToDoEntry		entry;

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need count of entries */
	error = OBEXGEN_InitCalLUID(s);
	if (error != ERR_NONE) return error;

	/* Delete all entries */
	entry.Location = 1;
	while (Priv->TodoCount > 0) {
		error = OBEXGEN_DeleteTodo(s, &entry);
		if (error != ERR_NONE && error != ERR_EMPTY) return error;
		entry.Location++;
	}
	return error;
}

/*@}*/

/**
 * \defgroup IrMCnote IrMC note support
 * \ingroup OBEXPhone
 * @{
 */

/**
 * Parses pb/info.log (note IrMC information log).
 */
GSM_Error OBEXGEN_GetNoteInformation(GSM_StateMachine *s, int *free_records, int *used)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	return OBEXGEN_GetInformation(s, "telecom/nt/info.log", free_records, used, &(Priv->NoteCap));

}

/**
 * Grabs note memory status
 */
GSM_Error OBEXGEN_GetNoteStatus(GSM_StateMachine *s, GSM_ToDoStatus *Status)
{
	return OBEXGEN_GetNoteInformation(s, &(Status->Free), &(Status->Used));

}

/**
 * Initializes note LUID database.
 */
GSM_Error OBEXGEN_InitNoteLUID(GSM_StateMachine *s)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* We might do validation here using telecom/nt/luid/cc.log fir IEL 4, but not on each request */
	if (Priv->NoteData != NULL) return ERR_NONE;

	return OBEXGEN_InitLUID(s, "telecom/nt.vcf", FALSE, "BEGIN:VNOTE",
			&(Priv->NoteData), &(Priv->NoteOffsets), &(Priv->NoteCount),
			&(Priv->NoteLUID), &(Priv->NoteLUIDCount),
			&(Priv->NoteIndex), &(Priv->NoteIndexCount));
}

/**
 * Read memory by reading static index.
 */
GSM_Error OBEXGEN_GetNoteIndex(GSM_StateMachine *s, GSM_NoteEntry *Entry)
{
	GSM_Error 	error;
	char		*data=NULL;
	char		*path=NULL;
	size_t		pos = 0;

	error = OBEXGEN_InitNoteLUID(s);
	if (error != ERR_NONE) return error;

	/* Calculate path */
	path = (char *)malloc(20 + 22); /* Length of string below + length of number */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/nt/%d.vnt", Entry->Location);
	smprintf(s, "Getting vNote %s\n", path);

	/* Grab vCard */
	error = OBEXGEN_GetTextFile(s, path, &data);
	free(path);
	path=NULL;
	if (error == ERR_FILENOTEXIST) return ERR_EMPTY;
	if (error != ERR_NONE) return error;

	/* Decode it */
	error = GSM_DecodeVNOTE(data, &pos, Entry);
	free(data);
	data=NULL;
	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

/**
 * Reads memory by reading from LUID location.
 */
GSM_Error OBEXGEN_GetNoteLUID(GSM_StateMachine *s, GSM_NoteEntry *Entry)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	char		*data=NULL;
	char		*path=NULL;
	size_t		pos = 0;

	error = OBEXGEN_InitNoteLUID(s);
	if (error != ERR_NONE) return error;

	/* Check bounds */
	if (Entry->Location > Priv->NoteLUIDCount) return ERR_EMPTY; /* Maybe invalid location? */
	if (Priv->NoteLUID[Entry->Location] == NULL) return ERR_EMPTY;

	/* Calculate path */
	path = (char *)malloc(strlen(Priv->NoteLUID[Entry->Location]) + 22); /* Length of string below */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/nt/luid/%s.vnt", Priv->NoteLUID[Entry->Location]);
	smprintf(s, "Getting vNote %s\n", path);

	/* Grab vCard */
	error = OBEXGEN_GetTextFile(s, path, &data);
	free(path);
	path=NULL;
	if (error != ERR_NONE) return error;

	/* Decode it */
	error = GSM_DecodeVNOTE(data, &pos, Entry);
	free(data);
	data=NULL;
	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

/**
 * Reads memory by reading from full data.
 */
GSM_Error OBEXGEN_GetNoteFull(GSM_StateMachine *s, GSM_NoteEntry *Entry)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	size_t		pos = 0;

	/* Read note data */
	error = OBEXGEN_InitNoteLUID(s);
	if (error != ERR_NONE) return error;

	/* Check bounds */
	if (Entry->Location > Priv->NoteCount) return ERR_EMPTY; /* Maybe invalid location? */

	/* Decode vNote */
	error = GSM_DecodeVNOTE(Priv->NoteData + Priv->NoteOffsets[Entry->Location], &pos, Entry);
	if (error != ERR_NONE) return error;

	return ERR_NONE;
}

GSM_Error OBEXGEN_GetNote(GSM_StateMachine *s, GSM_NoteEntry *Entry)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->NoteCap.IEL == -1) {
		error = OBEXGEN_GetNoteInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->NoteCap.IEL == 0x8 || Priv->NoteCap.IEL == 0x10) {
		return OBEXGEN_GetNoteLUID(s, Entry);
	} else if (Priv->NoteCap.IEL == 0x4) {
		return OBEXGEN_GetNoteIndex(s, Entry);
	} else if (Priv->NoteCap.IEL == 0x2) {
		return OBEXGEN_GetNoteFull(s, Entry);
	} else {
		smprintf(s, "Can not read note from IEL 1 phone\n");
		return ERR_NOTSUPPORTED;
	}
}

GSM_Error OBEXGEN_GetNextNote(GSM_StateMachine *s, GSM_NoteEntry *Entry, gboolean start)
{
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_Error 	error = ERR_EMPTY;;

	/* Get  location */
	if (start) {
		Entry->Location = 1;
		Priv->ReadPhonebook = 0;
	} else {
		Entry->Location++;
	}

	/* Do real getting */
	while (error == ERR_EMPTY) {

		/* Have we read them all? */
		/* Needs to be inside loop as we get count after
		 * first invocation of get function */
		if (Priv->ReadPhonebook == Priv->NoteCount) {
			return ERR_EMPTY;
		}

		error = OBEXGEN_GetNote(s, Entry);
		if (error == ERR_NONE) {
			Priv->ReadPhonebook++;
		} else if (error == ERR_EMPTY) {
			Entry->Location++;
		}
	}
	return error;
}

GSM_Error OBEXGEN_AddNote(GSM_StateMachine *s, GSM_NoteEntry *Entry)
{
	unsigned char 		req[5000];
	char			path[100];
	size_t			size=0;
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->NoteCap.IEL == -1) {
		error = OBEXGEN_GetNoteInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Encode vNote */
	error = GSM_EncodeVNTFile(req, sizeof(req), &size, Entry);
	if (error != ERR_NONE) return error;

	/* Use correct function according to supported IEL */
	if (Priv->NoteCap.IEL == 0x8 || Priv->NoteCap.IEL == 0x10) {
		/* We need to grab LUID list now in order to keep position later */
		error = OBEXGEN_InitNoteLUID(s);
		if (error != ERR_NONE) return error;

		smprintf(s,"Adding note entry %ld:\n%s\n", (long)size, req);
		Priv->UpdateNoteLUID = TRUE;
		error = OBEXGEN_SetFile(s, "telecom/nt/luid/.vnt", req, size, FALSE);
		Entry->Location = Priv->NoteLUIDCount;
		if (error == ERR_NONE) Priv->NoteCount++;
		return error;
	} else if (Priv->NoteCap.IEL == 0x4) {
		/* We need to grab LUID/Index list now in order to keep position later */
		error = OBEXGEN_InitNoteLUID(s);
		if (error != ERR_NONE) return error;

		Entry->Location = OBEXGEN_GetFirstFreeLocation(&Priv->NoteIndex, &Priv->NoteIndexCount);
		smprintf(s,"Adding note entry %ld at location %d:\n%s\n", (long)size, Entry->Location, req);
		sprintf(path, "telecom/nt/%d.vcf", Entry->Location);
		error = OBEXGEN_SetFile(s, path, req, size, FALSE);
		if (error == ERR_NONE) Priv->NoteCount++;
		return error;
	} else {
		/* I don't know add command for other levels, just plain send vCard */
		Entry->Location = 0;
		smprintf(s,"Sending note entry\n");
		return OBEXGEN_SetFile(s, "gammu.vnt", req, size, FALSE);
	}
}

GSM_Error OBEXGEN_SetNoteLUID(GSM_StateMachine *s, GSM_NoteEntry *Entry, const char *Data, int Size)
{
	GSM_Error 	error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	char		*path=NULL;

	error = OBEXGEN_InitNoteLUID(s);
	if (error != ERR_NONE) return error;

	/* Check bounds */
	if (Entry->Location > Priv->NoteLUIDCount ||
			Priv->NoteLUID[Entry->Location] == NULL) {
		/**
		 * \todo We should keep location here!
		 */
		return OBEXGEN_AddNote(s, Entry);
	}

	/* Calculate path */
	path = (char *)malloc(strlen(Priv->NoteLUID[Entry->Location]) + 22); /* Length of string below */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/nt/luid/%s.vnt", Priv->NoteLUID[Entry->Location]);
	smprintf(s, "Seting vNote %s\n", path);

	/* Forget entry if we're deleting */
	if (Size == 0) {
		free(Priv->NoteLUID[Entry->Location]);
		Priv->NoteLUID[Entry->Location] = NULL;
		Priv->NoteCount--;
	}

	/* Store vCard */
	error = OBEXGEN_SetFile(s, path, Data, Size, Size == 0 ? Priv->NoteCap.HD : FALSE);;
	free(path);
	return error;
}

GSM_Error OBEXGEN_SetNoteIndex(GSM_StateMachine *s, GSM_NoteEntry *Entry, const char *Data, int Size)
{
	char		*path=NULL;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_Error		error;

	/* Forget entry if we're deleting */
	if (Size == 0) {
		Priv->NoteCount--;
	}

	/* Calculate path */
	path = (char *)malloc(20 + 22); /* Length of string below + length of number */
	if (path == NULL) {
		return ERR_MOREMEMORY;
	}
	sprintf(path, "telecom/nt/%d.vnt", Entry->Location);
	smprintf(s, "Seting vNote %s\n", path);

	/* Store vCard */
	error = OBEXGEN_SetFile(s, path, Data, Size, FALSE);;
	free(path);
	return error;
}

GSM_Error OBEXGEN_SetNote(GSM_StateMachine *s, GSM_NoteEntry *Entry)
{
	unsigned char 		req[5000];
	size_t			size=0;
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->NoteCap.IEL == -1) {
		error = OBEXGEN_GetNoteInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Encode vNote */
	error = GSM_EncodeVNTFile(req, sizeof(req), &size, Entry);
	if (error != ERR_NONE) return error;

	/* Use correct function according to supported IEL */
	if (Priv->NoteCap.IEL == 0x8 || Priv->NoteCap.IEL == 0x10) {
		return OBEXGEN_SetNoteLUID(s, Entry, req, size);
	} else if (Priv->NoteCap.IEL == 0x4) {
		return OBEXGEN_SetNoteIndex(s, Entry, req, size);
	} else if (Priv->NoteCap.IEL == 0x2) {
		/* Work on full note */
		return ERR_NOTIMPLEMENTED;
	} else {
		return ERR_NOTSUPPORTED;
	}
}

GSM_Error OBEXGEN_DeleteNote(GSM_StateMachine *s, GSM_NoteEntry *Entry)
{
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need IEL to correctly talk to phone */
	if (Priv->NoteCap.IEL == -1) {
		error = OBEXGEN_GetNoteInformation(s, NULL, NULL);
		if (error != ERR_NONE) return error;
	}

	/* Use correct function according to supported IEL */
	if (Priv->NoteCap.IEL == 0x8 || Priv->NoteCap.IEL == 0x10) {
		return OBEXGEN_SetNoteLUID(s, Entry, "", 0);
	} else if (Priv->NoteCap.IEL == 0x4) {
		return OBEXGEN_SetNoteIndex(s, Entry, "", 0);
	} else if (Priv->NoteCap.IEL == 0x2) {
		/* Work on full note */
		return ERR_NOTIMPLEMENTED;
	} else {
		return ERR_NOTSUPPORTED;
	}
}

GSM_Error OBEXGEN_DeleteAllNotes(GSM_StateMachine *s)
{
	GSM_Error		error;
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;
	GSM_NoteEntry		entry;

	/* We need IrMC service for this */
	error = OBEXGEN_Connect(s, OBEX_IRMC);
	if (error != ERR_NONE) return error;

	/* We need count of entries */
	error = OBEXGEN_InitNoteLUID(s);
	if (error != ERR_NONE) return error;

	/* Delete all entries */
	entry.Location = 1;
	while (Priv->NoteCount > 0) {
		error = OBEXGEN_DeleteNote(s, &entry);
		if (error != ERR_NONE && error != ERR_EMPTY) return error;
		entry.Location++;
	}
	return error;
}

/*@}*/

/**
 * \defgroup OBEXcap Phone information using OBEX capability XML or IrMC devinfo
 * \ingroup OBEXPhone
 * @{
 */

GSM_Error OBEXGEN_GetDevinfoField(GSM_StateMachine *s, const char *Name, char *Dest)
{
	char			*pos;
	char			*dest;
	char			match[200];
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	if (Priv->OBEXDevinfo == NULL || strlen(Priv->OBEXDevinfo) == 0) return ERR_NOTSUPPORTED;

	/* Match begin tag */
	match[0] = 0;
	strcat(match, Name);
	strcat(match, ":");

	pos = strstr(Priv->OBEXDevinfo, match);
	if (pos == NULL) return ERR_INVALIDDATA;
	pos += strlen(match);

	/* Copy to end of line */
	dest = Dest;
	while (*pos != 0 && *pos != '\r' && *pos != '\n') {
		*(dest++) = *(pos++);
	}
	*dest  = 0;

	return ERR_NONE;
}

GSM_Error OBEXGEN_GetCapabilityField(GSM_StateMachine *s, const char *Name, char *Dest)
{
	char			*pos_start;
	char			*pos_end;
	char			match[200];
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	if (Priv->OBEXCapability == NULL || strlen(Priv->OBEXCapability) == 0) return ERR_NOTSUPPORTED;

	/* Match XML begin tag */
	match[0] = 0;
	strcat(match, "<");
	strcat(match, Name);
	strcat(match, ">");

	pos_start = strstr(Priv->OBEXCapability, match);
	if (pos_start == NULL) return ERR_INVALIDDATA;
	pos_start += strlen(match);

	/* Match XML end tag */
	match[0] = 0;
	strcat(match, "</");
	strcat(match, Name);
	strcat(match, ">");

	pos_end = strstr(pos_start, match);
	if (pos_end == NULL) return ERR_INVALIDDATA;

	/* Copy result string */
	strncpy(Dest, pos_start, pos_end - pos_start);
	Dest[pos_end - pos_start] = 0;

	return ERR_NONE;
}

GSM_Error OBEXGEN_GetCapabilityFieldAttrib(GSM_StateMachine *s, const char *Name, const char *Attrib, char *Dest)
{
	char			*pos_start;
	char			*pos_end;
	char			match[200];
	GSM_Phone_OBEXGENData	*Priv = &s->Phone.Data.Priv.OBEXGEN;

	if (Priv->OBEXCapability == NULL || strlen(Priv->OBEXCapability) == 0) return ERR_NOTSUPPORTED;

	/* Match XML begin tag */
	match[0] = 0;
	strcat(match, "<");
	strcat(match, Name);

	pos_start = strstr(Priv->OBEXCapability, match);
	if (pos_start == NULL) return ERR_INVALIDDATA;
	pos_start += strlen(match);

	/* Match attribute begin */
	match[0] = 0;
	strcat(match, Attrib);
	strcat(match, "=\"");

	pos_start = strstr(pos_start, match);
	if (pos_start == NULL) return ERR_INVALIDDATA;
	pos_start += strlen(match);

	/* Match end quote */
	pos_end = strchr(pos_start, '"');
	if (pos_end == NULL) return ERR_INVALIDDATA;

	/* Copy result string */
	strncpy(Dest, pos_start, pos_end - pos_start);
	Dest[pos_end - pos_start] = 0;

	return ERR_NONE;
}

GSM_Error OBEXGEN_GetManufacturer(GSM_StateMachine *s)
{
	GSM_Error		error;

	if (s->Phone.Data.Manufacturer[0] != 0) return ERR_NONE;

	error = OBEXGEN_GetCapabilityField(s, "Manufacturer", s->Phone.Data.Manufacturer);
	if (error == ERR_NONE) return ERR_NONE;

	return OBEXGEN_GetDevinfoField(s, "MANU", s->Phone.Data.Manufacturer);
}

GSM_Error OBEXGEN_GetModel(GSM_StateMachine *s)
{
	GSM_Error		error;
	GSM_Phone_Data		*Data = &s->Phone.Data;

	if (Data->Model[0] != 0) return ERR_NONE;

	error = OBEXGEN_GetCapabilityField(s, "Model", s->Phone.Data.Model);

	/* Retry with MOD if we failed */
	if (error != ERR_NONE) {
		error = OBEXGEN_GetDevinfoField(s, "MOD", s->Phone.Data.Model);
	}

	if (error == ERR_NONE) {
		Data->ModelInfo = GetModelData(s, NULL, Data->Model, NULL);
		if (Data->ModelInfo->number[0] == 0)
			Data->ModelInfo = GetModelData(s, NULL, NULL, Data->Model);
		if (Data->ModelInfo->number[0] == 0)
			Data->ModelInfo = GetModelData(s, Data->Model, NULL, NULL);
		return ERR_NONE;
	}
	return error;
}

GSM_Error OBEXGEN_GetFirmware(GSM_StateMachine *s)
{
	GSM_Error		error;

	if (s->Phone.Data.Version[0] != 0) return ERR_NONE;

	error = OBEXGEN_GetCapabilityFieldAttrib(s, "SW", "Version", s->Phone.Data.Version);
	if (error == ERR_NONE) {
		/* We don't care about error here, it is optional */
		OBEXGEN_GetCapabilityFieldAttrib(s, "SW", "Date", s->Phone.Data.VerDate);
	}
	if (error == ERR_NONE) return ERR_NONE;
	OBEXGEN_GetDevinfoField(s, "SW-DATE", s->Phone.Data.VerDate);

	return OBEXGEN_GetDevinfoField(s, "SW-VERSION", s->Phone.Data.Version);
}

GSM_Error OBEXGEN_GetIMEI(GSM_StateMachine *s)
{
	GSM_Error		error;

	if (s->Phone.Data.IMEI[0] != 0) return ERR_NONE;

	error = OBEXGEN_GetCapabilityField(s, "SN", s->Phone.Data.IMEI);
	if (error == ERR_NONE) return ERR_NONE;

	return OBEXGEN_GetDevinfoField(s, "SN", s->Phone.Data.IMEI);
}

/*@}*/
GSM_Reply_Function OBEXGENReplyFunctions[] = {
	/* CONTINUE block */
	{OBEXGEN_ReplyAddFilePart,	"\x90",0x00,0x00,ID_AddFile			},
	{OBEXGEN_ReplyGetFilePart,	"\x90",0x00,0x00,ID_GetFile			},

	/* OK block */
	{OBEXGEN_ReplyChangePath,	"\xA0",0x00,0x00,ID_SetPath			},
	{OBEXGEN_ReplyConnect,		"\xA0",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyAddFilePart,	"\xA0",0x00,0x00,ID_AddFile			},
	{OBEXGEN_ReplyGetFilePart,	"\xA0",0x00,0x00,ID_GetFile			},

	/* FOLDER CREATED block */
	{OBEXGEN_ReplyChangePath,	"\xA1",0x00,0x00,ID_SetPath			},

	/* NOT UNDERSTAND block */
	{OBEXGEN_ReplyConnect,		"\xC0",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyGetFilePart,	"\xC0",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyAddFilePart,	"\xC0",0x00,0x00,ID_AddFile			},

	/* Not allowed block */
	{OBEXGEN_ReplyConnect,		"\xC1",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyGetFilePart,	"\xC1",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyAddFilePart,	"\xC1",0x00,0x00,ID_AddFile			},

	/* FORBIDDEN block */
	{OBEXGEN_ReplyConnect,		"\xC3",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyChangePath,	"\xC3",0x00,0x00,ID_SetPath			},
	{OBEXGEN_ReplyGetFilePart,	"\xC3",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyAddFilePart,	"\xC3",0x00,0x00,ID_AddFile			},

	/* Not Acceptable block */
	{OBEXGEN_ReplyConnect,		"\xC6",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyChangePath,	"\xC6",0x00,0x00,ID_SetPath			},
	{OBEXGEN_ReplyGetFilePart,	"\xC6",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyAddFilePart,	"\xC6",0x00,0x00,ID_AddFile			},

	/* CONFLICT block */
	{OBEXGEN_ReplyConnect,		"\xC9",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyChangePath,	"\xC9",0x00,0x00,ID_SetPath			},
	{OBEXGEN_ReplyGetFilePart,	"\xC9",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyAddFilePart,	"\xC9",0x00,0x00,ID_AddFile			},

	/* NOT FOUND block */
	{OBEXGEN_ReplyConnect,		"\xC4",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyChangePath,	"\xC4",0x00,0x00,ID_SetPath			},
	{OBEXGEN_ReplyGetFilePart,	"\xC4",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyChangePath,	"\xC4",0x00,0x00,ID_SetPath			},

	/* Failed block */
	{OBEXGEN_ReplyConnect,		"\xCC",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyChangePath,	"\xCC",0x00,0x00,ID_SetPath			},
	{OBEXGEN_ReplyGetFilePart,	"\xCC",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyAddFilePart,	"\xCC",0x00,0x00,ID_AddFile			},

	/* Non standard Sharp GX reply */
	{OBEXGEN_ReplyGetFilePart,	"\x80",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyChangePath,	"\x80",0x00,0x00,ID_SetPath			},

	/* Internal server error */
	{OBEXGEN_ReplyConnect,		"\xD0",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyChangePath,	"\xD0",0x00,0x00,ID_SetPath			},
	{OBEXGEN_ReplyGetFilePart,	"\xD0",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyAddFilePart,	"\xD0",0x00,0x00,ID_AddFile			},

	/* Not implemented */
	{OBEXGEN_ReplyConnect,		"\xD1",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyChangePath,	"\xD1",0x00,0x00,ID_SetPath			},
	{OBEXGEN_ReplyGetFilePart,	"\xD1",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyAddFilePart,	"\xD1",0x00,0x00,ID_AddFile			},

	/* Service not available */
	{OBEXGEN_ReplyConnect,		"\xD3",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyChangePath,	"\xD3",0x00,0x00,ID_SetPath			},
	{OBEXGEN_ReplyGetFilePart,	"\xD3",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyAddFilePart,	"\xD3",0x00,0x00,ID_AddFile			},

	/* Database full */
	{OBEXGEN_ReplyConnect,		"\xE0",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyChangePath,	"\xE0",0x00,0x00,ID_SetPath			},
	{OBEXGEN_ReplyGetFilePart,	"\xE0",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyAddFilePart,	"\xE0",0x00,0x00,ID_AddFile			},

	/* Database locked */
	{OBEXGEN_ReplyConnect,		"\xE1",0x00,0x00,ID_Initialise			},
	{OBEXGEN_ReplyChangePath,	"\xE1",0x00,0x00,ID_SetPath			},
	{OBEXGEN_ReplyGetFilePart,	"\xE1",0x00,0x00,ID_GetFile			},
	{OBEXGEN_ReplyAddFilePart,	"\xE1",0x00,0x00,ID_AddFile			},

	{NULL,				"\x00",0x00,0x00,ID_None			}
};

GSM_Phone_Functions OBEXGENPhone = {
	"obex|seobex|obexfs|obexirmc|obexnone|mobex",
	OBEXGENReplyFunctions,
	NOTSUPPORTED,			/* 	Install			*/
	OBEXGEN_Initialise,
	OBEXGEN_Terminate,
	GSM_DispatchMessage,
	NOTIMPLEMENTED,			/* 	ShowStartInfo		*/
	OBEXGEN_GetManufacturer,
	OBEXGEN_GetModel,
	OBEXGEN_GetFirmware,
	OBEXGEN_GetIMEI,
	NOTIMPLEMENTED,			/*	GetOriginalIMEI		*/
	NOTIMPLEMENTED,			/*	GetManufactureMonth	*/
	NOTIMPLEMENTED,			/*	GetProductCode		*/
	NOTIMPLEMENTED,			/*	GetHardware		*/
	NOTIMPLEMENTED,			/*	GetPPM			*/
	NOTIMPLEMENTED,			/*	GetSIMIMSI		*/
	NOTIMPLEMENTED,			/*	GetDateTime		*/
	NOTIMPLEMENTED,			/*	SetDateTime		*/
	NOTIMPLEMENTED,			/*	GetAlarm		*/
	NOTIMPLEMENTED,			/*	SetAlarm		*/
	NOTSUPPORTED,			/* 	GetLocale		*/
	NOTSUPPORTED,			/* 	SetLocale		*/
	NOTIMPLEMENTED,			/*	PressKey		*/
	NOTIMPLEMENTED,			/*	Reset			*/
	NOTIMPLEMENTED,			/*	ResetPhoneSettings	*/
	NOTIMPLEMENTED,			/*	EnterSecurityCode	*/
	NOTIMPLEMENTED,			/*	GetSecurityStatus	*/
	NOTIMPLEMENTED,			/*	GetDisplayStatus	*/
	NOTIMPLEMENTED,			/*	SetAutoNetworkLogin	*/
	NOTIMPLEMENTED,			/*	GetBatteryCharge	*/
	NOTIMPLEMENTED,			/*	GetSignalQuality	*/
	NOTIMPLEMENTED,			/*	GetNetworkInfo		*/
	NOTIMPLEMENTED,     		/*  	GetCategory 		*/
 	NOTSUPPORTED,       		/*  	AddCategory 		*/
        NOTIMPLEMENTED,      		/*  	GetCategoryStatus 	*/
	OBEXGEN_GetMemoryStatus,
	OBEXGEN_GetMemory,
	OBEXGEN_GetNextMemory,
	OBEXGEN_SetMemory,
	OBEXGEN_AddMemory,
	OBEXGEN_DeleteMemory,
	OBEXGEN_DeleteAllMemory,
	NOTIMPLEMENTED,			/*	GetSpeedDial		*/
	NOTIMPLEMENTED,			/*	SetSpeedDial		*/
	NOTIMPLEMENTED,			/*	GetSMSC			*/
	NOTIMPLEMENTED,			/*	SetSMSC			*/
	NOTIMPLEMENTED,			/*	GetSMSStatus		*/
	NOTIMPLEMENTED,			/*	GetSMS			*/
	NOTIMPLEMENTED,			/*	GetNextSMS		*/
	NOTIMPLEMENTED,			/*	SetSMS			*/
	NOTIMPLEMENTED,			/*	AddSMS			*/
	NOTIMPLEMENTED,			/* 	DeleteSMS 		*/
	NOTIMPLEMENTED,			/*	SendSMSMessage		*/
	NOTSUPPORTED,			/*	SendSavedSMS		*/
	NOTSUPPORTED,			/*	SetFastSMSSending	*/
	NOTIMPLEMENTED,			/*	SetIncomingSMS		*/
	NOTIMPLEMENTED,			/* 	SetIncomingCB		*/
	NOTIMPLEMENTED,			/*	GetSMSFolders		*/
 	NOTIMPLEMENTED,			/* 	AddSMSFolder		*/
 	NOTIMPLEMENTED,			/* 	DeleteSMSFolder		*/
	NOTIMPLEMENTED,			/*	DialVoice		*/
        NOTIMPLEMENTED,			/*	DialService		*/
	NOTIMPLEMENTED,			/*	AnswerCall		*/
	NOTIMPLEMENTED,			/*	CancelCall		*/
 	NOTIMPLEMENTED,			/* 	HoldCall 		*/
 	NOTIMPLEMENTED,			/* 	UnholdCall 		*/
 	NOTIMPLEMENTED,			/* 	ConferenceCall 		*/
 	NOTIMPLEMENTED,			/* 	SplitCall		*/
 	NOTIMPLEMENTED,			/* 	TransferCall		*/
 	NOTIMPLEMENTED,			/* 	SwitchCall		*/
 	NOTIMPLEMENTED,			/* 	GetCallDivert		*/
 	NOTIMPLEMENTED,			/* 	SetCallDivert		*/
 	NOTIMPLEMENTED,			/* 	CancelAllDiverts	*/
	NOTIMPLEMENTED,			/*	SetIncomingCall		*/
	NOTIMPLEMENTED,			/*  	SetIncomingUSSD		*/
	NOTIMPLEMENTED,			/*	SendDTMF		*/
	NOTIMPLEMENTED,			/*	GetRingtone		*/
	NOTIMPLEMENTED,			/*	SetRingtone		*/
	NOTIMPLEMENTED,			/*	GetRingtonesInfo	*/
	NOTIMPLEMENTED,			/* 	DeleteUserRingtones	*/
	NOTIMPLEMENTED,			/*	PlayTone		*/
	NOTIMPLEMENTED,			/*	GetWAPBookmark		*/
	NOTIMPLEMENTED,			/* 	SetWAPBookmark 		*/
	NOTIMPLEMENTED, 		/* 	DeleteWAPBookmark 	*/
	NOTIMPLEMENTED,			/* 	GetWAPSettings 		*/
	NOTIMPLEMENTED,			/* 	SetWAPSettings 		*/
	NOTSUPPORTED,			/*	GetSyncMLSettings	*/
	NOTSUPPORTED,			/*	SetSyncMLSettings	*/
	NOTSUPPORTED,			/*	GetChatSettings		*/
	NOTSUPPORTED,			/*	SetChatSettings		*/
	NOTSUPPORTED,			/* 	GetMMSSettings		*/
	NOTSUPPORTED,			/* 	SetMMSSettings		*/
	NOTSUPPORTED,			/*	GetMMSFolders		*/
	NOTSUPPORTED,			/*	GetNextMMSFileInfo	*/
	NOTIMPLEMENTED,			/*	GetBitmap		*/
	NOTIMPLEMENTED,			/*	SetBitmap		*/
	OBEXGEN_GetTodoStatus,
	OBEXGEN_GetTodo,
	OBEXGEN_GetNextTodo,
	OBEXGEN_SetTodo,
	OBEXGEN_AddTodo,
	OBEXGEN_DeleteTodo,
	OBEXGEN_DeleteAllTodo,
	OBEXGEN_GetCalendarStatus,
	OBEXGEN_GetCalendar,
    	OBEXGEN_GetNextCalendar,
	OBEXGEN_SetCalendar,
	OBEXGEN_AddCalendar,
	OBEXGEN_DeleteCalendar,
	OBEXGEN_DeleteAllCalendar,
	NOTSUPPORTED,			/* 	GetCalendarSettings	*/
	NOTSUPPORTED,			/* 	SetCalendarSettings	*/
	OBEXGEN_GetNoteStatus,
	OBEXGEN_GetNote,
	OBEXGEN_GetNextNote,
	OBEXGEN_SetNote,
	OBEXGEN_AddNote,
	OBEXGEN_DeleteNote,
	OBEXGEN_DeleteAllNotes,
	NOTIMPLEMENTED, 		/*	GetProfile		*/
	NOTIMPLEMENTED, 		/*	SetProfile		*/
    	NOTIMPLEMENTED,			/*  	GetFMStation        	*/
    	NOTIMPLEMENTED,			/*  	SetFMStation        	*/
    	NOTIMPLEMENTED,			/*  	ClearFMStations       	*/
	OBEXGEN_GetNextFileFolder,
	NOTIMPLEMENTED,			/*	GetFolderListing	*/
	NOTSUPPORTED,			/*	GetNextRootFolder	*/
	NOTSUPPORTED,			/*	SetFileAttributes	*/
	OBEXGEN_GetFilePart,
	OBEXGEN_AddFilePart,
	OBEXGEN_SendFilePart,
	NOTIMPLEMENTED, 		/* 	GetFileSystemStatus	*/
	OBEXGEN_DeleteFile,
	OBEXGEN_AddFolder,
	OBEXGEN_DeleteFile,		/* 	DeleteFolder		*/
	NOTSUPPORTED,			/* 	GetGPRSAccessPoint	*/
	NOTSUPPORTED,			/* 	SetGPRSAccessPoint	*/
	NOTSUPPORTED,			/* 	GetScreenshot		*/
	NOTSUPPORTED,			/* 	SetPower		*/
	NOTSUPPORTED,			/* 	PostConnect	*/
	NONEFUNCTION			/*	PreAPICall		*/
};

#endif
/*@}*/
/*@}*/

/* How should editor hadle tabs in this file? Add editor commands here.
 * vim: noexpandtab sw=8 ts=8 sts=8:
 */
