Dan Mosedale <[EMAIL PROTECTED]> writes:
<snip>
>> One thing i am having touble understanding
>> is the relationship between the LDAP RDF datasource
>> and the nsIAbDirectory and nsIAbCard interfaces
>> and implementations of.
>
>Thus far, I've been mostly focussing on the autocomplete stuff and so
>unfortunately don't have as good of a handle on the rest of the
>addressbook. Perhaps Candice Huang, whom I've CCed on this note, can
>comment a little more on the use of RDF inside the addressbook code
>and what directions she thinks might work...
>
>> We are in the process of writing an LDAP webtop
>> implementation using the LDAP XPCOM directly.
>> We can be clever in how we return data by
>> implementing a class which is an enumerator
>> and also an LDAP message listener there by
>> (i assume!) allowing the object to be passed
>> up through the singular directory data source
>> without blocking until all results have been
>> obtained (e.g. get child cards). The same can
>> be done for querying.
>> I think the js datasource does somthing similar.
>> Just gotta make sure that the container (or queue)
>> which stores incomming results is guarded for re-entrant
>> code when entries are removed or obtained?
>
>Sort of. But part of the reason this works in the datasource is that
>the users of the datasource are assumed to have an nsIRDFObserver
>sitting around waiting for updates. So if the results of a particular
>query aren't already cached as an RDF delegate object, an empty
>enumerator is returned, and the results are called back (unrelated to
>the enumerator) with nsRDFObserver::onAssert() one at a time as they
>arrive. The only things that need to be threadsafe are the delegate
>objects, and (I assume) the JS engine provides that threadsafety for
>us under the covers. But I could be wrong. Peter V, can you confirm
>or deny this?
>
Ah, i understand more now.
The LDAP message listener components need to
be thread safe. Well in a C++ component XPCOM throws up
some warnings if i don't the use:
NS_IMPL_THREADSAFE_ISUPPORTS
macro, like in the LDAPChannel implementation.
I dunno much about the xpconnect stuff.
I think your assumption is correct.
The existing abdirectory data source (nsDirectoryDataSource.cpp)
does not 'exibit' this type of behaviour. This maybe one
of the caveats i alluded to in the posted document.
Methods on the nsIAbDirectory interface are sort of
synchronous, but asynchronisty (yuk word, sounds like
some anti-Jungian conspiracy!) is achieved through
adding listeners to an address book session which
propagates up calls to the RDF data source.
I think for an initial implmentation of a personal LDAP
address book it may not be as important as a corporate
because there is likely to be less information (i.e. cards)
returned, but then again it also depends on response
time...
>> However alot of the work/smarts has already
>> been done in the js LDAP datasource. But this
>> is general and not address specific and perhaps
>> address semantics should not impose on this at all.
>> Somthing extra would then need to map the appropriate
>> information.
>
>I assume that the only extra address book semantics necessary in the
>datasource would be a table of the attributes in the NC-rdf namespace
>that the addressbook uses and how they should be mapped from the
>corresponding inetOrgPerson attributes from the LDAPATTR-rdf
>namespace. Is that true? If so, that seems really lightweight and
>straightforward, so I don't have any particular problem with putting
>that in the datasource.
>
Yes.
From a purity perspective i don't like it.
But practically it makes sense.
It also depends if the data source will be used by
other types of client in the future, perhaps an
LDAP browser tool for sys admins. BUT time is tight
and i digress!
<snip>
><http://http://www.mozilla.org/mailnews/specs/proposals/LDAPAddressingPrefs.htm
l>
>has the prefs proposal that we've been discussing with Csaba.
>Currently, we've got the autocomplete preferences only depending on
>the directory server prefs (upon which the ldap address book prefs
>would also depend). Any opinion on this?
>
I have not been following the preferences discussion
that closely. The separation between the two and
the dependency on make sense.
<snip>
>> interface nsIAbDirectoryQueryStatus
>> {
>> const long failed = -1;
>> const long noMatch = 0;
>> const long matchFound = 1;
>> };
>
>Why define a separate interface here rather than just using the
>existing nsIAutoCompleteStatus?
>
Good point, probably cause of over pedantic reasons!
Thinking out loud with this idea.
I have written a read only perosonal LDAP address book
(based on code from John) using the refactored framework
and am able to copy cards from the LDAP to the local in
the mozilla GUI.
See attached for code.
(this is still a bit of a hack still because the LDAP location
is hardcoded and the bootstrap (or address book directory
manager component explicitly instantiates this implementation
because there is no creation/preferences functionality yet).
<brain-splurge>
This will not work for corporate LDAP address books
since there needs to be an associated query. Since
the corporate will be read only and will not support
mail lists many of the methods on the nsIAbDirectory
interface do not make sense. This implies that there
maybe no point using this at all and we need a
composite RDF directory data source able to operate
on the existing and LDAP datasource. I don't know
much about the consequences of this with respect
to changes in respective js code and xul.
In addition the use of the nsIAbCard interface may
not be required, as long as something interposes to
create a card property component when dragging from
the corporate LDAP to a local or personal LDAP.
.
.
Hmm.. sounds to me like a plan could come together
from this where work is nicely separated... and
there also appears to be changes to the way the
address book will be accessed through the mozilla
GUI so all in all it could be good.
(the only bad thing is it complicates the writing
of a general SDBC address book component :-/
but thats our problem!)
</brain-splurge>
<snip>
>> Given most of the use cases for address book query i
>> don't think it is worth going for a general query based
>> interfaces. The only use case where a general query
>> would be useful for efficiency is with the SDBC driver,
>> however i do not think this is a priority.
>
>I tend to agree that generalizing it would be nice, but not a
>priority. If that does get done at some point, though, the prefs
>panels would have to change from what's in the current proposal.
>
Yep.
About LDAP XPCOM usage:
I am curious to know why a two stage operation process is
used on queries. The first operation-listener listens on
a bind and instantiates a new operation which performs
the search.
Thanks,
Paul.
| ? + ? = To question
----------------\
Paul Sandoz
x19219
+353-1-8199219
#ifndef nsAbLDAPWTDirectory_h__
#define nsAbLDAPWTDirectory_h__
#include "nsRDFResource.h"
#include "nsAbDirProperty.h"
#include "nsILDAPConnection.h"
#include "nsILDAPOperation.h"
#include "nsILDAPURL.h"
class nsAbLDAPWTDirectory : public nsRDFResource, public nsAbDirProperty
{
public:
NS_DECL_ISUPPORTS_INHERITED
nsAbLDAPWTDirectory();
virtual ~nsAbLDAPWTDirectory();
// nsIAbDirectory methods:
NS_IMETHOD GetChildNodes(nsIEnumerator* *result);
NS_IMETHOD GetChildCards(nsIEnumerator* *result);
NS_IMETHOD DeleteDirectory(nsIAbDirectory *directory);
NS_IMETHOD DeleteCards(nsISupportsArray *cards);
NS_IMETHOD HasCard(nsIAbCard *cards, PRBool *hasCard);
NS_IMETHOD HasDirectory(nsIAbDirectory *dir, PRBool *hasDir);
NS_IMETHOD CreateNewDirectory(const PRUnichar *dirName, const char *fileName, PRBool migrating);
NS_IMETHOD AddMailList(nsIAbDirectory *list);
NS_IMETHOD AddCard(nsIAbCard *card);
NS_IMETHOD DropCard(nsIAbCard *card);
protected:
void InitLDAP ();
protected:
PRBool mInitialized;
nsCOMPtr<nsISupportsArray> mSubDirectories;
char *myURL;
nsCOMPtr<nsILDAPConnection> myconnection;
nsCOMPtr<nsILDAPURL> myurl ;
};
#endif
#include "nsAbLDAPWTDirectory.h"
#include "nsRDFCID.h"
#include "nsIRDFService.h"
#include "nsIRDFResource.h"
#include "nsAbBaseCID.h"
#include "nsIAbCard.h"
#include "nsIAddressBook.h"
#include "nsIAddrBookSession.h"
#include "nsILDAPMessageListener.h"
#include "prmem.h"
#include "prprf.h"
#define NS_LDAPCONNECTION_CID
"@mozilla.org/network/ldap-connection;1"
#define NS_LDAPOPERATION_CID
"@mozilla.org/network/ldap-operation;1"
#define NS_LDAPMESSAGE_CID "@mozilla.org/network/ldap-message;1"
#define NS_LDAPURL_CID "@mozilla.org/network/ldap-url;1"
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kAddrBookCID, NS_ADDRESSBOOK_CID);
static NS_DEFINE_CID(kAddressBookDBCID, NS_ADDRDATABASE_CID);
static NS_DEFINE_CID(kAddrBookSessionCID, NS_ADDRBOOKSESSION_CID);
// --------------------------------------------
// --------------------------------------------
// --------------------------------------------
// --------------------------------------------
// --------------------------------------------
// --------------------------------------------
#include "nsISimpleEnumerator.h"
#include "nsISupportsArray.h"
#include "prmon.h"
// frees all elements of an XPIDL out array of a given size using
// freeFunc(), then frees the array itself using nsMemory::Free().
// Thanks to <[EMAIL PROTECTED]> for suggesting this form, which can be
// used to NS_RELEASE entire arrays before freeing as well.
//
#define NSLDAP_FREE_XPIDL_ARRAY(size, array, freeFunc) \
for ( PRUint32 __iter__ = (size) ; __iter__ > 0 ; ) \
freeFunc((array)[--__iter__]); \
nsMemory::Free(array);
class childCardEnumeratorListener : public nsIEnumerator, public nsILDAPMessageListener
{
public:
NS_DECL_ISUPPORTS
childCardEnumeratorListener(const char* baseURI);
virtual ~childCardEnumeratorListener();
NS_DECL_NSIENUMERATOR
NS_DECL_NSILDAPMESSAGELISTENER
protected:
nsresult waitForData (PRBool* moreData);
nsresult onLDAPSearchEntry(nsILDAPMessage *aMessage);
nsresult createCard(const char *uriName, nsIAbCard **childCard);
PRBool setCardProperty (nsIAbCard* card, char* attribute, char** values,
PRUint32 valuesCount);
void addCard (nsIAbCard* card);
protected:
char* mBaseURI;
PRUint32 mReadIndex;
PRMonitor* monitor;
// Thread critical data
nsCOMPtr<nsISupportsArray> mArray;
PRBool mDone;
};
NS_IMPL_THREADSAFE_ISUPPORTS2(childCardEnumeratorListener, nsIEnumerator,
nsILDAPMessageListener)
childCardEnumeratorListener::childCardEnumeratorListener(const char* baseURI) :
mReadIndex(0),
mDone(PR_FALSE)
{
NS_INIT_REFCNT();
NS_NewISupportsArray(getter_AddRefs(mArray));
if (baseURI)
mBaseURI = PL_strdup (baseURI);
else
mBaseURI = 0;
monitor = PR_NewMonitor ();
}
childCardEnumeratorListener::~childCardEnumeratorListener()
{
if (mBaseURI)
PR_Free (mBaseURI);
PR_DestroyMonitor (monitor);
}
// nsIEnumerator methods
//
NS_IMETHODIMP childCardEnumeratorListener::First()
{
mReadIndex = 0;
return NS_OK;
}
NS_IMETHODIMP childCardEnumeratorListener::Next()
{
mReadIndex++;
PRBool moreData;
waitForData (&moreData);
if (moreData == PR_TRUE)
return NS_OK;
else
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP childCardEnumeratorListener::IsDone()
{
PRBool moreData;
waitForData (&moreData);
if (moreData == PR_TRUE)
return NS_ERROR_FAILURE;
else
return NS_OK;
}
NS_IMETHODIMP childCardEnumeratorListener::CurrentItem(nsISupports** retval)
{
if (IsDone () == NS_OK)
return NS_ERROR_FAILURE;
nsCOMPtr<nsISupports> s;
// Enter Montior
PR_EnterMonitor(monitor);
mArray->GetElementAt (mReadIndex, getter_AddRefs(s));
// Exit Monitor
PR_ExitMonitor(monitor);
*retval = s;
NS_IF_ADDREF(*retval);
return NS_OK;
}
nsresult childCardEnumeratorListener::waitForData (PRBool* moreData)
{
PRUint32 count;
nsresult rv;
// Enter Monitor
PR_EnterMonitor(monitor);
while (1)
{
// Check existing state
//
mArray->Count (&count);
if (mReadIndex < count)
{
*moreData = PR_TRUE;
rv = NS_OK;
break;
}
if (mDone == PR_TRUE)
{
*moreData = PR_FALSE;
rv = NS_OK;
break;
}
// Wait on Condition
PR_Wait (monitor, PR_INTERVAL_NO_TIMEOUT);
}
// Exit Monitor
PR_ExitMonitor(monitor);
return rv;
}
// nsILDAPMessageListener methods
//
NS_IMETHODIMP childCardEnumeratorListener::OnLDAPMessage(nsILDAPMessage *aMessage)
{
PRInt32 messageType;
// figure out what sort of message was returned
//
nsresult rv = aMessage->GetType(&messageType);
if (NS_FAILED(rv))
{
PR_fprintf(PR_STDERR,"Error OnLDAPMessage:MessageType [%d]\n",
messageType);
NS_ERROR("childCardEnumeratorListener::OnLDAPMessage(): unexpected
error in "
"nsILDAPMessage::GetType()");
return NS_ERROR_UNEXPECTED;
}
switch (messageType)
{
case LDAP_RES_BIND:
// a bind has completed
//
// Not interested in this
break;
case LDAP_RES_SEARCH_ENTRY:
// a search entry has been returned
//
return onLDAPSearchEntry (aMessage);
break;
case LDAP_RES_SEARCH_RESULT:
// the search is finished; we're all done
//
// Enter Montior
PR_EnterMonitor(monitor);
mDone = PR_TRUE;
// Notify Condition
PR_Notify (monitor);
// Exit Monitor
PR_ExitMonitor(monitor);
break;
default:
NS_WARNING("childCardEnumeratorListener::OnLDAPMessage(): unexpected
LDAP message "
"received");
}
return NS_OK;
}
nsresult childCardEnumeratorListener::onLDAPSearchEntry(nsILDAPMessage *aMessage)
{
nsresult rv;
PRUint32 attrCount;
char **attrs;
printf ("IN childCardEnumeratorListener::OnLDAPSearchEntry\n");
char* dn;
rv = aMessage->GetDn(&dn);
NS_ENSURE_SUCCESS(rv, rv);
char* cardURI = PR_smprintf("abldapwtcard:%s%s", mBaseURI, dn);
printf ("Message (URI): %s\n", cardURI);
nsCOMPtr<nsIAbCard> card;
rv = createCard (cardURI, getter_AddRefs(card));
NS_ENSURE_SUCCESS(rv, rv);
PR_Free (cardURI);
PR_Free (dn);
rv = aMessage->GetAttributes(&attrCount, &attrs);
NS_ENSURE_SUCCESS(rv, rv);
PRBool hasSetCardProperty = PR_FALSE;
for (PRUint32 i = 0; i < attrCount; i++)
{
char **vals;
PRUint32 valueCount;
rv = aMessage->GetValues(attrs[i], &valueCount, &vals);
if (NS_FAILED(rv))
{
PR_fprintf(PR_STDERR,"childCardEnumeratorListener::OnLDAPSearchEntry(): "
"aMessage->GetValues() failed\n");
NS_WARNING("childCardEnumeratorListener::OnLDAPSearchEntry(): "
"aMessage->GetValues() failed\n");
NSLDAP_FREE_XPIDL_ARRAY(attrCount, attrs, nsMemory::Free);
}
hasSetCardProperty |= setCardProperty (card, attrs[i], vals,
valueCount);
NSLDAP_FREE_XPIDL_ARRAY(valueCount, vals, nsMemory::Free);
}
NSLDAP_FREE_XPIDL_ARRAY(attrCount, attrs, nsMemory::Free);
if (hasSetCardProperty == PR_TRUE)
{
addCard (card);
}
else
printf ("NO PROPERTIES FOUND\n");
printf ("OUT childCardEnumeratorListener::OnLDAPSearchEntry\n");
return rv;
}
nsresult childCardEnumeratorListener::createCard(const char *uriName, nsIAbCard
**childCard)
{
if(!childCard)
return NS_ERROR_NULL_POINTER;
nsresult rv = NS_OK;
NS_WITH_SERVICE(nsIRDFService, rdf, kRDFServiceCID, &rv);
if(NS_FAILED(rv))
{
return rv;
}
nsCOMPtr<nsIRDFResource> res;
rv = rdf->GetResource(uriName, getter_AddRefs(res));
if (NS_FAILED(rv))
{
return rv;
}
nsCOMPtr<nsIAbCard> personCard(do_QueryInterface(res, &rv));
if (NS_FAILED(rv))
{
return rv;
}
*childCard = personCard;
NS_ADDREF(*childCard);
return rv;
}
PRBool childCardEnumeratorListener::setCardProperty (nsIAbCard* card, char* attribute,
char** values, PRUint32 valuesCount)
{
if (!valuesCount)
return PR_FALSE;
PRUnichar *unichars;
PRInt32 descLength;
descLength = PL_strlen(values[0]);
INTL_ConvertToUnicode((const char *)values[0],
descLength, (void**)&unichars);
if (PL_strcmp (attribute, "cn") == 0)
{
printf ("DISPLAY NAME: %s\n", values[0]);
card->SetDisplayName (unichars);
}
else if (PL_strcmp (attribute, "mail") == 0)
{
card->SetPrimaryEmail (unichars);
}
else if (PL_strcmp (attribute, "sn") == 0)
{
card->SetLastName (unichars);
}
else if (PL_strcmp (attribute, "givenname") == 0)
{
card->SetFirstName (unichars);
}
else if (PL_strcmp (attribute, "telephonenumber") == 0)
{
card->SetWorkPhone (unichars);
}
else
{
PR_FREEIF(unichars);
return PR_FALSE;
}
PR_FREEIF(unichars);
return PR_TRUE;
}
void childCardEnumeratorListener::addCard (nsIAbCard* card)
{
// Enter Montior
PR_EnterMonitor(monitor);
mArray->AppendElement (card);
// Notify Condition
PR_Notify (monitor);
// Exit Monitor
PR_ExitMonitor(monitor);
}
// --------------------------------------------
// --------------------------------------------
// --------------------------------------------
// --------------------------------------------
// --------------------------------------------
// --------------------------------------------
nsAbLDAPWTDirectory::nsAbLDAPWTDirectory()
: nsRDFResource(),
mInitialized(PR_FALSE)
{
printf ("IN nsAbLDAPWTDirectory\n");
NS_NewISupportsArray(getter_AddRefs(mSubDirectories));
printf ("OUT nsAbLDAPWTDirectory\n");
}
nsAbLDAPWTDirectory::~nsAbLDAPWTDirectory()
{
}
void nsAbLDAPWTDirectory::InitLDAP ()
{
printf("In nsAbLDAPWTDirectory::Init()\n");
nsresult rv;
char *host;
char *dn;
char *filter;
PRInt32 port;
PRInt32 scope;
if (mInitialized)
return;
myURL =
"ldap://koala2:389/ou=Bears,o=sun.com?cn,uid,mail,sn,givenname,telephonenumber?sub?(cn=*)";
// Create an instance of our url
myurl = do_CreateInstance(NS_LDAPURL_CID, &rv);
if (NS_FAILED(rv))
{
printf( "do_CreateInstance(nsILDAPURL) failed : \n");
return ;
}
rv = myurl->SetSpec(myURL);
if (NS_FAILED(rv))
{
printf("ERROR: Calling mozILDAPURL::SetSpec() \n");
return ;
}
myurl->GetHost(&host);
myurl->GetPort(&port),
myurl->GetDn(&dn);
myurl->GetScope(&scope),
myurl->GetFilter(&filter),
myconnection = do_CreateInstance(NS_LDAPCONNECTION_CID, &rv);
if (NS_FAILED(rv))
{
printf( "do_CreateInstance(nsILDAPConnection) failed : \n");
return;
}
rv = myconnection->Init(host, port, dn);
if (NS_FAILED(rv))
{
printf("ERROR: Calling mozILDAPConnection::Init() \n");
return ;
}
mInitialized = PR_TRUE;
printf("Out nsAbLDAPWTDirectory::Init()\n");
}
NS_IMPL_ISUPPORTS_INHERITED(nsAbLDAPWTDirectory, nsRDFResource, nsIAbDirectory)
// nsIAbDirectory methods:
NS_IMETHODIMP nsAbLDAPWTDirectory::GetChildNodes(nsIEnumerator* *result)
{
InitLDAP ();
printf("In nsAbLDAPWTDirectory::GetChildNodes()\n");
printf("Out nsAbLDAPWTDirectory::GetChildNodes()\n");
return mSubDirectories->Enumerate(result);
}
NS_IMETHODIMP nsAbLDAPWTDirectory::GetChildCards(nsIEnumerator* *result)
{
printf("In nsAbLDAPWTDirectory::GetChildCards()\n");
nsresult rv;
InitLDAP ();
char* baseURI = "//koala2:389/";
nsCOMPtr<nsILDAPOperation> operation = do_CreateInstance(NS_LDAPOPERATION_CID,
&rv);
if (NS_FAILED(rv))
{
printf( "do_CreateInstance(nsILDAPOperation) failed : \n");
return rv;
}
childCardEnumeratorListener* _listener =
new childCardEnumeratorListener (baseURI);
NS_ADDREF(_listener);
// Is there a bug in the LDAP which is not
// managing references properly?
nsCOMPtr<nsILDAPMessageListener> listener;
listener = getter_AddRefs (_listener);
rv = operation->Init(myconnection, listener);
if (NS_FAILED(rv))
{
printf( "Init() failed : \n");
return rv;
}
rv = operation->SimpleBind(NULL);
if (NS_FAILED(rv))
{
printf( "SimpleBind() failed : \n");
return rv;
}
rv = operation->UrlSearch(myURL, 0);
if (NS_FAILED(rv))
{
printf( "UrlSearch() failed : \n");
return rv;
}
nsCOMPtr<nsIEnumerator> enumerator(do_QueryInterface(listener));
*result = enumerator;
// TODO do we need to do this?
NS_ADDREF(*result);
printf("Out nsAbLDAPWTDirectory::GetChildCards()\n");
return rv;
}
NS_IMETHODIMP nsAbLDAPWTDirectory::DeleteDirectory(nsIAbDirectory *directory)
{
InitLDAP ();
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsAbLDAPWTDirectory::DeleteCards(nsISupportsArray *cards)
{
InitLDAP ();
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsAbLDAPWTDirectory::HasCard(nsIAbCard *cards, PRBool *hasCard)
{
InitLDAP ();
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsAbLDAPWTDirectory::HasDirectory(nsIAbDirectory *dir, PRBool *hasDir)
{
InitLDAP ();
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsAbLDAPWTDirectory::CreateNewDirectory(const PRUnichar *dirName, const
char *fileName, PRBool migrating)
{
printf("In nsAbLDAPWTDirectory::CreateNewDirectory()");
InitLDAP ();
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsAbLDAPWTDirectory::AddMailList(nsIAbDirectory *list)
{
InitLDAP ();
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsAbLDAPWTDirectory::AddCard(nsIAbCard *card)
{
InitLDAP ();
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsAbLDAPWTDirectory::DropCard(nsIAbCard *card)
{
InitLDAP ();
return NS_ERROR_NOT_IMPLEMENTED;
}