/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsAbLDAPCard.h" #include "nsIMutableArray.h" #include "nsCOMPtr.h" #include "nsILDAPModification.h" #include "nsILDAPBERValue.h" #include "nsILDAPMessage.h" #include "nsIAbLDAPAttributeMap.h" #include "nsServiceManagerUtils.h" #include "nsComponentManagerUtils.h" #include "nsAbBaseCID.h" #include "nsAbUtils.h" #include "nsILDAPErrors.h" #include #define kDNColumn "DN" nsAbLDAPCard::nsAbLDAPCard() { } nsAbLDAPCard::~nsAbLDAPCard() { } NS_IMPL_ISUPPORTS_INHERITED(nsAbLDAPCard, nsAbCardProperty, nsIAbLDAPCard) /* 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 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.Contains(oclass)) { m_objectClass.AppendElement(oclass); printf("LDAP : adding objectClass %s\n", oclass.get()); } } nsCOMPtr mod = do_CreateInstance("@mozilla.org/network/ldap-modification;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr values = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); for (uint32_t i = 0; i < m_objectClass.Length(); ++i) { nsCOMPtr 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 mod = do_CreateInstance("@mozilla.org/network/ldap-modification;1", &rv); NS_ENSURE_SUCCESS(rv, rv); size_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 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 != m_attributes.NoIndex) m_attributes.AppendElement(attr); } else if (aType == nsILDAPModification::MOD_REPLACE && index != m_attributes.NoIndex) { // If the new value is empty, we are performing an update // and the attribute was previously set, clear it nsCOMPtr 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; } NS_IMETHODIMP nsAbLDAPCard::BuildRdn(nsIAbLDAPAttributeMap *aAttributeMap, const uint32_t aAttrCount, const char **aAttributes, nsACString &aRdn) { NS_ENSURE_ARG_POINTER(aAttributeMap); NS_ENSURE_ARG_POINTER(aAttributes); nsresult rv; nsCString attr; nsAutoCString prop; nsCString propvalue; aRdn.Truncate(); for (uint32_t i = 0; i < aAttrCount; ++i) { attr.Assign(nsDependentCString(aAttributes[i])); // Lookup the property corresponding to the attribute rv = aAttributeMap->GetProperty(attr, prop); NS_ENSURE_SUCCESS(rv, rv); // Get the property value rv = GetPropertyAsAUTF8String(prop.get(), propvalue); // XXX The case where an attribute needed to build the Relative // Distinguished Name is not set needs to be handled by the caller, // so as to let the user know what is missing. if (NS_FAILED(rv) || propvalue.IsEmpty()) { NS_ERROR("nsAbLDAPCard::BuildRdn: a required attribute is not set"); return NS_ERROR_NOT_INITIALIZED; } aRdn.Append(attr); aRdn.AppendLiteral("="); aRdn.Append(propvalue); if (i < aAttrCount - 1) aRdn.AppendLiteral("+"); } return NS_OK; } NS_IMETHODIMP nsAbLDAPCard::GetDn(nsACString &aDN) { return GetPropertyAsAUTF8String(kDNColumn, aDN); } NS_IMETHODIMP nsAbLDAPCard::SetDn(const nsACString &aDN) { SetLocalId(aDN); return SetPropertyAsAUTF8String(kDNColumn, aDN); } 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; }