NS_IMETHODIMP nsAbLDAPChangeLogQuery::QueryChangedEntries(const nsACString & aChangedEntryDN)
{
    if(!mInitialized) 
        return NS_ERROR_NOT_INITIALIZED;
    if(aChangedEntryDN.IsEmpty()) 
        return NS_ERROR_UNEXPECTED;

    nsCAutoString urlFilter;
    nsresult rv = mURL->GetFilter(urlFilter);
    if(NS_FAILED(rv)) 
        return rv;

    PRInt32 scope;
    rv = mURL->GetScope(&scope);
    if(NS_FAILED(rv)) 
        return rv;

    CharPtrArrayGuard attributes;
    rv = mURL->GetAttributes(attributes.GetSizeAddr(), attributes.GetArrayAddr());
    if(NS_FAILED(rv)) 
        return rv;

    rv = CreateNewLDAPOperation();
    NS_ENSURE_SUCCESS(rv, rv);
    return mOperation->SearchExt(aChangedEntryDN, scope, urlFilter, 
                               attributes.GetSize(), attributes.GetArray(),
                               0, 0);
}
nsresult nsAbQueryLDAPMessageListener::DoSearch()
{
    nsresult rv;
    mCanceled = mFinished = PR_FALSE;

    mSearchOperation = do_CreateInstance(NS_LDAPOPERATION_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    nsCOMPtr<nsIProxyObjectManager> proxyMgr =
        do_GetService(NS_XPCOMPROXY_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    nsCOMPtr<nsILDAPMessageListener> proxyListener;
    rv = proxyMgr->GetProxyForObject( NS_UI_THREAD_EVENTQ, NS_GET_IID(nsILDAPMessageListener),
                                      this, PROXY_SYNC | PROXY_ALWAYS, getter_AddRefs(proxyListener));
    NS_ENSURE_SUCCESS(rv, rv);

    rv = mSearchOperation->Init (mConnection, proxyListener, nsnull);
    NS_ENSURE_SUCCESS(rv, rv);

    nsCAutoString dn;
    rv = mUrl->GetDn (dn);
    NS_ENSURE_SUCCESS(rv, rv);

    PRInt32 scope;
    rv = mUrl->GetScope (&scope);
    NS_ENSURE_SUCCESS(rv, rv);

    nsCAutoString filter;
    rv = mUrl->GetFilter (filter);
    NS_ENSURE_SUCCESS(rv, rv);

    CharPtrArrayGuard attributes;
    rv = mUrl->GetAttributes (attributes.GetSizeAddr (), attributes.GetArrayAddr ());
    NS_ENSURE_SUCCESS(rv, rv);

    // I don't _think_ it's ever actually possible to get here without having
    // an nsAbLDAPDirectory object, but, just in case, I'll do a QI instead
    // of just static casts...
    nsCOMPtr<nsIAbLDAPDirectory> nsIAbDir =
        do_QueryInterface(mDirectoryQuery, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    nsAbLDAPDirectory *dir =
        NS_STATIC_CAST(nsAbLDAPDirectory *,
                       NS_STATIC_CAST(nsIAbLDAPDirectory *, nsIAbDir.get()));

    rv = mSearchOperation->SetServerControls(dir->mSearchServerControls.get());
    NS_ENSURE_SUCCESS(rv, rv);

    rv = mSearchOperation->SetClientControls(dir->mSearchClientControls.get());
    NS_ENSURE_SUCCESS(rv, rv);

    rv = mSearchOperation->SearchExt (dn, scope, filter,
                                      attributes.GetSize (), attributes.GetArray (),
                                      mTimeOut, mResultLimit);
    return rv;
}
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;
}
NS_IMETHODIMP nsAbLDAPChangeLogQuery::QueryAuthDN(const nsACString & aValueUsedToFindDn)
{
    if(!mInitialized) 
        return NS_ERROR_NOT_INITIALIZED;

    nsresult rv = NS_OK; 

    CharPtrArrayGuard attributes;
    *attributes.GetSizeAddr() = 2;
    *attributes.GetArrayAddr() = NS_STATIC_CAST(char **, nsMemory::Alloc((*attributes.GetSizeAddr()) * sizeof(char *)));
    attributes.GetArray()[0] = ToNewCString(nsDependentCString(DIR_GetFirstAttributeString(mDirServer, cn)));
    attributes.GetArray()[1] = nsnull;
    
    nsCAutoString filter(DIR_GetFirstAttributeString(mDirServer, auth));
    filter += '=';
    filter += aValueUsedToFindDn;

    nsCAutoString dn;
    rv = mURL->GetDn(dn);
    if(NS_FAILED(rv)) 
        return rv;

    rv = CreateNewLDAPOperation();
    NS_ENSURE_SUCCESS(rv, rv);

    return mOperation->SearchExt(dn, nsILDAPURL::SCOPE_SUBTREE, filter, 
                               attributes.GetSize(), attributes.GetArray(),
                               0, 0);
}
NS_IMETHODIMP nsAbLDAPDirectory::ModifyCard(nsIAbCard *aUpdatedCard)
{
  NS_ENSURE_ARG_POINTER(aUpdatedCard);
  
  nsCOMPtr<nsIAbLDAPAttributeMap> attrMap;
  nsresult rv = GetAttributeMap(getter_AddRefs(attrMap));
  NS_ENSURE_SUCCESS(rv, rv);

  // Get the LDAP card
  nsCOMPtr<nsIAbLDAPCard> card = do_QueryInterface(aUpdatedCard, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = Initiate();
  NS_ENSURE_SUCCESS(rv, rv);
  
  // Retrieve preferences
  nsAutoCString prefString;
  rv = GetObjectClasses(prefString);
  NS_ENSURE_SUCCESS(rv, rv);
  
  CharPtrArrayGuard objClass;
  rv = SplitStringList(prefString, objClass.GetSizeAddr(),
    objClass.GetArrayAddr());
  NS_ENSURE_SUCCESS(rv, rv);

  // Process updates
  nsCOMPtr<nsIArray> modArray;
  rv = card->GetLDAPMessageInfo(attrMap, objClass.GetSize(), objClass.GetArray(),
    nsILDAPModification::MOD_REPLACE, getter_AddRefs(modArray));
  NS_ENSURE_SUCCESS(rv, rv);
  
  // Get current DN
  nsAutoCString oldDN;
  rv = card->GetDn(oldDN);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsILDAPService> ldapSvc = do_GetService(
    "@mozilla.org/network/ldap-service;1", &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  // Retrieve base DN and RDN attributes
  nsAutoCString baseDN;
  nsAutoCString oldRDN;
  CharPtrArrayGuard rdnAttrs;
  rv = ldapSvc->ParseDn(oldDN.get(), oldRDN, baseDN,
                        rdnAttrs.GetSizeAddr(), rdnAttrs.GetArrayAddr());
  NS_ENSURE_SUCCESS(rv, rv);

  // Calculate new RDN and check whether it has changed
  nsAutoCString newRDN;
  rv = card->BuildRdn(attrMap, rdnAttrs.GetSize(), rdnAttrs.GetArray(),
    newRDN);
  NS_ENSURE_SUCCESS(rv, rv);
      
  if (newRDN.Equals(oldRDN))
  {
    // Launch query
    rv = DoModify(this, nsILDAPModification::MOD_REPLACE, oldDN, modArray,
                  EmptyCString(), EmptyCString());
  }
  else
  {
    // Build and store the new DN
    nsAutoCString newDN(newRDN);
    newDN.AppendLiteral(",");
    newDN.Append(baseDN);
    
    rv = card->SetDn(newDN);
    NS_ENSURE_SUCCESS(rv, rv);
    
    // Launch query
    rv = DoModify(this, nsILDAPModification::MOD_REPLACE, oldDN, modArray,
                  newRDN, baseDN);
  }
  return rv;
}
NS_IMETHODIMP nsAbLDAPDirectory::AddCard(nsIAbCard *aUpdatedCard,
                                         nsIAbCard **aAddedCard)
{
  NS_ENSURE_ARG_POINTER(aUpdatedCard);
  NS_ENSURE_ARG_POINTER(aAddedCard);
  
  nsCOMPtr<nsIAbLDAPAttributeMap> attrMap;
  nsresult rv = GetAttributeMap(getter_AddRefs(attrMap));
  NS_ENSURE_SUCCESS(rv, rv);

  // Create a new LDAP card
  nsCOMPtr<nsIAbLDAPCard> card =
    do_CreateInstance(NS_ABLDAPCARD_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = Initiate();
  NS_ENSURE_SUCCESS(rv, rv);

  // Copy over the card data
  nsCOMPtr<nsIAbCard> copyToCard = do_QueryInterface(card, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = copyToCard->Copy(aUpdatedCard);
  NS_ENSURE_SUCCESS(rv, rv);
  
  // Retrieve preferences
  nsAutoCString prefString;
  rv = GetRdnAttributes(prefString);
  NS_ENSURE_SUCCESS(rv, rv);

  CharPtrArrayGuard rdnAttrs;
  rv = SplitStringList(prefString, rdnAttrs.GetSizeAddr(),
    rdnAttrs.GetArrayAddr());
  NS_ENSURE_SUCCESS(rv, rv);
  
  rv = GetObjectClasses(prefString);
  NS_ENSURE_SUCCESS(rv, rv);
  
  CharPtrArrayGuard objClass;
  rv = SplitStringList(prefString, objClass.GetSizeAddr(),
    objClass.GetArrayAddr());
  NS_ENSURE_SUCCESS(rv, rv);

  // Process updates
  nsCOMPtr<nsIArray> modArray;
  rv = card->GetLDAPMessageInfo(attrMap, objClass.GetSize(), objClass.GetArray(),
    nsILDAPModification::MOD_ADD, getter_AddRefs(modArray));
  NS_ENSURE_SUCCESS(rv, rv);
  
  // For new cards, the base DN is the search base DN
  nsCOMPtr<nsILDAPURL> currentUrl;
  rv = GetLDAPURL(getter_AddRefs(currentUrl));
  NS_ENSURE_SUCCESS(rv, rv);

  nsAutoCString baseDN;
  rv = currentUrl->GetDn(baseDN);
  NS_ENSURE_SUCCESS(rv, rv);
 
  // Calculate DN
  nsAutoCString cardDN;
  rv = card->BuildRdn(attrMap, rdnAttrs.GetSize(), rdnAttrs.GetArray(),
    cardDN);
  NS_ENSURE_SUCCESS(rv, rv);
  cardDN.AppendLiteral(",");
  cardDN.Append(baseDN);

  rv = card->SetDn(cardDN);
  NS_ENSURE_SUCCESS(rv, rv);

  nsAutoCString ourUuid;
  GetUuid(ourUuid);
  copyToCard->SetDirectoryId(ourUuid);

  // Launch query
  rv = DoModify(this, nsILDAPModification::MOD_ADD, cardDN, modArray,
                EmptyCString(), EmptyCString());
  NS_ENSURE_SUCCESS(rv, rv);

  NS_ADDREF(*aAddedCard = copyToCard);
  return NS_OK;
}
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;
}
/* Retrieves the changes to the LDAP card and stores them in an LDAP
 * update message.
 *
 * Calling this method changes the LDAP card, it updates the
 * meta-properties (m_*) to reflect what the LDAP contents will be once
 * the update has been performed. This allows you to do multiple (successful)
 * consecutive edits on a card in a search result. If the meta-properties
 * were not updated, incorrect assuptions would be made about what object
 * classes to add, or  what attributes to clear.
 *
 * XXX: We need to take care when integrating this code with the asynchronous
 * update dialogs, as the current code in nsAbLDAPDirectory has a problem
 * when an update fails: the modified card still gets stored and shown to
 * the user instead of being discarded. There is one especially tricky case:
 * when you do an update on a card which changes its DN, you have two
 * operations (rename, then update the other attributes). If the rename
 * operation succeeds and not the update of the attributes, you are 
 * "somewhere in between" the original card and the updated card.
*/
NS_IMETHODIMP nsAbLDAPCard::GetLDAPMessageInfo(
  nsIAbLDAPAttributeMap *aAttributeMap,
  const uint32_t aClassCount,
  const char **aClasses,
  int32_t aType,
  nsIArray **aLDAPAddMessageInfo)
{
  NS_ENSURE_ARG_POINTER(aAttributeMap);
  NS_ENSURE_ARG_POINTER(aClasses);
  NS_ENSURE_ARG_POINTER(aLDAPAddMessageInfo);
  
  nsresult rv;
  nsCOMPtr<nsIMutableArray> modArray =
    do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  
  // Add any missing object classes. We never remove any object
  // classes: if an entry has additional object classes, it's probably
  // for a good reason.
  nsAutoCString oclass;
  for (uint32_t i = 0; i < aClassCount; ++i)
  {
    oclass.Assign(nsDependentCString(aClasses[i]));
    ToLowerCase(oclass);
   
    if (m_objectClass.IndexOf(oclass) == nsTArray<nsCString>::NoIndex)
    {
      m_objectClass.AppendElement(oclass);
      printf("LDAP : adding objectClass %s\n", oclass.get());
    }
  }

  nsCOMPtr<nsILDAPModification> mod =
    do_CreateInstance("@mozilla.org/network/ldap-modification;1", &rv);
  NS_ENSURE_SUCCESS(rv, rv);
 
  nsCOMPtr<nsIMutableArray> values =
    do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  
  for (uint32_t i = 0; i < m_objectClass.Length(); ++i)
  {
    nsCOMPtr<nsILDAPBERValue> value =
      do_CreateInstance("@mozilla.org/network/ldap-ber-value;1", &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    rv = value->SetFromUTF8(m_objectClass.ElementAt(i));
    NS_ENSURE_SUCCESS(rv, rv);

    rv = values->AppendElement(value, false);
    NS_ENSURE_SUCCESS(rv, rv);
  }
  
  rv = mod->SetUpModification(aType, NS_LITERAL_CSTRING("objectClass"), values);
  NS_ENSURE_SUCCESS(rv, rv);

  modArray->AppendElement(mod, false);

  // Add card properties
  CharPtrArrayGuard props;
  rv = aAttributeMap->GetAllCardProperties(props.GetSizeAddr(),
    props.GetArrayAddr());
  NS_ENSURE_SUCCESS(rv, rv);

  nsAutoCString attr;
  nsCString propvalue;
  for (uint32_t i = 0; i < props.GetSize(); ++i)
  {
    // Skip some attributes that don't map to LDAP.
    //
    // BirthYear : by default this is mapped to 'birthyear',
    // which is not part of mozillaAbPersonAlpha
    //
    // LastModifiedDate : by default this is mapped to 'modifytimestamp',
    // which cannot be modified
    //
    // PreferMailFormat : by default this is mapped to 'mozillaUseHtmlMail',
    // which is a boolean, not plaintext/html/unknown
    if (!strcmp(props[i], kBirthYearProperty) ||
        !strcmp(props[i], kLastModifiedDateProperty) ||
        !strcmp(props[i], kPreferMailFormatProperty))
      continue;
    
    rv = aAttributeMap->GetFirstAttribute(nsDependentCString(props[i]),
      attr);
    NS_ENSURE_SUCCESS(rv, rv);
    ToLowerCase(attr);

    // If the property is not mapped to an attribute, skip it.
    if (attr.IsEmpty())
      continue;
 
    nsCOMPtr<nsILDAPModification> mod =
      do_CreateInstance("@mozilla.org/network/ldap-modification;1", &rv);
    NS_ENSURE_SUCCESS(rv, rv);
   
    uint32_t index = m_attributes.IndexOf(attr);

    rv = GetPropertyAsAUTF8String(props[i], propvalue);

    if (NS_SUCCEEDED(rv) &&!propvalue.IsEmpty())
    {
      // If the new value is not empty, add/update it
      nsCOMPtr<nsILDAPBERValue> value =
        do_CreateInstance("@mozilla.org/network/ldap-ber-value;1", &rv);
      NS_ENSURE_SUCCESS(rv, rv);

      rv = value->SetFromUTF8(propvalue);
      NS_ENSURE_SUCCESS(rv, rv);
 
      rv = mod->SetUpModificationOneValue(aType, attr, value);
      NS_ENSURE_SUCCESS(rv, rv);
    
      printf("LDAP : setting attribute %s (%s) to '%s'\n", attr.get(),
        props[i], propvalue.get());
      modArray->AppendElement(mod, false);
      if (index != nsTArray<nsCString>::NoIndex)
        m_attributes.AppendElement(attr);

    }
    else if (aType == nsILDAPModification::MOD_REPLACE &&
             index != nsTArray<nsCString>::NoIndex)
    {
      // If the new value is empty, we are performing an update
      // and the attribute was previously set, clear it
      nsCOMPtr<nsIMutableArray> novalues =
        do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
      NS_ENSURE_SUCCESS(rv, rv);

      rv = mod->SetUpModification(aType, attr, novalues);
      NS_ENSURE_SUCCESS(rv, rv);
      
      printf("LDAP : removing attribute %s (%s)\n", attr.get(), props[i]);
      modArray->AppendElement(mod, false);
      m_attributes.RemoveElementAt(index);
    }
  }

  NS_ADDREF(*aLDAPAddMessageInfo = modArray);

  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);
}