1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <memory>
23 #include <utility>
24 #include <vector>
25 
26 #include "MacabRecords.hxx"
27 #include "MacabRecord.hxx"
28 #include "MacabHeader.hxx"
29 #include "macabutilities.hxx"
30 
31 #include <premac.h>
32 #include <Carbon/Carbon.h>
33 #include <AddressBook/ABAddressBookC.h>
34 #include <postmac.h>
35 #include <com/sun/star/util/DateTime.hpp>
36 
37 using namespace connectivity::macab;
38 using namespace com::sun::star::util;
39 
40 namespace {
41 
manageDuplicateHeaders(macabfield ** _headerNames,const sal_Int32 _length)42 void manageDuplicateHeaders(macabfield **_headerNames, const sal_Int32 _length)
43 {
44     /* If we have two cases of, say, phone: home, this makes it:
45      * phone: home (1)
46      * phone: home (2)
47      */
48     sal_Int32 i, j;
49     sal_Int32 count;
50     for(i = _length-1; i >= 0; i--)
51     {
52         count = 1;
53         for( j = i-1; j >= 0; j--)
54         {
55             if(CFEqual(_headerNames[i]->value, _headerNames[j]->value))
56             {
57                 count++;
58             }
59         }
60 
61         // duplicate!
62         if(count != 1)
63         {
64             // There is probably a better way to do this...
65             OUString newName = CFStringToOUString(static_cast<CFStringRef>(_headerNames[i]->value));
66             CFRelease(_headerNames[i]->value);
67             newName += " (" + OUString::number(count) + ")";
68             _headerNames[i]->value = OUStringToCFString(newName);
69         }
70     }
71 }
72 
73 }
74 
MacabRecords(const ABAddressBookRef _addressBook,MacabHeader * _header,MacabRecord ** _records,sal_Int32 _numRecords)75 MacabRecords::MacabRecords(const ABAddressBookRef _addressBook, MacabHeader *_header, MacabRecord **_records, sal_Int32 _numRecords)
76    : recordsSize(_numRecords), currentRecord(_numRecords), recordType(kABPersonRecordType),
77      header(_header), records(_records), addressBook(_addressBook)
78 {
79     /* Variables constructed... */
80     bootstrap_CF_types();
81     bootstrap_requiredProperties();
82 }
83 
84 
85 /* Creates a MacabRecords from another: copies the length, name, and
86  * address book of the original, but the header or the records themselves.
87  * The idea is that the only reason to copy a MacabRecords is to create
88  * a filtered version of it, which can have the same length (to avoid
89  * resizing) and will work from the same base addressbook, but might have
90  * entirely different values and even (possibly in the future) a different
91  * header.
92  */
MacabRecords(const MacabRecords * _copy)93 MacabRecords::MacabRecords(const MacabRecords *_copy)
94     : recordsSize(_copy->recordsSize), currentRecord(0), recordType(kABPersonRecordType),
95       header(nullptr), records(new MacabRecord *[recordsSize]), addressBook(_copy->addressBook),
96       m_sName(_copy->m_sName)
97 {
98     /* Variables constructed... */
99     bootstrap_CF_types();
100     bootstrap_requiredProperties();
101 }
102 
103 
MacabRecords(const ABAddressBookRef _addressBook)104 MacabRecords::MacabRecords(const ABAddressBookRef _addressBook)
105     : recordsSize(0), currentRecord(0), recordType(kABPersonRecordType),
106       header(nullptr), records(nullptr), addressBook(_addressBook)
107 {
108     /* Variables constructed... */
109     bootstrap_CF_types();
110     bootstrap_requiredProperties();
111 }
112 
113 
initialize()114 void MacabRecords::initialize()
115 {
116 
117     /* Make sure everything is NULL before initializing. (We usually just
118      * initialize after we use the constructor that takes only a
119      * MacabAddressBook, so these variables will most likely already be
120      * NULL.
121      */
122     if(records != nullptr)
123     {
124         sal_Int32 i;
125 
126         for(i = 0; i < recordsSize; i++)
127             delete records[i];
128 
129         delete [] records;
130     }
131 
132     if(header != nullptr)
133         delete header;
134 
135     /* We can handle both default record Address Book record types in
136      * this method, though only kABPersonRecordType is ever used.
137      */
138     CFArrayRef allRecords;
139     if(CFStringCompare(recordType, kABPersonRecordType, 0) == kCFCompareEqualTo)
140         allRecords = ABCopyArrayOfAllPeople(addressBook);
141     else
142         allRecords = ABCopyArrayOfAllGroups(addressBook);
143 
144     ABRecordRef record;
145     sal_Int32 i;
146     recordsSize = static_cast<sal_Int32>(CFArrayGetCount(allRecords));
147     records = new MacabRecord *[recordsSize];
148 
149     /* First, we create the header... */
150     header = createHeaderForRecordType(allRecords, recordType);
151 
152     /* Then, we create each of the records... */
153     for(i = 0; i < recordsSize; i++)
154     {
155         record = const_cast<ABRecordRef>(CFArrayGetValueAtIndex(allRecords, i));
156         records[i] = createMacabRecord(record, header, recordType);
157     }
158     currentRecord = recordsSize;
159 
160     CFRelease(allRecords);
161 }
162 
163 
~MacabRecords()164 MacabRecords::~MacabRecords()
165 {
166 }
167 
168 
setHeader(MacabHeader * _header)169 void MacabRecords::setHeader(MacabHeader *_header)
170 {
171     if(header != nullptr)
172         delete header;
173     header = _header;
174 }
175 
176 
getHeader() const177 MacabHeader *MacabRecords::getHeader() const
178 {
179     return header;
180 }
181 
182 
183 /* Inserts a MacabRecord at a given location. If there is already a
184  * MacabRecord at that location, return it.
185  */
insertRecord(MacabRecord * _newRecord,const sal_Int32 _location)186 MacabRecord *MacabRecords::insertRecord(MacabRecord *_newRecord, const sal_Int32 _location)
187 {
188     MacabRecord *oldRecord;
189 
190     /* If the location is greater than the current allocated size of this
191      * MacabRecords, allocate more space.
192      */
193     if(_location >= recordsSize)
194     {
195         sal_Int32 i;
196         MacabRecord **newRecordsArray = new MacabRecord *[_location+1];
197         for(i = 0; i < recordsSize; i++)
198         {
199             newRecordsArray[i] = records[i];
200         }
201         delete [] records;
202         records = newRecordsArray;
203     }
204 
205     /* Remember: currentRecord refers to one above the highest existing
206      * record (i.e., it refers to where to place the next record if a
207      * location is not given).
208      */
209     if(_location >= currentRecord)
210         currentRecord = _location+1;
211 
212     oldRecord = records[_location];
213     records[_location] = _newRecord;
214     return oldRecord;
215 }
216 
217 
218 /* Insert a record at the next available place. */
insertRecord(MacabRecord * _newRecord)219 void MacabRecords::insertRecord(MacabRecord *_newRecord)
220 {
221     insertRecord(_newRecord, currentRecord);
222 }
223 
224 
getRecord(const sal_Int32 _location) const225 MacabRecord *MacabRecords::getRecord(const sal_Int32 _location) const
226 {
227     if(_location >= recordsSize)
228         return nullptr;
229     return records[_location];
230 }
231 
232 
getField(const sal_Int32 _recordNumber,const sal_Int32 _columnNumber) const233 macabfield *MacabRecords::getField(const sal_Int32 _recordNumber, const sal_Int32 _columnNumber) const
234 {
235     if(_recordNumber >= recordsSize)
236         return nullptr;
237 
238     MacabRecord *record = records[_recordNumber];
239 
240     if(_columnNumber < 0 || _columnNumber >= record->getSize())
241         return nullptr;
242 
243     return record->get(_columnNumber);
244 }
245 
246 
getField(const sal_Int32 _recordNumber,std::u16string_view _columnName) const247 macabfield *MacabRecords::getField(const sal_Int32 _recordNumber, std::u16string_view _columnName)
248     const
249 {
250     if(header != nullptr)
251     {
252         sal_Int32 columnNumber = header->getColumnNumber(_columnName);
253         if(columnNumber == -1)
254             return nullptr;
255 
256         return getField(_recordNumber, columnNumber);
257     }
258     else
259     {
260         // error: shouldn't access field with null header!
261         return nullptr;
262     }
263 }
264 
265 
getFieldNumber(std::u16string_view _columnName) const266 sal_Int32 MacabRecords::getFieldNumber(std::u16string_view _columnName) const
267 {
268     if(header != nullptr)
269         return header->getColumnNumber(_columnName);
270     else
271         // error: shouldn't access field with null header!
272         return -1;
273 }
274 
275 
276 /* Create the lcl_CFTypes array -- we need this because there is no
277  * way to get the ABType of an object from the object itself, and the
278  * function ABTypeOfProperty can't handle multiple levels of data
279  * (e.g., it can tell us that "address" is of type
280  * kABDictionaryProperty, but it cannot tell us that all of the keys
281  * and values in the dictionary have type kABStringProperty. On the
282  * other hand, we _can_ get the CFType out of any object.
283  * Unfortunately, all information about CFTypeIDs comes with the
284  * warning that they change between releases, so we build them
285  * ourselves here. (The one that we can't build is for multivalues,
286  * e.g., kABMultiStringProperty. All of these appear to have the
287  * same type: 1, but there is no function that I've found to give
288  * us that dynamically in case that number ever changes.
289  */
bootstrap_CF_types()290 void MacabRecords::bootstrap_CF_types()
291 {
292     lcl_CFTypes = {
293         {CFNumberGetTypeID(), kABIntegerProperty},
294         {CFStringGetTypeID(), kABStringProperty},
295         {CFDateGetTypeID(), kABDateProperty},
296         {CFArrayGetTypeID(), kABArrayProperty},
297         {CFDictionaryGetTypeID(), kABDictionaryProperty},
298         {CFDataGetTypeID(), kABDataProperty}};
299 }
300 
301 
302 /* This is based on the possible fields required in the mail merge template
303  * in sw. If the fields possible there change, it would be optimal to
304  * change these fields as well.
305  */
bootstrap_requiredProperties()306 void MacabRecords::bootstrap_requiredProperties()
307 {
308     requiredProperties = {
309         kABTitleProperty, kABFirstNameProperty, kABLastNameProperty, kABOrganizationProperty,
310         kABAddressProperty, kABPhoneProperty, kABEmailProperty};
311 }
312 
313 
314 /* Create the header for a given record type and a given array of records.
315  * Because the array of records and the record type are given, if you want
316  * to, you can run this method on the members of a group, or on any other
317  * filtered list of people and get a header relevant to them (e.g., if
318  * they only have home addresses, the work address fields won't show up).
319  */
createHeaderForRecordType(const CFArrayRef _records,const CFStringRef _recordType) const320 MacabHeader *MacabRecords::createHeaderForRecordType(const CFArrayRef _records, const CFStringRef _recordType) const
321 {
322     /* We have two types of properties for a given record type, nonrequired
323      * and required. Required properties are ones that will show up whether
324      * or not they are empty. Nonrequired properties will only show up if
325      * at least one record in the set has that property filled. The reason
326      * is that some properties, like the kABTitleProperty are required by
327      * the mail merge wizard (in module sw) but are by default not shown in
328      * the macOS address book, so they would be weeded out at this stage
329      * and not shown if they were not required.
330      *
331      * Note: with the addition of required properties, I am not sure that
332      * this method still works for kABGroupRecordType (since the required
333      * properties are all for kABPersonRecordType).
334      *
335      * Note: required properties are constructed in the method
336      * bootstrap_requiredProperties() (above).
337      */
338     CFArrayRef allProperties = ABCopyArrayOfPropertiesForRecordType(addressBook, _recordType);
339     CFStringRef *nonRequiredProperties;
340     sal_Int32 numRecords = static_cast<sal_Int32>(CFArrayGetCount(_records));
341     sal_Int32 numProperties = static_cast<sal_Int32>(CFArrayGetCount(allProperties));
342     sal_Int32 numNonRequiredProperties = numProperties - requiredProperties.size();
343 
344     /* While searching through the properties for required properties, these
345      * sal_Bools will keep track of what we have found.
346      */
347     auto const bFoundRequiredProperties = std::make_unique<bool[]>(requiredProperties.size());
348 
349 
350     /* We have three MacabHeaders: headerDataForProperty is where we
351      * store the result of createHeaderForProperty(), which return a
352      * MacabHeader for a single property. lcl_header is where we store
353      * the MacabHeader that we are constructing. And, nonRequiredHeader
354      * is where we construct the MacabHeader for non-required properties,
355      * so that we can sort them before adding them to lcl_header.
356      */
357     MacabHeader *headerDataForProperty;
358     MacabHeader *lcl_header = new MacabHeader();
359     MacabHeader *nonRequiredHeader = new MacabHeader();
360 
361     /* Other variables... */
362     sal_Int32 k;
363     ABRecordRef record;
364     CFStringRef property;
365 
366 
367     /* Allocate and initialize... */
368     nonRequiredProperties = new CFStringRef[numNonRequiredProperties];
369     k = 0;
370     for(std::vector<CFStringRef>::size_type i = 0; i < requiredProperties.size(); i++)
371         bFoundRequiredProperties[i] = false;
372 
373     /* Determine the non-required properties... */
374     for(sal_Int32 i = 0; i < numProperties; i++)
375     {
376         bool bFoundProperty = false;
377         property = static_cast<CFStringRef>(CFArrayGetValueAtIndex(allProperties, i));
378         for(std::vector<CFStringRef>::size_type j = 0; j < requiredProperties.size(); j++)
379         {
380             if(CFEqual(property, requiredProperties[j]))
381             {
382                 bFoundProperty = true;
383                 bFoundRequiredProperties[j] = true;
384                 break;
385             }
386         }
387 
388         if(!bFoundProperty)
389         {
390             /* If we have found too many non-required properties */
391             if(k == numNonRequiredProperties)
392             {
393                 k++; // so that the OSL_ENSURE below fails
394                 break;
395             }
396             nonRequiredProperties[k] = property;
397             k++;
398         }
399     }
400 
401     // Somehow, we got too many or too few non-required properties...
402     // Most likely, one of the required properties no longer exists, which
403     // we also test later.
404     OSL_ENSURE(k == numNonRequiredProperties, "MacabRecords::createHeaderForRecordType: Found an unexpected number of non-required properties");
405 
406     /* Fill the header with required properties first... */
407     for(std::vector<CFStringRef>::size_type i = 0; i < requiredProperties.size(); i++)
408     {
409         if(bFoundRequiredProperties[i])
410         {
411             /* The order of these matters (we want all address properties
412              * before any phone properties, or else things will look weird),
413              * so we get all possibilities for each property, going through
414              * each record, and then go onto the next property.
415              * (Note: the reason that we have to go through all records
416              * in the first place is that properties like address, phone, and
417              * e-mail are multi-value properties with an unknown number of
418              * values. A user could specify thirteen different kinds of
419              * e-mail addresses for one of her or his contacts, and we need to
420              * get all of them.
421              */
422             for(sal_Int32 j = 0; j < numRecords; j++)
423             {
424                 record = const_cast<ABRecordRef>(CFArrayGetValueAtIndex(_records, j));
425                 headerDataForProperty = createHeaderForProperty(record,requiredProperties[i],_recordType,true);
426                 if(headerDataForProperty != nullptr)
427                 {
428                     (*lcl_header) += headerDataForProperty;
429                     delete headerDataForProperty;
430                 }
431             }
432         }
433         else
434         {
435             // Couldn't find a required property...
436             OSL_FAIL(OString("MacabRecords::createHeaderForRecordType: could not find required property: " +
437                                     OUStringToOString(CFStringToOUString(requiredProperties[i]), RTL_TEXTENCODING_ASCII_US)).getStr());
438         }
439     }
440 
441     /* And now, non-required properties... */
442     for(sal_Int32 i = 0; i < numRecords; i++)
443     {
444         record = const_cast<ABRecordRef>(CFArrayGetValueAtIndex(_records, i));
445 
446         for(sal_Int32 j = 0; j < numNonRequiredProperties; j++)
447         {
448             property = nonRequiredProperties[j];
449             headerDataForProperty = createHeaderForProperty(record,property,_recordType,false);
450             if(headerDataForProperty != nullptr)
451             {
452                 (*nonRequiredHeader) += headerDataForProperty;
453                 delete headerDataForProperty;
454             }
455         }
456 
457     }
458     nonRequiredHeader->sortRecord();
459 
460     (*lcl_header) += nonRequiredHeader;
461     delete nonRequiredHeader;
462 
463     CFRelease(allProperties);
464     delete [] nonRequiredProperties;
465 
466     return lcl_header;
467 }
468 
469 
470 /* Create a header for a single property. Basically, this method gets
471  * the property's value and type and then calls another method of
472  * the same name to do the dirty work.
473  */
createHeaderForProperty(const ABRecordRef _record,const CFStringRef _propertyName,const CFStringRef _recordType,const bool _isPropertyRequired) const474 MacabHeader *MacabRecords::createHeaderForProperty(const ABRecordRef _record, const CFStringRef _propertyName, const CFStringRef _recordType, const bool _isPropertyRequired) const
475 {
476     // local variables
477     CFStringRef localizedPropertyName;
478     CFTypeRef propertyValue;
479     ABPropertyType propertyType;
480     MacabHeader *result;
481 
482     /* Get the property's value */
483     propertyValue = ABRecordCopyValue(_record,_propertyName);
484     if(propertyValue == nullptr && !_isPropertyRequired)
485         return nullptr;
486 
487     propertyType = ABTypeOfProperty(addressBook, _recordType, _propertyName);
488     localizedPropertyName = ABCopyLocalizedPropertyOrLabel(_propertyName);
489 
490     result = createHeaderForProperty(propertyType, propertyValue, localizedPropertyName);
491 
492     if(propertyValue != nullptr)
493         CFRelease(propertyValue);
494 
495     return result;
496 }
497 
498 
499 /* Create a header for a single property. This method is recursive
500  * because a single property might contain several sub-properties that
501  * we also want to treat singly.
502  */
createHeaderForProperty(const ABPropertyType _propertyType,const CFTypeRef _propertyValue,const CFStringRef _propertyName) const503 MacabHeader *MacabRecords::createHeaderForProperty(const ABPropertyType _propertyType, const CFTypeRef _propertyValue, const CFStringRef _propertyName) const
504 {
505     macabfield **headerNames = nullptr;
506     sal_Int32 length = 0;
507 
508     switch(_propertyType)
509     {
510         /* Scalars */
511         case kABStringProperty:
512         case kABRealProperty:
513         case kABIntegerProperty:
514         case kABDateProperty:
515             length = 1;
516             headerNames = new macabfield *[1];
517             headerNames[0] = new macabfield;
518             headerNames[0]->value = _propertyName;
519             headerNames[0]->type = _propertyType;
520             break;
521 
522         /* Multi-scalars */
523         case kABMultiIntegerProperty:
524         case kABMultiDateProperty:
525         case kABMultiStringProperty:
526         case kABMultiRealProperty:
527         case kABMultiDataProperty:
528             /* For non-scalars, we can only get more information if the property
529              * actually exists.
530              */
531             if(_propertyValue != nullptr)
532             {
533                 sal_Int32 i;
534 
535                 sal_Int32 multiLength = ABMultiValueCount(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)));
536                 CFStringRef multiLabel, localizedMultiLabel;
537                 OUString multiLabelString;
538                 OUString multiPropertyString;
539                 OUString headerNameString;
540                 ABPropertyType multiType = static_cast<ABPropertyType>(ABMultiValuePropertyType(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue))) - 0x100);
541 
542                 length = multiLength;
543                 headerNames = new macabfield *[multiLength];
544                 multiPropertyString = CFStringToOUString(_propertyName);
545 
546                 /* Go through each element, and - since each element is a scalar -
547                  * just create a new macabfield for it.
548                  */
549                 for(i = 0; i < multiLength; i++)
550                 {
551                     multiLabel = ABMultiValueCopyLabelAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i);
552                     localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel);
553                     multiLabelString = CFStringToOUString(localizedMultiLabel);
554                     CFRelease(multiLabel);
555                     CFRelease(localizedMultiLabel);
556                     headerNameString = multiPropertyString + ": " + fixLabel(multiLabelString);
557                     headerNames[i] = new macabfield;
558                     headerNames[i]->value = OUStringToCFString(headerNameString);
559                     headerNames[i]->type = multiType;
560                 }
561             }
562             break;
563 
564         /* Multi-array or dictionary */
565         case kABMultiArrayProperty:
566         case kABMultiDictionaryProperty:
567             /* For non-scalars, we can only get more information if the property
568              * actually exists.
569              */
570             if(_propertyValue != nullptr)
571             {
572                 sal_Int32 i,j,k;
573 
574                 // Total number of multi-array or multi-dictionary elements.
575                 sal_Int32 multiLengthFirstLevel = ABMultiValueCount(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)));
576 
577                 /* Total length, including the length of each element (e.g., if
578                  * this multi-dictionary contains three dictionaries, and each
579                  * dictionary has four elements, this variable will be twelve,
580                  * whereas multiLengthFirstLevel will be three.
581                  */
582                 sal_Int32 multiLengthSecondLevel = 0;
583 
584                 CFStringRef multiLabel, localizedMultiLabel;
585                 CFTypeRef multiValue;
586                 OUString multiLabelString;
587                 OUString multiPropertyString;
588                 std::vector<std::unique_ptr<MacabHeader>> multiHeaders;
589                 ABPropertyType multiType = static_cast<ABPropertyType>(ABMultiValuePropertyType(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue))) - 0x100);
590 
591                 multiPropertyString = CFStringToOUString(_propertyName);
592 
593                 /* Go through each element - since each element can really
594                  * contain anything, we run this method again on each element
595                  * and store the resulting MacabHeader (in the multiHeaders
596                  * array). Then, all we'll have to do is combine the MacabHeaders
597                  * into a single one.
598                  */
599                 for(i = 0; i < multiLengthFirstLevel; i++)
600                 {
601                     /* label */
602                     multiLabel = ABMultiValueCopyLabelAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i);
603                     multiValue = ABMultiValueCopyValueAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i);
604                     std::unique_ptr<MacabHeader> hdr;
605                     if(multiValue && multiLabel)
606                     {
607                         localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel);
608                         multiLabelString = multiPropertyString + ": " + fixLabel(CFStringToOUString(localizedMultiLabel));
609                         CFRelease(multiLabel);
610                         CFRelease(localizedMultiLabel);
611                         multiLabel = OUStringToCFString(multiLabelString);
612                         hdr.reset(createHeaderForProperty(multiType, multiValue, multiLabel));
613                         if (!hdr)
614                             hdr = std::make_unique<MacabHeader>();
615                         multiLengthSecondLevel += hdr->getSize();
616                     }
617                     else
618                     {
619                         hdr = std::make_unique<MacabHeader>();
620                     }
621                     if(multiValue)
622                         CFRelease(multiValue);
623                     if(multiLabel)
624                         CFRelease(multiLabel);
625                     multiHeaders.push_back(std::move(hdr));
626                 }
627 
628                 /* We now have enough information to create our final MacabHeader.
629                  * We go through each field of each header and add it to the
630                  * headerNames array (which is what is used below to construct
631                  * the MacabHeader we return).
632                  */
633                 length = multiLengthSecondLevel;
634                 headerNames = new macabfield *[multiLengthSecondLevel];
635 
636                 for(i = 0, j = 0, k = 0; i < multiLengthSecondLevel; i++,k++)
637                 {
638                     while(multiHeaders[j]->getSize() == k)
639                     {
640                         j++;
641                         k = 0;
642                     }
643 
644                     headerNames[i] = multiHeaders[j]->copy(k);
645                 }
646             }
647             break;
648 
649         /* Dictionary */
650         case kABDictionaryProperty:
651             /* For non-scalars, we can only get more information if the property
652              * actually exists.
653              */
654             if(_propertyValue != nullptr)
655             {
656                 /* Assume all keys are strings */
657                 sal_Int32 numRecords = static_cast<sal_Int32>(CFDictionaryGetCount(static_cast<CFDictionaryRef>(_propertyValue)));
658 
659                 /* The only method for getting info out of a CFDictionary, of both
660                  * keys and values, is to all of them all at once, so these
661                  * variables will hold them.
662                  */
663                 CFStringRef *dictKeys;
664                 CFTypeRef *dictValues;
665 
666                 sal_Int32 i,j,k;
667                 OUString dictKeyString, propertyNameString;
668                 ABPropertyType dictType;
669                 MacabHeader **dictHeaders = new MacabHeader *[numRecords];
670                 OUString dictLabelString;
671                 CFStringRef dictLabel, localizedDictKey;
672 
673                 /* Get the keys and values */
674                 dictKeys = static_cast<CFStringRef *>(malloc(sizeof(CFStringRef)*numRecords));
675                 dictValues = static_cast<CFTypeRef *>(malloc(sizeof(CFTypeRef)*numRecords));
676                 CFDictionaryGetKeysAndValues(static_cast<CFDictionaryRef>(_propertyValue), reinterpret_cast<const void **>(dictKeys), dictValues);
677 
678                 propertyNameString = CFStringToOUString(_propertyName);
679 
680                 length = 0;
681                 /* Go through each element - assuming that the key is a string but
682                  * that the value could be anything. Since the value could be
683                  * anything, we can't assume that it is scalar (it could even be
684                  * another dictionary), so we attempt to get its type using
685                  * the method getABTypeFromCFType and then run this method
686                  * recursively on that element, storing the MacabHeader that
687                  * results. Then, we just combine all of the MacabHeaders into
688                  * one.
689                  */
690                 for(i = 0; i < numRecords; i++)
691                 {
692                     dictType = getABTypeFromCFType( CFGetTypeID(dictValues[i]) );
693                     localizedDictKey = ABCopyLocalizedPropertyOrLabel(dictKeys[i]);
694                     dictKeyString = CFStringToOUString(localizedDictKey);
695                     dictLabelString = propertyNameString + ": " + fixLabel(dictKeyString);
696                     dictLabel = OUStringToCFString(dictLabelString);
697                     dictHeaders[i] = createHeaderForProperty(dictType, dictValues[i], dictLabel);
698                     if (!dictHeaders[i])
699                         dictHeaders[i] = new MacabHeader();
700                     length += dictHeaders[i]->getSize();
701                     CFRelease(dictLabel);
702                     CFRelease(localizedDictKey);
703                 }
704 
705                 /* Combine all of the macabfields in each MacabHeader into the
706                  * headerNames array, which (at the end of this method) is used
707                  * to create the MacabHeader that is returned.
708                  */
709                 headerNames = new macabfield *[length];
710                 for(i = 0, j = 0, k = 0; i < length; i++,k++)
711                 {
712                     while(dictHeaders[j]->getSize() == k)
713                     {
714                         j++;
715                         k = 0;
716                     }
717 
718                     headerNames[i] = dictHeaders[j]->copy(k);
719                 }
720 
721                 for(i = 0; i < numRecords; i++)
722                     delete dictHeaders[i];
723 
724                 delete [] dictHeaders;
725                 free(dictKeys);
726                 free(dictValues);
727             }
728             break;
729 
730         /* Array */
731         case kABArrayProperty:
732             /* For non-scalars, we can only get more information if the property
733              * actually exists.
734              */
735             if(_propertyValue != nullptr)
736             {
737                 sal_Int32 arrLength = static_cast<sal_Int32>(CFArrayGetCount(static_cast<CFArrayRef>(_propertyValue)));
738                 sal_Int32 i,j,k;
739                 CFTypeRef arrValue;
740                 ABPropertyType arrType;
741                 std::vector<std::unique_ptr<MacabHeader>> arrHeaders;
742                 OUString propertyNameString = CFStringToOUString(_propertyName);
743                 OUString arrLabelString;
744                 CFStringRef arrLabel;
745 
746                 length = 0;
747                 /* Go through each element - since the elements here do not have
748                  * unique keys like the ones in dictionaries, we create a unique
749                  * key out of the id of the element in the array (the first
750                  * element gets a 0 plopped onto the end of it, the second a 1...
751                  * As with dictionaries, the elements could be anything, including
752                  * another array, so we have to run this method recursively on
753                  * each element, storing the resulting MacabHeader into an array,
754                  * which we then combine into one MacabHeader that is returned.
755                  */
756                 for(i = 0; i < arrLength; i++)
757                 {
758                     arrValue = CFArrayGetValueAtIndex(static_cast<CFArrayRef>(_propertyValue), i);
759                     arrType = getABTypeFromCFType( CFGetTypeID(arrValue) );
760                     arrLabelString = propertyNameString + OUString::number(i);
761                     arrLabel = OUStringToCFString(arrLabelString);
762                     auto hdr = std::unique_ptr<MacabHeader>(createHeaderForProperty(arrType, arrValue, arrLabel));
763                     if (!hdr)
764                         hdr = std::make_unique<MacabHeader>();
765                     length += hdr->getSize();
766                     CFRelease(arrLabel);
767                     arrHeaders.push_back(std::move(hdr));
768                 }
769 
770                 headerNames = new macabfield *[length];
771                 for(i = 0, j = 0, k = 0; i < length; i++,k++)
772                 {
773                     while(arrHeaders[j]->getSize() == k)
774                     {
775                         j++;
776                         k = 0;
777                     }
778 
779                     headerNames[i] = arrHeaders[j]->copy(k);
780                 }
781             }
782             break;
783 
784             default:
785                 break;
786 
787     }
788 
789     /* If we succeeded at adding elements to the headerNames array, then
790      * length will no longer be 0. If it is, create a new MacabHeader
791      * out of the headerNames (after weeding out duplicate headers), and
792      * then return the result. If the length is still 0, return NULL: we
793      * failed to create a MacabHeader out of this property.
794      */
795     if(length != 0)
796     {
797         manageDuplicateHeaders(headerNames, length);
798         MacabHeader *headerResult = new MacabHeader(length, headerNames);
799         for(sal_Int32 i = 0; i < length; ++i)
800             delete headerNames[i];
801         delete [] headerNames;
802         return headerResult;
803     }
804     else
805         return nullptr;
806 }
807 
808 
809 /* Create a MacabRecord out of an ABRecord, using a given MacabHeader and
810  * the record's type. We go through each property for this record type
811  * then process it much like we processed the header (above), with two
812  * exceptions: if we come upon something not in the header, we ignore it
813  * (it's something we don't want to add), and once we find a corresponding
814  * location in the header, we store the property and the property type in
815  * a macabfield. (For the header, we stored the property type and the name
816  * of the property as a CFString.)
817  */
createMacabRecord(const ABRecordRef _abrecord,const MacabHeader * _header,const CFStringRef _recordType) const818 MacabRecord *MacabRecords::createMacabRecord(const ABRecordRef _abrecord, const MacabHeader *_header, const CFStringRef _recordType) const
819 {
820     /* The new record that we will create... */
821     MacabRecord *macabRecord = new MacabRecord(_header->getSize());
822 
823     CFArrayRef recordProperties = ABCopyArrayOfPropertiesForRecordType(addressBook, _recordType);
824     sal_Int32 numProperties = static_cast<sal_Int32>(CFArrayGetCount(recordProperties));
825 
826     sal_Int32 i;
827 
828     CFTypeRef propertyValue;
829     ABPropertyType propertyType;
830 
831     CFStringRef propertyName, localizedPropertyName;
832     OUString propertyNameString;
833     for(i = 0; i < numProperties; i++)
834     {
835         propertyName = static_cast<CFStringRef>(CFArrayGetValueAtIndex(recordProperties, i));
836         localizedPropertyName = ABCopyLocalizedPropertyOrLabel(propertyName);
837         propertyNameString = CFStringToOUString(localizedPropertyName);
838         CFRelease(localizedPropertyName);
839 
840         /* Get the property's value */
841         propertyValue = ABRecordCopyValue(_abrecord,propertyName);
842         if(propertyValue != nullptr)
843         {
844             propertyType = ABTypeOfProperty(addressBook, _recordType, propertyName);
845             if(propertyType != kABErrorInProperty)
846                 insertPropertyIntoMacabRecord(propertyType, macabRecord, _header, propertyNameString, propertyValue);
847 
848             CFRelease(propertyValue);
849         }
850     }
851     CFRelease(recordProperties);
852     return macabRecord;
853 }
854 
855 
856 /* Inserts a given property into a MacabRecord. This method calls another
857  * method by the same name after getting the property type (it only
858  * receives the property value). It is called when we aren't given the
859  * property's type already.
860  */
insertPropertyIntoMacabRecord(MacabRecord * _abrecord,const MacabHeader * _header,const OUString & _propertyName,const CFTypeRef _propertyValue) const861 void MacabRecords::insertPropertyIntoMacabRecord(MacabRecord *_abrecord, const MacabHeader *_header, const OUString& _propertyName, const CFTypeRef _propertyValue) const
862 {
863     CFTypeID cf_type = CFGetTypeID(_propertyValue);
864     ABPropertyType ab_type = getABTypeFromCFType( cf_type );
865 
866     if(ab_type != kABErrorInProperty)
867         insertPropertyIntoMacabRecord(ab_type, _abrecord, _header, _propertyName, _propertyValue);
868 }
869 
870 
871 /* Inserts a given property into a MacabRecord. This method is recursive
872  * because properties can contain many sub-properties.
873  */
insertPropertyIntoMacabRecord(const ABPropertyType _propertyType,MacabRecord * _abrecord,const MacabHeader * _header,const OUString & _propertyName,const CFTypeRef _propertyValue) const874 void MacabRecords::insertPropertyIntoMacabRecord(const ABPropertyType _propertyType, MacabRecord *_abrecord, const MacabHeader *_header, const OUString& _propertyName, const CFTypeRef _propertyValue) const
875 {
876     /* If there is no value, return */
877     if(_propertyValue == nullptr)
878         return;
879 
880     /* The main switch statement */
881     switch(_propertyType)
882     {
883         /* Scalars */
884         case kABStringProperty:
885         case kABRealProperty:
886         case kABIntegerProperty:
887         case kABDateProperty:
888         {
889             /* Only scalars actually insert a property into the MacabRecord.
890              * In all other cases, this method is called recursively until a
891              * scalar type, an error, or an unknown type are found.
892              * Because of that, the following checks only occur for this type.
893              * We store whether we have successfully placed this property
894              * into the MacabRecord (or whether an unrecoverable error occurred).
895              * Then, we try over and over again to place the property into the
896              * record. There are three possible results:
897              * 1) Success!
898              * 2) There is already a property stored at the column of this name,
899              * in which case we have a duplicate header (see the method
900              * manageDuplicateHeaders()). If that is the case, we add an ID
901              * to the end of the column name in the same format as we do in
902              * manageDuplicateHeaders() and try again.
903              * 3) No column of this name exists in the header. In this case,
904              * there is nothing we can do: we have failed to place this
905              * property into the record.
906              */
907             bool bPlaced = false;
908             OUString columnName = _propertyName;
909             sal_Int32 i = 1;
910 
911             // A big safeguard to prevent two fields from having the same name.
912             while(!bPlaced)
913             {
914                 sal_Int32 columnNumber = _header->getColumnNumber(columnName);
915                 bPlaced = true;
916                 if(columnNumber != -1)
917                 {
918                     // collision! A property already exists here!
919                     if(_abrecord->get(columnNumber) != nullptr)
920                     {
921                         bPlaced = false;
922                         i++;
923                         columnName = _propertyName + " (" + OUString::number(i) + ")";
924                     }
925 
926                     // success!
927                     else
928                     {
929                         _abrecord->insertAtColumn(_propertyValue, _propertyType, columnNumber);
930                     }
931                 }
932             }
933         }
934         break;
935 
936         /* Array */
937         case kABArrayProperty:
938             {
939                 /* An array is basically just a list of anything, so all we do
940                  * is go through the array, and rerun this method recursively
941                  * on each element.
942                  */
943                 sal_Int32 arrLength = static_cast<sal_Int32>(CFArrayGetCount(static_cast<CFArrayRef>(_propertyValue)));
944                 sal_Int32 i;
945                 OUString newPropertyName;
946 
947                 /* Going through each element... */
948                 for(i = 0; i < arrLength; i++)
949                 {
950                     const void *arrValue = CFArrayGetValueAtIndex(static_cast<CFArrayRef>(_propertyValue), i);
951                     newPropertyName = _propertyName + OUString::number(i);
952                     insertPropertyIntoMacabRecord(_abrecord, _header, newPropertyName, arrValue);
953                     CFRelease(arrValue);
954                 }
955 
956             }
957             break;
958 
959         /* Dictionary */
960         case kABDictionaryProperty:
961             {
962                 /* A dictionary is basically a hashmap. Technically, it can
963                  * hold any object as a key and any object as a value.
964                  * For our case, we assume that the key is a string (so that
965                  * we can use the key to get the column name and match it against
966                  * the header), but we don't assume anything about the value, so
967                  * we run this method recursively (or, rather, we run the version
968                  * of this method for when we don't know the object's type) until
969                  * we hit a scalar value.
970                  */
971 
972                 sal_Int32 numRecords = static_cast<sal_Int32>(CFDictionaryGetCount(static_cast<CFDictionaryRef>(_propertyValue)));
973                 OUString dictKeyString;
974                 sal_Int32 i;
975                 OUString newPropertyName;
976 
977                 /* Unfortunately, the only way to get both keys and values out
978                  * of a dictionary in Carbon is to get them all at once, so we
979                  * do that.
980                  */
981                 CFStringRef *dictKeys;
982                 CFStringRef localizedDictKey;
983                 CFTypeRef *dictValues;
984                 dictKeys = static_cast<CFStringRef *>(malloc(sizeof(CFStringRef)*numRecords));
985                 dictValues = static_cast<CFTypeRef *>(malloc(sizeof(CFTypeRef)*numRecords));
986                 CFDictionaryGetKeysAndValues(static_cast<CFDictionaryRef>(_propertyValue), reinterpret_cast<const void **>(dictKeys), dictValues);
987 
988                 /* Going through each element... */
989                 for(i = 0; i < numRecords; i++)
990                 {
991                     localizedDictKey = ABCopyLocalizedPropertyOrLabel(dictKeys[i]);
992                     dictKeyString = CFStringToOUString(localizedDictKey);
993                     CFRelease(localizedDictKey);
994                     newPropertyName = _propertyName + ": " + fixLabel(dictKeyString);
995                     insertPropertyIntoMacabRecord(_abrecord, _header, newPropertyName, dictValues[i]);
996                 }
997 
998                 free(dictKeys);
999                 free(dictValues);
1000             }
1001             break;
1002 
1003         /* Multivalue */
1004         case kABMultiIntegerProperty:
1005         case kABMultiDateProperty:
1006         case kABMultiStringProperty:
1007         case kABMultiRealProperty:
1008         case kABMultiDataProperty:
1009         case kABMultiDictionaryProperty:
1010         case kABMultiArrayProperty:
1011             {
1012                 /* All scalar multivalues are handled in the same way. Each element
1013                  * is a label and a value. All labels are strings
1014                  * (kABStringProperty), and all values have the same type
1015                  * (which is the type of the multivalue minus 255, or as
1016                  * Carbon's list of property types has it, minus 0x100.
1017                  * We just get the correct type, then go through each element
1018                  * and get the label and value and print them in a list.
1019                  */
1020 
1021                 sal_Int32 i;
1022                 sal_Int32 multiLength = ABMultiValueCount(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)));
1023                 CFStringRef multiLabel, localizedMultiLabel;
1024                 CFTypeRef multiValue;
1025                 OUString multiLabelString, newPropertyName;
1026                 ABPropertyType multiType = static_cast<ABPropertyType>(ABMultiValuePropertyType(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue))) - 0x100);
1027 
1028                 /* Go through each element... */
1029                 for(i = 0; i < multiLength; i++)
1030                 {
1031                     /* Label and value */
1032                     multiLabel = ABMultiValueCopyLabelAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i);
1033                     multiValue = ABMultiValueCopyValueAtIndex(static_cast<ABMutableMultiValueRef>(const_cast<void *>(_propertyValue)), i);
1034 
1035                     localizedMultiLabel = ABCopyLocalizedPropertyOrLabel(multiLabel);
1036                     multiLabelString = CFStringToOUString(localizedMultiLabel);
1037                     newPropertyName = _propertyName + ": " + fixLabel(multiLabelString);
1038                     insertPropertyIntoMacabRecord(multiType, _abrecord, _header, newPropertyName, multiValue);
1039 
1040                     /* free our variables */
1041                     CFRelease(multiLabel);
1042                     CFRelease(localizedMultiLabel);
1043                     CFRelease(multiValue);
1044                 }
1045             }
1046             break;
1047 
1048         /* Unhandled types */
1049         case kABErrorInProperty:
1050         case kABDataProperty:
1051         default:
1052             /* An error, as far as I have seen, only shows up as a type
1053              * returned by a function for dictionaries when the dictionary
1054              * holds many types of values. Since we do not use that function,
1055              * it shouldn't come up. I have yet to see the kABDataProperty,
1056              * and I am not sure how to represent it as a string anyway,
1057              * since it appears to just be a bunch of bytes. Assumably, if
1058              * these bytes made up a string, the type would be
1059              * kABStringProperty. I think that this is used when we are not
1060              * sure what the type is (e.g., it could be a string or a number).
1061              * That being the case, I still don't know how to represent it.
1062              * And, default should never come up, since we've exhausted all
1063              * of the possible types for ABPropertyType, but... just in case.
1064              */
1065             break;
1066     }
1067 
1068 }
1069 
1070 
getABTypeFromCFType(const CFTypeID cf_type) const1071 ABPropertyType MacabRecords::getABTypeFromCFType(const CFTypeID cf_type ) const
1072 {
1073     for(auto const & i: lcl_CFTypes)
1074     {
1075         /* A match! */
1076         if(i.cf == cf_type)
1077         {
1078             return static_cast<ABPropertyType>(i.ab);
1079         }
1080     }
1081     return kABErrorInProperty;
1082 }
1083 
1084 
size() const1085 sal_Int32 MacabRecords::size() const
1086 {
1087     return currentRecord;
1088 }
1089 
1090 
begin()1091 MacabRecords *MacabRecords::begin()
1092 {
1093     return this;
1094 }
1095 
1096 
iterator()1097 MacabRecords::iterator::iterator ()
1098 {
1099 }
1100 
1101 
operator =(MacabRecords * _records)1102 MacabRecords::iterator& MacabRecords::iterator::operator= (MacabRecords *_records)
1103 {
1104     id = 0;
1105     records = _records;
1106     return *this;
1107 }
1108 
1109 
operator ++()1110 void MacabRecords::iterator::operator++ ()
1111 {
1112     id++;
1113 }
1114 
1115 
operator !=(const sal_Int32 i) const1116 bool MacabRecords::iterator::operator!= (const sal_Int32 i) const
1117 {
1118     return(id != i);
1119 }
1120 
1121 
operator ==(const sal_Int32 i) const1122 bool MacabRecords::iterator::operator== (const sal_Int32 i) const
1123 {
1124     return(id == i);
1125 }
1126 
1127 
operator *() const1128 MacabRecord *MacabRecords::iterator::operator* () const
1129 {
1130     return records->getRecord(id);
1131 }
1132 
1133 
end() const1134 sal_Int32 MacabRecords::end() const
1135 {
1136     return currentRecord;
1137 }
1138 
1139 
swap(const sal_Int32 _id1,const sal_Int32 _id2)1140 void MacabRecords::swap(const sal_Int32 _id1, const sal_Int32 _id2)
1141 {
1142     MacabRecord *swapRecord = records[_id1];
1143 
1144     records[_id1] = records[_id2];
1145     records[_id2] = swapRecord;
1146 }
1147 
1148 
setName(const OUString & _sName)1149 void MacabRecords::setName(const OUString& _sName)
1150 {
1151     m_sName = _sName;
1152 }
1153 
1154 
getName() const1155 OUString const & MacabRecords::getName() const
1156 {
1157     return m_sName;
1158 }
1159 
1160 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1161