scott%scott-macgregor.org 6979575aa5 Bug #395701 --> migrate the address book to glue code.
Patch by Prasad Sunkari <prasad@medhas.org>
r=standard8
sr=mscott


git-svn-id: svn://10.0.0.236/trunk@237992 18797224-902f-48f8-a5cc-f745e15eee43
2007-10-22 17:47:16 +00:00

1379 lines
41 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Seth Spitzer <sspitzer@netscape.com>
* Dan Mosedale <dmose@mozilla.org>
* Mark Banner <mark@standard8.demon.co.uk>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* directory server preferences (used to be dirprefs.c in 4.x) */
#include "nsIPrefService.h"
#include "nsIPrefBranch2.h"
#include "nsDirPrefs.h"
#include "nsIPrefLocalizedString.h"
#include "nsIObserver.h"
#include "nsVoidArray.h"
#include "nsServiceManagerUtils.h"
#include "nsMemory.h"
#include "nsIAddrDatabase.h"
#include "nsAbBaseCID.h"
#include "nsIAddrBookSession.h"
#include "nsILocalFile.h"
#include "nsWeakReference.h"
#include "nsIAbMDBDirectory.h"
#if defined(MOZ_LDAP_XPCOM)
#include "nsIAbLDAPDirectory.h"
#endif
#include "prmem.h"
#include "prprf.h"
#include "plstr.h"
#include "nsQuickSort.h"
#include <ctype.h>
/*****************************************************************************
* Private definitions
*/
/* Default settings for site-configurable prefs */
#define kDefaultPosition 1
static PRBool dir_IsServerDeleted(DIR_Server * server);
static char *DIR_GetStringPref(const char *prefRoot, const char *prefLeaf, const char *defaultValue);
static PRInt32 DIR_GetIntPref(const char *prefRoot, const char *prefLeaf, PRInt32 defaultValue);
static char * dir_ConvertDescriptionToPrefName(DIR_Server * server);
void DIR_SetFileName(char** filename, const char* leafName);
static void DIR_SetIntPref(const char *prefRoot, const char *prefLeaf, PRInt32 value, PRInt32 defaultValue);
static DIR_Server *dir_MatchServerPrefToServer(nsVoidArray *wholeList, const char *pref);
static PRBool dir_ValidateAndAddNewServer(nsVoidArray *wholeList, const char *fullprefname);
static void DIR_DeleteServerList(nsVoidArray *wholeList);
static char *dir_CreateServerPrefName(DIR_Server *server);
static void DIR_GetPrefsForOneServer(DIR_Server *server);
static void DIR_InitServer(DIR_Server *server, DirectoryType dirType = (DirectoryType)0);
static DIR_PrefId DIR_AtomizePrefName(const char *prefname);
#define DIR_POS_APPEND 0x80000000
#define DIR_POS_DELETE 0x80000001
static PRBool DIR_SetServerPosition(nsVoidArray *wholeList, DIR_Server *server, PRInt32 position);
/* These two routines should be called to initialize and save
* directory preferences from the XP Java Script preferences
*/
static nsresult DIR_GetServerPreferences(nsVoidArray** list);
static void DIR_SaveServerPreferences(nsVoidArray *wholeList);
static PRInt32 dir_UserId = 0;
nsVoidArray *dir_ServerList = nsnull;
/*****************************************************************************
* Functions for creating the new back end managed DIR_Server list.
*/
class DirPrefObserver : public nsSupportsWeakReference,
public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
};
NS_IMPL_ISUPPORTS2(DirPrefObserver, nsISupportsWeakReference, nsIObserver)
NS_IMETHODIMP DirPrefObserver::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
{
nsCOMPtr<nsIPrefBranch> prefBranch(do_QueryInterface(aSubject));
const char *prefname = NS_ConvertUTF16toUTF8(aData).get();
DIR_PrefId id = DIR_AtomizePrefName(prefname);
// Just get out if we get nothing here - we don't need to do anything
if (id == idNone)
return NS_OK;
/* Check to see if the server is in the unified server list.
*/
DIR_Server *server = dir_MatchServerPrefToServer(dir_ServerList, prefname);
if (server)
{
/* If the server is in the process of being saved, just ignore this
* change. The DIR_Server structure is not really changing.
*/
if (server->savingServer)
return NS_OK;
/* If the pref that changed is the position, read it in. If the new
* position is zero, remove the server from the list.
*/
if (id == idPosition)
{
PRInt32 position;
/* We must not do anything if the new position is the same as the
* position in the DIR_Server. This avoids recursion in cases
* where we are deleting the server.
*/
prefBranch->GetIntPref(prefname, &position);
if (position != server->position)
{
server->position = position;
if (dir_IsServerDeleted(server))
DIR_SetServerPosition(dir_ServerList, server, DIR_POS_DELETE);
}
}
if (id == idDescription)
// Ensure the local copy of the description is kept up to date.
server->description = DIR_GetStringPref(prefname, "description", nsnull);
}
/* If the server is not in the unified list, we may need to add it. Servers
* are only added when the position, serverName and description are valid.
*/
else if (id == idPosition || id == idType || id == idDescription)
{
dir_ValidateAndAddNewServer(dir_ServerList, prefname);
}
return NS_OK;
}
// A pointer to the pref observer
static DirPrefObserver *prefObserver = nsnull;
static nsresult DIR_GetDirServers()
{
nsresult rv = NS_OK;
if (!dir_ServerList)
{
/* we need to build the DIR_Server list */
rv = DIR_GetServerPreferences(&dir_ServerList);
/* Register the preference call back if necessary. */
if (NS_SUCCEEDED(rv) && !prefObserver)
{
nsCOMPtr<nsIPrefBranch2> pbi(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return rv;
prefObserver = new DirPrefObserver();
if (!prefObserver)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(prefObserver);
pbi->AddObserver(PREF_LDAP_SERVER_TREE_NAME, prefObserver, PR_TRUE);
}
}
return rv;
}
nsVoidArray* DIR_GetDirectories()
{
if (!dir_ServerList)
DIR_GetDirServers();
return dir_ServerList;
}
DIR_Server* DIR_GetServerFromList(const char* prefName)
{
DIR_Server* result = nsnull;
if (!dir_ServerList)
DIR_GetDirServers();
if (dir_ServerList)
{
PRInt32 count = dir_ServerList->Count();
PRInt32 i;
for (i = 0; i < count; ++i)
{
DIR_Server *server = (DIR_Server *)dir_ServerList->ElementAt(i);
if (server && strcmp(server->prefName, prefName) == 0)
{
result = server;
break;
}
}
}
return result;
}
static nsresult SavePrefsFile()
{
nsresult rv;
nsCOMPtr<nsIPrefService> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return rv;
return pPref->SavePrefFile(nsnull);
}
nsresult DIR_ShutDown() /* FEs should call this when the app is shutting down. It frees all DIR_Servers regardless of ref count values! */
{
nsresult rv = SavePrefsFile();
NS_ENSURE_SUCCESS(rv, rv);
DIR_DeleteServerList(dir_ServerList);
dir_ServerList = nsnull;
/* unregister the preference call back, if necessary.
* we need to do this as DIR_Shutdown() is called when switching profiles
* when using turbo. (see nsAbDirectoryDataSource::Observe())
* When switching profiles, prefs get unloaded and then re-loaded
* we don't want our callback to get called for all that.
* We'll reset our callback the first time DIR_GetDirServers() is called
* after we've switched profiles.
*/
NS_IF_RELEASE(prefObserver);
return NS_OK;
}
nsresult DIR_ContainsServer(DIR_Server* pServer, PRBool *hasDir)
{
if (dir_ServerList)
{
PRInt32 count = dir_ServerList->Count();
PRInt32 i;
for (i = 0; i < count; i++)
{
DIR_Server* server = (DIR_Server *)(dir_ServerList->ElementAt(i));
if (server == pServer)
{
*hasDir = PR_TRUE;
return NS_OK;
}
}
}
*hasDir = PR_FALSE;
return NS_OK;
}
nsresult DIR_AddNewAddressBook(const nsAString &dirName,
const nsACString &fileName, const nsACString &uri,
DirectoryType dirType, DIR_Server** pServer)
{
DIR_Server * server = (DIR_Server *) PR_Malloc(sizeof(DIR_Server));
if (!server)
return NS_ERROR_OUT_OF_MEMORY;
DIR_InitServer(server, dirType);
if (!dir_ServerList)
DIR_GetDirServers();
if (dir_ServerList)
{
NS_ConvertUTF16toUTF8 utf8str(dirName);
server->description = ToNewCString(utf8str);
server->position = kDefaultPosition; // don't set position so alphabetic sort will happen.
if (!fileName.IsEmpty())
server->fileName = ToNewCString(fileName);
else
DIR_SetFileName(&server->fileName, kPersonalAddressbook);
if (dirType == LDAPDirectory) {
if (!uri.IsEmpty())
server->uri = ToNewCString(uri);
}
dir_ServerList->AppendElement(server);
DIR_SavePrefsForOneServer(server);
*pServer = server;
// save new address book into pref file
return SavePrefsFile();
}
return NS_ERROR_FAILURE;
}
/*****************************************************************************
* Functions for creating DIR_Servers
*/
static void DIR_InitServer(DIR_Server *server, DirectoryType dirType)
{
if (!server) {
NS_WARNING("DIR_InitServer: server parameter not initialized");
return;
}
memset(server, 0, sizeof(DIR_Server));
server->position = kDefaultPosition;
server->uri = nsnull;
server->savingServer = PR_FALSE;
server->dirType = dirType;
}
/* Function for setting the position of a server. Can be used to append,
* delete, or move a server in a server list.
*
* The third parameter specifies the new position the server is to occupy.
* The resulting position may differ depending on the lock state of the
* given server and other servers in the list. The following special values
* are supported:
* DIR_POS_APPEND - Appends the server to the end of the list. If the server
* is already in the list, does nothing.
* DIR_POS_DELETE - Deletes the given server from the list. Note that this
* does not cause the server structure to be freed.
*
* Returns PR_TRUE if the server list was re-sorted.
*/
static PRBool DIR_SetServerPosition(nsVoidArray *wholeList, DIR_Server *server, PRInt32 position)
{
NS_ENSURE_ARG_POINTER(wholeList);
PRInt32 i, count, num;
PRBool resort = PR_FALSE;
DIR_Server *s=nsnull;
switch (position) {
case DIR_POS_APPEND:
/* Do nothing if the request is to append a server that is already
* in the list.
*/
count = wholeList->Count();
for (i= 0; i < count; i++)
{
if ((s = (DIR_Server *)wholeList->ElementAt(i)) != nsnull)
if (s == server)
return PR_FALSE;
}
/* In general, if there are any servers already in the list, set the
* position to the position of the last server plus one. If there
* are none, set it to position 1.
*/
if (count > 0)
{
s = (DIR_Server *)wholeList->ElementAt(count - 1);
server->position = s->position + 1;
}
else
server->position = 1;
wholeList->AppendElement(server);
break;
case DIR_POS_DELETE:
/* Remove the prefs corresponding to the given server. If the prefName
* value is nsnull, the server has never been saved and there are no
* prefs to remove.
*/
if (server->prefName)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return PR_FALSE;
pPref->DeleteBranch(server->prefName);
// mark the server as deleted by setting its position to 0
DIR_SetIntPref(server->prefName, "position", 0, -1);
}
/* If the server is in the server list, remove it.
*/
num = wholeList->IndexOf(server);
if (num >= 0)
{
/* The list does not need to be re-sorted if the server is the
* last one in the list.
*/
count = wholeList->Count();
if (num == count - 1)
{
wholeList->RemoveElementAt(num);
}
else
{
resort = PR_TRUE;
wholeList->RemoveElement(server);
}
}
break;
default:
/* See if the server is already in the list.
*/
count = wholeList->Count();
for (i= 0; i < count; i++)
{
if ((s = (DIR_Server *)wholeList->ElementAt(i)) != nsnull)
if (s == server)
break;
}
/* If the server is not in the list, add it to the beginning and re-sort.
*/
if (s == nsnull)
{
server->position = position;
wholeList->AppendElement(server);
resort = PR_TRUE;
}
/* Don't re-sort if the server is already in the requested position.
*/
else if (server->position != position)
{
server->position = position;
wholeList->RemoveElement(server);
wholeList->AppendElement(server);
resort = PR_TRUE;
}
break;
}
/* Make sure our position changes get saved back to prefs
*/
DIR_SaveServerPreferences(wholeList);
return resort;
}
/*****************************************************************************
* DIR_Server Callback Notification Functions
*/
/* dir_matchServerPrefToServer
*
* This function finds the DIR_Server in the unified DIR_Server list to which
* the given preference string belongs.
*/
static DIR_Server *dir_MatchServerPrefToServer(nsVoidArray *wholeList, const char *pref)
{
DIR_Server *server;
PRInt32 count = wholeList->Count();
PRInt32 i;
for (i = 0; i < count; i++)
{
if ((server = (DIR_Server *)wholeList->ElementAt(i)) != nsnull)
{
if (server->prefName && PL_strstr(pref, server->prefName) == pref)
{
char c = pref[PL_strlen(server->prefName)];
if (c == 0 || c == '.')
return server;
}
}
}
return nsnull;
}
/* dir_ValidateAndAddNewServer
*
* This function verifies that the position, serverName and description values
* are set for the given prefName. If they are then it adds the server to the
* unified server list.
*/
static PRBool dir_ValidateAndAddNewServer(nsVoidArray *wholeList, const char *fullprefname)
{
PRBool rc = PR_FALSE;
const char *endname = PL_strchr(&fullprefname[PL_strlen(PREF_LDAP_SERVER_TREE_NAME) + 1], '.');
if (endname)
{
char *prefname = (char *)PR_Malloc(endname - fullprefname + 1);
if (prefname)
{
PRInt32 dirType;
char *t1 = nsnull, *t2 = nsnull;
PL_strncpyz(prefname, fullprefname, endname - fullprefname + 1);
dirType = DIR_GetIntPref(prefname, "dirType", -1);
if (dirType != -1 &&
DIR_GetIntPref(prefname, "position", 0) != 0 &&
(t1 = DIR_GetStringPref(prefname, "description", nsnull)) != nsnull)
{
if (dirType == PABDirectory ||
(t2 = DIR_GetStringPref(prefname, "serverName", nsnull)) != nsnull)
{
DIR_Server *server = (DIR_Server *)PR_Malloc(sizeof(DIR_Server));
if (server)
{
DIR_InitServer(server, (DirectoryType)dirType);
server->prefName = prefname;
DIR_GetPrefsForOneServer(server);
DIR_SetServerPosition(wholeList, server, server->position);
rc = PR_TRUE;
}
PR_FREEIF(t2);
}
PR_Free(t1);
}
else
PR_Free(prefname);
}
}
return rc;
}
static DIR_PrefId DIR_AtomizePrefName(const char *prefname)
{
if (!prefname)
return idNone;
DIR_PrefId rc = idNone;
/* Skip the "ldap_2.servers.<server-name>." portion of the string.
*/
if (PL_strstr(prefname, PREF_LDAP_SERVER_TREE_NAME) == prefname)
{
prefname = PL_strchr(&prefname[PL_strlen(PREF_LDAP_SERVER_TREE_NAME) + 1], '.');
if (!prefname)
return idNone;
else
prefname = prefname + 1;
}
switch (prefname[0]) {
case 'd':
switch (prefname[1]) {
case 'e': /* description */
rc = idDescription;
break;
case 'i': /* dirType */
rc = idType;
break;
}
break;
case 'f':
rc = idFileName;
break;
case 'p':
switch (prefname[1]) {
case 'o':
switch (prefname[2]) {
case 's': /* position */
rc = idPosition;
break;
}
break;
}
break;
case 'u': /* uri */
rc = idUri;
break;
}
return rc;
}
/*****************************************************************************
* Functions for destroying DIR_Servers
*/
/* this function determines if the passed in server is no longer part of the of
the global server list. */
static PRBool dir_IsServerDeleted(DIR_Server * server)
{
return (server && server->position == 0);
}
/* when the back end manages the server list, deleting a server just decrements its ref count,
in the old world, we actually delete the server */
static void DIR_DeleteServer(DIR_Server *server)
{
if (server)
{
/* when destroying the server check its clear flag to see if things need cleared */
#ifdef XP_FileRemove
if (DIR_TestFlag(server, DIR_CLEAR_SERVER))
{
if (server->fileName)
XP_FileRemove (server->fileName, xpAddrBookNew);
}
#endif /* XP_FileRemove */
PR_Free(server->prefName);
PR_Free(server->description);
PR_Free(server->fileName);
PR_Free(server->uri);
PR_Free(server);
}
}
nsresult DIR_DeleteServerFromList(DIR_Server *server)
{
if (!server)
return NS_ERROR_NULL_POINTER;
nsresult rv = NS_OK;
nsCOMPtr<nsILocalFile> dbPath;
nsCOMPtr<nsIAddrBookSession> abSession =
do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
rv = abSession->GetUserProfileDirectory(getter_AddRefs(dbPath));
if (NS_SUCCEEDED(rv))
{
// close the database, as long as it isn't the special ones
// (personal addressbook and collected addressbook)
// which can never be deleted. There was a bug where we would slap in
// "abook.mab" as the file name for LDAP directories, which would cause a crash
// on delete of LDAP directories. this is just extra protection.
if (strcmp(server->fileName, kPersonalAddressbook) &&
strcmp(server->fileName, kCollectedAddressbook))
{
nsCOMPtr<nsIAddrDatabase> database;
rv = dbPath->AppendNative(nsDependentCString(server->fileName));
NS_ENSURE_SUCCESS(rv, rv);
// close file before delete it
nsCOMPtr<nsIAddrDatabase> addrDBFactory =
do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv) && addrDBFactory)
rv = addrDBFactory->Open(dbPath, PR_FALSE, PR_TRUE, getter_AddRefs(database));
if (database) /* database exists */
{
database->ForceClosed();
rv = dbPath->Remove(PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
}
}
nsVoidArray *dirList = DIR_GetDirectories();
DIR_SetServerPosition(dirList, server, DIR_POS_DELETE);
DIR_DeleteServer(server);
return SavePrefsFile();
}
return NS_ERROR_NULL_POINTER;
}
static void DIR_DeleteServerList(nsVoidArray *wholeList)
{
if (wholeList)
{
DIR_Server *server = nsnull;
/* TBD: Send notifications? */
PRInt32 count = wholeList->Count();
PRInt32 i;
for (i = count - 1; i >=0; i--)
{
server = (DIR_Server *)wholeList->ElementAt(i);
if (server != nsnull)
DIR_DeleteServer(server);
}
delete wholeList;
}
}
/*****************************************************************************
* Functions for managing JavaScript prefs for the DIR_Servers
*/
PR_STATIC_CALLBACK(int)
comparePrefArrayMembers(const void* aElement1, const void* aElement2, void* aData)
{
const char* element1 = *static_cast<const char* const *>(aElement1);
const char* element2 = *static_cast<const char* const *>(aElement2);
const PRUint32 offset = *((const PRUint32*)aData);
// begin the comparison at |offset| chars into the string -
// this avoids comparing the "ldap_2.servers." portion of every element,
// which will always remain the same.
return strcmp(element1 + offset, element2 + offset);
}
static nsresult dir_GetChildList(const nsCString &aBranch,
PRUint32 *aCount, char ***aChildList)
{
PRUint32 branchLen = aBranch.Length();
nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!prefBranch) {
return NS_ERROR_FAILURE;
}
nsresult rv = prefBranch->GetChildList(aBranch.get(), aCount, aChildList);
if (NS_FAILED(rv)) {
return rv;
}
// traverse the list, and truncate all the descendant strings to just
// one branch level below the root branch.
for (PRUint32 i = *aCount; i--; ) {
// The prefname we passed to GetChildList was of the form
// "ldap_2.servers." and we are returned the descendants
// in the form of "ldap_2.servers.servername.foo"
// But we want the prefbranch of the servername, so
// write a NUL character in to terminate the string early.
char *endToken = strchr((*aChildList)[i] + branchLen, '.');
if (endToken)
*endToken = '\0';
}
if (*aCount > 1) {
// sort the list, in preparation for duplicate entry removal
NS_QuickSort(*aChildList, *aCount, sizeof(char*), comparePrefArrayMembers, &branchLen);
// traverse the list and remove duplicate entries.
// we use two positions in the list; the current entry and the next
// entry; and perform a bunch of in-place ptr moves. so |cur| points
// to the last unique entry, and |next| points to some (possibly much
// later) entry to test, at any given point. we know we have >= 2
// elements in the list here, so we just init the two counters sensibly
// to begin with.
PRUint32 cur = 0;
for (PRUint32 next = 1; next < *aCount; ++next) {
// check if the elements are equal or unique
if (!comparePrefArrayMembers(&((*aChildList)[cur]), &((*aChildList)[next]), &branchLen)) {
// equal - just free & increment the next element ptr
nsMemory::Free((*aChildList)[next]);
} else {
// cur & next are unique, so we need to shift the element.
// ++cur will point to the next free location in the
// reduced array (it's okay if that's == next)
(*aChildList)[++cur] = (*aChildList)[next];
}
}
// update the unique element count
*aCount = cur + 1;
}
return NS_OK;
}
static char *DIR_GetStringPref(const char *prefRoot, const char *prefLeaf, const char *defaultValue)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return nsnull;
nsCString value;
nsCAutoString prefLocation(prefRoot);
prefLocation.Append('.');
prefLocation.Append(prefLeaf);
if (NS_SUCCEEDED(pPref->GetCharPref(prefLocation.get(), getter_Copies(value))))
{
/* unfortunately, there may be some prefs out there which look like this */
if (value.EqualsLiteral("(null)"))
{
if (defaultValue)
value = defaultValue;
else
value.Truncate();
}
if (value.IsEmpty())
{
rv = pPref->GetCharPref(prefLocation.get(), getter_Copies(value));
}
}
else
{
value = defaultValue ? strdup(defaultValue) : nsnull;
}
return ToNewCString(value);
}
/*
Get localized unicode string pref from properties file, convert into an UTF8 string
since address book prefs store as UTF8 strings. So far there are 2 default
prefs stored in addressbook.properties.
"ldap_2.servers.pab.description"
"ldap_2.servers.history.description"
*/
static char *DIR_GetLocalizedStringPref
(const char *prefRoot, const char *prefLeaf, const char *defaultValue)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return nsnull;
nsCAutoString prefLocation(prefRoot);
prefLocation.Append('.');
prefLocation.Append(prefLeaf);
nsString wvalue;
nsCOMPtr<nsIPrefLocalizedString> locStr;
rv = pPref->GetComplexValue(prefLocation.get(), NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(locStr));
if (NS_SUCCEEDED(rv))
rv = locStr->ToString(getter_Copies(wvalue));
char *value = nsnull;
if (!wvalue.IsEmpty())
{
NS_ConvertUTF16toUTF8 utf8str(wvalue.get());
value = ToNewCString(utf8str);
}
else
value = defaultValue ? strdup(defaultValue) : nsnull;
return value;
}
static PRInt32 DIR_GetIntPref(const char *prefRoot, const char *prefLeaf, PRInt32 defaultValue)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return defaultValue;
PRInt32 value;
nsCAutoString prefLocation(prefRoot);
prefLocation.Append('.');
prefLocation.Append(prefLeaf);
if (NS_FAILED(pPref->GetIntPref(prefLocation.get(), &value)))
value = defaultValue;
return value;
}
/* This will convert from the old preference that was a path and filename */
/* to a just a filename */
static void DIR_ConvertServerFileName(DIR_Server* pServer)
{
char* leafName = pServer->fileName;
char* newLeafName = nsnull;
#if defined(XP_WIN) || defined(XP_OS2)
/* jefft -- bug 73349 This is to allow users share same address book.
* It only works if the user specify a full path filename.
*/
#ifdef XP_FileIsFullPath
if (! XP_FileIsFullPath(leafName))
newLeafName = XP_STRRCHR (leafName, '\\');
#endif /* XP_FileIsFullPath */
#else
newLeafName = strrchr(leafName, '/');
#endif
pServer->fileName = newLeafName ? strdup(newLeafName + 1) : strdup(leafName);
if (leafName) PR_Free(leafName);
}
/* This will generate a correct filename and then remove the path.
* Note: we are assuming that the default name is in the native
* filesystem charset. The filename will be returned as a UTF8
* string.
*/
void DIR_SetFileName(char** fileName, const char* defaultName)
{
if (!fileName)
return;
nsresult rv = NS_OK;
nsCOMPtr<nsILocalFile> dbPath;
*fileName = nsnull;
nsCOMPtr<nsIAddrBookSession> abSession =
do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
rv = abSession->GetUserProfileDirectory(getter_AddRefs(dbPath));
if (NS_SUCCEEDED(rv))
{
rv = dbPath->AppendNative(nsDependentCString(defaultName));
if (NS_SUCCEEDED(rv))
{
rv = dbPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0664);
nsAutoString realFileName;
rv = dbPath->GetLeafName(realFileName);
if (NS_SUCCEEDED(rv))
*fileName = ToNewUTF8String(realFileName);
}
}
}
/****************************************************************
Helper function used to generate a file name from the description
of a directory. Caller must free returned string.
An extension is not applied
*****************************************************************/
static char * dir_ConvertDescriptionToPrefName(DIR_Server * server)
{
#define MAX_PREF_NAME_SIZE 25
char * fileName = nsnull;
char fileNameBuf[MAX_PREF_NAME_SIZE];
PRInt32 srcIndex = 0;
PRInt32 destIndex = 0;
PRInt32 numSrcBytes = 0;
const char * descr = nsnull;
if (server && server->description)
{
descr = server->description;
numSrcBytes = PL_strlen(descr);
while (srcIndex < numSrcBytes && destIndex < MAX_PREF_NAME_SIZE-1)
{
if (isdigit(descr[srcIndex]) || isalpha(descr[srcIndex]) )
{
fileNameBuf[destIndex] = descr[srcIndex];
destIndex++;
}
srcIndex++;
}
fileNameBuf[destIndex] = '\0'; /* zero out the last character */
}
if (destIndex) /* have at least one character in the file name? */
fileName = strdup(fileNameBuf);
return fileName;
}
void DIR_SetServerFileName(DIR_Server *server)
{
char * tempName = nsnull;
const char * prefName = nsnull;
PRUint32 numHeaderBytes = 0;
if (server && (!server->fileName || !(*server->fileName)) )
{
PR_FREEIF(server->fileName); // might be one byte empty string.
/* make sure we have a pref name...*/
if (!server->prefName || !*server->prefName)
server->prefName = dir_CreateServerPrefName(server);
/* set default personal address book file name*/
if ((server->position == 1) && (server->dirType == PABDirectory))
server->fileName = strdup(kPersonalAddressbook);
else
{
/* now use the pref name as the file name since we know the pref name
will be unique */
prefName = server->prefName;
if (prefName && *prefName)
{
/* extract just the pref name part and not the ldap tree name portion from the string */
numHeaderBytes = PL_strlen(PREF_LDAP_SERVER_TREE_NAME) + 1; /* + 1 for the '.' b4 the name */
if (PL_strlen(prefName) > numHeaderBytes)
tempName = strdup(prefName + numHeaderBytes);
if (tempName)
{
server->fileName = PR_smprintf("%s%s", tempName, kABFileName_CurrentSuffix);
PR_Free(tempName);
}
}
}
if (!server->fileName || !*server->fileName) /* when all else has failed, generate a default name */
{
if (server->dirType == LDAPDirectory)
DIR_SetFileName(&(server->fileName), kMainLdapAddressBook); /* generates file name with an ldap prefix */
else
DIR_SetFileName(&(server->fileName), kPersonalAddressbook);
}
}
}
static char *dir_CreateServerPrefName (DIR_Server *server)
{
/* we are going to try to be smart in how we generate our server
pref name. We'll try to convert the description into a pref name
and then verify that it is unique. If it is unique then use it... */
char * leafName = dir_ConvertDescriptionToPrefName(server);
char * prefName = nsnull;
PRBool isUnique = PR_FALSE;
if (!leafName || !*leafName)
{
// we need to handle this in case the description has no alphanumeric chars
// it's very common for cjk users
leafName = strdup("_nonascii");
}
if (leafName)
{
PRInt32 uniqueIDCnt = 0;
char **children = nsnull;
/* we need to verify that this pref string name is unique */
prefName = PR_smprintf(PREF_LDAP_SERVER_TREE_NAME".%s", leafName);
isUnique = PR_FALSE;
PRUint32 prefCount;
nsresult rv = dir_GetChildList(NS_LITERAL_CSTRING(PREF_LDAP_SERVER_TREE_NAME "."),
&prefCount, &children);
if (NS_SUCCEEDED(rv))
{
while (!isUnique && prefName)
{
isUnique = PR_TRUE; /* now flip the logic and assume we are unique until we find a match */
for (PRUint32 i = 0; i < prefCount && isUnique; ++i)
{
if (!PL_strcasecmp(children[i], prefName)) /* are they the same branch? */
isUnique = PR_FALSE;
}
if (!isUnique) /* then try generating a new pref name and try again */
{
PR_smprintf_free(prefName);
prefName = PR_smprintf(PREF_LDAP_SERVER_TREE_NAME".%s_%d", leafName, ++uniqueIDCnt);
}
} /* if we have a list of pref Names */
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, children);
} /* while we don't have a unique name */
// fallback to "user_directory_N" form if we failed to verify
if (!isUnique && prefName)
{
PR_smprintf_free(prefName);
prefName = nsnull;
}
PR_Free(leafName);
} /* if leafName */
if (!prefName) /* last resort if we still don't have a pref name is to use user_directory string */
return PR_smprintf(PREF_LDAP_SERVER_TREE_NAME".user_directory_%d", ++dir_UserId);
else
return prefName;
}
static void DIR_GetPrefsForOneServer(DIR_Server *server)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return;
char *prefstring = server->prefName;
// this call fills in tempstring with the position pref, and
// we then check to see if it's locked.
server->position = DIR_GetIntPref (prefstring, "position", kDefaultPosition);
if (0 == PL_strcmp(prefstring, "ldap_2.servers.pab") ||
0 == PL_strcmp(prefstring, "ldap_2.servers.history"))
{
// get default address book name from addressbook.properties
server->description = DIR_GetLocalizedStringPref(prefstring, "description", "");
}
else
server->description = DIR_GetStringPref (prefstring, "description", "");
server->dirType = (DirectoryType)DIR_GetIntPref (prefstring, "dirType", LDAPDirectory);
server->fileName = DIR_GetStringPref (prefstring, "filename", "");
// if we don't have a file name try and get one
if (!server->fileName || !*(server->fileName))
DIR_SetServerFileName (server);
if (server->fileName && *server->fileName)
DIR_ConvertServerFileName(server);
// the string "s" is the default uri ( <scheme> + "://" + <filename> )
nsCString s((server->dirType == PABDirectory || server->dirType == MAPIDirectory) ?
#if defined(MOZ_LDAP_XPCOM)
kMDBDirectoryRoot : kLDAPDirectoryRoot);
#else
// Fallback to the all directory root in the non-ldap enabled case.
kMDBDirectoryRoot : kAllDirectoryRoot);
#endif
s.Append (server->fileName);
server->uri = DIR_GetStringPref (prefstring, "uri", s.get ());
}
static nsresult dir_GetPrefs(nsVoidArray **list)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return rv;
(*list) = new nsVoidArray();
if (!(*list))
return NS_ERROR_OUT_OF_MEMORY;
char **children;
PRUint32 prefCount;
rv = dir_GetChildList(NS_LITERAL_CSTRING(PREF_LDAP_SERVER_TREE_NAME "."),
&prefCount, &children);
if (NS_FAILED(rv))
return rv;
/* TBD: Temporary code to read broken "ldap" preferences tree.
* Remove line with if statement after M10.
*/
if (dir_UserId == 0)
pPref->GetIntPref(PREF_LDAP_GLOBAL_TREE_NAME".user_id", &dir_UserId);
for (PRUint32 i = 0; i < prefCount; ++i)
{
DIR_Server *server;
server = (DIR_Server *)PR_Calloc(1, sizeof(DIR_Server));
if (server)
{
DIR_InitServer(server);
server->prefName = strdup(children[i]);
DIR_GetPrefsForOneServer(server);
if (server->description && server->description[0] &&
((server->dirType == PABDirectory ||
server->dirType == MAPIDirectory ||
server->dirType == FixedQueryLDAPDirectory || // this one might go away
server->dirType == LDAPDirectory)))
{
if (!dir_IsServerDeleted(server))
{
(*list)->AppendElement(server);
}
else
DIR_DeleteServer(server);
}
else
{
DIR_DeleteServer(server);
}
}
}
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, children);
return NS_OK;
}
// I don't think we care about locked positions, etc.
void DIR_SortServersByPosition(nsVoidArray *serverList)
{
int i, j;
DIR_Server *server;
int count = serverList->Count();
for (i = 0; i < count - 1; i++)
{
for (j = i + 1; j < count; j++)
{
if (((DIR_Server *) serverList->ElementAt(j))->position < ((DIR_Server *) serverList->ElementAt(i))->position)
{
server = (DIR_Server *) serverList->ElementAt(i);
serverList->ReplaceElementAt(serverList->ElementAt(j), i);
serverList->ReplaceElementAt(server, j);
}
}
}
}
static nsresult DIR_GetServerPreferences(nsVoidArray** list)
{
nsresult err;
nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &err));
if (NS_FAILED(err))
return err;
PRInt32 version = -1;
nsVoidArray *newList = nsnull;
/* Update the ldap list version and see if there are old prefs to migrate. */
err = pPref->GetIntPref(PREF_LDAP_VERSION_NAME, &version);
NS_ENSURE_SUCCESS(err, err);
/* Find the new-style "ldap_2.servers" tree in prefs */
err = dir_GetPrefs(&newList);
if (version < kCurrentListVersion)
{
pPref->SetIntPref(PREF_LDAP_VERSION_NAME, kCurrentListVersion);
}
DIR_SortServersByPosition(newList);
*list = newList;
return err;
}
static void DIR_SetStringPref(const char *prefRoot, const char *prefLeaf, const char *value, const char *defaultValue)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return;
nsCString defaultPref;
nsCAutoString prefLocation(prefRoot);
prefLocation.Append('.');
prefLocation.Append(prefLeaf);
if (NS_SUCCEEDED(pPref->GetCharPref(prefLocation.get(), getter_Copies(defaultPref))))
{
/* If there's a default pref, just set ours in and let libpref worry
* about potential defaults in all.js
*/
if (value) /* added this check to make sure we have a value before we try to set it..*/
rv = pPref->SetCharPref (prefLocation.get(), value);
else
rv = pPref->ClearUserPref(prefLocation.get());
}
else
{
/* If there's no default pref, look for a user pref, and only set our value in
* if the user pref is different than one of them.
*/
nsCString userPref;
if (NS_SUCCEEDED(pPref->GetCharPref (prefLocation.get(), getter_Copies(userPref))))
{
if (value && (defaultValue ? PL_strcasecmp(value, defaultValue) : value != defaultValue))
rv = pPref->SetCharPref (prefLocation.get(), value);
else
rv = pPref->ClearUserPref(prefLocation.get());
}
else
{
if (value && (defaultValue ? PL_strcasecmp(value, defaultValue) : value != defaultValue))
rv = pPref->SetCharPref (prefLocation.get(), value);
}
}
NS_ASSERTION(NS_SUCCEEDED(rv), "Could not set pref in DIR_SetStringPref");
}
static void DIR_SetIntPref(const char *prefRoot, const char *prefLeaf, PRInt32 value, PRInt32 defaultValue)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return;
PRInt32 defaultPref;
nsCAutoString prefLocation(prefRoot);
prefLocation.Append('.');
prefLocation.Append(prefLeaf);
if (NS_SUCCEEDED(pPref->GetIntPref(prefLocation.get(), &defaultPref)))
{
/* solve the problem where reordering user prefs must override default prefs */
rv = pPref->SetIntPref(prefLocation.get(), value);
}
else
{
PRInt32 userPref;
if (NS_SUCCEEDED(pPref->GetIntPref(prefLocation.get(), &userPref)))
{
if (value != defaultValue)
rv = pPref->SetIntPref(prefLocation.get(), value);
else
rv = pPref->ClearUserPref(prefLocation.get());
}
else
{
if (value != defaultValue)
rv = pPref->SetIntPref(prefLocation.get(), value);
}
}
NS_ASSERTION(NS_SUCCEEDED(rv), "Could not set pref in DIR_SetIntPref");
}
void DIR_SavePrefsForOneServer(DIR_Server *server)
{
if (!server)
return;
char *prefstring;
if (server->prefName == nsnull)
server->prefName = dir_CreateServerPrefName(server);
prefstring = server->prefName;
server->savingServer = PR_TRUE;
DIR_SetIntPref (prefstring, "position", server->position, kDefaultPosition);
// Only save the non-default address book name
if (0 != PL_strcmp(prefstring, "ldap_2.servers.pab") &&
0 != PL_strcmp(prefstring, "ldap_2.servers.history"))
DIR_SetStringPref(prefstring, "description", server->description, "");
DIR_SetStringPref(prefstring, "filename", server->fileName, "");
DIR_SetIntPref(prefstring, "dirType", server->dirType, LDAPDirectory);
if (server->dirType == LDAPDirectory)
DIR_SetStringPref(prefstring, "uri", server->uri, "");
server->savingServer = PR_FALSE;
}
static void DIR_SaveServerPreferences(nsVoidArray *wholeList)
{
if (wholeList)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
NS_WARNING("DIR_SaveServerPreferences: Failed to get the pref service\n");
return;
}
PRInt32 i;
PRInt32 count = wholeList->Count();
DIR_Server *server;
for (i = 0; i < count; i++)
{
server = (DIR_Server *) wholeList->ElementAt(i);
if (server)
DIR_SavePrefsForOneServer(server);
}
pPref->SetIntPref(PREF_LDAP_GLOBAL_TREE_NAME".user_id", dir_UserId);
}
}