Back to index

lightning-sunbird  0.9+nobinonly
nsAbAutoCompleteSession.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "msgCore.h"
00040 #include "nsAbAutoCompleteSession.h"
00041 #include "nsString.h"
00042 #include "nsRDFCID.h"
00043 #include "nsIRDFService.h"
00044 #include "nsIAbDirectory.h"
00045 #include "nsIAbCard.h"
00046 #include "nsXPIDLString.h"
00047 #include "nsReadableUtils.h"
00048 #include "nsUnicharUtils.h"
00049 #include "nsMsgBaseCID.h"
00050 #include "nsMsgI18N.h"
00051 #include "nsIMsgIdentity.h"
00052 #include "prmem.h"
00053 #include "nsNetCID.h"
00054 #include "nsIIOService.h"
00055 #include "nsIPrefService.h"
00056 #include "nsIPrefBranch.h"
00057 
00058 NS_IMPL_ISUPPORTS2(nsAbAutoCompleteSession, nsIAbAutoCompleteSession, nsIAutoCompleteSession)
00059 
00060 nsAbAutoCompleteSession::nsAbAutoCompleteSession()
00061 {
00062     mParser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID);
00063 }
00064 
00065 
00066 nsAbAutoCompleteSession::~nsAbAutoCompleteSession()
00067 {
00068 }
00069 
00070 PRBool nsAbAutoCompleteSession::ItsADuplicate(PRUnichar* fullAddrStr, PRInt32 aPopularityIndex, nsIAutoCompleteResults* results)
00071 {
00072     nsresult rv;
00073 
00074     nsCOMPtr<nsISupportsArray> array;
00075     rv = results->GetItems(getter_AddRefs(array));
00076     if (NS_SUCCEEDED(rv))
00077     {
00078         nsCOMPtr<nsIEnumerator> enumerator;
00079         rv = array->Enumerate(getter_AddRefs(enumerator));
00080         if (NS_SUCCEEDED(rv))
00081         {
00082             nsCOMPtr<nsISupports> item;
00083             nsCOMPtr<nsIAutoCompleteItem> resultItem;
00084             nsAutoString valueStr;
00085 
00086             for (rv = enumerator->First(); NS_SUCCEEDED(rv); rv = enumerator->Next())
00087             {
00088                 rv = enumerator->CurrentItem(getter_AddRefs(item));
00089                 if (NS_SUCCEEDED(rv) && item)
00090                 {
00091                     resultItem = do_QueryInterface(item, &rv);
00092                     if (NS_SUCCEEDED(rv))
00093                     {
00094                         rv = resultItem->GetValue(valueStr);
00095                         if (NS_SUCCEEDED(rv) && !valueStr.IsEmpty() && nsDependentString(fullAddrStr).Equals(valueStr, nsCaseInsensitiveStringComparator()))
00096                         {
00097                           // ok, we have a duplicate, but before we ignore the dupe, check the popularity index
00098                           // and use the card that is the most popular so it gets sorted correctly
00099                           nsCOMPtr<nsISupports> currentItemParams;
00100                           rv = resultItem->GetParam(getter_AddRefs(currentItemParams));
00101                           if (NS_SUCCEEDED(rv))
00102                           {
00103                             nsAbAutoCompleteParam *param = (nsAbAutoCompleteParam *)(void *)currentItemParams;
00104                             if (aPopularityIndex > param->mPopularityIndex)
00105                             {
00106                               // remove the current autocomplete result, and return false so our dupe
00107                               // gets added in its place.
00108                               array->RemoveElement(item);
00109                               break; 
00110                             }
00111                           }
00112 
00113                           // it's a dupe, ignore it.
00114                             return PR_TRUE;
00115                         }
00116                     }
00117                 }
00118             }
00119         }
00120     }
00121     
00122     return PR_FALSE;
00123 }
00124 
00125 void 
00126 nsAbAutoCompleteSession::AddToResult(const PRUnichar* pNickNameStr,
00127                                      const PRUnichar* pDisplayNameStr,
00128                                      const PRUnichar* pFirstNameStr,
00129                                      const PRUnichar* pLastNameStr,
00130                                      const PRUnichar* pEmailStr, 
00131                                      const PRUnichar* pNotesStr, 
00132                                      const PRUnichar* pDirName,
00133                                      PRUint32 aPopularityIndex,
00134                                      PRBool bIsMailList, PRBool pDefaultMatch,
00135                                      nsIAutoCompleteResults* results)
00136 {
00137   nsresult rv;
00138   PRUnichar* fullAddrStr = nsnull;
00139 
00140   if (pDefaultMatch)
00141   {
00142     if (mDefaultDomain[0] == 0)
00143       return;
00144 
00145     nsAutoString aStr(pDisplayNameStr);
00146     if (aStr.FindChar('@') == kNotFound)
00147     {
00148       aStr.Append(PRUnichar('@'));
00149       aStr += mDefaultDomain;
00150     }
00151     fullAddrStr = ToNewUnicode(aStr);
00152   }
00153   else
00154   {
00155     if (mParser)
00156     {
00157       nsXPIDLCString fullAddress;
00158       nsXPIDLCString utf8Email;
00159       if (bIsMailList)
00160       {
00161         if (pNotesStr && pNotesStr[0] != 0)
00162           utf8Email.Adopt(ToNewUTF8String(nsDependentString(pNotesStr)));
00163         else
00164           utf8Email.Adopt(ToNewUTF8String(nsDependentString(pDisplayNameStr)));
00165       }
00166       else
00167         utf8Email.Adopt(ToNewUTF8String(nsDependentString(pEmailStr)));
00168 
00169       mParser->MakeFullAddress(nsnull, NS_ConvertUCS2toUTF8(pDisplayNameStr).get(),
00170                                utf8Email, getter_Copies(fullAddress));
00171       if (!fullAddress.IsEmpty())
00172       {
00173         /* We need to convert back the result from UTF-8 to Unicode */
00174         fullAddrStr = nsCRT::strdup(NS_ConvertUTF8toUCS2(fullAddress.get()).get());
00175       }
00176     }
00177   
00178     if (!fullAddrStr)
00179     {
00180       //oops, parser problem! I will try to do my best...
00181       const PRUnichar * pStr = nsnull;
00182       if (bIsMailList)
00183       {
00184         if (pNotesStr && pNotesStr[0] != 0)
00185           pStr = pNotesStr;
00186         else
00187           pStr = pDisplayNameStr;
00188       }
00189       else
00190         pStr = pEmailStr;
00191       // check this so we do not get a bogus entry "someName <>"
00192       if (pStr && pStr[0] != 0) {
00193         nsAutoString aStr(pDisplayNameStr);
00194         aStr.AppendLiteral(" <");
00195         aStr += pStr;
00196         aStr.AppendLiteral(">");
00197         fullAddrStr = ToNewUnicode(aStr);
00198       }
00199       else
00200         fullAddrStr = nsnull;
00201     }
00202   }
00203     
00204   if (fullAddrStr && ! ItsADuplicate(fullAddrStr, aPopularityIndex, results))
00205   {    
00206     nsCOMPtr<nsIAutoCompleteItem> newItem = do_CreateInstance(NS_AUTOCOMPLETEITEM_CONTRACTID, &rv);
00207     if (NS_SUCCEEDED(rv))
00208     {
00209       nsAbAutoCompleteParam *param = new nsAbAutoCompleteParam(pNickNameStr, pDisplayNameStr, pFirstNameStr, pLastNameStr, pEmailStr, pNotesStr, pDirName, aPopularityIndex, bIsMailList);
00210       NS_IF_ADDREF(param);
00211       newItem->SetParam(param);
00212       NS_IF_RELEASE(param);
00213 
00214       // how to process the comment column, if at all.  this value
00215       // comes from "mail.autoComplete.commentColumn", or, if that
00216       // doesn't exist, defaults to 0
00217       //
00218       // 0 = none
00219       // 1 = name of addressbook this card came from
00220       // 2 = other per-addressbook format (currrently unused here)
00221       //
00222       if (mAutoCompleteCommentColumn == 1) {
00223         rv = newItem->SetComment(pDirName);
00224         if (NS_FAILED(rv)) {
00225           NS_WARNING("nsAbAutoCompleteSession::AddToResult():"
00226                      " newItem->SetComment() failed\n");
00227         }
00228       }
00229 
00230       // if this isn't a default match, set the class name so we can style 
00231       // this cell with the local addressbook icon (or whatever)
00232       //
00233       rv = newItem->SetClassName(pDefaultMatch ? "default-match" :
00234                                  "local-abook");
00235       if (NS_FAILED(rv)) {
00236         NS_WARNING("nsAbAutoCompleteSession::AddToResult():"
00237                    " newItem->SetClassName() failed\n");
00238       }
00239 
00240       newItem->SetValue(nsDependentString(fullAddrStr));
00241       nsCOMPtr<nsISupportsArray> array;
00242       rv = results->GetItems(getter_AddRefs(array));
00243       if (NS_SUCCEEDED(rv))
00244       {
00245         PRUint32 nbrOfItems;      
00246         rv = array->Count(&nbrOfItems);
00247 
00248         PRInt32 insertPosition = 0;
00249 
00250         for (; insertPosition < nbrOfItems && !pDefaultMatch; insertPosition++)
00251         {
00252           nsCOMPtr<nsISupports> currentItemParams;
00253           nsCOMPtr<nsIAutoCompleteItem> resultItem;
00254           nsresult rv = array->QueryElementAt(insertPosition, NS_GET_IID(nsIAutoCompleteItem),
00255                                            getter_AddRefs(resultItem));
00256           if (NS_FAILED(rv))
00257             continue;
00258           rv = resultItem->GetParam(getter_AddRefs(currentItemParams));
00259           if (NS_FAILED(rv))
00260             continue;
00261 
00262           param = (nsAbAutoCompleteParam *)(void *)currentItemParams;
00263           if (aPopularityIndex > param->mPopularityIndex) // sort the search results by popularity index 
00264             break;
00265         }
00266 
00267         rv = array->InsertElementAt(newItem, insertPosition);
00268       }
00269     }
00270   }    
00271   PR_Free(fullAddrStr);
00272 }
00273 
00274 static PRBool CommonPrefix(const PRUnichar *aString, const PRUnichar *aSubstr, PRInt32 aSubstrLen)
00275 {
00276   if (!aSubstrLen || (nsCRT::strlen(aString) < NS_STATIC_CAST(PRUint32, aSubstrLen)))
00277     return PR_FALSE;
00278 
00279   return (Substring(aString,
00280                     aString+aSubstrLen).Equals(Substring(aSubstr, aSubstr+aSubstrLen),
00281                                                nsCaseInsensitiveStringComparator()));
00282 }
00283 
00284 
00285 PRBool
00286 nsAbAutoCompleteSession::CheckEntry(nsAbAutoCompleteSearchString* searchStr,
00287                                     const PRUnichar* nickName,
00288                                     const PRUnichar* displayName,
00289                                     const PRUnichar* firstName,
00290                                     const PRUnichar* lastName,
00291                                     const PRUnichar* emailAddress)
00292 {
00293   const PRUnichar * fullString;
00294   PRUint32 fullStringLen;
00295   PRBool isAMatch = PR_FALSE;
00296   
00297   if (searchStr->mFirstPartLen > 0 && searchStr->mSecondPartLen == 0)
00298   {
00299     fullString = searchStr->mFirstPart;
00300     fullStringLen = searchStr->mFirstPartLen;
00301   }
00302   else
00303   {
00304     fullString = searchStr->mFullString;
00305     fullStringLen = searchStr->mFullStringLen;
00306   }
00307 
00308   nsDependentString fullStringStr(fullString, fullStringLen);
00309   
00310   // Compare various properties looking for a match (exact or partial)
00311   if ( (nickName &&
00312         fullStringStr.Equals(nsDependentString(nickName), nsCaseInsensitiveStringComparator())) || 
00313        (displayName &&
00314         fullStringStr.Equals(nsDependentString(displayName), nsCaseInsensitiveStringComparator())) ||
00315        (firstName &&
00316         fullStringStr.Equals(nsDependentString(firstName), nsCaseInsensitiveStringComparator())) ||
00317        (lastName &&
00318         fullStringStr.Equals(nsDependentString(lastName), nsCaseInsensitiveStringComparator())) || 
00319        (emailAddress &&
00320         fullStringStr.Equals(nsDependentString(emailAddress), nsCaseInsensitiveStringComparator())) ||
00321        (nickName && CommonPrefix(nickName, fullString, fullStringLen)) ||
00322        (displayName && CommonPrefix(displayName, fullString, fullStringLen)) || 
00323        (firstName && CommonPrefix(firstName, fullString, fullStringLen)) ||
00324        (lastName && CommonPrefix(lastName, fullString, fullStringLen)) ||
00325        (emailAddress && CommonPrefix(emailAddress, fullString, fullStringLen)) )
00326     isAMatch = PR_TRUE;
00327   //If we have a muti-part search string, look for a partial match with first name and last name or reverse
00328   else if (searchStr->mFirstPartLen && searchStr->mSecondPartLen)
00329   {
00330     if (((firstName && CommonPrefix(firstName, searchStr->mFirstPart, searchStr->mFirstPartLen)) &&
00331         (lastName && CommonPrefix(lastName, searchStr->mSecondPart, searchStr->mSecondPartLen))) ||
00332         ((lastName && CommonPrefix(lastName, searchStr->mFirstPart, searchStr->mFirstPartLen)) &&
00333         (firstName && CommonPrefix(firstName, searchStr->mSecondPart, searchStr->mSecondPartLen))))
00334       isAMatch = PR_TRUE;
00335   }
00336 
00337   return isAMatch;
00338 }
00339 
00340 nsresult nsAbAutoCompleteSession::SearchCards(nsIAbDirectory* directory, nsAbAutoCompleteSearchString* searchStr, nsIAutoCompleteResults* results)
00341 {
00342   nsresult rv;    
00343   nsCOMPtr<nsIEnumerator> cardsEnumerator;
00344   nsCOMPtr<nsIAbCard> card;
00345   PRInt32 i;
00346   
00347   rv = directory->GetChildCards(getter_AddRefs(cardsEnumerator));
00348   if (NS_SUCCEEDED(rv) && cardsEnumerator)
00349   {
00350     nsCOMPtr<nsISupports> item;
00351     for (rv = cardsEnumerator->First(); NS_SUCCEEDED(rv); rv = cardsEnumerator->Next())
00352     {
00353       rv = cardsEnumerator->CurrentItem(getter_AddRefs(item));
00354       if (NS_SUCCEEDED(rv))
00355       {
00356         card = do_QueryInterface(item, &rv);
00357         if (NS_SUCCEEDED(rv))
00358         {
00359           // Skip if it's not a normal card (ie, they can't be added as members).
00360           PRBool isNormal;
00361           rv = card->GetIsANormalCard(&isNormal);
00362           if (NS_FAILED(rv) || !isNormal)
00363             continue;
00364 
00365           nsXPIDLString pEmailStr[MAX_NUMBER_OF_EMAIL_ADDRESSES]; //[0]=primary email, [1]=secondary email (no available with mailing list)
00366           nsXPIDLString pDisplayNameStr;
00367           nsXPIDLString pFirstNameStr;
00368           nsXPIDLString pLastNameStr;
00369           nsXPIDLString pNickNameStr;
00370           nsXPIDLString pNotesStr;
00371           PRUint32 popularityIndex = 0;
00372           PRBool bIsMailList;
00373 
00374           rv = card->GetIsMailList(&bIsMailList);
00375           if (NS_FAILED(rv))
00376             continue;
00377           if (bIsMailList)
00378           {
00379             rv = card->GetNotes(getter_Copies(pNotesStr));
00380             if (NS_FAILED(rv))
00381               continue;
00382           }
00383           else
00384           {
00385             for (i = 0 ; i < MAX_NUMBER_OF_EMAIL_ADDRESSES; i ++)
00386             {
00387               switch (i)
00388               {
00389                 case 0: rv = card->GetPrimaryEmail(getter_Copies(pEmailStr[i]));  break;
00390                 case 1: rv = card->GetSecondEmail(getter_Copies(pEmailStr[i]));   break;
00391                 default: return NS_ERROR_FAILURE;
00392               }
00393               if (NS_FAILED(rv))
00394                 continue;
00395 
00396               // Don't bother with card without an email address
00397               if (pEmailStr[i].IsEmpty())
00398                 continue;
00399 
00400               //...and does it looks like a valid address?
00401               if (pEmailStr[i].FindChar('@') <= 0)
00402                 pEmailStr[i].SetLength(0);
00403             }
00404             if (pEmailStr[0].IsEmpty() && pEmailStr[1].IsEmpty())
00405               continue;
00406           }
00407             
00408             //Now, retrive the user name and nickname
00409           rv = card->GetDisplayName(getter_Copies(pDisplayNameStr));
00410           if (NS_FAILED(rv))
00411               continue;
00412           rv = card->GetFirstName(getter_Copies(pFirstNameStr));
00413           if (NS_FAILED(rv))
00414               continue;
00415           rv = card->GetLastName(getter_Copies(pLastNameStr));
00416           if (NS_FAILED(rv))
00417               continue;
00418           rv = card->GetNickName(getter_Copies(pNickNameStr));
00419           if (NS_FAILED(rv))
00420               continue;
00421 
00422           (void) card->GetPopularityIndex(&popularityIndex);
00423 
00424           // in the address book a mailing list does not have an email address field. However,
00425           // we do "fix up" mailing lists in the UI sometimes to look like "My List <My List>"
00426           // if we are looking up an address and we are comparing it to a mailing list to see if it is a match
00427           // instead of just looking for an exact match on "My List", hijack the unused email address field 
00428           // and use that to test against "My List <My List>"
00429           if (bIsMailList)
00430             mParser->MakeFullAddressWString (pDisplayNameStr, pDisplayNameStr, getter_Copies(pEmailStr[0]));  
00431             
00432           for (i = 0 ; i < MAX_NUMBER_OF_EMAIL_ADDRESSES; i ++)
00433           {
00434             if (!bIsMailList && pEmailStr[i].IsEmpty())
00435               continue;
00436 
00437             if (CheckEntry(searchStr, pNickNameStr.get(), 
00438                                       pDisplayNameStr.get(), 
00439                                       pFirstNameStr.get(), 
00440                                       pLastNameStr.get(), pEmailStr[i].get()))
00441             {
00442               nsXPIDLString pDirName;
00443               if (mAutoCompleteCommentColumn == 1)
00444               {
00445                 rv = directory->GetDirName(getter_Copies(pDirName));
00446                 if (NS_FAILED(rv))
00447                   continue;
00448               }
00449 
00450               AddToResult(pNickNameStr.get(), pDisplayNameStr.get(), 
00451                           pFirstNameStr.get(), pLastNameStr.get(), 
00452                           pEmailStr[i].get(), pNotesStr.get(), 
00453                           pDirName.get(), popularityIndex, bIsMailList, PR_FALSE, results);
00454             }
00455           }
00456         }
00457       }
00458     }
00459   }
00460 
00461   return NS_OK;
00462 }
00463 
00464 static nsresult
00465 NeedToSearchReplicatedLDAPDirectories(nsIPrefBranch *aPrefs, PRBool *aNeedToSearch)
00466 {
00467   NS_ENSURE_ARG_POINTER(aPrefs);
00468   NS_ENSURE_ARG_POINTER(aNeedToSearch);
00469 
00470   // first check if the user is set up to do LDAP autocompletion
00471   nsresult rv = aPrefs->GetBoolPref("ldap_2.autoComplete.useDirectory", aNeedToSearch);
00472   NS_ENSURE_SUCCESS(rv, rv);
00473 
00474   // no need to search if not set up for LDAP autocompletion
00475   if (!*aNeedToSearch)
00476     return NS_OK;
00477 
00478   // now see if we are offline, if we are we need to search the
00479   // replicated directory
00480   nsCOMPtr <nsIIOService> ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
00481   NS_ENSURE_SUCCESS(rv,rv);
00482   rv = ioService->GetOffline(aNeedToSearch);
00483   NS_ENSURE_SUCCESS(rv,rv);
00484   return NS_OK;
00485 }
00486 
00487 nsresult 
00488 nsAbAutoCompleteSession::SearchReplicatedLDAPDirectories(nsIPrefBranch *aPref, nsAbAutoCompleteSearchString* searchStr, PRBool searchSubDirectory, nsIAutoCompleteResults* results)
00489 {
00490   NS_ENSURE_ARG_POINTER(aPref);
00491 
00492   nsXPIDLCString prefName;
00493   nsresult rv = aPref->GetCharPref("ldap_2.autoComplete.directoryServer", getter_Copies(prefName));
00494   NS_ENSURE_SUCCESS(rv,rv);
00495 
00496   if (prefName.IsEmpty())
00497     return NS_OK;
00498     
00499   // use the prefName to get the fileName pref
00500   nsCAutoString fileNamePref;
00501   fileNamePref = prefName + NS_LITERAL_CSTRING(".filename");
00502 
00503   nsXPIDLCString fileName;
00504   rv = aPref->GetCharPref(fileNamePref.get(), getter_Copies(fileName));
00505   NS_ENSURE_SUCCESS(rv,rv);
00506 
00507   // if there is no fileName, bail out now.
00508   if (fileName.IsEmpty())
00509     return NS_OK;
00510 
00511   // use the fileName to create the URI for the replicated directory
00512   nsCAutoString URI;
00513   URI = NS_LITERAL_CSTRING("moz-abmdbdirectory://") + fileName;
00514 
00515   // and then search the replicated directory
00516   return SearchDirectory(URI, searchStr, searchSubDirectory, results);
00517 }
00518 
00519 nsresult nsAbAutoCompleteSession::SearchDirectory(const nsACString& aURI, nsAbAutoCompleteSearchString* searchStr, PRBool searchSubDirectory, nsIAutoCompleteResults* results)
00520 {
00521     nsresult rv = NS_OK;
00522     nsCOMPtr<nsIRDFService> rdfService(do_GetService("@mozilla.org/rdf/rdf-service;1", &rv));
00523     NS_ENSURE_SUCCESS(rv, rv);
00524 
00525     nsCOMPtr <nsIRDFResource> resource;
00526     rv = rdfService->GetResource(aURI, getter_AddRefs(resource));
00527     NS_ENSURE_SUCCESS(rv, rv);
00528 
00529     // query interface 
00530     nsCOMPtr<nsIAbDirectory> directory(do_QueryInterface(resource, &rv));
00531     NS_ENSURE_SUCCESS(rv, rv);
00532     
00533     // when autocompleteing against directories, 
00534     // we only want to match against certain directories
00535     // we ask the directory if it wants to be used
00536     // for local autocompleting.
00537     PRBool searchDuringLocalAutocomplete;
00538     rv = directory->GetSearchDuringLocalAutocomplete(&searchDuringLocalAutocomplete);
00539     NS_ENSURE_SUCCESS(rv, rv);
00540     
00541     if (!searchDuringLocalAutocomplete)
00542       return NS_OK;
00543 
00544     if (!aURI.EqualsLiteral(kAllDirectoryRoot))
00545         rv = SearchCards(directory, searchStr, results);
00546     
00547     if (!searchSubDirectory)
00548         return rv;
00549   
00550     nsCOMPtr<nsISimpleEnumerator> subDirectories;
00551     if (NS_SUCCEEDED(directory->GetChildNodes(getter_AddRefs(subDirectories))) && subDirectories)
00552     {
00553         nsCOMPtr<nsISupports> item;
00554         PRBool hasMore;
00555         while (NS_SUCCEEDED(rv = subDirectories->HasMoreElements(&hasMore)) && hasMore)
00556         {
00557             if (NS_SUCCEEDED(subDirectories->GetNext(getter_AddRefs(item))))
00558             {
00559               directory = do_QueryInterface(item, &rv);
00560               if (NS_SUCCEEDED(rv))
00561               {
00562                 nsCOMPtr<nsIRDFResource> subResource(do_QueryInterface(item, &rv));
00563                 if (NS_SUCCEEDED(rv))
00564                 {
00565                     nsXPIDLCString URI;
00566                     subResource->GetValue(getter_Copies(URI));
00567                     rv = SearchDirectory(URI, searchStr, PR_TRUE, results);
00568                 }
00569               }
00570             }
00571         }
00572     }
00573     return rv;
00574 }
00575 
00576 nsresult nsAbAutoCompleteSession::SearchPreviousResults(nsAbAutoCompleteSearchString *searchStr, nsIAutoCompleteResults *previousSearchResult, nsIAutoCompleteResults* results)
00577 {
00578     if (!previousSearchResult)
00579         return NS_ERROR_NULL_POINTER;
00580         
00581     nsXPIDLString prevSearchString;
00582     nsresult rv;
00583 
00584     rv = previousSearchResult->GetSearchString(getter_Copies(prevSearchString));
00585     NS_ENSURE_SUCCESS(rv, rv);
00586     
00587     if (!(const PRUnichar*)prevSearchString || ((const PRUnichar*)prevSearchString)[0] == 0)
00588         return NS_ERROR_FAILURE;
00589     
00590     PRUint32 prevSearchStrLen = nsCRT::strlen(prevSearchString);
00591     if (searchStr->mFullStringLen < prevSearchStrLen ||
00592         CommonPrefix(searchStr->mFullString, prevSearchString, prevSearchStrLen))
00593         return NS_ERROR_ABORT;
00594 
00595     nsCOMPtr<nsISupportsArray> array;
00596     rv = previousSearchResult->GetItems(getter_AddRefs(array));
00597     if (NS_SUCCEEDED(rv))
00598     {
00599         PRUint32 nbrOfItems;
00600         PRUint32 i;
00601         PRUint32 pos;
00602         
00603         rv = array->Count(&nbrOfItems);
00604         if (NS_FAILED(rv) || nbrOfItems <= 0)
00605             return NS_ERROR_FAILURE;
00606         
00607         nsCOMPtr<nsISupports> item;
00608         nsCOMPtr<nsIAutoCompleteItem> resultItem;
00609         nsAbAutoCompleteParam *param;
00610 
00611         for (i = 0, pos = 0; i < nbrOfItems; i ++, pos ++)
00612         {
00613             rv = array->QueryElementAt(pos, NS_GET_IID(nsIAutoCompleteItem),
00614                                            getter_AddRefs(resultItem));
00615             NS_ENSURE_SUCCESS(rv, rv);
00616               
00617             rv = resultItem->GetParam(getter_AddRefs(item));
00618             NS_ENSURE_SUCCESS(rv, rv);
00619             if (!item)
00620                 return NS_ERROR_FAILURE;
00621 
00622             param = (nsAbAutoCompleteParam *)(void *)item;
00623             
00624             if (CheckEntry(searchStr, param->mNickName, param->mDisplayName,  param->mFirstName,  param->mLastName, param->mEmailAddress))
00625                 AddToResult(param->mNickName, param->mDisplayName, 
00626                             param->mFirstName, param->mLastName, 
00627                             param->mEmailAddress, param->mNotes, 
00628                             param->mDirName, param->mPopularityIndex, param->mIsMailList, PR_FALSE,
00629                             results);
00630         }
00631         return NS_OK;
00632     }
00633 
00634     return NS_ERROR_ABORT;
00635 }
00636 
00637 NS_IMETHODIMP nsAbAutoCompleteSession::OnStartLookup(const PRUnichar *uSearchString, nsIAutoCompleteResults *previousSearchResult, nsIAutoCompleteListener *listener)
00638 {
00639     nsresult rv = NS_OK;
00640     
00641     if (!listener)
00642         return NS_ERROR_NULL_POINTER;
00643     
00644     PRBool enableLocalAutocomplete;
00645     PRBool enableReplicatedLDAPAutocomplete;
00646 
00647     nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00648     NS_ENSURE_SUCCESS(rv, rv);
00649 
00650     // check if using autocomplete for local address books
00651     rv = prefs->GetBoolPref("mail.enable_autocomplete", &enableLocalAutocomplete);
00652     NS_ENSURE_SUCCESS(rv,rv);
00653 
00654     rv = NeedToSearchReplicatedLDAPDirectories(prefs, &enableReplicatedLDAPAutocomplete);
00655     NS_ENSURE_SUCCESS(rv,rv);
00656 
00657     if (uSearchString[0] == 0 || (!enableLocalAutocomplete && !enableReplicatedLDAPAutocomplete))
00658     {
00659         listener->OnAutoComplete(nsnull, nsIAutoCompleteStatus::ignored);
00660         return NS_OK;
00661     }
00662 
00663     // figure out what we're supposed to do about the comment column, and 
00664     // remember it for when the results start coming back
00665     //
00666     rv = prefs->GetIntPref("mail.autoComplete.commentColumn", 
00667                            &mAutoCompleteCommentColumn);
00668     if (NS_FAILED(rv)) {
00669       mAutoCompleteCommentColumn = 0;
00670     }
00671 
00672 
00673     // strings with commas (commas denote multiple names) should be ignored for 
00674     // autocomplete purposes
00675     PRInt32 i;
00676     for (i = nsCRT::strlen(uSearchString) - 1; i >= 0; i --)
00677         if (uSearchString[i] == ',')
00678         {
00679             listener->OnAutoComplete(nsnull, nsIAutoCompleteStatus::ignored);
00680             return NS_OK;
00681         }
00682         
00683     nsAbAutoCompleteSearchString searchStrings(uSearchString);
00684     
00685     nsCOMPtr<nsIAutoCompleteResults> results = do_CreateInstance(NS_AUTOCOMPLETERESULTS_CONTRACTID, &rv);
00686     if (NS_SUCCEEDED(rv))
00687       if (NS_FAILED(SearchPreviousResults(&searchStrings, previousSearchResult, results)))
00688       {
00689         nsresult rv1,rv2;
00690 
00691         if (enableLocalAutocomplete) {
00692           rv1 = SearchDirectory(NS_LITERAL_CSTRING(kAllDirectoryRoot), &searchStrings,
00693                                 PR_TRUE, results);
00694           NS_ASSERTION(NS_SUCCEEDED(rv1), "searching all local directories failed");
00695         }
00696         else 
00697           rv1 = NS_OK;
00698 
00699         if (enableReplicatedLDAPAutocomplete) {
00700           rv2 = SearchReplicatedLDAPDirectories(prefs, &searchStrings, PR_TRUE, results);
00701           NS_ASSERTION(NS_SUCCEEDED(rv2), "searching all replicated LDAP directories failed");
00702         }
00703         else
00704           rv2 = NS_OK;
00705         
00706         // only bail out if both failed.  otherwise, we have some results we can use
00707         if (NS_FAILED(rv1) && NS_FAILED(rv2))
00708           rv = NS_ERROR_FAILURE;
00709         else 
00710           rv = NS_OK;
00711     }
00712                 
00713     AutoCompleteStatus status = nsIAutoCompleteStatus::failed;
00714     if (NS_SUCCEEDED(rv) && results)
00715     {
00716         PRBool addedDefaultItem = PR_FALSE;
00717 
00718         results->SetSearchString(uSearchString);
00719         results->SetDefaultItemIndex(-1);
00720         if (mDefaultDomain[0] != 0)
00721         {
00722             PRUnichar emptyStr = 0;
00723             AddToResult(&emptyStr, uSearchString, &emptyStr, &emptyStr, 
00724                         &emptyStr, &emptyStr, &emptyStr, 0 /* popularity index */, PR_FALSE, 
00725                         PR_TRUE, results);
00726             addedDefaultItem = PR_TRUE;
00727         }
00728 
00729         nsCOMPtr<nsISupportsArray> array;
00730         rv = results->GetItems(getter_AddRefs(array));
00731         if (NS_SUCCEEDED(rv))
00732         {
00733           //If we have more than a match (without counting the default item), we don't
00734           //want to auto complete the user input therefore set the default item index to -1
00735 
00736           PRUint32 nbrOfItems;
00737           rv = array->Count(&nbrOfItems);
00738           if (NS_SUCCEEDED(rv))
00739             if (nbrOfItems == 0)
00740               status = nsIAutoCompleteStatus::noMatch;
00741             else
00742             {
00743               status = nsIAutoCompleteStatus::matchFound;
00744               if (addedDefaultItem)
00745               {
00746                 // If we have at least one REAL match then make it the default item. If we don't have any matches,
00747                 // just the default domain, then don't install a default item index on the widget.
00748                 results->SetDefaultItemIndex(nbrOfItems > 1 ? 1 : -1);
00749               }
00750               else
00751                 results->SetDefaultItemIndex(0);  
00752             }
00753         }
00754     }
00755     listener->OnAutoComplete(results, status);
00756     
00757     return NS_OK;
00758 }
00759 
00760 NS_IMETHODIMP nsAbAutoCompleteSession::OnStopLookup()
00761 {
00762     return NS_OK;
00763 }
00764 
00765 NS_IMETHODIMP nsAbAutoCompleteSession::OnAutoComplete(const PRUnichar *searchString, nsIAutoCompleteResults *previousSearchResult, nsIAutoCompleteListener *listener)
00766 {
00767     return OnStartLookup(searchString, previousSearchResult, listener);
00768 }
00769 
00770 NS_IMETHODIMP nsAbAutoCompleteSession::GetDefaultDomain(PRUnichar * *aDefaultDomain)
00771 {
00772     if (!aDefaultDomain)
00773         return NS_ERROR_NULL_POINTER;
00774 
00775     *aDefaultDomain = ToNewUnicode(mDefaultDomain);
00776     return NS_OK;
00777 }
00778 
00779 NS_IMETHODIMP nsAbAutoCompleteSession::SetDefaultDomain(const PRUnichar * aDefaultDomain)
00780 {
00781     mDefaultDomain = aDefaultDomain;
00782     return NS_OK;
00783 }
00784 
00785 NS_IMPL_ISUPPORTS1(nsAbAutoCompleteParam, nsISupports)
00786 
00787 nsAbAutoCompleteSearchString::nsAbAutoCompleteSearchString(const PRUnichar *uSearchString)
00788 {
00789   mFullString = nsCRT::strdup(uSearchString);
00790   mFullStringLen = nsCRT::strlen(mFullString);
00791   
00792   PRUint32 i;
00793   PRUnichar * aPtr;
00794   for (i = 0, aPtr = (PRUnichar*)mFullString; i < mFullStringLen; i ++, aPtr ++)
00795   {
00796     if (*aPtr == ' ')
00797     {
00798       mFirstPart = nsCRT::strndup(mFullString, i);
00799       mFirstPartLen = i;
00800       mSecondPart = nsCRT::strdup(++aPtr);
00801       mSecondPartLen = mFullStringLen - i - 1;
00802       return;
00803     }
00804   }
00805   
00806   /* If we did not find a space in the search string, initialize the first and second part as null */
00807   mFirstPart = nsnull;
00808   mFirstPartLen = 0;
00809   mSecondPart = nsnull;
00810   mSecondPartLen = 0;
00811 }
00812 
00813 nsAbAutoCompleteSearchString::~nsAbAutoCompleteSearchString()
00814 {
00815   if (mFullString)
00816      nsCRT::free((PRUnichar*)mFullString);
00817   if (mFirstPart)
00818      nsCRT::free((PRUnichar*)mFirstPart);
00819   if (mSecondPart)
00820      nsCRT::free((PRUnichar*)mSecondPart);
00821 }