NS_IMETHODIMP nsAbLDAPCard::SetMetaProperties(nsILDAPMessage *aMessage)
{
  NS_ENSURE_ARG_POINTER(aMessage);
  
  // Get DN
  nsAutoCString dn;
  nsresult rv = aMessage->GetDn(dn);
  NS_ENSURE_SUCCESS(rv, rv);

  SetDn(dn);

  // Get the list of set attributes
  CharPtrArrayGuard attrs;
  rv = aMessage->GetAttributes(attrs.GetSizeAddr(), attrs.GetArrayAddr());
  NS_ENSURE_SUCCESS(rv, rv);
 
  nsAutoCString attr;
  m_attributes.Clear();
  for (uint32_t i = 0; i < attrs.GetSize(); ++i)
  {
    attr.Assign(nsDependentCString(attrs[i]));
    ToLowerCase(attr);
    m_attributes.AppendElement(attr);
  }

  // Get the objectClass values
  m_objectClass.Clear();
  PRUnicharPtrArrayGuard vals;
  rv = aMessage->GetValues("objectClass", vals.GetSizeAddr(),
    vals.GetArrayAddr());

  // objectClass is not always included in search result entries and
  // nsILDAPMessage::GetValues returns NS_ERROR_LDAP_DECODING_ERROR if the
  // requested attribute doesn't exist.
  if (rv ==  NS_ERROR_LDAP_DECODING_ERROR)
    return NS_OK;

  NS_ENSURE_SUCCESS(rv, rv);
  
  nsAutoCString oclass;
  for (uint32_t i = 0; i < vals.GetSize(); ++i)
  {
    oclass.Assign(NS_LossyConvertUTF16toASCII(nsDependentString(vals[i])));
    ToLowerCase(oclass);
    m_objectClass.AppendElement(oclass);
  }

  return NS_OK;
}
nsresult nsAbLDAPProcessChangeLogData::ParseRootDSEEntry(nsILDAPMessage *aMessage)
{
    NS_ENSURE_ARG_POINTER(aMessage);
    if (!mInitialized)
        return NS_ERROR_NOT_INITIALIZED;

    // Populate the RootDSEChangeLogEntry
    CharPtrArrayGuard attrs;
    nsresult rv = aMessage->GetAttributes(attrs.GetSizeAddr(), attrs.GetArrayAddr());
    // No attributes
    if(NS_FAILED(rv)) 
        return rv;

    for(PRInt32 i=attrs.GetSize()-1; i >= 0; i--) {
        PRUnicharPtrArrayGuard vals;
        rv = aMessage->GetValues(attrs.GetArray()[i], vals.GetSizeAddr(), vals.GetArrayAddr());
        if(NS_FAILED(rv))
            continue;
        if(vals.GetSize()) {
            if (!PL_strcasecmp(attrs[i], "changelog"))
                CopyUTF16toUTF8(vals[0], mRootDSEEntry.changeLogDN);
            if (!PL_strcasecmp(attrs[i], "firstChangeNumber"))
                mRootDSEEntry.firstChangeNumber = atol(NS_LossyConvertUTF16toASCII(vals[0]).get());
            if (!PL_strcasecmp(attrs[i], "lastChangeNumber"))
                mRootDSEEntry.lastChangeNumber = atol(NS_LossyConvertUTF16toASCII(vals[0]).get());
            if (!PL_strcasecmp(attrs[i], "dataVersion"))
                CopyUTF16toUTF8(vals[0], mRootDSEEntry.dataVersion);
        }
    }

    PRInt32 lastChangeNumber;
    mDirectory->GetLastChangeNumber(&lastChangeNumber);

    if ((mRootDSEEntry.lastChangeNumber > 0) &&
        (lastChangeNumber < mRootDSEEntry.lastChangeNumber) &&
        (lastChangeNumber > mRootDSEEntry.firstChangeNumber))
        mUseChangeLog = PR_TRUE;

    if (mRootDSEEntry.lastChangeNumber &&
        (lastChangeNumber == mRootDSEEntry.lastChangeNumber)) {
        Done(PR_TRUE); // We are up to date no need to replicate, db not open yet so call Done
        return NS_OK;
    }

    return rv;
}
nsresult nsAbLDAPProcessChangeLogData::ParseChangeLogEntries(nsILDAPMessage *aMessage)
{
    NS_ENSURE_ARG_POINTER(aMessage);
    if(!mInitialized) 
        return NS_ERROR_NOT_INITIALIZED;

    // Populate the RootDSEChangeLogEntry
    CharPtrArrayGuard attrs;
    nsresult rv = aMessage->GetAttributes(attrs.GetSizeAddr(), attrs.GetArrayAddr());
    // No attributes
    if(NS_FAILED(rv)) 
        return rv;

    nsAutoString targetDN;
    UpdateOp operation = NO_OP;
    for(PRInt32 i = attrs.GetSize()-1; i >= 0; i--) {
        PRUnicharPtrArrayGuard vals;
        rv = aMessage->GetValues(attrs.GetArray()[i], vals.GetSizeAddr(), vals.GetArrayAddr());
        if(NS_FAILED(rv))
            continue;
        if(vals.GetSize()) {
            if (!PL_strcasecmp(attrs[i], "targetdn"))
                targetDN = vals[0];
            if (!PL_strcasecmp(attrs[i], "changetype")) {
                if (!Compare(nsDependentString(vals[0]), NS_LITERAL_STRING("add"), nsCaseInsensitiveStringComparator()))
                    operation = ENTRY_ADD;
                if (!Compare(nsDependentString(vals[0]), NS_LITERAL_STRING("modify"), nsCaseInsensitiveStringComparator()))
                    operation = ENTRY_MODIFY;
                if (!Compare(nsDependentString(vals[0]), NS_LITERAL_STRING("delete"), nsCaseInsensitiveStringComparator()))
                    operation = ENTRY_DELETE;
            }
        }
    }

    mChangeLogEntriesCount++;
    if(!(mChangeLogEntriesCount % 10)) { // Inform the listener every 10 entries
        mListener->OnProgressChange(nsnull,nsnull,mChangeLogEntriesCount, -1, mChangeLogEntriesCount, -1);
        // In case if the LDAP Connection thread is starved and causes problem
        // uncomment this one and try.
        // PR_Sleep(PR_INTERVAL_NO_WAIT); // give others a chance
    }

#ifdef DEBUG_rdayal
    printf ("ChangeLog Replication : Updated Entry : %s for OpType : %u\n", 
                                    NS_ConvertUTF16toUTF8(targetDN).get(), operation);
#endif

    switch(operation) {
    case ENTRY_ADD:
        // Add the DN to the add list if not already in the list
        if(!(mEntriesToAdd.IndexOf(targetDN) >= 0))
            mEntriesToAdd.AppendString(targetDN);
        break;
    case ENTRY_DELETE:
        // Do not check the return here since delete may fail if
        // entry deleted in changelog does not exist in DB
        // for e.g if the user specifies a filter, so go next entry
        DeleteCard(targetDN);
        break;
    case ENTRY_MODIFY:
        // For modify, delete the entry from DB and add updated entry
        // we do this since we cannot access the changes attribs of changelog
        rv = DeleteCard(targetDN);
        if (NS_SUCCEEDED(rv)) 
            if(!(mEntriesToAdd.IndexOf(targetDN) >= 0))
                mEntriesToAdd.AppendString(targetDN);
        break;
    default:
        // Should not come here, would come here only
        // if the entry is not a changeLog entry
        NS_WARNING("nsAbLDAPProcessChangeLogData::ParseChangeLogEntries"
           "Not an changelog entry");
    }

    // Go ahead processing the next entry, a modify or delete DB operation
    // can 'correctly' fail if the entry is not present in the DB,
    // e.g. in case a filter is specified.
    return NS_OK;
}
nsresult nsAbQueryLDAPMessageListener::OnLDAPMessageSearchEntry (nsILDAPMessage *aMessage,
        nsIAbDirectoryQueryResult** result)
{
    nsresult rv;

    if (!mDirectoryQuery)
        return NS_ERROR_NULL_POINTER;
    // the address book fields that we'll be asking for
    CharPtrArrayGuard properties;
    rv = mQueryArguments->GetReturnProperties (properties.GetSizeAddr(), properties.GetArrayAddr());
    NS_ENSURE_SUCCESS(rv, rv);

    // the map for translating between LDAP attrs <-> addrbook fields
    nsCOMPtr<nsISupports> iSupportsMap;
    rv = mQueryArguments->GetTypeSpecificArg(getter_AddRefs(iSupportsMap));
    NS_ENSURE_SUCCESS(rv, rv);
    nsCOMPtr<nsIAbLDAPAttributeMap> map = do_QueryInterface(iSupportsMap, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    // set up variables for handling the property values to be returned
    nsCOMPtr<nsISupportsArray> propertyValues;
    nsCOMPtr<nsIAbDirectoryQueryPropertyValue> propertyValue;
    rv = NS_NewISupportsArray(getter_AddRefs(propertyValues));
    NS_ENSURE_SUCCESS(rv, rv);

    if (!strcmp(properties[0], "card:nsIAbCard")) {
        // Meta property
        nsCAutoString dn;
        rv = aMessage->GetDn (dn);
        NS_ENSURE_SUCCESS(rv, rv);

        nsCOMPtr<nsIAbCard> card;
        rv = mDirectoryQuery->CreateCard (mUrl, dn.get(), getter_AddRefs (card));
        NS_ENSURE_SUCCESS(rv, rv);

        rv = map->SetCardPropertiesFromLDAPMessage(aMessage, card);
        NS_ENSURE_SUCCESS(rv, rv);

        propertyValue = new nsAbDirectoryQueryPropertyValue(properties[0], card);
        if (!propertyValue)
            return NS_ERROR_OUT_OF_MEMORY;

        rv = propertyValues->AppendElement(propertyValue);
        NS_ENSURE_SUCCESS(rv, rv);
    } else {

        for (PRUint32 i = 0; i < properties.GetSize(); i++)
        {
            // this is the precedence order list of attrs for this property
            CharPtrArrayGuard attrs;
            rv = map->GetAttributes(nsDependentCString(properties[i]),
                                    attrs.GetSizeAddr(),
                                    attrs.GetArrayAddr());

            // if there are no attrs for this property, just move on
            if (NS_FAILED(rv) || !strlen(attrs[0])) {
                continue;
            }

            // iterate through list, until first property found
            for (PRUint32 j=0; j < attrs.GetSize(); j++) {

                // try and get the values for this ldap attribute
                PRUnicharPtrArrayGuard vals;
                rv = aMessage->GetValues(attrs[j], vals.GetSizeAddr(),
                                         vals.GetArrayAddr());

                if (NS_SUCCEEDED(rv) && vals.GetSize()) {
                    propertyValue = new nsAbDirectoryQueryPropertyValue(
                        properties[i], vals[0]);
                    if (!propertyValue) {
                        return NS_ERROR_OUT_OF_MEMORY;
                    }

                    (void)propertyValues->AppendElement (propertyValue);
                    break;
                }
            }
        }
    }

    return QueryResultStatus (propertyValues, result, nsIAbDirectoryQueryResult::queryResultMatch);
}