From 3852377ef3b9aaf27aa2d68d65d0a73df271d2ee Mon Sep 17 00:00:00 2001 From: "cavin%netscape.com" Date: Sat, 17 Aug 2002 00:47:50 +0000 Subject: [PATCH] Added code for mailing list support. git-svn-id: svn://10.0.0.236/branches/ANYTHING_FOR_PUTTERMAN_BRANCH@127519 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/mailnews/absync/build/makefile.win | 1 + mozilla/mailnews/absync/src/makefile.win | 1 + mozilla/mailnews/absync/src/nsAbSync.cpp | 1707 +++++++++----- mozilla/mailnews/absync/src/nsAbSync.h | 123 +- mozilla/mailnews/absync/src/nsAbSyncList.cpp | 2095 +++++++++++++++++ .../mailnews/absync/src/nsSyncDecoderRing.h | 16 +- 6 files changed, 3335 insertions(+), 608 deletions(-) create mode 100644 mozilla/mailnews/absync/src/nsAbSyncList.cpp diff --git a/mozilla/mailnews/absync/build/makefile.win b/mozilla/mailnews/absync/build/makefile.win index 9e5ae2376c2..8f3df91ae1e 100644 --- a/mozilla/mailnews/absync/build/makefile.win +++ b/mozilla/mailnews/absync/build/makefile.win @@ -25,6 +25,7 @@ REQUIRES = xpcom \ docshell \ uriloader \ msgbase \ + msgbaseutil \ intl \ addrbook \ mork \ diff --git a/mozilla/mailnews/absync/src/makefile.win b/mozilla/mailnews/absync/src/makefile.win index f7ca23419cc..391a8828785 100644 --- a/mozilla/mailnews/absync/src/makefile.win +++ b/mozilla/mailnews/absync/src/makefile.win @@ -61,6 +61,7 @@ LIBRARY_NAME=absync_s CPP_OBJS= .\$(OBJDIR)\nsAbSyncPostEngine.obj \ .\$(OBJDIR)\nsAbSync.obj \ + .\$(OBJDIR)\nsAbSyncList.obj \ .\$(OBJDIR)\nsAbSyncCRCModel.obj \ .\$(OBJDIR)\nsSyncDecoderRing.obj \ .\$(OBJDIR)\nsABSyncDriver.obj \ diff --git a/mozilla/mailnews/absync/src/nsAbSync.cpp b/mozilla/mailnews/absync/src/nsAbSync.cpp index 08a257c9951..6b29e93dc46 100644 --- a/mozilla/mailnews/absync/src/nsAbSync.cpp +++ b/mozilla/mailnews/absync/src/nsAbSync.cpp @@ -34,6 +34,12 @@ * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ + +#ifdef MOZ_LOGGING +// sorry, this has to be before the pre-compiled header +#define FORCE_PR_LOG /* Allow logging in the release build */ +#endif + #include "nsAbSync.h" #include "prmem.h" #include "nsAbSyncCID.h" @@ -66,12 +72,9 @@ #include "nsIPrompt.h" #include "nsIWindowWatcher.h" -static NS_DEFINE_CID(kCAbSyncPostEngineCID, NS_ABSYNC_POST_ENGINE_CID); -static NS_DEFINE_CID(kAddrBookSessionCID, NS_ADDRBOOKSESSION_CID); -static NS_DEFINE_CID(kAddressBookDBCID, NS_ADDRDATABASE_CID); -static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); -static NS_DEFINE_CID(kAbCardPropertyCID, NS_ABCARDPROPERTY_CID); -static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); +//#define DEBUG_OFFLINE_TEST 1 // Take server response from a file. + +PRLogModuleInfo *ABSYNC; // Logging module /* Implementation file */ NS_IMPL_ISUPPORTS1(nsAbSync, nsIAbSync) @@ -89,6 +92,10 @@ nsAbSync::nsAbSync() InternalInit(); InitSchemaColumns(); + + // Init logging module. + if (!ABSYNC) + ABSYNC = PR_NewLogModule("ABSYNC"); } void @@ -103,9 +110,13 @@ nsAbSync::InternalInit() mAbSyncAddressBook = nsnull; mAbSyncAddressBookFileName = nsnull; mHistoryFile = nsnull; + mListHistoryFile = nsnull; mOldSyncMapingTable = nsnull; mNewSyncMapingTable = nsnull; mNewServerTable = nsnull; + mListOldSyncMapingTable = nsnull; + mListNewSyncMapingTable = nsnull; + mListNewServerTable = nsnull; mLastChangeNum = 1; @@ -144,6 +155,23 @@ nsAbSync::CleanServerTable(nsVoidArray *aArray) return NS_OK; } +nsresult nsAbSync::CleanListServerTable(nsVoidArray *aArray) +{ + if (!aArray) + return NS_OK; + + for (PRInt32 i=0; iCount(); i++) + { + syncListMappingRecord *tRec = (syncListMappingRecord *)aArray->ElementAt(i); + if (!tRec) + continue; + + nsCRT::free((char *)tRec); + } + + delete aArray; + return NS_OK; +} nsresult nsAbSync::InternalCleanup(nsresult aResult) { @@ -156,12 +184,18 @@ nsAbSync::InternalCleanup(nsresult aResult) PR_FREEIF(mOldSyncMapingTable); PR_FREEIF(mNewSyncMapingTable); + PR_FREEIF(mListOldSyncMapingTable); + PR_FREEIF(mListNewSyncMapingTable); + PR_FREEIF(mCrashTable); CleanServerTable(mNewServerTable); + CleanListServerTable(mListNewServerTable); if (mHistoryFile) mHistoryFile->CloseStream(); + if (mListHistoryFile) + mListHistoryFile->CloseStream(); if (mLockFile) { @@ -249,17 +283,25 @@ nsAbSync::InitSchemaColumns() mSchemaMappingList[24].abField = kJobTitleColumn; mSchemaMappingList[25].abField = kDepartmentColumn; mSchemaMappingList[26].abField = kCompanyColumn; - mSchemaMappingList[27].abField = kWebPage1Column; - mSchemaMappingList[28].abField = kWebPage2Column; - mSchemaMappingList[29].abField = kBirthYearColumn; - mSchemaMappingList[30].abField = kBirthMonthColumn; - mSchemaMappingList[31].abField = kBirthDayColumn; - mSchemaMappingList[32].abField = kCustom1Column; - mSchemaMappingList[33].abField = kCustom2Column; - mSchemaMappingList[34].abField = kCustom3Column; - mSchemaMappingList[35].abField = kCustom4Column; - mSchemaMappingList[36].abField = kNotesColumn; - mSchemaMappingList[37].abField = kLastModifiedDateColumn; + mSchemaMappingList[27].abField = kAnniversaryMonthColumn; // Don't change the mm, dd, yy ordering here + mSchemaMappingList[28].abField = kAnniversaryDayColumn; // since GenerateProtocolForCard() depends on it. + mSchemaMappingList[29].abField = kAnniversaryYearColumn; + mSchemaMappingList[30].abField = kSpouseNameColumn; + mSchemaMappingList[31].abField = kFamilyNameColumn; + mSchemaMappingList[32].abField = kDefaultAddressColumn; + mSchemaMappingList[33].abField = kCategoryColumn; + mSchemaMappingList[34].abField = kWebPage1Column; + mSchemaMappingList[35].abField = kWebPage2Column; + mSchemaMappingList[36].abField = kBirthMonthColumn; // Don't change the mm, dd, yy ordering here + mSchemaMappingList[37].abField = kBirthDayColumn; // since GenerateProtocolForCard() depends on it. + mSchemaMappingList[38].abField = kBirthYearColumn; + mSchemaMappingList[39].abField = kCustom1Column; + mSchemaMappingList[40].abField = kCustom2Column; + mSchemaMappingList[41].abField = kCustom3Column; + mSchemaMappingList[42].abField = kCustom4Column; + mSchemaMappingList[43].abField = kNotesColumn; + mSchemaMappingList[44].abField = kLastModifiedDateColumn; + mSchemaMappingList[45].abField = kDefaultEmailColumn; // Now setup the server fields... mSchemaMappingList[0].serverField = kServerFirstNameColumn; @@ -289,17 +331,25 @@ nsAbSync::InitSchemaColumns() mSchemaMappingList[24].serverField = kServerJobTitleColumn; mSchemaMappingList[25].serverField = kServerDepartmentColumn; mSchemaMappingList[26].serverField = kServerCompanyColumn; - mSchemaMappingList[27].serverField = kServerWebPage1Column; - mSchemaMappingList[28].serverField = kServerWebPage2Column; - mSchemaMappingList[29].serverField = kServerBirthYearColumn; - mSchemaMappingList[30].serverField = kServerBirthMonthColumn; - mSchemaMappingList[31].serverField = kServerBirthDayColumn; - mSchemaMappingList[32].serverField = kServerCustom1Column; - mSchemaMappingList[33].serverField = kServerCustom2Column; - mSchemaMappingList[34].serverField = kServerCustom3Column; - mSchemaMappingList[35].serverField = kServerCustom4Column; - mSchemaMappingList[36].serverField = kServerNotesColumn; - mSchemaMappingList[37].serverField = kServerLastModifiedDateColumn; + mSchemaMappingList[27].serverField = kServerAnniversaryMonthColumn; // Don't change the mm, dd, yy ordering here + mSchemaMappingList[28].serverField = kServerAnniversaryDayColumn; // since GenerateProtocolForCard() depends on it. + mSchemaMappingList[29].serverField = kServerAnniversaryYearColumn; + mSchemaMappingList[30].serverField = kServerSpouseNameColumn; + mSchemaMappingList[31].serverField = kServerFamilyNameColumn; + mSchemaMappingList[32].serverField = kServerDefaultAddressColumn; + mSchemaMappingList[33].serverField = kServerCategoryColumn; + mSchemaMappingList[34].serverField = kServerWebPage1Column; + mSchemaMappingList[35].serverField = kServerWebPage2Column; + mSchemaMappingList[36].serverField = kServerBirthMonthColumn; // Don't change the mm, dd, yy ordering here + mSchemaMappingList[37].serverField = kServerBirthDayColumn; // since GenerateProtocolForCard() depends on it. + mSchemaMappingList[38].serverField = kServerBirthYearColumn; + mSchemaMappingList[39].serverField = kServerCustom1Column; + mSchemaMappingList[40].serverField = kServerCustom2Column; + mSchemaMappingList[41].serverField = kServerCustom3Column; + mSchemaMappingList[42].serverField = kServerCustom4Column; + mSchemaMappingList[43].serverField = kServerNotesColumn; + mSchemaMappingList[44].serverField = kServerLastModifiedDateColumn; + mSchemaMappingList[45].serverField = kServerDefaultEmailColumn; return NS_OK; } @@ -575,7 +625,7 @@ NS_IMETHODIMP nsAbSync::OnStopOperation(PRInt32 aTransactionID, nsresult aStatus mCurrentState = nsIAbSyncState::nsIAbSyncIdle; -#ifdef DEBUG_rhp +#ifdef DEBUG_ABSYNC printf("ABSYNC: OnStopOperation: Status = %d\n", aStatus); #endif return NS_OK; @@ -594,7 +644,7 @@ NS_IMETHODIMP nsAbSync::GetCurrentState(PRInt32 *_retval) return NS_OK; } -/* void PerformAbSync (out PRInt32 aTransactionID); */ +/* void CancelAbSync (); */ NS_IMETHODIMP nsAbSync::CancelAbSync() { if (!mPostEngine) @@ -664,10 +714,15 @@ NS_IMETHODIMP nsAbSync::PerformAbSync(nsIDOMWindowInternal *aDOMWindow, PRInt32 if (NS_FAILED(rv)) goto EarlyExit; +#if DEBUG_OFFLINE_TEST // this is added for loop back test + rv = ProcessServerResponse("a test response"); + return NS_ERROR_FAILURE; +#endif + // We can keep this object around for reuse... if (!mPostEngine) { - rv = nsComponentManager::CreateInstance(kCAbSyncPostEngineCID, NULL, NS_GET_IID(nsIAbSyncPostEngine), getter_AddRefs(mPostEngine)); + rv = nsComponentManager::CreateInstance(NS_ABSYNC_POST_ENGINE_CONTRACTID, NULL, NS_GET_IID(nsIAbSyncPostEngine), getter_AddRefs(mPostEngine)); NS_ENSURE_SUCCESS(rv, rv); mPostEngine->AddPostListener((nsIAbSyncPostListener *)this); @@ -678,13 +733,9 @@ NS_IMETHODIMP nsAbSync::PerformAbSync(nsIDOMWindowInternal *aDOMWindow, PRInt32 goto EarlyExit; if (mPostString.IsEmpty()) - prefixStr = PR_smprintf("last=%u&protocol=%s&client=%s&ver=%s", - mLastChangeNum, ABSYNC_PROTOCOL, - clientIDStr, ABSYNC_VERSION); + prefixStr = PR_smprintf("last=%u&protocol=%s&client=%s&ver=%s", mLastChangeNum, ABSYNC_PROTOCOL, clientIDStr, ABSYNC_VERSION); else - prefixStr = PR_smprintf("last=%u&protocol=%s&client=%s&ver=%s&", - mLastChangeNum, ABSYNC_PROTOCOL, - clientIDStr, ABSYNC_VERSION); + prefixStr = PR_smprintf("last=%u&protocol=%s&client=%s&ver=%s&", mLastChangeNum, ABSYNC_PROTOCOL, clientIDStr, ABSYNC_VERSION); if (!prefixStr) { @@ -731,7 +782,7 @@ nsAbSync::OpenAB(char *aAbName, nsIAddrDatabase **aDatabase) nsFileSpec* dbPath = nsnull; nsCOMPtr abSession = - do_GetService(kAddrBookSessionCID, &rv); + do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv); if(NS_SUCCEEDED(rv)) abSession->GetUserProfileDirectory(&dbPath); @@ -742,8 +793,7 @@ nsAbSync::OpenAB(char *aAbName, nsIAddrDatabase **aDatabase) else (*dbPath) += aAbName; - nsCOMPtr addrDBFactory = - do_GetService(kAddressBookDBCID, &rv); + nsCOMPtr addrDBFactory = do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv) && addrDBFactory) rv = addrDBFactory->Open(dbPath, PR_TRUE, aDatabase, PR_TRUE); @@ -756,6 +806,57 @@ nsAbSync::OpenAB(char *aAbName, nsIAddrDatabase **aDatabase) return rv; } +NS_IMETHODIMP +nsAbSync::GenerateProtocolForList(nsIAbCard *aCard, PRBool aAddId, nsString &protLine) +{ + PRUnichar *aName = nsnull; + nsString tProtLine; + nsresult rv = NS_OK; + + protLine.Truncate(); + + if (aAddId) + { + PRUint32 aKey; + + nsCOMPtr dbcard(do_QueryInterface(aCard, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(dbcard->GetKey(&aKey))) + return NS_ERROR_FAILURE; + + char *tVal = PR_smprintf("%d", (aKey * -1)); + if (tVal) + { + tProtLine.Append(NS_LITERAL_STRING("%26cid%3D") + NS_ConvertASCIItoUCS2(tVal)); + nsCRT::free(tVal); + } + } + + if (NS_SUCCEEDED(aCard->GetCardValue(kDisplayNameColumn, &aName)) && (aName) && (*aName)) + { + tProtLine.Append(NS_LITERAL_STRING("&") + NS_LITERAL_STRING("listname") + NS_LITERAL_STRING("=")); + AddValueToProtocolLine(aName, tProtLine); + PR_FREEIF(aName); + } + + if (!tProtLine.IsEmpty()) + { + char *tLine = ToNewCString(tProtLine); + if (!tLine) + return NS_ERROR_OUT_OF_MEMORY; + + char *escData = nsEscape(tLine, url_Path); + if (escData) + tProtLine = NS_ConvertASCIItoUCS2(escData); + + PR_FREEIF(tLine); + nsCRT::free(escData); + protLine = tProtLine; + } + + return NS_OK; +} + NS_IMETHODIMP nsAbSync::GenerateProtocolForCard(nsIAbCard *aCard, PRBool aAddId, nsString &protLine) { @@ -777,7 +878,7 @@ nsAbSync::GenerateProtocolForCard(nsIAbCard *aCard, PRBool aAddId, nsString &pro if (NS_FAILED(dbcard->GetKey(&aKey))) return NS_ERROR_FAILURE; -#ifdef DEBUG_rhp +#ifdef DEBUG_ABSYNC printf("ABSYNC: GENERATING PROTOCOL FOR CARD - Address Book Card Key: %d\n", aKey); #endif @@ -789,6 +890,7 @@ nsAbSync::GenerateProtocolForCard(nsIAbCard *aCard, PRBool aAddId, nsString &pro } } + nsString birthday, anniversary; // for birthday & anniversary for (PRInt32 i=0; iGetCardValue(mSchemaMappingList[i].abField, &aName)) && (aName) && (*aName)) @@ -800,6 +902,32 @@ nsAbSync::GenerateProtocolForCard(nsIAbCard *aCard, PRBool aAddId, nsString &pro nsCaseInsensitiveStringComparator())) continue; + // Handle birthday & anniversary dates here + if (!nsCRT::strncasecmp(mSchemaMappingList[i].abField, kBirthYearColumn, strlen(kBirthYearColumn)) || + !nsCRT::strncasecmp(mSchemaMappingList[i].abField, kBirthDayColumn, strlen(kBirthDayColumn))) + { + birthday.Append(NS_LITERAL_STRING("/")); + birthday.Append(aName); + continue; + } + else if (!nsCRT::strncasecmp(mSchemaMappingList[i].abField, kBirthMonthColumn, strlen(kBirthMonthColumn))) + { + birthday.Assign(aName); + continue; + } + else if (!nsCRT::strncasecmp(mSchemaMappingList[i].abField, kAnniversaryYearColumn, strlen(kAnniversaryYearColumn)) || + !nsCRT::strncasecmp(mSchemaMappingList[i].abField, kAnniversaryDayColumn, strlen(kAnniversaryDayColumn))) + { + anniversary.Append(NS_LITERAL_STRING("/")); + anniversary.Append(aName); + continue; + } + else if (!nsCRT::strncasecmp(mSchemaMappingList[i].abField, kAnniversaryMonthColumn, strlen(kAnniversaryMonthColumn))) + { + anniversary.Assign(aName); + continue; + } + // Reset this flag... foundPhone = PR_FALSE; // If this is a phone number, we have to special case this because @@ -836,25 +964,19 @@ nsAbSync::GenerateProtocolForCard(nsIAbCard *aCard, PRBool aAddId, nsString &pro char *pVal = PR_smprintf("phone%d", phoneCount); if (pVal) { - tProtLine.Append(NS_LITERAL_STRING("&") + - NS_ConvertASCIItoUCS2(pVal) + - NS_LITERAL_STRING("=")); + tProtLine.Append(NS_LITERAL_STRING("&") + NS_ConvertASCIItoUCS2(pVal) + NS_LITERAL_STRING("=")); AddValueToProtocolLine(aName, tProtLine); - tProtLine.Append(NS_LITERAL_STRING("&") + - NS_ConvertASCIItoUCS2(pVal) + - NS_LITERAL_STRING("_type=") + - NS_ConvertASCIItoUCS2(phoneType)); + tProtLine.Append(NS_LITERAL_STRING("&") + NS_ConvertASCIItoUCS2(pVal) + + NS_LITERAL_STRING("_type=") + NS_ConvertASCIItoUCS2(phoneType)); PR_FREEIF(pVal); phoneCount++; } } else // Good ole' normal tag... { - tProtLine.Append(NS_LITERAL_STRING("&") + - mSchemaMappingList[i].serverField + - NS_LITERAL_STRING("=")); + tProtLine.Append(NS_LITERAL_STRING("&") + mSchemaMappingList[i].serverField + NS_LITERAL_STRING("=")); AddValueToProtocolLine(aName, tProtLine); } @@ -862,6 +984,18 @@ nsAbSync::GenerateProtocolForCard(nsIAbCard *aCard, PRBool aAddId, nsString &pro } } + // Now add birthday andd/or anniversary dates assembled earlier. + if (!birthday.IsEmpty()) + { + tProtLine.Append(NS_LITERAL_STRING("&") + kServerBirthdayColumn + NS_LITERAL_STRING("=")); + AddValueToProtocolLine(birthday.get(), tProtLine); + } + if (!anniversary.IsEmpty()) + { + tProtLine.Append(NS_LITERAL_STRING("&") + kServerAnniversaryColumn + NS_LITERAL_STRING("=")); + AddValueToProtocolLine(anniversary.get(), tProtLine); + } + if (!tProtLine.IsEmpty()) { // Now, check if this is that flag for the plain text email selection...if so, @@ -877,9 +1011,7 @@ nsAbSync::GenerateProtocolForCard(nsIAbCard *aCard, PRBool aAddId, nsString &pro // Just some sanity... if (aName) { - tProtLine.Append(NS_LITERAL_STRING("&") + - kServerPlainTextColumn + - NS_LITERAL_STRING("=")); + tProtLine.Append(NS_LITERAL_STRING("&") + kServerPlainTextColumn + NS_LITERAL_STRING("=")); AddValueToProtocolLine(aName, tProtLine); PR_FREEIF(aName); @@ -899,6 +1031,7 @@ nsAbSync::GenerateProtocolForCard(nsIAbCard *aCard, PRBool aAddId, nsString &pro rv = prefs->GetChildList(SYNC_PREF_PREFIX_CLIENT_MAP, &prefCount, &prefNames); if (NS_SUCCEEDED(rv) && prefCount > 0) + { for (i = 0; i < prefCount; i++) { nsXPIDLString genericValue; @@ -917,7 +1050,10 @@ nsAbSync::GenerateProtocolForCard(nsIAbCard *aCard, PRBool aAddId, nsString &pro } } } // end of for (i = 0; i < prefCount; i++) - } + + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, prefNames); + } + } } char *tLine = ToNewCString(tProtLine); @@ -931,7 +1067,7 @@ nsAbSync::GenerateProtocolForCard(nsIAbCard *aCard, PRBool aAddId, nsString &pro } PR_FREEIF(tLine); - PR_FREEIF(escData); + nsCRT::free(escData); protLine = tProtLine; } @@ -959,8 +1095,7 @@ nsresult nsAbSync::AddValueToProtocolLine(const PRUnichar *value, nsString &prot return NS_OK; } -long -GetCRC(char *str) +long nsAbSync::GetCRC(char *str) { cm_t crcModel; p_cm_t p = &crcModel; @@ -991,7 +1126,7 @@ GetCRC(char *str) } PRBool -nsAbSync::ThisCardHasChanged(nsIAbCard *aCard, syncMappingRecord *newSyncRecord, nsString &protLine) +nsAbSync::ThisCardHasChanged(nsIAbCard *aCard, syncMappingRecord *newSyncRecord, nsString &protLine, PRBool cardIsUser) { syncMappingRecord *historyRecord = nsnull; PRUint32 counter = 0; @@ -1020,8 +1155,14 @@ nsAbSync::ThisCardHasChanged(nsIAbCard *aCard, syncMappingRecord *newSyncRecord, // // Now, build up the compare protocol line for this entry. // - if (NS_FAILED(GenerateProtocolForCard(aCard, PR_FALSE, tempProtocolLine))) - return PR_FALSE; + if (cardIsUser) + { + if (NS_FAILED(GenerateProtocolForCard(aCard, PR_FALSE, tempProtocolLine))) + return PR_FALSE; + } + else + if (NS_FAILED(GenerateProtocolForList(aCard, PR_FALSE, tempProtocolLine))) + return PR_FALSE; if (tempProtocolLine.IsEmpty()) return PR_FALSE; @@ -1033,13 +1174,6 @@ nsAbSync::ThisCardHasChanged(nsIAbCard *aCard, syncMappingRecord *newSyncRecord, newSyncRecord->CRC = GetCRC(tLine); nsCRT::free(tLine); - // - // #define SYNC_MODIFIED 0x0001 // Must modify record on server - // #define SYNC_ADD 0x0002 // Must add record to server - // #define SYNC_DELETED 0x0004 // Must delete record from server - // #define SYNC_RETRY 0x0008 // Sent to server but failed...must retry! - // #define SYNC_RENUMBER 0x0010 // Renumber on the server - // // If we have a history record, we need to carry over old items! if (historyRecord) { @@ -1059,7 +1193,7 @@ nsAbSync::ThisCardHasChanged(nsIAbCard *aCard, syncMappingRecord *newSyncRecord, { newSyncRecord->flags |= SYNC_ADD; - nsresult rv = NS_OK; + nsresult rv = NS_OK; nsCOMPtr dbcard(do_QueryInterface(aCard, &rv)); NS_ENSURE_SUCCESS(rv, rv); if (NS_FAILED(dbcard->GetKey(&aKey))) @@ -1079,34 +1213,30 @@ nsAbSync::ThisCardHasChanged(nsIAbCard *aCard, syncMappingRecord *newSyncRecord, nsCRT::free(tVal); } else - { return PR_FALSE; - } } else { + // If 'aCard' is a mailing list then use 'list_id' instead of 'id' for the protocol. newSyncRecord->flags |= SYNC_MODIFIED; char *tVal2 = PR_smprintf("%d", historyRecord->serverID); if (tVal2) { - protLine.Append(NS_LITERAL_STRING("%26id%3D") + - NS_ConvertASCIItoUCS2(tVal2) + - tempProtocolLine); + if (cardIsUser) + protLine.Append(NS_LITERAL_STRING("%26id%3D") + NS_ConvertASCIItoUCS2(tVal2) + tempProtocolLine); + else + protLine.Append(NS_LITERAL_STRING("%26list_id%3D") + NS_ConvertASCIItoUCS2(tVal2) + tempProtocolLine); nsCRT::free(tVal2); } else - { return PR_FALSE; - } } return PR_TRUE; } - else // This is the same record as before. - { - return PR_FALSE; - } + else + return PR_FALSE; // This is the same record as before. return PR_FALSE; } @@ -1115,15 +1245,15 @@ char* BuildSyncTimestamp(void) { static char result[75] = ""; - PRExplodedTime now; + PRExplodedTime now; char buffer[128] = ""; - // Generate envelope line in format of: From - Sat Apr 18 20:01:49 1998 + // Generate a time line in format of: Sat Apr 18 20:01:49 1998 // // Use PR_FormatTimeUSEnglish() to format the date in US English format, - // then figure out what our local GMT offset is, and append it (since - // PR_FormatTimeUSEnglish() can't do that.) Generate four digit years as - // per RFC 1123 (superceding RFC 822.) + // then figure out what our local GMT offset is, and append it (since + // PR_FormatTimeUSEnglish() can't do that.) Generate four digit years as + // per RFC 1123 (superceding RFC 822.) // PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now); PR_FormatTimeUSEnglish(buffer, sizeof(buffer), @@ -1131,16 +1261,15 @@ BuildSyncTimestamp(void) &now); // This value must be in ctime() format, with English abbreviations. - // PL_strftime("... %c ...") is no good, because it is localized. + // PL_strftime("... %c ...") is no good, because it is localized. // - PL_strcpy(result, "Last - "); - PL_strcpy(result + 7, buffer); - PL_strcpy(result + 7 + 24, CRLF); + PL_strcpy(result, buffer); + PL_strcpy(result + 24, CRLF); return result; } NS_IMETHODIMP -nsAbSync::AnalyzeAllRecords(nsIAddrDatabase *aDatabase, nsIAbDirectory *directory) +nsAbSync::AnalyzeAllRecords(nsIAddrDatabase *aDatabase, nsIAbDirectory *directory, PRBool analyzeUser) { nsresult rv = NS_OK; nsIEnumerator *cardEnum = nsnull; @@ -1149,17 +1278,23 @@ nsAbSync::AnalyzeAllRecords(nsIAddrDatabase *aDatabase, nsIAbDirectory *director PRUint32 readCount = 0; PRInt32 readSize = 0; PRUint32 workCounter = 0; + PRUint32 listIndex = 0; nsString singleProtocolLine; // Init size vars... mOldTableSize = 0; + mListOldTableSize = 0; mNewTableSize = 0; + mListNewTableSize = 0; PR_FREEIF(mOldSyncMapingTable); PR_FREEIF(mNewSyncMapingTable); + PR_FREEIF(mListOldSyncMapingTable); + PR_FREEIF(mListNewSyncMapingTable); CleanServerTable(mNewServerTable); + CleanListServerTable(mListNewServerTable); mCurrentPostRecord = 1; -#ifdef DEBUG_rhp +#ifdef DEBUG_ABSYNC printf("ABSYNC: AnalyzeAllRecords:\n"); #endif @@ -1170,13 +1305,18 @@ nsAbSync::AnalyzeAllRecords(nsIAddrDatabase *aDatabase, nsIAbDirectory *director // we want /absync.dat // - nsCOMPtr historyFile; + nsCOMPtr historyFile; + nsCOMPtr listHistoryFile; nsCOMPtr lockFile; rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(historyFile)); NS_ENSURE_SUCCESS(rv, rv); + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(listHistoryFile)); + NS_ENSURE_SUCCESS(rv, rv); rv = historyFile->AppendNative(NS_LITERAL_CSTRING("absync.dat")); NS_ENSURE_SUCCESS(rv, rv); + rv = listHistoryFile->AppendNative(NS_LITERAL_CSTRING("absynclist.dat")); + NS_ENSURE_SUCCESS(rv, rv); // TODO: Convert the rest of the code to use // nsIFile and avoid this conversion hack. @@ -1184,6 +1324,8 @@ nsAbSync::AnalyzeAllRecords(nsIAddrDatabase *aDatabase, nsIAbDirectory *director { rv = NS_NewFileSpecFromIFile(historyFile, getter_AddRefs(mHistoryFile)); if (NS_FAILED(rv)) break; + rv = NS_NewFileSpecFromIFile(listHistoryFile, getter_AddRefs(mListHistoryFile)); + if (NS_FAILED(rv)) break; } while (0); @@ -1199,7 +1341,6 @@ nsAbSync::AnalyzeAllRecords(nsIAddrDatabase *aDatabase, nsIAbDirectory *director // //mOldSyncMapingTable = nsnull; //mNewSyncMapingTable = nsnull; - mHistoryFile->Exists(&exists); // Do this here to be used in case of a crash recovery situation... rv = aDatabase->EnumerateCards(directory, &cardEnum); @@ -1236,301 +1377,221 @@ nsAbSync::AnalyzeAllRecords(nsIAddrDatabase *aDatabase, nsIAbDirectory *director } while (0); - if (NS_SUCCEEDED(rv)) - { - PRBool tExists = PR_FALSE; - - mLockFile->Exists(&tExists); - if (tExists) + if (NS_SUCCEEDED(rv)) { - mLastSyncFailed = PR_TRUE; + PRBool tExists = PR_FALSE; - // Ok, we got here which means that the last operation must have failed and - // we should build a table of the CRC's of the address book we have locally - // and prevent us from adding the same person twice. Hope this works. - // - cardEnum->First(); - do + mLockFile->Exists(&tExists); + if (tExists) { - mCrashTableSize++; - } while (NS_SUCCEEDED(cardEnum->Next())); + // OK, the last operation must have failed. Try to recover. + RecoverUserSyncRecords(cardEnum); - mCrashTable = (syncMappingRecord *) PR_MALLOC(mCrashTableSize * sizeof(syncMappingRecord)); - if (!mCrashTable) - { - mCrashTableSize = 0; + // TODO: add code to do list recovery } - else + else // If here, create the lock file { - // Init the memory! - memset(mCrashTable, 0, (mCrashTableSize * sizeof(syncMappingRecord)) ); - nsString tProtLine; - - rv = NS_OK; - cardEnum->First(); - do + if (NS_SUCCEEDED(mLockFile->OpenStreamForWriting())) { - if (NS_FAILED(cardEnum->CurrentItem(getter_AddRefs(obj)))) - break; - else - { - nsCOMPtr card; - card = do_QueryInterface(obj, &rv); - if ( NS_SUCCEEDED(rv) && (card) ) - { - // First, we need to fill out the localID for this entry. This should - // be the ID from the local database for this card entry - // - PRUint32 aKey; - nsresult rv = NS_OK; - nsCOMPtr dbcard(do_QueryInterface(card, &rv)); - if (NS_FAILED(rv) || !dbcard) - continue; - if (NS_FAILED(dbcard->GetKey(&aKey))) - continue; + char *tMsg = BuildSyncTimestamp(); + PRInt32 tWriteSize; - // Ugh...this should never happen...BUT?? - if (aKey <= 0) - continue; - - // Ok, now get the data for this record.... - mCrashTable[workCounter].localID = aKey; - if (NS_SUCCEEDED(GenerateProtocolForCard(card, PR_FALSE, tProtLine))) - { - char *tCRCLine = ToNewCString(tProtLine); - if (tCRCLine) - { - mCrashTable[workCounter].CRC = GetCRC(tCRCLine); - PR_FREEIF(tCRCLine); - } - } - } - } - - workCounter++; - } while (NS_SUCCEEDED(cardEnum->Next())); + mLockFile->Write(tMsg, strlen(tMsg), &tWriteSize); + mLockFile->CloseStream(); + } } } - else // If here, create the lock file - { - if (NS_SUCCEEDED(mLockFile->OpenStreamForWriting())) - { - char *tMsg = BuildSyncTimestamp(); - PRInt32 tWriteSize; - - mLockFile->Write(tMsg, strlen(tMsg), &tWriteSize); - mLockFile->CloseStream(); - } - } - } - } + } } - // If the old table exists, then we need to load it up! + // If the old user table exists then load it up! + mHistoryFile->Exists(&exists); if (exists) { if (NS_SUCCEEDED(mHistoryFile->GetFileSize(&mOldTableSize))) { - if (NS_FAILED(mHistoryFile->OpenStreamForReading())) + if (NS_FAILED(LoadUsersFromHistoryFile())) { - rv = NS_ERROR_OUT_OF_MEMORY; + Log("Loading users from history file", "FAILED!"); goto GetOut; } - - mOldSyncMapingTable = (syncMappingRecord *) PR_MALLOC(mOldTableSize); - if (!mOldSyncMapingTable) - { - rv = NS_ERROR_OUT_OF_MEMORY; - goto GetOut; - } - - // Init the memory! - memset(mOldSyncMapingTable, 0, mOldTableSize); - - // Now get the number of records in the table size! - mOldTableSize /= sizeof(syncMappingRecord); - - // Now read the history file into memory! - while (readCount < mOldTableSize) - { - //if (NS_FAILED(mHistoryFile->Read((char **)&(mOldSyncMapingTable[readCount]), - // sizeof(syncMappingRecord), &readSize)) - // || (readSize != sizeof(syncMappingRecord))) - syncMappingRecord *tRecord = &mOldSyncMapingTable[readCount]; - if (NS_FAILED(mHistoryFile->Read((char **)&tRecord, - sizeof(syncMappingRecord), &readSize)) - || (readSize != sizeof(syncMappingRecord))) - { - rv = NS_ERROR_OUT_OF_MEMORY; - goto GetOut; - } - -#ifdef DEBUG_rhp - printf("------ Entry #%d --------\n", readCount); - printf("Old Sync Table: %d\n", mOldSyncMapingTable[readCount].serverID); - printf("Old Sync Table: %d\n", mOldSyncMapingTable[readCount].localID); - printf("Old Sync Table: %d\n", mOldSyncMapingTable[readCount].CRC); - printf("Old Sync Table: %d\n", mOldSyncMapingTable[readCount].flags); -#endif - - readCount++; - } } } - // - // Now create the NEW sync mapping table that we will use for this - // current operation. First, we have to count the total number of - // entries...ugh. - // - cardEnum->First(); - do + // If the old list table exists then load it up! + mListHistoryFile->Exists(&exists); + if (exists) { - mNewTableSize++; - } while (NS_SUCCEEDED(cardEnum->Next())); - - mNewSyncMapingTable = (syncMappingRecord *) PR_MALLOC(mNewTableSize * sizeof(syncMappingRecord)); - if (!mNewSyncMapingTable) - { - rv = NS_ERROR_OUT_OF_MEMORY; - goto GetOut; + if (NS_FAILED(LoadListsFromHistoryFile())) + { + Log("Loading lists from history file", "FAILED!"); + goto GetOut; + } } - // Init the memory! - memset(mNewSyncMapingTable, 0, (mNewTableSize * sizeof(syncMappingRecord)) ); + // + // Now go through all the existing cards and store them in the NEW sync + // mapping table (mNewSyncMapingTable). Comparing this table against the + // history one we can tell if some records were added, removed or modified. + // + if (NS_FAILED(rv = InitUserSyncTable(cardEnum))) + goto GetOut; + + // Now, init the list sync mapping table. + if (NS_FAILED(rv = InitListSyncTable(cardEnum))) + goto GetOut; rv = NS_OK; workCounter =0; - cardEnum->First(); - do + PRUint32 processCount; + + // We need to process users/cards first and then lists/groups. + // so we use the loop control count to do this (1 = users). + for (processCount=1; processCount<=2; processCount++) { - if (NS_FAILED(cardEnum->CurrentItem(getter_AddRefs(obj)))) - break; - else + cardEnum->First(); + do { - nsCOMPtr card; - card = do_QueryInterface(obj, &rv); - if ( NS_SUCCEEDED(rv) && (card) ) + if (NS_FAILED(cardEnum->CurrentItem(getter_AddRefs(obj)))) + break; + else { - // First, we need to fill out the localID for this entry. This should - // be the ID from the local database for this card entry - // - PRUint32 aKey; - nsresult rv = NS_OK; - nsCOMPtr dbcard(do_QueryInterface(card, &rv)); - if (NS_FAILED(rv) || !dbcard) - continue; - if (NS_FAILED(dbcard->GetKey(&aKey))) - continue; - - // Ugh...this should never happen...BUT?? - if (aKey <= 0) - continue; - - mNewSyncMapingTable[workCounter].localID = aKey; - - if (ThisCardHasChanged(card, &(mNewSyncMapingTable[workCounter]), singleProtocolLine)) + nsCOMPtr card; + card = do_QueryInterface(obj, &rv); + if ( NS_SUCCEEDED(rv) && (card) ) { - // If we get here, we should look at the flags in the mNewSyncMapingTable to see - // what we should add to the protocol header area and then tack on the singleProtcolLine - // we got back from this call. - // - // Need the separator for multiple operations... - if (!mPostString.IsEmpty()) - mPostString.Append(NS_LITERAL_STRING("&")); + // If the card is not what we're asked to analyze then ignore it. + PRBool isMailList = PR_FALSE; + rv = card->GetIsMailList(&isMailList); + if ( ((processCount==1) && isMailList) || ((processCount==2) && !isMailList) ) + continue; - if (mNewSyncMapingTable[workCounter].flags & SYNC_ADD) + // First, we need to fill out the localID for this entry. This should + // be the ID from the local database for this card entry + // + PRUint32 aKey; + nsresult rv = NS_OK; + nsCOMPtr dbcard(do_QueryInterface(card, &rv)); + if (NS_FAILED(rv) || !dbcard) + continue; + if (NS_FAILED(dbcard->GetKey(&aKey))) + continue; + + // Ugh...this should never happen...BUT?? + if (aKey <= 0) + continue; + + mNewSyncMapingTable[workCounter].localID = aKey; + // Store proper flag in the table. This flag is used when processing the deleted records. + // Since the cards were already removed from the addrbook there's no way to find out if + // the deleted cards were normal cards, aol groups, or aol additional email addresses. We + // can only rely on the stored flag in the history table. + if (isMailList) + mNewSyncMapingTable[workCounter].flags |= SYNC_IS_LIST; + else + mNewSyncMapingTable[workCounter].flags |= WhichCardType(card); + + if (ThisCardHasChanged(card, &(mNewSyncMapingTable[workCounter]), singleProtocolLine, (!isMailList))) { -#ifdef DEBUG_rhp + // For 'email address' type of cards we don't want to send servers any changes + // because the corresponding cards don't exist on servers. May have to do the + // same for AOL groups (because you can't change it on server side either). + if (mNewSyncMapingTable[workCounter].flags & SYNC_IS_AOL_ADDITIONAL_EMAIL) + { + workCounter++; + continue; + } + + // If we get here, we should know if the card is a new one or a + // modified one and then build the protocol line accordingly. + // + // Need the separator for multiple operations. + if (!mPostString.IsEmpty()) + mPostString.Append(NS_LITERAL_STRING("&")); + + if (mNewSyncMapingTable[workCounter].flags & SYNC_ADD) + { +#ifdef DEBUG_ABSYNC char *t = ToNewCString(singleProtocolLine); printf("ABSYNC: ADDING Card: %s\n", t); PR_FREEIF(t); #endif - char *tVal3 = PR_smprintf("%d", mCurrentPostRecord); - if (tVal3) - { - mPostString.Append(NS_ConvertASCIItoUCS2(tVal3) + - NS_LITERAL_STRING("=")); + char *tVal3 = PR_smprintf("%d", mCurrentPostRecord); + if (tVal3) + mPostString.Append(NS_ConvertASCIItoUCS2(tVal3) + NS_LITERAL_STRING("=")); + + // Use the appropriate cmd for the new list/user. + if (isMailList) + mPostString.Append(NS_ConvertASCIItoUCS2(SYNC_ESCAPE_ADDLIST) + singleProtocolLine); + else + mPostString.Append(NS_ConvertASCIItoUCS2(SYNC_ESCAPE_ADDUSER) + singleProtocolLine); + + PR_FREEIF(tVal3); + mCurrentPostRecord++; } - - mPostString.Append(NS_ConvertASCIItoUCS2(SYNC_ESCAPE_ADDUSER) + - singleProtocolLine); - - PR_FREEIF(tVal3); - mCurrentPostRecord++; - } - else if (mNewSyncMapingTable[workCounter].flags & SYNC_MODIFIED) - { -#ifdef DEBUG_rhp + else if (mNewSyncMapingTable[workCounter].flags & SYNC_MODIFIED) + { +#ifdef DEBUG_ABSYNC char *t = ToNewCString(singleProtocolLine); printf("ABSYNC: MODIFYING Card: %s\n", t); PR_FREEIF(t); #endif - char *tVal4 = PR_smprintf("%d", mCurrentPostRecord); - if (tVal4) - { - mPostString.Append(NS_ConvertASCIItoUCS2(tVal4) + - NS_LITERAL_STRING("=")); - } + char *tVal4 = PR_smprintf("%d", mCurrentPostRecord); + if (tVal4) + { + mPostString.Append(NS_ConvertASCIItoUCS2(tVal4) + NS_LITERAL_STRING("=")); + } - mPostString.Append(NS_ConvertASCIItoUCS2(SYNC_ESCAPE_MOD) + - singleProtocolLine); - PR_FREEIF(tVal4); - mCurrentPostRecord++; + // If the card is a mailing list then use SYNC_ESCAPE_MODLIST cmd for the protocol. + if (isMailList) + mPostString.Append(NS_ConvertASCIItoUCS2(SYNC_ESCAPE_MODLIST) + singleProtocolLine); + else + mPostString.Append(NS_ConvertASCIItoUCS2(SYNC_ESCAPE_MOD) + singleProtocolLine); + + PR_FREEIF(tVal4); + mCurrentPostRecord++; + } + } + + // If it's a list then need to generate protocol string for new memebers + // or for members have been modified (ie, modified or deleted). + if (isMailList) + { + singleProtocolLine.Truncate(); + if (mNewSyncMapingTable[workCounter].flags & SYNC_ADD) + GenerateMemberProtocolForNewList(card, listIndex, singleProtocolLine); + else + CheckCurrentListForChangedMember(card, listIndex, singleProtocolLine); + + if (!singleProtocolLine.IsEmpty()) + { + if (!mPostString.IsEmpty()) + mPostString.Append(NS_LITERAL_STRING("&")); + mPostString.Append(singleProtocolLine); + } + listIndex++; } } } - } - workCounter++; - } while (NS_SUCCEEDED(cardEnum->Next())); - - // - // Now, when we get here, we should go through the old history and see if - // there are records we need to delete since we didn't touch them when comparing - // against the current address book - // - readCount = 0; - while (readCount < mOldTableSize) - { - if (!(mOldSyncMapingTable[readCount].flags && SYNC_PROCESSED)) - { - // Need the separator for multiple operations... - if (!mPostString.IsEmpty()) - mPostString.Append(NS_LITERAL_STRING("&")); - - char *tVal = PR_smprintf("%d", mOldSyncMapingTable[readCount].serverID); - if (tVal) - { -#ifdef DEBUG_rhp - printf("ABSYNC: DELETING Card: %d\n", mOldSyncMapingTable[readCount].serverID); -#endif - - char *tVal2 = PR_smprintf("%d", mCurrentPostRecord); - if (tVal2) - { - mPostString.Append(NS_ConvertASCIItoUCS2(tVal2) + - NS_LITERAL_STRING("=")); - } - - mPostString.Append(NS_ConvertASCIItoUCS2(SYNC_ESCAPE_DEL) + - NS_LITERAL_STRING("%26id=") + - NS_ConvertASCIItoUCS2(tVal)); - nsCRT::free(tVal); - nsCRT::free(tVal2); - mCurrentPostRecord++; - } - } - - readCount++; + workCounter++; + } while (NS_SUCCEEDED(cardEnum->Next())); } + // + // Go through the old history and see if there are records we need to + // delete (the ones that are not marked PROCESSED in the history table). + // + CheckDeletedRecords(aDatabase, directory); + GetOut: if (cardEnum) delete cardEnum; if (mHistoryFile) mHistoryFile->CloseStream(); + if (mListHistoryFile) + mListHistoryFile->CloseStream(); if (NS_FAILED(rv)) { @@ -1538,8 +1599,21 @@ GetOut: mNewTableSize = 0; PR_FREEIF(mOldSyncMapingTable); PR_FREEIF(mNewSyncMapingTable); + mListOldTableSize = 0; + PR_FREEIF(mListOldSyncMapingTable); } + // Log the data to be sent to servers. + if (!mPostString.IsEmpty()) + { + nsCAutoString str; + str.AssignWithConversion(mPostString.get()); + char *unescapedStr = nsUnescape((char *)str.get()); + Log(" Protocol data sent to server", unescapedStr); + } + else + Log(" Protocol data sent to server", "Nothing has changed!"); + // Ok, get out! return rv; } @@ -1556,6 +1630,9 @@ nsAbSync::AnalyzeTheLocalAddressBook() nsCOMPtr resource = nsnull; nsCOMPtr directory = nsnull; + // Log current time + Log("Analyzing local addressbook at", BuildSyncTimestamp()); + // Init to null... mPostString.Truncate(); @@ -1565,7 +1642,7 @@ nsAbSync::AnalyzeTheLocalAddressBook() return rv; // Get the RDF service... - nsCOMPtr rdfService(do_GetService(kRDFServiceCID, &rv)); + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); if (NS_FAILED(rv)) goto EarlyExit; @@ -1586,7 +1663,7 @@ nsAbSync::AnalyzeTheLocalAddressBook() // then we need to create the mPostString protocol stuff to make // the changes // - rv = AnalyzeAllRecords(aDatabase, directory); + rv = AnalyzeAllRecords(aDatabase, directory, PR_TRUE); EarlyExit: // Database is open...make sure to close it @@ -1595,6 +1672,8 @@ EarlyExit: aDatabase->Close(PR_TRUE); } NS_IF_RELEASE(aDatabase); + + Log("Finish analyzing local addressbook at", BuildSyncTimestamp()); return rv; } @@ -1605,7 +1684,7 @@ nsAbSync::PatchHistoryTableWithNewID(PRInt32 clientID, PRInt32 serverID, PRInt32 { if (mNewSyncMapingTable[i].localID == (clientID * aMultiplier)) { -#ifdef DEBUG_rhp +#ifdef DEBUG_ABSYNC printf("ABSYNC: PATCHING History Table - Client: %d - Server: %d\n", clientID, serverID); #endif @@ -1619,7 +1698,6 @@ nsAbSync::PatchHistoryTableWithNewID(PRInt32 clientID, PRInt32 serverID, PRInt32 return NS_ERROR_FAILURE; } - /////////////////////////////////////////////// // The following is for protocol parsing /////////////////////////////////////////////// @@ -1627,9 +1705,12 @@ nsAbSync::PatchHistoryTableWithNewID(PRInt32 clientID, PRInt32 serverID, PRInt32 #define SERVER_OP_RETURN "~op_return" #define SERVER_OP_RETURN_LOCALE "dlocale=" -#define SERVER_OP_RETURN_RENAME "op=ren" +#define SERVER_OP_RETURN_RENAME "op=ren" // example: op=ren&cid=-55&sid=88 +#define SERVER_OP_RETURN_MAILLIST_RENAME "op=maillistRen" // example: op=maillistRen&cid=-57&sid=91 +#define SERVER_OP_RETURN_MAILLIST_MEMBER_RENAME "op=k2maillist" // example: op=k2maillist&list_id=91&-55=88 #define SERVER_OP_RETURN_CID "cid=" #define SERVER_OP_RETURN_SID "sid=" +#define SERVER_OP_RETURN_LIST_ID "list_id=" #define SERVER_NEW_RECORDS "~new_records_section " @@ -1701,6 +1782,64 @@ nsAbSync::ExtractCharacterString(char *aLine, char *aTag, char aDelim) return returnValue; } +nsresult +nsAbSync::ExtractMappedMemberIDs(char *aLine, char *aTag, PRInt32 *serverID, PRInt32 *memLocalID, PRInt32 *memServerID) +{ + // Example: "&list_id=166&-106=164" + if ((!aLine) || (!aTag)) + return NS_ERROR_FAILURE; + + *serverID = *memLocalID = *memServerID = 0; + char *fLoc = PL_strstr(aLine, aTag); + if (!fLoc) + return NS_ERROR_FAILURE; + + fLoc += strlen(aTag); + if (!*fLoc) + return NS_ERROR_FAILURE; + + char *endLoc = fLoc; + while ( (*endLoc) && (*endLoc != '&') ) + endLoc++; + + // Terminate it temporarily... + char saveLoc; + if (*endLoc) + { + saveLoc = *endLoc; + *endLoc = '\0'; + } + // Extract id from "&list_id=166" part. + *serverID = atoi(fLoc); + *endLoc = saveLoc; + + fLoc = endLoc; + if (*fLoc != '&') + return NS_ERROR_FAILURE; + + fLoc++; + endLoc = fLoc; + while ( (*endLoc) && (*endLoc != '=') ) + endLoc++; + + // Terminate it temporarily... + if (*endLoc) + { + saveLoc = *endLoc; + *endLoc = '\0'; + } + + // Extract ids from &-106=164 + *memLocalID = atoi(fLoc); + *endLoc = saveLoc; + if (*(endLoc+1)) + *memServerID = atoi(endLoc+1); + else + return NS_ERROR_FAILURE; + + return NS_OK; +} + // Return true if the server returned an error... PRBool nsAbSync::ErrorFromServer(char **errString) @@ -1745,8 +1884,8 @@ nsAbSync::ProcessOpReturn() mLocale = NS_ConvertASCIItoUCS2(locale); } // this is for renaming records from the server... - else if (!nsCRT::strncasecmp(workLine, SERVER_OP_RETURN_RENAME, - strlen(SERVER_OP_RETURN_RENAME))) + else if ( !nsCRT::strncasecmp(workLine, SERVER_OP_RETURN_RENAME, strlen(SERVER_OP_RETURN_RENAME)) || + !nsCRT::strncasecmp(workLine, SERVER_OP_RETURN_MAILLIST_RENAME, strlen(SERVER_OP_RETURN_MAILLIST_RENAME)) ) { char *renop = workLine; renop += strlen(SERVER_OP_RETURN_RENAME); @@ -1755,14 +1894,30 @@ nsAbSync::ProcessOpReturn() nsresult rv = NS_OK; PRInt32 clientID, serverID; - rv = ExtractInteger(renop, SERVER_OP_RETURN_CID, ' ', &clientID); - rv += ExtractInteger(renop, SERVER_OP_RETURN_SID, ' ', &serverID); + rv = ExtractInteger(renop, SERVER_OP_RETURN_CID, '&', &clientID); + rv += ExtractInteger(renop, SERVER_OP_RETURN_SID, '&', &serverID); if (NS_SUCCEEDED(rv)) { PatchHistoryTableWithNewID(clientID, serverID, -1, 0); + if (!nsCRT::strncasecmp(workLine, SERVER_OP_RETURN_MAILLIST_RENAME, strlen(SERVER_OP_RETURN_MAILLIST_RENAME))) + PatchListHistoryTableWithNewID(clientID, serverID, -1); } } } + else if ( !nsCRT::strncasecmp(workLine, SERVER_OP_RETURN_MAILLIST_MEMBER_RENAME, strlen(SERVER_OP_RETURN_MAILLIST_MEMBER_RENAME)) ) + { // handle server response for mailing list members. example: op=k2maillist&list_id=91&-55=88 + char *renop = workLine; + renop += strlen(SERVER_OP_RETURN_MAILLIST_MEMBER_RENAME); + if (*renop) + { + nsresult rv = NS_OK; + PRInt32 listServerID, memLocalID, memServerID; + + rv = ExtractMappedMemberIDs(renop, SERVER_OP_RETURN_LIST_ID, &listServerID, &memLocalID, &memServerID); + if (NS_SUCCEEDED(rv)) + PatchListHistoryTableWithNewMemberID(listServerID, memLocalID, memServerID, -1); + } + } PR_FREEIF(workLine); } @@ -1770,11 +1925,48 @@ nsAbSync::ProcessOpReturn() return NS_OK; } +PRUint32 nsAbSync::GetCardTypeByMemberId(PRUint32 aClientID) +{ + PRUint32 i; + for (i=0; iCount()) + { + syncMappingRecord *tRec = (syncMappingRecord *)mNewServerTable->ElementAt(cnt); + if (!tRec) + continue; + + if (tRec->serverID == aServerID) + { + *aClientID = tRec->localID; + return NS_OK; + } + cnt++; + } + } + return NS_ERROR_FAILURE; } +void nsAbSync::MarkDeletedInSyncTable(PRInt32 clientID) +{ + for (PRUint32 i=0; i rdfService(do_GetService(kRDFServiceCID, &rv)); + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); if (NS_FAILED(rv)) goto EarlyExit; @@ -1863,7 +2085,7 @@ nsAbSync::DeleteCardByServerID(PRInt32 aServerID) nsCOMPtr card; card = do_QueryInterface(obj, &rv); - nsresult rv = NS_OK; + nsresult rv = NS_OK; nsCOMPtr dbcard(do_QueryInterface(card, &rv)); if (NS_FAILED(rv) || !dbcard) continue; @@ -1873,6 +2095,9 @@ nsAbSync::DeleteCardByServerID(PRInt32 aServerID) if ((PRInt32) aKey == clientID) { rv = aDatabase->DeleteCard(card, PR_TRUE); + // Mark deleted in the user sync table as well. + if (NS_SUCCEEDED(rv)) + MarkDeletedInSyncTable(clientID); break; } } @@ -1893,7 +2118,7 @@ EarlyExit: } nsresult -nsAbSync::DeleteRecord() +nsAbSync::DeleteUsers() { PRInt32 i = 0; nsresult rv = NS_ERROR_FAILURE; @@ -1916,128 +2141,49 @@ nsAbSync::DeleteRecord() } nsresult -nsAbSync::DeleteList() +nsAbSync::ProcessDeletedRecords(PRUint32 sectionId) { - PRInt32 i = 0; - nsresult rv = NS_ERROR_FAILURE; - - for (i=0; iCount(); i+=mDeletedRecordTags->Count()) - { - nsString *val = mDeletedRecordValues->StringAt(i); - if ( (!val) || val->IsEmpty() ) - continue; - - PRInt32 aErrorCode; - PRInt32 delID = val->ToInteger(&aErrorCode); - if (NS_FAILED(aErrorCode)) - continue; - - // RICHIE_TODO - Ok, delete a list by a server ID - } - - return rv; -} - -nsresult -nsAbSync::DeleteGroup() -{ - PRInt32 i = 0; - nsresult rv = NS_ERROR_FAILURE; - - for (i=0; iCount(); i+=mDeletedRecordTags->Count()) - { - nsString *val = mDeletedRecordValues->StringAt(i); - if ( (!val) || val->IsEmpty() ) - continue; - - PRInt32 aErrorCode; - PRInt32 delID = val->ToInteger(&aErrorCode); - if (NS_FAILED(aErrorCode)) - continue; - - // RICHIE_TODO - Ok, delete a group by a server ID - } - - return rv; -} - -nsresult -nsAbSync::ProcessDeletedRecords() -{ - char *workLine; nsresult rv = NS_OK; - // Ok, first thing we need to do is get all of the tags for - // deleted records. - // - mDeletedRecordTags = new nsStringArray(); - if (!mDeletedRecordTags) - return NS_ERROR_OUT_OF_MEMORY; - - mDeletedRecordValues = new nsStringArray(); - if (!mDeletedRecordValues) - return NS_ERROR_OUT_OF_MEMORY; - - while ((workLine = ExtractCurrentLine()) != nsnull) - { - if (!*workLine) // end of this section - break; - - mDeletedRecordTags->AppendString(nsString(NS_ConvertASCIItoUCS2(workLine))); - PR_FREEIF(workLine); - } - - // Now, see what the next line is...if its a CRLF, then we - // really don't have anything to do here and we can just return - // - while ((workLine = ExtractCurrentLine()) != nsnull) - { - if (!*workLine) - break; - - // Ok, if we are here, then we need to loop and get the values - // for the tags in question starting at the second since the - // first has already been eaten - // - mDeletedRecordValues->AppendString(nsString(NS_ConvertASCIItoUCS2(workLine))); - for (PRInt32 i=0; iCount(); i++) - { - workLine = ExtractCurrentLine(); - if (!workLine) - return NS_ERROR_FAILURE; - - mDeletedRecordValues->AppendString(nsString(NS_ConvertASCIItoUCS2(workLine))); - } - } + rv =LoadInputValuesAndTags(&mDeletedRecordTags, &mDeletedRecordValues); + NS_ENSURE_SUCCESS(rv, rv); // Ok, now that we are here, we check to see if we have anything // in the records array. If we do, then deal with it! // // If nothing in the array...just return! - if (mDeletedRecordValues->Count() == 0) - return NS_OK; - - PRInt32 tType = DetermineTagType(mDeletedRecordTags); - switch (tType) + if (mDeletedRecordValues->Count() != 0) { - case SYNC_SINGLE_USER_TYPE: - rv += DeleteRecord(); - break; + //PRInt32 tType = DetermineTagType(mDeletedRecordTags); + switch (sectionId) + { + case 0: //SYNC_SINGLE_USER_TYPE: + rv += DeleteUsers(); + break; - case SYNC_MAILLIST_TYPE: - rv += DeleteList(); - break; + case 1: //SYNC_MAILLIST_TYPE: + rv += DeleteMailingLists(); + break; - case SYNC_GROUP_TYPE: - rv += DeleteGroup(); - break; + case 2: //SYNC_GROUP_TYPE: + rv += DeleteGroups(); + break; + + case 3: //SYNC_MAILLIST_MEMBER_TYPE: + rv = DeleteMailingListMembers(); + break; - case SYNC_UNKNOWN_TYPE: - default: - rv = NS_ERROR_FAILURE; - break; + //case SYNC_UNKNOWN_TYPE: + default: + rv = NS_ERROR_FAILURE; + break; + } } + delete mDeletedRecordValues; + mDeletedRecordValues = nsnull; + delete mDeletedRecordTags; + mDeletedRecordTags = nsnull; return rv; } @@ -2158,12 +2304,30 @@ nsAbSync::ParseNextSection() if (TagHit(SERVER_OP_RETURN, PR_TRUE)) rv = ProcessOpReturn(); - else if (TagHit(SERVER_NEW_RECORDS, PR_TRUE)) - rv = ProcessNewRecords(); - else if (TagHit(SERVER_DELETED_RECORDS, PR_TRUE)) - rv = ProcessDeletedRecords(); else if (TagHit(SERVER_LAST_CHANGED, PR_TRUE)) rv = ProcessLastChange(); + else if (TagHit(SERVER_NEW_RECORDS, PR_FALSE)) + { + if ((*mProtocolOffset >= '0') && (*mProtocolOffset <= '9')) + { + PRUint32 sesctionId = *mProtocolOffset - '0'; + AdvanceToNextLine(); + rv = ProcessNewRecords(sesctionId); + } + else + rv = NS_ERROR_FAILURE; + } + else if (TagHit(SERVER_DELETED_RECORDS, PR_FALSE)) + { + if ((*mProtocolOffset >= '0') && (*mProtocolOffset <= '9')) + { + PRUint32 sesctionId = *mProtocolOffset - '0'; + AdvanceToNextLine(); + rv = ProcessDeletedRecords(sesctionId); + } + else + rv = NS_ERROR_FAILURE; + } else // We shouldn't get here...but if we do... rv = AdvanceToNextSection(); @@ -2192,13 +2356,35 @@ nsAbSync::ProcessServerResponse(const char *aProtocolResponse) PRUnichar *outValue = GetString(NS_LITERAL_STRING("syncInvalidResponse").get()); DisplayErrorMessage(outValue); PR_FREEIF(outValue); + Log("Processing server data", "Invalid server response received!"); return NS_ERROR_FAILURE; } + // Log current event/time + Log("Processing server data at", BuildSyncTimestamp()); + // Assign the vars... mProtocolResponse = (char *)aProtocolResponse; mProtocolOffset = (char *)aProtocolResponse; + // Log the data received from servers. + if (mProtocolOffset) + ParseAndLogServerData(mProtocolOffset); + +#if DEBUG_OFFLINE_TEST + { + // Debug only - reading server data from a file. It provides a + // quick and easy way to debug server-to-client problems by simply + // preapring your server data in a text file. + char str[4*1024]; + PRInt32 numread; + FILE *fd = fopen("d:\\temp\\abserverInput.txt", "r"); + numread = fread(str, sizeof(char), 4*1024-1, fd); + mProtocolOffset = str; + fclose(fd); + } +#endif + if (ErrorFromServer(&errorString)) { PRUnichar *msgValue, *outValue=nsnull; @@ -2225,132 +2411,96 @@ nsAbSync::ProcessServerResponse(const char *aProtocolResponse) parseOk = ParseNextSection(); } - // Now, write out the history file with the new information - // - if (!mHistoryFile) - { - rv = NS_ERROR_OUT_OF_MEMORY; - goto ExitEarly; - } + // Write current existing users to user history file. + if (NS_FAILED(SaveCurrentUsersToHistoryFile())) + Log("Saving current users to history file", "FAILED!"); - if (NS_FAILED(mHistoryFile->OpenStreamForWriting())) - { - rv = NS_ERROR_OUT_OF_MEMORY; - goto ExitEarly; - } + // Write current existing lists to list history file. + if (NS_FAILED(SaveCurrentListsToHistoryFile())) + Log("Saving current lists to history file", "FAILED!"); - // Ok, this handles the entries that we knew about before we started. - while (writeCount < mNewTableSize) - { - // Sanity one more time... - if (mNewSyncMapingTable[writeCount].serverID != 0) - { - if (NS_FAILED(mHistoryFile->Write((char *)&(mNewSyncMapingTable[writeCount]), - sizeof(syncMappingRecord), &writeSize)) - || (writeSize != sizeof(syncMappingRecord))) - { - rv = NS_ERROR_OUT_OF_MEMORY; - goto ExitEarly; - } - } - - writeCount++; - } - - // These are entries that we got back from the server that are new - // to us! - writeCount = 0; - if (mNewServerTable) - { - while (writeCount < (PRUint32) mNewServerTable->Count()) - { - syncMappingRecord *tRec = (syncMappingRecord *)mNewServerTable->ElementAt(writeCount); - if (!tRec) - continue; - - if (NS_FAILED(mHistoryFile->Write((char *)(tRec), - sizeof(syncMappingRecord), &writeSize)) - || (writeSize != sizeof(syncMappingRecord))) - { - rv = NS_ERROR_OUT_OF_MEMORY; - goto ExitEarly; - } - - writeCount++; - } - } - - if (mHistoryFile) - mHistoryFile->CloseStream(); - -ExitEarly: if (mLastChangeNum > 1) { nsCOMPtr prefs(do_GetService(NS_PREF_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv) && prefs) { prefs->SetIntPref("mail.absync.last_change", mLastChangeNum); + prefs->SavePrefFile(nsnull); } } + Log("Finish processing server data at", BuildSyncTimestamp()); return NS_OK; } PRInt32 nsAbSync::DetermineTagType(nsStringArray *aArray) { - PRBool gotRecordID = PR_FALSE; - PRBool gotListID = PR_FALSE; - PRBool gotGroupID = PR_FALSE; + PRBool isCard = PR_FALSE; + PRBool isList = PR_FALSE; + PRBool isAOLGroup = PR_FALSE; + PRBool isListMember = PR_FALSE; + PRBool isListEmailMember = PR_FALSE; for (PRInt32 i = 0; iCount(); i++) { - nsString *val = mNewRecordTags->StringAt(0); + nsString *val = mNewRecordTags->StringAt(i); if ( (!val) || (val->IsEmpty()) ) continue; if (val->Equals(NS_LITERAL_STRING("record_id"))) - gotRecordID = PR_TRUE; - else if (val->Equals(NS_LITERAL_STRING("list_id"))) - gotListID = PR_TRUE; - else if (val->Equals(NS_LITERAL_STRING("group_id"))) - gotGroupID = PR_TRUE; + isCard = PR_TRUE; + else if (val->Equals(NS_LITERAL_STRING("listname"))) + isList = PR_TRUE; + else if (val->Equals(NS_LITERAL_STRING("group_name"))) + isAOLGroup = PR_TRUE; + else if (val->Equals(NS_LITERAL_STRING("contact_record_id"))) + isListMember = PR_TRUE; + else if (val->Equals(NS_LITERAL_STRING("email_string"))) + isListEmailMember = PR_TRUE; } - if (gotGroupID) + if (isAOLGroup) return SYNC_GROUP_TYPE; - else if (gotListID) + else if (isListMember) + return SYNC_MAILLIST_MEMBER_TYPE; + else if (isListEmailMember) + return SYNC_MAILLIST_MEMBER_EMAIL_TYPE; + else if (isList) return SYNC_MAILLIST_TYPE; - else if (gotRecordID) + else if (isCard) return SYNC_SINGLE_USER_TYPE; else // If we get here, don't have a clue! return SYNC_UNKNOWN_TYPE; } -nsresult -nsAbSync::ProcessNewRecords() +nsresult nsAbSync::LoadInputValuesAndTags(nsStringArray **recordTags, nsStringArray **recordValues) { - char *workLine; - nsresult rv = NS_OK; + NS_ENSURE_ARG_POINTER(recordTags); + NS_ENSURE_ARG_POINTER(recordValues); + *recordTags = *recordValues = nsnull; - // Ok, first thing we need to do is get all of the tags for - // new records. These are the server tags for address book fields. + // Get all of the tags for new records. These could be one set of tags with multiple sets of values. // - mNewRecordTags = new nsStringArray(); - if (!mNewRecordTags) + char *workLine; + nsStringArray *tagsArray = new nsStringArray(); + if (!tagsArray) return NS_ERROR_OUT_OF_MEMORY; - mNewRecordValues = new nsStringArray(); - if (!mNewRecordValues) + nsStringArray * valuesArray = new nsStringArray(); + if (!valuesArray) + { + delete tagsArray; return NS_ERROR_OUT_OF_MEMORY; + } while ((workLine = ExtractCurrentLine()) != nsnull) { if (!*workLine) // end of this section break; - mNewRecordTags->AppendString(nsString(NS_ConvertASCIItoUCS2(workLine))); + tagsArray->AppendString(nsString(NS_ConvertASCIItoUCS2(workLine))); PR_FREEIF(workLine); } @@ -2366,15 +2516,19 @@ nsAbSync::ProcessNewRecords() // for the tags in question starting at the second since the // first has already been eaten // - mNewRecordValues->AppendString(nsString(NS_ConvertASCIItoUCS2(workLine))); + valuesArray->AppendString(nsString(NS_ConvertASCIItoUCS2(workLine))); PR_FREEIF(workLine); - for (PRInt32 i=0; i<(mNewRecordTags->Count()-1); i++) + for (PRInt32 i=0; i<(tagsArray->Count()-1); i++) { workLine = ExtractCurrentLine(); if (!workLine) + { + delete tagsArray; + delete valuesArray; return NS_ERROR_FAILURE; + } - mNewRecordValues->AppendString(nsString(NS_ConvertASCIItoUCS2(workLine))); + valuesArray->AppendString(nsString(NS_ConvertASCIItoUCS2(workLine))); PR_FREEIF(workLine); } @@ -2382,32 +2536,59 @@ nsAbSync::ProcessNewRecords() workLine = ExtractCurrentLine(); PR_FREEIF(workLine); } - + + *recordTags = tagsArray; + *recordValues = valuesArray; + return NS_OK; +} + +nsresult +nsAbSync::ProcessNewRecords(PRUint32 sectionId) +{ + nsresult rv = NS_OK; + + rv =LoadInputValuesAndTags(&mNewRecordTags, &mNewRecordValues); + NS_ENSURE_SUCCESS(rv, rv); + // Ok, now that we are here, we need to figure out what type // of addition to the address book this is. // // But first...sanity! If nothing in the array...just return! - if (mNewRecordValues->Count() == 0) - return NS_OK; - - PRInt32 tType = DetermineTagType(mNewRecordTags); - switch (tType) + if (mNewRecordValues->Count() != 0) { - case SYNC_SINGLE_USER_TYPE: - rv = AddNewUsers(); - break; + switch (sectionId) + { + case 0: //SYNC_SINGLE_USER_TYPE: + rv = AddNewUsers(); + break; - case SYNC_MAILLIST_TYPE: - break; + case 1: //SYNC_MAILLIST_TYPE: + rv = AddNewMailingLists(); + break; - case SYNC_GROUP_TYPE: - break; + case 2: //SYNC_GROUP_TYPE: + rv = AddNewGroups(); + break; + + case 3: //SYNC_MAILLIST_MEMBER_TYPE: + rv = AddNewMailingListMembers(); + break; + + case 4: //SYNC_MAILLIST_MEMBER_EMAIL_TYPE: + rv = AddNewMailingListEmailMembers(); + break; - case SYNC_UNKNOWN_TYPE: - default: - return NS_ERROR_FAILURE; + //case SYNC_UNKNOWN_TYPE: + default: + return NS_ERROR_FAILURE; + } } + delete mNewRecordValues; + mNewRecordValues = nsnull; + delete mNewRecordTags; + mNewRecordTags = nsnull; + return rv; } @@ -2515,6 +2696,7 @@ PRInt32 nsAbSync::HuntForExistingABEntryInServerRecord(PRInt32 aPersonIndex, nsIAddrDatabase *aDatabase, nsIAbDirectory *directory, + PRBool isUser, PRInt32 *aServerID, nsIAbCard **newCard) { @@ -2526,6 +2708,8 @@ nsAbSync::HuntForExistingABEntryInServerRecord(PRInt32 aPersonIndex, // *aServerID = 0; *newCard = nsnull; + const char *columnName; + columnName = (isUser) ? "record_id" : "list_id"; for (j = 0; j < mNewRecordTags->Count(); j++) { nsString *val = mNewRecordValues->StringAt((aPersonIndex*(mNewRecordTags->Count())) + j); @@ -2533,12 +2717,19 @@ nsAbSync::HuntForExistingABEntryInServerRecord(PRInt32 aPersonIndex, { // See if this is the record_id... nsString *tagVal = mNewRecordTags->StringAt(j); - if (tagVal->Equals(NS_LITERAL_STRING("record_id"))) + if (isUser && tagVal->Equals(NS_LITERAL_STRING("record_id"))) { PRInt32 errorCode; *aServerID = val->ToInteger(&errorCode); break; - } + } + else + if ((!isUser) && tagVal->Equals(NS_LITERAL_STRING("list_id"))) + { + PRInt32 errorCode; + *aServerID = val->ToInteger(&errorCode); + break; + } } } @@ -2594,7 +2785,7 @@ nsAbSync::AddNewUsers() return rv; // Get the RDF service... - nsCOMPtr rdfService(do_GetService(kRDFServiceCID, &rv)); + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); if (NS_FAILED(rv)) goto EarlyExit; @@ -2636,7 +2827,7 @@ nsAbSync::AddNewUsers() // // NOTE: i is the entry of the person we are working on! // - localID = HuntForExistingABEntryInServerRecord(i, aDatabase, directory, &serverID, &tCard); + localID = HuntForExistingABEntryInServerRecord(i, aDatabase, directory, PR_TRUE, &serverID, &tCard); if ( (localID > 0) && (nsnull != tCard)) { // This is an existing entry in the local address book @@ -2647,7 +2838,7 @@ nsAbSync::AddNewUsers() else { // This is a new entry! - rv = nsComponentManager::CreateInstance(kAbCardPropertyCID, nsnull, NS_GET_IID(nsIAbCard), + rv = nsComponentManager::CreateInstance(NS_ABCARDPROPERTY_CONTRACTID, nsnull, NS_GET_IID(nsIAbCard), getter_AddRefs(newCard)); isNewCard = PR_TRUE; } @@ -2670,7 +2861,7 @@ nsAbSync::AddNewUsers() PRInt32 errorCode; serverID = val->ToInteger(&errorCode); -#ifdef DEBUG_rhp +#ifdef DEBUG_ABSYNC printf("ABSYNC: ADDING Card: %d\n", serverID); #endif } @@ -2818,6 +3009,7 @@ nsAbSync::AddNewUsers() newSyncRecord->CRC = GetCRC(tLine); newSyncRecord->serverID = serverID; newSyncRecord->localID = localID; + newSyncRecord->flags |= SYNC_IS_CARD; mNewServerTable->AppendElement((void *)newSyncRecord); } else @@ -2944,6 +3136,38 @@ nsAbSync::ProcessPhoneNumbersTheyAreSpecial(nsIAbCard *aCard) return NS_OK; } +void nsAbSync::ParseDateFields(nsIAbCard *aCard, nsString *aTagName, nsString *aTagValue) +{ + // Parse out date (in aTagValue) like 03/05/1996 and set yy, mm and dd accordingly. + PRInt32 atPos, cnt = 0; + nsString tempStr, digits; + tempStr.Assign(*aTagValue); + while ((atPos = tempStr.FindChar('/')) != -1) + { + tempStr.Left(digits, atPos); + if (cnt == 0) + { + if (aTagName->Equals(kServerBirthdayColumn)) + aCard->SetBirthMonth(digits.get()); // month + else if (aTagName->Equals(kServerAnniversaryColumn)) + aCard->SetAnniversaryMonth(digits.get()); // month + } + else + { + if (aTagName->Equals(kServerBirthdayColumn)) + aCard->SetBirthDay(digits.get()); // day + else if (aTagName->Equals(kServerAnniversaryColumn)) + aCard->SetAnniversaryDay(digits.get()); // day + } + tempStr.Cut(0, atPos+1); + cnt++; + } + if (aTagName->Equals(kServerBirthdayColumn)) + aCard->SetBirthYear(tempStr.get()); // year + else + aCard->SetAnniversaryYear(tempStr.get()); // year +} + nsresult nsAbSync::AddValueToNewCard(nsIAbCard *aCard, nsString *aTagName, nsString *aTagValue) { @@ -2971,9 +3195,7 @@ nsAbSync::AddValueToNewCard(nsIAbCard *aCard, nsString *aTagName, nsString *aTag nsCaseInsensitiveStringComparator())) { nsString tempVal; - tempVal.Append(*aTagName + - NS_LITERAL_STRING("=") + - *aTagValue); + tempVal.Append(*aTagName + NS_LITERAL_STRING("=") + *aTagValue); if (aTagName->FindChar(aChar) != -1) mPhoneTypes->AppendString(tempVal); @@ -2983,6 +3205,14 @@ nsAbSync::AddValueToNewCard(nsIAbCard *aCard, nsString *aTagName, nsString *aTag return NS_OK; } +#ifdef DEBUG_ABSYNC + char *name = ToNewCString(*aTagName); + char *value = ToNewCString(*aTagValue); + printf("ABSYNC: from server: %s = %s\n", name,value); + PR_FREEIF(name); + PR_FREEIF(value); +#endif + // Ok, we need to figure out what the tag name from the server maps to and assign // this value the new nsIAbCard // @@ -2995,16 +3225,11 @@ nsAbSync::AddValueToNewCard(nsIAbCard *aCard, nsString *aTagName, nsString *aTag else if (aTagName->Equals(kServerNicknameColumn)) aCard->SetNickName(aTagValue->get()); else if (aTagName->Equals(kServerPriEmailColumn)) - { -#ifdef DEBUG_rhp - char *t = ToNewCString(*aTagValue); - printf("Email: %s\n", t); - PR_FREEIF(t); -#endif aCard->SetPrimaryEmail(aTagValue->get()); - } else if (aTagName->Equals(kServer2ndEmailColumn)) aCard->SetSecondEmail(aTagValue->get()); + else if (aTagName->Equals(kServerDefaultEmailColumn)) + aCard->SetDefaultEmail(aTagValue->get()); else if (aTagName->Equals(kServerHomeAddressColumn)) aCard->SetHomeAddress(aTagValue->get()); else if (aTagName->Equals(kServerHomeAddress2Column)) @@ -3039,6 +3264,18 @@ nsAbSync::AddValueToNewCard(nsIAbCard *aCard, nsString *aTagName, nsString *aTag aCard->SetDepartment(aTagValue->get()); else if (aTagName->Equals(kServerCompanyColumn)) aCard->SetCompany(aTagValue->get()); + else if (aTagName->Equals(kServerBirthdayColumn)) + ParseDateFields(aCard, aTagName, aTagValue); + else if (aTagName->Equals(kServerAnniversaryColumn)) + ParseDateFields(aCard, aTagName, aTagValue); + else if (aTagName->Equals(kServerSpouseNameColumn)) + aCard->SetSpouseName(aTagValue->get()); + else if (aTagName->Equals(kServerFamilyNameColumn)) + aCard->SetFamilyName(aTagValue->get()); + else if (aTagName->Equals(kServerDefaultAddressColumn)) + aCard->SetDefaultAddress(aTagValue->get()); + else if (aTagName->Equals(kServerCategoryColumn)) + aCard->SetCategory(aTagValue->get()); else if (aTagName->Equals(kServerWebPage2Column)) aCard->SetWebPage2(aTagValue->get()); else if (aTagName->Equals(kServerCustom1Column)) @@ -3096,8 +3333,7 @@ nsAbSync::GetString(const PRUnichar *aStringName) { static const char propertyURL[] = AB_STRING_URL; - nsCOMPtr sBundleService = - do_GetService(kStringBundleServiceCID, &res); + nsCOMPtr sBundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &res); if (NS_SUCCEEDED(res) && (nsnull != sBundleService)) { res = sBundleService->CreateBundle(propertyURL, getter_AddRefs(mStringBundle)); @@ -3113,15 +3349,318 @@ nsAbSync::GetString(const PRUnichar *aStringName) return nsCRT::strdup(aStringName); } -/************ UNUSED FOR NOW -aCard->SetBirthYear(aTagValue->get()); -aCard->SetBirthMonth(aTagValue->get()); -aCard->SetBirthDay(aTagValue->get()); -char *kServerBirthYearColumn = "OMIT:BirthYear"; -char *kServerBirthMonthColumn = "OMIT:BirthMonth"; -char *kServerBirthDayColumn = "OMIT:BirthDay"; -**********************************************/ +nsresult nsAbSync::RecoverUserSyncRecords(nsIEnumerator *cardEnum) +{ + // Ok, when this function is called it means that the last operation must have + // failed and we should build a table of the CRC's of the address book we have + // locally and prevent us from adding the same person twice. Hope this works. + // + nsresult rv = NS_OK; + mLastSyncFailed = PR_TRUE; + cardEnum->First(); + do + { + mCrashTableSize++; + } while (NS_SUCCEEDED(cardEnum->Next())); -/************* FOR MAILING LISTS -aCard->SetIsMailList(aTagValue->get()); -************* FOR MAILING LISTS ***************/ + mCrashTable = (syncMappingRecord *) PR_MALLOC(mCrashTableSize * sizeof(syncMappingRecord)); + if (!mCrashTable) + { + mCrashTableSize = 0; + } + else + { + // Init the memory! + memset(mCrashTable, 0, (mCrashTableSize * sizeof(syncMappingRecord)) ); + nsString tProtLine; + PRUint32 workCounter = 0; + nsCOMPtr obj = nsnull; + + cardEnum->First(); + do + { + if (NS_FAILED(cardEnum->CurrentItem(getter_AddRefs(obj)))) + break; + else + { + nsCOMPtr card; + card = do_QueryInterface(obj, &rv); + if ( NS_SUCCEEDED(rv) && (card) ) + { + // First, we need to fill out the localID for this entry. This should + // be the ID from the local database for this card entry + // + PRUint32 aKey; + nsresult rv = NS_OK; + nsCOMPtr dbcard(do_QueryInterface(card, &rv)); + if (NS_FAILED(rv) || !dbcard) + continue; + if (NS_FAILED(dbcard->GetKey(&aKey))) + continue; + + // Ugh...this should never happen...BUT?? + if (aKey <= 0) + continue; + + // Ok, now get the data for this record.... + mCrashTable[workCounter].localID = aKey; + if (NS_SUCCEEDED(GenerateProtocolForCard(card, PR_FALSE, tProtLine))) + { + char *tCRCLine = ToNewCString(tProtLine); + if (tCRCLine) + { + mCrashTable[workCounter].CRC = GetCRC(tCRCLine); + PR_FREEIF(tCRCLine); + } + } + } + } + + workCounter++; + } while (NS_SUCCEEDED(cardEnum->Next())); + } + + return rv; +} + +nsresult nsAbSync::LoadUsersFromHistoryFile() +{ + if (NS_FAILED(mHistoryFile->OpenStreamForReading())) + return(NS_ERROR_OUT_OF_MEMORY); + + mOldSyncMapingTable = (syncMappingRecord *) PR_MALLOC(mOldTableSize); + if (!mOldSyncMapingTable) + return(NS_ERROR_OUT_OF_MEMORY); + + // Init the memory! + memset(mOldSyncMapingTable, 0, mOldTableSize); + + // Now get the number of records in the table size! + mOldTableSize /= sizeof(syncMappingRecord); + + // Now read the history file into memory! + PRUint32 readCount = 0; + PRInt32 readSize = 0; + while (readCount < mOldTableSize) + { + //if (NS_FAILED(mHistoryFile->Read((char **)&(mOldSyncMapingTable[readCount]), + // sizeof(syncMappingRecord), &readSize)) + // || (readSize != sizeof(syncMappingRecord))) + syncMappingRecord *tRecord = &mOldSyncMapingTable[readCount]; + if (NS_FAILED(mHistoryFile->Read((char **)&tRecord, sizeof(syncMappingRecord), &readSize)) + || (readSize != sizeof(syncMappingRecord))) + return(NS_ERROR_OUT_OF_MEMORY); + +#ifdef DEBUG_ABSYNC + printf("------ Entry #%d --------\n", readCount); + printf("Old Sync Table: %d\n", mOldSyncMapingTable[readCount].serverID); + printf("Old Sync Table: %d\n", mOldSyncMapingTable[readCount].localID); + printf("Old Sync Table: %d\n", mOldSyncMapingTable[readCount].CRC); + printf("Old Sync Table: %d\n", mOldSyncMapingTable[readCount].flags); +#endif + + readCount++; + } + return(NS_OK); +} + +nsresult nsAbSync::SaveCurrentUsersToHistoryFile() +{ + if (!mHistoryFile) + return NS_ERROR_FAILURE; + + if (NS_FAILED(mHistoryFile->OpenStreamForWriting())) + return NS_ERROR_FAILURE; + + // Ok, these are the lists that exist when we started the sync op. + PRInt32 writeSize; + PRUint32 writeCount = 0; + while (writeCount < mNewTableSize) + { + // Sanity one more time... + if (mNewSyncMapingTable[writeCount].serverID != 0) + { + if (NS_FAILED(mHistoryFile->Write((char *)&(mNewSyncMapingTable[writeCount]), + sizeof(syncMappingRecord), &writeSize)) + || (writeSize != sizeof(syncMappingRecord))) + return NS_ERROR_FAILURE; + } + + writeCount++; + } + + /// These are the lists that we got back from the server and are new to us now! + writeCount = 0; + if (mNewServerTable) + { + while (writeCount < (PRUint32) mNewServerTable->Count()) + { + syncMappingRecord *tRec = (syncMappingRecord *)mNewServerTable->ElementAt(writeCount); + if (!tRec) + continue; + + if (NS_FAILED(mHistoryFile->Write((char *)(tRec), + sizeof(syncMappingRecord), &writeSize)) + || (writeSize != sizeof(syncMappingRecord))) + return NS_ERROR_FAILURE; + + writeCount++; + } + } + + if (mHistoryFile) + mHistoryFile->CloseStream(); + return NS_OK; +} + +void nsAbSync::CheckDeletedRecords(nsIAddrDatabase *aDatabase, nsIAbDirectory *directory) +{ + // If a record has not been marked PROCESSED then it has been deleted. + // + PRUint32 readCount = 0; + while (readCount < mOldTableSize) + { + if (!(mOldSyncMapingTable[readCount].flags & SYNC_PROCESSED)) + { + // We can ignore SYNC_IS_AOL_ADDITIONAL_EMAIL since it doesn't exist on AOL side. + if (mOldSyncMapingTable[readCount].flags & SYNC_IS_AOL_ADDITIONAL_EMAIL) + { + readCount++; + continue; + } + + // Need the separator for multiple operations... + if (!mPostString.IsEmpty()) + mPostString.Append(NS_LITERAL_STRING("&")); + + char *tVal = PR_smprintf("%d", mOldSyncMapingTable[readCount].serverID); + if (tVal) + { +#ifdef DEBUG_ABSYNC + printf("ABSYNC: DELETING Card: %d\n", mOldSyncMapingTable[readCount].serverID); +#endif + + char *tVal2 = PR_smprintf("%d", mCurrentPostRecord); + if (tVal2) + mPostString.Append(NS_ConvertASCIItoUCS2(tVal2) + NS_LITERAL_STRING("=")); + + // If it's a list use 'list_id=' with 'maillistDel' cmd. Otherwise if it's an + // AOL group use 'grpDel' cmd instead of 'del' cmd. Use 'id=' in both cases. + if (mOldSyncMapingTable[readCount].flags & SYNC_IS_AOL_GROUPS) + mPostString.Append(NS_ConvertASCIItoUCS2(SYNC_ESCAPE_DELGROUP) + + NS_LITERAL_STRING("%26id=") + NS_ConvertASCIItoUCS2(tVal)); + else if (mOldSyncMapingTable[readCount].flags & SYNC_IS_CARD) + mPostString.Append(NS_ConvertASCIItoUCS2(SYNC_ESCAPE_DEL) + + NS_LITERAL_STRING("%26id=") + NS_ConvertASCIItoUCS2(tVal)); + else if (mOldSyncMapingTable[readCount].flags & SYNC_IS_LIST) + { // All members will be deleted once the list is removed. + mPostString.Append(NS_ConvertASCIItoUCS2(SYNC_ESCAPE_DELLIST) + + NS_LITERAL_STRING("%26list_id=") + NS_ConvertASCIItoUCS2(tVal)); + } + nsCRT::free(tVal); + nsCRT::free(tVal2); + mCurrentPostRecord++; + } + } + + readCount++; + } +} + +PRUint32 nsAbSync::WhichCardType(nsIAbCard *card) +{ + nsXPIDLString cardType; + nsresult rv = card->GetCardType(getter_Copies(cardType)); + if (cardType && cardType.Equals(NS_LITERAL_STRING(AB_CARD_IS_AOL_GROUPS))) + return SYNC_IS_AOL_GROUPS; + else if (cardType && cardType.Equals(NS_LITERAL_STRING(AB_CARD_IS_AOL_ADDITIONAL_EMAIL))) + return SYNC_IS_AOL_ADDITIONAL_EMAIL; + else + return SYNC_IS_CARD; +} + +nsresult nsAbSync::InitUserSyncTable(nsIEnumerator *cardEnum) +{ + cardEnum->First(); + do + { + mNewTableSize++; + } while (NS_SUCCEEDED(cardEnum->Next())); + + mNewSyncMapingTable = (syncMappingRecord *) PR_MALLOC(mNewTableSize * sizeof(syncMappingRecord)); + if (!mNewSyncMapingTable) + return NS_ERROR_OUT_OF_MEMORY; + + // Init the memory! + memset(mNewSyncMapingTable, 0, (mNewTableSize * sizeof(syncMappingRecord)) ); + return NS_OK; +} + +// This function is used to parse server protocol lines and log them line-by-line for +// easier reading. Note that absync server data lines are separated by a single '\10'. +void nsAbSync::ParseAndLogServerData(const char *logData) +{ + nsCAutoString workStr; + char *pChar, *start, *end; + + workStr.Assign(logData); + pChar = start = (char *)workStr.get(); + end = start + strlen(start); + while (start < end) + { + while ((pChar < end) && (*pChar != nsCRT::LF)) + pChar++; + + if (pChar < end) + { + // Found a line so log it. + *pChar = 0; + Log(" Protocol data received from server", start); + pChar++; + start = pChar; + } + else if (start < end) + { + // Log the last line and we're done. + *pChar = 0; + Log(" Protocol data received from server", start); + break; + } + } +} + +#define LOG_LINE_BUF_SIZE 256 + +void nsAbSync::Log(const char *logSubName, char *logData) +{ + if (PR_LOG_TEST(ABSYNC, PR_LOG_ALWAYS)) + { + PRInt32 length = strlen(logData); + if (length > LOG_LINE_BUF_SIZE) + { + // PR_LOG() only writes out 512 (LINE_BUF_SIZE in prlog.c) char buffer + // at a time so we need to fold the input data here. Note that depending + // on the CRLF in the log data the output buffer may be 20 or 40 bytes + // less than the original data, so it's safer to make our log data much + // smaller than 512. + char saveChar, *pChar = logData; + while (1) + { + saveChar = *(pChar + LOG_LINE_BUF_SIZE); + *(pChar + LOG_LINE_BUF_SIZE) = 0; + PR_LOG(ABSYNC, PR_LOG_ALWAYS, ("%s: %s", logSubName, pChar)); + *(pChar + LOG_LINE_BUF_SIZE) = saveChar; + length -= LOG_LINE_BUF_SIZE; + pChar = pChar + LOG_LINE_BUF_SIZE; + if (length < LOG_LINE_BUF_SIZE) + { + // Last block of data + PR_LOG(ABSYNC, PR_LOG_ALWAYS, ("%s: %s", logSubName, pChar)); + break; + } + } + } + else + PR_LOG(ABSYNC, PR_LOG_ALWAYS, ("%s: %s", logSubName, logData)); + } +} diff --git a/mozilla/mailnews/absync/src/nsAbSync.h b/mozilla/mailnews/absync/src/nsAbSync.h index e9f4c9a30c9..5ef02b494da 100644 --- a/mozilla/mailnews/absync/src/nsAbSync.h +++ b/mozilla/mailnews/absync/src/nsAbSync.h @@ -47,9 +47,11 @@ #include "nsIAbMDBCard.h" #include "nsAbSyncCRCModel.h" #include "nsVoidArray.h" +#include "nsUInt32Array.h" #include "nsIStringBundle.h" #include "nsIDocShell.h" #include "nsIFileSpec.h" +#include "prlog.h" // // Basic Sync Logic @@ -92,12 +94,25 @@ typedef struct { PRUint32 flags; } syncMappingRecord; -#define SYNC_MODIFIED 0x0001 // Must modify record on server -#define SYNC_ADD 0x0002 // Must add record to server -#define SYNC_DELETED 0x0004 // Must delete record from server -#define SYNC_RETRY 0x0008 // Sent to server but failed...must retry! -#define SYNC_RENUMBER 0x0010 // Renumber on the server -#define SYNC_PROCESSED 0x8000 // We processed the entry...nothing to do +typedef struct { + PRInt32 serverID; + PRInt32 localID; + ulong CRC; + nsUInt32Array memServerID; + nsUInt32Array memLocalID; + nsUInt32Array memFlags; +} syncListMappingRecord; + +#define SYNC_MODIFIED 0x0001 // Must modify record on server +#define SYNC_ADD 0x0002 // Must add record to server +#define SYNC_DELETED 0x0004 // Must delete record from server +#define SYNC_RETRY 0x0008 // Sent to server but failed...must retry! +#define SYNC_RENUMBER 0x0010 // Renumber on the server +#define SYNC_IS_CARD 0x0100 // It's a user record +#define SYNC_IS_LIST 0x0200 // It's a list record +#define SYNC_IS_AOL_GROUPS 0x0400 // It's an aol 'groups'. +#define SYNC_IS_AOL_ADDITIONAL_EMAIL 0x0800 // It's an aol additional email address. +#define SYNC_PROCESSED 0x8000 // We processed the entry...nothing to do #define SYNC_ALLTAGS 1000 #define SYNC_EMAILS 2000 @@ -120,20 +135,22 @@ typedef struct { #define SYNC_ESCAPE_DEL "op%3Ddel" //mailing list -#define SYNC_ESCAPE_MAIL_ADD "op%3DmaillistCreate" -#define SYNC_ESCAPE_MAIL_MOD "op%3DmaillistRen" -#define SYNC_ESCAPE_MAIL_DEL "op%3DmaillistDel" -#define SYNC_ESCAPE_MAIL_EMAIL_MOD "op%3DemailstringUpdate" -#define SYNC_ESCAPE_MAIL_CONTACT_ADD "op%3DmaillistAdd" -#define SYNC_ESCAPE_MAIL_CONTACT_DEL "op%3DmaillistMemberDel" +#define SYNC_ESCAPE_ADDLIST "op%3DmaillistCreate" +#define SYNC_ESCAPE_MODLIST "op%3DmaillistRen" +#define SYNC_ESCAPE_DELLIST "op%3DmaillistDel" +#define SYNC_ESCAPE_MOD_LIST_EMAIL "op%3DemailstringUpdate" +#define SYNC_ESCAPE_ADD_LIST_CONTACT "op%3DmaillistAdd" +#define SYNC_ESCAPE_DEL_LIST_CONTACT "op%3DmaillistMemberDel" // group -#define SYNC_ESCAPE_GROUP_DEL "op%3DgrpDel" +#define SYNC_ESCAPE_DELGROUP "op%3DgrpDel" // Defines for what type of add this may be? #define SYNC_SINGLE_USER_TYPE 1 #define SYNC_MAILLIST_TYPE 2 #define SYNC_GROUP_TYPE 3 +#define SYNC_MAILLIST_MEMBER_TYPE 4 +#define SYNC_MAILLIST_MEMBER_EMAIL_TYPE 5 #define SYNC_UNKNOWN_TYPE 0 // Server errors that need to be converted to more user-friendly ones. @@ -147,7 +164,7 @@ typedef struct { // We need this structure for mapping our field names to the server // field names // -#define kMaxColumns 38 +#define kMaxColumns 46 typedef struct { const char *abField; @@ -180,9 +197,9 @@ private: NS_IMETHOD InitSchemaColumns(); NS_IMETHOD OpenAB(char *aAbName, nsIAddrDatabase **aDatabase); - NS_IMETHOD AnalyzeAllRecords(nsIAddrDatabase *aDatabase, nsIAbDirectory *directory); + NS_IMETHOD AnalyzeAllRecords(nsIAddrDatabase *aDatabase, nsIAbDirectory *directory, PRBool analyzeUser); NS_IMETHOD GenerateProtocolForCard(nsIAbCard *aCard, PRBool aAddId, nsString &protLine); - PRBool ThisCardHasChanged(nsIAbCard *aCard, syncMappingRecord *syncRecord, nsString &protLine); + PRBool ThisCardHasChanged(nsIAbCard *aCard, syncMappingRecord *syncRecord, nsString &protLine, PRBool cardIsUser); void InternalInit(); nsresult InternalCleanup(nsresult aResult); nsresult CleanServerTable(nsVoidArray *aArray); @@ -210,6 +227,7 @@ private: PRInt32 mCurrentPostRecord; nsCOMPtr mHistoryFile; + nsCOMPtr mListHistoryFile; nsCOMPtr mLockFile; PRBool mLastSyncFailed; @@ -218,6 +236,12 @@ private: PRUint32 mNewTableSize; syncMappingRecord *mNewSyncMapingTable; // New table after reading address book nsVoidArray *mNewServerTable; // New entries from the server +// For lists + PRUint32 mListOldTableSize; + syncListMappingRecord *mListOldSyncMapingTable;// Old history table for list + PRUint32 mListNewTableSize; + syncListMappingRecord *mListNewSyncMapingTable;// New lis table for existing address book + nsVoidArray *mListNewServerTable; // New lists from the server PRUint32 mCrashTableSize; syncMappingRecord *mCrashTable; // Comparison table for crash recovery... @@ -230,6 +254,7 @@ private: /////////////////////////////////////////////// // The following is for protocol parsing /////////////////////////////////////////////// + long GetCRC(char *str); PRBool EndOfStream(); // If this returns true, we are done with the data... PRBool ParseNextSection(); // Deal with next section nsresult AdvanceToNextLine(); @@ -239,29 +264,82 @@ private: char *ExtractCharacterString(char *aLine, char *aTag, char aDelim); nsresult PatchHistoryTableWithNewID(PRInt32 clientID, PRInt32 serverID, PRInt32 aMultiplier, ulong crc); - nsresult DeleteRecord(); - nsresult DeleteList(); - nsresult DeleteGroup(); + nsresult DeleteUsers(); + nsresult DeleteMailingLists(); + nsresult DeleteGroups(); + nsresult DeleteMailingListMembers(); nsresult DeleteCardByServerID(PRInt32 aServerID); nsresult LocateClientIDFromServerID(PRInt32 aServerID, PRInt32 *aClientID); + nsresult LocateServerIDFromClientID(PRInt32 aClientID, PRInt32 *aServerID); PRInt32 DetermineTagType(nsStringArray *aArray); nsresult AddNewUsers(); + nsresult AddNewMailingLists(); + nsresult AddNewGroups(); + nsresult AddNewMailingListMembers(); + nsresult AddNewMailingListEmailMembers(); nsresult AddValueToNewCard(nsIAbCard *aCard, nsString *aTagName, nsString *aTagValue); PRBool TagHit(const char *aTag, PRBool advanceToNextLine); // See if we are sitting on a particular tag...and advance if asked PRBool ErrorFromServer(char **errString); // Return true if the server returned an error... nsresult ProcessOpReturn(); - nsresult ProcessNewRecords(); - nsresult ProcessDeletedRecords(); + nsresult ProcessNewRecords(PRUint32 sectionId); + nsresult ProcessDeletedRecords(PRUint32 sectionId); nsresult ProcessLastChange(); nsresult ProcessPhoneNumbersTheyAreSpecial(nsIAbCard *aCard); PRInt32 GetTypeOfPhoneNumber(const nsAString& tagName); nsresult AddValueToProtocolLine(const PRUnichar *value, nsString &protocolLine); + nsresult RecoverUserSyncRecords(nsIEnumerator *cardEnum); + nsresult LoadUsersFromHistoryFile(); + nsresult SaveCurrentUsersToHistoryFile(); + void CheckDeletedRecords(nsIAddrDatabase *aDatabase, nsIAbDirectory *directory); + nsresult InitUserSyncTable(nsIEnumerator *cardEnum); + nsresult LoadInputValuesAndTags(nsStringArray **recordTags, nsStringArray **recordValues); + void MarkDeletedInSyncTable(PRInt32 clientID); + PRUint32 WhichCardType(nsIAbCard *card); + void ParseDateFields(nsIAbCard *aCard, nsString *aTagName, nsString *aTagValue); + + // Routines for mail list sync + nsresult LoadListsFromHistoryFile(); + nsresult SaveCurrentListsToHistoryFile(); + nsresult InitListSyncTable(nsIEnumerator *cardEnum); + nsresult GetMemberListByCard(nsIAbCard *aCard, nsISupportsArray **aList); + nsresult GenerateMemberProtocolForNewList(nsIAbCard *aCard, PRUint32 listIndex, nsString &protLine); + nsresult CheckCurrentListForChangedMember(nsIAbCard *aCard, PRUint32 listIndex, nsString &protLine); + NS_IMETHOD GenerateProtocolForList(nsIAbCard *aCard, PRBool aAddId, nsString &protLine); + nsresult CreateANewMailingList(nsString listName, nsIAddrDatabase *pDb, nsIAbDirectory *directory, PRInt32 *localID); + nsresult CleanListServerTable(nsVoidArray *aArray); + nsresult ExtractMappedMemberIDs(char *aLine, char *aTag, PRInt32 *serverID, PRInt32 *memLocalID, PRInt32 *memServerID); + nsresult PatchListHistoryTableWithNewMemberID(PRInt32 listServerID, PRInt32 memLocalID, PRInt32 memServerID, PRInt32 aMultiplier); + nsresult PatchListHistoryTableWithNewID(PRInt32 clientID, PRInt32 listServerID, PRInt32 aMultiplier); + void ConvertListMappingEntryToString(syncListMappingRecord &listRecord, char **result); + PRUint32 CountListLines(const char *start, const char *end); + void ParseListMappingEntry(const char *start, const char *end, PRUint32 listNum); + void ExtractTwoIDs(const char *PCurPos, const char delim, PRInt32 *local, PRInt32 *server); + void ExtractThreeIDs(const char *str, const char delim, ulong *crc, PRInt32 *localId, PRInt32 *serverId); + nsresult LocateExistingListRecord(PRUint32 listID, syncListMappingRecord **result); + nsresult LocateHistoryListRecord(PRUint32 listID, syncListMappingRecord **result); + PRBool MemberNotFoundInHistory(syncListMappingRecord *listRecord, PRUint32 memberID); + void AddAListMememerToProtocolLine(PRUint32 listKey, PRUint32 memberKey, PRUint32 cid, nsString &protLine); + void CheckDeletedMembers(syncListMappingRecord *listRecord, nsString &protLine); + nsresult InitNewListTablesAndOpenDB(nsIAddrDatabase **aDatabase, nsIAbDirectory **directory); + nsresult AddMemberToList(nsIAbCard *listCard, nsIAbCard *newCard); + nsresult ChangeMailingListName(nsIAbDirectory *directory, nsString listName, nsIAbCard *listCard); + nsresult DeleteMemberFromList(nsIAddrDatabase *aDatabase, nsIAbCard *listCard, PRInt32 memberLocalID); + PRBool MemberAlreadyExists(syncListMappingRecord *listRecord, PRInt32 localID, PRInt32 serverID); + nsresult AddGroupsValueToNewCard(nsIAbCard *aCard, nsString *aTagName, nsString *aTagValue); + nsresult InitNewTables(); + nsresult ParseEmailMemberIds(nsString *idString, nsUInt32Array &memberServerIDs); + nsresult ParseEmailMemberAddresses(nsString *addrString, nsVoidArray &memberAddresses); + PRUint32 GetCardTypeByMemberId(PRUint32 aClientID); + void GenerateEmailStringUpdateProtocol(PRInt32 listServerID, nsString emailString, nsString &protLine); + void AppendProtocolCmdHeader(nsString &protLine, const char *cmd); + // For updating... PRInt32 HuntForExistingABEntryInServerRecord(PRInt32 aPersonIndex, nsIAddrDatabase *aDatabase, nsIAbDirectory *directory, + PRBool isUser, PRInt32 *aServerID, nsIAbCard **newCard); @@ -273,6 +351,9 @@ private: PRBool CardAlreadyInAddressBook(nsIAbCard *newCard, PRInt32 *aClientID, ulong *aRetCRC); + // Logging + void Log(const char *logSubName, char *logData); + void ParseAndLogServerData(const char *logData); nsString mLocale; // Charset of returned data! nsStringArray *mDeletedRecordTags; // The deleted record tags from the server... diff --git a/mozilla/mailnews/absync/src/nsAbSyncList.cpp b/mozilla/mailnews/absync/src/nsAbSyncList.cpp new file mode 100644 index 00000000000..c076c47fb73 --- /dev/null +++ b/mozilla/mailnews/absync/src/nsAbSyncList.cpp @@ -0,0 +1,2095 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifdef MOZ_LOGGING +// sorry, this has to be before the pre-compiled header +#define FORCE_PR_LOG /* Allow logging in the release build */ +#endif + +#include "nsAbSync.h" +#include "prmem.h" +#include "nsAbSyncCID.h" +#include "nsIPref.h" +#include "nsIServiceManager.h" +#include "prprf.h" +#include "nsIAddrBookSession.h" +#include "nsAbBaseCID.h" +#include "nsIRDFResource.h" +#include "nsIRDFService.h" +#include "nsRDFCID.h" +#include "nsIDirectoryService.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsXPIDLString.h" +#include "nsReadableUtils.h" +#include "nsEscape.h" +#include "nsSyncDecoderRing.h" +#include "plstr.h" +#include "nsString.h" +#include "nsReadableUtils.h" +#include "nsUnicharUtils.h" +#include "nsTextFormatter.h" +#include "nsIStringBundle.h" +#include "nsMsgI18N.h" +#include "nsIScriptGlobalObject.h" +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIDocShellTreeItem.h" +#include "nsIDocShellTreeNode.h" +#include "nsIPrompt.h" +#include "nsIWindowWatcher.h" +#include "nsIAbMDBDirectory.h" +#include "nsAbCardProperty.h" + +extern PRLogModuleInfo *ABSYNC; + +/* Implementation file */ + +// +// Each list is stored as a single line in the history file in the following sequence: +// +// list1_key/list1_server_id, +// list1_member1_key/list1_member1_server_id, +// list1_member2_key/list1_member2_server_id, +// etc. +// +// Examples: (list whose local key is 52 does not have any members): +// +// 52/73 +// 57/91,55/88,56/89 (with two members (55/88 and 56/89)) +// +// The file is loaded into memory in the following structure: +// +// typedef struct { +// PRInt32 serverID; +// PRInt32 localID; +// nsUInt32Array memServerID; +// nsUInt32Array memLocalID; +// nsUInt32Array memFlags; +// } syncListMappingRecord; +// + +nsresult nsAbSync::LoadListsFromHistoryFile() +{ + nsresult rv = NS_OK; + PRUint32 fileSize; + if (NS_FAILED(mListHistoryFile->GetFileSize(&fileSize))) + return NS_ERROR_FAILURE; + + if (NS_FAILED(mListHistoryFile->OpenStreamForReading())) + return(NS_ERROR_FAILURE); + + mListOldTableSize = 0; + char *listBuf = (char *)PR_Malloc(fileSize+1); + if (!listBuf) + return(NS_ERROR_OUT_OF_MEMORY); + + PRInt32 len = 0; + if (NS_SUCCEEDED(mListHistoryFile->Read(&listBuf, fileSize, &len)) && len > 0) + { + // Count the number of lists in the buffer. + *(listBuf+fileSize) = 0; // end buffer with '\0'. + PRUint32 listCount = CountListLines(listBuf, listBuf+fileSize); + if (listCount) + { + // Allocate the history table first. + mListOldSyncMapingTable = (syncListMappingRecord *) PR_MALLOC(listCount * sizeof(syncListMappingRecord)); + if (!mListOldSyncMapingTable) + { + PR_FREEIF(listBuf); + return NS_ERROR_OUT_OF_MEMORY; + } + + // Init memory & remember the list count in the history file + memset(mListOldSyncMapingTable, 0, listCount * sizeof(syncListMappingRecord)); + mListOldTableSize = listCount; + + char *pChar, *start, *end; + PRUint32 listNum = 0; + + pChar = start = listBuf; + end = listBuf + fileSize; + while (start < end) + { + while ((pChar < end) && (*pChar != nsCRT::CR) && (*(pChar+1) != nsCRT::LF)) + pChar++; + + if (pChar < end) + { + // Found a line so parse out the list and member ids. + *pChar = 0; + ParseListMappingEntry(start, pChar, listNum); + pChar += 2; + start = pChar; + } + else if (start < end) + { + // Check the last line and we're done. + *pChar = 0; + ParseListMappingEntry(start, pChar, listNum); + break; + } + listNum++; + } + + // Log list history table to file (can be removed later). + for (PRUint32 i = 0; i < listNum; i++) + { + if (mListOldSyncMapingTable[i].serverID != 0) + { + nsXPIDLCString str; + ConvertListMappingEntryToString(mListOldSyncMapingTable[i], getter_Copies(str)); + PR_LOG(ABSYNC, PR_LOG_DEBUG, ("%s[%d]: %s", " Loading history table", i, str.get())); + } + } + } + } + PR_FREEIF(listBuf); + + return NS_OK; +} + +PRUint32 nsAbSync::CountListLines(const char *start, const char *end) +{ + PRUint32 cnt = 0; + char *pChar, *pCurPos = (char *)start; + while (pChar = PL_strstr(pCurPos, CRLF)) + { + pCurPos = pChar+2; + cnt++; + if (pCurPos >= end) + break; + } + return cnt; +} + +void nsAbSync::ParseListMappingEntry(const char *start, const char *end, PRUint32 listNum) +{ + char *pChar, *PCurPos = (char *)start; + PRInt32 cnt=0, localId, serverId; + ulong crc; + + while (pChar < end) + { + // Get list id first. + pChar = PL_strchr(PCurPos, ','); + if (pChar) + *pChar = 0; + + if (cnt == 0) + { + ExtractThreeIDs(PCurPos, '/', &crc, &localId, &serverId); + mListOldSyncMapingTable[listNum].localID = localId; + mListOldSyncMapingTable[listNum].serverID = serverId; + mListOldSyncMapingTable[listNum].CRC = crc; + } + else + { + ExtractTwoIDs(PCurPos, '/', &localId, &serverId); + mListOldSyncMapingTable[listNum].memLocalID.Add(localId); + mListOldSyncMapingTable[listNum].memServerID.Add(serverId); + mListOldSyncMapingTable[listNum].memFlags.Add(0); // may not need this + } + + if (!pChar) + break; // we are done + + // continue to the next one + *pChar = ','; + pChar++; + PCurPos = pChar; + cnt++; + } +} + +void nsAbSync::ExtractTwoIDs(const char *str, const char delim, PRInt32 *localId, PRInt32 *serverId) +{ + *localId = *serverId = 0; + char *pChar = (char *)str; + while ( (*pChar) && (*pChar != delim) ) + pChar++; + + // Terminate the string temporarily... + if (*pChar) + *pChar = 0; + + // Now extract the ids off the form "-106=164" or "12/34". + *localId = atoi(str); + *pChar = delim; + if (*(pChar+1)) + *serverId = atoi(pChar+1); +} + +void nsAbSync::ExtractThreeIDs(const char *str, const char delim, ulong *crc, PRInt32 *localId, PRInt32 *serverId) +{ + *crc = 0; + char *pChar = (char *)str; + while ( (*pChar) && (*pChar != delim) ) + pChar++; + + // Terminate the string temporarily... + if (*pChar) + *pChar = 0; + + // Extract the crc first. + *crc = atol(str); + *pChar = delim; + if (*(pChar+1)) + ExtractTwoIDs(pChar+1, delim, localId, serverId); +} + +nsresult nsAbSync::InitListSyncTable(nsIEnumerator *cardEnum) +{ + nsresult rv; + nsCOMPtr card; + nsCOMPtr obj; + + cardEnum->First(); + do + { + if (NS_FAILED(cardEnum->CurrentItem(getter_AddRefs(obj)))) + break; + else + { + card = do_QueryInterface(obj, &rv); + if (NS_SUCCEEDED(rv) && (card)) + { + // If the card is not a list then ignore it. + PRBool isMailList = PR_FALSE; + rv = card->GetIsMailList(&isMailList); + if (isMailList) + mListNewTableSize++; + } + } + } while (NS_SUCCEEDED(cardEnum->Next())); + + if (! mListNewTableSize) + return NS_OK; + + mListNewSyncMapingTable = (syncListMappingRecord *) PR_MALLOC(mListNewTableSize * sizeof(syncListMappingRecord)); + if (!mListNewSyncMapingTable) + return NS_ERROR_OUT_OF_MEMORY; + + // Init the memory! + memset(mListNewSyncMapingTable, 0, (mListNewTableSize * sizeof(syncListMappingRecord))); + return NS_OK; +} + +nsresult nsAbSync::PatchListHistoryTableWithNewID(PRInt32 listLocalID, PRInt32 listServerID, PRInt32 aMultiplier) +{ + for (PRUint32 i = 0; i < mListNewTableSize; i++) + { + if (mListNewSyncMapingTable[i].localID == (listLocalID * aMultiplier)) + { + mListNewSyncMapingTable[i].serverID = listServerID; + return NS_OK; + } + } + + return NS_ERROR_FAILURE; +} + +nsresult nsAbSync::PatchListHistoryTableWithNewMemberID(PRInt32 listServerID, PRInt32 memLocalID, PRInt32 memServerID, PRInt32 aMultiplier) +{ + for (PRUint32 i = 0; i < mListNewTableSize; i++) + { + if (mListNewSyncMapingTable[i].serverID == listServerID) + { + PRUint32 cnt = mListNewSyncMapingTable[i].memLocalID.GetSize(); + for (PRUint32 j = 0; j < cnt; j++) + if (mListNewSyncMapingTable[i].memLocalID.GetAt(j) == (PRUint32)(memLocalID * aMultiplier)) + { + mListNewSyncMapingTable[i].memServerID.SetAt(j, memServerID); + return NS_OK; + } + } + } + + return NS_ERROR_FAILURE; +} + +nsresult nsAbSync::SaveCurrentListsToHistoryFile() +{ + if (!mListHistoryFile) + return NS_ERROR_FAILURE; + + if (NS_FAILED(mListHistoryFile->OpenStreamForWriting())) + return NS_ERROR_FAILURE; + + // Ok, these are the lists that exist when we started the sync op. + PRInt32 writeSize; + PRUint32 writeCount = 0; + while (writeCount < mListNewTableSize) + { + // If server id is not 0 then it's a valid record, so save it to file. + if (mListNewSyncMapingTable[writeCount].serverID != 0) + { + nsXPIDLCString str; + ConvertListMappingEntryToString(mListNewSyncMapingTable[writeCount], getter_Copies(str)); + PR_LOG(ABSYNC, PR_LOG_DEBUG, ("%s[%d]: %s", " Saving existing list", writeCount, str.get())); + if (NS_FAILED(mListHistoryFile->Write(str.get(), str.Length(), &writeSize)) + || (writeSize != (PRInt32)str.Length())) + return NS_ERROR_FAILURE; + } + writeCount++; + } + + // These are the lists that we got back from the server and are new to us now! + writeCount = 0; + if (mListNewServerTable) + { + while (writeCount < (PRUint32) mListNewServerTable->Count()) + { + syncListMappingRecord *tRec = (syncListMappingRecord *)mListNewServerTable->ElementAt(writeCount); + if (!tRec) + continue; + + nsXPIDLCString str; + ConvertListMappingEntryToString(*tRec, getter_Copies(str)); + PR_LOG(ABSYNC, PR_LOG_DEBUG, ("%s[%d]: %s", " Saving new list", writeCount, str.get())); + if (NS_FAILED(mListHistoryFile->Write(str.get(), str.Length(), &writeSize)) + || (writeSize != (PRInt32)str.Length())) + return NS_ERROR_FAILURE; + + writeCount++; + } + } + + if (mListHistoryFile) + mListHistoryFile->CloseStream(); + return NS_OK; +} + +// +// For a given syncListMappingRecord generate one of the following output string: +// CRC/52/73 +// CRC/57/91,55/88,56/89 +// +// Notet that 'crc' is the the check sum of all the email addresses from the 'eamil +// adress' type of members and is used to tell if we need to send server a list of +// new email addresses. This happens when the card of type 'email address' is edited +// and crd seems to be the best way to tell if something has changed (instead storing +// all email addresses in the history file). +// +void nsAbSync::ConvertListMappingEntryToString(syncListMappingRecord &listRecord, char **result) +{ + nsCAutoString idString; + char buf[24]; // enough for a ulong value. + + // Can't use AppendInt() call for an ulong since it may become negative. + sprintf(buf,"%lu", listRecord.CRC); + idString.Append(buf); + idString.Append("/"); + idString.AppendInt(listRecord.localID, 10 /* base 10 */); + idString.Append("/"); + idString.AppendInt(listRecord.serverID, 10 /* base 10 */); + + PRUint32 cnt = listRecord.memLocalID.GetSize(); + for (PRUint32 i = 0; i < cnt; i++) + if (listRecord.memServerID.GetAt(i) != 0) + { + idString.Append(","); + idString.AppendInt(listRecord.memLocalID.GetAt(i), 10 /* base 10 */); + idString.Append("/"); + idString.AppendInt(listRecord.memServerID.GetAt(i), 10 /* base 10 */); + } + + idString.Append(CRLF); // end with CRLF + *result = ToNewCString(idString); +} + +// For a given new list, generate protocol string for all new members. +// Also add it to the history table. +nsresult nsAbSync::GenerateMemberProtocolForNewList(nsIAbCard *listCard, PRUint32 listIndex, nsString &protLine) +{ + nsresult rv; + nsCOMPtr members; + + protLine.Truncate(); // init string + + nsCOMPtr dbcard(do_QueryInterface(listCard, &rv)); + if (NS_FAILED(rv) || !dbcard) + return NS_ERROR_FAILURE; + + PRUint32 listKey; + if (NS_FAILED(dbcard->GetKey(&listKey)) || (listKey <=0)) + return NS_ERROR_FAILURE; + + // Now fill in the list mapping table for this list + mListNewSyncMapingTable[listIndex].localID = listKey; + mListNewSyncMapingTable[listIndex].CRC = 0; // init crc + + rv = GetMemberListByCard(listCard, getter_AddRefs(members)); + if (NS_SUCCEEDED(rv) && members) + { + PRUint32 total = 0; + members->Count(&total); + if (total) + { + nsString emailString; + PRUint32 i; + for (i = 0; i < total; i++) + { + nsCOMPtr item = getter_AddRefs(members->ElementAt(i)); + nsCOMPtrdbCard = do_QueryInterface(item, &rv); + if (NS_FAILED(rv) || !dbCard) + break; + + PRUint32 aKey; + if (NS_FAILED(dbCard->GetKey(&aKey)) || (aKey <=0)) + continue; + + // This member may already has the server id so check it. + PRInt32 serverID; + if (NS_FAILED(LocateServerIDFromClientID(aKey, &serverID))) + continue; + + mListNewSyncMapingTable[listIndex].memLocalID.Add(aKey); + mListNewSyncMapingTable[listIndex].memServerID.Add(serverID); + mListNewSyncMapingTable[listIndex].memFlags.Add(SYNC_ADD); // may not need this + + // If it's an 'email address' card (type) then store the email address as + // we need to send servers a separate cmd for these email string members. + nsXPIDLString email; + nsCOMPtr card = do_QueryInterface(dbCard, &rv); + if (WhichCardType(card) == SYNC_IS_AOL_ADDITIONAL_EMAIL) + { + card->GetPrimaryEmail(getter_Copies(email)); + if (email && !email.IsEmpty()) + { + if (!emailString.IsEmpty()) + emailString.Append(NS_LITERAL_STRING(",")); + emailString.Append(email); + } + } + else + AddAListMememerToProtocolLine(listKey, aKey, i, protLine); + } + + // If we have 'email addresss' members then add 'emailstringUpdate' cmd to the protocol. + if (! emailString.IsEmpty()) + { + GenerateEmailStringUpdateProtocol(listKey, emailString, protLine); + // Now store the member CRC. + char *tVal = ToNewCString(emailString); + mListNewSyncMapingTable[listIndex].CRC = GetCRC(tVal); + nsCRT::free(tVal); + } + } + // If fails for any reason, rest protocol string so we don't send something wrong to the server. + if (NS_FAILED(rv)) + protLine.Truncate(); + } + return rv; +} + +void nsAbSync::AppendProtocolCmdHeader(nsString &protLine, const char *cmd) +{ + if (!protLine.IsEmpty()) + protLine.Append(NS_LITERAL_STRING("&")); + char *tVal = PR_smprintf("%d", mCurrentPostRecord); + protLine.Append(NS_ConvertASCIItoUCS2(tVal) + NS_LITERAL_STRING("=")); + protLine.Append(NS_ConvertASCIItoUCS2(cmd)); + mCurrentPostRecord++; + PR_FREEIF(tVal); +} + +void nsAbSync::GenerateEmailStringUpdateProtocol(PRInt32 listID, nsString emailString, nsString &protLine) +{ + // Construct protocol cmd header for updating member email strings, + AppendProtocolCmdHeader(protLine, SYNC_ESCAPE_MOD_LIST_EMAIL); + + PRInt32 listServerID; + if (NS_FAILED(LocateServerIDFromClientID(listID, &listServerID)) || (listServerID == 0)) + listServerID = listID * -1; + + NS_ASSERTION((listServerID>0),"ab sync: GenerateEmailStringUpdateProtocol(), serer id should be positive."); + + char *tVal = PR_smprintf("%d", listServerID); + if (tVal) + { + protLine.Append(NS_LITERAL_STRING("%26list_id%3D") + NS_ConvertASCIItoUCS2(tVal)); + nsCRT::free(tVal); + } + + protLine.Append(NS_LITERAL_STRING("%26email_string%3D") + emailString); +} + +void nsAbSync::AddAListMememerToProtocolLine(PRUint32 listKey, PRUint32 memberKey, PRUint32 cid, nsString &protLine) +{ + // Construct protocol cmd header for adding list members, + AppendProtocolCmdHeader(protLine, SYNC_ESCAPE_ADD_LIST_CONTACT); + + // If the id represents an existing local record then use the + // correspoding server id, otherwise, use the negative local id. + PRInt32 listID, recordID; + if (NS_FAILED(LocateServerIDFromClientID(listKey, &listID)) || (listID == 0)) + listID = listKey * -1; + + if (NS_FAILED(LocateServerIDFromClientID(memberKey, &recordID)) || (recordID == 0)) + recordID = memberKey * -1; + + // Now build the protocol line. + char *tVal = PR_smprintf("%d", recordID); + if (tVal) + { + protLine.Append(NS_LITERAL_STRING("%26contact_record_id%3D") + NS_ConvertASCIItoUCS2(tVal)); + nsCRT::free(tVal); + } + tVal = PR_smprintf("%d", listID); + if (tVal) + { + protLine.Append(NS_LITERAL_STRING("%26list_id%3D") + NS_ConvertASCIItoUCS2(tVal)); + nsCRT::free(tVal); + } + // cid for member list is not really used by us so we just use the member seq number here. + tVal = PR_smprintf("%d", ((cid+1) * -1)); + if (tVal) + { + protLine.Append(NS_LITERAL_STRING("%26cid%3D") + NS_ConvertASCIItoUCS2(tVal)); + nsCRT::free(tVal); + } +} + +nsresult nsAbSync::GetMemberListByCard(nsIAbCard *aCard, nsISupportsArray **aList) +{ + NS_ENSURE_ARG_POINTER(aList); + + nsresult rv; + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); + NS_ENSURE_SUCCESS(rv,rv); + + nsXPIDLCString mailListURI; + rv = aCard->GetMailListURI(getter_Copies(mailListURI)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr resource; + rv = rdfService->GetResource(mailListURI.get(), getter_AddRefs(resource)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr mailList = do_QueryInterface(resource, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + return(mailList->GetAddressLists(aList)); +} + +// For a given list card, generate protocol string for all changed members, if any. +nsresult nsAbSync::CheckCurrentListForChangedMember(nsIAbCard *listCard, PRUint32 listIndex, nsString &protLine) +{ + nsresult rv; + nsCOMPtr members; + + protLine.Truncate(); // init string + + nsCOMPtr dbcard(do_QueryInterface(listCard, &rv)); + if (NS_FAILED(rv) || !dbcard) + return NS_ERROR_FAILURE; + + PRUint32 listKey; + if (NS_FAILED(dbcard->GetKey(&listKey)) || (listKey <=0)) + return NS_ERROR_FAILURE; + + PRInt32 listServerID; + if (NS_FAILED(LocateServerIDFromClientID(listKey, &listServerID))) + return NS_ERROR_FAILURE; + + // Now fill in the list mapping table for this list + mListNewSyncMapingTable[listIndex].localID = listKey; + mListNewSyncMapingTable[listIndex].serverID = listServerID; + + rv = GetMemberListByCard(listCard, getter_AddRefs(members)); + if (NS_SUCCEEDED(rv) && members) + { + // Locate the list history entry. + syncListMappingRecord *historyRecord; + rv = LocateHistoryListRecord(listKey, &historyRecord); + NS_ENSURE_SUCCESS(rv, rv); + + nsString emailString; + PRUint32 total = 0; + members->Count(&total); + if (total) + { + // Get the member info from the history file so we can tell the difference. + // Note that the changed member is implemented by adding and deleting members. + PRUint32 i; + PRUint32 aKey; + PRInt32 serverID; + for (i = 0; i < total; i++) + { + nsCOMPtr item = getter_AddRefs(members->ElementAt(i)); + nsCOMPtr dbCard = do_QueryInterface(item, &rv); + if (NS_FAILED(rv) || !dbCard) + continue; // should we continue here? + + if (NS_FAILED(dbCard->GetKey(&aKey)) || (aKey <=0)) + continue; // should we continue here? + + if (NS_FAILED(LocateServerIDFromClientID(aKey, &serverID))) + continue; // should we continue here? + + // If it's an 'email address' card (type) then store the email address for later use. + nsXPIDLString email; + nsCOMPtr card = do_QueryInterface(dbCard, &rv); + if (WhichCardType(card) == SYNC_IS_AOL_ADDITIONAL_EMAIL) + { + card->GetPrimaryEmail(getter_Copies(email)); + if (email && !email.IsEmpty()) + { + if (!emailString.IsEmpty()) + emailString.Append(NS_LITERAL_STRING(",")); + emailString.Append(email); + } + } + + mListNewSyncMapingTable[listIndex].memLocalID.Add(aKey); + mListNewSyncMapingTable[listIndex].memServerID.Add(serverID); + mListNewSyncMapingTable[listIndex].memFlags.Add(SYNC_MODIFIED); // may not need this + + // If this member not found in the list then add it to the protocol string. + if (MemberNotFoundInHistory(historyRecord, aKey)) + AddAListMememerToProtocolLine(listKey, aKey, i, protLine); + } + } + + // Now, check non 'email address' members that have been deleted. + CheckDeletedMembers(historyRecord, protLine); + + // If email members have been altered then add 'emailstringUpdate' cmd to the protocol. + // We do this by comparing the new CRC against the one in the list history entry. + ulong newCRC = 0; + if (! emailString.IsEmpty()) + { + char *tVal = ToNewCString(emailString); + newCRC = GetCRC(tVal); + nsCRT::free(tVal); + } + + if (historyRecord->CRC != newCRC) + GenerateEmailStringUpdateProtocol(listKey, emailString, protLine); + + // Finally store the CRC in the list table. + mListNewSyncMapingTable[listIndex].CRC = newCRC; + } + return rv; +} + +void nsAbSync::CheckDeletedMembers(syncListMappingRecord *listRecord, nsString &protLine) +{ + PRUint32 cnt = listRecord->memLocalID.GetSize(); + + for (PRUint32 i = 0; i < cnt; i++) + if (!(listRecord->memFlags.GetAt(i) & SYNC_PROCESSED)) + { + // This member has been removed and if it's an "email address" card (type) + // then set emailMembersHaveChanged to true and continue. This is because + // we need a different protocol for this kind of card deletion. We also need + // to remove the card itself. + if (GetCardTypeByMemberId(listRecord->memLocalID.GetAt(i)) & SYNC_IS_AOL_ADDITIONAL_EMAIL) + { + nsresult rv = DeleteCardByServerID(listRecord->memServerID.GetAt(i)); + continue; + } + + // Construct protocol cmd header for deleting list members, + AppendProtocolCmdHeader(protLine, SYNC_ESCAPE_DEL_LIST_CONTACT); + + char *tVal = PR_smprintf("%d", listRecord->serverID); + if (tVal) + { + protLine.Append(NS_LITERAL_STRING("%26list_id%3D") + NS_ConvertASCIItoUCS2(tVal)); + nsCRT::free(tVal); + } + tVal = PR_smprintf("%d", listRecord->memServerID.GetAt(i)); + if (tVal) + { + protLine.Append(NS_LITERAL_STRING("%26contact_record_id%3D") + NS_ConvertASCIItoUCS2(tVal)); + nsCRT::free(tVal); + } + } +} + +nsresult nsAbSync::LocateExistingListRecord(PRUint32 listID, syncListMappingRecord **result) +{ + NS_ENSURE_ARG_POINTER(result); + + for (PRUint32 i = 0; i < mListNewTableSize; i++) + { + if (mListNewSyncMapingTable[i].localID == (PRInt32)listID) + { + *result = &mListNewSyncMapingTable[i]; + return NS_OK; + } + } + + if (mListNewServerTable) + { + PRUint32 writeCount = 0; + while (writeCount < (PRUint32) mListNewServerTable->Count()) + { + syncListMappingRecord *tRec = (syncListMappingRecord *)mListNewServerTable->ElementAt(writeCount); + if (!tRec) + continue; + + if (tRec->localID == (PRInt32)listID) + { + *result = tRec; + return NS_OK; + } + + writeCount++; + } + } + + return NS_ERROR_FAILURE; +} + +nsresult nsAbSync::LocateHistoryListRecord(PRUint32 listID, syncListMappingRecord **result) +{ + NS_ENSURE_ARG_POINTER(result); + + for (PRUint32 i = 0; i < mListOldTableSize; i++) + { + if (mListOldSyncMapingTable[i].localID == (PRInt32)listID) + { + *result = &mListOldSyncMapingTable[i]; + return NS_OK; + } + } + return NS_ERROR_FAILURE; +} + +PRBool nsAbSync::MemberNotFoundInHistory(syncListMappingRecord *listRecord, PRUint32 memberID) +{ + PRUint32 cnt = listRecord->memLocalID.GetSize(); + for (PRUint32 i = 0; i < cnt; i++) + if (listRecord->memLocalID.GetAt(i) == memberID) + { + listRecord->memFlags.SetAt(i, SYNC_PROCESSED); + return PR_FALSE; + } + return PR_TRUE; +} + +nsresult nsAbSync::InitNewListTablesAndOpenDB(nsIAddrDatabase **aDatabase, nsIAbDirectory **aDirectory) +{ + NS_ENSURE_ARG_POINTER(aDatabase); + NS_ENSURE_ARG_POINTER(aDirectory); + + nsresult rv; + // Get the RDF service... + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // this should not be hardcoded to abook.mab + // this works for any address book...not sure why + // absync on go againt abook.mab - candice + nsCOMPtr resource; + rv = rdfService->GetResource(kPersonalAddressbookUri, getter_AddRefs(resource)); + NS_ENSURE_SUCCESS(rv, rv); + + // query interface + nsCOMPtr directory; + directory = do_QueryInterface(resource, &rv); + NS_ENSURE_SUCCESS(rv, rv); + *aDirectory = directory; + + // Create the history array for users if it doesn't already exist. + if (!mNewServerTable) + { + mNewServerTable = new nsVoidArray(); + if (!mNewServerTable) + return NS_ERROR_OUT_OF_MEMORY; + } + + // Create the history array for lists if it doesn't already exist. + if (!mListNewServerTable) + { + mListNewServerTable = new nsVoidArray(); + if (!mListNewServerTable) + { + delete mNewServerTable; + mNewServerTable = nsnull; + return NS_ERROR_OUT_OF_MEMORY; + } + } + + // Now, open the database. + rv = OpenAB(mAbSyncAddressBookFileName, aDatabase); + return rv; +} + +nsresult nsAbSync::AddNewMailingLists() +{ + nsresult rv = NS_OK; + nsIAddrDatabase *aDatabase; + PRInt32 addCount = 0; + PRInt32 i,j; + PRInt32 serverID; + PRInt32 localID; + nsCOMPtr newCard; + nsIAbCard *tCard = nsnull; + nsString tempProtocolLine; + PRBool isNewCard = PR_TRUE; + + // Get the address book entry + nsCOMPtr resource = nsnull; + nsCOMPtr directory = nsnull; + + // Sanity check. If the numbers don't add up, then return an error. + if ((mNewRecordValues->Count() % mNewRecordTags->Count()) != 0) + return NS_ERROR_FAILURE; + + // Get the add count. + addCount = mNewRecordValues->Count() / mNewRecordTags->Count(); + + // First open the database. + rv = OpenAB(mAbSyncAddressBookFileName, &aDatabase); + if (NS_FAILED(rv)) + return rv; + + // Get the addrbook directory. + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); + if (NS_FAILED(rv)) + goto EarlyExit; + if (NS_FAILED(rdfService->GetResource(kPersonalAddressbookUri, getter_AddRefs(resource)))) + goto EarlyExit; + directory = do_QueryInterface(resource, &rv); + if (NS_FAILED(rv)) + goto EarlyExit; + + // Init new tables for new users/lists. + if (NS_FAILED(InitNewTables())) + goto EarlyExit; + + // + // Create the new card that we will eventually add to the + // database... + // + for (i = 0; i < addCount; i++) + { + serverID = 0; // for safety + + // Check to see if this is a new or existing card. + localID = HuntForExistingABEntryInServerRecord(i, aDatabase, directory, PR_FALSE, &serverID, &tCard); + if ( (localID > 0) && (nsnull != tCard)) + { + // This is an existing list in the local address book + newCard = tCard; + isNewCard = PR_FALSE; + } + else + { + // This is a new entry! + rv = nsComponentManager::CreateInstance(NS_ABCARDPROPERTY_CONTRACTID, nsnull, NS_GET_IID(nsIAbCard), + getter_AddRefs(newCard)); + isNewCard = PR_TRUE; + } + + if (NS_FAILED(rv)) + { + rv = NS_ERROR_OUT_OF_MEMORY; + goto EarlyExit; + } + + //char *listName = nsnull; + nsString listName; + PRInt32 listID; + for (j = 0; j < mNewRecordTags->Count(); j++) + { + nsString *val = mNewRecordValues->StringAt((i*(mNewRecordTags->Count())) + j); + if ( (val) && (!val->IsEmpty()) ) + { + // See if this is the record_id, keep it around for later... + nsString *tagVal = mNewRecordTags->StringAt(j); + + // Ok, "val" could still be URL Encoded, so we need to decode + // first and then pass into the call... + // + char *myTStr = ToNewCString(*val); + if (myTStr) + { + char *ret = nsUnescape(myTStr); + if (ret) + { + val->AssignWithConversion(ret); + PR_FREEIF(ret); + } + } + if (tagVal->Equals(NS_LITERAL_STRING("list_id"))) + { + PRInt32 errorCode; + listID = val->ToInteger(&errorCode); + //continue; + } + else if (tagVal->Equals(NS_LITERAL_STRING("listname"))) + listName.Assign(val->get()); + } + } + + // Ok, now that we are here, we should check if this is a recover from a crash situation. + // If it is, then we should try to find the CRC for this entry in the local address book + // and tweak a new flag if it is already there. + // + PRBool cardAlreadyThere = PR_FALSE; + + // See if last sync failed. + ulong tempCRC; + if (mLastSyncFailed) + cardAlreadyThere = CardAlreadyInAddressBook(newCard, &localID, &tempCRC); + + // Ok, now we need to modify or add the card! ONLY IF ITS NOT THERE ALREADY + if (!cardAlreadyThere) + { + // Now update the list info. + newCard->SetDisplayName(listName.get()); + newCard->SetLastName(listName.get()); + if (!isNewCard) + { + ChangeMailingListName(directory, listName, newCard); + } + else + { + // Create the new list and set the display name (for CRC purpose). + CreateANewMailingList(listName, aDatabase, directory, &localID); + } + } + + // + // Now, calculate the NEW CRC for the new or updated list... + // + if (NS_FAILED(GenerateProtocolForList(newCard, PR_FALSE, tempProtocolLine))) + continue; + + // Get the CRC for this temp entry line... + char *tLine = ToNewCString(tempProtocolLine); + if (!tLine) + continue; + + if (!isNewCard) + { + // First try to patch the old table if this is an old card...if that + // fails, then flip the flag to TRUE and have a new record created + // in newSyncRecord + // + if (NS_FAILED(PatchHistoryTableWithNewID(localID, serverID, 1, GetCRC(tLine)))) + isNewCard = PR_TRUE; + } + + if (isNewCard) + { + syncMappingRecord *newSyncRecord = nsnull; + syncListMappingRecord *newListSyncRecord = nsnull; + + newListSyncRecord = (syncListMappingRecord *)PR_Malloc(sizeof(syncListMappingRecord)); + newSyncRecord = (syncMappingRecord *)PR_Malloc(sizeof(syncMappingRecord)); + if (newListSyncRecord && newSyncRecord) + { + // Add ids to both user and list history arrays. + memset(newSyncRecord, 0, sizeof(syncMappingRecord)); + newSyncRecord->CRC = GetCRC(tLine); + newSyncRecord->serverID = serverID; + newSyncRecord->localID = localID; + newSyncRecord->flags |= SYNC_IS_LIST; + mNewServerTable->AppendElement((void *)newSyncRecord); + + memset(newListSyncRecord, 0, sizeof(syncListMappingRecord)); + newListSyncRecord->serverID = serverID; + newListSyncRecord->localID = localID; + mListNewServerTable->AppendElement((void *)newListSyncRecord); + } + else + { + PR_FREEIF(newSyncRecord); + PR_FREEIF(newListSyncRecord); + } + } + + + nsCRT::free(tLine); + + newCard = nsnull; + } + +EarlyExit: + // Database is open...make sure to close it + if (aDatabase) + aDatabase->Close(PR_TRUE); + NS_IF_RELEASE(aDatabase); + + return rv; +} + +// Used to create a new list +nsresult nsAbSync::CreateANewMailingList(nsString listName, nsIAddrDatabase *aDatabase, nsIAbDirectory *parentDir, PRInt32* localID) +{ + NS_ENSURE_ARG_POINTER(localID); + nsresult rv; + + nsCOMPtr newList; + rv = nsComponentManager::CreateInstance(NS_ABDIRPROPERTY_CONTRACTID, nsnull, NS_GET_IID(nsIAbDirectory), + getter_AddRefs(newList)); + NS_ENSURE_SUCCESS(rv, rv); + newList->SetDirName(listName.get()); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 key; + rv = parentDir->AddMailListWithKey(newList, &key); + NS_ENSURE_SUCCESS(rv, rv); + *localID = (PRUint32) key; + + return rv; +} + +PRBool nsAbSync::MemberAlreadyExists(syncListMappingRecord *listRecord, PRInt32 localID, PRInt32 serverID) +{ + PRUint32 cnt = listRecord->memLocalID.GetSize(); + for (PRUint32 i = 0; i < cnt; i++) + if ( (listRecord->memLocalID.GetAt(i) == (PRUint32)localID) && + (listRecord->memServerID.GetAt(i) == (PRUint32)serverID) ) + return PR_TRUE; + //else + // return PR_FALSE; + return PR_FALSE; +} + +nsresult nsAbSync::AddNewMailingListMembers() +{ + nsresult rv = NS_OK; + nsIAddrDatabase *aDatabase = nsnull; + PRInt32 addCount = 0; + nsIAbCard *tCard = nsnull; + nsString tempProtocolLine; + PRBool isNewCard = PR_TRUE; + // Get the address book entry + nsCOMPtr resource = nsnull; + nsCOMPtr directory = nsnull; + + // Sanity check. If the numbers don't add up, then return an error. + if ((mNewRecordValues->Count() % mNewRecordTags->Count()) != 0) + return NS_ERROR_FAILURE; + + // Get the add count. + addCount = mNewRecordValues->Count() / mNewRecordTags->Count(); + + // First open the database. + rv = OpenAB(mAbSyncAddressBookFileName, &aDatabase); + if (NS_FAILED(rv)) + return rv; + + // Get the addrbook directory. + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); + if (NS_FAILED(rv)) + goto EarlyExit; + if (NS_FAILED(rdfService->GetResource(kPersonalAddressbookUri, getter_AddRefs(resource)))) + goto EarlyExit; + directory = do_QueryInterface(resource, &rv); + if (NS_FAILED(rv)) + goto EarlyExit; + + // Init new tables for new users/lists. + if (NS_FAILED(InitNewTables())) + goto EarlyExit; + + // Find the list and member local ids (keys), get their + // cards from database and add the members to the list. + PRInt32 i, j; + for (i = 0; i < addCount; i++) + { + nsString listName; + PRInt32 listID=0, contactID=0, listLocalID=0, localID=0, errorCode; + for (j = 0; j < mNewRecordTags->Count(); j++) + { + nsString *val = mNewRecordValues->StringAt((i*(mNewRecordTags->Count())) + j); + if ( (val) && (!val->IsEmpty()) ) + { + // Look for list_id and contact_record_id. + nsString *tagVal = mNewRecordTags->StringAt(j); + + // Ok, "val" could still be URL Encoded, so we need to decode first. + char *myTStr = ToNewCString(*val); + if (myTStr) + { + char *ret = nsUnescape(myTStr); + if (ret) + { + val->AssignWithConversion(ret); + PR_FREEIF(ret); + } + } + + if (tagVal->Equals(NS_LITERAL_STRING("list_id"))) + listID = val->ToInteger(&errorCode); + else if (tagVal->Equals(NS_LITERAL_STRING("contact_record_id"))) + contactID = val->ToInteger(&errorCode); + } + } + + // Find the local list id (which is our db key) from server id. + if (NS_FAILED(LocateClientIDFromServerID(listID, &listLocalID))) + continue; // continue since the record has already been removed. + + // Find the local member id (which is our db key) from server id. + if (NS_FAILED(LocateClientIDFromServerID(contactID, &localID))) + continue; // should we continue? + + // See if the member already exists (in case servers send us one such for some reason). + syncListMappingRecord *listRecord; + if (NS_FAILED(LocateExistingListRecord(listLocalID, &listRecord))) + continue; + + if (MemberAlreadyExists(listRecord, localID, contactID)) + continue; // continue since the member already exists. + + // Now, we have the member local key, get the card in the address book. + nsCOMPtr newCard; + if (NS_FAILED(FindCardByClientID(localID, aDatabase, directory, getter_AddRefs(newCard)))) + continue; // continue since the record has already been removed. + + // Now, we have the list local key, get the card in the address book. + nsCOMPtr listCard; + if (NS_FAILED(FindCardByClientID(listLocalID, aDatabase, directory, getter_AddRefs(listCard)))) + continue; // should we continue? + + // Now, add the member to the list. + rv = AddMemberToList(listCard, newCard); + NS_ENSURE_SUCCESS(rv, rv); + rv = aDatabase->Commit(nsAddrDBCommitType::kLargeCommit); + + // Seems like verything is fine so add the member to the sync table. + if (NS_SUCCEEDED(rv)) + { + listRecord->memLocalID.Add(localID); + listRecord->memServerID.Add(contactID); + listRecord->memFlags.Add(SYNC_PROCESSED); // mark processed so no one can remove it. + } + } + +EarlyExit: + // Database is open...make sure to close it + if (aDatabase) + aDatabase->Close(PR_TRUE); + NS_IF_RELEASE(aDatabase); + + return rv; +} + +nsresult nsAbSync::ChangeMailingListName(nsIAbDirectory *parentDir, nsString listName, nsIAbCard *listCard) +{ + if (!parentDir || !listCard) + return NS_ERROR_FAILURE; + + nsresult rv = NS_OK; + + // Get the parent directory uri. + nsCOMPtr dbdirectory(do_QueryInterface(parentDir, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsXPIDLCString parentUri; + rv = dbdirectory->GetDirUri(getter_Copies(parentUri)); + NS_ENSURE_SUCCESS(rv, rv); + + // Now get the nsIAbDirectory ptr for the list itself. + nsXPIDLCString mailListURI; + rv = listCard->GetMailListURI(getter_Copies(mailListURI)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr resource; + rv = rdfService->GetResource(mailListURI.get(), getter_AddRefs(resource)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr mailList = do_QueryInterface(resource, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + mailList->SetDirName(listName.get()); + + rv = mailList->EditMailListToDatabase(parentUri.get(), listCard); + return rv; +} + +nsresult nsAbSync::AddMemberToList(nsIAbCard *listCard, nsIAbCard *newCard) +{ + if (!listCard) + return NS_ERROR_FAILURE; + + nsresult rv; + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); + NS_ENSURE_SUCCESS(rv,rv); + + nsXPIDLCString mailListURI; + rv = listCard->GetMailListURI(getter_Copies(mailListURI)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr resource; + rv = rdfService->GetResource(mailListURI.get(), getter_AddRefs(resource)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr mailList = do_QueryInterface(resource, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + // Add the new member to list. + nsCOMPtr dbList(do_QueryInterface(mailList, &rv)); + NS_ENSURE_SUCCESS(rv,rv); + dbList->AddAddressToList(newCard); + NS_ENSURE_SUCCESS(rv,rv); + + // Update the list. + rv = mailList->EditMailListToDatabase(mailListURI.get(), listCard); + return rv; +} + +nsresult nsAbSync::DeleteMemberFromList(nsIAddrDatabase *aDatabase, nsIAbCard *listCard, PRInt32 memberLocalID) +{ + if (!listCard) + return NS_ERROR_FAILURE; + + nsresult rv; + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); + NS_ENSURE_SUCCESS(rv,rv); + + nsXPIDLCString mailListURI; + rv = listCard->GetMailListURI(getter_Copies(mailListURI)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr resource; + rv = rdfService->GetResource(mailListURI.get(), getter_AddRefs(resource)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr mailList = do_QueryInterface(resource, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr members; + rv = mailList->GetAddressLists(getter_AddRefs(members)); + NS_ENSURE_SUCCESS(rv,rv); + + // Remove the member from the list's address list and then edit the list itself. + PRUint32 total = 0; + members->Count(&total); + if (total) + { + for (PRUint32 i = 0; i < total; i++) + { + nsCOMPtr item = getter_AddRefs(members->ElementAt(i)); + nsCOMPtr dbcard = do_QueryInterface(item, &rv); + if (NS_FAILED(rv) || !dbcard) + continue; + PRUint32 aKey; + if (NS_FAILED(dbcard->GetKey(&aKey)) || (aKey <=0)) + continue; + if (aKey == (PRUint32)memberLocalID) + { + members->DeleteElementAt(i); + rv = mailList->EditMailListToDatabase(mailListURI.get(), listCard); + break; + } + } + } + return rv; +} + +nsresult nsAbSync::DeleteMailingListMembers() +{ + // DeleteCardFromMailList(nsIAbDirectory *mailList, nsIAbCard *card, PRBool aNotify) + nsresult rv = NS_ERROR_FAILURE; + nsCOMPtr resource; + nsCOMPtr directory; + nsIAddrDatabase *aDatabase = nsnull; + + // Sanity check. If the numbers don't add up, then return an error. + if ((mDeletedRecordValues->Count() % mDeletedRecordTags->Count()) != 0) + return NS_ERROR_FAILURE; + + // Get the delete count. + PRInt32 delCount = mDeletedRecordValues->Count() / mDeletedRecordTags->Count(); + + // First open the database. + rv = OpenAB(mAbSyncAddressBookFileName, &aDatabase); + if (NS_FAILED(rv)) + return rv; + + // Get the addrbook directory. + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); + if (NS_FAILED(rv)) + goto EarlyExit; + if (NS_FAILED(rdfService->GetResource(kPersonalAddressbookUri, getter_AddRefs(resource)))) + goto EarlyExit; + directory = do_QueryInterface(resource, &rv); + if (NS_FAILED(rv)) + goto EarlyExit; + + // Init new tables for new users/lists. + if (NS_FAILED(InitNewTables())) + goto EarlyExit; + + // + // Find the list and member local ids (keys) and get their cards from database. + // + PRInt32 i, j; + for (i = 0; i < delCount; i++) + { + nsString listName; + PRInt32 listID=0, contactID=0, listLocalID=0, memberLocalID=0, errorCode; + for (j = 0; j < mDeletedRecordTags->Count(); j++) + { + nsString *val = mDeletedRecordValues->StringAt((i*(mDeletedRecordTags->Count())) + j); + if ( (val) && (!val->IsEmpty()) ) + { + nsString *tagVal = mDeletedRecordTags->StringAt(j); + // Look for list_id . + if (tagVal->Equals(NS_LITERAL_STRING("list_id"))) + listID = val->ToInteger(&errorCode); + else if (tagVal->Equals(NS_LITERAL_STRING("contact_record_id"))) + contactID = val->ToInteger(&errorCode); + } + } + + if (NS_FAILED(LocateClientIDFromServerID(listID, &listLocalID))) + continue; + if (NS_FAILED(LocateClientIDFromServerID(contactID, &memberLocalID))) + continue; + nsCOMPtr listCard; + if (NS_FAILED(FindCardByClientID(listLocalID, aDatabase, directory, getter_AddRefs(listCard)))) + continue; + //nsCOMPtr memberCard; + //if (NS_FAILED(FindCardByClientID(memberLocalID, aDatabase, directory, getter_AddRefs(memberCard)))) + // continue; + + // Now delete the member. + rv = DeleteMemberFromList(aDatabase, listCard, memberLocalID); + + if (NS_SUCCEEDED(rv)) + { + syncListMappingRecord *listRecord; + // reset server id so we don't save it to history table later. + rv = LocateExistingListRecord(listLocalID, &listRecord); + if (NS_SUCCEEDED(rv)) + { + PRUint32 cnt = listRecord->memLocalID.GetSize(); + for (PRUint32 k = 0; k < cnt; k++) + if (listRecord->memLocalID.GetAt(k) == (PRUint32)memberLocalID) + listRecord->memServerID.SetAt(k, 0); + } + } + } + +EarlyExit: + if (aDatabase) + aDatabase->Close(PR_TRUE); + NS_IF_RELEASE(aDatabase); + + return rv; +} + +nsresult +nsAbSync::DeleteMailingLists() +{ + nsresult rv = NS_ERROR_FAILURE; + nsCOMPtr resource; + nsCOMPtr directory; + nsIAddrDatabase *aDatabase = nsnull; + + // Sanity check. If the numbers don't add up, then return an error. + if ((mDeletedRecordValues->Count() % mDeletedRecordTags->Count()) != 0) + return NS_ERROR_FAILURE; + + // Get the delete count. + PRInt32 delCount = mDeletedRecordValues->Count() / mDeletedRecordTags->Count(); + + // First open the database. + rv = OpenAB(mAbSyncAddressBookFileName, &aDatabase); + if (NS_FAILED(rv)) + return rv; + + // Get the addrbook directory. + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); + if (NS_FAILED(rv)) + goto EarlyExit; + if (NS_FAILED(rdfService->GetResource(kPersonalAddressbookUri, getter_AddRefs(resource)))) + goto EarlyExit; + directory = do_QueryInterface(resource, &rv); + if (NS_FAILED(rv)) + goto EarlyExit; + + // Init new tables for new users/lists. + if (NS_FAILED(InitNewTables())) + goto EarlyExit; + + // This is the list of cards that will be deleted later. + //nsCOMPtr cardsToDelete; + //rv = NS_NewISupportsArray(getter_AddRefs(cardsToDelete)); + //NS_ENSURE_SUCCESS(rv,rv); + + // Find the list local ids (keys) and remove the cards from database. + PRInt32 i, j; + for (i = 0; i < delCount; i++) + { + nsString listName; + PRInt32 listID=0, errorCode; + for (j = 0; j < mDeletedRecordTags->Count(); j++) + { + nsString *val = mDeletedRecordValues->StringAt((i*(mDeletedRecordTags->Count())) + j); + if ( (val) && (!val->IsEmpty()) ) + { + nsString *tagVal = mDeletedRecordTags->StringAt(j); + // Look for list_id . + if (!tagVal->Equals(NS_LITERAL_STRING("list_id"))) + continue; + + listID = val->ToInteger(&errorCode); + if ((listID == 0) || NS_FAILED(errorCode)) + continue; + + // Now delete the list card. + rv = DeleteCardByServerID(listID); + if (NS_SUCCEEDED(rv)) + { + PRInt32 listLocalID; + syncListMappingRecord *listRecord; + if (NS_SUCCEEDED(LocateClientIDFromServerID(listID, &listLocalID))) + { + // Reset server id so we don't save it to history table later. + rv = LocateExistingListRecord(listLocalID, &listRecord); + listRecord->serverID = 0; + } + } + //nsCOMPtr listCard; + //if (NS_FAILED(FindCardBySserverID(listID, aDatabase, directory, getter_AddRefs(listCard)))) + // continue; + + //nsCOMPtr supports = do_QueryInterface(listCard, &rv); + //if (NS_FAILED(rv)) + // continue; + + //if (NS_FAILED(cardsToDelete->AppendElement(supports))) + // continue; + } + } + } + +EarlyExit: + //rv = directory->DeleteCards(cardsToDelete); + if (aDatabase) + aDatabase->Close(PR_TRUE); + NS_IF_RELEASE(aDatabase); + + return rv; +} + +nsresult nsAbSync::DeleteGroups() +{ + // Since we map a group to a sinlge card in the db AND the + // server respnose to delete a group is the same as deleting + // a user (ie, just record_id) so we simply call DeleteUsers() + // to delete the card for the group. + return (DeleteUsers()); +} + +nsresult nsAbSync::AddNewGroups() +{ + nsresult rv = NS_OK; + nsIAddrDatabase *aDatabase; + PRInt32 addCount = 0; + PRInt32 i,j; + PRInt32 serverID; + PRInt32 localID; + nsCOMPtr newCard; + nsIAbCard *tCard = nsnull; + nsString tempProtocolLine; + PRBool isNewCard = PR_TRUE; + + // Get the address book entry + nsCOMPtr resource = nsnull; + nsCOMPtr directory = nsnull; + + // Sanity check. If the numbers don't add up, then return an error. + if ((mNewRecordValues->Count() % mNewRecordTags->Count()) != 0) + return NS_ERROR_FAILURE; + + // Get the add count. + addCount = mNewRecordValues->Count() / mNewRecordTags->Count(); + + // First open the database. + rv = OpenAB(mAbSyncAddressBookFileName, &aDatabase); + if (NS_FAILED(rv)) + return rv; + + // Get the addrbook directory. + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); + if (NS_FAILED(rv)) + goto EarlyExit; + if (NS_FAILED(rdfService->GetResource(kPersonalAddressbookUri, getter_AddRefs(resource)))) + goto EarlyExit; + directory = do_QueryInterface(resource, &rv); + if (NS_FAILED(rv)) + goto EarlyExit; + + // Init new tables for new users/lists. + if (NS_FAILED(InitNewTables())) + goto EarlyExit; + + // + // Create the new card that we will eventually add to the database. + // + for (i = 0; i < addCount; i++) + { + serverID = 0; // for safety + + // Check to see if this is a new or existing card. + localID = HuntForExistingABEntryInServerRecord(i, aDatabase, directory, PR_TRUE, &serverID, &tCard); + if ( (localID > 0) && (nsnull != tCard)) + { + // This is an existing card in the local address book + newCard = tCard; + isNewCard = PR_FALSE; + } + else + { + // This is a new entry! + rv = nsComponentManager::CreateInstance(NS_ABCARDPROPERTY_CONTRACTID, nsnull, NS_GET_IID(nsIAbCard), + getter_AddRefs(newCard)); + isNewCard = PR_TRUE; + } + + if (NS_FAILED(rv) || !newCard) + { + rv = NS_ERROR_OUT_OF_MEMORY; + goto EarlyExit; + } + + // Parse info associated with the card type and store it in the new card. + for (j = 0; j < mNewRecordTags->Count(); j++) + { + nsString *val = mNewRecordValues->StringAt((i*(mNewRecordTags->Count())) + j); + if ( (val) && (!val->IsEmpty()) ) + { + // See if this is the record_id, keep it around for later... + nsString *tagVal = mNewRecordTags->StringAt(j); + if (tagVal->Equals(NS_LITERAL_STRING("record_id"))) + { + PRInt32 errorCode; + serverID = val->ToInteger(&errorCode); + } + + // Ok, "val" could still be URL Encoded, so we need to decode it first. + char *myTStr = ToNewCString(*val); + if (myTStr) + { + char *ret = nsUnescape(myTStr); + if (ret) + { + val->AssignWithConversion(ret); + PR_FREEIF(ret); + } + } + AddGroupsValueToNewCard(newCard, mNewRecordTags->StringAt(j), val); + } + } + + // Set default email to other email #1. + nsString defaulEmail; + defaulEmail.AssignWithConversion(AB_DEFAULT_EMAIL_IS_EMAIL_1); + newCard->SetDefaultEmail(defaulEmail.get()); + + // Set card type to AOL groups. + nsString cardType; + cardType.AssignWithConversion(AB_CARD_IS_AOL_GROUPS); + newCard->SetCardType(cardType.get()); + + // Ok, now that we are here, we should check if this is a recover from a crash situation. + // If it is, then we should try to find the CRC for this entry in the local address book + // and tweak a new flag if it is already there. + // + PRBool cardAlreadyThere = PR_FALSE; + + // See if last sync failed. + ulong tempCRC; + if (mLastSyncFailed) + cardAlreadyThere = CardAlreadyInAddressBook(newCard, &localID, &tempCRC); + + // Ok, now we need to modify or add the card! ONLY IF ITS NOT THERE ALREADY + if (!cardAlreadyThere) + { + if (!isNewCard) + rv = aDatabase->EditCard(newCard, PR_TRUE); + else + { + // hack + // there is no way (currently) to set generic attributes on simple abcardproperties + // so we have to use a mdbcard instead. We can remove this once #128567 is fixed. + nsCOMPtr dbcard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv); + if (NS_FAILED(rv)) + continue; + + nsCOMPtr card = do_QueryInterface(dbcard, &rv); + if (NS_FAILED(rv)) + continue; + + rv = card->Copy(newCard); + if (NS_FAILED(rv)) + continue; + + dbcard->SetAbDatabase(aDatabase); + PRUint32 tID; + // card has to be a mdbcard, or tID will come back as 0 + rv = aDatabase->CreateNewCardAndAddToDBWithKey(card, PR_TRUE, &tID); + localID = (PRInt32) tID; + newCard = card; + } + } + + // Now, calculate the CRC for the new group. Since it's mapped to a card so treat it that way. + if (NS_FAILED(GenerateProtocolForCard(newCard, PR_FALSE, tempProtocolLine))) + continue; + + // Get the CRC for this temp entry line... + char *tLine = ToNewCString(tempProtocolLine); + if (!tLine) + continue; + + if (!isNewCard) + { + // Try to patch the old table if this is an old card. If that fails, then + // flip the flag to TRUE and have a new record created in newSyncRecord + // + if (NS_FAILED(PatchHistoryTableWithNewID(localID, serverID, 1, GetCRC(tLine)))) + isNewCard = PR_TRUE; + } + + if (isNewCard) + { + syncMappingRecord *newSyncRecord = (syncMappingRecord *)PR_Malloc(sizeof(syncMappingRecord)); + if (newSyncRecord) + { + memset(newSyncRecord, 0, sizeof(syncMappingRecord)); + newSyncRecord->CRC = GetCRC(tLine); + newSyncRecord->serverID = serverID; + newSyncRecord->localID = localID; + newSyncRecord->flags |= SYNC_IS_AOL_GROUPS; + mNewServerTable->AppendElement((void *)newSyncRecord); + } + } + + nsCRT::free(tLine); + newCard = nsnull; + } + +EarlyExit: + // Database is open...make sure to close it + if (aDatabase) + aDatabase->Close(PR_TRUE); + NS_IF_RELEASE(aDatabase); + + return rv; +} + +nsresult nsAbSync::AddGroupsValueToNewCard(nsIAbCard *aCard, nsString *aTagName, nsString *aTagValue) +{ + nsresult rv = NS_OK; + + nsString outValue; + char *tValue = nsnull; + + tValue = ToNewCString(*aTagValue); + if (tValue) + { + rv = nsMsgI18NConvertToUnicode(nsCAutoString("UTF-8"), nsCAutoString(tValue), outValue); + if (NS_SUCCEEDED(rv)) + aTagValue->Assign(outValue); + PR_FREEIF(tValue); + } + + if (aTagName->Equals(NS_LITERAL_STRING("group_name"))) + { // Map group name to both last and display names. + aCard->SetLastName(aTagValue->get()); + aCard->SetDisplayName(aTagValue->get()); + } + else if (aTagName->Equals(NS_LITERAL_STRING("group_email"))) + aCard->SetPrimaryEmail(aTagValue->get()); + else if (aTagName->Equals(NS_LITERAL_STRING("group_url"))) + aCard->SetWebPage2(aTagValue->get()); + + return rv; +} + +nsresult nsAbSync::InitNewTables() +{ + // Create the table for new users from servers if it doesn't already exist. + if (!mNewServerTable) + { + mNewServerTable = new nsVoidArray(); + if (!mNewServerTable) + return NS_ERROR_OUT_OF_MEMORY; + } + // Create the table for new lists from servers if it doesn't already exist. + if (!mListNewServerTable) + { + mListNewServerTable = new nsVoidArray(); + if (!mListNewServerTable) + { + delete mNewServerTable; + mNewServerTable = nsnull; + return NS_ERROR_OUT_OF_MEMORY; + } + } + return NS_OK; +} + +nsresult nsAbSync::AddNewMailingListEmailMembers() +{ + nsresult rv = NS_OK; + nsIAddrDatabase *aDatabase; + PRInt32 addCount = 0; + PRInt32 i, j, cnt; + PRInt32 serverID, listServerID, listLocalID; + PRInt32 localID; + nsCOMPtr newCard; + nsIAbCard *tCard = nsnull; + nsString tempProtocolLine; + + // Get the address book entry + nsCOMPtr resource = nsnull; + nsCOMPtr directory = nsnull; + + // Sanity check. If the numbers don't add up, then return an error. + if ((mNewRecordValues->Count() % mNewRecordTags->Count()) != 0) + return NS_ERROR_FAILURE; + + // Get the add count. + addCount = mNewRecordValues->Count() / mNewRecordTags->Count(); + + // First open the database. + rv = OpenAB(mAbSyncAddressBookFileName, &aDatabase); + if (NS_FAILED(rv)) + return rv; + + // Get the addrbook directory. + nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); + if (NS_FAILED(rv)) + goto EarlyExit; + if (NS_FAILED(rdfService->GetResource(kPersonalAddressbookUri, getter_AddRefs(resource)))) + goto EarlyExit; + directory = do_QueryInterface(resource, &rv); + if (NS_FAILED(rv)) + goto EarlyExit; + + // Init new tables for new users/lists. + if (NS_FAILED(InitNewTables())) + goto EarlyExit; + + // For each email address, create a card of type 'email address'. + for (i = 0; i < addCount; i++) + { + // Parse info associated with the card type and store it in the new card. + nsVoidArray memberAddresses; + nsUInt32Array memberServerIDs; + for (j = 0; j < mNewRecordTags->Count(); j++) + { + PRInt32 errorCode; + nsString *val = mNewRecordValues->StringAt((i*(mNewRecordTags->Count())) + j); + if ( (val) && (!val->IsEmpty()) ) + { + // There may be multiple ids for 'id_string' so parse + // and store them in an array. Same for 'email_string'. + nsString *tagVal = mNewRecordTags->StringAt(j); + if (tagVal->Equals(NS_LITERAL_STRING("list_id"))) + listServerID = val->ToInteger(&errorCode); + else if (tagVal->Equals(NS_LITERAL_STRING("id_string"))) + ParseEmailMemberIds(val, memberServerIDs); + else if (tagVal->Equals(NS_LITERAL_STRING("email_string"))) + { + // Ok, "val" could still be URL Encoded, so we need to decode it first. + char *myTStr = ToNewCString(*val); + if (myTStr) + { + char *ret = nsUnescape(myTStr); + if (ret) + { + val->AssignWithConversion(ret); + PR_FREEIF(ret); + } + } + ParseEmailMemberAddresses(val, memberAddresses); + } + } + } + + // Sanity check. If the number of email string ids != number + // of addresses it's most milkely a server protocol error. + if (memberAddresses.Count() != (PRInt32) memberServerIDs.GetSize()) + continue; + + // Find the list local id (which is our db key) from server id. + if (NS_FAILED(LocateClientIDFromServerID(listServerID, &listLocalID))) + continue; // continue since the record has already been removed. + + // Locate the list mapping entry so we can examine its members. + syncListMappingRecord *listRecord; + if (NS_FAILED(LocateExistingListRecord(listLocalID, &listRecord))) + continue; + + // Locate the card for the list in the address book. + nsCOMPtr listCard; + if (NS_FAILED(FindCardByClientID(listLocalID, aDatabase, directory, getter_AddRefs(listCard)))) + continue; + + // The following two server protocol responses show that a2@abc.com (id=113) + // is removed from list id 110: + // + // 110 + // a1@abc.com,a2@abc.com,a3@abc.com + // 112 113 114 + // + // <2nd server response> + // 110 + // a1@abc.com,a3@abc.com + // 112 114 + // + // So the sync servers give you all the existing "additional email addresses" in + // the protocol instead of separate add and delete cmds. This is quite different + // from normal add/delete contacts and lists where servers give you one by one. + + // Delete all the existing email address type of cards associated with the list + // first and then add the new ones. + cnt = listRecord->memLocalID.GetSize(); + for (j = 0; j < cnt; j++) + if ( (GetCardTypeByMemberId(listRecord->memLocalID.GetAt(j)) & SYNC_IS_AOL_ADDITIONAL_EMAIL) ) + { + // Remove the member from the list first and then remove the card itself. + // Need to reset the member server id to 0 so it's not saved to history file. + rv = DeleteMemberFromList(aDatabase, listCard, listRecord->memLocalID.GetAt(j)); + if (NS_SUCCEEDED(rv)) + { + rv = DeleteCardByServerID(listRecord->memServerID.GetAt(j)); + listRecord->memServerID.SetAt(j, 0); + } + } + + nsString emailString; + // All we have to do here is create new cards for email addresses. + for (j=0; j < memberAddresses.Count(); j++) + { + serverID = memberServerIDs.GetAt(j); + rv = nsComponentManager::CreateInstance(NS_ABCARDPROPERTY_CONTRACTID, nsnull, NS_GET_IID(nsIAbCard), + getter_AddRefs(newCard)); + if (NS_FAILED(rv) || !newCard) + continue; + + // Set email address. Save it for member CRC as well. + nsString *email = (nsString *) memberAddresses.ElementAt(j); + newCard->SetPrimaryEmail((*email).get()); + + if (!emailString.IsEmpty()) + emailString.Append(NS_LITERAL_STRING(",")); + emailString.Append(*email); + + // Set default email to other email #1. + nsString defaulEmail; + defaulEmail.AssignWithConversion(AB_DEFAULT_EMAIL_IS_EMAIL_1); + newCard->SetDefaultEmail(defaulEmail.get()); + + // Set card type to AOL additional email address. + nsString cardType; + cardType.AssignWithConversion(AB_CARD_IS_AOL_ADDITIONAL_EMAIL); + newCard->SetCardType(cardType.get()); + + + // hack + // there is no way (currently) to set generic attributes on simple abcardproperties + // so we have to use a mdbcard instead. We can remove this once #128567 is fixed. + nsCOMPtr dbcard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv); + if (NS_FAILED(rv)) + continue; + + nsCOMPtr card = do_QueryInterface(dbcard, &rv); + if (NS_FAILED(rv)) + continue; + + rv = card->Copy(newCard); + if (NS_FAILED(rv)) + continue; + + dbcard->SetAbDatabase(aDatabase); + PRUint32 tID; + // card has to be a mdbcard, or tID will come back as 0 + rv = aDatabase->CreateNewCardAndAddToDBWithKey(card, PR_TRUE, &tID); + localID = (PRInt32) tID; + newCard = card; + + // Now, calculate the NEW CRC for the new card. + if (NS_FAILED(GenerateProtocolForCard(newCard, PR_FALSE, tempProtocolLine))) + continue; + + // Get the CRC for this temp entry line... + char *tLine = ToNewCString(tempProtocolLine); + if (!tLine) + continue; + + // Create a new sync mapping record for this card. + syncMappingRecord *newSyncRecord = (syncMappingRecord *)PR_Malloc(sizeof(syncMappingRecord)); + if (newSyncRecord) + { + memset(newSyncRecord, 0, sizeof(syncMappingRecord)); + newSyncRecord->CRC = GetCRC(tLine); + newSyncRecord->serverID = serverID; + newSyncRecord->localID = localID; + newSyncRecord->flags |= SYNC_IS_AOL_ADDITIONAL_EMAIL; + mNewServerTable->AppendElement((void *)newSyncRecord); + } + + // Now, add the member to the list. + rv = AddMemberToList(listCard, newCard); + NS_ENSURE_SUCCESS(rv, rv); + rv = aDatabase->Commit(nsAddrDBCommitType::kLargeCommit); + + // Seems like verything is fine so add the member to the list sync table. + if (NS_SUCCEEDED(rv)) + { + listRecord->memLocalID.Add(localID); + listRecord->memServerID.Add(serverID); + listRecord->memFlags.Add(SYNC_PROCESSED); + } + nsCRT::free(tLine); + newCard = nsnull; + } + + // Now store the new CRC. + if (emailString.IsEmpty()) + listRecord->CRC = 0; + else + { + char *tVal = ToNewCString(emailString); + listRecord->CRC = GetCRC(tVal); + nsCRT::free(tVal); + } + + // Free email address array + nsString *email; + for (j=0; j < memberAddresses.Count(); j++) + { + email = (nsString *) memberAddresses.ElementAt(j); + delete email; + } + } + +EarlyExit: + // Database is open...make sure to close it + if (aDatabase) + aDatabase->Close(PR_TRUE); + NS_IF_RELEASE(aDatabase); + + return rv; +} + +// Input id string is separated by a single space like: +// 112 113 114 +nsresult nsAbSync::ParseEmailMemberIds(nsString *idString, nsUInt32Array &memberServerIDs) +{ + if (idString->IsEmpty()) + return NS_OK; + + // Strip off while space (especailly leading & trailing ones) + idString->Trim("\b\t\r\n "); + + char *str = ToNewCString(*idString); + if (str) + { + char *pChar, *PCurPos, *end; + + pChar = PCurPos = str; + end = str + idString->Length(); + while (pChar < end) + { + // Find separated ids. + pChar = PL_strchr(PCurPos, ' '); + if (pChar) + *pChar = 0; + memberServerIDs.Add(atoi(PCurPos)); + if (! pChar) + break; // we're done + pChar++; + PCurPos = pChar; + } + PR_FREEIF(str); + } + return NS_OK; +} + +// Input email address string is separated by a single comma like: +// a1@abc.com,a2@abc.com,a3@abc.com +nsresult nsAbSync::ParseEmailMemberAddresses(nsString *addrString, nsVoidArray &memberAddresses) +{ + if (addrString->IsEmpty()) + return NS_OK; + + // Strip off while space (especailly leading & trailing ones) + addrString->Trim("\b\t\r\n "); + + nsString outValue; + char *tValue = ToNewCString(*addrString); + if (tValue) + { + nsresult rv = nsMsgI18NConvertToUnicode(nsCAutoString("UTF-8"), nsCAutoString(tValue), outValue); + NS_ENSURE_SUCCESS(rv,rv); + + addrString->Assign(outValue); + + nsString *email; + PRInt32 atPos; + while ((atPos = addrString->FindChar(',')) != -1) + { + email = new nsString(); + addrString->Left(*email, atPos); + memberAddresses.AppendElement((void *)email); + addrString->Cut(0, atPos+1); + } + + if (!addrString->IsEmpty()) + { + email = new nsString(); + *email = *addrString; + memberAddresses.AppendElement((void *)email); + } + + PR_FREEIF(tValue); + } + return NS_OK; +} + + + diff --git a/mozilla/mailnews/absync/src/nsSyncDecoderRing.h b/mozilla/mailnews/absync/src/nsSyncDecoderRing.h index f39bf457866..56e416011c7 100644 --- a/mozilla/mailnews/absync/src/nsSyncDecoderRing.h +++ b/mozilla/mailnews/absync/src/nsSyncDecoderRing.h @@ -54,6 +54,7 @@ #define kServerNicknameColumn NS_LITERAL_STRING("nick_name") #define kServerPriEmailColumn NS_LITERAL_STRING("email1") #define kServer2ndEmailColumn NS_LITERAL_STRING("email2") +#define kServerDefaultEmailColumn NS_LITERAL_STRING("default_email") #define kServerPlainTextColumn NS_LITERAL_STRING("Pref_rich_email") #define kServerWorkPhoneColumn NS_LITERAL_STRING("work_phone") #define kServerHomePhoneColumn NS_LITERAL_STRING("home_phone") @@ -75,11 +76,20 @@ #define kServerJobTitleColumn NS_LITERAL_STRING("job_title") #define kServerDepartmentColumn NS_LITERAL_STRING("department") #define kServerCompanyColumn NS_LITERAL_STRING("company") +#define kServerBirthdayColumn NS_LITERAL_STRING("birthday") +#define kServerAnniversaryColumn NS_LITERAL_STRING("anniversary") +#define kServerAnniversaryYearColumn NS_LITERAL_STRING("AnniversaryYear") +#define kServerAnniversaryMonthColumn NS_LITERAL_STRING("AnniversaryMonth") +#define kServerAnniversaryDayColumn NS_LITERAL_STRING("AnniversaryDay") +#define kServerSpouseNameColumn NS_LITERAL_STRING("spouse_name") +#define kServerFamilyNameColumn NS_LITERAL_STRING("family_name") +#define kServerDefaultAddressColumn NS_LITERAL_STRING("default_address") +#define kServerCategoryColumn NS_LITERAL_STRING("Category") #define kServerWebPage1Column NS_LITERAL_STRING("Work_web_page") #define kServerWebPage2Column NS_LITERAL_STRING("web_page") -#define kServerBirthYearColumn NS_LITERAL_STRING("OMIT:BirthYear") -#define kServerBirthMonthColumn NS_LITERAL_STRING("OMIT:BirthMonth") -#define kServerBirthDayColumn NS_LITERAL_STRING("OMIT:BirthDay") +#define kServerBirthYearColumn NS_LITERAL_STRING("BirthYear") +#define kServerBirthMonthColumn NS_LITERAL_STRING("BirthMonth") +#define kServerBirthDayColumn NS_LITERAL_STRING("BirthDay") #define kServerCustom1Column NS_LITERAL_STRING("Custom_1") #define kServerCustom2Column NS_LITERAL_STRING("Custom_2") #define kServerCustom3Column NS_LITERAL_STRING("Custom_3")