1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 // this file implements the nsAddrDatabase interface using the MDB Interface.
7 
8 #include "nsAddrDatabase.h"
9 #include "nsAbBaseCID.h"
10 #include "nsMsgUtils.h"
11 #include "nsMorkCID.h"
12 #include "nsIMdbFactoryFactory.h"
13 #include "nsSimpleEnumerator.h"
14 
15 #define kAddressCharSetColumn "AddrCharSet"
16 #define kMailListName "ListName"
17 #define kMailListNickName "ListNickName"
18 #define kMailListDescription "ListDescription"
19 #define kMailListTotalAddresses "ListTotalAddresses"
20 // not shown in the UI
21 #define kLowerPriEmailColumn "LowercasePrimaryEmail"
22 #define kLower2ndEmailColumn "LowercaseSecondEmail"
23 
24 #define ID_PAB_TABLE 1
25 
26 static const char kPabTableKind[] = "ns:addrbk:db:table:kind:pab";
27 static const char kDeletedCardsTableKind[] =
28     "ns:addrbk:db:table:kind:deleted";  // this table is used to keep the
29                                         // deleted cards
30 
31 static const char kCardRowScope[] = "ns:addrbk:db:row:scope:card:all";
32 static const char kListRowScope[] = "ns:addrbk:db:row:scope:list:all";
33 static const char kDataRowScope[] = "ns:addrbk:db:row:scope:data:all";
34 
35 #define COLUMN_STR_MAX 16
36 
37 static const char kRecordKeyColumn[] = "RecordKey";
38 static const char kLastRecordKeyColumn[] = "LastRecordKey";
39 static const char kRowIDProperty[] = "DbRowID";
40 
41 static const char kLowerListNameColumn[] = "LowercaseListName";
42 
43 struct mdbOid gAddressBookTableOID;
44 
45 static const char kMailListAddressFormat[] = "Address%d";
46 
nsAddrDatabase()47 nsAddrDatabase::nsAddrDatabase()
48     : m_mdbEnv(nullptr),
49       m_mdbStore(nullptr),
50       m_mdbPabTable(nullptr),
51       m_mdbTokensInitialized(false),
52       m_PabTableKind(0),
53       m_DeletedCardsTableKind(0),
54       m_CardRowScopeToken(0),
55       m_UIDColumnToken(0),
56       m_FirstNameColumnToken(0),
57       m_LastNameColumnToken(0),
58       m_PhoneticFirstNameColumnToken(0),
59       m_PhoneticLastNameColumnToken(0),
60       m_DisplayNameColumnToken(0),
61       m_NickNameColumnToken(0),
62       m_PriEmailColumnToken(0),
63       m_2ndEmailColumnToken(0),
64       m_WorkPhoneColumnToken(0),
65       m_HomePhoneColumnToken(0),
66       m_FaxColumnToken(0),
67       m_PagerColumnToken(0),
68       m_CellularColumnToken(0),
69       m_WorkPhoneTypeColumnToken(0),
70       m_HomePhoneTypeColumnToken(0),
71       m_FaxTypeColumnToken(0),
72       m_PagerTypeColumnToken(0),
73       m_CellularTypeColumnToken(0),
74       m_HomeAddressColumnToken(0),
75       m_HomeAddress2ColumnToken(0),
76       m_HomeCityColumnToken(0),
77       m_HomeStateColumnToken(0),
78       m_HomeZipCodeColumnToken(0),
79       m_HomeCountryColumnToken(0),
80       m_WorkAddressColumnToken(0),
81       m_WorkAddress2ColumnToken(0),
82       m_WorkCityColumnToken(0),
83       m_WorkStateColumnToken(0),
84       m_WorkZipCodeColumnToken(0),
85       m_WorkCountryColumnToken(0),
86       m_CompanyColumnToken(0),
87       m_AimScreenNameColumnToken(0),
88       m_AnniversaryYearColumnToken(0),
89       m_AnniversaryMonthColumnToken(0),
90       m_AnniversaryDayColumnToken(0),
91       m_SpouseNameColumnToken(0),
92       m_FamilyNameColumnToken(0),
93       m_DefaultAddressColumnToken(0),
94       m_CategoryColumnToken(0),
95       m_WebPage1ColumnToken(0),
96       m_WebPage2ColumnToken(0),
97       m_BirthYearColumnToken(0),
98       m_BirthMonthColumnToken(0),
99       m_BirthDayColumnToken(0),
100       m_Custom1ColumnToken(0),
101       m_Custom2ColumnToken(0),
102       m_Custom3ColumnToken(0),
103       m_Custom4ColumnToken(0),
104       m_NotesColumnToken(0),
105       m_LastModDateColumnToken(0),
106       m_MailFormatColumnToken(0),
107       m_PopularityIndexColumnToken(0),
108       m_AddressCharSetColumnToken(0) {}
109 
~nsAddrDatabase()110 nsAddrDatabase::~nsAddrDatabase() {
111   Close(false);  // better have already been closed.
112 
113   // RemoveFromCache(this);
114   // clean up after ourself!
115   if (m_mdbPabTable) m_mdbPabTable->Release();
116   NS_IF_RELEASE(m_mdbStore);
117   NS_IF_RELEASE(m_mdbEnv);
118 }
119 
GetMDBFactory(nsIMdbFactory ** aMdbFactory)120 nsresult nsAddrDatabase::GetMDBFactory(nsIMdbFactory** aMdbFactory) {
121   if (!mMdbFactory) {
122     nsresult rv;
123     nsCOMPtr<nsIMdbFactoryService> mdbFactoryService =
124         do_GetService(NS_MORK_CONTRACTID, &rv);
125     if (NS_SUCCEEDED(rv) && mdbFactoryService) {
126       rv = mdbFactoryService->GetMdbFactory(getter_AddRefs(mMdbFactory));
127       NS_ENSURE_SUCCESS(rv, rv);
128       if (!mMdbFactory) return NS_ERROR_FAILURE;
129     }
130   }
131   NS_ADDREF(*aMdbFactory = mMdbFactory);
132   return NS_OK;
133 }
134 
SetDbPath(nsIFile * aDbPath)135 nsresult nsAddrDatabase::SetDbPath(nsIFile* aDbPath) {
136   return aDbPath->Clone(getter_AddRefs(m_dbName));
137 }
138 
139 // Open the MDB database synchronously. If successful, this routine
140 // will set up the m_mdbStore and m_mdbEnv of the database object
141 // so other database calls can work.
OpenMDB(nsIFile * dbName,bool create)142 nsresult nsAddrDatabase::OpenMDB(nsIFile* dbName, bool create) {
143   nsCOMPtr<nsIMdbFactory> mdbFactory;
144   nsresult ret = GetMDBFactory(getter_AddRefs(mdbFactory));
145   NS_ENSURE_SUCCESS(ret, ret);
146 
147   ret = mdbFactory->MakeEnv(NULL, &m_mdbEnv);
148   if (NS_SUCCEEDED(ret)) {
149     nsIMdbThumb* thumb = nullptr;
150 
151     PathString filePath = dbName->NativePath();
152 
153     nsIMdbHeap* dbHeap = nullptr;
154 
155     if (m_mdbEnv) m_mdbEnv->SetAutoClear(true);
156 
157     bool dbNameExists = false;
158     ret = dbName->Exists(&dbNameExists);
159     NS_ENSURE_SUCCESS(ret, ret);
160 
161     if (!dbNameExists)
162       ret = NS_ERROR_FILE_NOT_FOUND;
163     else {
164       mdbOpenPolicy inOpenPolicy;
165       mdb_bool canOpen;
166       mdbYarn outFormatVersion;
167       nsIMdbFile* oldFile = nullptr;
168       int64_t fileSize;
169       ret = dbName->GetFileSize(&fileSize);
170       NS_ENSURE_SUCCESS(ret, ret);
171 
172       ret = mdbFactory->OpenOldFile(
173           m_mdbEnv, dbHeap, filePath.get(),
174           mdbBool_kFalse,  // not readonly, we want modifiable
175           &oldFile);
176       if (oldFile) {
177         if (NS_SUCCEEDED(ret)) {
178           ret = mdbFactory->CanOpenFilePort(m_mdbEnv,
179                                             oldFile,  // the file to investigate
180                                             &canOpen, &outFormatVersion);
181           if (NS_SUCCEEDED(ret) && canOpen) {
182             inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0;
183             inOpenPolicy.mOpenPolicy_MinMemory = 0;
184             inOpenPolicy.mOpenPolicy_MaxLazy = 0;
185 
186             ret = mdbFactory->OpenFileStore(m_mdbEnv, dbHeap, oldFile,
187                                             &inOpenPolicy, &thumb);
188           } else if (fileSize != 0)
189             ret = NS_ERROR_FILE_ACCESS_DENIED;
190         }
191         NS_RELEASE(oldFile);  // always release our file ref, store has own
192       }
193       if (NS_FAILED(ret)) ret = NS_ERROR_FILE_ACCESS_DENIED;
194     }
195 
196     if (NS_SUCCEEDED(ret) && thumb) {
197       mdb_count outTotal;        // total somethings to do in operation
198       mdb_count outCurrent;      // subportion of total completed so far
199       mdb_bool outDone = false;  // is operation finished?
200       mdb_bool outBroken;        // is operation irreparably dead and broken?
201       do {
202         ret = thumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone,
203                             &outBroken);
204         if (NS_FAILED(ret)) {
205           outDone = true;
206           break;
207         }
208       } while (NS_SUCCEEDED(ret) && !outBroken && !outDone);
209       if (NS_SUCCEEDED(ret) && outDone) {
210         ret = mdbFactory->ThumbToOpenStore(m_mdbEnv, thumb, &m_mdbStore);
211         if (NS_SUCCEEDED(ret) && m_mdbStore) {
212           ret = InitExistingDB();
213           create = false;
214         }
215       }
216     } else if (create && ret != NS_ERROR_FILE_ACCESS_DENIED) {
217       ret = NS_ERROR_NOT_IMPLEMENTED;
218     }
219     NS_IF_RELEASE(thumb);
220   }
221   return ret;
222 }
223 
CloseMDB(bool commit)224 nsresult nsAddrDatabase::CloseMDB(bool commit) {
225   if (commit) return NS_ERROR_NOT_IMPLEMENTED;
226   //???    RemoveFromCache(this);  // if we've closed it, better not leave it in
227   // the cache.
228   return NS_OK;
229 }
230 
231 // force the database to close - this'll flush out anybody holding onto
232 // a database without having a listener!
233 // This is evil in the com world, but there are times we need to delete the
234 // file.
ForceClosed()235 nsresult nsAddrDatabase::ForceClosed() {
236   nsresult err = NS_OK;
237 
238   // make sure someone has a reference so object won't get deleted out from
239   // under us.
240   // NS_ADDREF_THIS();
241   // OK, remove from cache first and close the store.
242   // RemoveFromCache(this);
243 
244   err = CloseMDB(false);  // since we're about to delete it, no need to commit.
245   NS_IF_RELEASE(m_mdbStore);
246   // NS_RELEASE_THIS();
247   return err;
248 }
249 
Close(bool forceCommit)250 nsresult nsAddrDatabase::Close(bool forceCommit /* = TRUE */) {
251   return CloseMDB(forceCommit);
252 }
253 
InitExistingDB()254 nsresult nsAddrDatabase::InitExistingDB() {
255   nsresult err = InitMDBInfo();
256   if (NS_SUCCEEDED(err)) {
257     if (!m_mdbStore || !m_mdbEnv) return NS_ERROR_NULL_POINTER;
258 
259     err = m_mdbStore->GetTable(m_mdbEnv, &gAddressBookTableOID, &m_mdbPabTable);
260     if (NS_SUCCEEDED(err) && m_mdbPabTable) {
261       // This code has always run here. Removing it fails an assertion in the
262       // Mork code which indicates a bad state. In the interest of saving
263       // effort, and since this whole file is doomed after the next release,
264       // I'm leaving it behind.
265       nsIMdbTableRowCursor* rowCursor = nullptr;
266       nsIMdbRow* findRow = nullptr;
267       mdb_pos rowPos = 0;
268 
269       err = m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, &rowCursor);
270       if (NS_SUCCEEDED(err) && rowCursor) {
271         do {
272           err = rowCursor->NextRow(m_mdbEnv, &findRow, &rowPos);
273         } while (NS_SUCCEEDED(err) && findRow);
274         rowCursor->Release();
275       }
276     }
277   }
278   return err;
279 }
280 
281 // initialize the various tokens and tables in our db's env
InitMDBInfo()282 nsresult nsAddrDatabase::InitMDBInfo() {
283   nsresult err = NS_OK;
284 
285   if (!m_mdbTokensInitialized && m_mdbStore && m_mdbEnv) {
286     m_mdbTokensInitialized = true;
287     err = m_mdbStore->StringToToken(m_mdbEnv, kCardRowScope,
288                                     &m_CardRowScopeToken);
289     err = m_mdbStore->StringToToken(m_mdbEnv, kListRowScope,
290                                     &m_ListRowScopeToken);
291     err = m_mdbStore->StringToToken(m_mdbEnv, kDataRowScope,
292                                     &m_DataRowScopeToken);
293     gAddressBookTableOID.mOid_Scope = m_CardRowScopeToken;
294     gAddressBookTableOID.mOid_Id = ID_PAB_TABLE;
295     if (NS_SUCCEEDED(err)) {
296       m_mdbStore->StringToToken(m_mdbEnv, kUIDProperty, &m_UIDColumnToken);
297       m_mdbStore->StringToToken(m_mdbEnv, kFirstNameProperty,
298                                 &m_FirstNameColumnToken);
299       m_mdbStore->StringToToken(m_mdbEnv, kLastNameProperty,
300                                 &m_LastNameColumnToken);
301       m_mdbStore->StringToToken(m_mdbEnv, kPhoneticFirstNameProperty,
302                                 &m_PhoneticFirstNameColumnToken);
303       m_mdbStore->StringToToken(m_mdbEnv, kPhoneticLastNameProperty,
304                                 &m_PhoneticLastNameColumnToken);
305       m_mdbStore->StringToToken(m_mdbEnv, kDisplayNameProperty,
306                                 &m_DisplayNameColumnToken);
307       m_mdbStore->StringToToken(m_mdbEnv, kNicknameProperty,
308                                 &m_NickNameColumnToken);
309       m_mdbStore->StringToToken(m_mdbEnv, kPriEmailProperty,
310                                 &m_PriEmailColumnToken);
311       m_mdbStore->StringToToken(m_mdbEnv, kLowerPriEmailColumn,
312                                 &m_LowerPriEmailColumnToken);
313       m_mdbStore->StringToToken(m_mdbEnv, k2ndEmailProperty,
314                                 &m_2ndEmailColumnToken);
315       m_mdbStore->StringToToken(m_mdbEnv, kLower2ndEmailColumn,
316                                 &m_Lower2ndEmailColumnToken);
317       m_mdbStore->StringToToken(m_mdbEnv, kPreferMailFormatProperty,
318                                 &m_MailFormatColumnToken);
319       m_mdbStore->StringToToken(m_mdbEnv, kPopularityIndexProperty,
320                                 &m_PopularityIndexColumnToken);
321       m_mdbStore->StringToToken(m_mdbEnv, kWorkPhoneProperty,
322                                 &m_WorkPhoneColumnToken);
323       m_mdbStore->StringToToken(m_mdbEnv, kHomePhoneProperty,
324                                 &m_HomePhoneColumnToken);
325       m_mdbStore->StringToToken(m_mdbEnv, kFaxProperty, &m_FaxColumnToken);
326       m_mdbStore->StringToToken(m_mdbEnv, kPagerProperty, &m_PagerColumnToken);
327       m_mdbStore->StringToToken(m_mdbEnv, kCellularProperty,
328                                 &m_CellularColumnToken);
329       m_mdbStore->StringToToken(m_mdbEnv, kWorkPhoneTypeProperty,
330                                 &m_WorkPhoneTypeColumnToken);
331       m_mdbStore->StringToToken(m_mdbEnv, kHomePhoneTypeProperty,
332                                 &m_HomePhoneTypeColumnToken);
333       m_mdbStore->StringToToken(m_mdbEnv, kFaxTypeProperty,
334                                 &m_FaxTypeColumnToken);
335       m_mdbStore->StringToToken(m_mdbEnv, kPagerTypeProperty,
336                                 &m_PagerTypeColumnToken);
337       m_mdbStore->StringToToken(m_mdbEnv, kCellularTypeProperty,
338                                 &m_CellularTypeColumnToken);
339       m_mdbStore->StringToToken(m_mdbEnv, kHomeAddressProperty,
340                                 &m_HomeAddressColumnToken);
341       m_mdbStore->StringToToken(m_mdbEnv, kHomeAddress2Property,
342                                 &m_HomeAddress2ColumnToken);
343       m_mdbStore->StringToToken(m_mdbEnv, kHomeCityProperty,
344                                 &m_HomeCityColumnToken);
345       m_mdbStore->StringToToken(m_mdbEnv, kHomeStateProperty,
346                                 &m_HomeStateColumnToken);
347       m_mdbStore->StringToToken(m_mdbEnv, kHomeZipCodeProperty,
348                                 &m_HomeZipCodeColumnToken);
349       m_mdbStore->StringToToken(m_mdbEnv, kHomeCountryProperty,
350                                 &m_HomeCountryColumnToken);
351       m_mdbStore->StringToToken(m_mdbEnv, kWorkAddressProperty,
352                                 &m_WorkAddressColumnToken);
353       m_mdbStore->StringToToken(m_mdbEnv, kWorkAddress2Property,
354                                 &m_WorkAddress2ColumnToken);
355       m_mdbStore->StringToToken(m_mdbEnv, kWorkCityProperty,
356                                 &m_WorkCityColumnToken);
357       m_mdbStore->StringToToken(m_mdbEnv, kWorkStateProperty,
358                                 &m_WorkStateColumnToken);
359       m_mdbStore->StringToToken(m_mdbEnv, kWorkZipCodeProperty,
360                                 &m_WorkZipCodeColumnToken);
361       m_mdbStore->StringToToken(m_mdbEnv, kWorkCountryProperty,
362                                 &m_WorkCountryColumnToken);
363       m_mdbStore->StringToToken(m_mdbEnv, kJobTitleProperty,
364                                 &m_JobTitleColumnToken);
365       m_mdbStore->StringToToken(m_mdbEnv, kDepartmentProperty,
366                                 &m_DepartmentColumnToken);
367       m_mdbStore->StringToToken(m_mdbEnv, kCompanyProperty,
368                                 &m_CompanyColumnToken);
369       m_mdbStore->StringToToken(m_mdbEnv, kScreenNameProperty,
370                                 &m_AimScreenNameColumnToken);
371       m_mdbStore->StringToToken(m_mdbEnv, kAnniversaryYearProperty,
372                                 &m_AnniversaryYearColumnToken);
373       m_mdbStore->StringToToken(m_mdbEnv, kAnniversaryMonthProperty,
374                                 &m_AnniversaryMonthColumnToken);
375       m_mdbStore->StringToToken(m_mdbEnv, kAnniversaryDayProperty,
376                                 &m_AnniversaryDayColumnToken);
377       m_mdbStore->StringToToken(m_mdbEnv, kSpouseNameProperty,
378                                 &m_SpouseNameColumnToken);
379       m_mdbStore->StringToToken(m_mdbEnv, kFamilyNameProperty,
380                                 &m_FamilyNameColumnToken);
381       m_mdbStore->StringToToken(m_mdbEnv, kWorkWebPageProperty,
382                                 &m_WebPage1ColumnToken);
383       m_mdbStore->StringToToken(m_mdbEnv, kHomeWebPageProperty,
384                                 &m_WebPage2ColumnToken);
385       m_mdbStore->StringToToken(m_mdbEnv, kBirthYearProperty,
386                                 &m_BirthYearColumnToken);
387       m_mdbStore->StringToToken(m_mdbEnv, kBirthMonthProperty,
388                                 &m_BirthMonthColumnToken);
389       m_mdbStore->StringToToken(m_mdbEnv, kBirthDayProperty,
390                                 &m_BirthDayColumnToken);
391       m_mdbStore->StringToToken(m_mdbEnv, kCustom1Property,
392                                 &m_Custom1ColumnToken);
393       m_mdbStore->StringToToken(m_mdbEnv, kCustom2Property,
394                                 &m_Custom2ColumnToken);
395       m_mdbStore->StringToToken(m_mdbEnv, kCustom3Property,
396                                 &m_Custom3ColumnToken);
397       m_mdbStore->StringToToken(m_mdbEnv, kCustom4Property,
398                                 &m_Custom4ColumnToken);
399       m_mdbStore->StringToToken(m_mdbEnv, kNotesProperty, &m_NotesColumnToken);
400       m_mdbStore->StringToToken(m_mdbEnv, kLastModifiedDateProperty,
401                                 &m_LastModDateColumnToken);
402       m_mdbStore->StringToToken(m_mdbEnv, kRecordKeyColumn,
403                                 &m_RecordKeyColumnToken);
404       m_mdbStore->StringToToken(m_mdbEnv, kAddressCharSetColumn,
405                                 &m_AddressCharSetColumnToken);
406       m_mdbStore->StringToToken(m_mdbEnv, kLastRecordKeyColumn,
407                                 &m_LastRecordKeyColumnToken);
408 
409       err = m_mdbStore->StringToToken(m_mdbEnv, kPabTableKind, &m_PabTableKind);
410 
411       m_mdbStore->StringToToken(m_mdbEnv, kMailListName,
412                                 &m_ListNameColumnToken);
413       m_mdbStore->StringToToken(m_mdbEnv, kMailListNickName,
414                                 &m_ListNickNameColumnToken);
415       m_mdbStore->StringToToken(m_mdbEnv, kMailListDescription,
416                                 &m_ListDescriptionColumnToken);
417       m_mdbStore->StringToToken(m_mdbEnv, kMailListTotalAddresses,
418                                 &m_ListTotalColumnToken);
419       m_mdbStore->StringToToken(m_mdbEnv, kLowerListNameColumn,
420                                 &m_LowerListNameColumnToken);
421       m_mdbStore->StringToToken(m_mdbEnv, kDeletedCardsTableKind,
422                                 &m_DeletedCardsTableKind);
423     }
424   }
425   return err;
426 }
427 
428 ////////////////////////////////////////////////////////////////////////////////
429 
GetListAddressTotal(nsIMdbRow * listRow)430 uint32_t nsAddrDatabase::GetListAddressTotal(nsIMdbRow* listRow) {
431   uint32_t count = 0;
432   GetIntColumn(listRow, m_ListTotalColumnToken, &count, 0);
433   return count;
434 }
435 
GetAddressRowByPos(nsIMdbRow * listRow,uint16_t pos,nsIMdbRow ** cardRow)436 nsresult nsAddrDatabase::GetAddressRowByPos(nsIMdbRow* listRow, uint16_t pos,
437                                             nsIMdbRow** cardRow) {
438   if (!m_mdbStore || !listRow || !cardRow || !m_mdbEnv)
439     return NS_ERROR_NULL_POINTER;
440 
441   mdb_token listAddressColumnToken;
442 
443   char columnStr[COLUMN_STR_MAX];
444   PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, pos);
445   m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
446 
447   nsAutoString tempString;
448   mdb_id rowID;
449   nsresult err =
450       GetIntColumn(listRow, listAddressColumnToken, (uint32_t*)&rowID, 0);
451   NS_ENSURE_SUCCESS(err, err);
452 
453   return GetCardRowByRowID(rowID, cardRow);
454 }
455 
GetStringColumn(nsIMdbRow * cardRow,mdb_token outToken,nsString & str)456 nsresult nsAddrDatabase::GetStringColumn(nsIMdbRow* cardRow, mdb_token outToken,
457                                          nsString& str) {
458   nsresult err = NS_ERROR_NULL_POINTER;
459   nsIMdbCell* cardCell;
460 
461   if (cardRow && m_mdbEnv) {
462     err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
463     if (NS_SUCCEEDED(err) && cardCell) {
464       struct mdbYarn yarn;
465       cardCell->AliasYarn(m_mdbEnv, &yarn);
466       NS_ConvertUTF8toUTF16 uniStr((const char*)yarn.mYarn_Buf,
467                                    yarn.mYarn_Fill);
468       if (!uniStr.IsEmpty())
469         str.Assign(uniStr);
470       else
471         err = NS_ERROR_FAILURE;
472       cardCell->Release();  // always release ref
473     } else
474       err = NS_ERROR_FAILURE;
475   }
476   return err;
477 }
478 
YarnToUInt32(struct mdbYarn * yarn,uint32_t * pResult)479 void nsAddrDatabase::YarnToUInt32(struct mdbYarn* yarn, uint32_t* pResult) {
480   uint8_t numChars = std::min<mdb_fill>(8, yarn->mYarn_Fill);
481   *pResult = MsgUnhex((char*)yarn->mYarn_Buf, numChars);
482 }
483 
GetIntColumn(nsIMdbRow * cardRow,mdb_token outToken,uint32_t * pValue,uint32_t defaultValue)484 nsresult nsAddrDatabase::GetIntColumn(nsIMdbRow* cardRow, mdb_token outToken,
485                                       uint32_t* pValue, uint32_t defaultValue) {
486   nsresult err = NS_ERROR_NULL_POINTER;
487   nsIMdbCell* cardCell;
488 
489   if (pValue) *pValue = defaultValue;
490   if (cardRow && m_mdbEnv) {
491     err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
492     if (NS_SUCCEEDED(err) && cardCell) {
493       struct mdbYarn yarn;
494       cardCell->AliasYarn(m_mdbEnv, &yarn);
495       YarnToUInt32(&yarn, pValue);
496       cardCell->Release();
497     } else
498       err = NS_ERROR_FAILURE;
499   }
500   return err;
501 }
502 
InitCardFromRow(nsIAbCard * newCard,nsIMdbRow * cardRow)503 nsresult nsAddrDatabase::InitCardFromRow(nsIAbCard* newCard,
504                                          nsIMdbRow* cardRow) {
505   nsresult rv = NS_OK;
506   if (!newCard || !cardRow || !m_mdbEnv) return NS_ERROR_NULL_POINTER;
507 
508   nsCOMPtr<nsIMdbRowCellCursor> cursor;
509   nsCOMPtr<nsIMdbCell> cell;
510 
511   rv = cardRow->GetRowCellCursor(m_mdbEnv, -1, getter_AddRefs(cursor));
512   NS_ENSURE_SUCCESS(rv, rv);
513 
514   mdb_column columnNumber;
515   char columnName[100];
516   struct mdbYarn colYarn = {columnName, 0, sizeof(columnName), 0, 0, nullptr};
517   struct mdbYarn cellYarn;
518 
519   do {
520     rv = cursor->NextCell(m_mdbEnv, getter_AddRefs(cell), &columnNumber,
521                           nullptr);
522     NS_ENSURE_SUCCESS(rv, rv);
523 
524     if (!cell) break;
525 
526     // Get the value of the cell
527     cell->AliasYarn(m_mdbEnv, &cellYarn);
528     NS_ConvertUTF8toUTF16 value(static_cast<const char*>(cellYarn.mYarn_Buf),
529                                 cellYarn.mYarn_Fill);
530 
531     if (!value.IsEmpty()) {
532       // Get the column of the cell
533       // Mork makes this so hard...
534       rv = m_mdbStore->TokenToString(m_mdbEnv, columnNumber, &colYarn);
535       NS_ENSURE_SUCCESS(rv, rv);
536 
537       char* name =
538           PL_strndup(static_cast<char*>(colYarn.mYarn_Buf), colYarn.mYarn_Fill);
539       newCard->SetPropertyAsAString(name, value);
540       PL_strfree(name);
541     }
542   } while (true);
543 
544   uint32_t key = 0;
545   rv = GetIntColumn(cardRow, m_RecordKeyColumnToken, &key, 0);
546   if (NS_SUCCEEDED(rv)) newCard->SetPropertyAsUint32(kRecordKeyColumn, key);
547 
548   return NS_OK;
549 }
550 
GetListCardFromDB(nsIAbCard * listCard,nsIMdbRow * listRow)551 nsresult nsAddrDatabase::GetListCardFromDB(nsIAbCard* listCard,
552                                            nsIMdbRow* listRow) {
553   nsresult err = NS_OK;
554   if (!listCard || !listRow) return NS_ERROR_NULL_POINTER;
555 
556   nsAutoString tempString;
557 
558   err = GetStringColumn(listRow, m_UIDColumnToken, tempString);
559   if (NS_SUCCEEDED(err) && !tempString.IsEmpty()) {
560     listCard->SetPropertyAsAString(kUIDProperty, tempString);
561   }
562   err = GetStringColumn(listRow, m_ListNameColumnToken, tempString);
563   if (NS_SUCCEEDED(err) && !tempString.IsEmpty()) {
564     listCard->SetDisplayName(tempString);
565     listCard->SetLastName(tempString);
566   }
567   err = GetStringColumn(listRow, m_ListNickNameColumnToken, tempString);
568   if (NS_SUCCEEDED(err) && !tempString.IsEmpty()) {
569     listCard->SetPropertyAsAString(kNicknameProperty, tempString);
570   }
571   err = GetStringColumn(listRow, m_ListDescriptionColumnToken, tempString);
572   if (NS_SUCCEEDED(err) && !tempString.IsEmpty()) {
573     listCard->SetPropertyAsAString(kNotesProperty, tempString);
574   }
575   uint32_t key = 0;
576   err = GetIntColumn(listRow, m_RecordKeyColumnToken, &key, 0);
577   if (NS_SUCCEEDED(err)) listCard->SetPropertyAsUint32(kRecordKeyColumn, key);
578   return err;
579 }
580 
581 class nsAddrDBEnumerator : public nsSimpleEnumerator {
582  public:
DefaultInterface()583   const nsID& DefaultInterface() override { return NS_GET_IID(nsIAbCard); }
584 
585   // nsISimpleEnumerator methods:
586   NS_DECL_NSISIMPLEENUMERATOR
587 
588   // nsAddrDBEnumerator methods:
589   explicit nsAddrDBEnumerator(nsAddrDatabase* aDb);
590   void Clear();
591 
592  protected:
593   nsAddrDatabase* mDb;
594   nsIMdbTable* mDbTable;
595   nsCOMPtr<nsIMdbTableRowCursor> mRowCursor;
596   nsCOMPtr<nsIMdbRow> mCurrentRow;
597   mdb_pos mRowPos;
598 };
599 
nsAddrDBEnumerator(nsAddrDatabase * aDb)600 nsAddrDBEnumerator::nsAddrDBEnumerator(nsAddrDatabase* aDb)
601     : mDb(aDb), mDbTable(aDb->GetPabTable()), mRowPos(-1) {}
602 
603 NS_IMETHODIMP
HasMoreElements(bool * aResult)604 nsAddrDBEnumerator::HasMoreElements(bool* aResult) {
605   NS_ENSURE_ARG_POINTER(aResult);
606   *aResult = false;
607 
608   if (!mDbTable || !mDb->GetEnv()) {
609     return NS_ERROR_NULL_POINTER;
610   }
611 
612   nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
613   mDbTable->GetTableRowCursor(mDb->GetEnv(), mRowPos,
614                               getter_AddRefs(rowCursor));
615   NS_ENSURE_TRUE(rowCursor, NS_ERROR_FAILURE);
616 
617   mdbOid rowOid;
618   rowCursor->NextRowOid(mDb->GetEnv(), &rowOid, nullptr);
619   while (rowOid.mOid_Id != (mdb_id)-1) {
620     if (mDb->IsListRowScopeToken(rowOid.mOid_Scope) ||
621         mDb->IsCardRowScopeToken(rowOid.mOid_Scope)) {
622       *aResult = true;
623 
624       return NS_OK;
625     }
626 
627     if (!mDb->IsDataRowScopeToken(rowOid.mOid_Scope)) {
628       return NS_ERROR_FAILURE;
629     }
630 
631     rowCursor->NextRowOid(mDb->GetEnv(), &rowOid, nullptr);
632   }
633 
634   return NS_OK;
635 }
636 
637 NS_IMETHODIMP
GetNext(nsISupports ** aResult)638 nsAddrDBEnumerator::GetNext(nsISupports** aResult) {
639   NS_ENSURE_ARG_POINTER(aResult);
640 
641   *aResult = nullptr;
642 
643   if (!mDbTable || !mDb->GetEnv()) {
644     return NS_ERROR_NULL_POINTER;
645   }
646 
647   if (!mRowCursor) {
648     mDbTable->GetTableRowCursor(mDb->GetEnv(), -1, getter_AddRefs(mRowCursor));
649     NS_ENSURE_TRUE(mRowCursor, NS_ERROR_FAILURE);
650   }
651 
652   nsCOMPtr<nsIAbCard> resultCard;
653   mRowCursor->NextRow(mDb->GetEnv(), getter_AddRefs(mCurrentRow), &mRowPos);
654   while (mCurrentRow) {
655     mdbOid rowOid;
656     if (NS_SUCCEEDED(mCurrentRow->GetOid(mDb->GetEnv(), &rowOid))) {
657       nsresult rv;
658       if (mDb->IsListRowScopeToken(rowOid.mOid_Scope)) {
659         rv = mDb->CreateABListCard(mCurrentRow, getter_AddRefs(resultCard));
660         NS_ENSURE_SUCCESS(rv, rv);
661       } else if (mDb->IsCardRowScopeToken(rowOid.mOid_Scope)) {
662         rv = mDb->CreateABCard(mCurrentRow, 0, getter_AddRefs(resultCard));
663         NS_ENSURE_SUCCESS(rv, rv);
664       } else if (!mDb->IsDataRowScopeToken(rowOid.mOid_Scope)) {
665         return NS_ERROR_FAILURE;
666       }
667 
668       if (resultCard) {
669         return CallQueryInterface(resultCard, aResult);
670       }
671     }
672 
673     mRowCursor->NextRow(mDb->GetEnv(), getter_AddRefs(mCurrentRow), &mRowPos);
674   }
675 
676   return NS_ERROR_FAILURE;
677 }
678 
679 class nsListAddressEnumerator final : public nsSimpleEnumerator {
680  public:
DefaultInterface()681   const nsID& DefaultInterface() override { return NS_GET_IID(nsIAbCard); }
682 
683   // nsISimpleEnumerator methods:
684   NS_DECL_NSISIMPLEENUMERATOR
685 
686   // nsListAddressEnumerator methods:
687   nsListAddressEnumerator(nsAddrDatabase* aDb, mdb_id aRowID);
688 
689  protected:
690   ~nsListAddressEnumerator() override = default;
691   nsAddrDatabase* mDb;
692   nsIMdbTable* mDbTable;
693   nsCOMPtr<nsIMdbRow> mListRow;
694   mdb_id mListRowID;
695   uint32_t mAddressTotal;
696   uint16_t mAddressPos;
697 };
698 
nsListAddressEnumerator(nsAddrDatabase * aDb,mdb_id aRowID)699 nsListAddressEnumerator::nsListAddressEnumerator(nsAddrDatabase* aDb,
700                                                  mdb_id aRowID)
701     : mDb(aDb),
702       mDbTable(aDb->GetPabTable()),
703       mListRowID(aRowID),
704       mAddressPos(0) {
705   mDb->GetListRowByRowID(mListRowID, getter_AddRefs(mListRow));
706   mAddressTotal = aDb->GetListAddressTotal(mListRow);
707 }
708 
709 NS_IMETHODIMP
HasMoreElements(bool * aResult)710 nsListAddressEnumerator::HasMoreElements(bool* aResult) {
711   NS_ENSURE_ARG_POINTER(aResult);
712 
713   *aResult = false;
714 
715   if (!mDbTable || !mDb->GetEnv()) {
716     return NS_ERROR_NULL_POINTER;
717   }
718 
719   // In some cases it is possible that GetAddressRowByPos returns success,
720   // but currentRow is null. This is typically due to the fact that a card
721   // has been deleted from the parent and not the list. Whilst we have fixed
722   // that there are still a few dbs around there that we need to support
723   // correctly. Therefore, whilst processing lists ensure that we don't return
724   // false if the only thing stopping us is a blank row, just skip it and try
725   // the next one.
726   while (mAddressPos < mAddressTotal) {
727     nsCOMPtr<nsIMdbRow> currentRow;
728     nsresult rv = mDb->GetAddressRowByPos(mListRow, mAddressPos + 1,
729                                           getter_AddRefs(currentRow));
730 
731     if (NS_SUCCEEDED(rv) && currentRow) {
732       *aResult = true;
733       break;
734     }
735 
736     ++mAddressPos;
737   }
738 
739   return NS_OK;
740 }
741 
742 NS_IMETHODIMP
GetNext(nsISupports ** aResult)743 nsListAddressEnumerator::GetNext(nsISupports** aResult) {
744   NS_ENSURE_ARG_POINTER(aResult);
745 
746   *aResult = nullptr;
747 
748   if (!mDbTable || !mDb->GetEnv()) {
749     return NS_ERROR_NULL_POINTER;
750   }
751 
752   while (++mAddressPos <= mAddressTotal) {
753     nsCOMPtr<nsIMdbRow> currentRow;
754     nsresult rv = mDb->GetAddressRowByPos(mListRow, mAddressPos,
755                                           getter_AddRefs(currentRow));
756     if (NS_SUCCEEDED(rv)) {
757       nsCOMPtr<nsIAbCard> resultCard;
758       rv =
759           mDb->CreateABCard(currentRow, mListRowID, getter_AddRefs(resultCard));
760       NS_ENSURE_SUCCESS(rv, rv);
761 
762       return CallQueryInterface(resultCard, aResult);
763     }
764   }
765 
766   return NS_ERROR_FAILURE;
767 }
768 
769 ////////////////////////////////////////////////////////////////////////////////
770 
EnumerateCards(nsISimpleEnumerator ** result)771 nsresult nsAddrDatabase::EnumerateCards(nsISimpleEnumerator** result) {
772   NS_ADDREF(*result = new nsAddrDBEnumerator(this));
773   return NS_OK;
774 }
775 
EnumerateListAddresses(uint32_t listRowID,nsISimpleEnumerator ** result)776 nsresult nsAddrDatabase::EnumerateListAddresses(uint32_t listRowID,
777                                                 nsISimpleEnumerator** result) {
778   NS_ADDREF(*result = new nsListAddressEnumerator(this, listRowID));
779   return NS_OK;
780 }
781 
CreateCard(nsIMdbRow * cardRow,mdb_id listRowID,nsIAbCard ** result)782 nsresult nsAddrDatabase::CreateCard(nsIMdbRow* cardRow, mdb_id listRowID,
783                                     nsIAbCard** result) {
784   if (!cardRow || !m_mdbEnv || !result) return NS_ERROR_NULL_POINTER;
785 
786   nsresult rv = NS_OK;
787 
788   mdbOid outOid;
789   mdb_id rowID = 0;
790 
791   if (NS_SUCCEEDED(cardRow->GetOid(m_mdbEnv, &outOid))) rowID = outOid.mOid_Id;
792 
793   if (NS_SUCCEEDED(rv)) {
794     nsCOMPtr<nsIAbCard> personCard;
795     personCard = do_CreateInstance(NS_ABCARDPROPERTY_CONTRACTID, &rv);
796     NS_ENSURE_SUCCESS(rv, rv);
797 
798     InitCardFromRow(personCard, cardRow);
799     personCard->SetPropertyAsUint32(kRowIDProperty, rowID);
800 
801     personCard.forget(result);
802   }
803 
804   return rv;
805 }
806 
CreateABCard(nsIMdbRow * cardRow,mdb_id listRowID,nsIAbCard ** result)807 nsresult nsAddrDatabase::CreateABCard(nsIMdbRow* cardRow, mdb_id listRowID,
808                                       nsIAbCard** result) {
809   return CreateCard(cardRow, listRowID, result);
810 }
811 
812 /* create a card for mailing list in the address book */
CreateABListCard(nsIMdbRow * listRow,nsIAbCard ** result)813 nsresult nsAddrDatabase::CreateABListCard(nsIMdbRow* listRow,
814                                           nsIAbCard** result) {
815   if (!listRow || !m_mdbEnv || !result) return NS_ERROR_NULL_POINTER;
816 
817   nsresult rv = NS_OK;
818 
819   mdbOid outOid;
820   mdb_id rowID = 0;
821 
822   if (NS_SUCCEEDED(listRow->GetOid(m_mdbEnv, &outOid))) rowID = outOid.mOid_Id;
823 
824   char* listURI = nullptr;
825 
826   nsAutoString fileName;
827   rv = m_dbName->GetLeafName(fileName);
828   NS_ENSURE_SUCCESS(rv, rv);
829   listURI = PR_smprintf("MailList%ld", rowID);
830 
831   nsCOMPtr<nsIAbCard> personCard;
832   personCard = do_CreateInstance(NS_ABCARDPROPERTY_CONTRACTID, &rv);
833   NS_ENSURE_SUCCESS(rv, rv);
834 
835   if (personCard) {
836     GetListCardFromDB(personCard, listRow);
837 
838     personCard->SetPropertyAsUint32(kRowIDProperty, rowID);
839     personCard->SetIsMailList(true);
840     personCard->SetMailListURI(listURI);
841   }
842 
843   personCard.forget(result);
844   if (listURI) PR_smprintf_free(listURI);
845 
846   return rv;
847 }
848 
GetCardRowByRowID(mdb_id rowID,nsIMdbRow ** dbRow)849 nsresult nsAddrDatabase::GetCardRowByRowID(mdb_id rowID, nsIMdbRow** dbRow) {
850   if (!m_mdbStore || !m_mdbEnv) return NS_ERROR_NULL_POINTER;
851 
852   mdbOid rowOid;
853   rowOid.mOid_Scope = m_CardRowScopeToken;
854   rowOid.mOid_Id = rowID;
855 
856   return m_mdbStore->GetRow(m_mdbEnv, &rowOid, dbRow);
857 }
858 
GetListRowByRowID(mdb_id rowID,nsIMdbRow ** dbRow)859 nsresult nsAddrDatabase::GetListRowByRowID(mdb_id rowID, nsIMdbRow** dbRow) {
860   if (!m_mdbStore || !m_mdbEnv) return NS_ERROR_NULL_POINTER;
861 
862   mdbOid rowOid;
863   rowOid.mOid_Scope = m_ListRowScopeToken;
864   rowOid.mOid_Id = rowID;
865 
866   return m_mdbStore->GetRow(m_mdbEnv, &rowOid, dbRow);
867 }
868