[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-pim
Subject:    Re: [Kde-pim] libkcal Segfault Proglems!
From:       Andrew De Ponte <cyphactor () socal ! rr ! com>
Date:       2005-01-10 18:34:39
Message-ID: 41E2CABF.1000002 () socal ! rr ! com
[Download RAW message or body]

Tobias Koenig wrote:

>Could you send us the part of code you are talking about, please?
>  
>
Sure can. I have attached the code that I thought was some way helpful 
in grasping what was going on. Here is a little description of each file.

ZaurusType.h - the specifications file for the ZaurusType class. This 
class is designed to basically obfuscate the Zaurus Synchronization 
Protocol from the client application.

ZaurusType.cc - the implementation file for the ZaurusType class. This 
file contains all  the code for the ZaurusType class including the three 
functions that get called when it makes it segfault.

TodoPluginType.hh - This is a virtual class specification for a plugin 
that will allow me to synchronize To-Do information with the Zaurus.

KOrgTodoPlugin.hh - This is the specifications file for a plugin which 
should allow synchronization with KOrganizer's To-Do list.

KOrgTodoPlugin.cc - This is the implementation file for the plugin. This 
contains the function which KCal::Todo::List.begin() is segfaulting in.

zync.cc - This is the client application for the above. It ties it all 
together by loading the shared object produced by KOrgTodoPlugin.hh and 
KOrgTodoPlugin.cc and using the ZaurusType class to performe a 
synchronization with the Zaurus.

>>This really does not make any sense to me. This does not happen if it is 
>>above three function calls. Those three function calls have nothing to 
>>do with the libkcal in anyway shape or form.
>>    
>>
>Really sure? ;)
>  
>
Well, to my knowledge they don't :). I am sure that there is some amount 
of possability that I could be mistaken.

Now, that you have the code let me really explain the problem. In the 
zync.cc starting on line 670 is the PerformTodoSync() function. This 
function is a function which is called by the child process after the 
fork() (it is the only function called in the child process). This 
function is what performs the synchronization.

Now, there are two types of syncs "Normal Sync" which works fine, and 
"Full Sync" which is the case that causes the segfault. "Full Sync" is 
done when the device being synchronized has never been synced with the 
desktop software before, this is used to create in initial sync state 
(hence, all data is exchanged between devices). "Normal Sync" is used 
after a sync state has been obtained via a "Full Sync", it just 
exchanges the data between the devices which needs changing. As you can 
see I check for if a "Full Sync" is required with the ZaurusType member 
function RequiresFullSync() on line 813.

Currently as you can see I have it setup to return 0 if it hits that 
point because the segfault will happen after this part has been 
performed. At least this way the user of the tool can sync even if it is 
a broken two step process. Anyways so normally the code on lines 821 to 
826 would not be their as you can see by the comment above it.

Following this path of execution down the file you will see the next 
thing I do is "Obtain the Changes from the Zaurus", this works fine. 
After that I display the number and type of the changes. This also works 
fine. Then on line 848 I check to see if a "Full Sync" is required 
again. If it is since we are continuing the same path of execution you 
would notice that I then call pTodoPlugin->GetAllTodoItems(). This is 
the function in the KOrgTodoPlugin.cc which the segfault happens in. It 
starts on line 182 of KOrgTodoPlugin.cc. If you follow the code you will 
see that on line 195 of that file I call a pointless 
kcalTodoList.begin(), this is because I have narrowed the segfualt down 
to this call. I also tested to make sure it was not the assignment 
operator. So it is the actual begin() call.

Now, the strange thing is that when zync.cc is in "Normal Sync" all of 
the stuff works fine. It just does not make any sense to me. Give me any 
ideas you can.

Thanks,
    Andrew De Ponte

["ZaurusType.hh" (text/plain)]

/*
 * Copyright 2004 Andrew De Ponte
 * 
 * This file is part of zsrep.
 * 
 * zsrep is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 * 
 * zsrep is distributed in the hopes that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with zsrep; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

/**
 * @file ZaurusType.hh
 * @brief A specifications file for an object representing a Zaurus.
 * @author Andrew De Ponte
 *
 * A specifications file for a class existing to represent a Zaurus. This
 * data type was created to provide a common interface that would obfuscate
 * the Zaurus Synchronization Protocol Implementation.
 */

#ifndef ZAURUSTYPE_H
#define ZAURUSTYPE_H

// Include the common data format for Todos from the zdata library.
#include <zdata_lib/TodoItemType.hh>

#include <time.h>

// Memory Comparison, Settings, etc. Includes
#include <string>

// Network Related Includes
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

// Error Handeling Includes
#include <errno.h>

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

#include <zmsg.h>

#include <iostream>

// The zaurus syncing softwares receiving port.
#define ZRECVPORT 4245

/**
 * @class ZaurusType
 * @brief A type representing a Zaurus.
 *
 * The ZaurusType is a class which represents a Zaurus. It is designed to
 * provide a common interface that obfuscates the Zaurus Synchronization
 * Protocol Implementation. It does not completely obfuscate the protocol due
 * to the fact that order of function calls matters.
 */
class ZaurusType {
public:
    ZaurusType(void);
    ~ZaurusType(void);

    int ListenConnection(void);
    void SetSyncType(const unsigned char type);
    bool RequiresPassword(void);
    int AuthenticatePassword(std::string passwd);
    time_t GetLastTimeSynced(void);
    int GetAllTodoSyncItems(TodoItemType::List &newItemList,
			    TodoItemType::List &modItemList,
			    SyncIDListType &delItemIdList);
    TodoItemType::List AddTodoItems(TodoItemType::List todoItems);
    int ModTodoItems(TodoItemType::List todoItems);
    int DelTodoItems(SyncIDListType todoItemIDs);
    bool RequiresFullSync(void) const;
    void TerminateSync(void);
    int FinishSync(void);
    int ResetSyncLog(void);
    int SendRSSMsg(void);
    int SetNextSyncAnch(void);

private:
    int InitiateSync(void);
    int ObtainDeviceInfo(void);
    int ObtainSyncLog(const unsigned char syncType);
    int ObtainLastSyncAnch(void);

    int ObtainSyncIDLists(const unsigned char type);
    TodoItemType GetTodoItem(unsigned char type, unsigned long int syncID);
    int DeleteTodoItem(unsigned char type, unsigned long int syncID);
    int ModifyTodoItem(unsigned char type, TodoItemType todoItem);
    TodoItemType AddTodoItem(unsigned char type, TodoItemType todoItem);
    int StateSyncDone(const unsigned char type);

    time_t ConvZDateTime(unsigned char *data, unsigned short int len);
    int ConvCalTime(time_t calTime, unsigned char *dest,
		    unsigned short int len);

    // These variables are used to store the Device Information.
    std::string model;
    std::string language;
    unsigned char authState;

    SyncIDListType newSyncIDList;
    SyncIDListType modSyncIDList;
    SyncIDListType delSyncIDList;
    bool obtainedSyncIDLists;

    // This variable is used to store the last time synced anchor after it has
    // been obtained from the Zaurus.
    time_t lastTimeSynced;

    bool reqFullSyncFlag;

    // This is a variable used to keep track of the socket/file descriptor for
    // the connection to the Zaurus.
    int connfd;

    // This variable is used to store the type of synchronization. This
    // variable should be set before any of the protocol functions are called.
    unsigned char syncType;
};

#endif

["ZaurusType.cc" (text/x-c++)]

/*
 * Copyright 2004 Andrew De Ponte
 * 
 * This file is part of zsrep.
 * 
 * zsrep is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 * 
 * zsrep is distributed in the hopes that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with zsrep; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

/**
 * @file ZaurusType.cc
 * @brief An implimentation file for the ZaurusType class.
 *
 * This type was developed to provide an object to represent a Zaurus. It is
 * designed to provide a common interface that obfuscates the Zaurus
 * Synchronization Protocol Implementation. 
 */

#include "ZaurusType.hh"

/**
 * Construct a default Zaurus object.
 *
 * Construct a default Zaurus object with all the basic initialization.
 */
ZaurusType::ZaurusType(void) {
    // Initialize the obtainedSyncIDLists flag to a state of false.
    obtainedSyncIDLists = false;

    // Initialize the requires full sync flag to a state of full sync not
    // required.
    reqFullSyncFlag = false;

    // Set the synchronization type to To-Do for default since it is only one
    // that is implimented at this point.
    syncType = 0x06;
}

/**
 * Destruct the ZaurusType object.
 *
 * Destruct the ZaurusType object by deallocating any dynamically allocated
 * memory.
 */
ZaurusType::~ZaurusType(void) {

}

/**
 * Listen for incoming synchronization connections.
 *
 * Create a new socket, bind it for listening for connections for
 * synchronization (probably from a Zaurus), the wait for a connection.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully accepted a connection.
 * @retval 1 Failed to create socket.
 * @retval 2 Failed to bind the socket to address.
 * @retval 3 Failed to put socket into listen mode.
 * @retval 4 Failed to accept connection.
 * @retval 5 Failed to convert connecting socket address to a string.
 */
int ZaurusType::ListenConnection(void) {
    int listenfd;
    int reuse_set_flag = 1;
    struct sockaddr_in servaddr;
    struct sockaddr_in clntaddr;
    char source_addr[16];
    socklen_t len;
    int retval;

    // Attepmt to create a socket to be used to allow the Synchronization
    // Client running on the Zaurus to connect to the Desktop Synchronization
    // Server (this object).
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == -1) {
	perror("ListenConnection");
	return 1;
    }

    // Here, I set a socket option so that if the application crashes the
    // socket is not blocked by the TIME_WAIT state.
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse_set_flag,
	       sizeof(reuse_set_flag));

    // Specify the addresses that I am going to allow to listen for
    // connections on. In this case I want to allow connections from any IP
    // address attempting to connect to port 4245 (ZRECVPORT) because I do not
    // want to limit the users configurations for synchoronization.
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(ZRECVPORT);

    // Since I have defined the address (or rather acceptance rules) for the
    // listening socket, I bind the address to the socket.
    retval = bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    if (retval == -1) {
        perror("ListenConnection");
        return 2;
    }

    // Now that the socket is prepared to listen for connections I attempt to
    // put the socket into listening mode. I only want one connection in the
    // queue at a time.
    retval = listen(listenfd, 1);
    if (retval == -1) {
        perror("ListenConnection");
        return 3;
    }

    // Here, I block until a connection is made to the listening socket.
    memset(&clntaddr, 0, sizeof(clntaddr));
    len = sizeof(clntaddr);
    connfd = accept(listenfd, (struct sockaddr *) &clntaddr, &len);
    if (connfd == -1) {
        perror("ListenConnection");
        return 4;
    }

    // Here, I print a message showing the socket that just connected.
    if (inet_ntop(AF_INET, &clntaddr.sin_addr, source_addr, 16) == NULL) {
        perror("ListenConnection");
        return 5;
    }
//    printf("Received connection from %s, port %d.\n", source_addr,
//	   ntohs(clntaddr.sin_port));

    // Return in success.
    return 0;
}

/**
 * Set the type of synchronization.
 *
 * Set the type of synchronization that will be performed. This should be
 * called before any of the protocol functions are called.
 * @param type This is the type of synchronization that will be
 * performed. Acceptable values and their meanings are To-Do: 0x06, Calendar:
 * 0x01, and Address Book: 0x07.
 */
void ZaurusType::SetSyncType(const unsigned char type) {
    syncType = type;
}

/**
 * Determine if a password is required.
 *
 * Determine if the Zaurus requires a password for synchronization.
 * @return A boolean value representing true (yes) or false (no).
 * @retval true The Zaurus does require a password for synchronization.
 * @retval false The Zaurus does NOT require a password for synchronization.
 */
bool ZaurusType::RequiresPassword(void) {
    int retval;

    // The first thing that happends in either a "Normal Sync" or a "Full
    // Sync" is that an RAY message is sent and an AAY message is
    // received. This is just used to start the synchronization process.
    if ((retval = InitiateSync()) != 0) {
	std::cerr << "Failed to Initiate the Sync process (" << retval
		  << ").\n";
	exit(1);
    }
    
    // The second thing that happends in either a "Normal Sync" or a "Full
    // Sync" is that an RIG message is sent and an AIG message is
    // received. This is a request and answer for device information (Model,
    // Language, Authentication State).
    if ((retval = ObtainDeviceInfo()) != 0) {
	std::cerr << "Failed to Obtain Device Info (" << retval << ").\n";
	exit(1);
    }
    

    // Check the value of the Authentication State received with the call to
    // the ObtainDeviceInfo function to see if a password is required.
    if ((authState == 0x0b) || (authState == 0x07))
	return true;
    else
	return false;
}

/**
 * Authenticate the password.
 *
 * Authenticate the given password with the Zaurus.
 * @param passwd The password to send to the Zaurus for authentication.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully authenticated to the Zaurus.
 * @retval 1 Failed to authenticate the password.
 */
int ZaurusType::AuthenticatePassword(std::string passwd) {
    int retval;
    MessageType msg;
    bool authenticated = false;
    int retryCount = 0;

    while ((!authenticated) && (retryCount < 3)) {

	if (RecvRqst(connfd) != 0) {
	    printf("ERROR: Failed to receive a req.\n");
	    exit(12);
	}

	SendRRL(connfd, (char *)passwd.c_str());
	if (RecvAck(connfd) != 0) {
	    printf("ERROR: Failed to receiving an ack for RRL.\n");
	    exit(12);
	}

	SendRqst(connfd);

	// Receive message, success AEX, failure 96 18
	retval = RecvMessage(connfd, &msg);
	if (retval != 0) {
	    if (retval == 8) {
		SendRqst(connfd);

		// Here I recv the ANG message.
		RecvMessage(connfd, &msg);
		SendAck(connfd);

		retryCount++;
	    }
	} else {
	    SendAck(connfd);
	    authenticated = true;
	}
    }

    if (authenticated) {
	return 0;
    } else {
	FinishSync();
	return 1;
    }
}

/**
 * Get the Last Time Synchronized.
 *
 * Obtain the last time synchronized from the Zaurus.
 * @return The last time synchronized as seconds since Epoch.
 */
time_t ZaurusType::GetLastTimeSynced(void) {
    int retval;

    if ((retval = ObtainDeviceInfo()) != 0) {
	std::cerr << "Failed to Obtain Device Info (" << retval << ").\n";
	exit(1);
    }

    if ((retval = ObtainSyncLog(syncType)) != 0) {
	std::cerr << "Failed to Obtain Sync Log (" << retval << ").\n";
	exit(1);
    }

    if ((retval = ObtainLastSyncAnch()) != 0) {
	std::cerr << "Failed to Obtain Last Sync Anchor (" << retval << ").\n";
	exit(1);
    }

    return lastTimeSynced;
}

/**
 * Obtain all the Todo sync items.
 *
 * Obtain all the new, modified, and deleted Todo sync items.
 * @param newItemList A reference to a list to hold the Todo items that are
 * new to the Zaurus. 
 * @param modItemList A reference to a list to hold the Todo items that have
 * been modified from the Zaurus.
 * @param delItemIdList A reference to a list to hold sync IDs of the Todo
 * items that have been deleted from the Zaurus.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully obtained the Todo sync items.
 * @retval 1 Failed to set the next sync anchor.
 */
int ZaurusType::GetAllTodoSyncItems(TodoItemType::List &newItemList,
				    TodoItemType::List &modItemList,
				    SyncIDListType &delItemIdList) {

    SyncIDListType::iterator syncIDIter;
    unsigned long int curSyncID;
    TodoItemType todoItem;
//    int retval;


//    if ((retval = SetNextSyncAnch()) != 0) {
//	return 1;
//    }

    if (!obtainedSyncIDLists)
	ObtainSyncIDLists(syncType);

    std::cout << "OBTAINED SYNC ID LIST.\n";

    // Loop through the newSyncIDList and obtain the data for each of the sync
    // IDs and store the data in the newItemList refrenced list.
    for (syncIDIter = newSyncIDList.begin(); syncIDIter != newSyncIDList.end();
	 syncIDIter++) {
	curSyncID = *(syncIDIter);
	std::cout << "GETTING DATA FOR " << curSyncID << std::endl;

	todoItem = GetTodoItem(syncType, curSyncID);

	std::cout << "GOT DATA FOR " << curSyncID << std::endl;
	newItemList.push_front(todoItem);
    }

    std::cout << "FINISHED LOOPIND NEW ITEMS\n";

    // Loop through the modSyncIDList and obtain the data for each of the sync
    // IDs and store the data in the modItemList referenced list.
    for (syncIDIter = modSyncIDList.begin(); syncIDIter != modSyncIDList.end();
	 syncIDIter++) {
	curSyncID = *(syncIDIter);

	todoItem = GetTodoItem(syncType, curSyncID);
	modItemList.push_front(todoItem);
    }

    // Set the delItemIdList equal to the list of sync ids of the deleted
    // items.
    delItemIdList = delSyncIDList;

    return 0;
}

/**
 * Add the Todo items.
 *
 * Add the Todo items to the Zaurus with the data in the items in the
 * passed list.
 * @param todoItems The list of items to add and their data.
 * @return A list of To-Do items which need their IDs mapped.
 */
TodoItemType::List ZaurusType::AddTodoItems(TodoItemType::List todoItems) {
    std::cout << "Entered the AddTodoItems() function.\n";
    TodoItemType::List::iterator pTodoItem;
    TodoItemType todoItem;
    TodoItemType::List mapIdList;
    std::cout << "Created function scoped variables.\n";

    // Here, I iterate through the list of items that should be added and
    // add them all.
    for (pTodoItem = todoItems.begin(); pTodoItem != todoItems.end();
	 pTodoItem++) {
	std::cout << "Began iteration.\n";
	todoItem = *(pTodoItem);
	std::cout << "Set the Current Todo Item.\n";

	std::cout << "About to add, " << todoItem.GetDescription();
	std::cout << std::endl;

	mapIdList.push_front(AddTodoItem(syncType, todoItem));

	std::cout << "Pushed item ID onto mapIdList.\n";

	std::cout << "Ended iteration.\n";
    }

    return mapIdList;
}

/**
 * Modify the Todo items.
 *
 * Modify the Todo items on the Zaurus with the data in the items in the
 * passed list.
 * @param todoItems The list of items to modify and their data.
 * @return The number of items failed, hence a value of zero is success.
 * @retval 0 Successfully modified all items contained in the passed list.
 */
int ZaurusType::ModTodoItems(TodoItemType::List todoItems) {
    TodoItemType::List::iterator pTodoItem;
    TodoItemType todoItem;

    int retval = 0;

    // Here, I iterate through the list of items that should be modified and
    // modify them all.
    for (pTodoItem = todoItems.begin(); pTodoItem != todoItems.end();
	 pTodoItem++) {
	todoItem = *(pTodoItem);

	if (ModifyTodoItem(syncType, todoItem) != 0)
	    retval++;
    }

    return retval;
}

/**
 * Delete the Todo items.
 *
 * Delete the Todo items that have sync IDs contained in the passed list.
 * @param todoItemIDs The Todo Item IDs of the items to remove.
 * @return The number of items failed, hence a value of zero is success.
 * @retval 0 Successfully removed all items contained in the passed list.
 */
int ZaurusType::DelTodoItems(SyncIDListType todoItemIDs) {
    SyncIDListType::iterator it;
    unsigned char type = 0x06;
    int retval;
    int numDelsFailed = 0;

    for (it = todoItemIDs.begin(); it != todoItemIDs.end(); it++) {
	retval = DeleteTodoItem(type, *(it));
	if (retval != 0) {
	    numDelsFailed++;
	}
    }

    return numDelsFailed;
}

/**
 * Determine if a full sync is required.
 *
 * Determine if the Zaurus requires a full synchronization.
 * @return A boolean value representing true (yes) or false (no).
 * @retval true The Zaurus does require a full synchronization.
 * @retval false The Zaurus does NOT require a full synchronization.
 */
bool ZaurusType::RequiresFullSync(void) const {
    return reqFullSyncFlag;
}

/**
 * Terminates the synchronization.
 *
 * Terminates the synchronization process and closes connection.
 */
void ZaurusType::TerminateSync(void) {
    int retval;

    if ((retval = StateSyncDone(syncType)) != 0) {
	std::cerr << "Failed to State Sync Done (" << retval << ").\n";
	exit(1);
    }


    if ((retval = ObtainDeviceInfo()) != 0) {
	std::cerr << "Failed to Obtain Device Info (" << retval << ").\n";
	exit(1);
    }

    if ((retval = FinishSync()) != 0) {
	std::cerr << "Failed to Finish Sync (" << retval << ").\n";
	exit(1);
    }
}

/////////////////////////////////////////////////////////////////////////////
// Private Member Function Section Lies Below
/////////////////////////////////////////////////////////////////////////////

/**
 * Initiate Synchronization.
 *
 * The InitiateSync function starts the synchronization process. This is the
 * first step in the synchronization process.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully initiated the synchronization process.
 * @retval 2 Failed to receive an ack message.
 * @retval 3 Failed to allocate memory to store AAY message.
 * @retval 4 Failed to receive an AAY message.
 */
int ZaurusType::InitiateSync(void) {
    SendRAY(connfd);

    if (RecvAck(connfd) != 0)
	return 2;

    SendRqst(connfd);

    AAYMessageType *pAAYMsg = new AAYMessageType;
    if (!pAAYMsg)
	return 3;

    if (RecvMessage(connfd, pAAYMsg) != 0) {
	delete pAAYMsg;
	return 4;
    }
    delete pAAYMsg;

    SendAck(connfd);

    return 0;
}

/**
 * Obtain Device Information.
 *
 * The ObtainDeviceInfo function obtains all the device info (Model, Language,
 * Authentication State, etc). The device information is stored within private
 * member variables for later access.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully obtained device info.
 * @retval 1 Failed to receive a request.
 * @retval 2 Failed to receive an ack message.
 * @retval 3 Failed to allocate memory to store AIG message.
 * @retval 4 Failed to receive an AIG message.
 */
int ZaurusType::ObtainDeviceInfo(void) {
    char buff[256];

    if (RecvRqst(connfd) != 0)
	return 1;

    SendRIG(connfd);

    if (RecvAck(connfd) != 0)
	return 2;

    SendRqst(connfd);

    AIGMessageType *pAIGMsg = new AIGMessageType;
    if (!pAIGMsg)
	return 3;

    if (RecvMessage(connfd, pAIGMsg) != 0) {
	delete pAIGMsg;
	return 4;
    }

    // Store the obtained device info.
    pAIGMsg->GetModel(buff, 256);
    model.assign(buff);

    pAIGMsg->GetModel(buff, 256);
    language.assign(buff);

    authState = pAIGMsg->GetAuthState();

    delete pAIGMsg;

    SendAck(connfd);

    return 0;
}

/**
 * Obtain Sync Log.
 *
 * The ObtainSyncLog function obtains the Synchronization Log from the
 * Zaurus. The state of the log is stored within a private member variable for
 * later use.
 * @param syncType The identifier specifying the type of synchronization.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully obtained sync log.
 * @retval 1 Failed to receive a request.
 * @retval 2 Failed to receive an ack message.
 * @retval 3 Failed to allocate memory to store AMG message.
 * @retval 4 Failed to receive an AMG message.
 */ 
int ZaurusType::ObtainSyncLog(const unsigned char syncType) {
    if (RecvRqst(connfd) != 0)
	return 1;

    SendRMG(connfd, syncType);

    if (RecvAck(connfd) != 0)
	return 2;

    SendRqst(connfd);

    AMGMessageType *pAMGMsg = new AMGMessageType;
    if (!pAMGMsg)
	return 3;

    if (RecvMessage(connfd, pAMGMsg) != 0) {
	delete pAMGMsg;
	return 4;
    }

    SendAck(connfd);

    if (pAMGMsg->IsEmptyLog())
	reqFullSyncFlag = true;

    delete pAMGMsg;

    return 0;
}

/**
 * Obtain Last Synchronization Anchor.
 *
 * The ObtainLastSyncAnch function obtains the Last Synchronization Anchor
 * from the Zaurus.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully obtained the last sync anchor.
 * @retval 1 Failed to receive a request.
 * @retval 2 Failed to receive an ack message.
 * @retval 3 Failed to allocate memory to store ATG message.
 * @retval 4 Failed to receive an ATG message.
 */ 
int ZaurusType::ObtainLastSyncAnch(void) {
    char buff[256];
    char tmpBuff[256];
    struct tm tmpTime;

    if (RecvRqst(connfd) != 0)
	return 1;

    SendRTG(connfd);

    if (RecvAck(connfd) != 0)
	return 2;

    SendRqst(connfd);

    ATGMessageType *pATGMsg = new ATGMessageType;
    if (!pATGMsg)
	return 3;

    if(RecvMessage(connfd, pATGMsg) != 0) {
	delete pATGMsg;
	return 4;
    }

    SendAck(connfd);

    pATGMsg->GetTimestamp(buff, 256);

    // Here I should step through the content of the TimeStamp and convert it
    // to integer values using atoi(). Using the converted items I should fill
    // a tm struct and pass it to the mktime() function to produce an
    // equivalent time_t value. At which point I should store it in a private
    // member variable of type time_t named lastTimeSynced.
    // Get and set the year.
    memcpy(tmpBuff, buff, 4);
    tmpBuff[4] = '\0';
    tmpTime.tm_year = (atoi(tmpBuff) - 1900);

    // Get and set the month.
    memcpy(tmpBuff, (buff + 4), 2);
    tmpBuff[2] = '\0';
    tmpTime.tm_mon = (atoi(tmpBuff) - 1);

    // Get and set the day of the month.
    memcpy(tmpBuff, (buff + 4 + 2), 2);
    tmpBuff[2] = '\0';
    tmpTime.tm_mday = (atoi(tmpBuff));

    // Get and set the hour
    memcpy(tmpBuff, (buff + 4 + 2 + 2), 2);
    tmpBuff[2] = '\0';
    tmpTime.tm_hour = (atoi(tmpBuff));

    // Get and set the minutes
    memcpy(tmpBuff, (buff + 4 + 2 + 2 + 2), 2);
    tmpBuff[2] = '\0';
    tmpTime.tm_min = (atoi(tmpBuff));

    // Get and set the seconds
    memcpy(tmpBuff, (buff + 4 + 2 + 2 + 2 + 2), 2);
    tmpBuff[2] = '\0';
    tmpTime.tm_sec = (atoi(tmpBuff));

    // The following are calculated and set by mktime(): tm_wday, tm_yday

    tmpTime.tm_isdst = -1;

    lastTimeSynced = mktime(&tmpTime);

    delete pATGMsg;

    return 0;
}

/**
 * Reset the Synchronization Log.
 *
 * Reset the Synchronization Log to a proper value. This is used to set the
 * log file state so that zync knows if it has been synced before or not. Such
 * is used in the case when a Full Sync is needed.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully reset the synchronization log.
 */
int ZaurusType::ResetSyncLog(void) {
    MessageType msg;
    int retval;

    // First i send an empty RMS message to notify the Zaurus that I am going
    // to reset the sync log.
    if (RecvRqst(connfd) != 0) {
	printf("ERROR: Failed to receive a request.\n");
	exit(12);
    }

    SendEmptyRMS(connfd);

    if (RecvAck(connfd) != 0) {
	printf("ERROR: Failed to receive an RTG ack.\n");
	exit(12);
    }

    SendRqst(connfd);

    if (RecvAbrt(connfd) != 0) {
	printf("ERROR: Failed to received an ABRT.\n");
    }

    SendRqst(connfd);

    retval = RecvMessage(connfd, &msg);
    if (retval != 0) {
	printf("ERROR: Failed to receive ANG message (%d).\n",
	       retval);
    }

    SendAck(connfd);

    // Then I send the Zaurus the new content of the sync log. The Zaurus
    // should take this content and store it for later return when I obtain
    // the synchronization log.
    if (RecvRqst(connfd) != 0) {
	printf("ERROR: Failed to received an RQST.\n");
    }

    SendRMS(connfd);

    if (RecvAck(connfd) != 0) {
	printf("ERROR: Failed to receive an RTG ack.\n");
	exit(12);
    }

    SendRqst(connfd);

    retval = RecvMessage(connfd, &msg);
    if (retval != 0) {
	printf("ERROR: Failed to receive AEX message (%d).\n",
	       retval);
    }

    SendAck(connfd);

    return 0;
}

int ZaurusType::SendRSSMsg(void) {
    int retval;
    MessageType msg;

    if (RecvRqst(connfd) != 0) {
	printf("ERROR: Failed to receive a request.\n");
	exit(12);
    }

    SendRSS(connfd);

    if (RecvAck(connfd) != 0) {
	printf("ERROR: Failed to receive an RTS ack.\n");
	exit(12);
    }

    SendRqst(connfd);
    
    retval = RecvMessage(connfd, &msg);
    if (retval != 0) {
	printf("ERROR: Failed to receive AEX message (%d).\n", retval);
    }

    SendAck(connfd);

    return 0;
}

/**
 * Set the Next Synchronization Anchor.
 *
 * The SetNextSyncAnchor sets the synchronization anchor on the Zaurus.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully set the next sync anchor.
 * @retval 1 Failed to receive a request.
 * @retval 2 Failed to receive an ack message.
 * @retval 4 Failed to receive an AEX message.
 */ 
int ZaurusType::SetNextSyncAnch(void) {
    MessageType msg;

    if (RecvRqst(connfd) != 0)
	return 1;

    SendRTS(connfd);

    if (RecvAck(connfd) != 0)
	return 2;

    SendRqst(connfd);

    if (RecvMessage(connfd, &msg) != 0) {
	return 4;
    }

    SendAck(connfd);

    return 0;
}

/**
 * Finish the synchronization.
 *
 * The FinishSync function finishes the synchronization by doing any garbage
 * clean up and closing the socket.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully set the next sync anchor.
 * @retval 1 Failed to receive a request.
 * @retval 2 Failed to receive an ack message.
 * @retval 4 Failed to receive an AEX message.
 * @retval 5 Failed to receive a second request.
 * @retavl 6 Failed in reading garbage data.
 */ 
int ZaurusType::FinishSync(void) {
    char garbageBuff[256];
    MessageType msg;
    int bytesRead;

    if (RecvRqst(connfd) != 0)
	return 1;

    SendRQT(connfd);

    if (RecvAck(connfd) != 0)
	return 2;

    SendRqst(connfd);

    if (RecvMessage(connfd, &msg) != 0) {
	return 4;
    }

    SendAck(connfd);

    if (RecvRqst(connfd) != 0)
	return 5;

    // In this section I have it perform the appropriate connection
    // termination so that the socket is not left in connection wait state.
    bytesRead = read(connfd, garbageBuff, 256);
    while (bytesRead > 0) {
	bytesRead = read(connfd, garbageBuff, 256);
    }

    if (bytesRead == 0) {
	close(connfd);
    } else if (bytesRead == -1) {
	return 6;
    }

    return 0;
}

/**
 * Obtain the lists of Sync IDs of new, modified, and deleted items.
 *
 * Obtain the lists of Sync IDs of new, modified, and deleted items from the
 * Zaurus and store them in private member variables of the class.
 * @param type An identifier representing the type of synchronization.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully set the next sync anchor.
 * @retval 1 Failed to receive a request.
 * @retval 2 Failed to receive an ack message.
 * @retval 3 Failed to allocate memory for the ASY message.
 * @retval 4 Failed to receive an ASY message.
 */
int ZaurusType::ObtainSyncIDLists(const unsigned char type) {
    SyncIDListType syncIDList;
    unsigned short int syncIDCnt;
    unsigned short int numSyncIDs;
    ASYMessageType *pASYMsg;

    if (RecvRqst(connfd) != 0)
	return 1;

    SendRSY(connfd, type);

    if (RecvAck(connfd) != 0)
	return 2;

    SendRqst(connfd);

    pASYMsg = new ASYMessageType;
    if (!pASYMsg)
	return 3;

    if (RecvMessage(connfd, pASYMsg) != 0)
	return 4;

    numSyncIDs = pASYMsg->GetNumNewSyncIDs();
    std::cout << "num New Sync IDs: " << numSyncIDs << ".\n";
    for (syncIDCnt = 0; syncIDCnt < numSyncIDs; syncIDCnt++)
	newSyncIDList.push_front(pASYMsg->GetNewSyncID(syncIDCnt));

    numSyncIDs = pASYMsg->GetNumModSyncIDs();
    std::cout << "num Mod Sync IDs: " << numSyncIDs << ".\n";    
    for (syncIDCnt = 0; syncIDCnt < numSyncIDs; syncIDCnt++)
	modSyncIDList.push_front(pASYMsg->GetModSyncID(syncIDCnt));

    numSyncIDs = pASYMsg->GetNumDelSyncIDs();
    std::cout << "num Del Sync IDs: " << numSyncIDs << ".\n";
    for (syncIDCnt = 0; syncIDCnt < numSyncIDs; syncIDCnt++)
	delSyncIDList.push_front(pASYMsg->GetDelSyncID(syncIDCnt));

    delete pASYMsg;

    SendAck(connfd);

    obtainedSyncIDLists = true;

    return 0;
}

/**
 * Get Todo Item.
 *
 * Obtain a Todo item from the Zaurus given the type of sync and the items
 * synchronization id.
 * @param type An identifier representing the type of sync.
 * @param syncID The sync ID of the item to retreive from the Zaurus.
 * @return A TodoItemType object containing the requested items data.
 */
TodoItemType ZaurusType::GetTodoItem(unsigned char type,
				     unsigned long int syncID) {
    ADRMessageType *pADRMsg;
    TodoItemType todoItem;
    unsigned char buff[256];
    char *tmpBuff[256];
    unsigned char *pCont;
    unsigned long int numFolBytes;
    std::string tmpStr;

    if (RecvRqst(connfd) != 0)
	return todoItem;

    // Here, I send the RDR to request the data content of an item given the
    // type (Todo, Calendar, etc) and the synchronization ID (unique ID) of
    // the item.
    SendRDR(connfd, type, syncID);

    if (RecvAck(connfd) != 0)
	return todoItem;

    SendRqst(connfd);

    // Here, I dynamically allocate memory for an ADR message and recv a
    // message, storing it in the ADR message (the response to the RDR).
    pADRMsg = new ADRMessageType;
    if (!pADRMsg)
	return todoItem;

    std::cout << "RECVING ADR MESSAGE.\n";

    if (RecvMessage(connfd, pADRMsg) != 0)
	return todoItem;

    std::cout << "RECVED ADR MESSAGE.\n";

    // Obtain a pointer to the message content.
    pCont = (unsigned char *)pADRMsg->GetContent();
    pCont = pCont + 35;

    std::cout << "OBTAINED MSG CONTENT.\n";

    // Here I parse the message content to obtain the item data within so that
    // I may create a todo item object with the proper data.

    // Created Time
    if (pADRMsg->GetCardCreatedTime((char *)buff, 256) != 0)
	return todoItem;

    todoItem.SetCreatedTime(
	ConvZDateTime(buff, pADRMsg->GetCardCreatedTimeSize()));

    std::cout << "SET CREATED TIME.\n";

    // Modified Time
    if (pADRMsg->GetCardModifiedTime((char *)buff, 256) != 0)
	return todoItem;

    todoItem.SetModifiedTime(
	ConvZDateTime(buff, pADRMsg->GetCardModifiedTimeSize()));

    std::cout << "SET MODIFIED TIME.\n";

    // Attribute
    todoItem.SetAttribute(pADRMsg->GetAttribute());

    std::cout << "SET ATTRIBUTE.\n";

    // SyncID
    todoItem.SetSyncID(pADRMsg->GetSyncID());

    std::cout << "SET SYNC ID.\n";

    // Category
    numFolBytes = *((unsigned long int *)pCont);
    pCont = pCont + 4;
    memcpy((void *)buff, (void *)pCont, numFolBytes);
    buff[numFolBytes] = '\0';
    tmpStr.assign((const char *)buff);
    todoItem.SetCategory(tmpStr);
    pCont = pCont + numFolBytes;

    std::cout << "SET CATEGORY.\n";

    // Start Date
    numFolBytes = *((unsigned long int *)pCont);
    pCont = pCont + 4;
    if (numFolBytes != 0) {
	memcpy((void *)buff, (void *)pCont, numFolBytes);
	todoItem.SetStartDate(
	    ConvZDateTime(buff, numFolBytes));
	pCont = pCont + numFolBytes;
    } else {
	todoItem.SetStartDate(0);
    }

    std::cout << "SET START DATE.\n";

    // Due Date
    numFolBytes = *((unsigned long int *)pCont);
    pCont = pCont + 4;
    if (numFolBytes != 0) {
	memcpy((void *)buff, (void *)pCont, numFolBytes);
	todoItem.SetDueDate(
	    ConvZDateTime(buff, numFolBytes));
	pCont = pCont + numFolBytes;
    } else {
	todoItem.SetDueDate(0);
    }

    std::cout << "SET DUE DATE.\n";

    // Completed Date
    numFolBytes = *((unsigned long int *)pCont);
    pCont = pCont + 4;
    if (numFolBytes != 0) {
	memcpy((void *)buff, (void *)pCont, numFolBytes);
	todoItem.SetCompletedDate(
	    ConvZDateTime(buff, numFolBytes));
	pCont = pCont + numFolBytes;
    } else {
	todoItem.SetCompletedDate(0);
    }

    std::cout << "SET COMPLETED DATE.\n";

    // Progress Status
    numFolBytes = *((unsigned long int *)pCont);
    pCont = pCont + 4;
    if (numFolBytes != 0) {
	memcpy((void *)buff, (void *)pCont, numFolBytes);
	todoItem.SetProgressStatus(*((unsigned char *)buff));
	pCont = pCont + numFolBytes;
    } else {
	todoItem.SetProgressStatus((unsigned char)1);
    }

    std::cout << "SET PROGRESS STATUS.\n";

    // Priority
    numFolBytes = *((unsigned long int *)pCont);
    pCont = pCont + 4;
    if (numFolBytes != 0) {
	memcpy((void *)buff, (void *)pCont, numFolBytes);
	todoItem.SetPriority(*((unsigned char *)buff));
	pCont = pCont + numFolBytes;
    } else {
	todoItem.SetPriority((unsigned char)3);
    }

    std::cout << "SET PRIORITY.\n";

    // Description
    numFolBytes = *((unsigned long int *)pCont);
    pCont = pCont + 4;
    memcpy((void *)buff, (void *)pCont, numFolBytes);
    buff[numFolBytes] = '\0';
    tmpStr.assign((const char *)buff);
    todoItem.SetDescription(tmpStr);
    pCont = pCont + numFolBytes;

    std::cout << "SET DESCRIPTION.\n";

    // Notes
    numFolBytes = *((unsigned long int *)pCont);
    std::cout << "numFolBytes = " << numFolBytes << std::endl;
    pCont = pCont + 4;
//    memcpy((void *)buff, (void *)pCont, numFolBytes);
//    buff[numFolBytes] = '\0';
    memcpy((void *)tmpBuff, (void *)pCont, numFolBytes);
    tmpBuff[numFolBytes] = '\0';
    //std::cout << "buff = " << tmpBuff;
    std::string fooStr = (const char *)tmpBuff;
    //tmpStr.assign((const char *)buff);
    //std::cout << "tmpStr = " << fooStr << std::endl;
    todoItem.SetNotes(fooStr);
    pCont = pCont + numFolBytes;

    std::cout << "SET NOTES.\n";

    delete pADRMsg;

    std::cout << "DELETED MSG.\n";

    SendAck(connfd);

    std::cout << "SENT ACK.\n";

    return todoItem;
}

/**
 * Delete Todo Item.
 *
 * Delete a Todo item from the Zaurus given the type of sync and the items
 * synchronization id.
 * @param type An identifier representing the type of sync.
 * @param syncID The sync ID of the item to delete from the Zaurus.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully, requested item be deleted.
 * @retval 1 Failed to receive a message request.
 * @retval 2 Failed to receive a message acknowledgment.
 * @retval 4 Failed to receive a AEX message in response.
 */
int ZaurusType::DeleteTodoItem(unsigned char type, unsigned long int syncID) {
    MessageType msg;

    if (RecvRqst(connfd) != 0)
	return 1;

    SendRDD(connfd, type, syncID);

    if (RecvAck(connfd) != 0)
	return 2;

    SendRqst(connfd);

    if (RecvMessage(connfd, &msg) != 0) {
	return 4;
    }

    SendAck(connfd);

    return 0;
}

/**
 * Modify Todo Item.
 *
 * Modify a Todo item on the Zaurus given the type of sync and the items data.
 * @param type An identifier representing the type of sync. The To-Do item
 * must have the same Sync ID as the item it's data should modify.
 * @param todoItem The todoItem and its data to update on the Zaurus.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully, requested item be deleted.
 * @retval 1 Failed to receive a message request.
 * @retval 2 Failed to receive a message acknowledgment.
 * @retval 3 Failed to allocate memory for building message content.
 * @retval 4 Failed to receive a ADW message in response.
 */
int ZaurusType::ModifyTodoItem(unsigned char type, TodoItemType todoItem) {
    RDWMessageType RDWMsg;
    MessageType msg;

    const int sizeComBuff = 23;
    unsigned char commonBuff[sizeComBuff];

    int sizeSyncSpecBuff = 0;
    unsigned long int categorySize = 0;
    unsigned long int startDateSize = 0;
    unsigned long int dueDateSize = 0;
    unsigned long int compDateSize = 0;
    unsigned long int progStatusSize = 0;
    unsigned long int prioritySize = 0;
    unsigned long int descSize = 0;
    unsigned long int notesSize = 0;

    std::string category;
    unsigned char startDate[5];
    unsigned char dueDate[5];
    unsigned char compDate[5];
    std::string desc;
    std::string notes;

    // Fill the common data buffer with the proper data.
    commonBuff[0] = type;
    *((unsigned short int *)(commonBuff + 1)) = 1;
    *((unsigned long int *)(commonBuff + 3)) = todoItem.GetSyncID();
    memset((void *)(commonBuff + 7), 0xff, 16);

    // Calculate the total size of the synchronization specific portion so I
    // can allocate memory for it.
    sizeSyncSpecBuff = (4 * 8);

    category = todoItem.GetCategory();
    if (!category.empty()) {
	categorySize = category.size();
	sizeSyncSpecBuff += categorySize;
    }

    if (todoItem.GetStartDate() != 0) {
	ConvCalTime(todoItem.GetStartDate(), startDate, 5);
	startDateSize = 5;
	sizeSyncSpecBuff += startDateSize;
    }

    if (todoItem.GetDueDate() != 0) {
	ConvCalTime(todoItem.GetDueDate(), dueDate, 5);
	dueDateSize = 5;
	sizeSyncSpecBuff += dueDateSize;
    }

    if (todoItem.GetCompletedDate() != 0) {
	ConvCalTime(todoItem.GetCompletedDate(), compDate, 5);
	compDateSize = 5;
	sizeSyncSpecBuff += compDateSize;
    }

    progStatusSize = 1;
    sizeSyncSpecBuff += progStatusSize;

    prioritySize = 1;
    sizeSyncSpecBuff += prioritySize;

    desc = todoItem.GetDescription();
    if (!desc.empty()) {
	descSize = desc.size();
	sizeSyncSpecBuff += descSize;
    }

    notes = todoItem.GetNotes();
    if (!notes.empty()) {
	notesSize = notes.size();
	sizeSyncSpecBuff += notesSize;
    }

    // At this point the sizeSyncSpecBuff varialbe should contain the proper
    // size in bytes of the synchronization specific portion. Now I can
    // allocate a buffer large enough to hold both the common section and the
    // synchronization specific section. I can then fill that memory with the
    // proper content format for the RDW message and pack it into the
    // RDWMessageType class instance for easy sending.
    unsigned char *pContBuff;
    unsigned char *pCurBuffPos;
    pContBuff = new unsigned char[(sizeSyncSpecBuff + sizeComBuff)];
    if (pContBuff == NULL)
	return 3;
    
    // Copy the common portion into the proper place.
    memcpy((void *)pContBuff, (void *)commonBuff, sizeComBuff);
    pCurBuffPos = (pContBuff + sizeComBuff);

    *((unsigned long int *)pCurBuffPos) = categorySize;
    pCurBuffPos += 4;
    if (categorySize) {
	memcpy((void *)pCurBuffPos, (void *)category.c_str(), categorySize);
	pCurBuffPos += categorySize;
    }

    *((unsigned long int *)pCurBuffPos) = startDateSize;
    pCurBuffPos += 4;
    if (startDateSize) {
	memcpy((void *)pCurBuffPos, (void *)startDate, startDateSize);
	pCurBuffPos += startDateSize;
    }

    *((unsigned long int *)pCurBuffPos) = dueDateSize;
    pCurBuffPos += 4;
    if (dueDateSize) {
	memcpy((void *)pCurBuffPos, (void *)dueDate, dueDateSize);
	pCurBuffPos += dueDateSize;
    }

    *((unsigned long int *)pCurBuffPos) = compDateSize;
    pCurBuffPos += 4;
    if (compDateSize) {
	memcpy((void *)pCurBuffPos, (void *)compDate, compDateSize);
	pCurBuffPos += compDateSize;
    }

    *((unsigned long int *)pCurBuffPos) = progStatusSize;
    pCurBuffPos += 4;
    *pCurBuffPos = todoItem.GetProgressStatus();
    pCurBuffPos += 1;

    *((unsigned long int *)pCurBuffPos) = prioritySize;
    pCurBuffPos += 4;
    *pCurBuffPos = todoItem.GetPriority();
    pCurBuffPos += 1;

    *((unsigned long int *)pCurBuffPos) = descSize;
    pCurBuffPos += 4;
    if (categorySize) {
	memcpy((void *)pCurBuffPos, (void *)desc.c_str(), descSize);
	pCurBuffPos += descSize;
    }

    *((unsigned long int *)pCurBuffPos) = notesSize;
    pCurBuffPos += 4;
    if (categorySize) {
	memcpy((void *)pCurBuffPos, (void *)notes.c_str(), notesSize);
	pCurBuffPos += notesSize;
    }

    // At this point pContBuff should be filled with all the proper data and
    // be in the proper format ready for packing in the RDW message so I pack
    // it.
    RDWMsg.SetContent((void *)pContBuff, (sizeComBuff + sizeSyncSpecBuff));

    // Here I deallocate the previously allocated memory.
    delete [] pContBuff;

    // Now that it has been packed into the RDWMsg the message is now ready to
    // be sent. However I must handle the rest of the protocol before I can
    // send the message.

    if (RecvRqst(connfd) != 0)
	return 1;

    SendMessage(connfd, &RDWMsg);

    if (RecvAck(connfd) != 0)
	return 2;

    SendRqst(connfd);

    if (RecvMessage(connfd, &msg) != 0) {
	return 4;
    }

    SendAck(connfd);

    return 0;
}

/**
 * Add Todo Item.
 *
 * Add a Todo item to the Zaurus given the type of sync and the items data.
 * @param type An identifier representing the type of sync.
 * @param todoItem The todoItem and its data to update on the Zaurus.
 * @return The To-Do item that was just added with its sync ID now set.
 */
TodoItemType ZaurusType::AddTodoItem(unsigned char type,
				     TodoItemType todoItem) {
    std::cout << "Entered the AddTodoItem() function.\n";
    TodoItemType errTodo;

    // Since adding an item to the Zaurus is a two step event I perform the
    // first step here, which is basically to request that the Zaurus allocate
    // space for a new item and have it send back the synchronization ID of
    // the allocated space so that in the second step we can send the data to
    // the Zaurus.
    RDWMessageType obtIdMsg;
    ADWMessageType idIsMsg;
    const int sizeObtIdBuff = 12;
    unsigned char obtIdBuff[sizeObtIdBuff];
    unsigned long int syncId;

    std::cout << "Created function scoped variables.\n";

    // I first build the RDW messages content because the RDWMessageType class
    // is a stub class it does not have any functions to allow for the setting
    // of the content other than SetContent.
    obtIdBuff[0] = type;
    *((unsigned short int *)(obtIdBuff + 1)) = 1;
    *((unsigned long int *)(obtIdBuff + 3)) = 0;
    *((unsigned long int *)(obtIdBuff + 7)) = 1;
    obtIdBuff[sizeObtIdBuff - 1] = 0x00;
    obtIdMsg.SetContent((void *)obtIdBuff, sizeObtIdBuff);

    std::cout << "Built RDW message content.\n";

    // At this point the message content has been built and packed into the
    // correct instance of the RDWMessageType class. Hence, I send the message
    // and attempt to get a ADW message back containing the sync ID of the
    // newly requested item.
    if (RecvRqst(connfd) != 0)
	return errTodo;

    std::cout << "Recved Request.\n";

    SendMessage(connfd, &obtIdMsg);

    std::cout << "Sent RDW message to obtain the sync id.\n";

    if (RecvAck(connfd) != 0)
	return errTodo;

    std::cout << "Got an ack message back.\n";

    SendRqst(connfd);

    std::cout << "Sent request for message containing sync id.\n";

    if (RecvMessage(connfd, &idIsMsg) != 0) {
	return errTodo;
    }

    std::cout << "Recved message containing sync id.\n";

    SendAck(connfd);

    std::cout << "Sent an ack mesasge.\n";
    
    // At this point I have requested and have obtained the message containing
    // the synchronization ID of the new item. Due to this I extract the
    // synchronization ID from the message and store it for later use when I
    // update tho item data.
    syncId = idIsMsg.GetSyncID();

    std::cout << "Obtained sync ID from message content.\n";

    // Now that I have obtained the synchronization ID I want to use the
    // synchronization ID to perform the second phase of the event. In this
    // phase I build another variation of the RDW message, which is used to
    // send the data of a new item to the Zaurus, and wait attempt to receive
    // an ADW message back stating succes.
    RDWMessageType RDWMsg;
    MessageType msg;

    const int sizeComBuff = 28;
    unsigned char commonBuff[sizeComBuff];

    int sizeSyncSpecBuff = 0;
    unsigned long int categorySize = 0;
    unsigned long int startDateSize = 0;
    unsigned long int dueDateSize = 0;
    unsigned long int compDateSize = 0;
    unsigned long int progStatusSize = 0;
    unsigned long int prioritySize = 0;
    unsigned long int descSize = 0;
    unsigned long int notesSize = 0;

    std::string category;
    unsigned char startDate[5];
    unsigned char dueDate[5];
    unsigned char compDate[5];
    std::string desc;
    std::string notes;

    // Fill the common data buffer with the proper data.
    commonBuff[0] = type;
    *((unsigned short int *)(commonBuff + 1)) = 1;
    *((unsigned long int *)(commonBuff + 3)) = 0;
    *((unsigned long int *)(commonBuff + 7)) = 1;
    *((unsigned char *)(commonBuff + 11)) = 0x00;
    *((unsigned long int *)(commonBuff + 12)) = 0;
    *((unsigned long int *)(commonBuff + 16)) = 0;
    *((unsigned long int *)(commonBuff + 20)) = 4;
    *((unsigned long int *)(commonBuff + 24)) = syncId;

    std::cout << "Filled common data buffer with proper data.\n";

    // Calculate the total size of the synchronization specific portion so I
    // can allocate memory for it.
    sizeSyncSpecBuff = (4 * 8);

    category = todoItem.GetCategory();
    if (!category.empty()) {
	categorySize = category.size();
	sizeSyncSpecBuff += categorySize;
    }

    if (todoItem.GetStartDate() != 0) {
	ConvCalTime(todoItem.GetStartDate(), startDate, 5);
	startDateSize = 5;
	sizeSyncSpecBuff += startDateSize;
    }

    if (todoItem.GetDueDate() != 0) {
	ConvCalTime(todoItem.GetDueDate(), dueDate, 5);
	dueDateSize = 5;
	sizeSyncSpecBuff += dueDateSize;
    }

    if (todoItem.GetCompletedDate() != 0) {
	ConvCalTime(todoItem.GetCompletedDate(), compDate, 5);
	compDateSize = 5;
	sizeSyncSpecBuff += compDateSize;
    }

    progStatusSize = 1;
    sizeSyncSpecBuff += progStatusSize;

    prioritySize = 1;
    sizeSyncSpecBuff += prioritySize;

    desc = todoItem.GetDescription();
    if (!desc.empty()) {
	descSize = desc.size();
	sizeSyncSpecBuff += descSize;
    }

    notes = todoItem.GetNotes();
    if (!notes.empty()) {
	notesSize = notes.size();
	sizeSyncSpecBuff += notesSize;
    }

    std::cout << "Calculated the total size of the sync specific portion.\n";

    // At this point the sizeSyncSpecBuff variable should contain the proper
    // size in bytes of the synchronization specific portion. Now I can
    // allocate a buffer large enough to hold both the common section and the
    // synchronization specific section. I can then fill that memory with the
    // proper content format for the RDW message and pack it into the
    // RDWMessageType class instance for easy sending.
    unsigned char *pContBuff;
    unsigned char *pCurBuffPos;
    pContBuff = new unsigned char[(sizeSyncSpecBuff + sizeComBuff)];
    if (pContBuff == NULL)
	return errTodo;

    std::cout << "Allocated memory for the sync specific portion.\n";
    
    // Copy the common portion into the proper place.
    memcpy((void *)pContBuff, (void *)commonBuff, sizeComBuff);
    pCurBuffPos = (pContBuff + sizeComBuff);

    std::cout << "Coppied the common portion into proper place.\n";

    // Set the data for the sync specific portion of the messaeg.
    *((unsigned long int *)pCurBuffPos) = categorySize;
    pCurBuffPos += 4;
    if (categorySize) {
	memcpy((void *)pCurBuffPos, (void *)category.c_str(), categorySize);
	pCurBuffPos += categorySize;
    }

    *((unsigned long int *)pCurBuffPos) = startDateSize;
    pCurBuffPos += 4;
    if (startDateSize) {
	memcpy((void *)pCurBuffPos, (void *)startDate, startDateSize);
	pCurBuffPos += startDateSize;
    }

    *((unsigned long int *)pCurBuffPos) = dueDateSize;
    pCurBuffPos += 4;
    if (dueDateSize) {
	memcpy((void *)pCurBuffPos, (void *)dueDate, dueDateSize);
	pCurBuffPos += dueDateSize;
    }

    *((unsigned long int *)pCurBuffPos) = compDateSize;
    pCurBuffPos += 4;
    if (compDateSize) {
	memcpy((void *)pCurBuffPos, (void *)compDate, compDateSize);
	pCurBuffPos += compDateSize;
    }

    *((unsigned long int *)pCurBuffPos) = progStatusSize;
    pCurBuffPos += 4;
    *pCurBuffPos = todoItem.GetProgressStatus();
    pCurBuffPos += 1;

    *((unsigned long int *)pCurBuffPos) = prioritySize;
    pCurBuffPos += 4;
    *pCurBuffPos = todoItem.GetPriority();
    pCurBuffPos += 1;

    *((unsigned long int *)pCurBuffPos) = descSize;
    pCurBuffPos += 4;
    if (categorySize) {
	memcpy((void *)pCurBuffPos, (void *)desc.c_str(), descSize);
	pCurBuffPos += descSize;
    }

    *((unsigned long int *)pCurBuffPos) = notesSize;
    pCurBuffPos += 4;
    if (categorySize) {
	memcpy((void *)pCurBuffPos, (void *)notes.c_str(), notesSize);
	pCurBuffPos += notesSize;
    }

    std::cout << "Set the data for the sync specific portion.\n";

    // At this point pContBuff should be filled with all the proper data and
    // be in the proper format ready for packing in the RDW message so I pack
    // it.
    RDWMsg.SetContent((void *)pContBuff, (sizeComBuff + sizeSyncSpecBuff));

    std::cout << "Set the message content.\n";

    // Here I deallocate the previously allocated memory.
    delete [] pContBuff;

    std::cout << "Freed the memory allocated for the temporary buffer.\n";

    // Now that it has been packed into the RDWMsg the message is now ready to
    // be sent. However I must handle the rest of the protocol before I can
    // send the message.

    if (RecvRqst(connfd) != 0)
	return errTodo;

    std::cout << "Recvd request message.\n";

    SendMessage(connfd, &RDWMsg);

    std::cout << "Sent RDW message.\n";

    if (RecvAck(connfd) != 0)
	return errTodo;

    std::cout << "Recved an ACK in response.\n";

    SendRqst(connfd);

    std::cout << "Sent request for a response message.\n";

    if (RecvMessage(connfd, &msg) != 0) {
	return errTodo;
    }

    std::cout << "Received a response message.\n";

    SendAck(connfd);

    std::cout << "Sent ACK message in response.\n";

    errTodo = todoItem;
    errTodo.SetSyncID(syncId);

    std::cout << "Set the sync ID of the returned Todo.\n";

    return errTodo;
}

/**
 * State the sync is done.
 *
 * The State that the synchronization is done.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully set the next sync anchor.
 * @retval 1 Failed to receive a request.
 * @retval 2 Failed to receive an ack message.
 * @retval 4 Failed to receive an AEX message.
 */ 
int ZaurusType::StateSyncDone(const unsigned char type) {
    MessageType msg;

    if (RecvRqst(connfd) != 0)
	return 1;

    SendRDS(connfd, type);

    if (RecvAck(connfd) != 0)
	return 2;

    SendRqst(connfd);

    if (RecvMessage(connfd, &msg) != 0) {
	return 4;
    }

    SendAck(connfd);

    return 0;
}


/**
 * Convert the Zaurus protocol packed time and date (DATA_ID_TIME).
 *
 * Convert the Zaurus protocol packed time and date into the number of seconds
 * sinc epoch (00:00:00 on January 1, 1970, Coordinate Universal Time
 * (UTC). Since the Zaurus protocol packed time and date is contained within 5
 * bytes, the data buffer should consist of 5 bytes of data and the length
 * should be set to 5 bytes.
 * @param data A pointer to buffer containing the Zaurus packed time and date.
 * @param len Length of the buf containing the Zaurus packed time and date.
 * @return Number of seconds elapsed since epoch, or a negative val in error.
 * @retval -1 Failed, The data pointer points to NULL.
 * @retval -2 Failed, The specified len value is NOT 5 bytes.
 * @retval -3 Failed, Failed in conversion from local time to calendar time.
 */
time_t ZaurusType::ConvZDateTime(unsigned char *data, unsigned short int len)
{
    struct tm tmpTime;
    time_t secsSinceEpoch;
    unsigned short int year;
    unsigned short int month;
    unsigned short int monthDay;
    unsigned short int hour;
    unsigned short int mins;
    unsigned short int secs;

    // Check to make sure that the data pointer points to data rather than to
    // nothing. If it points to nothing then return in an error.
    if (!data)
	return -1;

    // Check to make sure that the specified length of the provided data is
    // equal to 5. If it is not return in error.
    if (len != 5)
	return -2;

    // Fewww, this was a huge pain in the butt to figure out. I was NOT aware
    // that when bit shifting in C++ it converts your native little endian
    // byte order to big endian byte order before the actual shifting and
    // reverts back to little endian when done with the shifting. The only
    // reason this matters is because we are not starting with values we are
    // starting with bytes of unknown values. Normally, when one is dealing
    // with shifting they do not have to worry about this because they can
    // just think of their value in big endian and shift logically letting the
    // shifting operator handle the actual conversion from little to big and
    // back to little.

    // Here, I do all the proper bit shifting to obtain the proper values. I
    // decided that it was better to do it using bit shifting rather than bit
    // fields because of portability issues with bit fields.
    memcpy((void *)&year, (void *)(data + 3), 2);
    year = year << 4;
    year = year >> 8;

    memcpy((void *)&month, (void *)(data + 2), 2);
    month = month << 4;
    month = month >> 12;

    memcpy((void *)&monthDay, (void *)(data + 2), 2);
    monthDay = monthDay << 8;
    monthDay = monthDay >> 11;

    memcpy((void *)&hour, (void *)(data + 1), 2);
    hour = hour << 5;
    hour = hour >> 11;

    memcpy((void *)&mins, (void *)(data + 1), 2);
    mins = mins << 10;
    mins = mins >> 10;

    memcpy((void *)&secs, (void *)data, 2);
    secs = secs << 8;
    secs = secs >> 10;

    // Here I fill in the tm structure instance with the data from the Zaurus
    // DATA_ID_TIME type so that I can convert the tm struct to the number of
    // seconds since epoch.
    tmpTime.tm_sec = secs;
    tmpTime.tm_min = mins;

    // Convert and set the hour from the format that it is in from the Zaurus
    // into the format that the struct tm structure calls for. The Zaurus
    // format starts at 4pm being equal to 0 and increments appropriately for
    // the subsequent hours. It does wrap around, hence, midnight is 8 and 3pm
    // is 23. The struct tm requires its hour member to be the number of hours
    // past midnight in a range of 0 to 23.
    if (hour < 8)
	tmpTime.tm_hour = hour + 16;
    else
	tmpTime.tm_hour = hour - 8;

    tmpTime.tm_mday = monthDay;
    tmpTime.tm_mon = month - 1;
    tmpTime.tm_year = year;

    // Given the broken down time make calendar time (seconds since epoch) out
    // of the local broken down time. If the mktime() function fails to do so
    // then return an error.
    secsSinceEpoch = mktime(&tmpTime);
    if (secsSinceEpoch == -1)
	return -3;

    return secsSinceEpoch;
}

/**
 * Convert the C calendar time and date (time_t - secs since epoch).
 *
 * Convert the C calendar time and date (time_t - secs since epoch) into the
 * Zaurus protocol packed time and date (DATE_ID_TIME).
 * @param calTime The calTime (secs since epoch) to convert into Zaurus packed
 * time and date.
 * @param dest A pointer to buffer that should contain the Zaurus packed time
 * and date (must be 5 bytes long).
 * @param len Length of the buf that should contain the Zaurus packed time and
 * date.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully converted the calendar time to Zaurus packed time
 * and date.
 * @retval 1 Failed due to len NOT being 5 bytes in length.
 * @retval 2 Failed, due to an error in converting calendar time to broken
 * down time.
 */
int ZaurusType::ConvCalTime(time_t calTime, unsigned char *dest,
			    unsigned short int len) {
    struct tm brkDwnTime;
    unsigned short int *tmpPtr;
    unsigned short int month;
    unsigned short int hour;
    unsigned short int min;
    unsigned short int sec;

    // Check to make sure that the len is 5 bytes long.
    if (len != 5)
	return 1;

    // Here I convert the calendar time into broken down time so that I can
    // easily access the different broken down members for storage in the
    // Zaurus protocols packed format.
    if (localtime_r(&calTime, &brkDwnTime) == NULL) {
	return 2;
    }

    // Here I set all the bytes that are going to be used to store the Zaurus
    // time and date data to zero so that there are no problems with the
    // following inclusive ORs with garbage data.
    memset((void *)dest, 0x00, 5);

    // Here I do some bitwise operations to pack the year and month into the
    // proper bytes for the Zaurus protocols date and time format.

    // Here, I assign a temperary unsigned short pointer to point to the last
    // two bytes of the data array.
    tmpPtr = (unsigned short int *)(dest + 3);
    // Then I set the last to bytes equal to the proper value from the broken
    // down time.
    *tmpPtr = brkDwnTime.tm_year;
    // I then shift the year value four bits to the left (visually in big
    // endian) to make room for the month.
    (*tmpPtr) = (*tmpPtr) << 4;
    // Here, I set unsigned short int variable (month) equal to the proper
    // value to store in the month portion of the packed data. I then take and
    // bitwise inclusive OR the month value and the data that holds the year
    // to get the proper packed data format for the last to bytes of the data.
    month = brkDwnTime.tm_mon + 1;
    (*tmpPtr) = (*tmpPtr) | month;

    // Here I do some bitwise operations to pack hour, min, and month day into
    // the proper bytes for the Zaurus protocol date and time format.

    // Here, I assign a temporary unsigned short pointer to point to the
    // second to last, 2 bytes of the data array.
    tmpPtr = (unsigned short int *)(dest + 1);
    // Then I set the last bytes equal to the proper value from the broken
    // down time for the month day.
    *tmpPtr = brkDwnTime.tm_mday;
    // I then shift the month day value 11 bits to the left (visually in big
    // endian) to make room for the hour and min entries.
    (*tmpPtr) = (*tmpPtr) << 11;
    // Here, I set unsigned short int variable (hour) equal to the proper
    // value to store in the hour portion of the packed data. I then shift it
    // the proper number of bits and do an inclusive bitwise OR to move the
    // bits into the proper location in the data.
    if (brkDwnTime.tm_hour < 16)
	hour = brkDwnTime.tm_hour + 8;
    else
	hour = brkDwnTime.tm_hour - 16;
    hour = hour << 6;
    (*tmpPtr) = (*tmpPtr) | hour;
    // Here, I set unsigned short int variable (min) equal to the proper value
    // to store in the min portion of the packed data. I then inclusive OR it
    // into the data so that it is in the proper location in the data.
    min = brkDwnTime.tm_min;
    (*tmpPtr) = (*tmpPtr) | min;

    // Here I do some bitwise operations to pack the secs into the proper byte
    // for the Zaurus protocol date and time format.

    // Here, I assign a temporary unsigned short pointer to point to the first
    // two bytes of the data array.
    tmpPtr = (unsigned short int *)(dest);
    // Then I set unsigned short int variable (sec) equal to the proper value
    // to store in the sec portion of the packed data. I then shift it the
    // proper number of bits and do an inclusive OR to move the bits into the
    // proper location in te data.
    sec = brkDwnTime.tm_sec;
    sec = sec << 2;
    (*tmpPtr) = (*tmpPtr) | sec;

    // At this point all the proper information has been packed into the
    // proper format.

    return 0;
}

["TodoPluginType.hh" (text/plain)]

/*
 * Copyright 2004, 2005 Andrew De Ponte
 * 
 * This file is part of zsrep.
 * 
 * zsrep is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 * 
 * zsrep is distributed in the hopes that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with zsrep; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

/**
 * @file TodoPluginType.hh
 * @brief A specifications file for a base class for TodoPlugins.
 * @author Andrew De Ponte
 *
 * A specifications file for a class existing to provide a base class used as
 * a template for Todo plugins for the ZaurusSync application.
 */

#ifndef TODOPLUGINTYPE_H
#define TODOPLUGINTYPE_H

#include <time.h>
#include <string>
#include <zdata_lib/TodoItemType.hh>

/**
 * @class TodoPluginType
 * @brief A type existing as a template for TodoPlugins.
 *
 * The TodoPluginType is a class which exists to be the base class of actual
 * Todo plugins. One might say it is a rigid template which is required to be
 * followed when creating plugins. It specifies all the required member
 * functions.
 */
class TodoPluginType {
public:
    /**
     * Destruct the TodoPluginType object.
     *
     * Desctuct the TodoPluginType object by freeing any dynamically allocated
     * memory.
     */
    virtual ~TodoPluginType(void) { };

    /**
     * Initialize the TodoPluginType object.
     *
     * Initialize the TodoPluginType object in any way needed before the
     * normal synchronization based member functions start getting called.
     * @return An integer representing success (zero) or failure (non-zero).
     */
    virtual int Initialize(void) = 0;

    /**
     * Clean up the TodoPluginType object.
     *
     * Clean up the TodoPluginType object in any way needed after the normal
     * synchronization based member functions have been called.
     * @return An integer representing success (zero) or failure (non-zero).
     */
    virtual int CleanUp(void) = 0;

    /**
     * Get all Todo Items.
     *
     * Obtan all the Todo items that exist inside the object which this plugin
     * represents.
     * @return A list of all the Todo Items.
     */
    virtual TodoItemType::List GetAllTodoItems(void) = 0;

    /**
     * Get the new Todo Items.
     *
     * Obtain the Todo items that were either created after the last
     * synchronization the last synchronization.
     * @param lastTimeSynced The last time synchronized represented as number
     * of seconds since epoch.
     * @return A list of the new Todo Items.
     */
    virtual TodoItemType::List GetNewTodoItems(time_t lastTimeSynced) = 0;

    /**
     * Get the modified Todo Items.
     *
     * Obtain the Todo items that were modified after the last
     * synchronization.
     * @param lastTimeSynced The last time synchronized represented as number
     * of seconds since epoch.
     * @return A list of modified Todo Items.
     */
    virtual TodoItemType::List GetModTodoItems(time_t lastTimeSynced) = 0;

    /**
     * Get the deleted Todo Items.
     *
     * Obtain the IDs of the Todo items that were deleted after the last
     * synchronizaiton.
     * @param lastTimeSynced The last time synchronized represented as number
     * of seconds since epoch.
     * @return A list of sync IDs of deleted Todo Items.
     */
    virtual SyncIDListType GetDelTodoItemIDs(time_t lastTimeSynced) = 0;

    /**
     * Add the Todo Items.
     *
     * Add the Todo items within the passed list to the component represented
     * by the plugin.
     * @param todoItems A list of Todo Items to add to the plugin component.
     * @return An integer representing success (zero) or failure (non-zero).
     */
    virtual int AddTodoItems(TodoItemType::List todoItems) = 0;

    /**
     * Modify the Todo Items.
     *
     * Modify the Todo items within the passed list to the component
     * represented by the plugin.
     * @param todoItems A list of todo Items to delete from the plugin
     * component.
     * @return An integer representing success (zero) or failure (non-zero).
     */
    virtual int ModTodoItems(TodoItemType::List todoItems) = 0;

    /**
     * Delete the Todo Items.
     *
     * Delete the Todo items that have the sync IDs contained in the passed
     * list from the component represented by the plugin.
     * @param syncIDList A list of sync IDs of the items to delete from the
     * plugin component.
     * @return An integer representing success (zero) or failure (non-zero).
     */
    virtual int DelTodoItems(SyncIDListType syncIDList) = 0;

    /**
     * Map the Item IDs
     *
     * Map the Item IDs between the Zaurus and the Desktop PIM application.
     * @param todoItems A list of items which need their IDs mapped.
     * @return An integer representing success (zero) or failure (non-zero).
     */
    virtual int MapItemIDs(TodoItemType::List todoItems) = 0;

    /**
     * Get plugin description.
     *
     * Get the description of the plugin.
     * @return The plugin's description.
     */
    virtual std::string GetPluginDescription(void) const = 0;

    /**
     * Get plugin name.
     *
     * Get the name of the plugin.
     * @return The plugin's name.
     */
    virtual std::string GetPluginName(void) const = 0;

    /**
     * Get the plugin author.
     *
     * Get the author of the plugin.
     * @return The plugin's author.
     */
    virtual std::string GetPluginAuthor(void) const = 0;

    /**
     * Get the plugin version.
     *
     * Get the version of the plugin.
     * @return The plugin's version.
     */
    virtual std::string GetPluginVersion(void) const = 0;
};

typedef TodoPluginType* (*create_t)();
typedef void (*destroy_t)(TodoPluginType*);

/**
 * Create TodoPluginType instance.
 *
 * Creates an instance of the TodoPluginType class. This function exists so
 * that the C++ class object can be obtained from the library
 * dynamically. This only has to exist because of name mangeling.
 * @return A pointer to the instance of the Todo plugin.
 */
extern "C" TodoPluginType *create(void);

/**
 * Destroy TodoPluginType instance.
 *
 * Destroys the instance pointed to by the pointer passed over. This function
 * exists so the C++ class object can be destroyed after loading it from the
 * library dynamically. This only has to exst because of name mangeling.
 * @param pPlugin A pointer to an object created with the create function.
 */ 
extern "C" void destroy(TodoPluginType *pTodoPlugin);

#endif

["zync.cc" (text/x-c++)]

/*
 * Copyright 2003, 2004 Andrew De Ponte
 * 
 * This file is part of zsrep.
 * 
 * zsrep is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 * 
 * zsrep is distributed in the hopes that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with zsrep; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

// Standard Input/Output Includes
#include <iostream>
#include <string>

// Includes for fork()
#include <unistd.h>

// Includes for loading shared objects
#include <dlfcn.h>

//#include <pthread.h>

// Includes for waitpid()
#include <sys/types.h>
#include <sys/wait.h>

// Zaurus specific Includes.
#include <zmsg.h>
#include <zdata_lib/zdata.hh>

#include <ConfigManagerType.h>

#include "TodoPluginType.hh"
#include "ZaurusType.hh"

#define APP_VERSION "0.2.2"

// This is the port that the Zaurus listens on waiting for a connection to
// initiate a synchronization from the Desktop.
#define ZLISTPORT 4244

#define CONF_WIN_Z 1
#define CONF_WIN_D 2
#define CONF_WIN_B 3

struct sData {
    pthread_mutex_t ready_mutex;
    pthread_cond_t ready_cond;
    pthread_mutex_t confManager_mutex;
    ConfigManagerType *pConfManager;
    pthread_mutex_t conf_winner_mutex;
    unsigned short int conf_winner;
};

void DispWelcomeMsg(void);
void DispUsageMsg(void);
void DispVersion(void);
void DispRetVals(void);
int PerformTodoSync(unsigned short int confWinner,
		    ConfigManagerType *pConfManager);
TodoItemType::List::iterator FindSyncID(TodoItemType::List &todoList,
					unsigned long int syncID);
void ResolveDelModConflicts(SyncIDListType &zDelTodoItemIDList,
			    TodoItemType::List &zModTodoItemList,
			    TodoItemType::List &zAddTodoItemList,
			    SyncIDListType &dDelTodoItemIDList,
			    TodoItemType::List &dModTodoItemList,
			    TodoItemType::List &dAddTodoItemList);
void ResolveModModConflicts(TodoItemType::List &zModTodoItemList,
			    TodoItemType::List &dModTodoItemList,
			    TodoItemType::List &zAddTodoItemList,
			    TodoItemType::List &dAddTodoItemList,
			    unsigned short int conflict_winner);

int main(int argc, char **argv) {
    // Generic Variable used for return values of functions.
    int retval;

    // Variables used to for creating the proper socket.
    int sockfd;
    struct sockaddr_in servaddr;

    // Variables used to obtain data from config file.
    ConfigManagerType confManager;
    char *pEnvVarVal;
    char optVal[256];
    std::string confPath;

    // Variables used to store the IP of the Zaurus which to connect to.
    char zIpAddr[256];
    int zIpCmdLine = 0;

    // Variables used to handle the child sync process.
    pid_t childPid;
    int childStatus;

    // This variable is used as a flag to represent when a sync type has been
    // chosen and when a sync type has NOT been chosen.
    int choseSyncType = 0;

    // This variable is used to store the type of synchronization.
    unsigned char syncType;

    // This is a variable used to store the conflict winner.
    unsigned short int confWinner;
    ConfigManagerType *pConfManager;

    // The two variables below are used with getopt() to parse the command
    // line arguments.
    int optchar;
    extern char *optarg;

    pConfManager = &confManager;

    // The first thing I do is parse the command line arguments and set the
    // synchronization type and possibly the Zaurus IP address if it is so
    // specified.
    while (1) {
	optchar = getopt(argc, argv, "tacd:vhr");

	// If optchar is -1 the getopt function failed to find any more
	// options and it set optind to the first item in argv that was not an
	// option.
	if (optchar == -1)
	    break;

	switch ((char)optchar) {
	    case 't':
		if (choseSyncType) {
		    std::cout << "Command Argument Error: Multiple types of" \
			" synchronizations specified.\n";
		    DispUsageMsg();
		    return 1;
		} else {
		    syncType = 0x06;
		    choseSyncType = 1;
		}
		break;
	    case 'a':
		if (choseSyncType) {
		    std::cout << "Command Argument Error: Multiple types of" \
			" synchronizations specified.\n";
		    DispUsageMsg();
		    return 1;
		} else {
		    syncType = 0x07;
		    choseSyncType = 1;
		}
		break;
	    case 'c':
		if (choseSyncType) {
		    std::cout << "Command Argument Error: Multiple types of" \
			" synchronizations specified.\n";
		    DispUsageMsg();
		    return 1;
		} else {
		    syncType = 0x01;
		    choseSyncType = 1;
		}
		break;
	    case 'd':
		strncpy(zIpAddr, optarg, 256);
		zIpCmdLine = 1;
		break;
	    case 'v':
		DispVersion();
		return 0;
	    case 'h':
		DispUsageMsg();
		return 0;
	    case 'r':
		DispRetVals();
		return 0;
	    case '?':
		std::cout << "Command Argument Error: Unsupported option" \
		    " used.\n";
		DispUsageMsg();
		return 2;
	}
    }

    // Here I check to make sure that a synchronization type has been
    // specified on the command line. If not I display an error message along
    // with a usage message and return in error.
    if (!choseSyncType) {
	std::cout << "Command Argument Error: No synchronization type" \
	    " specified.\n";
	DispUsageMsg();
	return 3;
    }

    // Now that I have parsed the command line arguments I load in the options
    // from the config file.

    // Obtain the value of the HOME environment variable and build the path to
    // the config file so that it can be loaded.
    pEnvVarVal = getenv("HOME");
    if (!pEnvVarVal) {
	std::cout << "Failed to obtain the HOME environment variable.\n";
	return 11;
    }

    confPath.assign(pEnvVarVal);
    confPath.append("/.zync.conf");

    // Now I attempt to open and load the config file.
    retval = confManager.Open((char *)confPath.c_str());
    if (retval != 0) {
	if (retval == -1)
	    return 12;
    }

    // If I reach this point I know that the ConfigManager has opened the
    // file. Hence, I try to load the supported options.
    retval = confManager.GetValue("zaurus_ip", optVal, 256);
    if (retval != 0) {
	if (zIpCmdLine != 0) {
	    std::cout << "An IP for the Zaurus was not specified in the" \
		" config or on the command line. Please specify an IP" \
		" address via one of these methods.\n";
	    return 13;
	}
    } else {
	if (!zIpCmdLine) {
	    strncpy(zIpAddr, optVal, 256);
	}
    }

    // If I reach this point I know that the ConfigManager has opened the
    // file. Hence, I try to load the conflict_winner option.
    retval = confManager.GetValue("conflict_winner", optVal, 256);
    if (retval != 0) {
	std::cout << "A conflict_winner was not specified in the config" \
	    " file. Please specify the conflict_winner option in the config" \
	    " file with one of the three values \"zaurus\", \"desktop\"," \
	    " \"both\".\n";
	return 14;
    } else {
	if (strcmp(optVal, "zaurus") == 0) {
	    confWinner = CONF_WIN_Z;
	} else if (strcmp(optVal, "desktop") == 0) {
	    confWinner = CONF_WIN_D;
	} else if (strcmp(optVal, "both") == 0) {
	    confWinner = CONF_WIN_B;
	} else {
	    std::cout << "The conflict_winner option was specified in the" \
		" config file with an incorrect value. The value must be" \
		" one of the following three, \"zaurus\", \"desktop\"," \
		" \"both\".\n";
	    return 15;
	}
    }

    // After parsing the command line arguments I display the welcome message
    // and then move onto running the proper synchronization process based on
    // the type of synchronization specified in the command line arguments.
    DispWelcomeMsg();

    // Now before I fork any processes I set the stdout file descriptor to not
    // buffer.
    setbuf(stdout, NULL);

    // Initiate the synchronization based on the type specified by the command
    // line argument.
    if (syncType == 0x06) {
	// Note: I had to use fork() here for creating the other process
	// because I ran into problems with using libkcal in the thread due to
	// it not being thread safe.
	if ((childPid = fork()) == 0) {
	    // This is the child process. In this case it is actually the
	    // sync server which is going to listen for a connection from the
	    // Zaurus and perform the synchronization.

	    retval = PerformTodoSync(confWinner, pConfManager);
	    exit(retval);
	}

	if (childPid == -1) {
	    std::cout << "zync: Error in forking for To-Do sync.\n";
	    perror("main");
	    return 4;
	}
    } else if (syncType == 0x07) {
	std::cout << "Address Book synchronization has NOT been" \
	    " implemented yet.\n";
	return 0;
    } else if (syncType == 0x01) {
	std::cout << "Calendar synchronization has NOT been" \
	    " implemented yet.\n";
	return 0;
    }

    // This is where execution returns to the parent process. In this case we
    // are using the parent process as the Desktop Client which basically
    // connects to the Zaurus and tells the Zaurus that it should initiate a
    // synchronization.

    // Attempt to create a socket to be used to connect to the Zaurus
    // synchronization server.
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
	perror("main");
	return 7;
    }

    /// Now that I have the socket I have to create a address for the socket
    /// to attempt to connect to. The address consists of an ip address and a
    /// port for which I would like to connect.
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(ZLISTPORT);
    if (inet_pton(AF_INET, zIpAddr, &servaddr.sin_addr) <= 0) {
	perror("main");
	return 8;
    }

    // Here, I call a wait on a condition to determine if the synchronization
    // server thread spawned above is ready to accept connections.
    /*
    pthread_mutex_lock(&data.ready_mutex);
    pthread_cond_wait(&data.ready_cond, &data.ready_mutex);
    pthread_mutex_unlock(&data.ready_mutex);
    */

    sleep(5);

    // Now that I have the address set appropriateley for the server I would
    // like to make a connection to, I make the connection.
    retval = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    if (retval == -1) {
	perror("main");
	return 9;
    }

    // Now that the connection has been made I initiate the synchronization by
    // sending an RAY message to the Zaurus over the new connection.
    SendRAY(sockfd);

    // I wait for the sync server process to exit and then I send the finish
    // message and close the socket. If I did not due this the child process
    // would get killed and terminate thy sync process part way through.
    retval = waitpid(childPid, &childStatus, 0);
    if (retval == -1) {
	std::cout << "zync: Error while waiting for child pid.\n";
	perror("main");
	return 10;
    }

    // Now that the actual synchronization portion has finished I close the
    // connection to the Zaurus synchronization server.
    send_fin_msg(sockfd);
    close(sockfd);

    // Return in success
    return 0;
}

/**
 * Display the Welcome Message.
 *
 * Display the Welcome Message including the license statement.
 */
void DispWelcomeMsg(void) {
    using namespace std;
    cout << "Welcome to the zync!" << endl;
    cout << "zync is released under the GNU General Public License" \
	" version 2." << endl << endl;
    cout << "You should have received a copy of the GNU General Public" \
	" License" << endl;
    cout << "along with zync; if not, write to the Free Software\n";
    cout << "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA" \
	" 02111-1307 USA" << endl << endl;
    cout << "Please enjoy our teams efforts to bring Zaurus" \
	" Synchronization to the Linux" << endl;
    cout << "users, ranging from novice to expert." << endl << endl;
}

/**
 * Display the Usage Message.
 *
 * Display the Message which shows the proper usage of the tool.
 */
void DispUsageMsg(void) {
    using namespace std;
    cout << "Usage:\n";
    cout << "------\n";
    cout << "zync [ -t | -a | -c ] [ -d <zaurus ip> ] [ -v ] [ -h ] [ -r ]\n";
    cout << "t - Synchronize To-Do.\n";
    cout << "a - Synchronize Address Book.\n";
    cout << "c - Synchronize Calendar.\n";
    cout << "d - Specify Zaurus IP address (overrides config).\n";
    cout << "v - Display application version information.\n";
    cout << "h - Display the application usage.\n";
    cout << "r - Display the applications possible return values.\n";
}

/**
 * Display the Version.
 *
 * Display the Version of the tool.
 */
void DispVersion(void) {
    using namespace std;
    cout << "zync " << APP_VERSION << "\n";
}

/**
 * Display the return values.
 *
 * Display a list of the applications return values and their meanings.
 */
void DispRetVals(void) {
    using namespace std;
    cout << "Return Values:\n";
    cout << "--------------\n";
    cout << "00: The application performed as it should have, success.\n";
    cout << "01: Command line specified multiple synchronization types.\n";
    cout << "02: Unsupported command line option was passed.\n";
    cout << "03: No synchronization type specified in the command line.\n";
    cout << "04: Failed to fork process for Todo sync server.\n";
    cout << "05: Failed to fork process for Address Book sync server.\n";
    cout << "06: Failed to fork process for Calendar sync server.\n";
    cout << "07: Failed to create socket for synchronization client to use.\n";
    cout << "08: Failed to convert string IP address to net address struct.\n";
    cout << "09: Failed to connect to the Zaurus synchronization server.\n";
    cout << "10: Failed to wait for Desktop synchronization server process.\n";
    cout << "11: Failed to obtain value of the HOME environment variable.\n";
    cout << "12: Failed to open the .zync.conf file for reading.\n";
    cout << "13: Zaurus IP not set in the .zync.conf file or cmd line.\n";
    cout << "14: The conflict_winner is not set in the .zync.conf file.\n";
    cout << "15: The conflict_winner is set to an illegal vaule.\n";
}

/**
 * Find item with sync ID.
 *
 * Find an item within a list of To-Do items which has the given
 * synchronization ID. If this function fails to find the item the empty end
 * nodes iterator is returned else the iterator of the item is found. Note:
 * This returns the first instance found.
 * @return The iterator of the item with the matching sync ID if found, empty
 * end node of the passed list if not found.
 */
TodoItemType::List::iterator FindSyncID(TodoItemType::List &todoList,
					unsigned long int syncID) {
    TodoItemType::List::iterator iter;

    for (iter = todoList.begin(); iter != todoList.end(); ++iter)
	if ((*iter).GetSyncID() == syncID)
	    return iter;

    return todoList.end();
}

/**
 * Resolve Deltion/Modification conflicts.
 *
 * This function searches for and resolves any conflicts found between
 * deletion of items and modification of items. If a conflict is found it is
 * handled by replacing the deleted item with the modified item.
 */
void ResolveDelModConflicts(SyncIDListType &zDelTodoItemIDList,
			    TodoItemType::List &zModTodoItemList,
			    TodoItemType::List &zAddTodoItemList,
			    SyncIDListType &dDelTodoItemIDList,
			    TodoItemType::List &dModTodoItemList,
			    TodoItemType::List &dAddTodoItemList) {

    //TodoItemType::List::iterator zIter;
    SyncIDListType::iterator zSyncIDIter;
    TodoItemType::List::iterator fndItemIter;
    SyncIDListType::iterator tmpIDIter;

    // Now in this case in this function I am resolving any conflicts between
    // deletion and modification. This is the only type of conflict that
    // should ever be created other than a deletion on both ends. Since
    // deletion is such a harsh action and can cause data loss, which can be
    // very bad in certain situations, the choice for resolving such conflicts
    // is that a modification wins in conflict with a delete. Hence, if one
    // deletes an item from their Desktop PIM software and modifies that same
    // item on the Zaurus. When the next synchronization is performed the item
    // should be recreated on their Desktop PIM software with the updated
    // information.

    std::cout << "Entered the ResolveDelmodConflicts() function.\n";

    zSyncIDIter = zDelTodoItemIDList.begin();
    while (zSyncIDIter != zDelTodoItemIDList.end()) {

	// First I want to check if there are any conflicts with the sync ID
	// of what ever the current deleted item is.
	fndItemIter = FindSyncID(dModTodoItemList, (*zSyncIDIter));
	if (fndItemIter != dModTodoItemList.end()) {
	    std::cout << "Found a matching sync ID.\n";
	    // If I have gotten to this point I know that there is a conflict
	    // and that fndItemIter is the iterator for the item in conflict.

	    // The first thing I want to do in this case is Add the
	    // Modification of the conflicting item to the appropriate add
	    // list so that it will be added.
	    dAddTodoItemList.push_front(*fndItemIter);

	    std::cout << "Added the item to the proper add list.\n";

	    // The second thing I want to do is remove the item from the
	    // modification list that it was found in so that after the item
	    // it is not needlessly modified with the same data.
	    dModTodoItemList.erase(fndItemIter);

	    std::cout << "Erased the item from the proper Mod list.\n";

	    // The third thing I want to do is remove the item from the
	    // deletion list so that it is not deleted after it has been
	    // added again.
	    if (zSyncIDIter == zDelTodoItemIDList.begin()) {
		zDelTodoItemIDList.erase(zSyncIDIter);
		zSyncIDIter = zDelTodoItemIDList.begin();
	    } else {
		tmpIDIter = zSyncIDIter;
		tmpIDIter--;
		zDelTodoItemIDList.erase(zSyncIDIter);
		zSyncIDIter = tmpIDIter;
		zSyncIDIter++;
	    }

	    std::cout << "Erased the sync id from the deleted list.\n";
	} else {
	    // If I have gotten to this point I know that there was no
	    // conflict and I want to go to the next item in the list and
	    // check for a conflict.
	    zSyncIDIter++;
	}
    }

    // Now that the conflicts have been resolved in the above case I handle
    // the conflicts in the case where the Desktop PIM application has deleted
    // an item and the Zaurus has modified an item.
    zSyncIDIter = dDelTodoItemIDList.begin();
    while (zSyncIDIter != dDelTodoItemIDList.end()) {

	// First I want to check if there are any conflicts with the sync ID
	// of what ever the current deleted item is.
	fndItemIter = FindSyncID(zModTodoItemList, (*zSyncIDIter));
	if (fndItemIter != zModTodoItemList.end()) {
	    // If I have gotten to this point I know that there is a conflict
	    // and that fndItemIter is the iterator for the item in conflict.

	    // The first thing I want to do in this case is Add the
	    // Modification of the conflicting item to the appropriate add
	    // list so that it will be added.
	    zAddTodoItemList.push_front(*fndItemIter);

	    // The second thing I want to do is remove the item from the
	    // modification list that it was found in so that after the item
	    // it is not needlessly modified with the same data.
	    zModTodoItemList.erase(fndItemIter);

	    // The third thing I want to do is remove the item from the
	    // deletion list so that it is not deleted after it has been
	    // added again.
	    if (zSyncIDIter == dDelTodoItemIDList.begin()) {
		dDelTodoItemIDList.erase(zSyncIDIter);
		zSyncIDIter = dDelTodoItemIDList.begin();
	    } else {
		tmpIDIter = zSyncIDIter;
		tmpIDIter--;
		dDelTodoItemIDList.erase(zSyncIDIter);
		zSyncIDIter = tmpIDIter;
		zSyncIDIter++;
	    }
	} else {
	    // If I have gotten to this point I know that there was no
	    // conflict and I want to go to the next item in the list and
	    // check for a conflict.
	    zSyncIDIter++;
	}
    }

    std::cout << "Exited the ResolveDelmodConflicts() function.\n";

}

/**
 * Resolve Modification/Modification conflicts.
 *
 * This function searches for and resolves any conflicts found between
 * modification of items and modification of items. If a conflict is found it
 * is handled using the conflict winner specified config option.
 */
void ResolveModModConflicts(TodoItemType::List &zModTodoItemList,
			    TodoItemType::List &dModTodoItemList,
			    TodoItemType::List &zAddTodoItemList,
			    TodoItemType::List &dAddTodoItemList,
			    unsigned short int conflict_winner) {

    std::cout << "Entered the ResolveModModConflicts() function.\n";

    TodoItemType::List::iterator fndItemIter;
    TodoItemType::List::iterator iter;
    TodoItemType::List::iterator tmpIter;

    iter = zModTodoItemList.begin();
    while (iter != zModTodoItemList.end()) {

	// First I want to check if there are any conflicts with the sync ID
	// of what ever the current modified item is.
	fndItemIter = FindSyncID(dModTodoItemList, (*iter).GetSyncID());
	if (fndItemIter != dModTodoItemList.end()) {
	    std::cout << "Found a matching sync ID.\n";
	    // If I have gotten to this point I know that there is a conflict
	    // and that fndItemIter is the iterator for the item in conflict.

	    if (conflict_winner == CONF_WIN_Z) {
		dModTodoItemList.erase(fndItemIter);
	    } else if (conflict_winner == CONF_WIN_D) {
		if (iter == zModTodoItemList.begin()) {
		    zModTodoItemList.erase(iter);
		    iter = zModTodoItemList.begin();
		} else {
		    tmpIter = iter;
		    tmpIter--;
		    zModTodoItemList.erase(iter);
		    iter = tmpIter;
		    iter++;
		}
	    } else if (conflict_winner == CONF_WIN_B) {
		dAddTodoItemList.push_front((*fndItemIter));
		dModTodoItemList.erase(fndItemIter);

		zAddTodoItemList.push_front((*iter));
		if (iter == zModTodoItemList.begin()) {
		    zModTodoItemList.erase(iter);
		    iter = zModTodoItemList.begin();
		} else {
		    tmpIter = iter;
		    tmpIter--;
		    zModTodoItemList.erase(iter);
		    iter = tmpIter;
		    iter++;
		}
	    }
	} else {
	    // If I have gotten to this point I know that there was no
	    // conflict and I want to go to the next item in the list and
	    // check for a conflict.
	    iter++;
	}
    }

    std::cout << "Exited the ResolveModModConflicts() function.\n";

}


/**
 * Perform a Todo synchronization.
 *
 * Perform a Todo synchronization.
 */
int PerformTodoSync(unsigned short int confWinner,
		    ConfigManagerType *pConfManager) {
    void *libHandle;
    create_t pCreateFunc;
    destroy_t pDestroyFunc;
    TodoPluginType *pTodoPlugin;
    char optVal[256];
    int retval;
    ZaurusType zaurus;

    // The following three lists exist to contain the new, mod, del item
    // information obtained from the Zaurus for conflict management and
    // synchronization.
    TodoItemType::List zNewTodoItemList;
    TodoItemType::List zModTodoItemList;
    SyncIDListType zDelTodoItemIDList;

    // The following three lists exist to contain the new, mod, del item
    // information obtain from the Desktop Todo Plugin for conflict management
    // and synchronization.
    TodoItemType::List dNewTodoItemList;
    TodoItemType::List dModTodoItemList;
    SyncIDListType dDelTodoItemIDList;

    TodoItemType::List mapIdList;

    SyncIDListType::iterator zSyncIDIter;
    time_t lastTimeSynced;
    TodoItemType::List::iterator zIter;
    TodoItemType curItem;

    // Normally the path to the selected Todo synchronization plugin would be
    // loaded from the configuration file. However, the configuration file has
    // not been implemented into this application yet. Hence, it is hard coded
    // into the code right now for development of the first plugin.
    retval = pConfManager->GetValue("todo_plugin_path", optVal, 256);
    if (retval != 0) {
	std::cout << "Error: No todo_plugin_path entry found in the" \
	    " .zync.conf configuration file.\n";
	return 1;
    }

    // Open the plugin and load the creation and destroy symbols.
    libHandle = dlopen(optVal, RTLD_LAZY);
    if (!libHandle) {
	std::cout << "zync: Failed to open the KOrganizer Todo Plugin.\n";
	std::cout << dlerror() << std::endl;
	return 2;
    }

    pCreateFunc = (create_t)dlsym(libHandle, "create");
    if (!pCreateFunc) {
	std::cout << "zync: Failed to find the create symbol in plugin.\n";
	std::cout << dlerror() << std::endl;
	dlclose(libHandle);
	return 3;
    }

    pDestroyFunc = (destroy_t)dlsym(libHandle, "destroy");
    if (!pDestroyFunc) {
	std::cout << "zync: Failed to find the destroy symbol in plugin.\n";
	std::cout << dlerror() << std::endl;
	dlclose(libHandle);
	return 4;
    }

    // Create an instance of the plugin.
    pTodoPlugin = pCreateFunc();
    if (!pTodoPlugin) {
	std::cout << "zync: Failed to create intance of the plugin object.\n";
	dlclose(libHandle);
	return 5;
    }

    // At this point I have opened the plugin and have loaded the creation and
    // destroy methods. This means I have the capability to create an instance
    // of the TodoPluginType class and destroy it. Hence, I know that all the
    // class functions have been implemented (because they are pure virtual)
    // and assume that they work correctly. It should be fine to perform the
    // synchronization now.

    // Display the general plugin information.
    std::cout << "Plugin Information\n";
    std::cout << "------------------\n";
    std::cout << "Plugin Name: " << pTodoPlugin->GetPluginName() + "\n";
    std::cout << "Plugin Version: " << pTodoPlugin->GetPluginVersion() + "\n";
    std::cout << "Plugin Author: " << pTodoPlugin->GetPluginAuthor() + "\n";
    std::cout << "Plugin Desc: " << pTodoPlugin->GetPluginDescription() + "\n";
    std::cout << std::endl;

    // Attempt to initialize the plugin.
    retval = pTodoPlugin->Initialize();
    if (retval != 0) {
	std::cout << "zync: Failed to Initialize To-Do Plugin (" << retval;
	std::cout << ")." << std::endl;
	pDestroyFunc(pTodoPlugin);
	dlclose(libHandle);
	return 6;
    }

    // Tell the Desktop synchronization server to listen for a connection from
    // a Zaurus.
    zaurus.ListenConnection();

    // Set the type of synchronization to the value that represents the To-Do
    // synchronization type.
    zaurus.SetSyncType(0x06);

    // Perform the actual todo synchronization.
    if(zaurus.RequiresPassword()) {
	// This first step in the authentication process is to obtain the
	// password to send to the Zaurus for authentication. In this case the
	// password should be in the config file. Hence, I try and obtain the
	// passcode from the config file.
	retval = pConfManager->GetValue("passcode", optVal, 256);
	if (retval != 0) {
	    std::cout << "zync: Failed to obtain password from config.\n";
	    zaurus.FinishSync();
	    pDestroyFunc(pTodoPlugin);
	    dlclose(libHandle);
	    return 7;
	}

	// The second phase of the Authentication process is authenticating
	// the password with the Zaurus. Hence, I attempt to authenticate the
	// obtained password.
	if (zaurus.AuthenticatePassword(optVal) != 0) {
	    std::cout << "zync: Failed to authenticate password.\n";
	    pDestroyFunc(pTodoPlugin);
	    dlclose(libHandle);
	    return 8;
	}
    }

    lastTimeSynced = zaurus.GetLastTimeSynced();

    // If I move the KOrgTodoPlugin calls here I don't get the segfault with
    // them. However, if I leave them down below then I still get the
    // segfault.

    // Check if the Full Sync is required then try and clear the log, reset
    // the log and exit with out saving sync state. Hence, all items should be
    // seen as new items the next time one syncs (we hope).
    if (zaurus.RequiresFullSync()) {
	zaurus.ResetSyncLog();
	//zaurus.SetNextSyncAnch();
	zaurus.SendRSSMsg();

	// Normally I would not have the full synchronization finish
	// here. But, until I get that working I am leaving it as a two step
	// operation.
	zaurus.FinishSync();
	pDestroyFunc(pTodoPlugin);
	dlclose(libHandle);
	std::cout << "The synchronization state has been reset.\n";
	std::cout << "Please sync again to now perform an actual sync.\n";
	return 0;
    } else {
	zaurus.SetNextSyncAnch();
    }

    // Obtain the changes from the Zaurus.
    if (zaurus.GetAllTodoSyncItems(zNewTodoItemList, zModTodoItemList,
				   zDelTodoItemIDList) != 0) {
	std::cout << "Failed to get all sync items.\n";
    }

    // Display the Zaurus changes information.
    std::cout << "Zaurus Changes\n";
    std::cout << "--------------\n";
    std::cout << "Found " << zNewTodoItemList.size() << " new items on" \
	" the Zaurus.\n";
    std::cout << "Found " << zModTodoItemList.size() << " modified items" \
	" on the Zaurus.\n";
    std::cout << "Found " << zDelTodoItemIDList.size() << " items deleted" \
	" from the Zaurus.\n";

    // Obtain the changes from the Desktop PIM application todo plugin.
    if (zaurus.RequiresFullSync()) {
	std::cout << "Attemting to get all plugin todo items.\n";
	dNewTodoItemList = pTodoPlugin->GetAllTodoItems();
	std::cout << "Obtained all plugin todo items.\n";
    } else {
	dNewTodoItemList = pTodoPlugin->GetNewTodoItems(lastTimeSynced);
	dModTodoItemList = pTodoPlugin->GetModTodoItems(lastTimeSynced);
	dDelTodoItemIDList = pTodoPlugin->GetDelTodoItemIDs(lastTimeSynced);
    }

    // Display the To-Do plugin changes.
    std::cout << "To-Do Plugin Changes\n";
    std::cout << "--------------------\n";
    std::cout << "Found " << dNewTodoItemList.size() << " new items on" \
	" the To-Do plugin.\n";
    std::cout << "Found " << dModTodoItemList.size() << " modified items" \
	" on the To-Do plugin.\n";
    std::cout << "Found " << dDelTodoItemIDList.size() << " items deleted" \
	" from the To-Do plugin.\n";

    if (!zaurus.RequiresFullSync()) {
	// Compare the todo item lists for conflicts and resolve the conflicts.
	ResolveDelModConflicts(zDelTodoItemIDList, zModTodoItemList,
			       zNewTodoItemList, dDelTodoItemIDList,
			       dModTodoItemList, dNewTodoItemList);

	ResolveModModConflicts(zModTodoItemList, dModTodoItemList,
			       zNewTodoItemList, dNewTodoItemList,
			       confWinner);
    }

    // I then want to do what I just did above but for the opposite
    // component. Once I do that the deletion conflicts should all be handled.

    if (!zaurus.RequiresFullSync()) {
	// Perform the Desktop side of the synchronization.
	std::cout << "Plugin About to Del Todo Items.\n";
	pTodoPlugin->DelTodoItems(zDelTodoItemIDList);
	std::cout << "Plugin Deleted Todo Items.\n";
	std::cout << "Plugin About to Mod Todo Items.\n";
	pTodoPlugin->ModTodoItems(zModTodoItemList);
	std::cout << "Plugin Modified Todo Items.\n";
	std::cout << "Plugin About to Add Todo Items.\n";
	pTodoPlugin->AddTodoItems(zNewTodoItemList);
	std::cout << "Plugin Added Todo Items.\n";

	// Perform the Zaurus side of the synchronization.
	zaurus.DelTodoItems(dDelTodoItemIDList);
	zaurus.ModTodoItems(dModTodoItemList);
	mapIdList = zaurus.AddTodoItems(dNewTodoItemList);

	// Map the proper IDs.
	pTodoPlugin->MapItemIDs(mapIdList);
    } else {
	std::cout << "Attempting to add todo items to the plugin.\n";
	pTodoPlugin->AddTodoItems(zNewTodoItemList);
	std::cout << "Added the todo items to the plugin.\n";

	std::cout << "Attempting to add todo items to the Zaurus.\n";
	mapIdList = zaurus.AddTodoItems(dNewTodoItemList);
	std::cout << "Added the todo items to the Zaurus.\n";

	std::cout << "Attempting to Map Item IDs.\n";
	pTodoPlugin->MapItemIDs(mapIdList);
	std::cout << "Mapped item IDs.\n";
    }

    zaurus.TerminateSync();

    /////////////////////////////////////////////////////////////////////////
    // The code below needs to stay to handle destruction of the plugin and
    // closing of the shared object that is the plugin.
    /////////////////////////////////////////////////////////////////////////

    retval = pTodoPlugin->CleanUp();
    if (retval != 0) {
	std::cout << "ERROR: Failed to Clean up To-Do Plugin (" << retval;
	std::cout << ")." << std::endl;
	pDestroyFunc(pTodoPlugin);
	dlclose(libHandle);
	return 9;
    }

    // Destroy the plugin object and close the plugin.
    pDestroyFunc(pTodoPlugin);
    dlclose(libHandle);

    return 0;
}

["KOrgTodoPlugin.hh" (text/plain)]

/*
 * Copyright 2004, 2005 Andrew De Ponte
 * 
 * This file is part of zsrep.
 * 
 * zsrep is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 * 
 * zsrep is distributed in the hopes that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with zsrep; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

/**
 * @file KOrgTodoPlugin.hh
 * @brief A specifications file for an object that is a TodoPlugin.
 * @author Andrew De Ponte
 *
 * A specifications file for an object that is a TodoPlugin for the
 * ZaurusSyncer application. This plugin is designed to allow the ZaurusSyncer 
 * application to sync with the KOrganizer's Todo list.
 */

#ifndef KORGTODOPLUGIN_H
#define KORGTODOPLUGIN_H

// Plugin Includes
#include <zync/TodoPluginType.hh>

// KOrganizer Includes
#include <qstring.h>
#include <qdatetime.h>
#include <libkcal/calendarlocal.h>
#include <libkcal/todo.h>

#include <iostream>
#include <fstream>

// Config File Includes
#include "ConfigManagerType.h"
#include <stdlib.h>
#include <stdio.h>

/**
 * @class KOrgTodoPlugin
 * @brief A type existing as a plugin to sync with KOrganizer's Todo list.
 *
 * The KOrgTodoPlugin class is the implementation of a plugin which allows for
 * the ZaurusSyncer application to synchronize its Todo list with the Todo
 * list stored in KOrganizer.
 */
class KOrgTodoPlugin : public TodoPluginType {
public:
    KOrgTodoPlugin(void);

    int Initialize(void);
    int CleanUp(void);

    TodoItemType::List GetAllTodoItems(void);
    TodoItemType::List GetNewTodoItems(time_t lastTimeSynced);
    TodoItemType::List GetModTodoItems(time_t lastTimeSynced);
    SyncIDListType GetDelTodoItemIDs(time_t lastTimeSynced);
    int AddTodoItems(TodoItemType::List todoItems);
    int ModTodoItems(TodoItemType::List todoItems);
    int DelTodoItems(SyncIDListType todoItemIDs);
    int MapItemIDs(TodoItemType::List todoItems);

    std::string GetPluginDescription(void) const;
    std::string GetPluginName(void) const;
    std::string GetPluginAuthor(void) const;
    std::string GetPluginVersion(void) const;
private:
    int GetAllTodoSyncItems(time_t lastTimeSynced,
			    TodoItemType::List &newItemList,
			    TodoItemType::List &modItemList,
			    SyncIDListType &delItemIdList);
    int SaveSyncIDLog(void);
    TodoItemType ConvKCalTodo(KCal::Todo *pKcalTodo);
    KCal::Todo *ConvTodoItemType(TodoItemType *pTodoItem);
    void UpdateKCalTodoItem(KCal::Todo *pKCalTodo, TodoItemType todoItem);
    time_t ConvQDateTime(QDateTime dateTime);
    KCal::CalendarLocal calendar;
    QString qCalPath;
    std::string homeDir;
    bool openedCalFlag;
    bool openedConfFlag;

    bool obtainedSyncLists;
    TodoItemType::List newTodoItemList;
    TodoItemType::List modTodoItemList;
    SyncIDListType delTodoItemIdList;
};

#endif

["KOrgTodoPlugin.cc" (text/x-c++)]

/*
 * Copyright 2004, 2005 Andrew De Ponte
 * 
 * This file is part of zsrep.
 * 
 * zsrep is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 * 
 * zsrep is distributed in the hopes that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with zsrep; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

/**
 * @file KOrgTodoPlugin.cc
 * @brief An implementation file for an object that is a TodoPlugin.
 * @author Andrew De Ponte
 *
 * An implementation file for an object that is a TodoPlugin for the
 * ZaurusSyncer application. This plugin is designed to allow the ZaurusSyncer
 * application to sync with the KOrganizer's Todo list.
 */

#include "KOrgTodoPlugin.hh"

TodoPluginType *create() {
    return (TodoPluginType *)new KOrgTodoPlugin;
}

void destroy(TodoPluginType *pTodoPlugin) {
    delete pTodoPlugin;
}

/**
 * Construct a default KOrgTodoPlugin object.
 *
 * Construct a defualt KOrgTodoPlugin object with all the basic
 * initialization.
 */
KOrgTodoPlugin::KOrgTodoPlugin(void) {
    openedCalFlag = false;
    openedConfFlag = true;
    obtainedSyncLists = false;
}

/**
 * Initialize the KOrgTodoPlugin object.
 *
 * Initialize the KOrgTodoPlugin object by loading in its configuration from
 * the config file and preparing it for synchronization.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully initialized the KOrgTodo plugin.
 * @retval 1 Failed to obtain the value of the HOME environment variable.
 * @retval 2 Failed to open the plugin's associated KOrg calendar file.
 */
int KOrgTodoPlugin::Initialize(void) {
    char *pEnvVarVal;
    std::string confPath;
    std::string calPath;
    int retval;
    char optVal[256];
    ConfigManagerType confManager;

    // Obtain the value of the HOME environment variable and build the path to
    // the config file so that it can be loaded.
    pEnvVarVal = getenv("HOME");
    if (!pEnvVarVal) {
	std::cout << "KOrgTodoPlugin: Error: Failed to obtain the HOME " \
	    "environment variable.\n";
	return 1;
    }

    homeDir.assign(pEnvVarVal);
    confPath.assign(pEnvVarVal);
    confPath.append("/.KOrgTodoPlugin.conf");

    // Now I attempt to open and load the config file.
    retval = confManager.Open((char *)confPath.c_str());
    if (retval != 0) {
	if (retval == -1) {
	    std::cout << "KOrgTodoPlugin: Error: Failed to open " << confPath
		      << " for reading.\n";
	} else if (retval == -2) {
	    std::cout << "KOrgTodoPlugin: Error: Failed to find an equals" \
		" on atleast one non comment line in the config file. These" \
		" lines of the config file have been ignored and the config" \
		" file has been loaded. Fix your config file, it is probably" \
		" a typo.\n";
	} else {
	    std::cout << "KOrgTodoPlugin: Error: An unhandled error occured" \
		" while trying to open the config file.\n";
	}
	openedConfFlag = false;
    }

    // Here I attempt to load the path to the calendar file from the config.
    if (openedConfFlag) {
	retval = confManager.GetValue("korg_cal_path", optVal, 256);
	if (retval == 0) {
	    calPath.assign(optVal);
	} else {
	    calPath.assign(pEnvVarVal);
	    calPath.append("/.kde/share/apps/korganizer/std.ics");
	    std::cout << "KOrgTodoPlugin: Warning: Failed to find an item" \
		" with the title " \
		"(korg_cal_path) in the config file (" << confPath << ")." \
		" Using the default value (" << calPath << ").\n";
	}
    } else {
	calPath.assign(pEnvVarVal);
	calPath.append("/.kde/share/apps/korganizer/std.ics");
	std::cout << "KOrgTodoPlugin: Warning: The above described error " \
	    " states that there was a failure in opening the config" \
	    " file (" << confPath << "). Due, to this failure the KOrganizer" \
	    " Calendar path is now assumed to be (" << calPath << ").\n";
    }

    // Load the file located at calPath into the calendar object.
    qCalPath = calPath;
    if (calendar.load(qCalPath)) {
	openedCalFlag = true;
    } else {
	std::cout << "KOrgTodoPlugin: Error: Failed to load the KOrganizer" \
	    " Calendar file (" << calPath << ")." \
	    " Please edit the config file in your home directory, or" \
	    " the permisions on the calendar file to fix this problem.\n";
	return 2;
    }

    return 0;
}

/**
 * Clean up the KOrgTodoPlugin instance.
 *
 * Clean up the KOrgTodoPlugin after synchronization has been performed.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully cleaned up after synchronization.
 * @retval 1 Failed to save sync ID log.
 * @retval 2 Failed to save Calendar file.
 */
int KOrgTodoPlugin::CleanUp(void) {
    int retval = 0;

    // Here, I try to save the synchronization ID log so that the next time I
    // a synchronization is performed I can load it and determine the sync IDs
    // of the items which have been deleted since the last synchronization.
    retval = SaveSyncIDLog();
    if (retval != 0) {
	std::cout << "KOrgTodoPlugin: Error: Failed to save sync ID log (";
	std::cout << retval << ")." << std::endl;
	retval = 1;
    }

    // Here I attempt to save and close the Calendar file.
    if (openedCalFlag) {
	if (!calendar.save(qCalPath)) {
	    std::cout << "KOrgTodoPlugin: Error: Failed to save calendar. ";
	    std::cout << "This means that your synchronization on the ";
	    std::cout << "Desktop side didn't happen.\n";
	    retval = 2;
	}
	calendar.close();
    }

    return retval;
}

/**
 * Get all the Todo items.
 *
 * Get all the Todo items existing within the KOrganizer.
 * @return A list of all the Todo Items.
 */
TodoItemType::List KOrgTodoPlugin::GetAllTodoItems(void) {
    std::cout << "Entered the GetAllTodoItems() function.\n";
    TodoItemType::List todoItemList;
    KCal::Todo::List kcalTodoList;
    KCal::Todo::List::iterator kcalIt;
    TodoItemType newItem;
    std::cout << "Created all function scoped variables.\n";

    // Obtain a list of all the Todo items within the KCal object.
    kcalTodoList = calendar.rawTodos();

    std::cout << "Obtained todo list from calendar.\n";

    kcalIt = kcalTodoList.begin();

    std::cout << "Got the beginning of the list.\n";

    // Here I handle the creation of the modified and new item lists. I do so
    // by iterating through the todo items in the calendar and comparing their
    // time of creation and time of last modification to the last time of
    // synchronization to see if they are newer, or newly modified. Then given
    // the case I add the items to the proper list so that it may be returned
    // later.
    if (!kcalTodoList.empty()) {
	std::cout << "Calendar list is not empty.\n";
	for (kcalIt = kcalTodoList.begin(); kcalIt != kcalTodoList.end();
	     ++kcalIt)
	{
	    std::cout << "Setting pKcalTodo to the item pointer.\n";
	    KCal::Todo *pKcalTodo = *kcalIt;
	    std::cout << "Set the pKcalTodo item.\n";
	    newItem = ConvKCalTodo(pKcalTodo);
	    todoItemList.push_front(newItem);
	}
    }

    std::cout << "Exiting the GetAllTodoItems() function.\n";

    return todoItemList;
}

/**
 * Get the new Todo items.
 *
 * Get the Todo items that are newer than the last time of synchronized.
 * @param lastTimeSynced last time synchronized, as seconds since Epoch.
 * @return List of the Todo items which are new (created after the last
 * synchronization).
 */
TodoItemType::List KOrgTodoPlugin::GetNewTodoItems(time_t lastTimeSynced) {
    int retval;

    if (obtainedSyncLists)
	return newTodoItemList;
    else {
	std::cout << "GetNewTodoItems: Called GetAllTodoSyncItems.\n";
	retval = GetAllTodoSyncItems(lastTimeSynced, newTodoItemList,
				     modTodoItemList, delTodoItemIdList);
	std::cout << "GetNewTodoItems: GetAllTodoSyncItems returned.\n";
    }

    return newTodoItemList;
}

/**
 * Get the modified Todo items.
 *
 * Obtain the Todo items that were modified after the last synchronization.
 * @param lastTimeSynced last time synchronized, as seconds since Epoch.
 * @return List of the Todo items that have been modified since the last
 * synchronization.
 */
TodoItemType::List KOrgTodoPlugin::GetModTodoItems(time_t lastTimeSynced) {
    int retval;

    if (obtainedSyncLists)
	return modTodoItemList;
    else {
	retval = GetAllTodoSyncItems(lastTimeSynced, newTodoItemList,
				     modTodoItemList, delTodoItemIdList);
    }

    return modTodoItemList;
}

/**
 * Get the deleted Todo Item IDs.
 * 
 * Obtain the IDs of the Todo items that were deleted some point after the
 * last synchronization.
 * @param lastTimeSynced last time synchronized, as seconds since Epoch.
 * @return List of the Todo item IDs of items that have been deleted since the
 * last synchronization.
 */
SyncIDListType KOrgTodoPlugin::GetDelTodoItemIDs(time_t lastTimeSynced) {
    int retval;

    if (obtainedSyncLists)
	return delTodoItemIdList;
    else {
	retval = GetAllTodoSyncItems(lastTimeSynced, newTodoItemList,
				     modTodoItemList, delTodoItemIdList);
    }

    return delTodoItemIdList;
}

/**
 * Add the Todo items.
 *
 * Add the possed Todo items to the KOrganizer Todo list.
 * @param todoItems List of Todo items to add.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Successfully added the items to the KOrg Todo list.
 * @retval 1 Failed to allocate memory for one of the todo items.
 * @retval 2 Failed to add on of the todo items to the KOrg Todo list.
 * @retval 3 Failed to open the calendar file, hence, no adding.
 */
int KOrgTodoPlugin::AddTodoItems(TodoItemType::List todoItems) {
    TodoItemType::List::iterator it;
    TodoItemType curItem;
    KCal::Todo *pKCalTodo;
    bool tmpBool;

    std::cout << "Created the function scoped variables.\n";

    // If the calendar was not opened then I want to return notifying the
    // client application of it.
    if (!openedCalFlag)
	return 3;

    std::cout << "The calendar file is open.\n";

    for (it = todoItems.begin(); it != todoItems.end(); it++) {
	std::cout << "Began an iteration of one of the todo items.\n";
	curItem = *it;
	std::cout << "Set current item using iterator.\n";

	pKCalTodo = ConvTodoItemType(&curItem);
	std::cout << "Converted the TodoItem to KCal Todo Item.\n";
	if (pKCalTodo) {
	    std::cout << "Attempting to add todo to calendar.\n";
	    tmpBool = calendar.addTodo(pKCalTodo);
	    std::cout << "Added todo item to calendar.\n";
	    if (!tmpBool) {
		std::cerr << "Failed to add Todo item to calendar.\n";
		delete pKCalTodo;
		return 2;
	    }
	} else {
	    std::cerr << "Failed to allocate space for new todo item.\n";
	    return 1;
	}
    }

    return 0;
}

/**
 * Modify the Todo items.
 *
 * Modify the Todo items of the KOrganizer Todo list with the values contained
 * in the list of passed Todo items.
 * @param todoItems List of Todo items to use as new data for update.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Success.
 * @retval 3 Failed to open the calendar file, hence, no modifying.
 */
int KOrgTodoPlugin::ModTodoItems(TodoItemType::List todoItems) {
    TodoItemType::List::iterator it;
    TodoItemType curTodoItem;
    KCal::Todo::List kcalTodoList;
    KCal::Todo::List::iterator kcalIt;

    // If the calendar was not opened then I want to return notifying the
    // client application of it.
    if (!openedCalFlag)
	return 3;

    kcalTodoList = calendar.rawTodos();

    for (it = todoItems.begin(); it != todoItems.end(); it++) {
	curTodoItem = (*it);

	for (kcalIt = kcalTodoList.begin(); kcalIt != kcalTodoList.end();
	     kcalIt++)
	{
	    KCal::Todo *pKcalTodo = *kcalIt;

	    // If the SyncID mathes then I want to remove this item from the
	    // KOrganizer todo calendar file.
	    if ((unsigned long int)pKcalTodo->pilotId() == 
		curTodoItem.GetSyncID()) {
		// Perform the actual modification of the item now that it
		// has been found.
		UpdateKCalTodoItem(pKcalTodo, curTodoItem);
	    }
	}
    }

    return 0;
}

/**
 * Delete the Todo items.
 *
 * Delete the Todo items that have sync IDs contained in the passed list.
 * @param todoItemIDs The Todo Item IDs of the items to remove.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Success.
 * @retval 3 Failed to open the calendar file. Hence, no deleting.
 */
int KOrgTodoPlugin::DelTodoItems(SyncIDListType todoItemIDs) {
    std::cout << "Entered the DelTodoItems function.\n";
    SyncIDListType::iterator it;
    std::cout << "Created SyncIDListType iterator.\n";
    //KCal::Todo::List kcalTodoList;
    KCal::Todo::List *pKCalTodoList;
    pKCalTodoList = NULL;
    std::cout << "Created pKCalTodoList object.\n";
    KCal::Todo::List::iterator kcalIt;
    std::cout << "Created function scoped variables.\n";

    // This line causes it to segfault, I am not sure if eliminating this is
    //ok or not. Will have to test it.
    pKCalTodoList = new KCal::Todo::List;

    std::cout << "Attempting to check for opened calendar file.\n";
    // If the calendar was not opened then I want to return notifying the
    // client application of it.
    if (!openedCalFlag)
	return 3;
    std::cout << "Checked for open calendar file.\n";

    std::cout << "Obtaining KOrg Todo List.\n";
    (*pKCalTodoList) = calendar.rawTodos();
    std::cout << "Obtained KOrg Todo List.\n";

    if ((!todoItemIDs.empty()) && (!pKCalTodoList->empty())) {
	std::cout << "Both lists are NOT empty.\n";
	for (it = todoItemIDs.begin(); it != todoItemIDs.end(); it++) {
	    for (kcalIt = pKCalTodoList->begin();
		 kcalIt != pKCalTodoList->end();
		 kcalIt++)
	    {
		KCal::Todo *pKcalTodo = *kcalIt;

		// If the SyncID mathes then I want to remove this item from
		// the KOrganizer todo calendar file.
		if ((unsigned long int)pKcalTodo->pilotId() == (*it)) {
		    calendar.deleteTodo(pKcalTodo);
		}
	    }
	}
    }

    return 0;
}

/**
 * Map the item IDs.
 *
 * Map the unique identifiers between the Zaurus and KOrganizer.
 * @return An integer representing sucess (zero) or failure (non-zero).
 */
int KOrgTodoPlugin::MapItemIDs(TodoItemType::List todoItems) {
    TodoItemType::List::iterator it;
    TodoItemType curTodoItem;
    QString actAppId;
    KCal::Todo *pKcalTodo;

    // If the calendar was not opened then I want to return notifying the
    // client application of it.
    if (!openedCalFlag)
	return 3;

    for (it = todoItems.begin(); it != todoItems.end(); it++) {
	curTodoItem = (*it);

	actAppId = curTodoItem.GetAppID().c_str();

	pKcalTodo = calendar.todo(actAppId);

	pKcalTodo->setPilotId(curTodoItem.GetSyncID());
    }

    return 0;

}

/**
 * Get the plugin description.
 *
 * Obtain the description of the plugin.
 * @return The description of the plugin.
 */
std::string KOrgTodoPlugin::GetPluginDescription(void) const {
    std::string descStr;
    descStr.assign("A plugin which provides the capability of " \
		   "synchronization of the Zaurus Todo PIM application " \
		   "with the KOrganizer PIM application's Todo list.");
    return descStr;
}

/**
 * Get the plugin name.
 *
 * Obtain the name of the plugin.
 * @return The name of the plugin.
 */
std::string KOrgTodoPlugin::GetPluginName(void) const {
    std::string nameStr;
    nameStr.assign("KOrganizer Todo Sync Plugin");
    return nameStr;
}

/**
 * Get the plugin author.
 *
 * Obtain the plugin's author.
 * @return The name of the author of the plugin.
 */
std::string KOrgTodoPlugin::GetPluginAuthor(void) const {
    std::string authorStr;
    authorStr.assign("Andrew De Ponte (cyphactor@socal.rr.com)");
    return authorStr;
}

/**
 * Get the plugin version.
 *
 * Obtain the plugin's version.
 * @return The version of the plugin.
 */
std::string KOrgTodoPlugin::GetPluginVersion(void) const {
    std::string versionStr;
    versionStr.assign("0.1.0");
    return versionStr;
}

/**
 * Obtain all the sync data from KOrganizer for the Todo synchronization.
 *
 * Obtain all the sync data from KOrganizer for the Todo synchronization. This
 * includes all the New, Modified, and Deleted Todo item information so that
 * the synchronization can be performed.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Success.
 * @retval 1 Failed because calendar file was never opened.
 * @retval 2 Failed to allocate memory for log file content.
 * @retval 3 Failed to save SyncID log.
 */
int KOrgTodoPlugin::GetAllTodoSyncItems(time_t lastTimeSynced,
					TodoItemType::List &newItemList,
					TodoItemType::List &modItemList,
					SyncIDListType &delItemIdList)
{
    KCal::Todo::List kcalTodoList;
    KCal::Todo::List::iterator kcalIt;
    QDateTime lastSynced;
    TodoItemType newItem;
    std::fstream fin, fout;
    bool foundSyncID = false;
    std::string tmpPath;
    unsigned long int numSyncIDs;
    unsigned long int *pSyncIDs;
    unsigned long int syncCount;

    tmpPath = homeDir;
    tmpPath.append("/.KOrgTodoPlugin.log");

    // If the calendar was never opened then return with no data so nothing is
    // synchronized.
    if (!openedCalFlag)
	return 1;

    lastSynced.setTime_t(lastTimeSynced);

    // Obtain a list of all the Todo items within the KCal object.
    kcalTodoList = calendar.rawTodos();

    std::cout << "GetAllTodoSyncItems: Got Raw Todos.\n";

    kcalTodoList.begin();

    std::cout << "Set the kcal iterator to the beginning of the list.\n";

    // Here I handle the creation of the modified and new item lists. I do so
    // by iterating through the todo items in the calendar and comparing their
    // time of creation and time of last modification to the last time of
    // synchronization to see if they are newer, or newly modified. Then given
    // the case I add the items to the proper list so that it may be returned
    // later.
    if (!kcalTodoList.empty()) {
	std::cout << "The kcalTodoList is not empty.\n";
	for (kcalIt = kcalTodoList.begin(); kcalIt != kcalTodoList.end();
	     ++kcalIt)
	{
	    std::cout << "Setting pKcalTodo to the item pointer.\n";
	    KCal::Todo *pKcalTodo = *kcalIt;
	    std::cout << "Set the pKcalTodo item.\n";
	    if (pKcalTodo->created() > lastSynced) {
		std::cout << "Created after lastSynced.\n";
		newItem = ConvKCalTodo(pKcalTodo);
		std::cout << "Converted KCal Todo.\n";
		newItemList.push_front(newItem);
		std::cout << "Added new item to list.\n";
	    } else if (pKcalTodo->lastModified() > lastSynced) {
		std::cout << "Modified after lastSynced.\n";
		newItem = ConvKCalTodo(pKcalTodo);
		std::cout << "Converted KCal Todo.\n";
		modItemList.push_front(newItem);
		std::cout << "Added modified item to list.\n";
	    }
	    std::cout << "GetAllTodoSyncItems: Ended one iteration.\n";
	}
	std::cout << "Exited for loop.\n";
    }

    std::cout << "GetAllTodoSyncItems: Created New and Mod lists.\n";

    // Here I handle the creation of the deletion list. The idea behind the
    // deletion list is that a list exist containing all the SyncIDs (UIDs) of
    // the items that have been removed from the calendar since the last time
    // of synchronization.
    // 
    // I do this by storeing a list of the SyncIDs contained within the
    // current list of Todo items to a hidden file in the executing users home
    // directory. The purpose for this is so that the next time they sync the
    // file can be loaded and if I fail to find any of those SyncIDs in the
    // current calendar Todo list then I know that, that I item has since been
    // removed.

    std::cout << "Attempting to open log.\n";
    std::cout << "tmpPath = " << tmpPath << std::endl;
    fin.open(tmpPath.c_str(), std::fstream::in);
    std::cout << "Finished open log call.\n";
    if (fin.is_open()) {

	std::cout << "Opened the log file.\n";

	// Obtain the number of sync ids contained within the log file so I
	// know how much memory to allocate and I know how many bytes to read
	// in.
	fin.read((char *)&numSyncIDs, 4);

	std::cout << "Read the num of sync ids in log.\n";

	// Dynamically allocate an array large enough to hold all the sync ids
	// stored in the log file.
	pSyncIDs = new unsigned long int[numSyncIDs];
	if (pSyncIDs == NULL)
	    return 2;

	// Load all the sync ids into the dynamically allocated array.
	for (syncCount = 0; syncCount < numSyncIDs; syncCount++) {
	    fin.read((char *)&pSyncIDs[syncCount], 4);
	}

	std::cout << "Read in sync ids.\n";

	// At this point the array should be filled with all of the sync
	// ids. Hence, I should be able to continue with the process.
	for (syncCount = 0; syncCount < numSyncIDs; syncCount++) {
	    foundSyncID = false;

	    if (!kcalTodoList.empty()) {
		std::cout << "KCal Todo list is not empty.\n";
		for (kcalIt = kcalTodoList.begin();
		     kcalIt != kcalTodoList.end();
		     kcalIt++)
		{
		    KCal::Todo *pKcalTodo = *kcalIt;
		    
		    if ((unsigned long int)pKcalTodo->pilotId() ==
			pSyncIDs[syncCount]) {
			foundSyncID = true;
		    }
		}
	    }

	    if (!foundSyncID) {
		std::cout << "sync id not found.\n";
		delItemIdList.push_front(pSyncIDs[syncCount]);
		std::cout << "Added to del item list.\n";
	    }
	}

	std::cout << "Finished sync id loop.\n";

	delete [] pSyncIDs;

	std::cout << "Freed sync id list memory.\n";
    }

    std::cout << "Attempting to close log file.\n";

    fin.close();

    std::cout << "Closed log file.\n";

    obtainedSyncLists = true;

    std::cout << "GetAllTodoSyncITems: Exiting function.\n";

    // Return in succes.
    return 0;
}

/**
 * Save the SyncID Log.
 *
 * Save the SyncID log (a log file containing a record of all the items
 * SyncIDs). This is used to save the current state of the SyncIDs of
 * KOrganizer's Todo list for access at a later point. Specifically so that it
 * can be used to check for removal of items for the purpose of generating the
 * deltoodItemIdList.
 * @return An integer representing success (zero) or failure (non-zero).
 * @retval 0 Success.
 * @retval 1 Failed to open the file for output.
 */
int KOrgTodoPlugin::SaveSyncIDLog(void) {
    std::string tmpPath = homeDir;
    std::fstream fout;
    unsigned long int numSyncIDs;
    unsigned long int tmpSyncID;
    KCal::Todo::List kcalTodoList;
    KCal::Todo::List::iterator kcalIt;

    tmpPath.append("/.KOrgTodoPlugin.log");

    // Obtain a list of all the Todo items within the KCal object.
    kcalTodoList = calendar.rawTodos();

    fout.open(tmpPath.c_str(), std::fstream::out);
    if (!fout.is_open())
	return 1;

    // Count the number of items that have a pilotId() (rather SyncID) greater
    // than zero. Write this value of the number SyncIDs.
    numSyncIDs = 0;
    for (kcalIt = kcalTodoList.begin(); kcalIt != kcalTodoList.end(); kcalIt++)
    {
	KCal::Todo *pKcalTodo = *kcalIt;
	if (pKcalTodo->pilotId() != 0)
	    numSyncIDs++;
    }
    fout.write((const char *)&numSyncIDs, 4);

    // Loop through the list of todo Items and write their values in series if
    // the value is larger than zero.
    for (kcalIt = kcalTodoList.begin(); kcalIt != kcalTodoList.end(); kcalIt++)
    {
	KCal::Todo *pKcalTodo = *kcalIt;
	tmpSyncID = pKcalTodo->pilotId();
	if (tmpSyncID != 0)
	    fout.write((const char *)&tmpSyncID, 4);
    }

    fout.close();

    return 0;
}

/**
 * Convert a KCal::Todo object into a common TodoItemType object.
 *
 * Convert a KCal::Todo object into a common TodoItemType object so that the
 * plugin interface can use the common format to synchronize the data.
 * @param pKcalTodo Pointer to the KCal::Todo object to convert.
 * @return A TodoItemType object containing the converted data.
 */
TodoItemType KOrgTodoPlugin::ConvKCalTodo(KCal::Todo *pKcalTodo) {
    TodoItemType todoItem;
    QCString appId;

    // Here I convert all the common data.

    // Set the attribute.
    todoItem.SetAttribute((unsigned char)0);

    // Set the created time to zero signifying that it has not been set.
    todoItem.SetCreatedTime(ConvQDateTime(pKcalTodo->created()));

    // Set the modified time to zero signifying that it has not been set.
    todoItem.SetModifiedTime(ConvQDateTime(pKcalTodo->lastModified()));

    // Set the sync id.
    todoItem.SetSyncID((unsigned long int)pKcalTodo->pilotId());

    // Set the application id.
    appId = pKcalTodo->uid().utf8();
    todoItem.SetAppID((std::string)appId);

    // Below I convert all the Todo specific data.

    // Set the item category. Now in this case the Zaurus Todo item only has
    // one category and in the KOrganizer Todo items they can have multiple
    // categories. In this case I have decided to only use the first category
    // of the KOrganizers categories list.
    QStringList kOrgCatList;
    QStringList::Iterator catIt;
    QCString category;

    kOrgCatList = pKcalTodo->categories();
    catIt = kOrgCatList.begin();
    category = (*catIt).utf8();
    todoItem.SetCategory((std::string)category);

    // Set Start Date
    if (pKcalTodo->hasStartDate()) {
	todoItem.SetStartDate(ConvQDateTime(pKcalTodo->dtStart()));
    } else {
	todoItem.SetStartDate(0);
    }

    // Set Due Date
    if (pKcalTodo->hasDueDate()) {
	todoItem.SetDueDate(ConvQDateTime(pKcalTodo->dtDue()));
    } else {
	todoItem.SetDueDate(0);
    }

    // Set Completed Date
    if (pKcalTodo->hasCompletedDate()) {
	todoItem.SetCompletedDate(ConvQDateTime(pKcalTodo->completed()));
    } else {
	todoItem.SetCompletedDate(0);
    }

    // Set the item progress status.
    if (pKcalTodo->isCompleted())
	todoItem.SetProgressStatus(0);
    else
	todoItem.SetProgressStatus(1);

    // Set the item priority. I got lucky in this case the KOrganizer todo
    // list uses the same range for priority as the Zaurus. All I had to do
    // was type cast the int into a unsigned char because the methods of
    // storage are different.
    todoItem.SetPriority((unsigned char)pKcalTodo->priority());

    // Set Description.
    QCString descStr;

    descStr = (pKcalTodo->summary()).utf8();
    todoItem.SetDescription((std::string)descStr);

    // Set Notes.
    QCString notesStr;

    notesStr = (pKcalTodo->description()).utf8();
    todoItem.SetNotes((std::string)notesStr);

    return todoItem;
}

/**
 * Convert a TodoItemType object into a KOrganizers KCal::Todo object.
 *
 * Convert a TodoItemType object into a KCal::Todo object so that the
 * the an item that was of type TodoItemType can now be added to the
 * KOrganizer Todo list. Note: This dynamically allocates the memory for the
 * new KCal::Todo item. Hence, if it is not added to the calender it needs to
 * be deallocated at some point or there will be a memory leak.
 * @param pTodoItem Pointer to the TodoItemType object to convert.
 * @return A pointer to the new KCal::Todo object.
 * @retval NULL Failed to allocate memory for the new object.
 */
KCal::Todo *KOrgTodoPlugin::ConvTodoItemType(TodoItemType *pTodoItem) {
    KCal::Todo *pKCalTodo;
    QDateTime tmpTime;
    QString tmpStr;
    QString uId;

    // Create the instance of the object for the list of todo items stored in
    // the calendar. If failed to allocate the memory for it then return NULL.
    pKCalTodo = new KCal::Todo;
    if (!pKCalTodo)
	return pKCalTodo;

    UpdateKCalTodoItem(pKCalTodo, *(pTodoItem));

    /*
    std::cout << "ConvTodoItemType\n";

    // Go through all the todo data items and convert them into a KCalTodo
    // data items. 

    // Set the created time.
    tmpTime.setTime_t(pTodoItem->GetCreatedTime());
    pKCalTodo->setCreated(tmpTime);

    // Set the modified time.
    tmpTime.setTime_t(pTodoItem->GetModifiedTime());
    pKCalTodo->setLastModified(tmpTime);

    // Set the sync ID (pilot id).
    pKCalTodo->setPilotId((int)pTodoItem->GetSyncID());

    // Now I convert the Todo specific data items.
    
    // Set the category.
    tmpStr = pTodoItem->GetCategory();
    pKCalTodo->setCategories(tmpStr);
    std::cout << "Category: " << tmpStr << std::endl;

    // Set the start date.
    if (pTodoItem->GetStartDate() != 0) {
	tmpTime.setTime_t(pTodoItem->GetStartDate());
	pKCalTodo->setDtStart(tmpTime);
	pKCalTodo->setHasStartDate(true);
    } else {
	pKCalTodo->setHasStartDate(false);
    }

    // Set the due date.
    if (pTodoItem->GetDueDate() != 0) {
	tmpTime.setTime_t(pTodoItem->GetDueDate());
	pKCalTodo->setDtDue(tmpTime);
	pKCalTodo->setHasDueDate(true);
    } else {
	pKCalTodo->setHasDueDate(false);
    }

    // Set the completed date.
    if (pTodoItem->GetCompletedDate() != 0) {
	tmpTime.setTime_t(pTodoItem->GetCompletedDate());
	pKCalTodo->setCompleted(tmpTime);
    }

    // Set the progress status.
    if (pTodoItem->GetProgressStatus() == 0) {
	pKCalTodo->setCompleted(true);
    } else {
	pKCalTodo->setCompleted(false);
    }

    // Set the priority.
    pKCalTodo->setPriority((int)pTodoItem->GetPriority());
    std::cout << "Priority: " << pTodoItem->GetPriority() << std::endl;

    // Set the description (KOrg Summary).
    tmpStr = pTodoItem->GetDescription();
    pKCalTodo->setSummary(tmpStr);

    // Set the notes (KOrg Description).
    tmpStr = pTodoItem->GetNotes();
    pKCalTodo->setDescription(tmpStr);
    */

    return pKCalTodo;
}

/**
 * Update a KCal TodoItem with values in TodoItemType object.
 *
 * This basically sets the values of the KCal Todo item to the proper values
 * based on the values of the TodoItemType object.
 * @param pKCalTodo Pointer to the KCal::Todo item to update.
 * @param todoItem The TodoItemType object to get data from for the update.
 */
void KOrgTodoPlugin::UpdateKCalTodoItem(KCal::Todo *pKCalTodo,
					TodoItemType todoItem) {
    QDateTime tmpTime;
    QString tmpStr;
    QString uId;
    TodoItemType *pTodoItem;

    struct tm brTime;
    char buff[256];
    time_t zTime;

    pTodoItem = &todoItem;

    // Go through all the todo data items and convert them into a KCalTodo
    // data items. 

    // Set the created time.
    tmpTime.setTime_t(pTodoItem->GetCreatedTime());
    pKCalTodo->setCreated(tmpTime);

    std::cout << "-=Created Time=-\n";
    zTime = pTodoItem->GetCreatedTime();
    localtime_r(&zTime, &brTime);
    asctime_r(&brTime, buff);
    std::cout << "pTodoItem: " << buff << std::endl;

    tmpStr = tmpTime.toString("dd-MM-yyyy hh:mm:ss");
    std::cout << "pKCalTodo: " << tmpStr << std::endl;

    // Set the modified time.
    tmpTime.setTime_t(pTodoItem->GetModifiedTime());
    pKCalTodo->setLastModified(tmpTime);

    // Set the sync ID (pilot id).
    pKCalTodo->setPilotId((int)pTodoItem->GetSyncID());

    // Now I convert the Todo specific data items.
    
    // Set the category.
    tmpStr = pTodoItem->GetCategory();
    pKCalTodo->setCategories(tmpStr);

    // Set the start date.
    if (pTodoItem->GetStartDate() != 0) {
	tmpTime.setTime_t(pTodoItem->GetStartDate());
	pKCalTodo->setDtStart(tmpTime);
	pKCalTodo->setHasStartDate(true);
    } else {
	pKCalTodo->setHasStartDate(false);
    }

    // Set the due date.
    if (pTodoItem->GetDueDate() != 0) {
	tmpTime.setTime_t(pTodoItem->GetDueDate());
	pKCalTodo->setDtDue(tmpTime);
	pKCalTodo->setHasDueDate(true);
    } else {
	pKCalTodo->setHasDueDate(false);
    }

    // Set the completed date.
    if (pTodoItem->GetCompletedDate() != 0) {
	tmpTime.setTime_t(pTodoItem->GetCompletedDate());
	pKCalTodo->setCompleted(tmpTime);
    }

    // Set the progress status.
    if (pTodoItem->GetProgressStatus() == 0) {
	pKCalTodo->setCompleted(true);
    } else {
	pKCalTodo->setCompleted(false);
    }

    // Set the priority.
    pKCalTodo->setPriority((int)pTodoItem->GetPriority());

    // Set the description (KOrg Summary).
    tmpStr = pTodoItem->GetDescription();
    pKCalTodo->setSummary(tmpStr);

    // Set the notes (KOrg Description).
    tmpStr = pTodoItem->GetNotes();
    pKCalTodo->setDescription(tmpStr);
}

/**
 * Convert a QDateTime object to secs since Epoch.
 *
 * Convert a QDateTime object into the number of seconds since Epoch (00:00:00
 * UTC, January 1, 1970), measured in seconds.
 * @param dateTime The QDateTime object to be converted to secs since Epoch.
 * @return The converted QDateTime in seconds since Epoch.
 */
time_t KOrgTodoPlugin::ConvQDateTime(QDateTime dateTime) {
    struct tm tmpTime;
    QDate date;
    QTime time;

    date = dateTime.date();
    time = dateTime.time();

    tmpTime.tm_sec = time.second();
    tmpTime.tm_min = time.minute();
    tmpTime.tm_hour = time.hour();
    tmpTime.tm_mday = date.day();
    tmpTime.tm_mon = (date.month() - 1);
    tmpTime.tm_year = (date.year() - 1900);
    tmpTime.tm_wday = (date.dayOfWeek() - 1);
    tmpTime.tm_yday = date.dayOfYear();
    tmpTime.tm_isdst = -1;

    return mktime(&tmpTime);
}


_______________________________________________
kde-pim mailing list
kde-pim@kde.org
https://mail.kde.org/mailman/listinfo/kde-pim
kde-pim home page at http://pim.kde.org/

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic