Notes Scheduling C/C++ API problems

Hi,

I’m trying to develop an application that extracts appointments from all user’s calendars. To get this working as effective as possible I’m trying to use the Scheduling API’s for Lotus Notes.

However, I have a couple of problems…

  1. The API calls for the scheduling API seems to be more or less undocumented. The only “real” information I’ve found is the sample application. The documentation lacks descriptions of what the different calls really do, it just says that it exist. Am I missing something here, or is the documentation really this bad?

  2. When calling SchRetrieve() with the option flag SCHRQST_EXTFORMAT, and then SchContainer_GetFirstSchedule() + Schedule_ExtractSchedList(), the list that is returned is NOT in the SCHED_ENTRY_EXT format, it is in SCHED_ENTRY format. According to the documentation it should be returned in SCHED_ENTRY_EXT format in R6 and later. I’m using 8.5 of both server and client, and I’m using the latest API’s - is there a way to get this working?

  3. When trying to call SchContainer_GetFirstSchedule() and then Schedule_GetFirstDetails(), en error is returned (1006), which inidicates that the scheduling object is invalid. However, I know it’s NOT invalid, and when using it in the call above it works as expected. Is this a bug, or am I doing something wrong?

I can attach my test code if that would help, but most of it is from the sample application with some minor modifications.

I would really appreciate any help I can get on this, I’m totally stumbling in the dark here, and I’ve found no help on Google… :frowning:

Regards,

Peter Olsson

Subject: Yes please post your code…

I’m presenting a session on C&S that ties into what you’re trying to do, so we may have some common purpose here. You should also have a look at my blog post about the session, and comment if you have any thoughts: http://www.lotusguru.com/lotusguru/LGBlog.nsf/d6plinks/20091213-7YPJPY

Subject: Some sample code…

Hi Kevin,

Thanks for your response. I’ve attached the code that I’m using today. There is a lot of unneeded code, it’s just used for testing right now, and I’ve copied most of it from the C API sample “schedule”, and then converted it into some basic C++ class…

Below I have two problems. After calling Schedule_ExtractSchedList() and parsing the result, it only returns SCHED_ENTRY, when it should return SCHED_ENTRY_EXT. The call to SchRetrieve() uses the option SCHRQST_EXTFORMAT to force this format.

The other problem is when I try to call Schedule_GetFirstDetails(), this call just returns error 1006 (invalid schedule object), even though it’s not invalid.

#include “stdafx.h”

#include

#include

#include

#include

#include “lncppapi.h”

#include “schedule.h”

#include <textlist.h>

using namespace std;

#define ERR_BUF_SIZE 512

#define STRING_LENGTH 256

class NotesConnector

{

public:

NotesConnector();

virtual ~NotesConnector();



int Init(int argc, char *argv[]);

STATUS LNPUBLIC LookupSchedules(char *start_date, char *end_date);

void PrintAPIError(STATUS api_error);



int ProgramStatus;

char AUser[STRING_LENGTH];

private:

void ProcessArguments(int argc, char **argv, char **Subject, char **DatabasePath, char **ServerName);

void PrintUsage();

void LNPUBLIC GetTDString(TIMEDATE *ptdModified, char *szTimedate);

STATUS LNPUBLIC FindEntries(char *pSchedList);



char *Subject;

char *DatabasePath;

char *ServerName;

LNNotesSession *Session;

LNDatabase Db;

};

NotesConnector::NotesConnector()

{

ProgramStatus = 0;

AUser[0] = '\0';



Session = new LNNotesSession(); 

}

NotesConnector::~NotesConnector()

{

delete Session;

Session = NULL;

}

void NotesConnector::PrintUsage()

{

cout << "Usage:" << endl;

cout << "\tmailscan subject [database [server]] " << endl << endl;

cout << "where:" << endl;

cout << "\tsubject   Message subject to search for" << endl;

cout << "\tdatabase  Optional pathname of database" << endl;

cout << "\tserver    Optional server name" << endl;

}

void NotesConnector::ProcessArguments(int argc, char **argv, char **Subject, char **DatabasePath, char **ServerName)

{

int curParam = 0;

int index;



// Set the default values for the arguments (that is, NULL!).

*Subject = NULL;              // The logic of the main program depends

*DatabasePath = NULL;         // on these pointers being set to NULL if

*ServerName = NULL;           // no user input is supplied!



// Check argument count

if (argc < 2)

	throw "No subject provided";

if (argc > 4)

	throw "Too many arguments";



// Scan the argument list, item by item.

for (index = 1; index < argc; index++)

{

	// Check for option switches.

	if (('-' == ((argv[index])[0])) || ('/' == ((argv[index])[0])))

	{

		throw "Unknown switch";

	}

	else

	{

		// String argument - assign to 

		switch (curParam)

		{

			case 0:

				*Subject = argv[index];

				break;



			case 1:

				*DatabasePath = argv[index];

				break;



			case 2:

				*ServerName = argv[index];

				break;



			default:

				throw "More than 3 arguments";

		}

		curParam++;

	}

}



if (((char *) NULL) == (*Subject))

	throw "No subject provided";



return;

}

int NotesConnector::Init(int argc, char *argv)

{

// Parse the argument list.

try

{

	ProcessArguments(argc, argv, &Subject, &DatabasePath, &ServerName);

}

catch (const char *pErrorMessage)

{

	cout << "\nError: " << pErrorMessage << endl << endl;

	PrintUsage();

	return 1;

}



NotesInit();



/*LNSetThrowAllErrors(TRUE);



if (Session->Init())//(argc, argv))

	throw "Unable to initialize the API";



if (DatabasePath == ((char *) NULL))

{

	// No database specified; get user's mail database.

	Session->GetMailDatabase(&Db);

}

else

{

	// Get the specified database.

	Session->GetDatabase(DatabasePath, &Db, ServerName);

}



// Open the database.

Db.Open();*/



return 0;

}

STATUS LNPUBLIC NotesConnector::LookupSchedules(char *start_date, char *end_date)

{

STATUS sError = NOERROR;

char *theSchedList;

TIMEDATE_PAIR theApptInterval;

TIMEDATE_PAIR pInterval;

VOID *list_ptr;                /* pointer to same list */

WORD list_size;                /* total size of list structure */

SCHEDULE *pSchedule;

UNID theApptUnid;

HCNTNR rethCntnr = NULLHANDLE; /* container handle */

DHANDLE list_handle;           /* handle to Domino and Notes list structure */

DHANDLE rethSchedList;

HCNTNROBJ hSchedObj, hSchedObj2;

HCNTNROBJ hMoreObj = NULLCNTNROBJ;

DWORD retdwSize;

WORD NumEntries;

SCHED_DETAIL_LIST *sched_details;



/* Create an empty text list data structure. */



sError = ListAllocate(0, 0, FALSE, &list_handle, &list_ptr, &list_size);

NumEntries = ListGetNumEntries(list_ptr, FALSE);

OSUnlockObject(list_handle);



if (sError)

	return sError;



/* Add the user we're looking for */

sError = ListAddEntry(list_handle, FALSE, &list_size, NumEntries, AUser, (WORD)strlen(AUser));



if (sError)

	return sError;



/* convert start time string to TIMEDATE format */

sError = ConvertTextToTIMEDATE(NULL, NULL, &start_date, (WORD)strlen(start_date), &theApptInterval.Lower);



if (sError)

	return sError;



/* convert end time string to TIMEDATE format */

sError = ConvertTextToTIMEDATE(NULL, NULL, &end_date, (WORD)strlen(end_date), &theApptInterval.Upper);



if (sError)

	goto Done;



list_ptr = OSLockObject(list_handle);



sError = SchRetrieve(&theApptUnid, NULL, SCHRQST_EACHPERSON|SCHRQST_FORCEREMOTE|SCHRQST_EXTFORMAT, &theApptInterval, (LIST *)list_ptr, &rethCntnr, NULL, NULL, NULL);



if (sError)

	goto Done;



/* Get the first schedule */

sError = SchContainer_GetFirstSchedule(rethCntnr, &hSchedObj, &pSchedule);



if (sError)

	goto Done;



sError = Schedule_ExtractSchedList(rethCntnr, hSchedObj, &pInterval, (unsigned long *)&retdwSize, &rethSchedList, &hMoreObj);



if (sError)

	goto Done;



//sError = Schedule_GetFirstDetails(rethCntnr, hSchedObj, &hSchedObj2, &sched_details);



//if (sError)

//	goto Done;



theSchedList = (char *)OSLockObject(rethSchedList);



/* Attempt to find schedule entry in list */

sError = FindEntries(theSchedList);



OSUnlockObject(rethSchedList);

Done:

OSUnlockObject(list_handle);

return sError;

}

STATUS LNPUBLIC NotesConnector::FindEntries(char *pSchedList)

{

int i;

int num;

SCHED_LIST SchedList;

SCHED_ENTRY_EXT SchedEntry;

WORD entry_size = 0;

STATUS sError=0;

char szTemp[MAXALPHATIMEDATE+1];

char szTimedate[MAXALPHATIMEDATE+1];

char szUpperTD[MAXALPHATIMEDATE+1];



/* Get the SCHED_LIST from the location specified by pSchedList */

memcpy((char*)&SchedList, pSchedList, sizeof(SCHED_LIST));

entry_size = SchedList.Spare;



if (entry_size == 0)

	entry_size = sizeof(SCHED_ENTRY);



pSchedList += sizeof(SCHED_LIST);



num = SchedList.NumEntries;



for (i=0; i < num; i++)

{

	if (entry_size > sizeof(SCHED_ENTRY_EXT))

		memcpy((char *)&SchedEntry, pSchedList, sizeof(SCHED_ENTRY_EXT));

	else

		memcpy((char *)&SchedEntry, pSchedList, entry_size);



	GetTDString(&(SchedEntry.Unid).Note, szTemp);

	GetTDString(&(SchedEntry).Interval.Lower, szTimedate);

	GetTDString(&(SchedEntry).Interval.Upper, szUpperTD);



	pSchedList += entry_size;

}



return(sError);

}

void LNPUBLIC NotesConnector::GetTDString(TIMEDATE *ptdModified, char *szTimedate)

{

WORD wLen;



ConvertTIMEDATEToText(NULL, NULL, ptdModified, szTimedate, MAXALPHATIMEDATE, &wLen);

szTimedate[wLen] = '\0';

}

void NotesConnector::PrintAPIError(STATUS api_error)

{

STATUS string_id = ERR(api_error);

char error_text[200];

WORD text_len;



/* Get the message for this Lotus C API for Domino and Notes error code from the resource string table. */



text_len = OSLoadString(NULLHANDLE, string_id, error_text, sizeof(error_text));



/* Print it. */



fprintf(stderr, "\n%s\n", error_text);

}

int main(int argc, char ** argv)

{

STATUS err;

NotesConnector connector;



try

{

	connector.ProgramStatus = connector.Init(argc, argv);



	if (connector.ProgramStatus)

		return connector.ProgramStatus;



	SECKFMUserInfo(KFM_ui_GetUserInfo, connector.AUser, NULL);



	err = connector.LookupSchedules("2009-10-01 00:00:00", "2010-01-01 00:00:00");



	if (err)

		connector.PrintAPIError(err);

}

catch (...)

{

	return 1;

}



return connector.ProgramStatus;

}