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