Back to index

lightning-sunbird  0.9+nobinonly
nsAbOutlookDirectory.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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  * Sun Microsystems, Inc.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Created by Cyrille Moureaux <Cyrille.Moureaux@sun.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 #include "nsAbOutlookDirectory.h"
00039 #include "nsAbWinHelper.h"
00040 
00041 #include "nsIRDFService.h"
00042 
00043 #include "nsAbBaseCID.h"
00044 #include "nsIAbCard.h"
00045 #include "nsXPIDLString.h"
00046 #include "nsAbDirectoryQuery.h"
00047 #include "nsIAbBooleanExpression.h"
00048 #include "nsIAddressBook.h"
00049 #include "nsIAddrBookSession.h"
00050 #include "nsAbQueryStringToExpression.h"
00051 #include "nsAbUtils.h"
00052 #include "nsIProxyObjectManager.h"
00053 #include "nsEnumeratorUtils.h"
00054 #include "prlog.h"
00055 #include "prthread.h"
00056 
00057 #ifdef PR_LOGGING
00058 static PRLogModuleInfo* gAbOutlookDirectoryLog
00059     = PR_NewLogModule("nsAbOutlookDirectoryLog");
00060 #endif
00061 
00062 #define PRINTF(args) PR_LOG(gAbOutlookDirectoryLog, PR_LOG_DEBUG, args)
00063 
00064 
00065 // Class for the int key
00066 class nsIntegerKey : public nsHashKey
00067 {
00068 public:
00069     nsIntegerKey(PRInt32 aInteger) 
00070         : mInteger(aInteger) {
00071     }
00072     virtual ~nsIntegerKey(void) {}
00073     
00074     PRUint32 HashCode(void) const { return NS_STATIC_CAST(PRUint32, mInteger) ; }
00075     PRBool Equals(const nsHashKey *aKey) const { 
00076         return aKey && NS_REINTERPRET_CAST(const nsIntegerKey *, aKey)->mInteger == mInteger ; 
00077     }
00078     nsHashKey *Clone(void) const { return new nsIntegerKey(mInteger) ; }
00079     
00080 protected:
00081     PRInt32 mInteger ;
00082     
00083 private:
00084 } ;
00085 
00086 nsAbOutlookDirectory::nsAbOutlookDirectory(void)
00087 : nsAbDirectoryRDFResource(), nsAbDirProperty(), nsAbDirSearchListenerContext(), 
00088 mQueryThreads(16, PR_TRUE), mCurrentQueryId(0), mSearchContext(-1), 
00089 mAbWinType(nsAbWinType_Unknown), mMapiData(nsnull)
00090 {
00091     mMapiData = new nsMapiEntry ;
00092     mProtector = PR_NewLock() ;
00093 }
00094 
00095 nsAbOutlookDirectory::~nsAbOutlookDirectory(void)
00096 {
00097     if (mMapiData) { delete mMapiData ; }
00098     if (mProtector) { PR_DestroyLock(mProtector) ; }
00099 }
00100 
00101 NS_IMPL_ISUPPORTS_INHERITED3(nsAbOutlookDirectory, nsAbDirectoryRDFResource, 
00102                              nsIAbDirectory, nsIAbDirectoryQuery, nsIAbDirectorySearch)
00103 
00104 // nsIRDFResource method
00105 NS_IMETHODIMP nsAbOutlookDirectory::Init(const char *aUri)
00106 {
00107     nsresult retCode = nsAbDirectoryRDFResource::Init(aUri) ;
00108     
00109     NS_ENSURE_SUCCESS(retCode, retCode) ;
00110     nsCAutoString entry ;
00111     nsCAutoString stub ;
00112 
00113     mAbWinType = getAbWinType(kOutlookDirectoryScheme, mURINoQuery.get(), stub, entry) ;
00114     if (mAbWinType == nsAbWinType_Unknown) {
00115         PRINTF(("Huge problem URI=%s.\n", mURINoQuery)) ;
00116         return NS_ERROR_INVALID_ARG ;
00117     }
00118     nsAbWinHelperGuard mapiAddBook (mAbWinType) ;
00119     nsString prefix ;
00120     nsAutoString unichars ;
00121     ULONG objectType = 0 ;
00122 
00123     if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; }
00124     mMapiData->Assign(entry) ;
00125     if (!mapiAddBook->GetPropertyLong(*mMapiData, PR_OBJECT_TYPE, objectType)) {
00126         PRINTF(("Cannot get type.\n")) ;
00127         return NS_ERROR_FAILURE ;
00128     }
00129     if (!mapiAddBook->GetPropertyUString(*mMapiData, PR_DISPLAY_NAME_W, unichars)) {
00130         PRINTF(("Cannot get name.\n")) ;
00131         return NS_ERROR_FAILURE ;
00132     }
00133     if (mAbWinType == nsAbWinType_Outlook) { prefix.AssignLiteral("OP ") ; }
00134     else { prefix.AssignLiteral("OE ") ; }
00135     prefix.Append(unichars) ;
00136     SetDirName(prefix.get()) ; 
00137     if (objectType == MAPI_DISTLIST) {
00138         SetDirName(unichars.get()) ; 
00139         SetIsMailList(PR_TRUE) ;
00140     }
00141     else { 
00142         SetIsMailList(PR_FALSE) ;
00143     }
00144     return UpdateAddressList() ;
00145 }
00146 
00147 // nsIAbDirectory methods
00148 NS_IMETHODIMP nsAbOutlookDirectory::GetChildNodes(nsISimpleEnumerator **aNodes)
00149 {
00150     if (!aNodes) { return NS_ERROR_NULL_POINTER ; }
00151     *aNodes = nsnull ;
00152     nsCOMPtr<nsISupportsArray> nodeList ;
00153     nsresult retCode = NS_OK ;
00154     
00155     if (mIsQueryURI) {
00156         NS_NewISupportsArray(getter_AddRefs(nodeList)) ;
00157     }
00158     else {
00159         retCode = GetChildNodes(getter_AddRefs(nodeList)) ;
00160     }
00161     if (NS_SUCCEEDED(retCode))
00162       retCode = NS_NewArrayEnumerator(aNodes, nodeList);
00163     return retCode;
00164 }
00165 
00166 NS_IMETHODIMP nsAbOutlookDirectory::GetChildCards(nsIEnumerator **aCards)
00167 {
00168     if (!aCards) { return NS_ERROR_NULL_POINTER ; }
00169     *aCards = nsnull ;
00170     nsCOMPtr<nsISupportsArray> cardList ;
00171     nsresult retCode ;
00172     
00173     mCardList.Reset() ;
00174     if (mIsQueryURI) {
00175         retCode = StartSearch() ;
00176         NS_NewISupportsArray(getter_AddRefs(cardList)) ;
00177     }
00178     else {
00179         retCode = GetChildCards(getter_AddRefs(cardList), nsnull) ;
00180     }
00181     if (NS_SUCCEEDED(retCode)) {
00182         // Fill the results array and update the card list
00183         // Also update the address list and notify any changes.
00184         PRUint32 nbCards = 0 ;
00185         nsCOMPtr<nsISupports> element ;
00186         
00187         cardList->Enumerate(aCards) ;
00188         cardList->Count(&nbCards) ;
00189         for (PRUint32 i = 0 ; i < nbCards ; ++ i) {
00190             cardList->GetElementAt(i, getter_AddRefs(element)) ;
00191             nsVoidKey newKey (NS_STATIC_CAST(void *, element)) ;
00192             nsCOMPtr<nsISupports> oldElement = mCardList.Get(&newKey) ;
00193 
00194             if (!oldElement) {
00195                 // We are dealing with a new element (probably directly
00196                 // added from Outlook), we may need to sync m_AddressList
00197                 mCardList.Put(&newKey, element) ;
00198                 nsCOMPtr<nsIAbCard> card (do_QueryInterface(element, &retCode)) ;
00199 
00200                 NS_ENSURE_SUCCESS(retCode, retCode) ;
00201                 PRBool isMailList = PR_FALSE ;
00202 
00203                 retCode = card->GetIsMailList(&isMailList) ;
00204                 NS_ENSURE_SUCCESS(retCode, retCode) ;
00205                 if (isMailList) {
00206                     // We can have mailing lists only in folder, 
00207                     // we must add the directory to m_AddressList
00208                     nsXPIDLCString mailListUri ;
00209 
00210                     retCode = card->GetMailListURI(getter_Copies(mailListUri)) ;
00211                     NS_ENSURE_SUCCESS(retCode, retCode) ;
00212                     nsCOMPtr<nsIRDFResource> resource ;
00213 
00214                     retCode = gRDFService->GetResource(mailListUri, getter_AddRefs(resource)) ;
00215                     NS_ENSURE_SUCCESS(retCode, retCode) ;
00216                     nsCOMPtr<nsIAbDirectory> mailList(do_QueryInterface(resource, &retCode)) ;
00217                     
00218                     NS_ENSURE_SUCCESS(retCode, retCode) ;
00219                     m_AddressList->AppendElement(mailList) ;
00220                     NotifyItemAddition(mailList) ;
00221                 }
00222                 else if (m_IsMailList) {
00223                     m_AddressList->AppendElement(card) ;
00224                     NotifyItemAddition(card) ;
00225                 }
00226             }
00227             else {
00228                 NS_ASSERTION(oldElement == element, "Different card stored") ;
00229             }
00230         }
00231     }
00232     return retCode ;
00233 }
00234 
00235 NS_IMETHODIMP nsAbOutlookDirectory::HasCard(nsIAbCard *aCard, PRBool *aHasCard)
00236 {
00237     if (!aCard || !aHasCard) { return NS_ERROR_NULL_POINTER ; }
00238     nsVoidKey key (NS_STATIC_CAST(void *, aCard)) ;
00239 
00240     *aHasCard = mCardList.Exists(&key) ;
00241     return NS_OK ;
00242 }
00243 
00244 NS_IMETHODIMP nsAbOutlookDirectory::HasDirectory(nsIAbDirectory *aDirectory, PRBool *aHasDirectory)
00245 {
00246     if (!aDirectory || !aHasDirectory ) { return NS_ERROR_NULL_POINTER ; }
00247     *aHasDirectory = (m_AddressList->IndexOf(aDirectory) >= 0) ;
00248     return NS_OK ;
00249 }
00250 
00251 static nsresult ExtractEntryFromUri(nsIRDFResource *aResource, nsCString &aEntry,
00252                                     const char *aPrefix)
00253 {
00254     aEntry.Truncate() ;
00255     if (!aPrefix ) { return NS_ERROR_NULL_POINTER ; }
00256     nsXPIDLCString uri ;
00257     nsresult retCode = aResource->GetValue(getter_Copies(uri)) ;
00258     
00259     NS_ENSURE_SUCCESS(retCode, retCode) ;
00260     nsCAutoString stub ;
00261     nsAbWinType objType = getAbWinType(aPrefix, uri.get(), stub, aEntry) ;
00262 
00263     return NS_OK ;
00264 }
00265 
00266 static nsresult ExtractCardEntry(nsIAbCard *aCard, nsCString& aEntry)
00267 {
00268     aEntry.Truncate() ;
00269     nsresult retCode = NS_OK ;
00270     nsCOMPtr<nsIRDFResource> resource (do_QueryInterface(aCard, &retCode)) ;
00271     
00272     // Receiving a non-RDF card is accepted
00273     if (NS_FAILED(retCode)) { return NS_OK ; }
00274     return ExtractEntryFromUri(resource, aEntry, kOutlookCardScheme) ;
00275 }
00276 
00277 static nsresult ExtractDirectoryEntry(nsIAbDirectory *aDirectory, nsCString& aEntry)
00278 {
00279     aEntry.Truncate() ;
00280     nsresult retCode = NS_OK ;
00281     nsCOMPtr<nsIRDFResource> resource (do_QueryInterface(aDirectory, &retCode)) ;
00282     
00283     // Receiving a non-RDF directory is accepted
00284     if (NS_FAILED(retCode)) { return NS_OK ; }
00285     return ExtractEntryFromUri(resource, aEntry, kOutlookDirectoryScheme) ;
00286 }
00287 
00288 NS_IMETHODIMP nsAbOutlookDirectory::DeleteCards(nsISupportsArray *aCardList)
00289 {
00290     if (mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; }
00291     if (!aCardList) { return NS_ERROR_NULL_POINTER ; }
00292     PRUint32 nbCards = 0 ;
00293     nsresult retCode = NS_OK ;
00294     nsAbWinHelperGuard mapiAddBook (mAbWinType) ;
00295 
00296     if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; }
00297     
00298     retCode = aCardList->Count(&nbCards) ;
00299     NS_ENSURE_SUCCESS(retCode, retCode) ;
00300     PRUint32 i = 0 ;
00301     nsCOMPtr<nsISupports> element ;
00302     nsCAutoString entryString ;
00303     nsMapiEntry cardEntry ;
00304 
00305     for (i = 0 ; i < nbCards ; ++ i) {
00306         retCode = aCardList->GetElementAt(i, getter_AddRefs(element)) ;
00307         NS_ENSURE_SUCCESS(retCode, retCode) ;
00308         nsCOMPtr<nsIAbCard> card (do_QueryInterface(element, &retCode)) ;
00309         
00310         NS_ENSURE_SUCCESS(retCode, retCode) ;
00311 
00312         retCode = ExtractCardEntry(card, entryString) ;
00313         if (NS_SUCCEEDED(retCode) && !entryString.IsEmpty()) {
00314 
00315             cardEntry.Assign(entryString) ;
00316             if (!mapiAddBook->DeleteEntry(*mMapiData, cardEntry)) {
00317                 PRINTF(("Cannot delete card %s.\n", entryString.get())) ;
00318             }
00319             else {
00320                 nsVoidKey key (NS_STATIC_CAST(void *, element)) ;
00321                 
00322                 mCardList.Remove(&key) ;
00323                 if (m_IsMailList) { m_AddressList->RemoveElement(element) ; }
00324                 retCode = NotifyItemDeletion(element) ;
00325                 NS_ENSURE_SUCCESS(retCode, retCode) ;
00326             }
00327         }
00328         else {
00329             PRINTF(("Card doesn't belong in this directory.\n")) ;
00330         }
00331     }
00332     return NS_OK ;
00333 }
00334 
00335 NS_IMETHODIMP nsAbOutlookDirectory::ModifyDirectory(nsIAbDirectory *directory, nsIAbDirectoryProperties *aProperties)
00336 {
00337   return NS_ERROR_NOT_IMPLEMENTED;
00338 }
00339 
00340 
00341 NS_IMETHODIMP nsAbOutlookDirectory::DeleteDirectory(nsIAbDirectory *aDirectory)
00342 {
00343     if (mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; }
00344     if (!aDirectory) { return NS_ERROR_NULL_POINTER ; }
00345     nsresult retCode = NS_OK ;
00346     nsAbWinHelperGuard mapiAddBook (mAbWinType) ;
00347     nsCAutoString entryString ;
00348 
00349     if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; }
00350     retCode = ExtractDirectoryEntry(aDirectory, entryString) ;
00351     if (NS_SUCCEEDED(retCode) && !entryString.IsEmpty()) {
00352         nsMapiEntry directoryEntry ;
00353 
00354         directoryEntry.Assign(entryString) ;
00355         if (!mapiAddBook->DeleteEntry(*mMapiData, directoryEntry)) {
00356             PRINTF(("Cannot delete directory %s.\n", entryString.get())) ;
00357         }
00358         else {
00359             m_AddressList->RemoveElement(aDirectory) ;
00360             retCode = NotifyItemDeletion(aDirectory) ;
00361             NS_ENSURE_SUCCESS(retCode, retCode) ;
00362         }
00363     }
00364     else {
00365         PRINTF(("Directory doesn't belong to this folder.\n")) ;
00366     }
00367     return retCode ;
00368 }
00369 
00370 NS_IMETHODIMP nsAbOutlookDirectory::AddCard(nsIAbCard *aData, nsIAbCard **addedCard)
00371 {
00372     if (mIsQueryURI)
00373       return NS_ERROR_NOT_IMPLEMENTED;
00374 
00375     NS_ENSURE_ARG_POINTER(aData);
00376 
00377     nsresult retCode = NS_OK ;
00378     PRBool hasCard = PR_FALSE ;
00379     
00380     retCode = HasCard(aData, &hasCard) ;
00381     NS_ENSURE_SUCCESS(retCode, retCode) ;
00382     if (hasCard) {
00383         PRINTF(("Has card.\n")) ;
00384         NS_IF_ADDREF(*addedCard = aData);
00385         return NS_OK ; 
00386     }
00387     retCode = CreateCard(aData, addedCard) ;
00388     NS_ENSURE_SUCCESS(retCode, retCode) ;
00389     nsVoidKey newKey (NS_STATIC_CAST(void *, *addedCard)) ;
00390     
00391     mCardList.Put(&newKey, *addedCard) ;
00392     if (m_IsMailList) { m_AddressList->AppendElement(*addedCard) ; }
00393     NotifyItemAddition(*addedCard) ;
00394     return retCode ;
00395 }
00396 
00397 NS_IMETHODIMP nsAbOutlookDirectory::DropCard(nsIAbCard *aData, PRBool needToCopyCard)
00398 {
00399     nsCOMPtr <nsIAbCard> addedCard;
00400     return AddCard(aData, getter_AddRefs(addedCard));
00401 }
00402 
00403 NS_IMETHODIMP nsAbOutlookDirectory::AddMailList(nsIAbDirectory *aMailList)
00404 {
00405     if (mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; }
00406     if (!aMailList) { return NS_ERROR_NULL_POINTER ; }
00407     if (m_IsMailList) { return NS_OK ; }
00408     nsresult retCode = NS_OK ;
00409     nsAbWinHelperGuard mapiAddBook (mAbWinType) ;
00410     nsCAutoString entryString ;
00411     nsMapiEntry newEntry ;
00412     PRBool didCopy = PR_FALSE ;
00413 
00414     if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; }
00415     retCode = ExtractDirectoryEntry(aMailList, entryString) ;
00416     if (NS_SUCCEEDED(retCode) && !entryString.IsEmpty()) {
00417         nsMapiEntry sourceEntry ;
00418 
00419         sourceEntry.Assign(entryString) ;
00420         mapiAddBook->CopyEntry(*mMapiData, sourceEntry, newEntry) ;
00421     }
00422     if (newEntry.mByteCount == 0) {
00423         if (!mapiAddBook->CreateDistList(*mMapiData, newEntry)) {
00424             return NS_ERROR_FAILURE ; 
00425         }
00426     }
00427     else { didCopy = PR_TRUE ; }
00428     newEntry.ToString(entryString) ;
00429     nsCAutoString uri ;
00430 
00431     buildAbWinUri(kOutlookDirectoryScheme, mAbWinType, uri) ;
00432     uri.Append(entryString) ;
00433     nsCOMPtr<nsIRDFResource> resource ;
00434     retCode = gRDFService->GetResource(uri, getter_AddRefs(resource)) ;
00435     NS_ENSURE_SUCCESS(retCode, retCode) ;
00436     nsCOMPtr<nsIAbDirectory> newList(do_QueryInterface(resource, &retCode)) ;
00437     
00438     NS_ENSURE_SUCCESS(retCode, retCode) ;
00439     if (!didCopy) {
00440         retCode = newList->CopyMailList(aMailList) ;
00441         NS_ENSURE_SUCCESS(retCode, retCode) ;
00442         retCode = newList->EditMailListToDatabase(mURINoQuery.get(), nsnull) ;
00443         NS_ENSURE_SUCCESS(retCode, retCode) ;
00444     }
00445     m_AddressList->AppendElement(newList) ;
00446     NotifyItemAddition(newList) ;
00447     return retCode ;
00448 }
00449 
00450 NS_IMETHODIMP nsAbOutlookDirectory::EditMailListToDatabase(const char *aUri, nsIAbCard *listCard)
00451 {
00452     if (mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; }
00453     nsresult retCode = NS_OK ;
00454     nsXPIDLString name ;
00455     nsAbWinHelperGuard mapiAddBook (mAbWinType) ;
00456 
00457     if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; }
00458     retCode = GetDirName(getter_Copies(name)) ;
00459     NS_ENSURE_SUCCESS(retCode, retCode) ;
00460     if (!mapiAddBook->SetPropertyUString(*mMapiData, PR_DISPLAY_NAME_W, name.get())) {
00461         return NS_ERROR_FAILURE ;
00462     }
00463     retCode = CommitAddressList() ;
00464     return retCode ;
00465 }
00466 
00467 struct OutlookTableAttr
00468 {
00469     const char *mOuterName ;
00470     ULONG mMapiProp ;
00471 } ;
00472 
00473 /*static const OutlookTableAttr OutlookTableStringToProp [] = 
00474 {
00475 {"FirstName", PR_GIVEN_NAME_W},
00476 {"LastName", PR_SURNAME_W},
00477 {"DisplayName", PR_DISPLAY_NAME_W},
00478 {"NickName", PR_NICKNAME_W},
00479 {"PrimaryEmail", PR_EMAIL_ADDRESS_W},
00480 {"WorkPhone", PR_BUSINESS_TELEPHONE_NUMBER_W},
00481 {"HomePhone", PR_HOME_TELEPHONE_NUMBER_W},
00482 {"FaxNumber", PR_BUSINESS_FAX_NUMBER_W},
00483 {"PagerNumber", PR_PAGER_TELEPHONE_NUMBER_W},
00484 {"CellularNumber", PR_MOBILE_TELEPHONE_NUMBER_W},
00485 {"HomeAddress", PR_HOME_ADDRESS_STREET_W},
00486 {"HomeCity", PR_HOME_ADDRESS_CITY_W},
00487 {"HomeState", PR_HOME_ADDRESS_STATE_OR_PROVINCE_W},
00488 {"HomeZipCode", PR_HOME_ADDRESS_POSTAL_CODE_W},
00489 {"HomeCountry", PR_HOME_ADDRESS_COUNTRY_W},
00490 {"WorkAddress", PR_BUSINESS_ADDRESS_STREET_W}, 
00491 {"WorkCity", PR_BUSINESS_ADDRESS_CITY_W},
00492 {"WorkState", PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_W},
00493 {"WorkZipCode", PR_BUSINESS_ADDRESS_POSTAL_CODE_W},
00494 {"WorkCountry", PR_BUSINESS_ADDRESS_COUNTRY_W},
00495 {"JobTitle", PR_TITLE_W},
00496 {"Department", PR_DEPARTMENT_NAME_W},
00497 {"Company", PR_COMPANY_NAME_W},
00498 {"WebPage1", PR_BUSINESS_HOME_PAGE_W},
00499 {"WebPage2", PR_PERSONAL_HOME_PAGE_W},
00500 // For the moment, we don't support querying on the birthday
00501 // sub-elements.
00502 #if 0
00503 {"BirthYear", PR_BIRTHDAY},
00504 {"BirthMonth", PR_BIRTHDAY}, 
00505 {"BirthDay", PR_BIRTHDAY},
00506 #endif // 0
00507 {"Notes", PR_COMMENT_W}
00508 } ;*/
00509 
00510 // Here, we are forced to use the Ascii versions of the properties
00511 // instead of the widechar ones, because the content restriction
00512 // operators do not work on unicode strings in mapi.
00513 static const OutlookTableAttr OutlookTableStringToProp [] = 
00514 {
00515     // replace "PrimaryEmail" with kPriEmailColumn etc.
00516     {"FirstName", PR_GIVEN_NAME_A},
00517     {"LastName", PR_SURNAME_A},
00518     {"DisplayName", PR_DISPLAY_NAME_A},
00519     {"NickName", PR_NICKNAME_A},
00520     {"PrimaryEmail", PR_EMAIL_ADDRESS_A},
00521     {"WorkPhone", PR_BUSINESS_TELEPHONE_NUMBER_A},
00522     {"HomePhone", PR_HOME_TELEPHONE_NUMBER_A},
00523     {"FaxNumber", PR_BUSINESS_FAX_NUMBER_A},
00524     {"PagerNumber", PR_PAGER_TELEPHONE_NUMBER_A},
00525     {"CellularNumber", PR_MOBILE_TELEPHONE_NUMBER_A},
00526     {"HomeAddress", PR_HOME_ADDRESS_STREET_A},
00527     {"HomeCity", PR_HOME_ADDRESS_CITY_A},
00528     {"HomeState", PR_HOME_ADDRESS_STATE_OR_PROVINCE_A},
00529     {"HomeZipCode", PR_HOME_ADDRESS_POSTAL_CODE_A},
00530     {"HomeCountry", PR_HOME_ADDRESS_COUNTRY_A},
00531     {"WorkAddress", PR_BUSINESS_ADDRESS_STREET_A}, 
00532     {"WorkCity", PR_BUSINESS_ADDRESS_CITY_A},
00533     {"WorkState", PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_A},
00534     {"WorkZipCode", PR_BUSINESS_ADDRESS_POSTAL_CODE_A},
00535     {"WorkCountry", PR_BUSINESS_ADDRESS_COUNTRY_A},
00536     {"JobTitle", PR_TITLE_A},
00537     {"Department", PR_DEPARTMENT_NAME_A},
00538     {"Company", PR_COMPANY_NAME_A},
00539     {"WebPage1", PR_BUSINESS_HOME_PAGE_A},
00540     {"WebPage2", PR_PERSONAL_HOME_PAGE_A},
00541     // For the moment, we don't support querying on the birthday
00542     // sub-elements.
00543 #if 0
00544     {"BirthYear", PR_BIRTHDAY},
00545     {"BirthMonth", PR_BIRTHDAY}, 
00546     {"BirthDay", PR_BIRTHDAY},
00547 #endif // 0
00548     {"Notes", PR_COMMENT_A}
00549 } ;
00550 
00551 static const PRUint32 OutlookTableNbProps = sizeof(OutlookTableStringToProp) /
00552                                             sizeof(OutlookTableStringToProp [0]) ;
00553 
00554 static ULONG findPropertyTag(const char *aName) {
00555     PRUint32 i = 0 ;
00556     
00557     for (i = 0 ; i < OutlookTableNbProps ; ++ i) {
00558         if (nsCRT::strcmp(aName, OutlookTableStringToProp [i].mOuterName) == 0) {
00559             return OutlookTableStringToProp [i].mMapiProp ;
00560         }
00561     }
00562     return 0 ;
00563 }
00564 
00565 static nsresult BuildRestriction(nsIAbBooleanConditionString *aCondition, 
00566                                  SRestriction& aRestriction,
00567                                  PRBool& aSkipItem)
00568 {
00569     if (!aCondition) { return NS_ERROR_NULL_POINTER ; }
00570     aSkipItem = PR_FALSE ;
00571     nsAbBooleanConditionType conditionType = 0 ;
00572     nsresult retCode = NS_OK ;
00573     nsXPIDLCString name ;
00574     nsXPIDLString value ;
00575     ULONG propertyTag = 0 ;
00576     nsCAutoString valueAscii ;
00577     
00578     retCode = aCondition->GetCondition(&conditionType) ;
00579     NS_ENSURE_SUCCESS(retCode, retCode) ;
00580     retCode = aCondition->GetName(getter_Copies(name)) ;
00581     NS_ENSURE_SUCCESS(retCode, retCode) ;
00582     retCode = aCondition->GetValue(getter_Copies(value)) ;
00583     NS_ENSURE_SUCCESS(retCode, retCode) ;
00584     valueAscii.AssignWithConversion(value.get()) ;
00585     propertyTag = findPropertyTag(name.get()) ;
00586     if (propertyTag == 0) {
00587         aSkipItem = PR_TRUE ;
00588         return retCode ;
00589     }
00590     switch (conditionType) {
00591     case nsIAbBooleanConditionTypes::Exists :
00592         aRestriction.rt = RES_EXIST ;
00593         aRestriction.res.resExist.ulPropTag = propertyTag ;
00594         break ;
00595     case nsIAbBooleanConditionTypes::DoesNotExist :
00596         aRestriction.rt = RES_NOT ;
00597         aRestriction.res.resNot.lpRes = new SRestriction ;
00598         aRestriction.res.resNot.lpRes->rt = RES_EXIST ;
00599         aRestriction.res.resNot.lpRes->res.resExist.ulPropTag = propertyTag ;
00600         break ;
00601     case nsIAbBooleanConditionTypes::Contains :
00602         aRestriction.rt = RES_CONTENT ;
00603         aRestriction.res.resContent.ulFuzzyLevel = FL_SUBSTRING | FL_LOOSE ;
00604         aRestriction.res.resContent.ulPropTag = propertyTag ;
00605         aRestriction.res.resContent.lpProp = new SPropValue ;
00606         aRestriction.res.resContent.lpProp->ulPropTag = propertyTag ;
00607         aRestriction.res.resContent.lpProp->Value.lpszA = nsCRT::strdup(valueAscii.get()) ;
00608         break ;
00609     case nsIAbBooleanConditionTypes::DoesNotContain :
00610         aRestriction.rt = RES_NOT ;
00611         aRestriction.res.resNot.lpRes = new SRestriction ;
00612         aRestriction.res.resNot.lpRes->rt = RES_CONTENT ;
00613         aRestriction.res.resNot.lpRes->res.resContent.ulFuzzyLevel = FL_SUBSTRING | FL_LOOSE ;
00614         aRestriction.res.resNot.lpRes->res.resContent.ulPropTag = propertyTag ;
00615         aRestriction.res.resNot.lpRes->res.resContent.lpProp = new SPropValue ;
00616         aRestriction.res.resNot.lpRes->res.resContent.lpProp->ulPropTag = propertyTag ;
00617         aRestriction.res.resNot.lpRes->res.resContent.lpProp->Value.lpszA = nsCRT::strdup(valueAscii.get()) ;
00618         break ;
00619     case nsIAbBooleanConditionTypes::Is :
00620         aRestriction.rt = RES_CONTENT ;
00621         aRestriction.res.resContent.ulFuzzyLevel = FL_FULLSTRING | FL_LOOSE ;
00622         aRestriction.res.resContent.ulPropTag = propertyTag ;
00623         aRestriction.res.resContent.lpProp = new SPropValue ;
00624         aRestriction.res.resContent.lpProp->ulPropTag = propertyTag ;
00625         aRestriction.res.resContent.lpProp->Value.lpszA = nsCRT::strdup(valueAscii.get()) ;
00626         break ;
00627     case nsIAbBooleanConditionTypes::IsNot :
00628         aRestriction.rt = RES_NOT ;
00629         aRestriction.res.resNot.lpRes = new SRestriction ;
00630         aRestriction.res.resNot.lpRes->rt = RES_CONTENT ;
00631         aRestriction.res.resNot.lpRes->res.resContent.ulFuzzyLevel = FL_FULLSTRING | FL_LOOSE ;
00632         aRestriction.res.resNot.lpRes->res.resContent.ulPropTag = propertyTag ;
00633         aRestriction.res.resNot.lpRes->res.resContent.lpProp = new SPropValue ;
00634         aRestriction.res.resNot.lpRes->res.resContent.lpProp->ulPropTag = propertyTag ;
00635         aRestriction.res.resNot.lpRes->res.resContent.lpProp->Value.lpszA = nsCRT::strdup(valueAscii.get()) ;
00636         break ;
00637     case nsIAbBooleanConditionTypes::BeginsWith :
00638         aRestriction.rt = RES_CONTENT ;
00639         aRestriction.res.resContent.ulFuzzyLevel = FL_PREFIX | FL_LOOSE ;
00640         aRestriction.res.resContent.ulPropTag = propertyTag ;
00641         aRestriction.res.resContent.lpProp = new SPropValue ;
00642         aRestriction.res.resContent.lpProp->ulPropTag = propertyTag ;
00643         aRestriction.res.resContent.lpProp->Value.lpszA = nsCRT::strdup(valueAscii.get()) ;
00644         break ;
00645     case nsIAbBooleanConditionTypes::EndsWith :
00646         // This condition should be implemented through regular expressions,
00647         // but MAPI doesn't match them correctly.
00648 #if 0
00649         aRestriction.rt = RES_PROPERTY ;
00650         aRestriction.res.resProperty.relop = RELOP_RE ;
00651         aRestriction.res.resProperty.ulPropTag = propertyTag ;
00652         aRestriction.res.resProperty.lpProp = new SPropValue ;
00653         aRestriction.res.resProperty.lpProp->ulPropTag = propertyTag ;
00654         aRestriction.res.resProperty.lpProp->Value.lpszA = nsCRT::strdup(valueAscii.get()) ;
00655 #else
00656         aSkipItem = PR_TRUE ;
00657 #endif // 0
00658         break ;
00659     case nsIAbBooleanConditionTypes::SoundsLike :
00660         // This condition cannot be implemented in MAPI.
00661         aSkipItem = PR_TRUE ;
00662         break ;
00663     case nsIAbBooleanConditionTypes::RegExp :
00664         // This condition should be implemented this way, but the following
00665         // code will never match (through MAPI's fault).
00666 #if 0
00667         aRestriction.rt = RES_PROPERTY ;
00668         aRestriction.res.resProperty.relop = RELOP_RE ;
00669         aRestriction.res.resProperty.ulPropTag = propertyTag ;
00670         aRestriction.res.resProperty.lpProp = new SPropValue ;
00671         aRestriction.res.resProperty.lpProp->ulPropTag = propertyTag ;
00672         aRestriction.res.resProperty.lpProp->Value.lpszA = nsCRT::strdup(valueAscii.get()) ;
00673 #else
00674         aSkipItem = PR_TRUE ;
00675 #endif // 0
00676         break ;
00677     case nsIAbBooleanConditionTypes::LessThan :
00678         aRestriction.rt = RES_PROPERTY ;
00679         aRestriction.res.resProperty.relop = RELOP_LT ;
00680         aRestriction.res.resProperty.ulPropTag = propertyTag ;
00681         aRestriction.res.resProperty.lpProp = new SPropValue ;
00682         aRestriction.res.resProperty.lpProp->ulPropTag = propertyTag ;
00683         aRestriction.res.resProperty.lpProp->Value.lpszA = nsCRT::strdup(valueAscii.get()) ;
00684         break ;
00685     case nsIAbBooleanConditionTypes::GreaterThan :
00686         aRestriction.rt = RES_PROPERTY ;
00687         aRestriction.res.resProperty.relop = RELOP_GT ;
00688         aRestriction.res.resProperty.ulPropTag = propertyTag ;
00689         aRestriction.res.resProperty.lpProp = new SPropValue ;
00690         aRestriction.res.resProperty.lpProp->ulPropTag = propertyTag ;
00691         aRestriction.res.resProperty.lpProp->Value.lpszA = nsCRT::strdup(valueAscii.get()) ;
00692         break ;
00693     default :
00694         aSkipItem = PR_TRUE ;
00695         break ;
00696     }
00697     return retCode ;
00698 }
00699 
00700 static nsresult BuildRestriction(nsIAbBooleanExpression *aLevel, 
00701                                  SRestriction& aRestriction)
00702 {
00703     if (!aLevel) { return NS_ERROR_NULL_POINTER ; }
00704     aRestriction.rt = RES_COMMENT ;
00705     nsresult retCode = NS_OK ;
00706     nsAbBooleanOperationType operationType = 0 ;
00707     PRUint32 nbExpressions = 0 ;
00708     nsCOMPtr<nsISupportsArray> expressions ;
00709     
00710     retCode = aLevel->GetOperation(&operationType) ;
00711     NS_ENSURE_SUCCESS(retCode, retCode) ;
00712     retCode = aLevel->GetExpressions(getter_AddRefs(expressions)) ;
00713     NS_ENSURE_SUCCESS(retCode, retCode) ;
00714     retCode = expressions->Count(&nbExpressions) ;
00715     NS_ENSURE_SUCCESS(retCode, retCode) ;
00716     if (nbExpressions == 0) { 
00717         PRINTF(("Error, no expressions.\n")) ;
00718         return NS_OK ;
00719     }
00720     if (operationType == nsIAbBooleanOperationTypes::NOT && nbExpressions != 1) {
00721         PRINTF(("Error, unary operation NOT with multiple operands.\n")) ;
00722         return NS_OK ;
00723     }
00724     LPSRestriction restrictionArray = new SRestriction [nbExpressions] ;
00725     PRUint32 realNbExpressions = 0 ;
00726     PRBool skipItem = PR_FALSE ;
00727     PRUint32 i = 0 ;
00728     nsCOMPtr<nsISupports> element ;
00729 
00730     for (i = 0 ; i < nbExpressions ; ++ i) {
00731         retCode = expressions->GetElementAt(i, getter_AddRefs(element)) ;
00732         if (NS_SUCCEEDED(retCode)) {
00733             nsCOMPtr<nsIAbBooleanConditionString> condition (do_QueryInterface(element, &retCode)) ;
00734             
00735             if (NS_SUCCEEDED(retCode)) {
00736                 retCode = BuildRestriction(condition, *restrictionArray, skipItem) ;
00737                 if (NS_SUCCEEDED(retCode)) {
00738                     if (!skipItem) { ++ restrictionArray ; ++ realNbExpressions ; }
00739                 }
00740                 else { PRINTF(("Cannot build restriction for item %d %08x.\n", i, retCode)) ; }
00741             }
00742             else { 
00743                 nsCOMPtr<nsIAbBooleanExpression> subExpression (do_QueryInterface(element, &retCode)) ;
00744 
00745                 if (NS_SUCCEEDED(retCode)) {
00746                     retCode = BuildRestriction(subExpression, *restrictionArray) ;
00747                     if (NS_SUCCEEDED(retCode)) {
00748                         if (restrictionArray->rt != RES_COMMENT) {
00749                             ++ restrictionArray ; ++ realNbExpressions ; 
00750                         }
00751                     }
00752                 }
00753                 else { PRINTF(("Cannot get interface for item %d %08x.\n", i, retCode)) ; }
00754             }
00755         }
00756         else { PRINTF(("Cannot get item %d %08x.\n", i, retCode)) ; }
00757     }
00758     restrictionArray -= realNbExpressions ;
00759     if (realNbExpressions > 1) {
00760         if (operationType == nsIAbBooleanOperationTypes::OR) {
00761             aRestriction.rt = RES_OR ;
00762             aRestriction.res.resOr.lpRes = restrictionArray ;
00763             aRestriction.res.resOr.cRes = realNbExpressions ;
00764         }
00765         else if (operationType == nsIAbBooleanOperationTypes::AND) {
00766             aRestriction.rt = RES_AND ;
00767             aRestriction.res.resAnd.lpRes = restrictionArray ;
00768             aRestriction.res.resAnd.cRes = realNbExpressions ;
00769         }
00770         else {
00771             PRINTF(("Unsupported operation %d.\n", operationType)) ;
00772         }
00773     }
00774     else if (realNbExpressions == 1) {
00775         if (operationType == nsIAbBooleanOperationTypes::NOT) {
00776             aRestriction.rt = RES_NOT ;
00777             // This copy is to ensure that every NOT restriction is being 
00778             // allocated by new and not new[] (see destruction of restriction)
00779             aRestriction.res.resNot.lpRes = new SRestriction ;
00780             memcpy(aRestriction.res.resNot.lpRes, restrictionArray, sizeof(SRestriction)) ;
00781         }
00782         else {
00783             // Case where the restriction array is redundant,
00784             // we need to fill the restriction directly.
00785             memcpy(&aRestriction, restrictionArray, sizeof(SRestriction)) ;
00786         }
00787         delete [] restrictionArray ;
00788     }
00789     if (aRestriction.rt == RES_COMMENT) {
00790         // This means we haven't really built any useful expression
00791         delete [] restrictionArray ;
00792     }
00793     return NS_OK ;
00794 }
00795 
00796 static nsresult BuildRestriction(nsIAbDirectoryQueryArguments *aArguments, 
00797                                  SRestriction& aRestriction)
00798 {
00799     if (!aArguments) { return NS_ERROR_NULL_POINTER ; }
00800     nsresult retCode = NS_OK ;
00801     nsCOMPtr<nsIAbBooleanExpression> booleanQuery ;
00802 
00803     retCode = aArguments->GetExpression(getter_AddRefs(booleanQuery)) ;
00804     NS_ENSURE_SUCCESS(retCode, retCode) ;
00805     retCode = BuildRestriction(booleanQuery, aRestriction) ;
00806     return retCode ;
00807 }
00808 
00809 static void DestroyRestriction(SRestriction& aRestriction)
00810 {
00811     switch(aRestriction.rt) {
00812     case RES_AND :
00813     case RES_OR :
00814         {
00815             for (ULONG i = 0 ; i < aRestriction.res.resAnd.cRes ; ++ i) {
00816                 DestroyRestriction(aRestriction.res.resAnd.lpRes [i]) ;
00817             }
00818             delete [] aRestriction.res.resAnd.lpRes ;
00819         }
00820         break ;
00821     case RES_COMMENT :
00822         break ;
00823     case RES_CONTENT :
00824         if (PROP_TYPE(aRestriction.res.resContent.ulPropTag) == PT_UNICODE) {
00825             nsCRT::free(aRestriction.res.resContent.lpProp->Value.lpszW) ;
00826         }
00827         else if (PROP_TYPE(aRestriction.res.resContent.ulPropTag) == PT_STRING8) {
00828             nsCRT::free(aRestriction.res.resContent.lpProp->Value.lpszA) ;
00829         }
00830         delete aRestriction.res.resContent.lpProp ;
00831         break ;
00832     case RES_EXIST :
00833         break ;
00834     case RES_NOT :
00835         DestroyRestriction(*aRestriction.res.resNot.lpRes) ;
00836         delete aRestriction.res.resNot.lpRes ;
00837         break ;
00838     case RES_BITMASK :
00839     case RES_COMPAREPROPS :
00840         break ;
00841     case RES_PROPERTY :
00842         if (PROP_TYPE(aRestriction.res.resProperty.ulPropTag) == PT_UNICODE) {
00843             nsCRT::free(aRestriction.res.resProperty.lpProp->Value.lpszW) ;
00844         }
00845         else if (PROP_TYPE(aRestriction.res.resProperty.ulPropTag) == PT_STRING8) {
00846             nsCRT::free(aRestriction.res.resProperty.lpProp->Value.lpszA) ;
00847         }
00848         delete aRestriction.res.resProperty.lpProp ;
00849     case RES_SIZE :
00850     case RES_SUBRESTRICTION :
00851         break ;
00852     }
00853 }
00854 
00855 nsresult FillPropertyValues(nsIAbCard *aCard, nsIAbDirectoryQueryArguments *aArguments, 
00856                             nsISupportsArray **aValues)
00857 {
00858     if (!aCard || !aArguments || !aValues ) { 
00859         return NS_ERROR_NULL_POINTER ; 
00860     }
00861     *aValues = nsnull ;
00862     CharPtrArrayGuard properties ;
00863     nsCOMPtr<nsISupportsArray> values ;
00864     nsresult retCode = NS_OK ;
00865     
00866     retCode = aArguments->GetReturnProperties(properties.GetSizeAddr(), properties.GetArrayAddr()) ;
00867     NS_ENSURE_SUCCESS(retCode, retCode) ;
00868     PRUint32 i = 0 ;
00869     nsAbDirectoryQueryPropertyValue *newValue = nsnull ;
00870        nsCOMPtr<nsIAbDirectoryQueryPropertyValue> propertyValue ;
00871 
00872     for (i = 0 ; i < properties.GetSize() ; ++ i) {
00873         const char* cPropName = properties[i] ;
00874         newValue = nsnull ;
00875               
00876     if (!nsCRT::strcmp(cPropName, "card:nsIAbCard")) {
00877                      nsCOMPtr<nsISupports> bogusInterface (do_QueryInterface(aCard, &retCode)) ;
00878 
00879                      NS_ENSURE_SUCCESS(retCode, retCode) ;
00880                      newValue = new nsAbDirectoryQueryPropertyValue (cPropName, bogusInterface) ;
00881               }
00882     else {
00883                      nsXPIDLString value ;
00884 
00885                      retCode = aCard->GetCardValue(cPropName, getter_Copies(value)) ;
00886                      NS_ENSURE_SUCCESS(retCode, retCode) ;
00887             if (!value.IsEmpty()) {
00888                 newValue = new nsAbDirectoryQueryPropertyValue(cPropName, value.get()) ;
00889             }
00890               }
00891         if (newValue) {
00892                      if (!values) {
00893                             NS_NewISupportsArray(getter_AddRefs(values)) ;
00894                      }
00895                      propertyValue = newValue ;
00896                      values->AppendElement (propertyValue) ;
00897         }
00898     }
00899     *aValues = values ;
00900     NS_ADDREF(*aValues) ;
00901     return retCode ;
00902 }
00903 
00904 struct QueryThreadArgs 
00905 {
00906     nsAbOutlookDirectory *mThis ;
00907     nsCOMPtr<nsIAbDirectoryQueryArguments> mArguments ;
00908     nsCOMPtr<nsIAbDirectoryQueryResultListener> mListener ;
00909     PRInt32 mResultLimit ;
00910     PRInt32 mTimeout ;
00911     PRInt32 mThreadId ;
00912 } ;
00913 
00914 static void QueryThreadFunc(void *aArguments)
00915 {
00916     QueryThreadArgs *arguments = NS_REINTERPRET_CAST(QueryThreadArgs *, aArguments) ;
00917 
00918     if (!aArguments) { return ; }
00919     arguments->mThis->ExecuteQuery(arguments->mArguments, arguments->mListener,
00920                                    arguments->mResultLimit, arguments->mTimeout,
00921                                    arguments->mThreadId) ;
00922     delete arguments ;
00923 }
00924 
00925 nsresult nsAbOutlookDirectory::DoQuery(nsIAbDirectoryQueryArguments *aArguments,
00926                                        nsIAbDirectoryQueryResultListener *aListener,
00927                                        PRInt32 aResultLimit, PRInt32 aTimeout,
00928                                        PRInt32 *aReturnValue) 
00929 {
00930     if (!aArguments || !aListener || !aReturnValue)  { 
00931         return NS_ERROR_NULL_POINTER ; 
00932     }
00933     *aReturnValue = -1 ;
00934     
00935     QueryThreadArgs *threadArgs = new QueryThreadArgs ;
00936     PRThread *newThread = nsnull ;
00937 
00938     if (!threadArgs) { 
00939         return NS_ERROR_OUT_OF_MEMORY ;
00940     }
00941     threadArgs->mThis = this ;
00942     threadArgs->mArguments = aArguments ;
00943     threadArgs->mListener = aListener ;
00944     threadArgs->mResultLimit = aResultLimit ; 
00945     threadArgs->mTimeout = aTimeout ;
00946     PR_Lock(mProtector) ;
00947     *aReturnValue = ++ mCurrentQueryId ;
00948     PR_Unlock(mProtector) ;
00949     threadArgs->mThreadId = *aReturnValue ;
00950     newThread = PR_CreateThread(PR_USER_THREAD,
00951                                 QueryThreadFunc,
00952                                 threadArgs,
00953                                 PR_PRIORITY_NORMAL,
00954                                 PR_GLOBAL_THREAD,
00955                                 PR_UNJOINABLE_THREAD,
00956                                 0) ;
00957     if (!newThread ) {
00958         delete threadArgs ;
00959         return NS_ERROR_OUT_OF_MEMORY ;
00960     }
00961     nsIntegerKey newKey(*aReturnValue) ;
00962     
00963     mQueryThreads.Put(&newKey, newThread) ;
00964     return NS_OK ;
00965 }
00966 
00967 nsresult nsAbOutlookDirectory::StopQuery(PRInt32 aContext)
00968 {
00969     nsIntegerKey contextKey(aContext) ;
00970     PRThread *queryThread = NS_REINTERPRET_CAST(PRThread *, mQueryThreads.Get(&contextKey)) ;
00971 
00972     if (queryThread) { 
00973         PR_Interrupt(queryThread) ; 
00974         mQueryThreads.Remove(&contextKey) ;
00975     }
00976     return NS_OK ;
00977 }
00978 
00979 // nsIAbDirectorySearch methods
00980 NS_IMETHODIMP nsAbOutlookDirectory::StartSearch(void)
00981 {
00982     if (!mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; }
00983     nsresult retCode = NS_OK ;
00984     
00985     retCode = StopSearch() ;
00986     NS_ENSURE_SUCCESS(retCode, retCode) ;
00987     mCardList.Reset() ;
00988 
00989     nsCOMPtr<nsIAbBooleanExpression> expression ;
00990 
00991     nsCOMPtr<nsIAbDirectoryQueryArguments> arguments = do_CreateInstance(NS_ABDIRECTORYQUERYARGUMENTS_CONTRACTID,&retCode);
00992     NS_ENSURE_SUCCESS(retCode, retCode);
00993 
00994     retCode = nsAbQueryStringToExpression::Convert(mQueryString.get (), getter_AddRefs(expression)) ;
00995     NS_ENSURE_SUCCESS(retCode, retCode) ;
00996     retCode = arguments->SetExpression(expression) ;
00997     NS_ENSURE_SUCCESS(retCode, retCode) ;
00998     
00999     const char *arr = "card:nsIAbCard";
01000     retCode = arguments->SetReturnProperties(1, &arr);
01001     NS_ENSURE_SUCCESS(retCode, retCode) ;
01002     retCode = arguments->SetQuerySubDirectories(PR_TRUE) ;
01003     NS_ENSURE_SUCCESS(retCode, retCode) ;
01004     nsCOMPtr<nsIAbDirectoryQueryResultListener> proxyListener;
01005 
01006     retCode = NS_GetProxyForObject(NS_UI_THREAD_EVENTQ,
01007                      NS_GET_IID(nsIAbDirectoryQueryResultListener),
01008                      NS_STATIC_CAST(nsIAbDirectoryQueryResultListener *, new nsAbDirSearchListener(this)),
01009                      PROXY_SYNC | PROXY_ALWAYS,
01010                      getter_AddRefs(proxyListener));
01011     NS_ENSURE_SUCCESS(retCode, retCode) ;
01012 
01013     return DoQuery(arguments, proxyListener, -1, 0, &mSearchContext) ;
01014 }
01015 
01016 NS_IMETHODIMP nsAbOutlookDirectory::StopSearch(void) 
01017 {
01018     if (!mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; }
01019     return StopQuery(mSearchContext) ;
01020 }
01021 
01022 // nsAbDirSearchListenerContext
01023 nsresult nsAbOutlookDirectory::OnSearchFinished(PRInt32 aResult)
01024 {
01025     return NS_OK ;
01026 }
01027 
01028 nsresult nsAbOutlookDirectory::OnSearchFoundCard(nsIAbCard *aCard) 
01029 {
01030     nsVoidKey newKey (NS_STATIC_CAST(void *, aCard)) ;
01031     nsresult retCode = NS_OK ;
01032     
01033     mCardList.Put(&newKey, aCard) ;
01034     nsCOMPtr<nsIAddrBookSession> abSession = do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &retCode);
01035     if (NS_SUCCEEDED(retCode)) { abSession->NotifyDirectoryItemAdded(this, aCard) ; }
01036     return retCode ;
01037 }
01038 
01039 nsresult nsAbOutlookDirectory::ExecuteQuery(nsIAbDirectoryQueryArguments *aArguments,
01040                                             nsIAbDirectoryQueryResultListener *aListener,
01041                                             PRInt32 aResultLimit, PRInt32 aTimeout,
01042                                             PRInt32 aThreadId) 
01043 
01044 {
01045     if (!aArguments || !aListener) { 
01046         return NS_ERROR_NULL_POINTER ; 
01047     }
01048     SRestriction arguments ;
01049     nsresult retCode = NS_OK ;
01050     
01051     retCode = BuildRestriction(aArguments, arguments) ;
01052     NS_ENSURE_SUCCESS(retCode, retCode) ;
01053     nsCOMPtr<nsISupportsArray> resultsArray ;
01054     PRUint32 nbResults = 0 ;
01055     
01056     retCode = GetChildCards(getter_AddRefs(resultsArray), 
01057                             arguments.rt == RES_COMMENT ? nsnull : &arguments) ;
01058     DestroyRestriction(arguments) ;
01059     NS_ENSURE_SUCCESS(retCode, retCode) ;
01060     retCode = resultsArray->Count(&nbResults) ;
01061     NS_ENSURE_SUCCESS(retCode, retCode) ;
01062     nsCOMPtr<nsIAbDirectoryQueryResult> result ;
01063     nsAbDirectoryQueryResult *newResult = nsnull ;
01064 
01065     if (aResultLimit > 0 && nbResults > NS_STATIC_CAST(PRUint32, aResultLimit)) { 
01066         nbResults = NS_STATIC_CAST(PRUint32, aResultLimit) ; 
01067     }
01068     PRUint32 i = 0 ;
01069     nsCOMPtr<nsISupports> element ;
01070     nsCOMPtr<nsISupportsArray> propertyValues ;
01071     
01072     for (i = 0 ; i < nbResults ; ++ i) {
01073         retCode = resultsArray->GetElementAt(i, getter_AddRefs(element)) ;
01074         NS_ENSURE_SUCCESS(retCode, retCode) ;
01075         nsCOMPtr<nsIAbCard> card (do_QueryInterface(element, &retCode)) ;
01076         
01077         NS_ENSURE_SUCCESS(retCode, retCode) ;
01078         FillPropertyValues(card, aArguments, getter_AddRefs(propertyValues)) ;
01079         newResult = new nsAbDirectoryQueryResult(0, aArguments,
01080                                                  nsIAbDirectoryQueryResult::queryResultMatch, 
01081                                                  propertyValues) ;
01082         if (!newResult) { return NS_ERROR_OUT_OF_MEMORY ; }
01083         result = newResult ;
01084         aListener->OnQueryItem(result) ;
01085     }
01086     nsIntegerKey hashKey(aThreadId) ;
01087     
01088     mQueryThreads.Remove(&hashKey) ;
01089     newResult = new nsAbDirectoryQueryResult(0, aArguments,
01090                                              nsIAbDirectoryQueryResult::queryResultComplete, 
01091                                              0) ;
01092     result = newResult ;
01093     aListener->OnQueryItem(result) ;
01094     return retCode ;
01095 }
01096 nsresult nsAbOutlookDirectory::GetChildCards(nsISupportsArray **aCards, 
01097                                              void *aRestriction)
01098 {
01099     if (!aCards) { return NS_ERROR_NULL_POINTER ; }
01100     *aCards = nsnull ;
01101     nsresult retCode = NS_OK ;
01102     nsCOMPtr<nsISupportsArray> cards ;
01103     nsAbWinHelperGuard mapiAddBook (mAbWinType) ;
01104     nsMapiEntryArray cardEntries ;
01105     LPSRestriction restriction = (LPSRestriction) aRestriction ;
01106 
01107     if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; }
01108     retCode = NS_NewISupportsArray(getter_AddRefs(cards)) ;
01109     NS_ENSURE_SUCCESS(retCode, retCode) ;
01110     if (!mapiAddBook->GetCards(*mMapiData, restriction, cardEntries)) {
01111         PRINTF(("Cannot get cards.\n")) ;
01112         return NS_ERROR_FAILURE ;
01113     }
01114     nsCAutoString entryId ;
01115     nsCAutoString uriName ;
01116     nsCOMPtr<nsIRDFResource> resource ;
01117     nsCOMPtr <nsIAbCard> childCard;
01118         
01119     for (ULONG card = 0 ; card < cardEntries.mNbEntries ; ++ card) {
01120         cardEntries.mEntries [card].ToString(entryId) ;
01121         buildAbWinUri(kOutlookCardScheme, mAbWinType, uriName) ;
01122         uriName.Append(entryId) ;
01123         childCard = do_CreateInstance(NS_ABOUTLOOKCARD_CONTRACTID, &retCode);
01124         NS_ENSURE_SUCCESS(retCode, retCode) ;
01125         resource = do_QueryInterface(childCard, &retCode) ;
01126         NS_ENSURE_SUCCESS(retCode, retCode) ;
01127         retCode = resource->Init(uriName.get()) ;
01128         NS_ENSURE_SUCCESS(retCode, retCode) ;
01129         cards->AppendElement(childCard) ;
01130     }
01131     *aCards = cards ;
01132     NS_ADDREF(*aCards) ;
01133     return retCode ;
01134 }
01135 
01136 nsresult nsAbOutlookDirectory::GetChildNodes(nsISupportsArray **aNodes)
01137 {
01138     if (!aNodes) { return NS_ERROR_NULL_POINTER ; }
01139     *aNodes = nsnull ;
01140     nsresult retCode = NS_OK ;
01141     nsCOMPtr<nsISupportsArray> nodes ;
01142     nsAbWinHelperGuard mapiAddBook (mAbWinType) ;
01143     nsMapiEntryArray nodeEntries ;
01144 
01145     if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; }
01146     retCode = NS_NewISupportsArray(getter_AddRefs(nodes)) ;
01147     NS_ENSURE_SUCCESS(retCode, retCode) ;
01148     if (!mapiAddBook->GetNodes(*mMapiData, nodeEntries)) {
01149         PRINTF(("Cannot get nodes.\n")) ;
01150         return NS_ERROR_FAILURE ;
01151     }
01152     nsCAutoString entryId ;
01153     nsCAutoString uriName ;
01154     nsCOMPtr <nsIRDFResource> resource ;
01155 
01156     for (ULONG node = 0 ; node < nodeEntries.mNbEntries ; ++ node) {
01157         nodeEntries.mEntries [node].ToString(entryId) ;
01158         buildAbWinUri(kOutlookDirectoryScheme, mAbWinType, uriName) ;
01159         uriName.Append(entryId) ;
01160         retCode = gRDFService->GetResource(uriName, getter_AddRefs(resource)) ;
01161         NS_ENSURE_SUCCESS(retCode, retCode) ;
01162         nodes->AppendElement(resource) ;
01163     }
01164     *aNodes = nodes ;
01165     NS_ADDREF(*aNodes) ;
01166     return retCode ;
01167 }
01168 
01169 nsresult nsAbOutlookDirectory::NotifyItemDeletion(nsISupports *aItem) 
01170 {
01171     nsresult retCode = NS_OK ;
01172     
01173     nsCOMPtr<nsIAddrBookSession> abSession = do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &retCode);
01174     NS_ENSURE_SUCCESS(retCode, retCode) ;
01175     retCode = abSession->NotifyDirectoryItemDeleted(this, aItem) ;
01176     return retCode ;
01177 }
01178 
01179 nsresult nsAbOutlookDirectory::NotifyItemAddition(nsISupports *aItem) 
01180 {
01181     nsresult retCode = NS_OK ;
01182     
01183     nsCOMPtr<nsIAddrBookSession> abSession = do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &retCode);
01184     NS_ENSURE_SUCCESS(retCode, retCode) ;
01185     retCode = abSession->NotifyDirectoryItemAdded(this, aItem) ;
01186     return retCode ;
01187 }
01188 
01189 // This is called from EditMailListToDatabase.
01190 // We got m_AddressList containing the list of cards the mailing
01191 // list is supposed to contain at the end.
01192 nsresult nsAbOutlookDirectory::CommitAddressList(void)
01193 {
01194     nsresult retCode = NS_OK ;
01195     nsCOMPtr<nsISupportsArray> oldList ;
01196     PRUint32 nbCards = 0 ;
01197     PRUint32 i = 0 ;
01198     
01199     if (!m_IsMailList) { 
01200         PRINTF(("We are not in a mailing list, no commit can be done.\n")) ;
01201         return NS_ERROR_UNEXPECTED ;
01202     }
01203     retCode = GetChildCards(getter_AddRefs(oldList), nsnull) ;
01204     NS_ENSURE_SUCCESS(retCode, retCode) ;
01205     retCode = m_AddressList->Count(&nbCards) ;
01206     NS_ENSURE_SUCCESS(retCode, retCode) ;
01207     nsCOMPtr<nsISupports> element ;
01208     nsCOMPtr<nsIAbCard> newCard ;
01209 
01210     for (i = 0 ; i < nbCards ; ++ i) {
01211         retCode = m_AddressList->GetElementAt(i, getter_AddRefs(element)) ;
01212         NS_ENSURE_SUCCESS(retCode, retCode) ;
01213         if (!oldList->RemoveElement(element)) {
01214             // The entry was not already there
01215             nsCOMPtr<nsIAbCard> card (do_QueryInterface(element, &retCode)) ;
01216             
01217             NS_ENSURE_SUCCESS(retCode, retCode) ;
01218 
01219             retCode = CreateCard(card, getter_AddRefs(newCard)) ;
01220             NS_ENSURE_SUCCESS(retCode, retCode) ;
01221             m_AddressList->ReplaceElementAt(newCard, i) ;
01222         }
01223     }
01224     retCode = DeleteCards(oldList) ;
01225     return retCode ;
01226 }
01227 
01228 nsresult nsAbOutlookDirectory::UpdateAddressList(void)
01229 {
01230     nsresult retCode = S_OK ;
01231     
01232     if (m_IsMailList) {
01233         retCode = GetChildCards(getter_AddRefs(m_AddressList), nsnull) ; 
01234     }
01235     else { 
01236         retCode = GetChildNodes(getter_AddRefs(m_AddressList)) ; 
01237     }
01238     return retCode ;
01239 }
01240 
01241 nsresult nsAbOutlookDirectory::CreateCard(nsIAbCard *aData, nsIAbCard **aNewCard) 
01242 {
01243     if (!aData || !aNewCard) { return NS_ERROR_NULL_POINTER ; }
01244     *aNewCard = nsnull ;
01245     nsresult retCode = NS_OK ;
01246     nsAbWinHelperGuard mapiAddBook (mAbWinType) ;
01247     nsMapiEntry newEntry ;
01248     nsCAutoString entryString ;
01249     PRBool didCopy = PR_FALSE ;
01250 
01251     if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; }
01252     // If we get a RDF resource and it maps onto an Outlook card uri,
01253     // we simply copy the contents of the Outlook card.
01254     retCode = ExtractCardEntry(aData, entryString) ;
01255     if (NS_SUCCEEDED(retCode) && !entryString.IsEmpty()) {
01256         nsMapiEntry sourceEntry ;
01257         
01258         
01259         sourceEntry.Assign(entryString) ;
01260         if (m_IsMailList) {
01261             // In the case of a mailing list, we can use the address
01262             // as a direct template to build the new one (which is done
01263             // by CopyEntry).
01264             mapiAddBook->CopyEntry(*mMapiData, sourceEntry, newEntry) ;
01265             didCopy = PR_TRUE ;
01266         }
01267         else {
01268             // Else, we have to create a temporary address and copy the
01269             // source into it. Yes it's silly.
01270             mapiAddBook->CreateEntry(*mMapiData, newEntry) ;
01271         }
01272     }
01273     // If this approach doesn't work, well we're back to creating and copying.
01274     if (newEntry.mByteCount == 0) {
01275         // In the case of a mailing list, we cannot directly create a new card,
01276         // we have to create a temporary one in a real folder (to be able to use
01277         // templates) and then copy it to the mailing list.
01278         if (m_IsMailList) {
01279             nsMapiEntry parentEntry ;
01280             nsMapiEntry temporaryEntry ;
01281 
01282             if (!mapiAddBook->GetDefaultContainer(parentEntry)) {
01283                 return NS_ERROR_FAILURE ;
01284             }
01285             if (!mapiAddBook->CreateEntry(parentEntry, temporaryEntry)) {
01286                 return NS_ERROR_FAILURE ;
01287             }
01288             if (!mapiAddBook->CopyEntry(*mMapiData, temporaryEntry, newEntry)) {
01289                 return NS_ERROR_FAILURE ;
01290             }
01291             if (!mapiAddBook->DeleteEntry(parentEntry, temporaryEntry)) {
01292                 return NS_ERROR_FAILURE ;
01293             }
01294         }
01295         // If we're on a real address book folder, we can directly create an
01296         // empty card.
01297         else if (!mapiAddBook->CreateEntry(*mMapiData, newEntry)) {
01298             return NS_ERROR_FAILURE ;
01299         }
01300     }
01301     newEntry.ToString(entryString) ;
01302     nsCAutoString uri ;
01303 
01304     buildAbWinUri(kOutlookCardScheme, mAbWinType, uri) ;
01305     uri.Append(entryString) ;
01306     nsCOMPtr<nsIRDFResource> resource ;
01307     
01308     nsCOMPtr<nsIAbCard> newCard = do_CreateInstance(NS_ABOUTLOOKCARD_CONTRACTID, &retCode);
01309     NS_ENSURE_SUCCESS(retCode, retCode) ;
01310     resource = do_QueryInterface(newCard, &retCode) ;
01311     NS_ENSURE_SUCCESS(retCode, retCode) ;
01312     retCode = resource->Init(uri.get()) ;
01313     NS_ENSURE_SUCCESS(retCode, retCode) ;
01314     if (!didCopy) {
01315         retCode = newCard->Copy(aData) ;
01316         NS_ENSURE_SUCCESS(retCode, retCode) ;
01317         retCode = newCard->EditCardToDatabase(mURINoQuery.get()) ;
01318         NS_ENSURE_SUCCESS(retCode, retCode) ;
01319     }
01320     *aNewCard = newCard ;
01321     NS_ADDREF(*aNewCard) ;
01322     return retCode ;
01323 }